Centralize MoreObjects import
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / test / java / org / opendaylight / mdsal / binding / java / api / generator / BuilderGeneratorTest.java
index b2317e15f6db8132225dde649ea632b15e8d5b52..3f78e889121632d11abe55d9949280673a493ab7 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.mdsal.binding.java.api.generator;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -15,6 +16,7 @@ import static org.mockito.Mockito.spy;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
+import org.eclipse.xtend2.lib.StringConcatenation;
 import org.junit.Test;
 import org.opendaylight.mdsal.binding.generator.impl.DefaultBindingGenerator;
 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
@@ -34,11 +36,128 @@ public class BuilderGeneratorTest {
         assertEquals("", new BuilderGenerator().generate(mock(Type.class)));
     }
 
+    @Test
+    public void builderTemplateGenerateHashcodeWithPropertyTest() {
+        final GeneratedType genType = mockGenType("get" + TEST);
+
+        assertXtendEquals("/**\n"
+                + " * Default implementation of {@link Object#hashCode()} contract for this interface.\n"
+                + " * Implementations of this interface are encouraged to defer to this method to get consistent"
+                + " hashing\n"
+                + " * results across all implementations.\n"
+                + " *\n"
+                + " * @param obj Object for which to generate hashCode() result.\n"
+                + " * @return Hash code value of data modeled by this interface.\n"
+                + " * @throws NullPointerException if {@code obj} is null\n"
+                + " */\n"
+                + "static int bindingHashCode(final test.@NonNull test obj) {\n"
+                + "    final int prime = 31;\n"
+                + "    int result = 1;\n"
+                + "    result = prime * result + Objects.hashCode(obj.getTest());\n"
+                + "    return result;\n"
+                + "}\n", genHashCode(genType).toString());
+    }
+
+    @Test
+    public void builderTemplateGenerateHashCodeWithoutAnyPropertyTest() throws Exception {
+        assertEquals("", genHashCode(mockGenType(TEST)).toString());
+    }
+
+    @Test
+    public void builderTemplateGenerateHashCodeWithMorePropertiesTest() throws Exception {
+        assertXtendEquals("/**\n"
+                + " * Default implementation of {@link Object#hashCode()} contract for this interface.\n"
+                + " * Implementations of this interface are encouraged to defer to this method to get consistent"
+                + " hashing\n"
+                + " * results across all implementations.\n"
+                + " *\n"
+                + " * @param obj Object for which to generate hashCode() result.\n"
+                + " * @return Hash code value of data modeled by this interface.\n"
+                + " * @throws NullPointerException if {@code obj} is null\n"
+                + " */\n"
+                + "static int bindingHashCode(final test.@NonNull test obj) {\n"
+                + "    final int prime = 31;\n"
+                + "    int result = 1;\n"
+                + "    result = prime * result + Objects.hashCode(obj.getTest1());\n"
+                + "    result = prime * result + Objects.hashCode(obj.getTest2());\n"
+                + "    return result;\n"
+                + "}\n", genHashCode(mockGenTypeMoreMeth("get" + TEST)).toString());
+    }
+
+    @Test
+    public void builderTemplateGenerateHashCodeWithoutPropertyWithAugmentTest() throws Exception {
+        assertXtendEquals("/**\n"
+                + " * Default implementation of {@link Object#hashCode()} contract for this interface.\n"
+                + " * Implementations of this interface are encouraged to defer to this method to get consistent"
+                + " hashing\n"
+                + " * results across all implementations.\n"
+                + " *\n"
+                + " * @param obj Object for which to generate hashCode() result.\n"
+                + " * @return Hash code value of data modeled by this interface.\n"
+                + " * @throws NullPointerException if {@code obj} is null\n"
+                + " */\n"
+                + "static int bindingHashCode(final test.@NonNull test obj) {\n"
+                + "    final int prime = 31;\n"
+                + "    int result = 1;\n"
+                + "    for (var augmentation : obj.augmentations().values()) {\n"
+                + "        result += augmentation.hashCode();\n"
+                + "    }\n"
+                + "    return result;\n"
+                + "}\n", genHashCode(mockAugment(mockGenType(TEST))).toString());
+    }
+
+    @Test
+    public void builderTemplateGenerateHashCodeWithPropertyWithAugmentTest() throws Exception {
+        assertXtendEquals("/**\n"
+                + " * Default implementation of {@link Object#hashCode()} contract for this interface.\n"
+                + " * Implementations of this interface are encouraged to defer to this method to get consistent"
+                + " hashing\n"
+                + " * results across all implementations.\n"
+                + " *\n"
+                + " * @param obj Object for which to generate hashCode() result.\n"
+                + " * @return Hash code value of data modeled by this interface.\n"
+                + " * @throws NullPointerException if {@code obj} is null\n"
+                + " */\n"
+                + "static int bindingHashCode(final test.@NonNull test obj) {\n"
+                + "    final int prime = 31;\n"
+                + "    int result = 1;\n"
+                + "    result = prime * result + Objects.hashCode(obj.getTest());\n"
+                + "    for (var augmentation : obj.augmentations().values()) {\n"
+                + "        result += augmentation.hashCode();\n"
+                + "    }\n"
+                + "    return result;\n"
+                + "}\n", genHashCode(mockAugment(mockGenType("get" + TEST))).toString());
+    }
+
+    @Test
+    public void builderTemplateGenerateHashCodeWithMorePropertiesWithAugmentTest() throws Exception {
+        assertXtendEquals("/**\n"
+                + " * Default implementation of {@link Object#hashCode()} contract for this interface.\n"
+                + " * Implementations of this interface are encouraged to defer to this method to get consistent"
+                + " hashing\n"
+                + " * results across all implementations.\n"
+                + " *\n"
+                + " * @param obj Object for which to generate hashCode() result.\n"
+                + " * @return Hash code value of data modeled by this interface.\n"
+                + " * @throws NullPointerException if {@code obj} is null\n"
+                + " */\n"
+                + "static int bindingHashCode(final test.@NonNull test obj) {\n"
+                + "    final int prime = 31;\n"
+                + "    int result = 1;\n"
+                + "    result = prime * result + Objects.hashCode(obj.getTest1());\n"
+                + "    result = prime * result + Objects.hashCode(obj.getTest2());\n"
+                + "    for (var augmentation : obj.augmentations().values()) {\n"
+                + "        result += augmentation.hashCode();\n"
+                + "    }\n"
+                + "    return result;\n"
+                + "}\n", genHashCode(mockAugment(mockGenTypeMoreMeth("get" + TEST))).toString());
+    }
+
     @Test
     public void builderTemplateGenerateToStringWithPropertyTest() {
         final GeneratedType genType = mockGenType("get" + TEST);
 
-        assertEquals("/**\n"
+        assertXtendEquals("/**\n"
                 + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
                 + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
                 + "\n * representations across all implementations.\n"
@@ -47,8 +166,8 @@ public class BuilderGeneratorTest {
                 + " * @return {@link String} value of data modeled by this interface.\n"
                 + " * @throws NullPointerException if {@code obj} is null\n"
                 + " */\n"
-                + "static String bindingToString(final test.test obj) {\n"
-                + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
+                + "static String bindingToString(final test.@NonNull test obj) {\n"
+                + "    final var helper = MoreObjects.toStringHelper(\"test\");\n"
                 + "    CodeHelpers.appendValue(helper, \"test\", obj.gettest());\n"
                 + "    return helper.toString();\n"
                 + "}\n", genToString(genType).toString());
@@ -56,7 +175,7 @@ public class BuilderGeneratorTest {
 
     @Test
     public void builderTemplateGenerateToStringWithoutAnyPropertyTest() throws Exception {
-        assertEquals("/**\n"
+        assertXtendEquals("/**\n"
                 + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
                 + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
                 + "\n * representations across all implementations.\n"
@@ -65,15 +184,15 @@ public class BuilderGeneratorTest {
                 + " * @return {@link String} value of data modeled by this interface.\n"
                 + " * @throws NullPointerException if {@code obj} is null\n"
                 + " */\n"
-                + "static String bindingToString(final test.test obj) {\n"
-                + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
+                + "static String bindingToString(final test.@NonNull test obj) {\n"
+                + "    final var helper = MoreObjects.toStringHelper(\"test\");\n"
                 + "    return helper.toString();\n"
                 + "}\n", genToString(mockGenType(TEST)).toString());
     }
 
     @Test
     public void builderTemplateGenerateToStringWithMorePropertiesTest() throws Exception {
-        assertEquals("/**\n"
+        assertXtendEquals("/**\n"
                 + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
                 + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
                 + "\n * representations across all implementations.\n"
@@ -82,8 +201,8 @@ public class BuilderGeneratorTest {
                 + " * @return {@link String} value of data modeled by this interface.\n"
                 + " * @throws NullPointerException if {@code obj} is null\n"
                 + " */\n"
-                + "static String bindingToString(final test.test obj) {\n"
-                + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
+                + "static String bindingToString(final test.@NonNull test obj) {\n"
+                + "    final var helper = MoreObjects.toStringHelper(\"test\");\n"
                 + "    CodeHelpers.appendValue(helper, \"test1\", obj.gettest1());\n"
                 + "    CodeHelpers.appendValue(helper, \"test2\", obj.gettest2());\n"
                 + "    return helper.toString();\n"
@@ -92,66 +211,57 @@ public class BuilderGeneratorTest {
 
     @Test
     public void builderTemplateGenerateToStringWithoutPropertyWithAugmentTest() throws Exception {
-        assertEquals("/**\n"
+        assertXtendEquals("/**\n"
                 + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
                 + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
                 + "\n * representations across all implementations.\n"
                 + " *\n"
-                + " * @param <T$$> implementation type, which has to also implement AugmentationHolder interface\n"
-                + " *              contract.\n"
                 + " * @param obj Object for which to generate toString() result.\n"
                 + " * @return {@link String} value of data modeled by this interface.\n"
                 + " * @throws NullPointerException if {@code obj} is null\n"
                 + " */\n"
-                + "static <T$$ extends test.test & AugmentationHolder<test.test>> String bindingToString(final @NonNull"
-                + " T$$ obj) {\n"
-                + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
-                + "    CodeHelpers.appendValue(helper, \"augmentation\", obj.augmentations().values());\n"
+                + "static String bindingToString(final test.@NonNull test obj) {\n"
+                + "    final var helper = MoreObjects.toStringHelper(\"test\");\n"
+                + "    CodeHelpers.appendAugmentations(helper, \"augmentation\", obj);\n"
                 + "    return helper.toString();\n"
                 + "}\n", genToString(mockAugment(mockGenType(TEST))).toString());
     }
 
     @Test
     public void builderTemplateGenerateToStringWithPropertyWithAugmentTest() throws Exception {
-        assertEquals("/**\n"
+        assertXtendEquals("/**\n"
                 + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
                 + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
                 + "\n * representations across all implementations.\n"
                 + " *\n"
-                + " * @param <T$$> implementation type, which has to also implement AugmentationHolder interface\n"
-                + " *              contract.\n"
                 + " * @param obj Object for which to generate toString() result.\n"
                 + " * @return {@link String} value of data modeled by this interface.\n"
                 + " * @throws NullPointerException if {@code obj} is null\n"
                 + " */\n"
-                + "static <T$$ extends test.test & AugmentationHolder<test.test>> String bindingToString(final @NonNull"
-                + " T$$ obj) {\n"
-                + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
+                + "static String bindingToString(final test.@NonNull test obj) {\n"
+                + "    final var helper = MoreObjects.toStringHelper(\"test\");\n"
                 + "    CodeHelpers.appendValue(helper, \"test\", obj.gettest());\n"
-                + "    CodeHelpers.appendValue(helper, \"augmentation\", obj.augmentations().values());\n"
+                + "    CodeHelpers.appendAugmentations(helper, \"augmentation\", obj);\n"
                 + "    return helper.toString();\n"
                 + "}\n", genToString(mockAugment(mockGenType("get" + TEST))).toString());
     }
 
     @Test
     public void builderTemplateGenerateToStringWithMorePropertiesWithAugmentTest() throws Exception {
-        assertEquals("/**\n"
+        assertXtendEquals("/**\n"
                 + " * Default implementation of {@link Object#toString()} contract for this interface.\n"
                 + " * Implementations of this interface are encouraged to defer to this method to get consistent string"
                 + "\n * representations across all implementations.\n"
                 + " *\n"
-                + " * @param <T$$> implementation type, which has to also implement AugmentationHolder interface\n"
-                + " *              contract.\n"
                 + " * @param obj Object for which to generate toString() result.\n"
                 + " * @return {@link String} value of data modeled by this interface.\n"
                 + " * @throws NullPointerException if {@code obj} is null\n"
                 + " */\n"
-                + "static <T$$ extends test.test & AugmentationHolder<test.test>> String bindingToString(final @NonNull"
-                + " T$$ obj) {\n"
-                + "    final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n"
+                + "static String bindingToString(final test.@NonNull test obj) {\n"
+                + "    final var helper = MoreObjects.toStringHelper(\"test\");\n"
                 + "    CodeHelpers.appendValue(helper, \"test1\", obj.gettest1());\n"
                 + "    CodeHelpers.appendValue(helper, \"test2\", obj.gettest2());\n"
-                + "    CodeHelpers.appendValue(helper, \"augmentation\", obj.augmentations().values());\n"
+                + "    CodeHelpers.appendAugmentations(helper, \"augmentation\", obj);\n"
                 + "    return helper.toString();\n"
                 + "}\n", genToString(mockAugment(mockGenTypeMoreMeth("get" + TEST))).toString());
     }
@@ -160,8 +270,11 @@ public class BuilderGeneratorTest {
     public void builderTemplateGenerateToEqualsComparingOrderTest() {
         final EffectiveModelContext context = YangParserTestUtils.parseYangResource(
                 "/test-types.yang");
-        final List<Type> types = new DefaultBindingGenerator().generateTypes(context);
-        final BuilderTemplate bt = BuilderGenerator.templateForType((GeneratedType) types.get(19));
+        final List<GeneratedType> types = new DefaultBindingGenerator().generateTypes(context);
+        assertEquals(29, types.size());
+
+        final BuilderTemplate bt = BuilderGenerator.templateForType(
+            types.stream().filter(t -> t.getName().equals("Nodes")).findFirst().orElseThrow());
 
         final List<String> sortedProperties = bt.properties.stream()
                 .sorted(ByTypeMemberComparator.getInstance())
@@ -215,6 +328,10 @@ public class BuilderGeneratorTest {
         return new InterfaceTemplate(genType).generateBindingToString();
     }
 
+    private static CharSequence genHashCode(final GeneratedType genType) {
+        return new InterfaceTemplate(genType).generateBindingHashCode();
+    }
+
     private static GeneratedType mockGenType(final String methodeName) {
         final GeneratedType genType = spy(GeneratedType.class);
         doReturn(TYPE_NAME).when(genType).getIdentifier();
@@ -235,9 +352,17 @@ public class BuilderGeneratorTest {
         final MethodSignature methSign = mock(MethodSignature.class);
         doReturn(methodeName).when(methSign).getName();
         final Type methType = mock(Type.class);
+        doCallRealMethod().when(methType).getFullyQualifiedName();
         doReturn(TYPE_NAME).when(methType).getIdentifier();
+        doReturn(TEST).when(methType).getName();
         doReturn(methType).when(methSign).getReturnType();
         doReturn(ValueMechanics.NORMAL).when(methSign).getMechanics();
         return methSign;
     }
+
+    // Xtend's StringConcatenation is using runtime-configured line separator, which can change between runs, notably
+    // it has a different value on Windows. Make sure we account for that.
+    private static void assertXtendEquals(final String expected, final String actual) {
+        assertEquals(expected.replace("\n", StringConcatenation.DEFAULT_LINE_DELIMITER), actual);
+    }
 }