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