X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=binding%2Fmdsal-binding-java-api-generator%2Fsrc%2Ftest%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fjava%2Fapi%2Fgenerator%2FBuilderGeneratorTest.java;h=3f78e889121632d11abe55d9949280673a493ab7;hb=refs%2Fchanges%2F88%2F100488%2F3;hp=2fa5bc79711c200cca47104577412542d605603d;hpb=af0d41fe67efb0a8aee67de5f7dd942e3e88def4;p=mdsal.git diff --git a/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGeneratorTest.java b/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGeneratorTest.java index 2fa5bc7971..3f78e88912 100644 --- a/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGeneratorTest.java +++ b/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGeneratorTest.java @@ -8,104 +8,293 @@ 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; -import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; +import org.eclipse.xtend2.lib.StringConcatenation; import org.junit.Test; -import org.opendaylight.mdsal.binding.model.api.GeneratedProperty; +import org.opendaylight.mdsal.binding.generator.impl.DefaultBindingGenerator; import org.opendaylight.mdsal.binding.model.api.GeneratedType; +import org.opendaylight.mdsal.binding.model.api.JavaTypeName; import org.opendaylight.mdsal.binding.model.api.MethodSignature; +import org.opendaylight.mdsal.binding.model.api.MethodSignature.ValueMechanics; import org.opendaylight.mdsal.binding.model.api.Type; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; public class BuilderGeneratorTest { - - private static final String PROPERTIES_FIELD_NAME = "properties"; - private static final String GEN_TO_STRING_FIRST_PART = - "@Override\npublic java.lang.String toString() {\n java.lang.String name = \"test [\";\n " - + "java.lang.StringBuilder builder = new java.lang.StringBuilder (name);"; - private static final String GEN_TO_STRING_LAST_PART = "\n return builder.append(']').toString();\n}\n"; - private static final String GEN_TO_STRING_AUGMENT_PART = - "\n builder.append(\"augmentation=\");\n builder.append(augmentation.values());"; - private static final String APPEND_COMMA = "builder.append(\", \");"; - private static final String APPEND_COMMA_AUGMENT = "final int builderLength = builder.length();\n" - + " final int builderAdditionalLength = builder.substring(name.length(), builderLength).length();\n" - + " if (builderAdditionalLength > 2 && !builder.substring(builderLength - 2, builderLength).equals(\", \")) {\n" - + " " + APPEND_COMMA + "\n" + " }"; private static final String TEST = "test"; + private static final JavaTypeName TYPE_NAME = JavaTypeName.create(TEST, TEST); @Test public void basicTest() { 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("@Override\n" + - "public java.lang.String toString() {\n" + - " final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n" + - " CodeHelpers.appendValue(helper, \"_test\", _test);\n" + - " return helper.toString();\n" + - "}\n", genToString(genType).toString()); + 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 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 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()); } @Test public void builderTemplateGenerateToStringWithoutAnyPropertyTest() throws Exception { - assertEquals("@Override\n" + - "public java.lang.String toString() {\n" + - " final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n" + - " return helper.toString();\n" + - "}\n", genToString(mockGenType(TEST)).toString()); + 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 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 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("@Override\n" + - "public java.lang.String toString() {\n" + - " final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n" + - " CodeHelpers.appendValue(helper, \"_test1\", _test1);\n" + - " CodeHelpers.appendValue(helper, \"_test2\", _test2);\n" + - " return helper.toString();\n" + - "}\n", genToString(mockGenTypeMoreMeth("get" + TEST)).toString()); + 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 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 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" + + "}\n", genToString(mockGenTypeMoreMeth("get" + TEST)).toString()); } @Test public void builderTemplateGenerateToStringWithoutPropertyWithAugmentTest() throws Exception { - assertEquals("@Override\n" + - "public java.lang.String toString() {\n" + - " final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n" + - " CodeHelpers.appendValue(helper, \"augmentation\", augmentation.values()); \n" + - " return helper.toString();\n" + - "}\n", genToString(mockAugment(mockGenType(TEST))).toString()); + 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 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 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("@Override\n" + - "public java.lang.String toString() {\n" + - " final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n" + - " CodeHelpers.appendValue(helper, \"_test\", _test);\n" + - " CodeHelpers.appendValue(helper, \"augmentation\", augmentation.values()); \n" + - " return helper.toString();\n" + - "}\n", genToString(mockAugment(mockGenType("get" + TEST))).toString()); + 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 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 String bindingToString(final test.@NonNull test obj) {\n" + + " final var helper = MoreObjects.toStringHelper(\"test\");\n" + + " CodeHelpers.appendValue(helper, \"test\", obj.gettest());\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("@Override\n" + - "public java.lang.String toString() {\n" + - " final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n" + - " CodeHelpers.appendValue(helper, \"_test1\", _test1);\n" + - " CodeHelpers.appendValue(helper, \"_test2\", _test2);\n" + - " CodeHelpers.appendValue(helper, \"augmentation\", augmentation.values()); \n" + - " return helper.toString();\n" + - "}\n", genToString(mockAugment(mockGenTypeMoreMeth("get" + TEST))).toString()); + 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 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 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.appendAugmentations(helper, \"augmentation\", obj);\n" + + " return helper.toString();\n" + + "}\n", genToString(mockAugment(mockGenTypeMoreMeth("get" + TEST))).toString()); + } + + @Test + public void builderTemplateGenerateToEqualsComparingOrderTest() { + final EffectiveModelContext context = YangParserTestUtils.parseYangResource( + "/test-types.yang"); + final List 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 sortedProperties = bt.properties.stream() + .sorted(ByTypeMemberComparator.getInstance()) + .map(BuilderGeneratedProperty::getName) + .collect(Collectors.toList()); + + assertEquals(List.of( + // numeric types (boolean, byte, short, int, long, biginteger, bigdecimal), identityrefs, empty + "id16", "id16Def", "id32", "id32Def", "id64", "id64Def", "id8", "id8Def", "idBoolean", "idBooleanDef", + "idDecimal64", "idDecimal64Def","idEmpty", "idEmptyDef", "idIdentityref", "idIdentityrefDef", + "idLeafref", "idLeafrefDef", "idU16", "idU16Def", "idU32", "idU32Def", "idU64", "idU64Def", "idU8", + "idU8Def", + // string, binary, bits + "idBinary", "idBinaryDef", "idBits", "idBitsDef", "idGroupLeafString", "idLeafrefContainer1", + "idLeafrefContainer1Def", "idString", "idStringDef", + // instance identifier + "idInstanceIdentifier", "idInstanceIdentifierDef", + // other types + "idContainer1", "idContainer2", "idEnumeration", "idEnumerationDef", + "idGroupContainer", "idList", "idUnion", "idUnionDef"), sortedProperties); } private static GeneratedType mockAugment(final GeneratedType genType) { @@ -119,6 +308,7 @@ public class BuilderGeneratorTest { private static GeneratedType mockGenTypeMoreMeth(final String methodeName) { final GeneratedType genType = spy(GeneratedType.class); + doReturn(TYPE_NAME).when(genType).getIdentifier(); doReturn(TEST).when(genType).getName(); doReturn(TEST).when(genType).getPackageName(); @@ -134,20 +324,17 @@ public class BuilderGeneratorTest { return genType; } - @SuppressWarnings("unchecked") private static CharSequence genToString(final GeneratedType genType) { - try { - final BuilderTemplate bt = new BuilderTemplate(genType); - final Field propertiesField = bt.getClass().getDeclaredField(PROPERTIES_FIELD_NAME); - propertiesField.setAccessible(true); - return bt.generateToString((Collection) propertiesField.get(bt)); - } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { - throw new RuntimeException(e); - } + 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(); doReturn(TEST).when(genType).getName(); doReturn(TEST).when(genType).getPackageName(); @@ -165,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(TEST).when(methType).getPackageName(); 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); + } }