From 81be061f85c3dd010e3cd5c8214e1937def6069e Mon Sep 17 00:00:00 2001 From: Chaoren Lin Date: Sat, 5 Oct 2024 15:02:27 -0700 Subject: [PATCH] Fix `@Require` annotations so that features implied by absent features are not also required to be absent. Fixes #7401 RELNOTES=`collect.testing.features`: Fixed `@Require` annotations so that features implied by absent features are not also required to be absent. PiperOrigin-RevId: 682731934 --- .../collect/testing/features/FeatureUtil.java | 37 +- .../testing/features/FeatureUtilTest.java | 409 ++++++++++-------- .../collect/testing/features/FeatureUtil.java | 37 +- .../testing/features/FeatureUtilTest.java | 409 ++++++++++-------- 4 files changed, 494 insertions(+), 398 deletions(-) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/features/FeatureUtil.java b/android/guava-testlib/src/com/google/common/collect/testing/features/FeatureUtil.java index ce5b73f4774f..660dd86d3bdf 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/features/FeatureUtil.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/features/FeatureUtil.java @@ -16,15 +16,17 @@ package com.google.common.collect.testing.features; +import static com.google.common.collect.testing.Helpers.copyToSet; +import static java.util.Collections.disjoint; +import static java.util.Collections.unmodifiableList; + import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; @@ -39,9 +41,9 @@ * @author George van den Driessche */ @GwtIncompatible -public class FeatureUtil { +public final class FeatureUtil { /** A cache of annotated objects (typically a Class or Method) to its set of annotations. */ - private static Map> annotationCache = new HashMap<>(); + private static final Map> annotationCache = new HashMap<>(); private static final Map, TesterRequirements> classTesterRequirementsCache = new HashMap<>(); @@ -181,16 +183,14 @@ private static TesterRequirements buildTesterRequirements(Annotation testerAnnot Feature[] presentFeatures; Feature[] absentFeatures; try { - presentFeatures = (Feature[]) annotationClass.getMethod("value").invoke(testerAnnotation); - absentFeatures = (Feature[]) annotationClass.getMethod("absent").invoke(testerAnnotation); + presentFeatures = (Feature[]) annotationClass.getMethod("value").invoke(testerAnnotation); + absentFeatures = (Feature[]) annotationClass.getMethod("absent").invoke(testerAnnotation); } catch (Exception e) { throw new IllegalArgumentException("Error extracting features from tester annotation.", e); } - Set> allPresentFeatures = - addImpliedFeatures(Helpers.>copyToSet(presentFeatures)); - Set> allAbsentFeatures = - addImpliedFeatures(Helpers.>copyToSet(absentFeatures)); - if (!Collections.disjoint(allPresentFeatures, allAbsentFeatures)) { + Set> allPresentFeatures = addImpliedFeatures(copyToSet(presentFeatures)); + Set> allAbsentFeatures = copyToSet(absentFeatures); + if (!disjoint(allPresentFeatures, allAbsentFeatures)) { throw new ConflictingRequirementsException( "Annotation explicitly or " + "implicitly requires one or more features to be both present " @@ -239,7 +239,7 @@ public static Iterable getTesterAnnotations(AnnotatedElement classOr annotations.add(a); } } - annotations = Collections.unmodifiableList(annotations); + annotations = unmodifiableList(annotations); annotationCache.put(classOrMethod, annotations); } return annotations; @@ -279,7 +279,7 @@ private static void checkConflict( Set> newFeatures, Object source) throws ConflictingRequirementsException { - if (!Collections.disjoint(newFeatures, earlierFeatures)) { + if (!disjoint(newFeatures, earlierFeatures)) { throw new ConflictingRequirementsException( String.format( Locale.ROOT, @@ -292,10 +292,17 @@ private static void checkConflict( } } - /** Construct a new {@link java.util.Set} that is the intersection of the given sets. */ + /** + * Construct a new {@link java.util.Set} that is the intersection of the given sets. + * + * @deprecated Use {@link com.google.common.collect.Sets#intersection(Set, Set)} instead. + */ + @Deprecated public static Set intersection(Set set1, Set set2) { - Set result = Helpers.copyToSet(set1); + Set result = copyToSet(set1); result.retainAll(set2); return result; } + + private FeatureUtil() {} } diff --git a/android/guava-testlib/test/com/google/common/collect/testing/features/FeatureUtilTest.java b/android/guava-testlib/test/com/google/common/collect/testing/features/FeatureUtilTest.java index 9fda91f1215c..a77820938b37 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/features/FeatureUtilTest.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/features/FeatureUtilTest.java @@ -16,16 +16,29 @@ package com.google.common.collect.testing.features; +import static com.google.common.collect.Sets.newHashSet; +import static com.google.common.collect.testing.features.FeatureEnumTest.assertGoodFeatureEnum; +import static com.google.common.collect.testing.features.FeatureUtil.addImpliedFeatures; +import static com.google.common.collect.testing.features.FeatureUtil.buildDeclaredTesterRequirements; +import static com.google.common.collect.testing.features.FeatureUtil.buildTesterRequirements; +import static com.google.common.collect.testing.features.FeatureUtil.getTesterAnnotations; +import static com.google.common.collect.testing.features.FeatureUtil.impliedFeatures; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.BAR; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.FOO; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.IMPLIES_BAR; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.IMPLIES_FOO; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.IMPLIES_IMPLIES_FOO; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR; import static com.google.common.truth.Truth.assertThat; +import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; +import com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.NotTesterAnnotation; +import com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.Require; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; -import java.util.Collections; import java.util.Set; import junit.framework.TestCase; @@ -33,239 +46,267 @@ * @author George van den Driessche */ public class FeatureUtilTest extends TestCase { - interface ExampleBaseInterface { - void behave(); - } - - interface ExampleDerivedInterface extends ExampleBaseInterface { - void misbehave(); - } - - enum ExampleBaseFeature implements Feature { - BASE_FEATURE_1, - BASE_FEATURE_2; + enum ExampleFeature implements Feature { + FOO, + IMPLIES_FOO, + IMPLIES_IMPLIES_FOO, + BAR, + IMPLIES_BAR, + IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR; @Override - public Set> getImpliedFeatures() { - return Collections.emptySet(); + public ImmutableSet> getImpliedFeatures() { + switch (this) { + case IMPLIES_FOO: + return ImmutableSet.of(FOO); + case IMPLIES_IMPLIES_FOO: + return ImmutableSet.of(IMPLIES_FOO); + case IMPLIES_BAR: + return ImmutableSet.of(BAR); + case IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR: + return ImmutableSet.of(IMPLIES_FOO, IMPLIES_BAR); + default: + return ImmutableSet.of(); + } } - @Retention(RetentionPolicy.RUNTIME) + @Retention(RUNTIME) @Inherited @TesterAnnotation @interface Require { - ExampleBaseFeature[] value() default {}; + ExampleFeature[] value() default {}; - ExampleBaseFeature[] absent() default {}; + ExampleFeature[] absent() default {}; + } + + @Retention(RUNTIME) + @Inherited + @interface NotTesterAnnotation { + ExampleFeature[] value() default {}; + + ExampleFeature[] absent() default {}; } } - enum ExampleDerivedFeature implements Feature { - DERIVED_FEATURE_1, - DERIVED_FEATURE_2(ExampleBaseFeature.BASE_FEATURE_1), - DERIVED_FEATURE_3, + public void testTestFeatureEnums() { + // Haha! Let's test our own test rig! + assertGoodFeatureEnum(ExampleFeature.class); + } - COMPOUND_DERIVED_FEATURE( - DERIVED_FEATURE_1, DERIVED_FEATURE_2, ExampleBaseFeature.BASE_FEATURE_2); + public void testAddImpliedFeatures_returnsSameSetInstance() { + Set> features = newHashSet(FOO); + assertThat(addImpliedFeatures(features)).isSameInstanceAs(features); + } - private Set> implied; + public void testAddImpliedFeatures_addsImpliedFeatures() { + assertThat(addImpliedFeatures(newHashSet(FOO))).containsExactly(FOO); - ExampleDerivedFeature(Feature... implied) { - this.implied = ImmutableSet.copyOf(implied); - } + assertThat(addImpliedFeatures(newHashSet(IMPLIES_IMPLIES_FOO))) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO); - @Override - public Set> getImpliedFeatures() { - return implied; - } + assertThat(addImpliedFeatures(newHashSet(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR))) + .containsExactly(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR, IMPLIES_FOO, FOO, IMPLIES_BAR, BAR); + } - @Retention(RetentionPolicy.RUNTIME) - @Inherited - @TesterAnnotation - @interface Require { - ExampleDerivedFeature[] value() default {}; + public void testImpliedFeatures_returnsNewSetInstance() { + Set> features = newHashSet(IMPLIES_FOO); + assertThat(impliedFeatures(features)).isNotSameInstanceAs(features); + } - ExampleDerivedFeature[] absent() default {}; - } + public void testImpliedFeatures_returnsImpliedFeatures() { + assertThat(impliedFeatures(newHashSet(FOO))).isEmpty(); + + assertThat(impliedFeatures(newHashSet(IMPLIES_IMPLIES_FOO))).containsExactly(IMPLIES_FOO, FOO); + + assertThat(impliedFeatures(newHashSet(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR))) + .containsExactly(IMPLIES_FOO, FOO, IMPLIES_BAR, BAR); } - @Retention(RetentionPolicy.RUNTIME) - @interface NonTesterAnnotation {} + public void testBuildTesterRequirements_class_notAnnotated() throws Exception { + class Tester {} - @ExampleBaseFeature.Require({ExampleBaseFeature.BASE_FEATURE_1}) - private abstract static class ExampleBaseInterfaceTester extends TestCase { - protected final void doNotActuallyRunThis() { - fail("Nobody's meant to actually run this!"); - } + TesterRequirements requirements = buildTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()).isEmpty(); + assertThat(requirements.getAbsentFeatures()).isEmpty(); } - @AndroidIncompatible // Android attempts to run directly - @NonTesterAnnotation - @ExampleDerivedFeature.Require({ExampleDerivedFeature.DERIVED_FEATURE_2}) - private static class ExampleDerivedInterfaceTester extends ExampleBaseInterfaceTester { - // Exists to test that our framework doesn't run it: - @SuppressWarnings("unused") - @ExampleDerivedFeature.Require({ - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2 - }) - public void testRequiringTwoExplicitDerivedFeatures() throws Exception { - doNotActuallyRunThis(); - } + public void testBuildTesterRequirements_class_empty() throws Exception { + @Require + class Tester {} - // Exists to test that our framework doesn't run it: - @SuppressWarnings("unused") - @ExampleDerivedFeature.Require({ - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_3 - }) - public void testRequiringAllThreeDerivedFeatures() { - doNotActuallyRunThis(); - } + TesterRequirements requirements = buildTesterRequirements(Tester.class); - // Exists to test that our framework doesn't run it: - @SuppressWarnings("unused") - @ExampleBaseFeature.Require(absent = {ExampleBaseFeature.BASE_FEATURE_1}) - public void testRequiringConflictingFeatures() throws Exception { - doNotActuallyRunThis(); - } + assertThat(requirements.getPresentFeatures()).isEmpty(); + assertThat(requirements.getAbsentFeatures()).isEmpty(); } - @ExampleDerivedFeature.Require(absent = {ExampleDerivedFeature.DERIVED_FEATURE_2}) - private static class ConflictingRequirementsExampleDerivedInterfaceTester - extends ExampleBaseInterfaceTester {} + public void testBuildTesterRequirements_class_present() throws Exception { + @Require({IMPLIES_IMPLIES_FOO, IMPLIES_BAR}) + class Tester {} - public void testTestFeatureEnums() throws Exception { - // Haha! Let's test our own test rig! - FeatureEnumTest.assertGoodFeatureEnum(FeatureUtilTest.ExampleBaseFeature.class); - FeatureEnumTest.assertGoodFeatureEnum(FeatureUtilTest.ExampleDerivedFeature.class); + TesterRequirements requirements = buildTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO, IMPLIES_BAR, BAR); + assertThat(requirements.getAbsentFeatures()).isEmpty(); } - public void testAddImpliedFeatures_returnsSameSetInstance() throws Exception { - Set> features = Sets.>newHashSet(ExampleBaseFeature.BASE_FEATURE_1); - assertSame(features, FeatureUtil.addImpliedFeatures(features)); + public void testBuildTesterRequirements_class_absent() throws Exception { + @Require(absent = {IMPLIES_IMPLIES_FOO, IMPLIES_BAR}) + class Tester {} + + TesterRequirements requirements = buildTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()).isEmpty(); + assertThat(requirements.getAbsentFeatures()).containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_BAR); } - public void testAddImpliedFeatures_addsImpliedFeatures() throws Exception { - Set> features; - - features = Sets.>newHashSet(ExampleDerivedFeature.DERIVED_FEATURE_1); - assertThat(FeatureUtil.addImpliedFeatures(features)) - .contains(ExampleDerivedFeature.DERIVED_FEATURE_1); - - features = Sets.>newHashSet(ExampleDerivedFeature.DERIVED_FEATURE_2); - assertThat(FeatureUtil.addImpliedFeatures(features)) - .containsExactly( - ExampleDerivedFeature.DERIVED_FEATURE_2, ExampleBaseFeature.BASE_FEATURE_1); - - features = Sets.>newHashSet(ExampleDerivedFeature.COMPOUND_DERIVED_FEATURE); - assertThat(FeatureUtil.addImpliedFeatures(features)) - .containsExactly( - ExampleDerivedFeature.COMPOUND_DERIVED_FEATURE, - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2, - ExampleBaseFeature.BASE_FEATURE_1, - ExampleBaseFeature.BASE_FEATURE_2); + public void testBuildTesterRequirements_class_present_and_absent() throws Exception { + @Require(value = IMPLIES_FOO, absent = IMPLIES_IMPLIES_FOO) + class Tester {} + + TesterRequirements requirements = buildTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()).containsExactly(IMPLIES_FOO, FOO); + assertThat(requirements.getAbsentFeatures()).containsExactly(IMPLIES_IMPLIES_FOO); } - public void testImpliedFeatures_returnsNewSetInstance() throws Exception { - Set> features = Sets.>newHashSet(ExampleBaseFeature.BASE_FEATURE_1); - assertNotSame(features, FeatureUtil.impliedFeatures(features)); + public void testBuildTesterRequirements_class_present_method_present() throws Exception { + @Require(IMPLIES_BAR) + class Tester { + @Require(IMPLIES_IMPLIES_FOO) + public void test() {} + } + + TesterRequirements requirements = buildTesterRequirements(Tester.class.getMethod("test")); + + assertThat(requirements.getPresentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO, IMPLIES_BAR, BAR); + assertThat(requirements.getAbsentFeatures()).isEmpty(); } - public void testImpliedFeatures_returnsImpliedFeatures() throws Exception { - Set> features; + public void testBuildTesterRequirements_class_absent_method_absent() throws Exception { + @Require(absent = IMPLIES_BAR) + class Tester { + @Require(absent = IMPLIES_IMPLIES_FOO) + public void test() {} + } + + TesterRequirements requirements = buildTesterRequirements(Tester.class.getMethod("test")); + + assertThat(requirements.getPresentFeatures()).isEmpty(); + assertThat(requirements.getAbsentFeatures()).containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_BAR); + } - features = Sets.>newHashSet(ExampleDerivedFeature.DERIVED_FEATURE_1); - assertTrue(FeatureUtil.impliedFeatures(features).isEmpty()); + public void testBuildTesterRequirements_class_present_method_absent() throws Exception { + @Require(IMPLIES_IMPLIES_FOO) + class Tester { + @Require(absent = IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR) + public void test() {} + } - features = Sets.>newHashSet(ExampleDerivedFeature.DERIVED_FEATURE_2); - assertThat(FeatureUtil.impliedFeatures(features)).contains(ExampleBaseFeature.BASE_FEATURE_1); + TesterRequirements requirements = buildTesterRequirements(Tester.class.getMethod("test")); - features = Sets.>newHashSet(ExampleDerivedFeature.COMPOUND_DERIVED_FEATURE); - assertThat(FeatureUtil.impliedFeatures(features)) - .containsExactly( - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2, - ExampleBaseFeature.BASE_FEATURE_1, - ExampleBaseFeature.BASE_FEATURE_2); + assertThat(requirements.getPresentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO); + assertThat(requirements.getAbsentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists - public void testBuildTesterRequirements_class() throws Exception { - assertEquals( - FeatureUtil.buildTesterRequirements(ExampleBaseInterfaceTester.class), - new TesterRequirements( - Sets.>newHashSet(ExampleBaseFeature.BASE_FEATURE_1), - Collections.>emptySet())); - - assertEquals( - FeatureUtil.buildTesterRequirements(ExampleDerivedInterfaceTester.class), - new TesterRequirements( - Sets.>newHashSet( - ExampleBaseFeature.BASE_FEATURE_1, ExampleDerivedFeature.DERIVED_FEATURE_2), - Collections.>emptySet())); + public void testBuildTesterRequirements_class_absent_method_present() throws Exception { + @Require(absent = IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR) + class Tester { + @Require(IMPLIES_IMPLIES_FOO) + public void test() {} + } + + TesterRequirements requirements = buildTesterRequirements(Tester.class.getMethod("test")); + + assertThat(requirements.getPresentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO); + assertThat(requirements.getAbsentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists - public void testBuildTesterRequirements_method() throws Exception { - assertEquals( - FeatureUtil.buildTesterRequirements( - ExampleDerivedInterfaceTester.class.getMethod( - "testRequiringTwoExplicitDerivedFeatures")), - new TesterRequirements( - Sets.>newHashSet( - ExampleBaseFeature.BASE_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2), - Collections.>emptySet())); - assertEquals( - FeatureUtil.buildTesterRequirements( - ExampleDerivedInterfaceTester.class.getMethod("testRequiringAllThreeDerivedFeatures")), - new TesterRequirements( - Sets.>newHashSet( - ExampleBaseFeature.BASE_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2, - ExampleDerivedFeature.DERIVED_FEATURE_3), - Collections.>emptySet())); + public void testBuildTesterRequirements_classClassConflict() { + @Require(value = FOO, absent = FOO) + class Tester {} + + ConflictingRequirementsException e = + assertThrows( + ConflictingRequirementsException.class, () -> buildTesterRequirements(Tester.class)); + assertThat(e.getConflicts()).containsExactly(FOO); + assertThat(e.getSource()).isEqualTo(Tester.class.getAnnotation(Require.class)); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists - public void testBuildTesterRequirements_classClassConflict() throws Exception { + public void testBuildTesterRequirements_classClassConflict_inherited() { + @Require(FOO) + abstract class BaseTester {} + @Require(absent = FOO) + class Tester extends BaseTester {} + ConflictingRequirementsException e = assertThrows( - ConflictingRequirementsException.class, - () -> - FeatureUtil.buildTesterRequirements( - ConflictingRequirementsExampleDerivedInterfaceTester.class)); - assertThat(e.getConflicts()).contains(ExampleBaseFeature.BASE_FEATURE_1); - assertEquals(ConflictingRequirementsExampleDerivedInterfaceTester.class, e.getSource()); + ConflictingRequirementsException.class, () -> buildTesterRequirements(Tester.class)); + assertThat(e.getConflicts()).containsExactly(FOO); + assertThat(e.getSource()).isEqualTo(Tester.class); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists - public void testBuildTesterRequirements_methodClassConflict() throws Exception { - final Method method = - ExampleDerivedInterfaceTester.class.getMethod("testRequiringConflictingFeatures"); + public void testBuildTesterRequirements_classClassConflict_implied() { + @Require(value = IMPLIES_FOO, absent = FOO) + class Tester {} + ConflictingRequirementsException e = assertThrows( - ConflictingRequirementsException.class, - () -> FeatureUtil.buildTesterRequirements(method)); - assertThat(e.getConflicts()).contains(ExampleBaseFeature.BASE_FEATURE_1); - assertEquals(method, e.getSource()); + ConflictingRequirementsException.class, () -> buildTesterRequirements(Tester.class)); + assertThat(e.getConflicts()).containsExactly(FOO); + assertThat(e.getSource()).isEqualTo(Tester.class.getAnnotation(Require.class)); + } + + public void testBuildTesterRequirements_methodClassConflict() throws Exception { + @Require(IMPLIES_FOO) + class Tester { + @Require(absent = FOO) + public void test() {} + } + + Method method = Tester.class.getMethod("test"); + ConflictingRequirementsException e = + assertThrows(ConflictingRequirementsException.class, () -> buildTesterRequirements(method)); + assertThat(e.getConflicts()).containsExactly(FOO); + assertThat(e.getSource()).isEqualTo(method); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists public void testBuildDeclaredTesterRequirements() throws Exception { - assertEquals( - FeatureUtil.buildDeclaredTesterRequirements( - ExampleDerivedInterfaceTester.class.getMethod( - "testRequiringTwoExplicitDerivedFeatures")), - new TesterRequirements( - FeatureUtil.addImpliedFeatures( - Sets.>newHashSet( - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2)), - Collections.>emptySet())); + @Require(IMPLIES_FOO) + abstract class BaseTester {} + @Require(IMPLIES_BAR) + class Tester extends BaseTester {} + + TesterRequirements requirements = buildDeclaredTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()).containsExactly(IMPLIES_BAR, BAR); + assertThat(requirements.getAbsentFeatures()).isEmpty(); + } + + public void testGetTesterAnnotations_class() { + @Require + @NotTesterAnnotation + class Tester {} + + assertThat(getTesterAnnotations(Tester.class)) + .containsExactly(Tester.class.getAnnotation(Require.class)); + } + + public void testGetTesterAnnotations_method() throws Exception { + class Tester { + @Require + @NotTesterAnnotation + public void test() {} + } + Method method = Tester.class.getMethod("test"); + + assertThat(getTesterAnnotations(method)).containsExactly(method.getAnnotation(Require.class)); } } diff --git a/guava-testlib/src/com/google/common/collect/testing/features/FeatureUtil.java b/guava-testlib/src/com/google/common/collect/testing/features/FeatureUtil.java index ce5b73f4774f..660dd86d3bdf 100644 --- a/guava-testlib/src/com/google/common/collect/testing/features/FeatureUtil.java +++ b/guava-testlib/src/com/google/common/collect/testing/features/FeatureUtil.java @@ -16,15 +16,17 @@ package com.google.common.collect.testing.features; +import static com.google.common.collect.testing.Helpers.copyToSet; +import static java.util.Collections.disjoint; +import static java.util.Collections.unmodifiableList; + import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; @@ -39,9 +41,9 @@ * @author George van den Driessche */ @GwtIncompatible -public class FeatureUtil { +public final class FeatureUtil { /** A cache of annotated objects (typically a Class or Method) to its set of annotations. */ - private static Map> annotationCache = new HashMap<>(); + private static final Map> annotationCache = new HashMap<>(); private static final Map, TesterRequirements> classTesterRequirementsCache = new HashMap<>(); @@ -181,16 +183,14 @@ private static TesterRequirements buildTesterRequirements(Annotation testerAnnot Feature[] presentFeatures; Feature[] absentFeatures; try { - presentFeatures = (Feature[]) annotationClass.getMethod("value").invoke(testerAnnotation); - absentFeatures = (Feature[]) annotationClass.getMethod("absent").invoke(testerAnnotation); + presentFeatures = (Feature[]) annotationClass.getMethod("value").invoke(testerAnnotation); + absentFeatures = (Feature[]) annotationClass.getMethod("absent").invoke(testerAnnotation); } catch (Exception e) { throw new IllegalArgumentException("Error extracting features from tester annotation.", e); } - Set> allPresentFeatures = - addImpliedFeatures(Helpers.>copyToSet(presentFeatures)); - Set> allAbsentFeatures = - addImpliedFeatures(Helpers.>copyToSet(absentFeatures)); - if (!Collections.disjoint(allPresentFeatures, allAbsentFeatures)) { + Set> allPresentFeatures = addImpliedFeatures(copyToSet(presentFeatures)); + Set> allAbsentFeatures = copyToSet(absentFeatures); + if (!disjoint(allPresentFeatures, allAbsentFeatures)) { throw new ConflictingRequirementsException( "Annotation explicitly or " + "implicitly requires one or more features to be both present " @@ -239,7 +239,7 @@ public static Iterable getTesterAnnotations(AnnotatedElement classOr annotations.add(a); } } - annotations = Collections.unmodifiableList(annotations); + annotations = unmodifiableList(annotations); annotationCache.put(classOrMethod, annotations); } return annotations; @@ -279,7 +279,7 @@ private static void checkConflict( Set> newFeatures, Object source) throws ConflictingRequirementsException { - if (!Collections.disjoint(newFeatures, earlierFeatures)) { + if (!disjoint(newFeatures, earlierFeatures)) { throw new ConflictingRequirementsException( String.format( Locale.ROOT, @@ -292,10 +292,17 @@ private static void checkConflict( } } - /** Construct a new {@link java.util.Set} that is the intersection of the given sets. */ + /** + * Construct a new {@link java.util.Set} that is the intersection of the given sets. + * + * @deprecated Use {@link com.google.common.collect.Sets#intersection(Set, Set)} instead. + */ + @Deprecated public static Set intersection(Set set1, Set set2) { - Set result = Helpers.copyToSet(set1); + Set result = copyToSet(set1); result.retainAll(set2); return result; } + + private FeatureUtil() {} } diff --git a/guava-testlib/test/com/google/common/collect/testing/features/FeatureUtilTest.java b/guava-testlib/test/com/google/common/collect/testing/features/FeatureUtilTest.java index 9fda91f1215c..a77820938b37 100644 --- a/guava-testlib/test/com/google/common/collect/testing/features/FeatureUtilTest.java +++ b/guava-testlib/test/com/google/common/collect/testing/features/FeatureUtilTest.java @@ -16,16 +16,29 @@ package com.google.common.collect.testing.features; +import static com.google.common.collect.Sets.newHashSet; +import static com.google.common.collect.testing.features.FeatureEnumTest.assertGoodFeatureEnum; +import static com.google.common.collect.testing.features.FeatureUtil.addImpliedFeatures; +import static com.google.common.collect.testing.features.FeatureUtil.buildDeclaredTesterRequirements; +import static com.google.common.collect.testing.features.FeatureUtil.buildTesterRequirements; +import static com.google.common.collect.testing.features.FeatureUtil.getTesterAnnotations; +import static com.google.common.collect.testing.features.FeatureUtil.impliedFeatures; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.BAR; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.FOO; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.IMPLIES_BAR; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.IMPLIES_FOO; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.IMPLIES_IMPLIES_FOO; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR; import static com.google.common.truth.Truth.assertThat; +import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; +import com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.NotTesterAnnotation; +import com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.Require; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; -import java.util.Collections; import java.util.Set; import junit.framework.TestCase; @@ -33,239 +46,267 @@ * @author George van den Driessche */ public class FeatureUtilTest extends TestCase { - interface ExampleBaseInterface { - void behave(); - } - - interface ExampleDerivedInterface extends ExampleBaseInterface { - void misbehave(); - } - - enum ExampleBaseFeature implements Feature { - BASE_FEATURE_1, - BASE_FEATURE_2; + enum ExampleFeature implements Feature { + FOO, + IMPLIES_FOO, + IMPLIES_IMPLIES_FOO, + BAR, + IMPLIES_BAR, + IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR; @Override - public Set> getImpliedFeatures() { - return Collections.emptySet(); + public ImmutableSet> getImpliedFeatures() { + switch (this) { + case IMPLIES_FOO: + return ImmutableSet.of(FOO); + case IMPLIES_IMPLIES_FOO: + return ImmutableSet.of(IMPLIES_FOO); + case IMPLIES_BAR: + return ImmutableSet.of(BAR); + case IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR: + return ImmutableSet.of(IMPLIES_FOO, IMPLIES_BAR); + default: + return ImmutableSet.of(); + } } - @Retention(RetentionPolicy.RUNTIME) + @Retention(RUNTIME) @Inherited @TesterAnnotation @interface Require { - ExampleBaseFeature[] value() default {}; + ExampleFeature[] value() default {}; - ExampleBaseFeature[] absent() default {}; + ExampleFeature[] absent() default {}; + } + + @Retention(RUNTIME) + @Inherited + @interface NotTesterAnnotation { + ExampleFeature[] value() default {}; + + ExampleFeature[] absent() default {}; } } - enum ExampleDerivedFeature implements Feature { - DERIVED_FEATURE_1, - DERIVED_FEATURE_2(ExampleBaseFeature.BASE_FEATURE_1), - DERIVED_FEATURE_3, + public void testTestFeatureEnums() { + // Haha! Let's test our own test rig! + assertGoodFeatureEnum(ExampleFeature.class); + } - COMPOUND_DERIVED_FEATURE( - DERIVED_FEATURE_1, DERIVED_FEATURE_2, ExampleBaseFeature.BASE_FEATURE_2); + public void testAddImpliedFeatures_returnsSameSetInstance() { + Set> features = newHashSet(FOO); + assertThat(addImpliedFeatures(features)).isSameInstanceAs(features); + } - private Set> implied; + public void testAddImpliedFeatures_addsImpliedFeatures() { + assertThat(addImpliedFeatures(newHashSet(FOO))).containsExactly(FOO); - ExampleDerivedFeature(Feature... implied) { - this.implied = ImmutableSet.copyOf(implied); - } + assertThat(addImpliedFeatures(newHashSet(IMPLIES_IMPLIES_FOO))) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO); - @Override - public Set> getImpliedFeatures() { - return implied; - } + assertThat(addImpliedFeatures(newHashSet(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR))) + .containsExactly(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR, IMPLIES_FOO, FOO, IMPLIES_BAR, BAR); + } - @Retention(RetentionPolicy.RUNTIME) - @Inherited - @TesterAnnotation - @interface Require { - ExampleDerivedFeature[] value() default {}; + public void testImpliedFeatures_returnsNewSetInstance() { + Set> features = newHashSet(IMPLIES_FOO); + assertThat(impliedFeatures(features)).isNotSameInstanceAs(features); + } - ExampleDerivedFeature[] absent() default {}; - } + public void testImpliedFeatures_returnsImpliedFeatures() { + assertThat(impliedFeatures(newHashSet(FOO))).isEmpty(); + + assertThat(impliedFeatures(newHashSet(IMPLIES_IMPLIES_FOO))).containsExactly(IMPLIES_FOO, FOO); + + assertThat(impliedFeatures(newHashSet(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR))) + .containsExactly(IMPLIES_FOO, FOO, IMPLIES_BAR, BAR); } - @Retention(RetentionPolicy.RUNTIME) - @interface NonTesterAnnotation {} + public void testBuildTesterRequirements_class_notAnnotated() throws Exception { + class Tester {} - @ExampleBaseFeature.Require({ExampleBaseFeature.BASE_FEATURE_1}) - private abstract static class ExampleBaseInterfaceTester extends TestCase { - protected final void doNotActuallyRunThis() { - fail("Nobody's meant to actually run this!"); - } + TesterRequirements requirements = buildTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()).isEmpty(); + assertThat(requirements.getAbsentFeatures()).isEmpty(); } - @AndroidIncompatible // Android attempts to run directly - @NonTesterAnnotation - @ExampleDerivedFeature.Require({ExampleDerivedFeature.DERIVED_FEATURE_2}) - private static class ExampleDerivedInterfaceTester extends ExampleBaseInterfaceTester { - // Exists to test that our framework doesn't run it: - @SuppressWarnings("unused") - @ExampleDerivedFeature.Require({ - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2 - }) - public void testRequiringTwoExplicitDerivedFeatures() throws Exception { - doNotActuallyRunThis(); - } + public void testBuildTesterRequirements_class_empty() throws Exception { + @Require + class Tester {} - // Exists to test that our framework doesn't run it: - @SuppressWarnings("unused") - @ExampleDerivedFeature.Require({ - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_3 - }) - public void testRequiringAllThreeDerivedFeatures() { - doNotActuallyRunThis(); - } + TesterRequirements requirements = buildTesterRequirements(Tester.class); - // Exists to test that our framework doesn't run it: - @SuppressWarnings("unused") - @ExampleBaseFeature.Require(absent = {ExampleBaseFeature.BASE_FEATURE_1}) - public void testRequiringConflictingFeatures() throws Exception { - doNotActuallyRunThis(); - } + assertThat(requirements.getPresentFeatures()).isEmpty(); + assertThat(requirements.getAbsentFeatures()).isEmpty(); } - @ExampleDerivedFeature.Require(absent = {ExampleDerivedFeature.DERIVED_FEATURE_2}) - private static class ConflictingRequirementsExampleDerivedInterfaceTester - extends ExampleBaseInterfaceTester {} + public void testBuildTesterRequirements_class_present() throws Exception { + @Require({IMPLIES_IMPLIES_FOO, IMPLIES_BAR}) + class Tester {} - public void testTestFeatureEnums() throws Exception { - // Haha! Let's test our own test rig! - FeatureEnumTest.assertGoodFeatureEnum(FeatureUtilTest.ExampleBaseFeature.class); - FeatureEnumTest.assertGoodFeatureEnum(FeatureUtilTest.ExampleDerivedFeature.class); + TesterRequirements requirements = buildTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO, IMPLIES_BAR, BAR); + assertThat(requirements.getAbsentFeatures()).isEmpty(); } - public void testAddImpliedFeatures_returnsSameSetInstance() throws Exception { - Set> features = Sets.>newHashSet(ExampleBaseFeature.BASE_FEATURE_1); - assertSame(features, FeatureUtil.addImpliedFeatures(features)); + public void testBuildTesterRequirements_class_absent() throws Exception { + @Require(absent = {IMPLIES_IMPLIES_FOO, IMPLIES_BAR}) + class Tester {} + + TesterRequirements requirements = buildTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()).isEmpty(); + assertThat(requirements.getAbsentFeatures()).containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_BAR); } - public void testAddImpliedFeatures_addsImpliedFeatures() throws Exception { - Set> features; - - features = Sets.>newHashSet(ExampleDerivedFeature.DERIVED_FEATURE_1); - assertThat(FeatureUtil.addImpliedFeatures(features)) - .contains(ExampleDerivedFeature.DERIVED_FEATURE_1); - - features = Sets.>newHashSet(ExampleDerivedFeature.DERIVED_FEATURE_2); - assertThat(FeatureUtil.addImpliedFeatures(features)) - .containsExactly( - ExampleDerivedFeature.DERIVED_FEATURE_2, ExampleBaseFeature.BASE_FEATURE_1); - - features = Sets.>newHashSet(ExampleDerivedFeature.COMPOUND_DERIVED_FEATURE); - assertThat(FeatureUtil.addImpliedFeatures(features)) - .containsExactly( - ExampleDerivedFeature.COMPOUND_DERIVED_FEATURE, - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2, - ExampleBaseFeature.BASE_FEATURE_1, - ExampleBaseFeature.BASE_FEATURE_2); + public void testBuildTesterRequirements_class_present_and_absent() throws Exception { + @Require(value = IMPLIES_FOO, absent = IMPLIES_IMPLIES_FOO) + class Tester {} + + TesterRequirements requirements = buildTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()).containsExactly(IMPLIES_FOO, FOO); + assertThat(requirements.getAbsentFeatures()).containsExactly(IMPLIES_IMPLIES_FOO); } - public void testImpliedFeatures_returnsNewSetInstance() throws Exception { - Set> features = Sets.>newHashSet(ExampleBaseFeature.BASE_FEATURE_1); - assertNotSame(features, FeatureUtil.impliedFeatures(features)); + public void testBuildTesterRequirements_class_present_method_present() throws Exception { + @Require(IMPLIES_BAR) + class Tester { + @Require(IMPLIES_IMPLIES_FOO) + public void test() {} + } + + TesterRequirements requirements = buildTesterRequirements(Tester.class.getMethod("test")); + + assertThat(requirements.getPresentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO, IMPLIES_BAR, BAR); + assertThat(requirements.getAbsentFeatures()).isEmpty(); } - public void testImpliedFeatures_returnsImpliedFeatures() throws Exception { - Set> features; + public void testBuildTesterRequirements_class_absent_method_absent() throws Exception { + @Require(absent = IMPLIES_BAR) + class Tester { + @Require(absent = IMPLIES_IMPLIES_FOO) + public void test() {} + } + + TesterRequirements requirements = buildTesterRequirements(Tester.class.getMethod("test")); + + assertThat(requirements.getPresentFeatures()).isEmpty(); + assertThat(requirements.getAbsentFeatures()).containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_BAR); + } - features = Sets.>newHashSet(ExampleDerivedFeature.DERIVED_FEATURE_1); - assertTrue(FeatureUtil.impliedFeatures(features).isEmpty()); + public void testBuildTesterRequirements_class_present_method_absent() throws Exception { + @Require(IMPLIES_IMPLIES_FOO) + class Tester { + @Require(absent = IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR) + public void test() {} + } - features = Sets.>newHashSet(ExampleDerivedFeature.DERIVED_FEATURE_2); - assertThat(FeatureUtil.impliedFeatures(features)).contains(ExampleBaseFeature.BASE_FEATURE_1); + TesterRequirements requirements = buildTesterRequirements(Tester.class.getMethod("test")); - features = Sets.>newHashSet(ExampleDerivedFeature.COMPOUND_DERIVED_FEATURE); - assertThat(FeatureUtil.impliedFeatures(features)) - .containsExactly( - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2, - ExampleBaseFeature.BASE_FEATURE_1, - ExampleBaseFeature.BASE_FEATURE_2); + assertThat(requirements.getPresentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO); + assertThat(requirements.getAbsentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists - public void testBuildTesterRequirements_class() throws Exception { - assertEquals( - FeatureUtil.buildTesterRequirements(ExampleBaseInterfaceTester.class), - new TesterRequirements( - Sets.>newHashSet(ExampleBaseFeature.BASE_FEATURE_1), - Collections.>emptySet())); - - assertEquals( - FeatureUtil.buildTesterRequirements(ExampleDerivedInterfaceTester.class), - new TesterRequirements( - Sets.>newHashSet( - ExampleBaseFeature.BASE_FEATURE_1, ExampleDerivedFeature.DERIVED_FEATURE_2), - Collections.>emptySet())); + public void testBuildTesterRequirements_class_absent_method_present() throws Exception { + @Require(absent = IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR) + class Tester { + @Require(IMPLIES_IMPLIES_FOO) + public void test() {} + } + + TesterRequirements requirements = buildTesterRequirements(Tester.class.getMethod("test")); + + assertThat(requirements.getPresentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO); + assertThat(requirements.getAbsentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists - public void testBuildTesterRequirements_method() throws Exception { - assertEquals( - FeatureUtil.buildTesterRequirements( - ExampleDerivedInterfaceTester.class.getMethod( - "testRequiringTwoExplicitDerivedFeatures")), - new TesterRequirements( - Sets.>newHashSet( - ExampleBaseFeature.BASE_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2), - Collections.>emptySet())); - assertEquals( - FeatureUtil.buildTesterRequirements( - ExampleDerivedInterfaceTester.class.getMethod("testRequiringAllThreeDerivedFeatures")), - new TesterRequirements( - Sets.>newHashSet( - ExampleBaseFeature.BASE_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2, - ExampleDerivedFeature.DERIVED_FEATURE_3), - Collections.>emptySet())); + public void testBuildTesterRequirements_classClassConflict() { + @Require(value = FOO, absent = FOO) + class Tester {} + + ConflictingRequirementsException e = + assertThrows( + ConflictingRequirementsException.class, () -> buildTesterRequirements(Tester.class)); + assertThat(e.getConflicts()).containsExactly(FOO); + assertThat(e.getSource()).isEqualTo(Tester.class.getAnnotation(Require.class)); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists - public void testBuildTesterRequirements_classClassConflict() throws Exception { + public void testBuildTesterRequirements_classClassConflict_inherited() { + @Require(FOO) + abstract class BaseTester {} + @Require(absent = FOO) + class Tester extends BaseTester {} + ConflictingRequirementsException e = assertThrows( - ConflictingRequirementsException.class, - () -> - FeatureUtil.buildTesterRequirements( - ConflictingRequirementsExampleDerivedInterfaceTester.class)); - assertThat(e.getConflicts()).contains(ExampleBaseFeature.BASE_FEATURE_1); - assertEquals(ConflictingRequirementsExampleDerivedInterfaceTester.class, e.getSource()); + ConflictingRequirementsException.class, () -> buildTesterRequirements(Tester.class)); + assertThat(e.getConflicts()).containsExactly(FOO); + assertThat(e.getSource()).isEqualTo(Tester.class); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists - public void testBuildTesterRequirements_methodClassConflict() throws Exception { - final Method method = - ExampleDerivedInterfaceTester.class.getMethod("testRequiringConflictingFeatures"); + public void testBuildTesterRequirements_classClassConflict_implied() { + @Require(value = IMPLIES_FOO, absent = FOO) + class Tester {} + ConflictingRequirementsException e = assertThrows( - ConflictingRequirementsException.class, - () -> FeatureUtil.buildTesterRequirements(method)); - assertThat(e.getConflicts()).contains(ExampleBaseFeature.BASE_FEATURE_1); - assertEquals(method, e.getSource()); + ConflictingRequirementsException.class, () -> buildTesterRequirements(Tester.class)); + assertThat(e.getConflicts()).containsExactly(FOO); + assertThat(e.getSource()).isEqualTo(Tester.class.getAnnotation(Require.class)); + } + + public void testBuildTesterRequirements_methodClassConflict() throws Exception { + @Require(IMPLIES_FOO) + class Tester { + @Require(absent = FOO) + public void test() {} + } + + Method method = Tester.class.getMethod("test"); + ConflictingRequirementsException e = + assertThrows(ConflictingRequirementsException.class, () -> buildTesterRequirements(method)); + assertThat(e.getConflicts()).containsExactly(FOO); + assertThat(e.getSource()).isEqualTo(method); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists public void testBuildDeclaredTesterRequirements() throws Exception { - assertEquals( - FeatureUtil.buildDeclaredTesterRequirements( - ExampleDerivedInterfaceTester.class.getMethod( - "testRequiringTwoExplicitDerivedFeatures")), - new TesterRequirements( - FeatureUtil.addImpliedFeatures( - Sets.>newHashSet( - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2)), - Collections.>emptySet())); + @Require(IMPLIES_FOO) + abstract class BaseTester {} + @Require(IMPLIES_BAR) + class Tester extends BaseTester {} + + TesterRequirements requirements = buildDeclaredTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()).containsExactly(IMPLIES_BAR, BAR); + assertThat(requirements.getAbsentFeatures()).isEmpty(); + } + + public void testGetTesterAnnotations_class() { + @Require + @NotTesterAnnotation + class Tester {} + + assertThat(getTesterAnnotations(Tester.class)) + .containsExactly(Tester.class.getAnnotation(Require.class)); + } + + public void testGetTesterAnnotations_method() throws Exception { + class Tester { + @Require + @NotTesterAnnotation + public void test() {} + } + Method method = Tester.class.getMethod("test"); + + assertThat(getTesterAnnotations(method)).containsExactly(method.getAnnotation(Require.class)); } }