You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sd...@apache.org on 2021/12/07 17:33:48 UTC
[ignite-3] branch main updated: IGNITE-16058 Create annotation to load test parameters from a static variable (#491)
This is an automated email from the ASF dual-hosted git repository.
sdanilov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new ecfe921 IGNITE-16058 Create annotation to load test parameters from a static variable (#491)
ecfe921 is described below
commit ecfe9210989c21f037ce29c2cb93642bea359482
Author: Alexander Polovtcev <al...@gmail.com>
AuthorDate: Tue Dec 7 20:31:54 2021 +0300
IGNITE-16058 Create annotation to load test parameters from a static variable (#491)
---
modules/core/pom.xml | 6 ++
.../testframework/JunitExtensionTestUtils.java | 69 ++++++++++++++
.../testframework/VariableArgumentsProvider.java | 86 +++++++++++++++++
.../internal/testframework/VariableSource.java | 43 +++++++++
.../internal/testframework/VariableSourceTest.java | 104 +++++++++++++++++++++
.../testframework/WorkDirectoryExtensionTest.java | 68 ++++----------
6 files changed, 324 insertions(+), 52 deletions(-)
diff --git a/modules/core/pom.xml b/modules/core/pom.xml
index cf3ccbc..b0182ec 100644
--- a/modules/core/pom.xml
+++ b/modules/core/pom.xml
@@ -64,6 +64,12 @@
</dependency>
<dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-params</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-testkit</artifactId>
<scope>test</scope>
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/testframework/JunitExtensionTestUtils.java b/modules/core/src/test/java/org/apache/ignite/internal/testframework/JunitExtensionTestUtils.java
new file mode 100644
index 0000000..d2f67ad
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/testframework/JunitExtensionTestUtils.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.internal.testframework;
+
+import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
+import static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;
+import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;
+import static org.junit.platform.testkit.engine.EventConditions.type;
+
+import org.assertj.core.api.Condition;
+import org.junit.platform.testkit.engine.EngineExecutionResults;
+import org.junit.platform.testkit.engine.EngineTestKit;
+import org.junit.platform.testkit.engine.EventType;
+
+/**
+ * Utilities for writing tests for JUnit extensions.
+ *
+ * @see <a href="https://junit.org/junit5/docs/current/user-guide/#testkit">JUnit Platform Test Kit</a>
+ */
+class JunitExtensionTestUtils {
+ /**
+ * Executes the given test class on the Jupiter test engine.
+ */
+ private static EngineExecutionResults execute(Class<?> testClass) {
+ return EngineTestKit.engine("junit-jupiter")
+ .selectors(selectClass(testClass))
+ .execute();
+ }
+
+ /**
+ * Executes the given test class and checks that it has run all its tests successfully.
+ */
+ static void assertExecutesSuccessfully(Class<?> testClass) {
+ execute(testClass)
+ .allEvents()
+ .assertThatEvents()
+ .filteredOn(type(EventType.FINISHED))
+ .isNotEmpty()
+ .are(finishedSuccessfully());
+ }
+
+ /**
+ * Executes the given test class and checks that it fails with matching error conditions.
+ */
+ @SafeVarargs
+ static void assertExecutesWithFailure(Class<?> testClass, Condition<Throwable>... conditions) {
+ execute(testClass)
+ .allEvents()
+ .assertThatEvents()
+ .filteredOn(finishedWithFailure())
+ .isNotEmpty()
+ .are(finishedWithFailure(conditions));
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/testframework/VariableArgumentsProvider.java b/modules/core/src/test/java/org/apache/ignite/internal/testframework/VariableArgumentsProvider.java
new file mode 100644
index 0000000..a5b5260
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/testframework/VariableArgumentsProvider.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.internal.testframework;
+
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+import static org.junit.platform.commons.util.CollectionUtils.toStream;
+import static org.junit.platform.commons.util.ReflectionUtils.isStatic;
+
+import java.lang.reflect.Field;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.support.AnnotationConsumer;
+import org.junit.platform.commons.util.ReflectionUtils;
+
+/**
+ * {@link ArgumentsProvider} implementation that extracts arguments from a static variable, which name is specified by the
+ * {@link VariableSource} annotation.
+ */
+class VariableArgumentsProvider implements ArgumentsProvider, AnnotationConsumer<VariableSource> {
+ private String variableName;
+
+ @Override
+ public void accept(VariableSource variableSource) {
+ variableName = variableSource.value();
+ }
+
+ @Override
+ public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception {
+ Class<?> testClass = context.getRequiredTestClass();
+
+ Field source = testClass.getDeclaredField(variableName);
+
+ if (!isStatic(source)) {
+ throw new IllegalArgumentException("Variable marked with @VariableSource must be static: " + variableName);
+ }
+
+ source.setAccessible(true);
+
+ Object sourceValue = source.get(null);
+
+ return toStream(sourceValue).map(VariableArgumentsProvider::toArguments);
+ }
+
+ /**
+ * Converts the given value to {@link Arguments}.
+ * Copy-pasted from {@code org.junit.jupiter.params.provider.MethodArgumentsProvider#toArguments}
+ */
+ private static Arguments toArguments(Object item) {
+ // Nothing to do except cast.
+ if (item instanceof Arguments) {
+ return (Arguments) item;
+ }
+
+ // Pass all multidimensional arrays "as is", in contrast to Object[].
+ // See https://github.com/junit-team/junit5/issues/1665
+ if (ReflectionUtils.isMultidimensionalArray(item)) {
+ return arguments(item);
+ }
+
+ // Special treatment for one-dimensional reference arrays.
+ // See https://github.com/junit-team/junit5/issues/1665
+ if (item instanceof Object[]) {
+ return arguments((Object[]) item);
+ }
+
+ // Pass everything else "as is".
+ return arguments(item);
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/testframework/VariableSource.java b/modules/core/src/test/java/org/apache/ignite/internal/testframework/VariableSource.java
new file mode 100644
index 0000000..9547c34
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/testframework/VariableSource.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.internal.testframework;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.jupiter.params.provider.ArgumentsSource;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * When placed on a test method, this annotation will be used to resolve its parameters using a static variable declared in the enclosing
+ * test class.
+ *
+ * <p>This annotation is conceptually similar to the {@link MethodSource} annotation and uses the same parameter resolution procedures.
+ */
+@Documented
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@ArgumentsSource(VariableArgumentsProvider.class)
+public @interface VariableSource {
+ /**
+ * The name of the static variable.
+ */
+ String value();
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/testframework/VariableSourceTest.java b/modules/core/src/test/java/org/apache/ignite/internal/testframework/VariableSourceTest.java
new file mode 100644
index 0000000..247c5e1
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/testframework/VariableSourceTest.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.internal.testframework;
+
+import static org.apache.ignite.internal.testframework.JunitExtensionTestUtils.assertExecutesSuccessfully;
+import static org.apache.ignite.internal.testframework.JunitExtensionTestUtils.assertExecutesWithFailure;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;
+import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;
+
+import java.util.List;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+
+class VariableSourceTest {
+ /**
+ * Test class for the {@link #testStaticFieldInjection()} test.
+ */
+ static class NormalVariableSourceTest {
+ private static final List<Object[]> arguments = List.of(
+ new Object[] {0, "foo"},
+ new Object[] {1, "bar"},
+ new Object[] {2, "baz"}
+ );
+
+ @ParameterizedTest
+ @VariableSource("arguments")
+ public void test(int i, String argument) {
+ assertEquals(arguments.get(i)[1], argument);
+ }
+ }
+
+ /**
+ * Tests the happy case of the {@link VariableSource} usage.
+ */
+ @Test
+ void testStaticFieldInjection() {
+ assertExecutesSuccessfully(NormalVariableSourceTest.class);
+ }
+
+ /**
+ * Test class for the {@link #testNonStaticFieldInjection()} test.
+ */
+ static class NonStaticVariableSourceTest {
+ private final String arguments = "foo";
+
+ @ParameterizedTest
+ @VariableSource("arguments")
+ public void test(String argument) {
+ fail("Should not reach here");
+ }
+ }
+
+ /**
+ * Tests the case when the {@code VariableSource} variable is not static.
+ */
+ @Test
+ void testNonStaticFieldInjection() {
+ assertExecutesWithFailure(
+ NonStaticVariableSourceTest.class,
+ instanceOf(IllegalArgumentException.class),
+ message(m -> m.contains("must be static"))
+ );
+ }
+
+ /**
+ * Test class for the {@link #testMissingVariableSource()} test.
+ */
+ static class MissingVariableSourceTest {
+ @ParameterizedTest
+ @VariableSource("missing")
+ public void test(String argument) {
+ fail("Should not reach here");
+ }
+ }
+
+ /**
+ * Tests the case when the {@code VariableSource} variable is not declared.
+ */
+ @Test
+ void testMissingVariableSource() {
+ assertExecutesWithFailure(
+ MissingVariableSourceTest.class,
+ instanceOf(NoSuchFieldException.class),
+ message(m -> m.contains("missing"))
+ );
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/testframework/WorkDirectoryExtensionTest.java b/modules/core/src/test/java/org/apache/ignite/internal/testframework/WorkDirectoryExtensionTest.java
index 003f5ff..52dea1c 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/testframework/WorkDirectoryExtensionTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/testframework/WorkDirectoryExtensionTest.java
@@ -17,16 +17,14 @@
package org.apache.ignite.internal.testframework;
+import static org.apache.ignite.internal.testframework.JunitExtensionTestUtils.assertExecutesSuccessfully;
+import static org.apache.ignite.internal.testframework.JunitExtensionTestUtils.assertExecutesWithFailure;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
-import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
-import static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;
-import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;
-import static org.junit.platform.testkit.engine.EventConditions.type;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;
@@ -43,19 +41,14 @@ import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ParameterResolutionException;
-import org.junit.platform.testkit.engine.EngineExecutionResults;
-import org.junit.platform.testkit.engine.EngineTestKit;
-import org.junit.platform.testkit.engine.EventType;
/**
* Tests for the {@link WorkDirectoryExtension}.
*
* <p>This class uses an approach when several nested classes are executed manually on the JUnit test engine, because some test methods
* should fail as part of these meta-tests. Nested classes are skipped by the surefire plugin and must not be executed during the build.
- *
- * @see <a href="https://junit.org/junit5/docs/current/user-guide/#testkit">JUnit Platform Test Kit</a>
*/
-public class WorkDirectoryExtensionTest {
+class WorkDirectoryExtensionTest {
/**
* Test class for the {@link #testStaticFieldInjection()} test.
*/
@@ -165,15 +158,11 @@ public class WorkDirectoryExtensionTest {
*/
@Test
void testDuplicateFieldAndParameterInjection() {
- execute(ErrorParameterResolutionTest.class)
- .testEvents()
- .assertThatEvents()
- .filteredOn(type(EventType.FINISHED))
- .isNotEmpty()
- .are(finishedWithFailure(
- instanceOf(ParameterResolutionException.class),
- message(m -> m.contains("there exists a field annotated with @WorkDirectory"))
- ));
+ assertExecutesWithFailure(
+ ErrorParameterResolutionTest.class,
+ instanceOf(ParameterResolutionException.class),
+ message(m -> m.contains("there exists a field annotated with @WorkDirectory"))
+ );
}
/**
@@ -198,15 +187,11 @@ public class WorkDirectoryExtensionTest {
*/
@Test
void testDuplicateFieldInjection() {
- execute(ErrorFieldInjectionTest.class)
- .allEvents()
- .assertThatEvents()
- .filteredOn(finishedWithFailure())
- .isNotEmpty()
- .are(finishedWithFailure(
- instanceOf(IllegalStateException.class),
- message(m -> m.contains("Test class must have a single field of type"))
- ));
+ assertExecutesWithFailure(
+ ErrorFieldInjectionTest.class,
+ instanceOf(IllegalStateException.class),
+ message(m -> m.contains("Test class must have a single field of type"))
+ );
}
/**
@@ -259,12 +244,12 @@ public class WorkDirectoryExtensionTest {
@Disabled
@Test
- void test() {}
+ void test() {
+ }
}
/**
- * Tests {@code WorkDirectoryExtension} lifecycle works correctly on a test class with all test methods being
- * disabled.
+ * Tests {@code WorkDirectoryExtension} lifecycle works correctly on a test class with all test methods being disabled.
*
* @see <a href="https://issues.apache.org/jira/browse/IGNITE-15799">IGNITE-15799</a>
*/
@@ -272,25 +257,4 @@ public class WorkDirectoryExtensionTest {
void testEmptyClass() {
assertExecutesSuccessfully(TestEmptyClass.class);
}
-
- /**
- * Executes the given test class on the test engine.
- */
- private static EngineExecutionResults execute(Class<?> testClass) {
- return EngineTestKit.engine("junit-jupiter")
- .selectors(selectClass(testClass))
- .execute();
- }
-
- /**
- * Executes the given test class and checks that it has run all its tests successfully.
- */
- private static void assertExecutesSuccessfully(Class<?> testClass) {
- execute(testClass)
- .allEvents()
- .assertThatEvents()
- .filteredOn(type(EventType.FINISHED))
- .isNotEmpty()
- .are(finishedSuccessfully());
- }
}