RFC8040 'rc:yang-data' support for mdsal binding generator
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / test / java / org / opendaylight / mdsal / binding / java / api / generator / CompilationTest.java
index d629cf0a900e233e483c4bcf3655206e893c2b02..53ce2472609e67828a4f7c76927a454a3d320219 100644 (file)
@@ -11,6 +11,7 @@ import static org.hamcrest.CoreMatchers.startsWith;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
 import com.google.common.collect.Collections2;
@@ -24,20 +25,22 @@ import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
 import java.lang.reflect.WildcardType;
-import java.math.BigDecimal;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HexFormat;
 import java.util.List;
 import java.util.stream.Collectors;
 import org.junit.Test;
 import org.opendaylight.mdsal.binding.model.ri.TypeConstants;
 import org.opendaylight.yangtools.yang.binding.ChildOf;
 import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
+import org.opendaylight.yangtools.yang.common.Decimal64;
 import org.opendaylight.yangtools.yang.common.Empty;
 import org.opendaylight.yangtools.yang.common.Uint16;
 import org.opendaylight.yangtools.yang.common.Uint32;
@@ -118,7 +121,7 @@ public class CompilationTest extends BaseCompilationTest {
         // Test generated 'list links'
         assertTrue(linksClass.isInterface());
         CompilationTestUtils.assertImplementsIfc(linksClass, keyArgsClass);
-        assertEquals(7, abstractMethods(linksClass).size());
+        assertEquals(8, abstractMethods(linksClass).size());
         CompilationTestUtils.assertContainsMethod(linksClass,
             "org.opendaylight.yang.gen.v1.urn.opendaylight.test.rev131008.links.Text", "getText", loader);
         CompilationTestUtils.assertContainsMethod(linksClass,
@@ -134,6 +137,56 @@ public class CompilationTest extends BaseCompilationTest {
         CompilationTestUtils.cleanUp(sourcesOutputDir, compiledOutputDir);
     }
 
+    /**
+     * Test that nonnull getter method is generated for non-presence containers only.
+     *
+     * @throws Exception when any exception occurs during the test
+     */
+    @Test
+    public void testContainerGettersGeneration() throws Exception {
+        final File sourcesOutputDir = CompilationTestUtils.generatorOutput("containers-gen");
+        final File compiledOutputDir = CompilationTestUtils.compilerOutput("containers-gen");
+        generateTestSources("/compilation/containers-gen", sourcesOutputDir);
+
+        // Test if all sources were generated from 'module containers'
+        File parent = new File(sourcesOutputDir, CompilationTestUtils.NS_TEST);
+        assertTrue(new File(parent, "RootContainer.java").exists());
+        assertTrue(new File(parent, "rootcontainer/PresenceContainer.java").exists());
+        assertTrue(new File(parent, "rootcontainer/NonPresenceContainer.java").exists());
+        CompilationTestUtils.assertFilesCount(parent, 5);
+
+        // Test if sources are compilable
+        CompilationTestUtils.testCompilation(sourcesOutputDir, compiledOutputDir);
+
+        final ClassLoader loader = new URLClassLoader(new URL[] { compiledOutputDir.toURI().toURL() });
+        final Class<?> rootClass = Class.forName(CompilationTestUtils.BASE_PKG
+                + ".urn.opendaylight.test.rev131008.RootContainer", true, loader);
+
+        // Test generated 'container root'
+        assertTrue(rootClass.isInterface());
+        assertEquals(3, abstractMethods(rootClass).size());
+
+        // Test generated getter and not-generated nonnull method for presence container
+        CompilationTestUtils.assertContainsMethod(rootClass,
+                "org.opendaylight.yang.gen.v1.urn.opendaylight.test.rev131008.rootcontainer.PresenceContainer",
+                "getPresenceContainer", loader);
+        final var error = assertThrows(AssertionError.class, () ->
+                CompilationTestUtils.assertContainsMethod(rootClass,
+                        "org.opendaylight.yang.gen.v1.urn.opendaylight.test.rev131008.rootcontainer.PresenceContainer",
+                        "nonnullPresenceContainer", loader));
+        assertTrue(error.getCause() instanceof NoSuchMethodException);
+
+        // Test generated getter and nonnull methods for non-presence container
+        CompilationTestUtils.assertContainsMethod(rootClass,
+                "org.opendaylight.yang.gen.v1.urn.opendaylight.test.rev131008.rootcontainer.NonPresenceContainer",
+                "getNonPresenceContainer", loader);
+        CompilationTestUtils.assertContainsMethod(rootClass,
+                "org.opendaylight.yang.gen.v1.urn.opendaylight.test.rev131008.rootcontainer.NonPresenceContainer",
+                "nonnullNonPresenceContainer", loader);
+
+        CompilationTestUtils.cleanUp(sourcesOutputDir, compiledOutputDir);
+    }
+
     @Test
     public void testAugmentUnderUsesGeneration() throws Exception {
         final File sourcesOutputDir = CompilationTestUtils.generatorOutput("augment-under-uses");
@@ -147,7 +200,8 @@ public class CompilationTest extends BaseCompilationTest {
         assertTrue(new File(parent, "OpenObject.java").exists());
         assertTrue(new File(parent, "ExplicitRouteObject.java").exists());
         assertTrue(new File(parent, "PathKeySubobject.java").exists());
-        CompilationTestUtils.assertFilesCount(parent, 10);
+        assertTrue(new File(parent, "FooData.java").exists());
+        CompilationTestUtils.assertFilesCount(parent, 11);
 
         parent = new File(parent, "object");
         assertTrue(new File(parent, "Nodes.java").exists());
@@ -202,10 +256,11 @@ public class CompilationTest extends BaseCompilationTest {
 
         // Test if all sources were generated from 'module bar'
         parent = new File(sourcesOutputDir, CompilationTestUtils.NS_BAR);
+        assertTrue(new File(parent, "BarData.java").exists());
         assertTrue(new File(parent, "BasicExplicitRouteSubobjects.java").exists());
         assertTrue(new File(parent, "ExplicitRouteSubobjects.java").exists());
         assertTrue(new File(parent, "RouteSubobjects.java").exists());
-        CompilationTestUtils.assertFilesCount(parent, 6);
+        CompilationTestUtils.assertFilesCount(parent, 7);
 
         parent = new File(parent, "route");
         CompilationTestUtils.assertFilesCount(parent, 1);
@@ -244,15 +299,12 @@ public class CompilationTest extends BaseCompilationTest {
 
         // Test if all sources were generated from 'module foo'
         File parent = new File(sourcesOutputDir, CompilationTestUtils.NS_FOO);
-        final File fooListener = new File(parent, "FooListener.java");
-        File pathAttributes = new File(parent, "PathAttributes.java");
-        final File update = new File(parent, "Update.java");
-        final File updateBuilder = new File(parent, "UpdateBuilder.java");
-        assertTrue(fooListener.exists());
-        assertTrue(pathAttributes.exists());
-        assertTrue(update.exists());
-        assertTrue(updateBuilder.exists());
-        CompilationTestUtils.assertFilesCount(parent, 7);
+        assertTrue(new File(parent, "FooData.java").exists());
+        assertTrue(new File(parent, "FooListener.java").exists());
+        assertTrue(new File(parent, "PathAttributes.java").exists());
+        assertTrue(new File(parent, "Update.java").exists());
+        assertTrue(new File(parent, "UpdateBuilder.java").exists());
+        CompilationTestUtils.assertFilesCount(parent, 8);
 
         parent = new File(sourcesOutputDir, CompilationTestUtils.NS_FOO + CompilationTestUtils.FS + "path");
         CompilationTestUtils.assertFilesCount(parent, 1);
@@ -265,20 +317,16 @@ public class CompilationTest extends BaseCompilationTest {
 
         parent = new File(sourcesOutputDir, CompilationTestUtils.NS_FOO + CompilationTestUtils.FS + "update");
         CompilationTestUtils.assertFilesCount(parent, 2);
-        pathAttributes = new File(parent, "PathAttributes.java");
-        final File pathAttributesBuilder = new File(parent, "PathAttributesBuilder.java");
-        assertTrue(pathAttributes.exists());
-        assertTrue(pathAttributesBuilder.exists());
+        assertTrue(new File(parent, "PathAttributes.java").exists());
+        assertTrue(new File(parent, "PathAttributesBuilder.java").exists());
 
         // Test if all sources were generated from 'module bar'
         parent = new File(sourcesOutputDir, CompilationTestUtils.NS_BAR);
-        final File destination = new File(parent, "Destination.java");
-        final File pathAttributes1 = new File(parent, "PathAttributes1.java");
-        final File pathAttributes1Builder = new File(parent, "PathAttributes1Builder.java");
-        assertTrue(destination.exists());
-        assertTrue(pathAttributes1.exists());
-        assertTrue(pathAttributes1Builder.exists());
-        CompilationTestUtils.assertFilesCount(parent, 6);
+        assertTrue(new File(parent, "BarData.java").exists());
+        assertTrue(new File(parent, "Destination.java").exists());
+        assertTrue(new File(parent, "PathAttributes1.java").exists());
+        assertTrue(new File(parent, "PathAttributes1Builder.java").exists());
+        CompilationTestUtils.assertFilesCount(parent, 7);
 
         parent = new File(sourcesOutputDir, CompilationTestUtils.NS_BAR + CompilationTestUtils.FS + "destination");
         CompilationTestUtils.assertFilesCount(parent, 2);
@@ -310,17 +358,15 @@ public class CompilationTest extends BaseCompilationTest {
         parent = new File(parent, "unreach");
         CompilationTestUtils.assertFilesCount(parent, 1);
         parent = new File(parent, "nlri");
-        final File withdrawnRoutes = new File(parent, "WithdrawnRoutes.java");
-        final File withdrawnRoutesBuilder = new File(parent, "WithdrawnRoutesBuilder.java");
-        assertTrue(withdrawnRoutes.exists());
-        assertTrue(withdrawnRoutesBuilder.exists());
+        assertTrue(new File(parent, "WithdrawnRoutes.java").exists());
+        assertTrue(new File(parent, "WithdrawnRoutesBuilder.java").exists());
         CompilationTestUtils.assertFilesCount(parent, 2);
 
         // Test if all sources were generated from 'module baz'
         parent = new File(sourcesOutputDir, CompilationTestUtils.NS_BAZ);
-        CompilationTestUtils.assertFilesCount(parent, 3);
-        final File linkstateDestination = new File(parent, "LinkstateDestination.java");
-        assertTrue(linkstateDestination.exists());
+        assertTrue(new File(parent, "BazData.java").exists());
+        assertTrue(new File(parent, "LinkstateDestination.java").exists());
+        CompilationTestUtils.assertFilesCount(parent, 4);
 
         parent = new File(sourcesOutputDir, CompilationTestUtils.NS_BAZ + CompilationTestUtils.FS + "update");
         CompilationTestUtils.assertFilesCount(parent, 1);
@@ -384,8 +430,7 @@ public class CompilationTest extends BaseCompilationTest {
         assertTrue(new File(parent, "Nodes.java").exists());
         assertTrue(new File(parent, "NodesBuilder.java").exists());
         assertTrue(new File(parent, "Alg.java").exists());
-        assertTrue(new File(parent, "NodesIdUnionBuilder.java").exists());
-        CompilationTestUtils.assertFilesCount(parent, 6);
+        CompilationTestUtils.assertFilesCount(parent, 5);
 
         // Test if sources are compilable
         CompilationTestUtils.testCompilation(sourcesOutputDir, compiledOutputDir);
@@ -400,7 +445,7 @@ public class CompilationTest extends BaseCompilationTest {
         CompilationTestUtils.assertContainsMethod(nodesClass, b.getClass(), "getIdBinary");
         CompilationTestUtils.assertContainsMethod(nodesClass, pkg + ".Nodes$IdBits", "getIdBits", loader);
         CompilationTestUtils.assertContainsMethod(nodesClass, Boolean.class, "getIdBoolean");
-        CompilationTestUtils.assertContainsMethod(nodesClass, BigDecimal.class, "getIdDecimal64");
+        CompilationTestUtils.assertContainsMethod(nodesClass, Decimal64.class, "getIdDecimal64");
         CompilationTestUtils.assertContainsMethod(nodesClass, Empty.class, "getIdEmpty");
         CompilationTestUtils.assertContainsMethod(nodesClass, pkg + ".Nodes$IdEnumeration", "getIdEnumeration", loader);
         testReturnTypeIdentityref(nodesClass, "getIdIdentityref", pkg + ".Alg");
@@ -424,15 +469,15 @@ public class CompilationTest extends BaseCompilationTest {
         final List<Range<Integer>> lengthConstraints = new ArrayList<>();
         lengthConstraints.add(Range.closed(1, 10));
         byte[] arg = new byte[] {};
-        String expectedMsg = String.format("Invalid length: %s, expected: %s.", Arrays.toString(arg),
+        String expectedMsg = String.format("Invalid length: %s, expected: %s.", HexFormat.of().formatHex(arg),
             lengthConstraints);
         CompilationTestUtils.assertContainsRestrictionCheck(builderObj, method, expectedMsg, arg);
 
         method = CompilationTestUtils.assertContainsMethod(builderClass, builderClass, "setIdDecimal64",
-            BigDecimal.class);
-        final List<Range<BigDecimal>> rangeConstraints = new ArrayList<>();
-        rangeConstraints.add(Range.closed(new BigDecimal("1.5"), new BigDecimal("5.5")));
-        Object arg1 = new BigDecimal("1.4");
+            Decimal64.class);
+        final List<Range<Decimal64>> rangeConstraints = new ArrayList<>();
+        rangeConstraints.add(Range.closed(Decimal64.valueOf("1.5"), Decimal64.valueOf("5.5")));
+        Object arg1 = Decimal64.valueOf("1.4");
         expectedMsg = String.format("Invalid range: %s, expected: %s.", arg1, rangeConstraints);
         CompilationTestUtils.assertContainsRestrictionCheck(builderObj, method, expectedMsg, arg1);
 
@@ -454,7 +499,8 @@ public class CompilationTest extends BaseCompilationTest {
         assertTrue(new File(fooParent, "NodesBuilder.java").exists());
 
         final File barParent = new File(sourcesOutputDir, CompilationTestUtils.NS_BAR);
-        CompilationTestUtils.assertFilesCount(barParent, 2);
+        CompilationTestUtils.assertFilesCount(barParent, 3);
+        assertTrue(new File(barParent, "BarData.java").exists());
         assertTrue(new File(barParent, "IdentityClass.java").exists());
 
         // Test if sources are compilable
@@ -746,7 +792,7 @@ public class CompilationTest extends BaseCompilationTest {
         generateTestSources("/compilation/union-string-pattern", sourcesOutputDir);
         CompilationTestUtils.testCompilation(sourcesOutputDir, compiledOutputDir);
 
-        final ClassLoader loader = new URLClassLoader(new URL[] { compiledOutputDir.toURI().toURL() });
+        final ClassLoader loader = new URLClassLoader(new URL[]{compiledOutputDir.toURI().toURL()});
         final Class<?> fooClass = Class.forName(CompilationTestUtils.BASE_PKG + ".foo.norev.Foo", true, loader);
 
         final Field patterns = fooClass.getDeclaredField(TypeConstants.PATTERN_CONSTANT_NAME);
@@ -755,19 +801,72 @@ public class CompilationTest extends BaseCompilationTest {
         CompilationTestUtils.cleanUp(sourcesOutputDir, compiledOutputDir);
     }
 
+    @Test
+    public void yangDataCompilation() throws Exception {
+        final File sourcesOutputDir = CompilationTestUtils.generatorOutput("yang-data-gen");
+        final File compiledOutputDir = CompilationTestUtils.compilerOutput("yang-data-gen");
+
+        generateTestSources("/compilation/yang-data-gen", sourcesOutputDir);
+        CompilationTestUtils.testCompilation(sourcesOutputDir, compiledOutputDir);
+
+        final ClassLoader loader = new URLClassLoader(new URL[]{compiledOutputDir.toURI().toURL()});
+        final List<String> artifactNames = List.of(
+                // module with top level container
+                "$YangModuleInfoImpl", "YangDataDemoData", "RootContainer", "RootContainerBuilder",
+
+                // yang-data artifacts
+                "YangDataWithContainer", "YangDataWithContainerBuilder",
+                "YangDataWithList", "YangDataWithListBuilder",
+                "YangDataWithLeaf", "YangDataWithLeafBuilder",
+                "YangDataWithLeafList", "YangDataWithLeafListBuilder",
+                "YangDataWithAnydata", "YangDataWithAnydataBuilder",
+                "YangDataWithAnyxml", "YangDataWithAnyxmlBuilder",
+
+                // yang-data content artifacts
+                "yang.data.with.container.ContainerFromYangData",
+                "yang.data.with.container.ContainerFromYangDataBuilder",
+                "yang.data.with.list.ListFromYangData", "yang.data.with.list.ListFromYangDataBuilder",
+                "yang.data.with.anydata.AnydataFromYangData", "yang.data.with.anyxml.AnyxmlFromYangData",
+
+                // yang-data artifacts using groups
+                "YangDataWithContainerFromGroup", "YangDataWithContainerFromGroupBuilder",
+                "YangDataWithListFromGroup", "YangDataWithListFromGroupBuilder",
+                "YangDataWithLeafFromGroup", "YangDataWithLeafFromGroupBuilder",
+                "YangDataWithLeafListFromGroup", "YangDataWithLeafListFromGroupBuilder",
+                "YangDataWithAnydataFromGroup", "YangDataWithAnydataFromGroupBuilder",
+                "YangDataWithAnyxmlFromGroup", "YangDataWithAnyxmlFromGroupBuilder",
+
+                // group artifacts
+                "GrpForContainer", "GrpForList", "GrpForLeaf", "GrpForLeafList", "GrpForAnydata", "GrpForAnyxml",
+
+                // group content artifacts
+                "grp._for.container.ContainerFromGroup", "grp._for.container.ContainerFromGroupBuilder",
+                "grp._for.list.ListFromGroup", "grp._for.list.ListFromGroupBuilder",
+                "grp._for.anydata.AnydataFromGroup", "grp._for.anyxml.AnyxmlFromGroup",
+
+                // artifacts for non-ascii template naming: yang data artifact, inner container + builder
+                "$ľaľaho$20$papľuhu", "$ľaľaho$20$papľuhuBuilder",
+                "$ľaľaho$20$papľuhu$.LatinNaming", "$ľaľaho$20$papľuhu$.LatinNamingBuilder",
+                "привет", "приветBuilder", "привет$.CyrillicNaming", "привет$.CyrillicNamingBuilder"
+        );
+
+        for (String name : artifactNames) {
+            final String className = CompilationTestUtils.BASE_PKG + ".urn.test.yang.data.demo.rev220222." + name;
+            // ensure class source is generated
+            final String srcPath = className.replace('.', File.separatorChar) + ".java";
+            assertTrue(srcPath + " exists", new File(sourcesOutputDir, srcPath).exists());
+            // ensure class is loadable
+            Class.forName(className, true, loader);
+        }
+
+        CompilationTestUtils.cleanUp(sourcesOutputDir, compiledOutputDir);
+    }
+
     private static void testReturnTypeIdentityref(final Class<?> clazz, final String methodName,
             final String returnTypeStr) throws NoSuchMethodException {
-        Method method = clazz.getMethod(methodName);
-        assertEquals(java.lang.Class.class, method.getReturnType());
-        java.lang.reflect.Type returnType = method.getGenericReturnType();
-        assertTrue(returnType instanceof ParameterizedType);
-        final ParameterizedType pt = (ParameterizedType) returnType;
-        final java.lang.reflect.Type[] parameters = pt.getActualTypeArguments();
-        assertEquals(1, parameters.length);
-        final java.lang.reflect.Type parameter = parameters[0];
-        assertTrue(parameter instanceof WildcardType);
-        final WildcardType wildcardType = (WildcardType) parameter;
-        assertEquals("? extends " + returnTypeStr, wildcardType.toString());
+        Class<?> returnType = clazz.getMethod(methodName).getReturnType();
+        assertTrue(returnType.isInterface());
+        assertEquals(returnTypeStr, returnType.getName());
     }
 
     private static void testReturnTypeInstanceIdentitifer(final ClassLoader loader, final Class<?> clazz,
@@ -776,12 +875,12 @@ public class CompilationTest extends BaseCompilationTest {
         final Class<?> rawReturnType = Class.forName("org.opendaylight.yangtools.yang.binding.InstanceIdentifier", true,
             loader);
         assertEquals(rawReturnType, method.getReturnType());
-        final java.lang.reflect.Type returnType = method.getGenericReturnType();
+        final Type returnType = method.getGenericReturnType();
         assertTrue(returnType instanceof ParameterizedType);
         final ParameterizedType pt = (ParameterizedType) returnType;
-        final java.lang.reflect.Type[] parameters = pt.getActualTypeArguments();
+        final Type[] parameters = pt.getActualTypeArguments();
         assertEquals(1, parameters.length);
-        final java.lang.reflect.Type parameter = parameters[0];
+        final Type parameter = parameters[0];
         assertTrue(parameter instanceof WildcardType);
         final WildcardType wildcardType = (WildcardType) parameter;
         assertEquals("?", wildcardType.toString());