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