From b51cebd6ba9ca7f524418ed3b2d9b2540308b8d7 Mon Sep 17 00:00:00 2001 From: Jonathan Scott Date: Thu, 5 Aug 2021 16:56:21 +0100 Subject: Import TestParameterInjector. Test: atest TestParameterInjectorTest Change-Id: I4eee53057041be2223ae133c2d2cf1d14fa752d0 --- .../ParameterValueParsingTest.java | 136 +++ .../PluggableTestRunnerTest.java | 74 ++ .../junit/testparameterinjector/TestInfoTest.java | 249 +++++ ...TestParameterAnnotationMethodProcessorTest.java | 1077 ++++++++++++++++++++ .../testparameterinjector/TestParameterTest.java | 211 ++++ .../TestParametersMethodProcessorTest.java | 474 +++++++++ 6 files changed, 2221 insertions(+) create mode 100644 src/test/java/com/google/testing/junit/testparameterinjector/ParameterValueParsingTest.java create mode 100644 src/test/java/com/google/testing/junit/testparameterinjector/PluggableTestRunnerTest.java create mode 100644 src/test/java/com/google/testing/junit/testparameterinjector/TestInfoTest.java create mode 100644 src/test/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessorTest.java create mode 100644 src/test/java/com/google/testing/junit/testparameterinjector/TestParameterTest.java create mode 100644 src/test/java/com/google/testing/junit/testparameterinjector/TestParametersMethodProcessorTest.java (limited to 'src/test') diff --git a/src/test/java/com/google/testing/junit/testparameterinjector/ParameterValueParsingTest.java b/src/test/java/com/google/testing/junit/testparameterinjector/ParameterValueParsingTest.java new file mode 100644 index 0000000..0f19466 --- /dev/null +++ b/src/test/java/com/google/testing/junit/testparameterinjector/ParameterValueParsingTest.java @@ -0,0 +1,136 @@ +/* + * Copyright 2021 Google Inc. + * + * 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 com.google.testing.junit.testparameterinjector; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.protobuf.ByteString; + +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(TestParameterInjector.class) +public class ParameterValueParsingTest { + + @Test + public void parseEnum_success() throws Exception { + Enum result = ParameterValueParsing.parseEnum("BBB", TestEnum.class); + + assertThat(result).isEqualTo(TestEnum.BBB); + } + + @Test + @TestParameters({ + "{yamlString: '{a: b, c: 15}', valid: true}", + "{yamlString: '{a: b c: 15', valid: false}", + "{yamlString: 'a: b c: 15', valid: false}", + }) + @Ignore("b/195657808 @TestParameters is not supported on Android") + public void isValidYamlString_success(String yamlString, boolean valid) throws Exception { + boolean result = ParameterValueParsing.isValidYamlString(yamlString); + + assertThat(result).isEqualTo(valid); + } + + enum ParseYamlValueToJavaTypeCases { + STRING_TO_STRING( + /* yamlString= */ "abc", /* javaClass= */ String.class, /* expectedResult= */ "abc"), + BOOLEAN_TO_STRING( + /* yamlString= */ "true", /* javaClass= */ String.class, /* expectedResult= */ "true"), + INT_TO_STRING( + /* yamlString= */ "123", /* javaClass= */ String.class, /* expectedResult= */ "123"), + LONG_TO_STRING( + /* yamlString= */ "442147483648", + /* javaClass= */ String.class, + /* expectedResult= */ "442147483648"), + DOUBLE_TO_STRING( + /* yamlString= */ "1.23", /* javaClass= */ String.class, /* expectedResult= */ "1.23"), + + BOOLEAN_TO_BOOLEAN( + /* yamlString= */ "true", /* javaClass= */ Boolean.class, /* expectedResult= */ true), + + INT_TO_INT(/* yamlString= */ "123", /* javaClass= */ int.class, /* expectedResult= */ 123), + + LONG_TO_LONG( + /* yamlString= */ "442147483648", + /* javaClass= */ long.class, + /* expectedResult= */ 442147483648L), + INT_TO_LONG(/* yamlString= */ "123", /* javaClass= */ Long.class, /* expectedResult= */ 123L), + + DOUBLE_TO_DOUBLE( + /* yamlString= */ "1.23", /* javaClass= */ Double.class, /* expectedResult= */ 1.23), + INT_TO_DOUBLE( + /* yamlString= */ "123", /* javaClass= */ Double.class, /* expectedResult= */ 123.0), + LONG_TO_DOUBLE( + /* yamlString= */ "442147483648", + /* javaClass= */ Double.class, + /* expectedResult= */ 442147483648.0), + + DOUBLE_TO_FLOAT( + /* yamlString= */ "1.23", /* javaClass= */ Float.class, /* expectedResult= */ 1.23F), + INT_TO_FLOAT(/* yamlString= */ "123", /* javaClass= */ Float.class, /* expectedResult= */ 123F), + + STRING_TO_ENUM( + /* yamlString= */ "AAA", + /* javaClass= */ TestEnum.class, + /* expectedResult= */ TestEnum.AAA), + + STRING_TO_BYTES( + /* yamlString= */ "data", + /* javaClass= */ byte[].class, + /* expectedResult= */ "data".getBytes()), + + BYTES_TO_BYTES( + /* yamlString= */ "!!binary 'ZGF0YQ=='", + /* javaClass= */ byte[].class, + /* expectedResult= */ "data".getBytes()), + + STRING_TO_BYTESTRING( + /* yamlString= */ "'data'", + /* javaClass= */ ByteString.class, + /* expectedResult= */ ByteString.copyFromUtf8("data")), + + BINARY_TO_BYTESTRING( + /* yamlString= */ "!!binary 'ZGF0YQ=='", + /* javaClass= */ ByteString.class, + /* expectedResult= */ ByteString.copyFromUtf8("data")); + + final String yamlString; + final Class javaClass; + final Object expectedResult; + + ParseYamlValueToJavaTypeCases(String yamlString, Class javaClass, Object expectedResult) { + this.yamlString = yamlString; + this.javaClass = javaClass; + this.expectedResult = expectedResult; + } + } + + @Test + public void parseYamlStringToJavaType_success( + @TestParameter ParseYamlValueToJavaTypeCases parseYamlValueToJavaTypeCases) throws Exception { + Object result = + ParameterValueParsing.parseYamlStringToJavaType( + parseYamlValueToJavaTypeCases.yamlString, parseYamlValueToJavaTypeCases.javaClass); + + assertThat(result).isEqualTo(parseYamlValueToJavaTypeCases.expectedResult); + } + + private enum TestEnum { + AAA, + BBB; + } +} diff --git a/src/test/java/com/google/testing/junit/testparameterinjector/PluggableTestRunnerTest.java b/src/test/java/com/google/testing/junit/testparameterinjector/PluggableTestRunnerTest.java new file mode 100644 index 0000000..686b152 --- /dev/null +++ b/src/test/java/com/google/testing/junit/testparameterinjector/PluggableTestRunnerTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2021 Google Inc. + * + * 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 com.google.testing.junit.testparameterinjector; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableList; +import java.util.List; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.MethodRule; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; + +@RunWith(JUnit4.class) +public class PluggableTestRunnerTest { + + private static int ruleInvocationCount = 0; + + public static class TestAndMethodRule implements MethodRule, TestRule { + + @Override + public Statement apply(Statement base, Description description) { + ruleInvocationCount++; + return base; + } + + @Override + public Statement apply(Statement base, FrameworkMethod method, Object target) { + ruleInvocationCount++; + return base; + } + } + + @RunWith(PluggableTestRunner.class) + public static class PluggableTestRunnerTestClass { + + @Rule public TestAndMethodRule rule = new TestAndMethodRule(); + + @Test + public void test() { + // no-op + } + } + + @Test + public void ruleThatIsBothTestRuleAndMethodRuleIsInvokedOnceOnly() throws Exception { + PluggableTestRunner.run( + new PluggableTestRunner(PluggableTestRunnerTestClass.class) { + @Override + protected List createTestMethodProcessorList() { + return ImmutableList.of(); + } + }); + + assertThat(ruleInvocationCount).isEqualTo(1); + } +} diff --git a/src/test/java/com/google/testing/junit/testparameterinjector/TestInfoTest.java b/src/test/java/com/google/testing/junit/testparameterinjector/TestInfoTest.java new file mode 100644 index 0000000..ae817f6 --- /dev/null +++ b/src/test/java/com/google/testing/junit/testparameterinjector/TestInfoTest.java @@ -0,0 +1,249 @@ +/* + * Copyright 2021 Google Inc. + * + * 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 com.google.testing.junit.testparameterinjector; + +import static com.google.common.truth.Truth.assertThat; +import static java.util.stream.Collectors.toList; + +import com.google.common.collect.ImmutableList; +import com.google.common.truth.IterableSubject; +import com.google.testing.junit.testparameterinjector.TestInfo.TestInfoParameter; +import java.util.List; +import java.util.stream.IntStream; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class TestInfoTest { + + @Test + public void shortenNamesIfNecessary_emptyTestInfos() throws Exception { + ImmutableList result = TestInfo.shortenNamesIfNecessary(ImmutableList.of()); + + assertThat(result).isEmpty(); + } + + @Test + public void shortenNamesIfNecessary_noParameters() throws Exception { + ImmutableList givenTestInfos = ImmutableList.of(fakeTestInfo()); + + ImmutableList result = TestInfo.shortenNamesIfNecessary(givenTestInfos); + + assertThat(result).containsExactlyElementsIn(givenTestInfos).inOrder(); + } + + @Test + public void shortenNamesIfNecessary_veryLongTestMethodName_noParameters() throws Exception { + ImmutableList givenTestInfos = + ImmutableList.of( + TestInfo.createWithoutParameters( + getClass() + .getMethod( + "unusedMethodThatHasAVeryLongNameForTest000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000"), + /* annotations= */ ImmutableList.of())); + + ImmutableList result = TestInfo.shortenNamesIfNecessary(givenTestInfos); + + assertThat(result).containsExactlyElementsIn(givenTestInfos).inOrder(); + } + + @Test + public void shortenNamesIfNecessary_noShorteningNeeded() throws Exception { + ImmutableList givenTestInfos = + ImmutableList.of( + fakeTestInfo( + TestInfoParameter.create( + /* name= */ "short", /* value= */ 1, /* indexInValueSource= */ 1), + TestInfoParameter.create( + /* name= */ "shorter", /* value= */ null, /* indexInValueSource= */ 3)), + fakeTestInfo( + TestInfoParameter.create( + /* name= */ "short", /* value= */ 1, /* indexInValueSource= */ 1), + TestInfoParameter.create( + /* name= */ "shortest", /* value= */ 20, /* indexInValueSource= */ 0))); + + ImmutableList result = TestInfo.shortenNamesIfNecessary(givenTestInfos); + + assertThat(result).containsExactlyElementsIn(givenTestInfos).inOrder(); + } + + @Test + public void shortenNamesIfNecessary_singleParameterTooLong_twoParameters() throws Exception { + ImmutableList result = + TestInfo.shortenNamesIfNecessary( + ImmutableList.of( + fakeTestInfo( + TestInfoParameter.create( + /* name= */ "short", /* value= */ 1, /* indexInValueSource= */ 0), + TestInfoParameter.create( + /* name= */ "shorter", /* value= */ null, /* indexInValueSource= */ 0)), + fakeTestInfo( + TestInfoParameter.create( + /* name= */ "short", /* value= */ 1, /* indexInValueSource= */ 0), + TestInfoParameter.create( + /* name= */ "very long parameter name for test" + + " 00000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000", + /* value= */ 20, + /* indexInValueSource= */ 1)))); + + assertThatTestNamesOf(result) + .containsExactly( + "toLowerCase[short,1.shorter]", + "toLowerCase[short,2.very long parameter name for test " + + "0000000000000000000000000000000000000000000000000000...]") + .inOrder(); + } + + @Test + public void shortenNamesIfNecessary_singleParameterTooLong_onlyParameter() throws Exception { + ImmutableList result = + TestInfo.shortenNamesIfNecessary( + ImmutableList.of( + fakeTestInfo( + TestInfoParameter.create( + /* name= */ "shorter", /* value= */ null, /* indexInValueSource= */ 0)), + fakeTestInfo( + TestInfoParameter.create( + /* name= */ "very long parameter name for test" + + " 00000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000", + /* value= */ 20, + /* indexInValueSource= */ 1)))); + + assertThatTestNamesOf(result) + .containsExactly( + "toLowerCase[1.shorter]", + "toLowerCase[2.very long parameter name for test" + + " 000000000000000000000000000000000000000000000000000000000000...]") + .inOrder(); + } + + @Test + public void shortenNamesIfNecessary_tooManyParameters() throws Exception { + TestInfo testInfoWithManyParams = + fakeTestInfo( + IntStream.range(0, 50) + .mapToObj( + i -> + TestInfoParameter.create( + /* name= */ "short", /* value= */ i, /* indexInValueSource= */ i)) + .toArray(TestInfoParameter[]::new)); + + ImmutableList result = + TestInfo.shortenNamesIfNecessary(ImmutableList.of(testInfoWithManyParams)); + + assertThatTestNamesOf(result) + .containsExactly( + "toLowerCase[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26," + + "27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50]"); + } + + @Test + public void deduplicateTestNames_noDuplicates() throws Exception { + ImmutableList givenTestInfos = + ImmutableList.of( + fakeTestInfo( + TestInfoParameter.create( + /* name= */ "aaa", /* value= */ 1, /* indexInValueSource= */ 1), + TestInfoParameter.create( + /* name= */ "bbb", /* value= */ null, /* indexInValueSource= */ 3)), + fakeTestInfo( + TestInfoParameter.create( + /* name= */ "aaa", /* value= */ 1, /* indexInValueSource= */ 1), + TestInfoParameter.create( + /* name= */ "ccc", /* value= */ 1, /* indexInValueSource= */ 0))); + + ImmutableList result = TestInfo.deduplicateTestNames(givenTestInfos); + + assertThat(result).containsExactlyElementsIn(givenTestInfos).inOrder(); + } + + @Test + public void deduplicateTestNames_duplicateParameterNamesWithDifferentTypes() throws Exception { + ImmutableList result = + TestInfo.deduplicateTestNames( + ImmutableList.of( + fakeTestInfo( + TestInfoParameter.create( + /* name= */ "aaa", /* value= */ 1, /* indexInValueSource= */ 1), + TestInfoParameter.create( + /* name= */ "null", /* value= */ null, /* indexInValueSource= */ 3)), + fakeTestInfo( + TestInfoParameter.create( + /* name= */ "aaa", /* value= */ 1, /* indexInValueSource= */ 1), + TestInfoParameter.create( + /* name= */ "null", /* value= */ "null", /* indexInValueSource= */ 0)), + fakeTestInfo( + TestInfoParameter.create( + /* name= */ "aaa", /* value= */ 1, /* indexInValueSource= */ 1), + TestInfoParameter.create( + /* name= */ "bbb", /* value= */ "b", /* indexInValueSource= */ 0)))); + + assertThatTestNamesOf(result) + .containsExactly( + "toLowerCase[aaa,null (null reference)]", + "toLowerCase[aaa,null (String)]", + "toLowerCase[aaa,bbb]") + .inOrder(); + } + + @Test + public void deduplicateTestNames_duplicateParameterNamesWithSameTypes() throws Exception { + ImmutableList result = + TestInfo.deduplicateTestNames( + ImmutableList.of( + fakeTestInfo( + TestInfoParameter.create( + /* name= */ "aaa", /* value= */ 1, /* indexInValueSource= */ 0), + TestInfoParameter.create( + /* name= */ "bbb", /* value= */ 1, /* indexInValueSource= */ 0)), + fakeTestInfo( + TestInfoParameter.create( + /* name= */ "aaa", /* value= */ 1, /* indexInValueSource= */ 0), + TestInfoParameter.create( + /* name= */ "bbb", /* value= */ 1, /* indexInValueSource= */ 1)), + fakeTestInfo( + TestInfoParameter.create( + /* name= */ "aaa", /* value= */ 1, /* indexInValueSource= */ 0), + TestInfoParameter.create( + /* name= */ "ccc", /* value= */ "b", /* indexInValueSource= */ 2)))); + + assertThatTestNamesOf(result) + .containsExactly( + "toLowerCase[1.aaa,1.bbb]", "toLowerCase[1.aaa,2.bbb]", "toLowerCase[1.aaa,3.ccc]") + .inOrder(); + } + + private static TestInfo fakeTestInfo(TestInfoParameter... parameters) + throws NoSuchMethodException { + return TestInfo.createWithoutParameters( + String.class.getMethod("toLowerCase"), /* annotations= */ ImmutableList.of()) + .withExtraParameters(ImmutableList.copyOf(parameters)); + } + + private static IterableSubject assertThatTestNamesOf(List result) { + return assertThat(result.stream().map(TestInfo::getName).collect(toList())); + } + + public void + unusedMethodThatHasAVeryLongNameForTest000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000() {} +} diff --git a/src/test/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessorTest.java b/src/test/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessorTest.java new file mode 100644 index 0000000..51a328d --- /dev/null +++ b/src/test/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessorTest.java @@ -0,0 +1,1077 @@ +/* + * Copyright 2021 Google Inc. + * + * 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 com.google.testing.junit.testparameterinjector; + +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.truth.Truth.assertThat; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.fail; + +import com.google.common.collect.ImmutableList; +import com.google.testing.junit.testparameterinjector.TestParameter.TestParameterValuesProvider; +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; +import org.junit.runner.notification.Failure; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.junit.runners.model.TestClass; + +/** + * Test class to test the PluggableTestRunner test harness works with {@link + * TestParameterAnnotation}s. + */ +@RunWith(Parameterized.class) +public class TestParameterAnnotationMethodProcessorTest { + + @Retention(RUNTIME) + @interface ClassTestResult { + Result value(); + } + + enum Result { + /** + * A successful test run is expected in both for + * TestParameterAnnotationMethodProcessor#forAllAnnotationPlacements and + * TestParameterAnnotationMethodProcessor#onlyForFieldsAndParameters. + */ + SUCCESS_ALWAYS, + SUCCESS_FOR_ALL_PLACEMENTS_ONLY, + FAILURE, + } + + public enum TestEnum { + UNDEFINED, + ONE, + TWO, + THREE, + FOUR, + FIVE + } + + @Retention(RUNTIME) + @TestParameterAnnotation + public @interface EnumParameter { + TestEnum[] value() default {}; + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class SingleAnnotationClass { + + private static List testedParameters; + + @EnumParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) + TestEnum enumParameter; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void test() { + testedParameters.add(enumParameter); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO, TestEnum.THREE); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class MultipleAllEnumValuesAnnotationClass { + + private static List testedParameters; + + @TestParameter TestEnum enumParameter1; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void test(@TestParameter TestEnum enumParameter2) { + testedParameters.add(enumParameter1 + ":" + enumParameter2); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).hasSize(TestEnum.values().length * TestEnum.values().length); + } + } + + @ClassTestResult(Result.SUCCESS_FOR_ALL_PLACEMENTS_ONLY) + public static class SingleParameterAnnotationClass { + + private static List testedParameters; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + @EnumParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) + public void test(TestEnum enumParameter) { + testedParameters.add(enumParameter); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO, TestEnum.THREE); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class SingleAnnotatedParameterAnnotationClass { + + private static List testedParameters; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void test( + @EnumParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) TestEnum enumParameter) { + testedParameters.add(enumParameter); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO, TestEnum.THREE); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class AnnotatedSuperclassParameter { + + private static List testedParameters; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void test( + @EnumParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) Object enumParameter) { + testedParameters.add(enumParameter); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO, TestEnum.THREE); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class DuplicatedAnnotatedParameterAnnotationClass { + + private static List> testedParameters; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void test( + @EnumParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) TestEnum enumParameter, + @EnumParameter({TestEnum.FOUR, TestEnum.FIVE}) TestEnum enumParameter2) { + testedParameters.add(ImmutableList.of(enumParameter, enumParameter2)); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters) + .containsExactly( + ImmutableList.of(TestEnum.ONE, TestEnum.FOUR), + ImmutableList.of(TestEnum.ONE, TestEnum.FIVE), + ImmutableList.of(TestEnum.TWO, TestEnum.FOUR), + ImmutableList.of(TestEnum.TWO, TestEnum.FIVE), + ImmutableList.of(TestEnum.THREE, TestEnum.FOUR), + ImmutableList.of(TestEnum.THREE, TestEnum.FIVE)); + } + } + + @ClassTestResult(Result.FAILURE) + public static class SingleAnnotatedParameterAnnotationClassWithMissingValue { + + private static List testedParameters; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void test(@EnumParameter TestEnum enumParameter) { + testedParameters.add(enumParameter); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO, TestEnum.THREE); + } + } + + @ClassTestResult(Result.SUCCESS_FOR_ALL_PLACEMENTS_ONLY) + public static class MultipleAnnotationTestClass { + + private static List testedParameters; + + @EnumParameter({TestEnum.ONE, TestEnum.TWO}) + TestEnum enumParameter; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + @EnumParameter({TestEnum.THREE}) + public void parameterized() { + testedParameters.add(enumParameter); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.THREE); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class TooLongTestNamesShortened { + + @Rule public TestName testName = new TestName(); + + private static List allTestNames; + + @BeforeClass + public static void resetStaticState() { + allTestNames = new ArrayList<>(); + } + + @Test + public void test1( + @TestParameter({ + "ABC", + "This is a very long string (240 characters) that would normally cause Sponge+Tin to" + + " exceed the filename limit of 255 characters." + + " ===========================================================================" + + "===================================" + }) + String testString) { + allTestNames.add(testName.getMethodName()); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(allTestNames) + .containsExactly( + "test1[1.ABC]", + "test1[2.This is a very long string (240 characters) that would normally cause" + + " Sponge+Tin to exceed the...]"); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class DuplicateTestNames { + + @Rule public TestName testName = new TestName(); + + private static List allTestNames; + private static List allTestParameterValues; + + @BeforeClass + public static void resetStaticState() { + allTestNames = new ArrayList<>(); + allTestParameterValues = new ArrayList<>(); + } + + @Test + public void test1(@TestParameter({"ABC", "ABC"}) String testString) { + allTestNames.add(testName.getMethodName()); + allTestParameterValues.add(testString); + } + + private static final class Test2Provider implements TestParameterValuesProvider { + @Override + public List provideValues() { + return newArrayList(123, "123", "null", null); + } + } + + @Test + public void test2(@TestParameter(valuesProvider = Test2Provider.class) Object testObject) { + allTestNames.add(testName.getMethodName()); + allTestParameterValues.add(testObject); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(allTestNames) + .containsExactly( + "test1[1.ABC]", + "test1[2.ABC]", + "test2[123 (Integer)]", + "test2[123 (String)]", + "test2[null (String)]", + "test2[null (null reference)]"); + assertThat(allTestParameterValues).containsExactly("ABC", "ABC", 123, "123", "null", null); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class DuplicateFieldAnnotationTestClass { + + private static List testedParameters; + + @TestParameter({"foo", "bar"}) + String stringParameter; + + @TestParameter({"baz", "qux"}) + String stringParameter2; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void test() { + testedParameters.add(stringParameter + ":" + stringParameter2); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly("foo:baz", "foo:qux", "bar:baz", "bar:qux"); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class DuplicateIdenticalFieldAnnotationTestClass { + + private static List testedParameters; + + @TestParameter({"foo", "bar"}) + String stringParameter; + + @TestParameter({"foo", "bar"}) + String stringParameter2; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void test() { + testedParameters.add(stringParameter + ":" + stringParameter2); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly("foo:foo", "foo:bar", "bar:foo", "bar:bar"); + } + } + + @ClassTestResult(Result.FAILURE) + public static class ErrorDuplicateFieldAnnotationTestClass { + + @EnumParameter({TestEnum.ONE, TestEnum.TWO}) + TestEnum parameter1; + + @EnumParameter({TestEnum.THREE, TestEnum.FOUR}) + TestEnum parameter2; + + @Test + @EnumParameter(TestEnum.FIVE) + public void test() {} + } + + @ClassTestResult(Result.FAILURE) + public static class ErrorDuplicateFieldAndClassAnnotationTestClass { + + @EnumParameter({TestEnum.ONE, TestEnum.TWO}) + TestEnum parameter; + + @EnumParameter(TestEnum.FIVE) + public ErrorDuplicateFieldAndClassAnnotationTestClass() {} + + @Test + public void test() {} + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class SingleAnnotationTestClassWithAnnotation { + + private static List testedParameters; + + @EnumParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) + TestEnum enumParameter; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void test() { + testedParameters.add(enumParameter); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO, TestEnum.THREE); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class MultipleAnnotationTestClassWithAnnotation { + + private static List testedParameters; + + @EnumParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) + TestEnum enumParameter; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void parameterized(@TestParameter({"foo", "bar"}) String stringParameter) { + testedParameters.add(stringParameter + ":" + enumParameter); + } + + @Test + public void nonParameterized() { + testedParameters.add("none:" + enumParameter); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters) + .containsExactly( + "none:ONE", + "none:TWO", + "none:THREE", + "foo:ONE", + "foo:TWO", + "foo:THREE", + "bar:ONE", + "bar:TWO", + "bar:THREE"); + } + } + + public abstract static class BaseClassWithAnnotations { + @Rule public TestName testName = new TestName(); + + static List allTestNames; + + @TestParameter boolean boolInBase; + + @BeforeClass + public static void resetStaticState() { + allTestNames = new ArrayList<>(); + } + + @Before + public void setUp() { + assertThat(allTestNames).doesNotContain(testName.getMethodName()); + } + + @After + public void tearDown() { + assertThat(allTestNames).contains(testName.getMethodName()); + } + + @Test + public void testInBase(@TestParameter({"ONE", "TWO"}) TestEnum enumInBase) { + allTestNames.add(testName.getMethodName()); + } + + @Test + public abstract void abstractTestInBase(); + + @Test + public void overridableTestInBase() { + throw new UnsupportedOperationException("Expected the base class to override this"); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class AnnotationInheritedFromBaseClass extends BaseClassWithAnnotations { + + @TestParameter boolean boolInChild; + + @Test + public void testInChild(@TestParameter({"TWO", "THREE"}) TestEnum enumInChild) { + allTestNames.add(testName.getMethodName()); + } + + @Override + public void abstractTestInBase() { + allTestNames.add(testName.getMethodName()); + } + + @Override + public void overridableTestInBase() { + allTestNames.add(testName.getMethodName()); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(allTestNames) + .containsExactly( + "testInBase[boolInChild=false,boolInBase=false,ONE]", + "testInBase[boolInChild=false,boolInBase=false,TWO]", + "testInBase[boolInChild=false,boolInBase=true,ONE]", + "testInBase[boolInChild=false,boolInBase=true,TWO]", + "testInBase[boolInChild=true,boolInBase=false,ONE]", + "testInBase[boolInChild=true,boolInBase=false,TWO]", + "testInBase[boolInChild=true,boolInBase=true,ONE]", + "testInBase[boolInChild=true,boolInBase=true,TWO]", + "testInChild[boolInChild=false,boolInBase=false,TWO]", + "testInChild[boolInChild=false,boolInBase=false,THREE]", + "testInChild[boolInChild=false,boolInBase=true,TWO]", + "testInChild[boolInChild=false,boolInBase=true,THREE]", + "testInChild[boolInChild=true,boolInBase=false,TWO]", + "testInChild[boolInChild=true,boolInBase=false,THREE]", + "testInChild[boolInChild=true,boolInBase=true,TWO]", + "testInChild[boolInChild=true,boolInBase=true,THREE]", + "abstractTestInBase[boolInChild=false,boolInBase=false]", + "abstractTestInBase[boolInChild=false,boolInBase=true]", + "abstractTestInBase[boolInChild=true,boolInBase=false]", + "abstractTestInBase[boolInChild=true,boolInBase=true]", + "overridableTestInBase[boolInChild=false,boolInBase=false]", + "overridableTestInBase[boolInChild=false,boolInBase=true]", + "overridableTestInBase[boolInChild=true,boolInBase=false]", + "overridableTestInBase[boolInChild=true,boolInBase=true]"); + } + } + + @Retention(RUNTIME) + @TestParameterAnnotation(validator = TestEnumValidator.class, processor = TestEnumProcessor.class) + public @interface EnumEvaluatorParameter { + TestEnum[] value() default {}; + } + + public static class TestEnumValidator implements TestParameterValidator { + + @Override + public boolean shouldSkip(Context context) { + return context.has(EnumEvaluatorParameter.class, TestEnum.THREE); + } + } + + public static class TestEnumProcessor implements TestParameterProcessor { + + static List beforeCalls = new ArrayList<>(); + static List afterCalls = new ArrayList<>(); + + static void init() { + beforeCalls.clear(); + afterCalls.clear(); + } + + static TestEnum currentValue; + + @Override + public void before(Object testParameterValue) { + beforeCalls.add(testParameterValue); + currentValue = (TestEnum) testParameterValue; + } + + @Override + public void after(Object testParameterValue) { + afterCalls.add(testParameterValue); + currentValue = null; + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class MethodEvaluatorClass { + + private static List testedParameters; + + @Test + public void test( + @EnumEvaluatorParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) TestEnum value) { + if (value == TestEnum.THREE) { + fail(); + } else { + testedParameters.add(value); + } + } + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @BeforeClass + public static void init() { + TestEnumProcessor.init(); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO); + assertThat(TestEnumProcessor.beforeCalls).containsExactly(TestEnum.ONE, TestEnum.TWO); + assertThat(TestEnumProcessor.afterCalls).containsExactly(TestEnum.ONE, TestEnum.TWO); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class FieldEvaluatorClass { + + private static List testedParameters; + + @EnumEvaluatorParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) + TestEnum value; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void test() { + if (value == TestEnum.THREE) { + fail(); + } else { + testedParameters.add(value); + } + } + + @BeforeClass + public static void init() { + TestEnumProcessor.init(); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO); + assertThat(TestEnumProcessor.beforeCalls).containsExactly(TestEnum.ONE, TestEnum.TWO); + assertThat(TestEnumProcessor.afterCalls).containsExactly(TestEnum.ONE, TestEnum.TWO); + } + } + + @ClassTestResult(Result.SUCCESS_FOR_ALL_PLACEMENTS_ONLY) + @EnumEvaluatorParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) + public static class ClassEvaluatorClass { + + private static List testedParameters; + + public ClassEvaluatorClass() {} + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void test() { + if (TestEnumProcessor.currentValue == TestEnum.THREE) { + fail(); + } else { + testedParameters.add(TestEnumProcessor.currentValue); + } + } + + @BeforeClass + public static void init() { + TestEnumProcessor.init(); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO); + assertThat(TestEnumProcessor.beforeCalls).containsExactly(TestEnum.ONE, TestEnum.TWO); + assertThat(TestEnumProcessor.afterCalls).containsExactly(TestEnum.ONE, TestEnum.TWO); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class ConstructorClass { + + private static List testedParameters; + final TestEnum enumParameter; + + public ConstructorClass( + @EnumParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) TestEnum enumParameter) { + this.enumParameter = enumParameter; + } + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void test() { + testedParameters.add(enumParameter); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO, TestEnum.THREE); + } + } + + @ClassTestResult(Result.SUCCESS_FOR_ALL_PLACEMENTS_ONLY) + public static class MethodFieldOverrideClass { + + private static List testedParameters; + + @EnumParameter({TestEnum.ONE, TestEnum.TWO}) + TestEnum enumParameter; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + @EnumParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) + public void test() { + testedParameters.add(enumParameter); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO, TestEnum.THREE); + } + } + + @ClassTestResult(Result.SUCCESS_FOR_ALL_PLACEMENTS_ONLY) + @EnumEvaluatorParameter({TestEnum.ONE}) + public static class MethodClassOverrideClass { + + private static List testedParameters; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + @EnumEvaluatorParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) + public void test() { + testedParameters.add(TestEnumProcessor.currentValue); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO); + } + } + + @ClassTestResult(Result.SUCCESS_FOR_ALL_PLACEMENTS_ONLY) + public static class ErrorDuplicatedConstructorMethodAnnotation { + + private static List testedParameters; + final TestEnum enumParameter; + + @EnumParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) + public ErrorDuplicatedConstructorMethodAnnotation(TestEnum enumParameter) { + this.enumParameter = enumParameter; + } + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + @EnumParameter({TestEnum.ONE, TestEnum.TWO}) + public void test(TestEnum otherParameter) { + testedParameters.add(enumParameter + ":" + otherParameter); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters) + .containsExactly("ONE:ONE", "ONE:TWO", "TWO:ONE", "TWO:TWO", "THREE:ONE", "THREE:TWO"); + } + } + + @ClassTestResult(Result.FAILURE) + @EnumParameter({TestEnum.ONE, TestEnum.TWO, TestEnum.THREE}) + public static class ErrorDuplicatedClassFieldAnnotation { + + private static List testedParameters; + + @EnumParameter({TestEnum.ONE, TestEnum.TWO}) + TestEnum enumParameter; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void test() { + testedParameters.add(enumParameter); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO); + } + } + + @ClassTestResult(Result.FAILURE) + public static class ErrorNonStaticProviderClass { + + @Test + public void test(@TestParameter(valuesProvider = NonStaticProvider.class) int i) {} + + @SuppressWarnings("ClassCanBeStatic") + class NonStaticProvider implements TestParameterValuesProvider { + @Override + public List provideValues() { + return ImmutableList.of(); + } + } + } + + public enum EnumA { + A1, + A2 + } + + public enum EnumB { + B1, + B2 + } + + public enum EnumC { + C1, + C2, + C3 + } + + @Retention(RUNTIME) + @TestParameterAnnotation(validator = TestBaseValidatorValidator.class) + public @interface EnumAParameter { + EnumA[] value() default {EnumA.A1, EnumA.A2}; + } + + @Retention(RUNTIME) + @TestParameterAnnotation(validator = TestBaseValidatorValidator.class) + public @interface EnumBParameter { + EnumB[] value() default {EnumB.B1, EnumB.B2}; + } + + @Retention(RUNTIME) + @TestParameterAnnotation(validator = TestBaseValidatorValidator.class) + public @interface EnumCParameter { + EnumC[] value() default {EnumC.C1, EnumC.C2, EnumC.C3}; + } + + public static class TestBaseValidatorValidator extends BaseTestParameterValidator { + + @Override + protected List>> getIndependentParameters(Context context) { + return ImmutableList.of( + ImmutableList.of(EnumAParameter.class, EnumBParameter.class, EnumCParameter.class)); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class IndependentAnnotation { + + @EnumAParameter EnumA enumA; + @EnumBParameter EnumB enumB; + @EnumCParameter EnumC enumC; + + private static List> testedParameters; + + @BeforeClass + public static void resetStaticState() { + testedParameters = new ArrayList<>(); + } + + @Test + public void test() { + testedParameters.add(ImmutableList.of(enumA, enumB, enumC)); + } + + @AfterClass + public static void completedAllParameterizedTests() { + // Only 3 tests should have been sufficient to cover all cases. + assertThat(testedParameters).hasSize(3); + assertAllEnumsAreIncluded(EnumA.values()); + assertAllEnumsAreIncluded(EnumB.values()); + assertAllEnumsAreIncluded(EnumC.values()); + } + + private static > void assertAllEnumsAreIncluded(Enum[] values) { + Set> enumSet = new HashSet<>(Arrays.asList(values)); + for (List enumList : testedParameters) { + enumSet.removeAll(enumList); + } + assertThat(enumSet).isEmpty(); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class TestNamesTest { + + @Rule public TestName name = new TestName(); + + @TestParameter("8") + long fieldParam; + + @Test + public void withPrimitives( + @TestParameter("true") boolean param1, @TestParameter("2") int param2) { + assertThat(name.getMethodName()) + .isEqualTo("withPrimitives[fieldParam=8,param1=true,param2=2]"); + } + + @Test + public void withString(@TestParameter("AAA") String param1) { + assertThat(name.getMethodName()).isEqualTo("withString[fieldParam=8,AAA]"); + } + + @Test + public void withEnum(@EnumParameter(TestEnum.TWO) TestEnum param1) { + assertThat(name.getMethodName()).isEqualTo("withEnum[fieldParam=8,TWO]"); + } + } + + @ClassTestResult(Result.SUCCESS_ALWAYS) + public static class MethodNameContainsOrderedParameterNames { + + @Rule public TestName name = new TestName(); + + @Test + public void pretest(@TestParameter({"a", "b"}) String foo) {} + + @Test + public void test( + @EnumParameter({TestEnum.ONE, TestEnum.TWO}) TestEnum e, @TestParameter({"c"}) String foo) { + assertThat(name.getMethodName()).isEqualTo("test[" + e.name() + "," + foo + "]"); + } + } + + @Parameters(name = "{0}:{2}") + public static Collection parameters() { + return Arrays.stream(TestParameterAnnotationMethodProcessorTest.class.getClasses()) + .filter(cls -> cls.isAnnotationPresent(ClassTestResult.class)) + .map( + cls -> + new Object[] { + cls.getSimpleName(), cls, cls.getAnnotation(ClassTestResult.class).value() + }) + .collect(toImmutableList()); + } + + private final Class testClass; + private final Result result; + + public TestParameterAnnotationMethodProcessorTest( + String name, Class testClass, Result result) { + this.testClass = testClass; + this.result = result; + } + + @Test + @Ignore("b/195657808 @TestParameters is not supported on Android") + public void test() throws Exception { + List failures; + switch (result) { + case SUCCESS_ALWAYS: + failures = + PluggableTestRunner.run( + newTestRunnerWithParameterizedSupport( + TestParameterAnnotationMethodProcessor::forAllAnnotationPlacements)); + assertThat(failures).isEmpty(); + + failures = + PluggableTestRunner.run( + newTestRunnerWithParameterizedSupport( + TestParameterAnnotationMethodProcessor::onlyForFieldsAndParameters)); + assertThat(failures).isEmpty(); + break; + + case SUCCESS_FOR_ALL_PLACEMENTS_ONLY: + failures = + PluggableTestRunner.run( + newTestRunnerWithParameterizedSupport( + TestParameterAnnotationMethodProcessor::forAllAnnotationPlacements)); + assertThat(failures).isEmpty(); + + assertThrows( + IllegalStateException.class, + () -> + PluggableTestRunner.run( + newTestRunnerWithParameterizedSupport( + TestParameterAnnotationMethodProcessor::onlyForFieldsAndParameters))); + break; + + case FAILURE: + assertThrows( + IllegalStateException.class, + () -> + PluggableTestRunner.run( + newTestRunnerWithParameterizedSupport( + TestParameterAnnotationMethodProcessor::forAllAnnotationPlacements))); + assertThrows( + IllegalStateException.class, + () -> + PluggableTestRunner.run( + newTestRunnerWithParameterizedSupport( + TestParameterAnnotationMethodProcessor::onlyForFieldsAndParameters))); + break; + } + } + + private PluggableTestRunner newTestRunnerWithParameterizedSupport( + Function processor) throws Exception { + return new PluggableTestRunner(testClass) { + @Override + protected List createTestMethodProcessorList() { + return ImmutableList.of(processor.apply(getTestClass())); + } + }; + } +} diff --git a/src/test/java/com/google/testing/junit/testparameterinjector/TestParameterTest.java b/src/test/java/com/google/testing/junit/testparameterinjector/TestParameterTest.java new file mode 100644 index 0000000..b16d5e1 --- /dev/null +++ b/src/test/java/com/google/testing/junit/testparameterinjector/TestParameterTest.java @@ -0,0 +1,211 @@ +/* + * Copyright 2021 Google Inc. + * + * 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 com.google.testing.junit.testparameterinjector; + +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.common.base.CharMatcher; +import com.google.testing.junit.testparameterinjector.TestParameter.TestParameterValuesProvider; +import java.lang.annotation.Retention; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runner.notification.Failure; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** Test class to test the @TestParameter's value provider. */ +@RunWith(Parameterized.class) +public class TestParameterTest { + + @Retention(RUNTIME) + @interface RunAsTest {} + + public enum TestEnum { + ONE, + TWO, + THREE, + } + + @RunAsTest + public static class AnnotatedField { + private static List testedParameters; + + @TestParameter TestEnum enumParameter; + + @BeforeClass + public static void initializeStaticFields() { + assertWithMessage("Expecting this class to be run only once").that(testedParameters).isNull(); + testedParameters = new ArrayList<>(); + } + + @Test + public void test() { + testedParameters.add(enumParameter); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO, TestEnum.THREE); + } + } + + @RunAsTest + public static class AnnotatedConstructorParameter { + private static List testedParameters; + + private final TestEnum enumParameter; + + public AnnotatedConstructorParameter(@TestParameter TestEnum enumParameter) { + this.enumParameter = enumParameter; + } + + @BeforeClass + public static void initializeStaticFields() { + assertWithMessage("Expecting this class to be run only once").that(testedParameters).isNull(); + testedParameters = new ArrayList<>(); + } + + @Test + public void test() { + testedParameters.add(enumParameter); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters).containsExactly(TestEnum.ONE, TestEnum.TWO, TestEnum.THREE); + } + } + + @RunAsTest + public static class MultipleAnnotatedParameters { + private static List testedParameters; + + @BeforeClass + public static void initializeStaticFields() { + assertWithMessage("Expecting this class to be run only once").that(testedParameters).isNull(); + testedParameters = new ArrayList<>(); + } + + @Test + public void test( + @TestParameter TestEnum enumParameterA, + @TestParameter({"TWO", "THREE"}) TestEnum enumParameterB, + @TestParameter({"!!binary 'ZGF0YQ=='", "data2"}) byte[] bytes) { + testedParameters.add( + String.format("%s:%s:%s", enumParameterA, enumParameterB, new String(bytes))); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters) + .containsExactly( + "ONE:TWO:data", + "ONE:THREE:data", + "TWO:TWO:data", + "TWO:THREE:data", + "THREE:TWO:data", + "THREE:THREE:data", + "ONE:TWO:data2", + "ONE:THREE:data2", + "TWO:TWO:data2", + "TWO:THREE:data2", + "THREE:TWO:data2", + "THREE:THREE:data2"); + } + } + + @RunAsTest + public static class WithValuesProvider { + private static List testedParameters; + + @BeforeClass + public static void initializeStaticFields() { + assertWithMessage("Expecting this class to be run only once").that(testedParameters).isNull(); + testedParameters = new ArrayList<>(); + } + + @Test + public void stringTest( + @TestParameter(valuesProvider = TestStringProvider.class) String string) { + testedParameters.add(string); + } + + @Test + public void charMatcherTest( + @TestParameter(valuesProvider = CharMatcherProvider.class) CharMatcher charMatcher) { + testedParameters.add(charMatcher); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testedParameters) + .containsExactly( + "A", "B", null, CharMatcher.any(), CharMatcher.ascii(), CharMatcher.whitespace()); + } + + private static final class TestStringProvider implements TestParameterValuesProvider { + @Override + public List provideValues() { + return newArrayList("A", "B", null); + } + } + + private static final class CharMatcherProvider implements TestParameterValuesProvider { + @Override + public List provideValues() { + return newArrayList(CharMatcher.any(), CharMatcher.ascii(), CharMatcher.whitespace()); + } + } + } + + @Parameters(name = "{0}") + public static Collection parameters() { + return Arrays.stream(TestParameterTest.class.getClasses()) + .filter(cls -> cls.isAnnotationPresent(RunAsTest.class)) + .map(cls -> new Object[] {cls.getSimpleName(), cls}) + .collect(toImmutableList()); + } + + private final Class testClass; + + public TestParameterTest(String name, Class testClass) { + this.testClass = testClass; + } + + @Test + public void test() throws Exception { + List failures = + PluggableTestRunner.run( + new PluggableTestRunner(testClass) { + @Override + protected List createTestMethodProcessorList() { + return TestMethodProcessors.createNewParameterizedProcessorsWithLegacyFeatures( + getTestClass()); + } + }); + + assertThat(failures).isEmpty(); + } +} diff --git a/src/test/java/com/google/testing/junit/testparameterinjector/TestParametersMethodProcessorTest.java b/src/test/java/com/google/testing/junit/testparameterinjector/TestParametersMethodProcessorTest.java new file mode 100644 index 0000000..b27ce7a --- /dev/null +++ b/src/test/java/com/google/testing/junit/testparameterinjector/TestParametersMethodProcessorTest.java @@ -0,0 +1,474 @@ +/* + * Copyright 2021 Google Inc. + * + * 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 com.google.testing.junit.testparameterinjector; + +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.truth.Truth.assertThat; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.common.collect.ImmutableList; +import com.google.testing.junit.testparameterinjector.TestParameters.TestParametersValues; +import com.google.testing.junit.testparameterinjector.TestParameters.TestParametersValuesProvider; +import java.lang.annotation.Retention; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; +import org.junit.runner.notification.Failure; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +@Ignore("b/195657808 @TestParameters is not supported on Android") +public class TestParametersMethodProcessorTest { + + @Retention(RUNTIME) + @interface RunAsTest {} + + public enum TestEnum { + ONE, + TWO, + THREE; + } + + @RunAsTest + public static class SimpleMethodAnnotation { + @Rule public TestName testName = new TestName(); + + private static Map testNameToStringifiedParametersMap; + + @BeforeClass + public static void resetStaticState() { + testNameToStringifiedParametersMap = new LinkedHashMap<>(); + } + + @Test + @TestParameters({ + "{testEnum: ONE, testLong: 11, testBoolean: false, testString: ABC}", + "{testEnum: TWO,\ntestLong: 22,\ntestBoolean: true,\r\n\r\n testString: 'DEF'}", + "{testEnum: null, testLong: 33, testBoolean: false, testString: null}", + }) + @Ignore("b/195657808 @TestParameters is not supported on Android") + public void test(TestEnum testEnum, long testLong, boolean testBoolean, String testString) { + testNameToStringifiedParametersMap.put( + testName.getMethodName(), + String.format("%s,%s,%s,%s", testEnum, testLong, testBoolean, testString)); + } + + @Test + @TestParameters({ + "{testString: ABC}", + "{testString: 'This is a very long string (240 characters) that would normally cause" + + " Sponge+Tin to exceed the filename limit of 255 characters." + + " =================================================================================" + + "=============='}" + }) + @Ignore("b/195657808 @TestParameters is not supported on Android") + public void test2_withLongNames(String testString) { + testNameToStringifiedParametersMap.put(testName.getMethodName(), testString); + } + + @Test + @TestParameters({ + "{testEnums: [ONE, TWO, THREE], testLongs: [11, 4], testBooleans: [false, true]," + + " testStrings: [ABC, '123']}", + "{testEnums: [TWO],\ntestLongs: [22],\ntestBooleans: [true],\r\n\r\n testStrings: ['DEF']}", + "{testEnums: [], testLongs: [], testBooleans: [], testStrings: []}", + }) + @Ignore("b/195657808 @TestParameters is not supported on Android") + public void test3_withRepeatedParams( + List testEnums, + List testLongs, + List testBooleans, + List testStrings) { + testNameToStringifiedParametersMap.put( + testName.getMethodName(), + String.format("%s,%s,%s,%s", testEnums, testLongs, testBooleans, testStrings)); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testNameToStringifiedParametersMap) + .containsExactly( + "test[{testEnum: ONE, testLong: 11, testBoolean: false, testString: ABC}]", + "ONE,11,false,ABC", + "test[{testEnum: TWO, testLong: 22, testBoolean: true, testString: 'DEF'}]", + "TWO,22,true,DEF", + "test[{testEnum: null, testLong: 33, testBoolean: false, testString: null}]", + "null,33,false,null", + "test2_withLongNames[1.{testString: ABC}]", + "ABC", + "test2_withLongNames[2.{testString: 'This is a very long string (240 characters)" + + " that would normally cause Sponge+Tin...]", + "This is a very long string (240 characters) that would normally cause Sponge+Tin to" + + " exceed the filename limit of 255 characters." + + " =================================================================================" + + "==============", + "test3_withRepeatedParams[1.{testEnums: [ONE, TWO, THREE], testLongs: [11, 4]," + + " testBooleans: [false, true], testStrings: [...]", + "[ONE, TWO, THREE],[11, 4],[false, true],[ABC, 123]", + "test3_withRepeatedParams[2.{testEnums: [TWO], testLongs: [22], testBooleans: [true]," + + " testStrings: ['DEF']}]", + "[TWO],[22],[true],[DEF]", + "test3_withRepeatedParams[3.{testEnums: [], testLongs: [], testBooleans: []," + + " testStrings: []}]", + "[],[],[],[]"); + } + } + + @RunAsTest + public static class SimpleConstructorAnnotation { + + @Rule public TestName testName = new TestName(); + + private static Map testNameToStringifiedParametersMap; + + private final TestEnum testEnum; + private final long testLong; + private final boolean testBoolean; + private final String testString; + + @TestParameters({ + "{testEnum: ONE, testLong: 11, testBoolean: false, testString: ABC}", + "{testEnum: TWO, testLong: 22, testBoolean: true, testString: DEF}", + "{testEnum: null, testLong: 33, testBoolean: false, testString: null}", + }) + public SimpleConstructorAnnotation( + TestEnum testEnum, long testLong, boolean testBoolean, String testString) { + this.testEnum = testEnum; + this.testLong = testLong; + this.testBoolean = testBoolean; + this.testString = testString; + } + + @BeforeClass + public static void resetStaticState() { + testNameToStringifiedParametersMap = new LinkedHashMap<>(); + } + + @Test + public void test1() { + testNameToStringifiedParametersMap.put( + testName.getMethodName(), + String.format("%s,%s,%s,%s", testEnum, testLong, testBoolean, testString)); + } + + @Test + public void test2() { + testNameToStringifiedParametersMap.put( + testName.getMethodName(), + String.format("%s,%s,%s,%s", testEnum, testLong, testBoolean, testString)); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testNameToStringifiedParametersMap) + .containsExactly( + "test1[{testEnum: ONE, testLong: 11, testBoolean: false, testString: ABC}]", + "ONE,11,false,ABC", + "test1[{testEnum: TWO, testLong: 22, testBoolean: true, testString: DEF}]", + "TWO,22,true,DEF", + "test1[{testEnum: null, testLong: 33, testBoolean: false, testString: null}]", + "null,33,false,null", + "test2[{testEnum: ONE, testLong: 11, testBoolean: false, testString: ABC}]", + "ONE,11,false,ABC", + "test2[{testEnum: TWO, testLong: 22, testBoolean: true, testString: DEF}]", + "TWO,22,true,DEF", + "test2[{testEnum: null, testLong: 33, testBoolean: false, testString: null}]", + "null,33,false,null"); + } + } + + @RunAsTest + public static class ConstructorAnnotationWithProvider { + + @Rule public TestName testName = new TestName(); + + private static Map testNameToParameterMap; + + private final TestEnum testEnum; + + @TestParameters(valuesProvider = TestEnumValuesProvider.class) + public ConstructorAnnotationWithProvider(TestEnum testEnum) { + this.testEnum = testEnum; + } + + @BeforeClass + public static void resetStaticState() { + testNameToParameterMap = new LinkedHashMap<>(); + } + + @Test + public void test1() { + testNameToParameterMap.put(testName.getMethodName(), testEnum); + } + + @Test + public void test2() { + testNameToParameterMap.put(testName.getMethodName(), testEnum); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(testNameToParameterMap) + .containsExactly( + "test1[one]", TestEnum.ONE, + "test1[two]", TestEnum.TWO, + "test1[null-case]", null, + "test2[one]", TestEnum.ONE, + "test2[two]", TestEnum.TWO, + "test2[null-case]", null); + } + + private static final class TestEnumValuesProvider implements TestParametersValuesProvider { + @Override + public List provideValues() { + return ImmutableList.of( + TestParametersValues.builder() + .name("one") + .addParameter("testEnum", TestEnum.ONE) + .build(), + TestParametersValues.builder() + .name("two") + .addParameter("testEnum", TestEnum.TWO) + .build(), + TestParametersValues.builder() + .name("null-case") + .addParameter("testEnum", null) + .build()); + } + } + } + + public abstract static class BaseClassWithMethodAnnotation { + @Rule public TestName testName = new TestName(); + + static List allTestNames; + + @BeforeClass + public static void resetStaticState() { + allTestNames = new ArrayList<>(); + } + + @Before + public void setUp() { + assertThat(allTestNames).doesNotContain(testName.getMethodName()); + } + + @After + public void tearDown() { + assertThat(allTestNames).contains(testName.getMethodName()); + } + + @Test + @TestParameters({"{testEnum: ONE}", "{testEnum: TWO}"}) + public void testInBase(TestEnum testEnum) { + allTestNames.add(testName.getMethodName()); + } + } + + @RunAsTest + public static class AnnotationInheritedFromBaseClass extends BaseClassWithMethodAnnotation { + + @Test + @TestParameters({"{testEnum: TWO}", "{testEnum: THREE}"}) + public void testInChild(TestEnum testEnum) { + allTestNames.add(testName.getMethodName()); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(allTestNames) + .containsExactly( + "testInBase[{testEnum: ONE}]", + "testInBase[{testEnum: TWO}]", + "testInChild[{testEnum: TWO}]", + "testInChild[{testEnum: THREE}]"); + } + } + + @RunAsTest + public static class MixedWithTestParameterMethodAnnotation { + @Rule public TestName testName = new TestName(); + + private static List allTestNames; + private static List testNamesThatInvokedBefore; + private static List testNamesThatInvokedAfter; + + @TestParameters({"{testEnum: ONE}", "{testEnum: TWO}"}) + public MixedWithTestParameterMethodAnnotation(TestEnum testEnum) {} + + @BeforeClass + public static void resetStaticState() { + allTestNames = new ArrayList<>(); + testNamesThatInvokedBefore = new ArrayList<>(); + testNamesThatInvokedAfter = new ArrayList<>(); + } + + @Before + public void setUp() { + assertThat(allTestNames).doesNotContain(testName.getMethodName()); + testNamesThatInvokedBefore.add(testName.getMethodName()); + } + + @After + public void tearDown() { + assertThat(allTestNames).contains(testName.getMethodName()); + testNamesThatInvokedAfter.add(testName.getMethodName()); + } + + @Test + public void test1(@TestParameter TestEnum testEnum) { + assertThat(testNamesThatInvokedBefore).contains(testName.getMethodName()); + allTestNames.add(testName.getMethodName()); + } + + @Test + @TestParameters({"{testString: ABC}", "{testString: DEF}"}) + public void test2(String testString) { + allTestNames.add(testName.getMethodName()); + } + + @Test + @TestParameters({ + "{testString: ABC}", + "{testString: 'This is a very long string (240 characters) that would normally cause" + + " Sponge+Tin to exceed the filename limit of 255 characters." + + " =================================================================================" + + "=============='}" + }) + public void test3_withLongNames(String testString) { + allTestNames.add(testName.getMethodName()); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(allTestNames) + .containsExactly( + "test1[{testEnum: ONE},ONE]", + "test1[{testEnum: ONE},TWO]", + "test1[{testEnum: ONE},THREE]", + "test1[{testEnum: TWO},ONE]", + "test1[{testEnum: TWO},TWO]", + "test1[{testEnum: TWO},THREE]", + "test2[{testEnum: ONE},{testString: ABC}]", + "test2[{testEnum: ONE},{testString: DEF}]", + "test2[{testEnum: TWO},{testString: ABC}]", + "test2[{testEnum: TWO},{testString: DEF}]", + "test3_withLongNames[{testEnum: ONE},1.{testString: ABC}]", + "test3_withLongNames[{testEnum: ONE},2.{testString: 'This is a very long string" + + " (240 characters) that would normally caus...]", + "test3_withLongNames[{testEnum: TWO},1.{testString: ABC}]", + "test3_withLongNames[{testEnum: TWO},2.{testString: 'This is a very long string" + + " (240 characters) that would normally caus...]"); + + assertThat(testNamesThatInvokedBefore).containsExactlyElementsIn(allTestNames).inOrder(); + assertThat(testNamesThatInvokedAfter).containsExactlyElementsIn(allTestNames).inOrder(); + } + } + + @RunAsTest + public static class MixedWithTestParameterFieldAnnotation { + @Rule public TestName testName = new TestName(); + + private static List allTestNames; + + @TestParameter TestEnum testEnumA; + + @TestParameters({"{testEnumB: ONE}", "{testEnumB: TWO}"}) + public MixedWithTestParameterFieldAnnotation(TestEnum testEnumB) {} + + @BeforeClass + public static void resetStaticState() { + allTestNames = new ArrayList<>(); + } + + @Before + public void setUp() { + assertThat(allTestNames).doesNotContain(testName.getMethodName()); + } + + @After + public void tearDown() { + assertThat(allTestNames).contains(testName.getMethodName()); + } + + @Test + @TestParameters({"{testString: ABC}", "{testString: DEF}"}) + @Ignore("b/195657808 @TestParameters is not supported on Android") + public void test1(String testString) { + allTestNames.add(testName.getMethodName()); + } + + @AfterClass + public static void completedAllParameterizedTests() { + assertThat(allTestNames) + .containsExactly( + "test1[{testEnumB: ONE},{testString: ABC},ONE]", + "test1[{testEnumB: ONE},{testString: ABC},TWO]", + "test1[{testEnumB: ONE},{testString: ABC},THREE]", + "test1[{testEnumB: ONE},{testString: DEF},ONE]", + "test1[{testEnumB: ONE},{testString: DEF},TWO]", + "test1[{testEnumB: ONE},{testString: DEF},THREE]", + "test1[{testEnumB: TWO},{testString: ABC},ONE]", + "test1[{testEnumB: TWO},{testString: ABC},TWO]", + "test1[{testEnumB: TWO},{testString: ABC},THREE]", + "test1[{testEnumB: TWO},{testString: DEF},ONE]", + "test1[{testEnumB: TWO},{testString: DEF},TWO]", + "test1[{testEnumB: TWO},{testString: DEF},THREE]"); + } + } + + @Parameters(name = "{0}") + public static Collection parameters() { + return Arrays.stream(TestParametersMethodProcessorTest.class.getClasses()) + .filter(cls -> cls.isAnnotationPresent(RunAsTest.class)) + .map(cls -> new Object[] {cls.getSimpleName(), cls}) + .collect(toImmutableList()); + } + + private final Class testClass; + + public TestParametersMethodProcessorTest(String name, Class testClass) { + this.testClass = testClass; + } + + @Test + public void test() throws Exception { + List failures = PluggableTestRunner.run(newTestRunner()); + assertThat(failures).isEmpty(); + } + + private PluggableTestRunner newTestRunner() throws Exception { + return new PluggableTestRunner(testClass) { + @Override + protected List createTestMethodProcessorList() { + return TestMethodProcessors.createNewParameterizedProcessorsWithLegacyFeatures( + getTestClass()); + } + }; + } +} -- cgit v1.2.3