summaryrefslogtreecommitdiffstats
path: root/infra/test-utils/test-tools
diff options
context:
space:
mode:
authorJan Srnicek <jsrnicek@cisco.com>2016-10-26 10:14:11 +0200
committerMaros Marsalek <mmarsale@cisco.com>2016-10-26 08:48:16 +0000
commitf4600723aa5e0fa99905eacd3338b33728a6c68f (patch)
treeb783639f92d2d3178b5d2503ec46591dcc0e5b17 /infra/test-utils/test-tools
parent921d1729eb2736c4f9b8b14205436934bcf81813 (diff)
HONEYCOMB-266 - Test data injection with @InjectTestData
Field/Method param injection of data from json files that are bindable/parsable by provided yang schema Change-Id: I726ef5d92e85d93d1e48175287b6192538965dd5 Signed-off-by: Maros Marsalek <mmarsale@cisco.com> Signed-off-by: Jan Srnicek <jsrnicek@cisco.com>
Diffstat (limited to 'infra/test-utils/test-tools')
-rw-r--r--infra/test-utils/test-tools/asciidoc/Readme.adoc3
-rw-r--r--infra/test-utils/test-tools/pom.xml87
-rw-r--r--infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/HoneycombTestRunner.java128
-rw-r--r--infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/InjectableTestMethodInvoker.java48
-rw-r--r--infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangContextProducer.java80
-rw-r--r--infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectTestData.java33
-rw-r--r--infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectablesProcessor.java101
-rw-r--r--infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/SchemaContextProvider.java27
-rw-r--r--infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/ChildNodeDataFactory.java77
-rw-r--r--infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/RootNodeDataFactory.java42
-rw-r--r--infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/YangDataFactory.java109
-rw-r--r--infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/HoneycombTestRunnerTest.java129
-rw-r--r--infra/test-utils/test-tools/src/test/resources/containerInList.json5
-rw-r--r--infra/test-utils/test-tools/src/test/resources/leafInAugment.json7
-rw-r--r--infra/test-utils/test-tools/src/test/resources/nestedContainer.json5
-rw-r--r--infra/test-utils/test-tools/src/test/resources/simpleContainerEmpty.json4
16 files changed, 885 insertions, 0 deletions
diff --git a/infra/test-utils/test-tools/asciidoc/Readme.adoc b/infra/test-utils/test-tools/asciidoc/Readme.adoc
new file mode 100644
index 000000000..eaa7e6bc4
--- /dev/null
+++ b/infra/test-utils/test-tools/asciidoc/Readme.adoc
@@ -0,0 +1,3 @@
+= test-tools
+
+Overview of test-tools \ No newline at end of file
diff --git a/infra/test-utils/test-tools/pom.xml b/infra/test-utils/test-tools/pom.xml
new file mode 100644
index 000000000..90a155390
--- /dev/null
+++ b/infra/test-utils/test-tools/pom.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (c) 2016 Cisco and/or its affiliates.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at:
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<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">
+ <parent>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <artifactId>impl-parent</artifactId>
+ <version>1.16.12-SNAPSHOT</version>
+ <relativePath>../../../common/impl-parent</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>io.fd.honeycomb.infra</groupId>
+ <artifactId>test-tools</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>io.fd.honeycomb.infra.test</groupId>
+ <artifactId>test-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>translate-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-binding-dom-codec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-codec-gson</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.skinny-framework</groupId>
+ <artifactId>skinny-logback</artifactId>
+ <version>${skinny.logback.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+</project> \ No newline at end of file
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/HoneycombTestRunner.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/HoneycombTestRunner.java
new file mode 100644
index 000000000..906a5fc87
--- /dev/null
+++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/HoneycombTestRunner.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools;
+
+import io.fd.honeycomb.test.tools.annotations.InjectablesProcessor;
+import io.fd.honeycomb.test.tools.factories.ChildNodeDataFactory;
+import io.fd.honeycomb.test.tools.factories.RootNodeDataFactory;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Parameter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HoneycombTestRunner extends BlockJUnit4ClassRunner implements YangContextProducer, InjectablesProcessor {
+
+ private static final Logger LOG = LoggerFactory.getLogger(HoneycombTestRunner.class);
+
+ private SchemaContext schemaContext;
+ private BindingToNormalizedNodeCodec serializer;
+ private AbstractModuleStringInstanceIdentifierCodec iidParser;
+
+ private ChildNodeDataFactory childNodeDataFactory;
+ private RootNodeDataFactory rootNodeDataFactory;
+
+ public HoneycombTestRunner(final Class<?> klass) throws InitializationError {
+ super(klass);
+ }
+
+ @Override
+ protected Object createTest() throws Exception {
+ final Object test = super.createTest();
+ LOG.debug("Initializing test {}", test);
+
+ // Get schema context from annotated method
+ final ModuleInfoBackedContext ctx = getCheckedModuleInfoContext(test);
+ schemaContext = ctx.getSchemaContext();
+ // Create serializer from it in order to later transform NormalizedNodes into BA
+ serializer = createSerializer(ctx, schemaContext);
+ // Create InstanceIdentifier Codec in order to later transform string represented IID into YangInstanceIdentifier
+ iidParser = getIIDCodec(ctx);
+
+ childNodeDataFactory = new ChildNodeDataFactory(schemaContext, serializer, iidParser);
+ rootNodeDataFactory = new RootNodeDataFactory(schemaContext, serializer, iidParser);
+
+ injectFields(test);
+ return test;
+ }
+
+ @Override
+ protected void validatePublicVoidNoArgMethods(final Class<? extends Annotation> annotation, final boolean isStatic,
+ final List<Throwable> errors) {
+ // ignores this check,in case its super needed, can be implemented, but without checking no args(because of custom invoker)
+ }
+
+ /**
+ * Allows method parameters injection
+ * */
+ @Override
+ protected Statement methodInvoker(final FrameworkMethod method, final Object test) {
+ return new InjectableTestMethodInvoker(method, test, Arrays.stream(method.getMethod().getParameters())
+ .map(this::injectValueOrNull)
+ .collect(Collectors.toList())
+ .toArray());
+ }
+
+ private Object injectValueOrNull(final Parameter parameter) {
+ return isInjectable(parameter)
+ ? getData(resourcePath(parameter), instanceIdentifier(parameter).orNull(), parameter.getType())
+ : null;
+ }
+
+ /**
+ * Inject fields with dat from @InjectTestData.resourcePath
+ */
+ private void injectFields(final Object testInstance) {
+
+ // iterate over all injectable fields
+ injectableFields(testInstance.getClass()).forEach(field -> {
+ LOG.debug("Processing field {}", field);
+ injectField(field, testInstance,
+ getData(resourcePath(field), instanceIdentifier(field).orNull(), field.getType()));
+ });
+ }
+
+ private DataObject getData(final String resourcePath, @Nullable final String identifier,
+ final Class<?> injectedType) {
+ try {
+ if (StringUtils.isNotEmpty(identifier)) {
+ LOG.debug("Processing {} as child node {}", injectedType, identifier);
+ return childNodeDataFactory.getChildNodeData(identifier, resourcePath);
+ } else {
+ LOG.debug("Processing {} as root node", injectedType);
+ return rootNodeDataFactory.getRootNodeData(getRootInstanceIdentifier(injectedType), resourcePath);
+ }
+ } catch (DeserializationException | IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/InjectableTestMethodInvoker.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/InjectableTestMethodInvoker.java
new file mode 100644
index 000000000..b63371328
--- /dev/null
+++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/InjectableTestMethodInvoker.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools;
+
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Invoker that allows injecting of method parameters
+ */
+class InjectableTestMethodInvoker extends Statement {
+
+ private static final Logger LOG = LoggerFactory.getLogger(InjectableTestMethodInvoker.class);
+
+ private final FrameworkMethod testMethod;
+ private final Object target;
+ private final Object[] invocationParams;
+
+
+ InjectableTestMethodInvoker(final FrameworkMethod testMethod, final Object target,
+ final Object[] invocationParams) {
+ this.testMethod = testMethod;
+ this.target = target;
+ this.invocationParams = invocationParams;
+ }
+
+ @Override
+ public void evaluate() throws Throwable {
+ LOG.debug("Invoking @Test[{}] on target[{}] with params {}", testMethod.getName(), target, invocationParams);
+ testMethod.invokeExplosively(target, invocationParams);
+ }
+}
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangContextProducer.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangContextProducer.java
new file mode 100644
index 000000000..23af9733d
--- /dev/null
+++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/YangContextProducer.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import io.fd.honeycomb.test.tools.annotations.SchemaContextProvider;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import javassist.ClassPool;
+import org.apache.commons.lang3.reflect.MethodUtils;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory;
+import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Common logic to initialize serializers/deserializers/etc while working with yang based data
+ */
+interface YangContextProducer {
+
+ default ModuleInfoBackedContext getCheckedModuleInfoContext(final Object test)
+ throws IllegalAccessException, InvocationTargetException {
+ final Method[] suitableMethods =
+ MethodUtils.getMethodsWithAnnotation(test.getClass(), SchemaContextProvider.class);
+ checkState(suitableMethods.length == 1, "Only single method should be @SchemaContextProvider, actual : %s",
+ suitableMethods);
+ final Object possibleContext = suitableMethods[0].invoke(test);
+ checkState(possibleContext instanceof ModuleInfoBackedContext, "%s is not ModuleInfoBackedContext",
+ possibleContext);
+ return ModuleInfoBackedContext.class.cast(possibleContext);
+ }
+
+ /**
+ * Get a codec for instance identifiers.
+ */
+ default AbstractModuleStringInstanceIdentifierCodec getIIDCodec(final ModuleInfoBackedContext ctx)
+ throws NoSuchMethodException, ClassNotFoundException, InstantiationException, IllegalAccessException,
+ java.lang.reflect.InvocationTargetException {
+ // Reusing codec for JSON ... not public so here goes reflection
+
+ final JSONCodecFactory jsonCodecFactory = JSONCodecFactory.create(ctx.getSchemaContext());
+ final Constructor<?> cstr =
+ Class.forName("org.opendaylight.yangtools.yang.data.codec.gson.JSONStringInstanceIdentifierCodec")
+ .getDeclaredConstructor(SchemaContext.class, JSONCodecFactory.class);
+ cstr.setAccessible(true);
+ return (AbstractModuleStringInstanceIdentifierCodec) cstr.newInstance(ctx.getSchemaContext(), jsonCodecFactory);
+ }
+
+ default BindingToNormalizedNodeCodec createSerializer(final ModuleInfoBackedContext moduleInfoBackedContext,
+ final SchemaContext schemaContexts) {
+
+ final BindingNormalizedNodeCodecRegistry codecRegistry =
+ new BindingNormalizedNodeCodecRegistry(
+ StreamWriterGenerator.create(JavassistUtils.forClassPool(ClassPool.getDefault())));
+ codecRegistry
+ .onBindingRuntimeContextUpdated(BindingRuntimeContext.create(moduleInfoBackedContext, schemaContexts));
+ return new BindingToNormalizedNodeCodec(moduleInfoBackedContext, codecRegistry);
+ }
+}
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectTestData.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectTestData.java
new file mode 100644
index 000000000..e30d82ce0
--- /dev/null
+++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectTestData.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.PARAMETER})
+public @interface InjectTestData {
+
+ String NO_ID = "NO_ID";
+
+ String resourcePath();
+
+ String id() default NO_ID;
+}
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectablesProcessor.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectablesProcessor.java
new file mode 100644
index 000000000..c364938ed
--- /dev/null
+++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/InjectablesProcessor.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools.annotations;
+
+import static io.fd.honeycomb.test.tools.annotations.InjectTestData.NO_ID;
+
+import com.google.common.base.Optional;
+import java.lang.reflect.Field;
+import java.lang.reflect.Parameter;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.$YangModuleInfoImpl;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Common logic for @InjectTestData
+ */
+public interface InjectablesProcessor {
+
+ default List<Field> injectableFields(final Class<?> testClass) {
+ return FieldUtils.getFieldsListWithAnnotation(testClass, InjectTestData.class);
+ }
+
+ default boolean isInjectable(final Parameter parameter) {
+ return parameter.isAnnotationPresent(InjectTestData.class);
+ }
+
+ default String resourcePath(final Field field) {
+ return field.getAnnotation(InjectTestData.class).resourcePath();
+ }
+
+ default String resourcePath(final Parameter parameter) {
+ return parameter.getAnnotation(InjectTestData.class).resourcePath();
+ }
+
+ default Optional<String> instanceIdentifier(final Field field) {
+ final String identifier = field.getAnnotation(InjectTestData.class).id();
+ // == used instead of equals to ensure constant was used
+ if (NO_ID.equals(identifier)) {
+ return Optional.absent();
+ } else {
+ return Optional.of(identifier);
+ }
+ }
+
+ default Optional<String> instanceIdentifier(final Parameter parameter) {
+ final String identifier = parameter.getAnnotation(InjectTestData.class).id();
+ // == used instead of equals to ensure constant was used
+ if (NO_ID.equals(identifier)) {
+ return Optional.absent();
+ } else {
+ return Optional.of(identifier);
+ }
+ }
+
+ default void injectField(final Field field, final Object testInstance, final DataObject data) {
+ field.setAccessible(true);
+ try {
+ FieldUtils.writeField(field, testInstance, data);
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException("Unable to access field " + field);
+ }
+ }
+
+ default YangInstanceIdentifier getRootInstanceIdentifier(final Class type) {
+ try {
+ return YangInstanceIdentifier.of(QName.class.cast(type.getField("QNAME").get(null)));
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException("Constant QNAME not accessible for type" + type);
+ } catch (NoSuchFieldException e) {
+ throw new IllegalStateException("Class " + type + " does not have QName defined");
+ }
+ }
+
+ default ModuleInfoBackedContext provideSchemaContextFor(final Set<? extends YangModuleInfo> modules) {
+ final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
+ moduleInfoBackedContext.addModuleInfos(modules);
+ return moduleInfoBackedContext;
+ }
+}
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/SchemaContextProvider.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/SchemaContextProvider.java
new file mode 100644
index 000000000..5639dd837
--- /dev/null
+++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/annotations/SchemaContextProvider.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface SchemaContextProvider {
+}
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/ChildNodeDataFactory.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/ChildNodeDataFactory.java
new file mode 100644
index 000000000..05812123e
--- /dev/null
+++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/ChildNodeDataFactory.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools.factories;
+
+
+import com.google.common.base.Optional;
+import java.io.IOException;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.yangtools.sal.binding.generator.impl.BindingSchemaContextUtils;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ChildNodeDataFactory extends YangDataFactory {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ChildNodeDataFactory.class);
+
+ public ChildNodeDataFactory(@Nonnull final SchemaContext schemaContext,
+ @Nonnull final BindingToNormalizedNodeCodec serializer,
+ @Nonnull final AbstractModuleStringInstanceIdentifierCodec iidParser) {
+ super(schemaContext, serializer, iidParser);
+ }
+
+ public DataObject getChildNodeData(final String instanceIdentifier,
+ final String resourcePath) throws DeserializationException, IOException {
+ // Parse string ID into YangId
+ final YangInstanceIdentifier nodeYid = iidParser.deserialize(instanceIdentifier);
+ // Look for parent YangId
+ final YangInstanceIdentifier parentYid = nodeYid.getParent();
+
+ if (parentYid.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Attempt to process root node as children has been detected,to process root nodes just don't use id in @InjectTestData");
+ }
+ // Find Schema node for parent of data that's currently being parsed (needed when parsing the data into NormalizedNodes)
+ return getDataForNode(nodeYid, resourcePath, getNonRootParentSchema(parentYid));
+ }
+
+ private DataNodeContainer getNonRootParentSchema(final YangInstanceIdentifier parentYangId)
+ throws DeserializationException {
+ LOG.debug("Processing parent identifier {}", parentYangId);
+ final Optional<InstanceIdentifier<? extends DataObject>> parentInstanceId = serializer.toBinding(parentYangId);
+ if (!parentInstanceId.isPresent()) {
+ throw new IllegalStateException("Unable to resolve " + parentYangId + " to instance identifier");
+ }
+
+ final Optional<DataNodeContainer> dataNodeContainerOptional =
+ BindingSchemaContextUtils.findDataNodeContainer(schemaContext, parentInstanceId.get());
+
+ if (!dataNodeContainerOptional.isPresent()) {
+ throw new IllegalArgumentException("Error finding DataNodeContainer for " + parentInstanceId.get());
+ }
+
+ return dataNodeContainerOptional.get();
+ }
+}
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/RootNodeDataFactory.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/RootNodeDataFactory.java
new file mode 100644
index 000000000..3aacea016
--- /dev/null
+++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/RootNodeDataFactory.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools.factories;
+
+
+import java.io.IOException;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class RootNodeDataFactory extends YangDataFactory {
+
+ public RootNodeDataFactory(@Nonnull final SchemaContext schemaContext,
+ @Nonnull final BindingToNormalizedNodeCodec serializer,
+ @Nonnull final AbstractModuleStringInstanceIdentifierCodec iidParser) {
+ super(schemaContext, serializer, iidParser);
+ }
+
+ public DataObject getRootNodeData(final YangInstanceIdentifier rootInstanceIdentifier,
+ final String resourcePath) throws DeserializationException, IOException {
+ //entire schema context is parent schema in this case
+ return getDataForNode(rootInstanceIdentifier, resourcePath, schemaContext);
+ }
+}
diff --git a/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/YangDataFactory.java b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/YangDataFactory.java
new file mode 100644
index 000000000..ecf556cd2
--- /dev/null
+++ b/infra/test-utils/test-tools/src/main/java/io/fd/honeycomb/test/tools/factories/YangDataFactory.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools.factories;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
+import io.fd.honeycomb.translate.util.JsonUtils;
+import java.io.IOException;
+import java.util.AbstractMap;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+/**
+ * Common logic for reading of yang data
+ */
+abstract class YangDataFactory {
+
+ final SchemaContext schemaContext;
+ final BindingToNormalizedNodeCodec serializer;
+ final AbstractModuleStringInstanceIdentifierCodec iidParser;
+
+ YangDataFactory(@Nonnull final SchemaContext schemaContext,
+ @Nonnull final BindingToNormalizedNodeCodec serializer,
+ @Nonnull final AbstractModuleStringInstanceIdentifierCodec iidParser) {
+ this.schemaContext = checkNotNull(schemaContext, "SchemaContext cannot be null");
+ this.serializer = checkNotNull(serializer, "Serializer cannot be null");
+ this.iidParser = checkNotNull(iidParser, "Instance identifier parser cannot be null");
+ }
+
+ DataObject getDataForNode(final YangInstanceIdentifier nodeYangIdentifier,
+ final String resourcePath,
+ final DataNodeContainer parentSchema)
+ throws DeserializationException, IOException {
+
+ // Reads resources from provided resource path
+ final ContainerNode rootData = getCheckedRootData(resourcePath, parentSchema);
+
+ // Now transform the single child from JSON into BA format
+ final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> actualData =
+ extractCheckedSingleChild(rootData);
+
+ return getCheckedBinding(nodeYangIdentifier, actualData).getValue();
+ }
+
+ private ContainerNode getCheckedRootData(final String resourcePath, final DataNodeContainer parentSchema)
+ throws IOException {
+ // TODO the cast to SchemaNode is dangerous and would not work for Augments, Choices and some other nodes maybe. At least check
+ // TODO not sure if this is true, while testing this code was working fine event while processing choices/cases,
+ // TODO only problem is to find suitable codec that can process cases,etc
+ // Transform JSON into NormalizedNode
+
+ final ContainerNode rootData = JsonUtils.readJson(schemaContext,
+ checkNotNull(this.getClass().getResource(resourcePath), "Unable to find resource %s", resourcePath)
+ .openStream(), ((SchemaNode) parentSchema));
+
+ checkArgument(rootData.getValue().size() == 1, "Only a single data node is expected in %s, but there were: %s",
+ resourcePath, rootData.getValue());
+ return rootData;
+ }
+
+ private DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> extractCheckedSingleChild(
+ final ContainerNode rootData) {
+ // Now transform the single child from JSON into BA format
+ final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> actualData =
+ Iterables.getFirst(rootData.getValue(), null);
+
+ checkNotNull(actualData, "Unable to extract single child from %s", rootData);
+ return actualData;
+ }
+
+ private Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject> getCheckedBinding(
+ final YangInstanceIdentifier nodeYangIdentifier,
+ final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> actualData)
+ throws DeserializationException {
+ final Optional<Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject>> ba =
+ serializer.toBinding(new AbstractMap.SimpleImmutableEntry<>(nodeYangIdentifier, actualData));
+
+ checkArgument(ba.isPresent(), "Unable to convert to binding %s", nodeYangIdentifier);
+ return ba.get();
+ }
+}
diff --git a/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/HoneycombTestRunnerTest.java b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/HoneycombTestRunnerTest.java
new file mode 100644
index 000000000..42de7ed4d
--- /dev/null
+++ b/infra/test-utils/test-tools/src/test/java/io/fd/honeycomb/test/tools/HoneycombTestRunnerTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.test.tools;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import io.fd.honeycomb.test.tools.annotations.InjectTestData;
+import io.fd.honeycomb.test.tools.annotations.InjectablesProcessor;
+import io.fd.honeycomb.test.tools.annotations.SchemaContextProvider;
+import java.util.Collections;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.$YangModuleInfoImpl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.AugContainerAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.SimpleContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.SimpleContainerBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.NestedContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.simple.list.ContUnderList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.data.rev150105.simple.container.simple.list.ContUnderListBuilder;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+
+@RunWith(HoneycombTestRunner.class)
+public class HoneycombTestRunnerTest implements InjectablesProcessor {
+
+ @InjectTestData(resourcePath = "/simpleContainerEmpty.json")
+ private SimpleContainer simpleContainer;
+
+ @InjectTestData(resourcePath = "/nestedContainer.json", id = "/hc-data:simple-container/hc-data:nested-container")
+ private NestedContainer nestedContainer;
+
+ @InjectTestData(resourcePath = "/leafInAugment.json")
+ private SimpleContainer containerWithLeafInAugment;
+
+ @InjectTestData(resourcePath = "/containerInList.json", id = "/hc-data:simple-container" +
+ "/hc-data:simple-list[hc-data:name='nameUnderSimpleList']" +
+ "/hc-data:cont-under-list")
+ private ContUnderList containerUnderList;
+
+ @SchemaContextProvider
+ public ModuleInfoBackedContext getSchemaContext() {
+ return provideSchemaContextFor(Collections.singleton($YangModuleInfoImpl.getInstance()));
+ }
+
+ @Test
+ public void testSimpleContainer() {
+ assertNotNull(simpleContainer);
+ }
+
+ @Test
+ public void testNestedContainer() {
+ assertNotNull(nestedContainer);
+ assertEquals("abcd", nestedContainer.getName());
+ }
+
+ @Test
+ public void testLeafInAugmentedContainer() {
+ assertNotNull(containerWithLeafInAugment);
+ assertNotNull(containerWithLeafInAugment.getAugmentedContainer());
+ assertEquals("nameInAugment", containerWithLeafInAugment.getAugmentedContainer().getAugmentation(
+ AugContainerAugmentation.class).getNameInAugment());
+ }
+
+ @Test
+ public void testContainerUnderList() {
+ assertNotNull(containerUnderList);
+ assertEquals("nestedName", containerUnderList.getNestedName());
+ }
+
+ @Test
+ public void testParameterInjectionRootNode(
+ @InjectTestData(resourcePath = "/simpleContainerEmpty.json") SimpleContainer container) {
+ assertNotNull(container);
+ }
+
+ @Test
+ public void testParameterInjectionChildNode(
+ @InjectTestData(resourcePath = "/nestedContainer.json",
+ id = "/hc-data:simple-container/hc-data:nested-container") NestedContainer container) {
+ assertNotNull(container);
+ }
+
+ @Test
+ public void testParameterInjectionMultiple(
+ @InjectTestData(resourcePath = "/simpleContainerEmpty.json") SimpleContainer containerFirst,
+ @InjectTestData(resourcePath = "/nestedContainer.json",
+ id = "/hc-data:simple-container/hc-data:nested-container") NestedContainer containerSecond) {
+ assertNotNull(containerFirst);
+ assertNotNull(containerSecond);
+ }
+
+ @Test
+ public void testParameterInjectionOneNonInjectable(
+ @InjectTestData(resourcePath = "/simpleContainerEmpty.json") SimpleContainer containerFirst,
+ String thisOneShouldBeNull) {
+ assertNotNull(containerFirst);
+ assertNull(thisOneShouldBeNull);
+ }
+
+ @Test
+ public void testConsistenceSimpleNode(
+ @InjectTestData(resourcePath = "/simpleContainerEmpty.json") SimpleContainer container) {
+
+ assertEquals(new SimpleContainerBuilder().build(), simpleContainer);
+ }
+
+ @Test
+ public void testConsistenceWithLeafNode(
+ @InjectTestData(resourcePath = "/containerInList.json", id = "/hc-data:simple-container" +
+ "/hc-data:simple-list[hc-data:name='nameUnderSimpleList']" +
+ "/hc-data:cont-under-list") ContUnderList containerUnderList) {
+ assertEquals(new ContUnderListBuilder().setNestedName("nestedName").build(), containerUnderList);
+ }
+} \ No newline at end of file
diff --git a/infra/test-utils/test-tools/src/test/resources/containerInList.json b/infra/test-utils/test-tools/src/test/resources/containerInList.json
new file mode 100644
index 000000000..b55675e7c
--- /dev/null
+++ b/infra/test-utils/test-tools/src/test/resources/containerInList.json
@@ -0,0 +1,5 @@
+{
+ "cont-under-list":{
+ "nested-name":"nestedName"
+ }
+} \ No newline at end of file
diff --git a/infra/test-utils/test-tools/src/test/resources/leafInAugment.json b/infra/test-utils/test-tools/src/test/resources/leafInAugment.json
new file mode 100644
index 000000000..d3c52f3b8
--- /dev/null
+++ b/infra/test-utils/test-tools/src/test/resources/leafInAugment.json
@@ -0,0 +1,7 @@
+{
+ "simple-container": {
+ "augmented-container":{
+ "name-in-augment": "nameInAugment"
+ }
+ }
+} \ No newline at end of file
diff --git a/infra/test-utils/test-tools/src/test/resources/nestedContainer.json b/infra/test-utils/test-tools/src/test/resources/nestedContainer.json
new file mode 100644
index 000000000..9cc986b87
--- /dev/null
+++ b/infra/test-utils/test-tools/src/test/resources/nestedContainer.json
@@ -0,0 +1,5 @@
+{
+ "nested-container":{
+ "name":"abcd"
+ }
+} \ No newline at end of file
diff --git a/infra/test-utils/test-tools/src/test/resources/simpleContainerEmpty.json b/infra/test-utils/test-tools/src/test/resources/simpleContainerEmpty.json
new file mode 100644
index 000000000..4d56712c3
--- /dev/null
+++ b/infra/test-utils/test-tools/src/test/resources/simpleContainerEmpty.json
@@ -0,0 +1,4 @@
+{
+ "simple-container": {
+ }
+} \ No newline at end of file