New test utility AssertDataObjects 53/48753/4
authorMichael Vorburger <vorburger@redhat.com>
Wed, 12 Oct 2016 15:10:48 +0000 (17:10 +0200)
committerRobert Varga <nite@hq.sk>
Tue, 20 Dec 2016 13:35:08 +0000 (13:35 +0000)
This adapts https://github.com/vorburger/xtendbeans to OpenDaylight.

That library can be used as-is for "normal" Java objects, such as Flow
beans, as seen e.g. in https://git.opendaylight.org/gerrit/#/c/42109/.

This change make it possible to use that library to compare actual
DataObject obtained from the DataBroker with expected objects in tests,
as illustrated e.g. in https://git.opendaylight.org/gerrit/#/c/44000/
(Those objects have a number of peculiarities which need adaption,
provided by the classes in this change.)

One can use AssertDataObjects.assertEqualBeans(Object expected, Object
actual)
WITHOUT ever writing any Xtend code, and WITHOUT any code generation,
and use this utility JUST to get a very readable text diff between two
object trees; this will look like shown in this presentation screenshot:
https://docs.google.com/presentation/d/1bnwj8CrFGo5KekONYSeIHySdkoXZiewJxkHcZjXnzkQ/edit#slide=id.g17d8ae4d92_0_150

It is ALSO possible, but not required, to use the text syntax shown (see
linked screenshot) to declare the expected object by copy/paste of that
syntax into an *.xtend source file (from which an Xtend Maven plugin
code
generates Java code), because that text "happens" (intentionally) to be
valid Xtend.

This last step is purely optional though, and while some downstream
projects may adopt this to define expected objects in a readable syntax,
others may not, and simply define their expected objects with normal
Java code and Builders (or reading them from XML, or whatever) - yet can
still use this this new utility - just for a great diff / comparison
kind of view, only.

This code has dependencies on yangtools, mdsal, plus a test
model for its self test - and the external xtendbeans library (which
should be considered like any of the 100s of other ext. deps. we have).

Change-Id: I34aac96bd13b936d6fb76a484a512081c18e0b11
Signed-off-by: Michael Vorburger <vorburger@redhat.com>
(cherry picked from commit 18170324fab2eb494c7d8721521a429802dd48f9)

12 files changed:
binding/mdsal-binding-test-utils/pom.xml [new file with mode: 0644]
binding/mdsal-binding-test-utils/src/main/java/org/opendaylight/mdsal/binding/testutils/AssertDataObjects.java [new file with mode: 0644]
binding/mdsal-binding-test-utils/src/main/java/org/opendaylight/mdsal/binding/testutils/AugmentableExtension.java [new file with mode: 0644]
binding/mdsal-binding-test-utils/src/main/java/org/opendaylight/mdsal/binding/testutils/XtendBuilderExtensions.java [new file with mode: 0644]
binding/mdsal-binding-test-utils/src/main/java/org/opendaylight/mdsal/binding/testutils/XtendYangBeanGenerator.java [new file with mode: 0644]
binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/AssertDataObjectsTest.java [new file with mode: 0644]
binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/AssertNonDataObjectsTest.java [new file with mode: 0644]
binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/AugmentableExtensionTest.java [new file with mode: 0644]
binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/ExampleYangObjects.java [new file with mode: 0644]
binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/ExpectedObjects.java [new file with mode: 0644]
binding/pom.xml
common/artifacts/pom.xml

diff --git a/binding/mdsal-binding-test-utils/pom.xml b/binding/mdsal-binding-test-utils/pom.xml
new file mode 100644 (file)
index 0000000..82c8d06
--- /dev/null
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2016 Red Hat, Inc. and others.  All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.opendaylight.odlparent</groupId>
+    <artifactId>odlparent</artifactId>
+    <version>1.7.2-SNAPSHOT</version>
+    <relativePath />
+  </parent>
+
+  <groupId>org.opendaylight.mdsal</groupId>
+  <artifactId>mdsal-binding-test-utils</artifactId>
+  <version>2.1.2-SNAPSHOT</version>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.opendaylight.mdsal</groupId>
+        <artifactId>mdsal-artifacts</artifactId>
+        <version>${project.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <dependencies>
+    <!-- NB: All of these are, intentionally, NOT <scope>test</scope>,
+         because downstream consumer projects of the test utilities
+         will necessarily also require these dependencies in order
+         to run tests such as the AssertDataObjectsTest. -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>ch.vorburger</groupId>
+      <artifactId>xtendbeans</artifactId>
+      <scope>compile</scope>
+      <version>1.2.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.mdsal</groupId>
+      <artifactId>mdsal-binding-dom-adapter</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.mdsal</groupId>
+      <artifactId>mdsal-binding-dom-adapter</artifactId>
+      <type>test-jar</type>
+    </dependency>
+    <!-- The following are dependencies which are listed
+         as scope test in mdsal-binding-dom-adapter; we
+         wanted most of them as scope compile here...      -->
+    <dependency>
+      <groupId>org.opendaylight.mdsal</groupId>
+      <artifactId>mdsal-dom-broker</artifactId>
+    </dependency>
+
+    <!-- The following, contrary to the above, ARE <scope>test,
+         because they are only required by the test utilities
+         self test code in src/test, but not (necessarily)
+         by a downstream consumer of the test utilities.   -->
+    <dependency>
+      <groupId>org.opendaylight.mdsal</groupId>
+      <artifactId>mdsal-binding-test-model</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <configuration>
+          <propertyExpansion>checkstyle.violationSeverity=error</propertyExpansion>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <!--
+      Maven Site Configuration
+
+      The following configuration is necessary for maven-site-plugin to
+      correctly identify the correct deployment path for OpenDaylight Maven
+      sites.
+  -->
+  <url>${odl.site.url}/${project.groupId}/${stream}/${project.artifactId}/</url>
+
+  <distributionManagement>
+    <site>
+      <id>opendaylight-site</id>
+      <url>${nexus.site.url}/${project.artifactId}/</url>
+    </site>
+  </distributionManagement>
+
+</project>
diff --git a/binding/mdsal-binding-test-utils/src/main/java/org/opendaylight/mdsal/binding/testutils/AssertDataObjects.java b/binding/mdsal-binding-test-utils/src/main/java/org/opendaylight/mdsal/binding/testutils/AssertDataObjects.java
new file mode 100644 (file)
index 0000000..ce17bba
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.testutils;
+
+import ch.vorburger.xtendbeans.AssertBeans;
+import org.junit.ComparisonFailure;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Assertion utilities for YANG {@link DataObject}s.
+ *
+ * <p>This compares two {@link DataObject}s by creating a textual representation of them,
+ * and comparing them.  If they are not equals, then the thrown ComparisonFailure provides
+ * for a very highly readable comparison due to a syntax which immediately makes the difference obvious.
+ *
+ * <p>The syntax used happens to be valid Xtend code, and as such could be directly copy/pasted
+ * into an *.xtend source file of an expected object definition.  This is optional though; this
+ * utility can very well be used with any object, not necessarily created by Xtend source code.
+ *
+ * <p>This also works for any Java object that is not a {@link DataObject},
+ * like the {@link AssertBeans} which this is based upon.
+ *
+ * @see AssertBeans for more background
+ *
+ * @author Michael Vorburger
+ */
+public final class AssertDataObjects {
+
+    private static final XtendYangBeanGenerator GENERATOR = new XtendYangBeanGenerator();
+
+    private AssertDataObjects() {
+    }
+
+    /**
+     * Assert that an actual YANG DataObject (DataContainer) is equals to an expected one.
+     *
+     * <p>The argument types are intentionally of type Object instead of YANG DataContainer or DataObject.
+     * This is important so that this can be directly used on e.g. a List or Map etc. of DataObjects.
+     *
+     * @param expected the expected object
+     * @param actual the actual object to check against <code>expected</code>
+     *
+     * @see AssertBeans#assertEqualBeans(Object, Object)
+     */
+    public static void assertEqualBeans(Object expected, Object actual) throws ComparisonFailure {
+        String expectedText = GENERATOR.getExpression(expected);
+        assertEqualByText(expectedText, actual);
+    }
+
+    // package local method used only in the self tests of this utility (not intended for usage by client code)
+    static void assertEqualByText(String expectedText, Object actual) throws ComparisonFailure {
+        String actualText = GENERATOR.getExpression(actual);
+        if (!expectedText.equals(actualText)) {
+            throw new ComparisonFailure("Expected and actual beans do not match", expectedText, actualText);
+        }
+    }
+
+}
diff --git a/binding/mdsal-binding-test-utils/src/main/java/org/opendaylight/mdsal/binding/testutils/AugmentableExtension.java b/binding/mdsal-binding-test-utils/src/main/java/org/opendaylight/mdsal/binding/testutils/AugmentableExtension.java
new file mode 100644 (file)
index 0000000..675ac68
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.testutils;
+
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import org.eclipse.xtext.xbase.lib.util.ReflectExtensions;
+import org.opendaylight.yangtools.binding.data.codec.util.AugmentationReader;
+import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+
+/**
+ * Adds an {@link #getAugmentations(Augmentable)} method to {@link Augmentable}.
+ *
+ * <p>Note that the generated *Impl classes in the *Builder do not implement
+ * {@link AugmentationReader}, only the LazyDataObject (package local) does.
+ *
+ * @see Augmentable
+ * @see AugmentationReader
+ *
+ * @author Michael Vorburger
+ */
+//package-local: no need to expose this, consider it an implementation detail; public API is the AssertDataObjects
+class AugmentableExtension {
+
+    private static final ReflectExtensions REFLECT_EXTENSIONS = new ReflectExtensions();
+
+    public ClassToInstanceMap<Augmentation<?>> getAugmentations(Augmentable<?> augmentable) {
+        if (augmentable instanceof AugmentationReader) {
+            AugmentationReader augmentationReader = (AugmentationReader) augmentable;
+            return ImmutableClassToInstanceMap.copyOf(augmentationReader.getAugmentations(augmentable));
+        } else if (Proxy.isProxyClass(augmentable.getClass())) {
+            InvocationHandler invocationHandler = Proxy.getInvocationHandler(augmentable);
+            // class LazyDataObject implements InvocationHandler, AugmentationReader
+            AugmentationReader augmentationReader = (AugmentationReader) invocationHandler;
+            return ImmutableClassToInstanceMap.copyOf(augmentationReader.getAugmentations(augmentable));
+        } else {
+            try {
+                return ImmutableClassToInstanceMap.copyOf(REFLECT_EXTENSIONS.get(augmentable, "augmentation"));
+            } catch (ClassCastException | SecurityException | NoSuchFieldException | IllegalArgumentException
+                    | IllegalAccessException e) {
+                throw new IllegalArgumentException("TODO Implement getAugmentations() for an Augmentable which "
+                        + "is neither a (Proxy of an) AugmentationReader nor has an internal field named "
+                        + "'augmentation': " + augmentable.getClass(), e);
+            }
+        }
+    }
+
+}
diff --git a/binding/mdsal-binding-test-utils/src/main/java/org/opendaylight/mdsal/binding/testutils/XtendBuilderExtensions.java b/binding/mdsal-binding-test-utils/src/main/java/org/opendaylight/mdsal/binding/testutils/XtendBuilderExtensions.java
new file mode 100644 (file)
index 0000000..0b43dce
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.testutils;
+
+import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
+import org.opendaylight.yangtools.concepts.Builder;
+
+/**
+ * Xtend extension method for &gt;&gt; operator support for {@link Builder}s.
+ *
+ * <pre>import static extension org.opendaylight.mdsal.binding.testutils
+ * .XtendBuilderExtensions.operator_doubleGreaterThan</pre>
+ *
+ * <p>allows to write (in an *.xtend, not *.java):
+ *
+ * <pre>new InterfaceBuilder &gt;&gt; [
+ *          name = "hello, world"
+ *      ]</pre>
+ *
+ * <p>instead of:
+ *
+ * <pre>(new InterfaceBuilder =&gt; [
+ *          name = "hello, world"
+ *      ]).build</pre>
+ *
+ * <p>See also org.eclipse.xtext.xbase.lib.ObjectExtensions.operator_doubleArrow for background.
+ *
+ * @author Michael Vorburger
+ */
+public final class XtendBuilderExtensions {
+
+    private XtendBuilderExtensions() { }
+
+    public static <P extends Object, T extends Builder<P>> P operator_doubleGreaterThan(
+            final T object, final Procedure1<? super T> block) {
+
+        block.apply(object);
+        return object.build();
+    }
+
+}
diff --git a/binding/mdsal-binding-test-utils/src/main/java/org/opendaylight/mdsal/binding/testutils/XtendYangBeanGenerator.java b/binding/mdsal-binding-test-utils/src/main/java/org/opendaylight/mdsal/binding/testutils/XtendYangBeanGenerator.java
new file mode 100644 (file)
index 0000000..2f7f7aa
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.testutils;
+
+import ch.vorburger.xtendbeans.XtendBeanGenerator;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.Iterables;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * {@link XtendBeanGenerator} customized for YANG beans stored in MD SAL
+ * DataBroker.
+ *
+ * <p>This is required: (a) because YANG model DataObject beans (when read from a
+ * DataBroker) are funky java.lang.reflect.Proxy instances, and XtendBeanGenerator
+ * cannot find the Builder or the property getters for them without a bit of help,
+ * which this class provides;
+ *
+ * <p>(b) to integrate it with the {@link XtendBuilderExtensions}
+ * (for ">>" instead of "->" and no build() method calls);
+ *
+ * <p>(c) to integrate it with the {@link AugmentableExtension}.
+ *
+ * @see XtendBeanGenerator
+ *
+ * @author Michael Vorburger
+ */
+// package-local: no need to expose this, consider it an implementation detail; public API is the AssertDataObjects
+class XtendYangBeanGenerator extends XtendBeanGenerator {
+
+    private final AugmentableExtension augmentableExtension = new AugmentableExtension();
+
+    private boolean useBuilderExtensions(Object bean) {
+        return bean instanceof DataObject;
+    }
+
+    @Override
+    public String getExpression(Object bean) {
+        final String beanText = super.getExpression(bean);
+        if (useBuilderExtensions(bean)) {
+            return new StringBuilder("import static extension ").append(XtendBuilderExtensions.class.getName())
+                    .append(".operator_doubleGreaterThan\n\n").append(beanText).toString();
+        } else {
+            return beanText;
+        }
+    }
+
+    @Override
+    protected boolean isUsingBuilder(Object bean, Class<?> builderClass) {
+        if (useBuilderExtensions(bean)) {
+            return false;
+        } else {
+            return super.isUsingBuilder(bean, builderClass);
+        }
+    }
+
+    @Override
+    protected String getOperator(Object bean, Class<?> builderClass) {
+        if (useBuilderExtensions(bean)) {
+            return ">>";
+        } else {
+            return super.getOperator(bean, builderClass);
+        }
+    }
+
+    @Override
+    protected CharSequence getNewBeanExpression(Object bean) {
+        if (bean instanceof DataContainer) {
+            DataContainer dataContainerBean = (DataContainer) bean;
+            Class<?> builderClass = getBuilderClassByAppendingBuilderToClassName(
+                    dataContainerBean.getImplementedInterface());
+            return super.getNewBeanExpression(dataContainerBean, builderClass);
+        } else {
+            return super.getNewBeanExpression(bean);
+        }
+    }
+
+    @Override
+    protected String stringify(Class<?> klass) {
+        return klass.getSimpleName();
+    }
+
+    @Override
+    protected Iterable<Property> filter(Iterable<Property> properties) {
+        // YANG keys duplicate existing other properties (there are getter/setter for both), so filter them
+        return Iterables.filter(properties, property -> !property.getName().equals("key"));
+    }
+
+    private Optional<ClassToInstanceMap<Augmentation<?>>> getAugmentations(Object bean) {
+        if (bean instanceof Augmentable<?>) {
+            Augmentable<?> augmentable = (Augmentable<?>) bean;
+            ClassToInstanceMap<Augmentation<?>> augmentables = augmentableExtension.getAugmentations(augmentable);
+            if (!augmentables.isEmpty()) {
+                return Optional.of(augmentables);
+            }
+        }
+        return Optional.empty();
+    }
+
+    @Override
+    protected CharSequence getAdditionalInitializationExpression(Object bean, Class<?> builderClass) {
+        Optional<ClassToInstanceMap<Augmentation<?>>> optional = getAugmentations(bean);
+        if (optional.isPresent()) {
+            StringBuilder sb = new StringBuilder();
+            optional.get().forEach((klass, augmentation) -> {
+                sb.append("addAugmentation(");
+                sb.append(stringify(klass));
+                sb.append(", ");
+                sb.append(getNewBeanExpression(augmentation));
+                sb.append(")");
+            });
+            return sb;
+        } else {
+            return "";
+        }
+    }
+
+/*
+    TODO activate this once YANG objects either have a setAugmentations(Map)
+      or implement a new TBD interface AugmentableBuilder with a method like:
+          <E extends Augmentation<T>> Builder? addAugmentation(Class<E> augmentationType, E augmentation);
+      which an extension method could jump on.
+
+    @Override
+    public Iterable<Property> getAdditionalSpecialProperties(Object bean, Class<?> builderClass) {
+        Optional<ClassToInstanceMap<Augmentation<?>>> optional = getAugmentations(bean);
+        if (optional.isPresent()) {
+            Property augmentableProperty = new Property("augmentations", true, Map.class, () -> optional.get(), null);
+            return Collections.singleton(augmentableProperty);
+        } else {
+            return Collections.emptyList();
+        }
+    }
+ */
+
+}
diff --git a/binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/AssertDataObjectsTest.java b/binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/AssertDataObjectsTest.java
new file mode 100644 (file)
index 0000000..e446193
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.testutils;
+
+import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
+
+import java.util.Map;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.api.ReadTransaction;
+import org.opendaylight.mdsal.binding.api.WriteTransaction;
+import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataBrokerTest;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Tests the {@link AssertDataObjects} utility.
+ *
+ * @author Michael Vorburger
+ */
+public class AssertDataObjectsTest extends AbstractDataBrokerTest {
+
+    private static final String HEADER = "import static extension org.opendaylight.mdsal.binding.testutils."
+            + "XtendBuilderExtensions.operator_doubleGreaterThan\n\n";
+
+    @Test
+    public void testAssertDataObjectsWithTopLevelListKey() {
+        AssertDataObjects.assertEqualByText("new TopLevelListKey(\"test\")\n", new TopLevelListKey("test"));
+    }
+
+    @Test
+    public void testAssertDataObjectsWithEmptyTop() {
+        AssertDataObjects.assertEqualByText(HEADER + "new TopBuilder\n", ExampleYangObjects.topEmpty().getValue());
+    }
+
+    @Test
+    public void testAssertDataObjectsWithComplexTopWithKey() {
+        AssertDataObjects.assertEqualByText(HEADER + "new TopBuilder >> [\n"
+                + "    topLevelList = #[\n"
+                + "        new TopLevelListBuilder >> [\n"
+                + "            name = \"foo\"\n"
+                + "            addAugmentation(TreeComplexUsesAugment, new TreeComplexUsesAugmentBuilder >> [\n"
+                + "                containerWithUses = new ContainerWithUsesBuilder >> [\n"
+                + "                    leafFromGrouping = \"foo\"\n"
+                + "                ]\n"
+                + "            ])\n"
+                + "        ]\n"
+                + "    ]\n"
+                + "]", ExpectedObjects.top());
+    }
+
+    @Test
+    public void testAssertDataObjectsWithTopLevelList() {
+        AssertDataObjects.assertEqualBeans(ExpectedObjects.topLevelList(),
+                ExampleYangObjects.topLevelList().getValue());
+        AssertDataObjects.assertEqualByText(HEADER + "new TopLevelListBuilder >> [\n"
+                + "    name = \"foo\"\n"
+                + "    addAugmentation(TreeComplexUsesAugment, new TreeComplexUsesAugmentBuilder >> [\n"
+                + "        containerWithUses = new ContainerWithUsesBuilder >> [\n"
+                + "            leafFromGrouping = \"foo\"\n"
+                + "        ]\n"
+                + "    ])\n"
+                + "]", ExampleYangObjects.topLevelList().getValue());
+    }
+
+    @Test
+    public void testAssertDataObjectsWithDataBroker() throws Exception {
+        WriteTransaction initialTx = getDataBroker().newWriteOnlyTransaction();
+        put(initialTx, OPERATIONAL, ExampleYangObjects.topEmpty());
+        put(initialTx, OPERATIONAL, ExampleYangObjects.topLevelList());
+        initialTx.submit().checkedGet();
+
+        ReadTransaction readTx = getDataBroker().newReadOnlyTransaction();
+        InstanceIdentifier<Top> id = InstanceIdentifier.create(Top.class);
+        Top actualTop = readTx.read(OPERATIONAL, id).checkedGet().get();
+
+        AssertDataObjects.assertEqualBeans(ExpectedObjects.top(), actualTop);
+
+        String expectedTopText = "import static extension org.opendaylight.mdsal.binding.testutils."
+                + "XtendBuilderExtensions.operator_doubleGreaterThan\n\n"
+                + "new TopBuilder >> [\n"
+                + "    topLevelList = #[\n"
+                + "        new TopLevelListBuilder >> [\n"
+                + "            name = \"foo\"\n"
+                + "            addAugmentation(TreeComplexUsesAugment, new TreeComplexUsesAugmentBuilder >> [\n"
+                + "                containerWithUses = new ContainerWithUsesBuilder >> [\n"
+                + "                    leafFromGrouping = \"foo\"\n"
+                + "                ]\n"
+                + "            ])\n"
+                + "        ]\n"
+                + "    ]\n"
+                + "]";
+        AssertDataObjects.assertEqualByText(expectedTopText, actualTop);
+    }
+
+    <T extends DataObject> void put(WriteTransaction tx, LogicalDatastoreType store,
+            Map.Entry<InstanceIdentifier<T>, T> obj) {
+        tx.put(OPERATIONAL, obj.getKey(), obj.getValue());
+    }
+
+}
diff --git a/binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/AssertNonDataObjectsTest.java b/binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/AssertNonDataObjectsTest.java
new file mode 100644 (file)
index 0000000..341d910
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.testutils;
+
+import ch.vorburger.xtendbeans.AssertBeans;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Tests that the {@link AssertDataObjects} utility also works for any Java
+ * object that is not a {@link DataObject}, like the {@link AssertBeans} which
+ * it's based on. There is absolutely no particular reason why it wouldn't,
+ * because {@link AssertDataObjects} is essentially just a customization of
+ * {@link AssertBeans} - this is just to make sure none of the base
+ * functionality gets broken in the customization.
+ *
+ * @author Michael Vorburger
+ */
+public class AssertNonDataObjectsTest {
+
+    public static class SomeBean {
+        private String name;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+
+    @Test
+    public void testString() {
+        AssertDataObjects.assertEqualBeans("hello, world", "hello, world");
+    }
+
+    @Test
+    public void testSomeBean() {
+        SomeBean first = new SomeBean();
+        first.setName("hello, world");
+
+        SomeBean second = new SomeBean();
+        second.setName("hello, world");
+
+        AssertDataObjects.assertEqualBeans(first, second);
+    }
+
+}
diff --git a/binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/AugmentableExtensionTest.java b/binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/AugmentableExtensionTest.java
new file mode 100644 (file)
index 0000000..e335ced
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.testutils;
+
+import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
+
+import ch.vorburger.xtendbeans.AssertBeans;
+import java.util.Map;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.api.ReadTransaction;
+import org.opendaylight.mdsal.binding.api.WriteTransaction;
+import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataBrokerTest;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Test for {@link AugmentableExtension}.
+ *
+ * @author Michael Vorburger
+ */
+public class AugmentableExtensionTest extends AbstractDataBrokerTest {
+
+    private final AugmentableExtension augmentableExtension = new AugmentableExtension();
+
+    @Test
+    public void testAugmentableExtensionOnYangObjectByBuilder() {
+        TopLevelList topLevelList = ExampleYangObjects.topLevelList().getValue();
+        Map<Class<? extends Augmentation<?>>, Augmentation<?>> augmentations = augmentableExtension
+                .getAugmentations(topLevelList);
+        AssertBeans.assertEqualByText("#{\n"
+                + "    org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test"
+                +        ".augment.rev140709.TreeComplexUsesAugment -> (new TreeComplexUsesAugmentBuilder => [\n"
+                + "        containerWithUses = (new ContainerWithUsesBuilder => [\n"
+                + "            leafFromGrouping = \"foo\"\n"
+                + "        ]).build()\n"
+                + "    ]).build()\n"
+                + "}", augmentations);
+    }
+
+    @Test
+    public void testAugmentableExtensionWithDataBroker() throws Exception {
+        WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+        put(writeTx, OPERATIONAL, ExampleYangObjects.topLevelList());
+        writeTx.submit().checkedGet();
+
+        ReadTransaction readTx = getDataBroker().newReadOnlyTransaction();
+        InstanceIdentifier<Top> id = InstanceIdentifier.create(Top.class);
+        Top actualTop = readTx.read(OPERATIONAL, id).checkedGet().get();
+        AssertBeans.assertEqualByText("#{\n}", augmentableExtension.getAugmentations(actualTop));
+
+        TopLevelList topLevelList = actualTop.getTopLevelList().get(0);
+        AssertDataObjects.assertEqualByText("#{\n"
+                + "    TreeComplexUsesAugment -> new TreeComplexUsesAugmentBuilder >> [\n"
+                + "        containerWithUses = new ContainerWithUsesBuilder >> [\n"
+                + "            leafFromGrouping = \"foo\"\n"
+                + "        ]\n"
+                + "    ]\n"
+                + "}", augmentableExtension.getAugmentations(topLevelList));
+    }
+
+    <T extends DataObject> void put(WriteTransaction tx, LogicalDatastoreType store,
+            Map.Entry<InstanceIdentifier<T>, T> obj) {
+        tx.put(OPERATIONAL, obj.getKey(), obj.getValue());
+    }
+
+}
diff --git a/binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/ExampleYangObjects.java b/binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/ExampleYangObjects.java
new file mode 100644 (file)
index 0000000..9e4402d
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.testutils;
+
+import java.util.AbstractMap;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.TopBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class ExampleYangObjects {
+
+    public static AbstractMap.SimpleImmutableEntry<InstanceIdentifier<Top>, Top> topEmpty() {
+        return new SimpleImmutableEntry<>(InstanceIdentifier.create(Top.class), new TopBuilder().build());
+    }
+
+    public static AbstractMap.SimpleImmutableEntry<InstanceIdentifier<TopLevelList>, TopLevelList> topLevelList() {
+        TreeComplexUsesAugment fooAugment = new TreeComplexUsesAugmentBuilder()
+                .setContainerWithUses(new ContainerWithUsesBuilder().setLeafFromGrouping("foo").build()).build();
+        return new SimpleImmutableEntry<>(
+                ListsBindingUtils.path(ListsBindingUtils.TOP_FOO_KEY),
+                    ListsBindingUtils.topLevelList(ListsBindingUtils.TOP_FOO_KEY, fooAugment));
+    }
+}
diff --git a/binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/ExpectedObjects.java b/binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/ExpectedObjects.java
new file mode 100644 (file)
index 0000000..88bec30
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.testutils;
+
+import java.util.Collections;
+import org.eclipse.xtext.xbase.lib.CollectionLiterals;
+import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.complex.from.grouping.ContainerWithUses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.TopBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
+
+/**
+ * The object initialization code in this class was generated by AssertDataObjects.
+ *
+ * @see AssertDataObjects
+ * @see AssertDataObjectsTest
+ */
+public class ExpectedObjects {
+
+    public static TopLevelList topLevelList() {
+        TopLevelListBuilder it = new TopLevelListBuilder();
+        TopLevelListKey topLevelListKey = new TopLevelListKey("foo");
+        it.setKey(topLevelListKey);
+        it.setName("foo");
+        TreeComplexUsesAugmentBuilder treeComplexUsesAugmentBuilder = new TreeComplexUsesAugmentBuilder();
+        ContainerWithUsesBuilder containerWithUsesBuilder = new ContainerWithUsesBuilder();
+        containerWithUsesBuilder.setLeafFromGrouping("foo");
+        ContainerWithUses containerWithUses = containerWithUsesBuilder.build();
+        treeComplexUsesAugmentBuilder.setContainerWithUses(containerWithUses);
+        TreeComplexUsesAugment treeComplexUsesAugment = treeComplexUsesAugmentBuilder.build();
+        it.addAugmentation(TreeComplexUsesAugment.class, treeComplexUsesAugment);
+        return it.build();
+    }
+
+    public static Top top() {
+        TopBuilder topBuilder = new TopBuilder();
+        final Procedure1<TopBuilder> _function = (TopBuilder it) -> {
+            TopLevelListBuilder topLevelListBuilder = new TopLevelListBuilder();
+            final Procedure1<TopLevelListBuilder> _function_1 = (TopLevelListBuilder it1) -> {
+                it1.setName("foo");
+                TreeComplexUsesAugmentBuilder treeComplexUsesAugmentBuilder = new TreeComplexUsesAugmentBuilder();
+                final Procedure1<TreeComplexUsesAugmentBuilder> _function_2 = (TreeComplexUsesAugmentBuilder it2) -> {
+                    ContainerWithUsesBuilder containerWithUsesBuilder = new ContainerWithUsesBuilder();
+                    final Procedure1<ContainerWithUsesBuilder> _function_3 = (ContainerWithUsesBuilder it3) -> {
+                        it3.setLeafFromGrouping("foo");
+                    };
+                    ContainerWithUses doubleGreaterThan = XtendBuilderExtensions
+                            .<ContainerWithUses, ContainerWithUsesBuilder>operator_doubleGreaterThan(
+                                    containerWithUsesBuilder, _function_3);
+                    it2.setContainerWithUses(doubleGreaterThan);
+                };
+                TreeComplexUsesAugment doubleGreaterThan = XtendBuilderExtensions
+                        .<TreeComplexUsesAugment, TreeComplexUsesAugmentBuilder>operator_doubleGreaterThan(
+                                treeComplexUsesAugmentBuilder, _function_2);
+                it1.addAugmentation(TreeComplexUsesAugment.class, doubleGreaterThan);
+            };
+            TopLevelList doubleGreaterThan = XtendBuilderExtensions
+                    .<TopLevelList, TopLevelListBuilder>operator_doubleGreaterThan(topLevelListBuilder, _function_1);
+            it.setTopLevelList(Collections
+                    .<TopLevelList>unmodifiableList(CollectionLiterals.<TopLevelList>newArrayList(doubleGreaterThan)));
+        };
+        return XtendBuilderExtensions.<Top, TopBuilder>operator_doubleGreaterThan(topBuilder, _function);
+    }
+}
index 1544cbcca8b6b40d8bafa1157e6377dbc841a300..7063bd9c322dfc29059b9a3b8871ce5fb68e328b 100644 (file)
@@ -37,6 +37,7 @@
 
         <module>mdsal-binding-api</module>
         <module>mdsal-binding-util</module>
+        <module>mdsal-binding-test-utils</module>
         <module>mdsal-binding-dom-adapter</module>
     </modules>
 
index b9138a363b8e1e13a9dccc1a244cc85738bd87c8..c8551855fbc47cf0655dc9328d4f042b2c615998 100644 (file)
                 <artifactId>mdsal-binding-dom-adapter</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.opendaylight.mdsal</groupId>
+                <artifactId>mdsal-binding-dom-adapter</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+                <scope>test</scope>
+            </dependency>
             <dependency>
                 <groupId>org.opendaylight.mdsal</groupId>
                 <artifactId>mdsal-binding-util</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.opendaylight.mdsal</groupId>
+                <artifactId>mdsal-binding-test-utils</artifactId>
+                <version>${project.version}</version>
+            </dependency>
 
             <dependency>
                 <groupId>org.opendaylight.mdsal.model</groupId>