Improve mdsal-binding-java-api-generator asserts
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / test / java / org / opendaylight / mdsal / binding / java / api / generator / BuilderGeneratorTest.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.binding.java.api.generator;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.mockito.Mockito.doCallRealMethod;
12 import static org.mockito.Mockito.doReturn;
13 import static org.mockito.Mockito.mock;
14 import static org.mockito.Mockito.spy;
15
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.stream.Collectors;
19 import org.eclipse.xtend2.lib.StringConcatenation;
20 import org.junit.Test;
21 import org.opendaylight.mdsal.binding.generator.impl.DefaultBindingGenerator;
22 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
23 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
24 import org.opendaylight.mdsal.binding.model.api.MethodSignature;
25 import org.opendaylight.mdsal.binding.model.api.MethodSignature.ValueMechanics;
26 import org.opendaylight.mdsal.binding.model.api.Type;
27 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
28 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
29
30 public class BuilderGeneratorTest {
31     private static final String TEST = "test";
32     private static final JavaTypeName TYPE_NAME = JavaTypeName.create(TEST, TEST);
33
34     @Test
35     public void basicTest() {
36         assertEquals("", new BuilderGenerator().generate(mock(Type.class)));
37     }
38
39     @Test
40     public void builderTemplateGenerateHashcodeWithPropertyTest() {
41         final GeneratedType genType = mockGenType("get" + TEST);
42
43         assertXtendEquals("/**\n"
44                 + " * Default implementation of {@link Object#hashCode()} contract for this interface.\n"
45                 + " * Implementations of this interface are encouraged to defer to this method to get consistent"
46                 + " hashing\n"
47                 + " * results across all implementations.\n"
48                 + " *\n"
49                 + " * @param obj Object for which to generate hashCode() result.\n"
50                 + " * @return Hash code value of data modeled by this interface.\n"
51                 + " * @throws NullPointerException if {@code obj} is null\n"
52                 + " */\n"
53                 + "static int bindingHashCode(final test.@NonNull test obj) {\n"
54                 + "    final int prime = 31;\n"
55                 + "    int result = 1;\n"
56                 + "    result = prime * result + Objects.hashCode(obj.getTest());\n"
57                 + "    return result;\n"
58                 + "}\n", genHashCode(genType).toString());
59     }
60
61     @Test
62     public void builderTemplateGenerateHashCodeWithoutAnyPropertyTest() throws Exception {
63         assertEquals("", genHashCode(mockGenType(TEST)).toString());
64     }
65
66     @Test
67     public void builderTemplateGenerateHashCodeWithMorePropertiesTest() throws Exception {
68         assertXtendEquals("/**\n"
69                 + " * Default implementation of {@link Object#hashCode()} contract for this interface.\n"
70                 + " * Implementations of this interface are encouraged to defer to this method to get consistent"
71                 + " hashing\n"
72                 + " * results across all implementations.\n"
73                 + " *\n"
74                 + " * @param obj Object for which to generate hashCode() result.\n"
75                 + " * @return Hash code value of data modeled by this interface.\n"
76                 + " * @throws NullPointerException if {@code obj} is null\n"
77                 + " */\n"
78                 + "static int bindingHashCode(final test.@NonNull test obj) {\n"
79                 + "    final int prime = 31;\n"
80                 + "    int result = 1;\n"
81                 + "    result = prime * result + Objects.hashCode(obj.getTest1());\n"
82                 + "    result = prime * result + Objects.hashCode(obj.getTest2());\n"
83                 + "    return result;\n"
84                 + "}\n", genHashCode(mockGenTypeMoreMeth("get" + TEST)).toString());
85     }
86
87     @Test
88     public void builderTemplateGenerateHashCodeWithoutPropertyWithAugmentTest() throws Exception {
89         assertXtendEquals("/**\n"
90                 + " * Default implementation of {@link Object#hashCode()} contract for this interface.\n"
91                 + " * Implementations of this interface are encouraged to defer to this method to get consistent"
92                 + " hashing\n"
93                 + " * results across all implementations.\n"
94                 + " *\n"
95                 + " * @param obj Object for which to generate hashCode() result.\n"
96                 + " * @return Hash code value of data modeled by this interface.\n"
97                 + " * @throws NullPointerException if {@code obj} is null\n"
98                 + " */\n"
99                 + "static int bindingHashCode(final test.@NonNull test obj) {\n"
100                 + "    final int prime = 31;\n"
101                 + "    int result = 1;\n"
102                 + "    result = prime * result + obj.augmentations().hashCode();\n"
103                 + "    return result;\n"
104                 + "}\n", genHashCode(mockAugment(mockGenType(TEST))).toString());
105     }
106
107     @Test
108     public void builderTemplateGenerateHashCodeWithPropertyWithAugmentTest() throws Exception {
109         assertXtendEquals("/**\n"
110                 + " * Default implementation of {@link Object#hashCode()} contract for this interface.\n"
111                 + " * Implementations of this interface are encouraged to defer to this method to get consistent"
112                 + " hashing\n"
113                 + " * results across all implementations.\n"
114                 + " *\n"
115                 + " * @param obj Object for which to generate hashCode() result.\n"
116                 + " * @return Hash code value of data modeled by this interface.\n"
117                 + " * @throws NullPointerException if {@code obj} is null\n"
118                 + " */\n"
119                 + "static int bindingHashCode(final test.@NonNull test obj) {\n"
120                 + "    final int prime = 31;\n"
121                 + "    int result = 1;\n"
122                 + "    result = prime * result + Objects.hashCode(obj.getTest());\n"
123                 + "    result = prime * result + obj.augmentations().hashCode();\n"
124                 + "    return result;\n"
125                 + "}\n", genHashCode(mockAugment(mockGenType("get" + TEST))).toString());
126     }
127
128     @Test
129     public void builderTemplateGenerateHashCodeWithMorePropertiesWithAugmentTest() throws Exception {
130         assertXtendEquals("/**\n"
131                 + " * Default implementation of {@link Object#hashCode()} contract for this interface.\n"
132                 + " * Implementations of this interface are encouraged to defer to this method to get consistent"
133                 + " hashing\n"
134                 + " * results across all implementations.\n"
135                 + " *\n"
136                 + " * @param obj Object for which to generate hashCode() result.\n"
137                 + " * @return Hash code value of data modeled by this interface.\n"
138                 + " * @throws NullPointerException if {@code obj} is null\n"
139                 + " */\n"
140                 + "static int bindingHashCode(final test.@NonNull test obj) {\n"
141                 + "    final int prime = 31;\n"
142                 + "    int result = 1;\n"
143                 + "    result = prime * result + Objects.hashCode(obj.getTest1());\n"
144                 + "    result = prime * result + Objects.hashCode(obj.getTest2());\n"
145                 + "    result = prime * result + obj.augmentations().hashCode();\n"
146                 + "    return result;\n"
147                 + "}\n", genHashCode(mockAugment(mockGenTypeMoreMeth("get" + TEST))).toString());
148     }
149
150     @Test
151     public void builderTemplateGenerateToStringWithPropertyTest() {
152         final GeneratedType genType = mockGenType("get" + TEST);
153
154         assertXtendEquals("/**\n"
155                 + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
156                 + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
157                 + "\n * representations across all implementations.\n"
158                 + " *\n"
159                 + " * @param obj Object for which to generate toString() result.\n"
160                 + " * @return {@link String} value of data modeled by this interface.\n"
161                 + " * @throws NullPointerException if {@code obj} is null\n"
162                 + " */\n"
163                 + "static String bindingToString(final test.@NonNull test obj) {\n"
164                 + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
165                 + "    CodeHelpers.appendValue(helper, \"test\", obj.gettest());\n"
166                 + "    return helper.toString();\n"
167                 + "}\n", genToString(genType).toString());
168     }
169
170     @Test
171     public void builderTemplateGenerateToStringWithoutAnyPropertyTest() throws Exception {
172         assertXtendEquals("/**\n"
173                 + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
174                 + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
175                 + "\n * representations across all implementations.\n"
176                 + " *\n"
177                 + " * @param obj Object for which to generate toString() result.\n"
178                 + " * @return {@link String} value of data modeled by this interface.\n"
179                 + " * @throws NullPointerException if {@code obj} is null\n"
180                 + " */\n"
181                 + "static String bindingToString(final test.@NonNull test obj) {\n"
182                 + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
183                 + "    return helper.toString();\n"
184                 + "}\n", genToString(mockGenType(TEST)).toString());
185     }
186
187     @Test
188     public void builderTemplateGenerateToStringWithMorePropertiesTest() throws Exception {
189         assertXtendEquals("/**\n"
190                 + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
191                 + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
192                 + "\n * representations across all implementations.\n"
193                 + " *\n"
194                 + " * @param obj Object for which to generate toString() result.\n"
195                 + " * @return {@link String} value of data modeled by this interface.\n"
196                 + " * @throws NullPointerException if {@code obj} is null\n"
197                 + " */\n"
198                 + "static String bindingToString(final test.@NonNull test obj) {\n"
199                 + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
200                 + "    CodeHelpers.appendValue(helper, \"test1\", obj.gettest1());\n"
201                 + "    CodeHelpers.appendValue(helper, \"test2\", obj.gettest2());\n"
202                 + "    return helper.toString();\n"
203                 + "}\n", genToString(mockGenTypeMoreMeth("get" + TEST)).toString());
204     }
205
206     @Test
207     public void builderTemplateGenerateToStringWithoutPropertyWithAugmentTest() throws Exception {
208         assertXtendEquals("/**\n"
209                 + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
210                 + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
211                 + "\n * representations across all implementations.\n"
212                 + " *\n"
213                 + " * @param obj Object for which to generate toString() result.\n"
214                 + " * @return {@link String} value of data modeled by this interface.\n"
215                 + " * @throws NullPointerException if {@code obj} is null\n"
216                 + " */\n"
217                 + "static String bindingToString(final test.@NonNull test obj) {\n"
218                 + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
219                 + "    CodeHelpers.appendValue(helper, \"augmentation\", obj.augmentations().values());\n"
220                 + "    return helper.toString();\n"
221                 + "}\n", genToString(mockAugment(mockGenType(TEST))).toString());
222     }
223
224     @Test
225     public void builderTemplateGenerateToStringWithPropertyWithAugmentTest() throws Exception {
226         assertXtendEquals("/**\n"
227                 + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
228                 + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
229                 + "\n * representations across all implementations.\n"
230                 + " *\n"
231                 + " * @param obj Object for which to generate toString() result.\n"
232                 + " * @return {@link String} value of data modeled by this interface.\n"
233                 + " * @throws NullPointerException if {@code obj} is null\n"
234                 + " */\n"
235                 + "static String bindingToString(final test.@NonNull test obj) {\n"
236                 + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
237                 + "    CodeHelpers.appendValue(helper, \"test\", obj.gettest());\n"
238                 + "    CodeHelpers.appendValue(helper, \"augmentation\", obj.augmentations().values());\n"
239                 + "    return helper.toString();\n"
240                 + "}\n", genToString(mockAugment(mockGenType("get" + TEST))).toString());
241     }
242
243     @Test
244     public void builderTemplateGenerateToStringWithMorePropertiesWithAugmentTest() throws Exception {
245         assertXtendEquals("/**\n"
246                 + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
247                 + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
248                 + "\n * representations across all implementations.\n"
249                 + " *\n"
250                 + " * @param obj Object for which to generate toString() result.\n"
251                 + " * @return {@link String} value of data modeled by this interface.\n"
252                 + " * @throws NullPointerException if {@code obj} is null\n"
253                 + " */\n"
254                 + "static String bindingToString(final test.@NonNull test obj) {\n"
255                 + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
256                 + "    CodeHelpers.appendValue(helper, \"test1\", obj.gettest1());\n"
257                 + "    CodeHelpers.appendValue(helper, \"test2\", obj.gettest2());\n"
258                 + "    CodeHelpers.appendValue(helper, \"augmentation\", obj.augmentations().values());\n"
259                 + "    return helper.toString();\n"
260                 + "}\n", genToString(mockAugment(mockGenTypeMoreMeth("get" + TEST))).toString());
261     }
262
263     @Test
264     public void builderTemplateGenerateToEqualsComparingOrderTest() {
265         final EffectiveModelContext context = YangParserTestUtils.parseYangResource(
266                 "/test-types.yang");
267         final List<GeneratedType> types = new DefaultBindingGenerator().generateTypes(context);
268         assertEquals(29, types.size());
269
270         final BuilderTemplate bt = BuilderGenerator.templateForType(
271             types.stream().filter(t -> t.getName().equals("Nodes")).findFirst().orElseThrow());
272
273         final List<String> sortedProperties = bt.properties.stream()
274                 .sorted(ByTypeMemberComparator.getInstance())
275                 .map(BuilderGeneratedProperty::getName)
276                 .collect(Collectors.toList());
277
278         assertEquals(List.of(
279                 // numeric types (boolean, byte, short, int, long, biginteger, bigdecimal), identityrefs, empty
280                 "id16", "id16Def", "id32", "id32Def", "id64", "id64Def", "id8", "id8Def", "idBoolean", "idBooleanDef",
281                 "idDecimal64", "idDecimal64Def","idEmpty", "idEmptyDef", "idIdentityref", "idIdentityrefDef",
282                 "idLeafref", "idLeafrefDef", "idU16", "idU16Def", "idU32", "idU32Def", "idU64", "idU64Def", "idU8",
283                 "idU8Def",
284                 // string, binary, bits
285                 "idBinary", "idBinaryDef", "idBits", "idBitsDef", "idGroupLeafString", "idLeafrefContainer1",
286                 "idLeafrefContainer1Def", "idString", "idStringDef",
287                 // instance identifier
288                 "idInstanceIdentifier", "idInstanceIdentifierDef",
289                 // other types
290                 "idContainer1", "idContainer2", "idEnumeration", "idEnumerationDef",
291                 "idGroupContainer", "idList", "idUnion", "idUnionDef"), sortedProperties);
292     }
293
294     private static GeneratedType mockAugment(final GeneratedType genType) {
295         final List<Type> impls = new ArrayList<>();
296         final Type impl = mock(Type.class);
297         doReturn("org.opendaylight.yangtools.yang.binding.Augmentable").when(impl).getFullyQualifiedName();
298         impls.add(impl);
299         doReturn(impls).when(genType).getImplements();
300         return genType;
301     }
302
303     private static GeneratedType mockGenTypeMoreMeth(final String methodeName) {
304         final GeneratedType genType = spy(GeneratedType.class);
305         doReturn(TYPE_NAME).when(genType).getIdentifier();
306         doReturn(TEST).when(genType).getName();
307         doReturn(TEST).when(genType).getPackageName();
308
309         final List<MethodSignature> listMethodSign = new ArrayList<>();
310         for (int i = 0; i < 2; i++) {
311             final MethodSignature methSign = mockMethSign(methodeName + (i + 1));
312             listMethodSign.add(methSign);
313         }
314         doReturn(listMethodSign).when(genType).getMethodDefinitions();
315
316         final List<Type> impls = new ArrayList<>();
317         doReturn(impls).when(genType).getImplements();
318         return genType;
319     }
320
321     private static CharSequence genToString(final GeneratedType genType) {
322         return new InterfaceTemplate(genType).generateBindingToString();
323     }
324
325     private static CharSequence genHashCode(final GeneratedType genType) {
326         return new InterfaceTemplate(genType).generateBindingHashCode();
327     }
328
329     private static GeneratedType mockGenType(final String methodeName) {
330         final GeneratedType genType = spy(GeneratedType.class);
331         doReturn(TYPE_NAME).when(genType).getIdentifier();
332         doReturn(TEST).when(genType).getName();
333         doReturn(TEST).when(genType).getPackageName();
334
335         final List<MethodSignature> listMethodSign = new ArrayList<>();
336         final MethodSignature methSign = mockMethSign(methodeName);
337         listMethodSign.add(methSign);
338         doReturn(listMethodSign).when(genType).getMethodDefinitions();
339
340         final List<Type> impls = new ArrayList<>();
341         doReturn(impls).when(genType).getImplements();
342         return genType;
343     }
344
345     private static MethodSignature mockMethSign(final String methodeName) {
346         final MethodSignature methSign = mock(MethodSignature.class);
347         doReturn(methodeName).when(methSign).getName();
348         final Type methType = mock(Type.class);
349         doCallRealMethod().when(methType).getFullyQualifiedName();
350         doReturn(TYPE_NAME).when(methType).getIdentifier();
351         doReturn(TEST).when(methType).getName();
352         doReturn(methType).when(methSign).getReturnType();
353         doReturn(ValueMechanics.NORMAL).when(methSign).getMechanics();
354         return methSign;
355     }
356
357     // Xtend's StringConcatenation is using runtime-configured line separator, which can change between runs, notably
358     // it has a different value on Windows. Make sure we account for that.
359     private static void assertXtendEquals(final String expected, final String actual) {
360         assertEquals(expected.replace("\n", StringConcatenation.DEFAULT_LINE_DELIMITER), actual);
361     }
362 }