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