2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.java.api.generator;
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;
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;
29 public class BuilderGeneratorTest {
30 private static final String TEST = "test";
31 private static final JavaTypeName TYPE_NAME = JavaTypeName.create(TEST, TEST);
34 public void basicTest() {
35 assertEquals("", new BuilderGenerator().generate(mock(Type.class)));
39 public void builderTemplateGenerateHashcodeWithPropertyTest() {
40 final GeneratedType genType = mockGenType("get" + TEST);
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"
46 + " * results across all implementations.\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"
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"
57 + "}\n", genHashCode(genType).toString());
61 public void builderTemplateGenerateHashCodeWithoutAnyPropertyTest() throws Exception {
62 assertEquals("", genHashCode(mockGenType(TEST)).toString());
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"
71 + " * results across all implementations.\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"
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"
83 + "}\n", genHashCode(mockGenTypeMoreMeth("get" + TEST)).toString());
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"
92 + " * results across all implementations.\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"
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"
104 + " return result;\n"
105 + "}\n", genHashCode(mockAugment(mockGenType(TEST))).toString());
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"
114 + " * results across all implementations.\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"
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"
127 + " return result;\n"
128 + "}\n", genHashCode(mockAugment(mockGenType("get" + TEST))).toString());
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"
137 + " * results across all implementations.\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"
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"
151 + " return result;\n"
152 + "}\n", genHashCode(mockAugment(mockGenTypeMoreMeth("get" + TEST))).toString());
156 public void builderTemplateGenerateToStringWithPropertyTest() {
157 final GeneratedType genType = mockGenType("get" + TEST);
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"
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"
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());
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"
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"
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());
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"
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"
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());
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"
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"
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());
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"
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"
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());
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"
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"
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());
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());
274 final BuilderTemplate bt = BuilderGenerator.templateForType(
275 types.stream().filter(t -> t.getName().equals("Nodes")).findFirst().orElseThrow());
277 final List<String> sortedProperties = bt.properties.stream()
278 .sorted(ByTypeMemberComparator.getInstance())
279 .map(BuilderGeneratedProperty::getName)
280 .collect(Collectors.toList());
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",
288 // string, binary, bits
289 "idBinary", "idBinaryDef", "idBits", "idBitsDef", "idGroupLeafString", "idLeafrefContainer1",
290 "idLeafrefContainer1Def", "idString", "idStringDef",
291 // instance identifier
292 "idInstanceIdentifier", "idInstanceIdentifierDef",
294 "idContainer1", "idContainer2", "idEnumeration", "idEnumerationDef",
295 "idGroupContainer", "idList", "idUnion", "idUnionDef"), sortedProperties);
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();
303 doReturn(impls).when(genType).getImplements();
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();
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);
318 doReturn(listMethodSign).when(genType).getMethodDefinitions();
320 final List<Type> impls = new ArrayList<>();
321 doReturn(impls).when(genType).getImplements();
325 private static CharSequence genToString(final GeneratedType genType) {
326 return new InterfaceTemplate(genType).generateBindingToString();
329 private static CharSequence genHashCode(final GeneratedType genType) {
330 return new InterfaceTemplate(genType).generateBindingHashCode();
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();
339 final List<MethodSignature> listMethodSign = new ArrayList<>();
340 final MethodSignature methSign = mockMethSign(methodeName);
341 listMethodSign.add(methSign);
342 doReturn(listMethodSign).when(genType).getMethodDefinitions();
344 final List<Type> impls = new ArrayList<>();
345 doReturn(impls).when(genType).getImplements();
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();
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);