diff --git a/src/main/java/org/junit/experimental/categories/Categories.java b/src/main/java/org/junit/experimental/categories/Categories.java index 49a4433b5e9e..4d436853dcef 100644 --- a/src/main/java/org/junit/experimental/categories/Categories.java +++ b/src/main/java/org/junit/experimental/categories/Categories.java @@ -2,10 +2,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - +import java.util.Set; +import java.util.Collections; +import java.util.HashSet; import org.junit.runner.Description; import org.junit.runner.manipulation.Filter; import org.junit.runner.manipulation.NoTestsRemainException; @@ -17,12 +16,11 @@ * From a given set of test classes, runs only the classes and methods that are * annotated with either the category given with the @IncludeCategory * annotation, or a subtype of that category. - * + *

* Note that, for now, annotating suites with {@code @Category} has no effect. * Categories must be annotated on the direct method or class. - * + *

* Example: - * *

  * public interface FastTests {
  * }
@@ -30,33 +28,54 @@
  * public interface SlowTests {
  * }
  *
+ * public interface SmokeTests
+ * }
+ *
  * public static class A {
- *  @Test
- *  public void a() {
- *      fail();
+ *     @Test
+ *     public void a() {
+ *         fail();
+ *     }
+ *
+ *     @Category(SlowTests.class)
+ *     @Test
+ *     public void b() {
  *     }
  *
- *  @Category(SlowTests.class)
- *  @Test
- *  public void b() {
+ *     @Category({FastTests.class, SmokeTests.class})
+ *     @Test
+ *     public void c() {
  *     }
  * }
  *
- * @Category( { SlowTests.class, FastTests.class })
+ * @Category({SlowTests.class, FastTests.class})
  * public static class B {
- *  @Test
- *  public void c() {
- *
+ *     @Test
+ *     public void d() {
  *     }
  * }
  *
  * @RunWith(Categories.class)
  * @IncludeCategory(SlowTests.class)
- * @SuiteClasses( { A.class, B.class })
+ * @SuiteClasses({A.class, B.class})
  * // Note that Categories is a kind of Suite
  * public static class SlowTestSuite {
+ *     // Will run A.b and B.d, but not A.a and A.c
  * }
  * 
+ * + * Example to run multiple categories: + *
+ * @RunWith(Categories.class)
+ * @IncludeCategory({FastTests.class, SmokeTests.class})
+ * @SuiteClasses({A.class, B.class})
+ * public static class FastOrSmokeTestSuite {
+ *     // Will run A.c and B.d, but not A.b because it is not any of FastTests or SmokeTests
+ * }
+ * 
+ * + * @version 4.12 + * @see Categories at JUnit wiki */ public class Categories extends Suite { // the way filters are implemented makes this unnecessarily complicated, @@ -66,32 +85,111 @@ public class Categories extends Suite { @Retention(RetentionPolicy.RUNTIME) public @interface IncludeCategory { - public Class value(); + /** + * Determines the tests to run that are annotated with categories specified in + * the value of this annotation or their subtypes unless excluded with {@link ExcludeCategory}. + */ + public Class[] value() default {}; + + /** + * If true, runs tests annotated with any of the categories in + * {@link IncludeCategory#value()}. Otherwise, runs tests only if annotated with all of the categories. + */ + public boolean matchAny() default true; } @Retention(RetentionPolicy.RUNTIME) public @interface ExcludeCategory { - public Class value(); + /** + * Determines the tests which do not run if they are annotated with categories specified in the + * value of this annotation or their subtypes regardless of being included in {@link IncludeCategory#value()}. + */ + public Class[] value() default {}; + + /** + * If true, the tests annotated with any of the categories in {@link ExcludeCategory#value()} + * do not run. Otherwise, the tests do not run if and only if annotated with all categories. + */ + public boolean matchAny() default true; } public static class CategoryFilter extends Filter { - public static CategoryFilter include(Class categoryType) { - return new CategoryFilter(categoryType, null); + private final Set> fIncluded; + private final Set> fExcluded; + private final boolean fIncludedAny; + private final boolean fExcludedAny; + + public static CategoryFilter include(boolean matchAny, Class... categories) { + if (hasNull(categories)) { + throw new NullPointerException("has null category"); + } + return categoryFilter(matchAny, createSet(categories), true, null); } - private final Class fIncluded; + public static CategoryFilter include(Class category) { + return include(true, category); + } + + public static CategoryFilter include(Class... categories) { + return include(true, categories); + } + + public static CategoryFilter exclude(boolean matchAny, Class... categories) { + if (hasNull(categories)) { + throw new NullPointerException("has null category"); + } + return categoryFilter(true, null, matchAny, createSet(categories)); + } + + public static CategoryFilter exclude(Class category) { + return exclude(true, category); + } + + public static CategoryFilter exclude(Class... categories) { + return exclude(true, categories); + } - private final Class fExcluded; + public static CategoryFilter categoryFilter(boolean matchAnyInclusions, Set> inclusions, + boolean matchAnyExclusions, Set> exclusions) { + return new CategoryFilter(matchAnyInclusions, inclusions, matchAnyExclusions, exclusions); + } - public CategoryFilter(Class includedCategory, - Class excludedCategory) { - fIncluded = includedCategory; - fExcluded = excludedCategory; + private CategoryFilter(boolean matchAnyIncludes, Set> includes, + boolean matchAnyExcludes, Set> excludes) { + fIncludedAny= matchAnyIncludes; + fExcludedAny= matchAnyExcludes; + fIncluded= copyAndRefine(includes); + fExcluded= copyAndRefine(excludes); } + /** + * @see #toString() + */ @Override public String describe() { - return "category " + fIncluded; + return toString(); + } + + /** + * Returns string in the form "[included categories] - [excluded categories]", where both + * sets have comma separated names of categories. + * + * @return string representation for the relative complement of excluded categories set + * in the set of included categories. Examples: + *
    + *
  • "categories [all]" for all included categories and no excluded ones; + *
  • "categories [all] - [A, B]" for all included categories and given excluded ones; + *
  • "categories [A, B] - [C, D]" for given included categories and given excluded ones. + *
+ * @see Class#toString() name of category + */ + @Override public String toString() { + StringBuilder description= new StringBuilder("categories ") + .append(fIncluded.isEmpty() ? "[all]" : fIncluded); + if (!fExcluded.isEmpty()) { + description.append(" - ").append(fExcluded); + } + return description.toString(); } @Override @@ -99,82 +197,153 @@ public boolean shouldRun(Description description) { if (hasCorrectCategoryAnnotation(description)) { return true; } + for (Description each : description.getChildren()) { if (shouldRun(each)) { return true; } } + return false; } private boolean hasCorrectCategoryAnnotation(Description description) { - List> categories = categories(description); - if (categories.isEmpty()) { - return fIncluded == null; + final Set> childCategories= categories(description); + + // If a child has no categories, immediately return. + if (childCategories.isEmpty()) { + return fIncluded.isEmpty(); } - for (Class each : categories) { - if (fExcluded != null && fExcluded.isAssignableFrom(each)) { - return false; + + if (!fExcluded.isEmpty()) { + if (fExcludedAny) { + if (matchesAnyParentCategories(childCategories, fExcluded)) { + return false; + } + } else { + if (matchesAllParentCategories(childCategories, fExcluded)) { + return false; + } } } - for (Class each : categories) { - if (fIncluded == null || fIncluded.isAssignableFrom(each)) { + + if (fIncluded.isEmpty()) { + // Couldn't be excluded, and with no suite's included categories treated as should run. + return true; + } else { + if (fIncludedAny) { + return matchesAnyParentCategories(childCategories, fIncluded); + } else { + return matchesAllParentCategories(childCategories, fIncluded); + } + } + } + + /** + * @return true if at least one (any) parent category match a child, otherwise false. + * If empty parentCategories, returns false. + */ + private boolean matchesAnyParentCategories(Set> childCategories, Set> parentCategories) { + for (Class parentCategory : parentCategories) { + if (hasAssignableTo(childCategories, parentCategory)) { return true; } } return false; } - private List> categories(Description description) { - ArrayList> categories = new ArrayList>(); - categories.addAll(Arrays.asList(directCategories(description))); - categories.addAll(Arrays.asList(directCategories(parentDescription(description)))); + /** + * @return false if at least one parent category does not match children, otherwise true. + * If empty parentCategories, returns true. + */ + private boolean matchesAllParentCategories(Set> childCategories, Set> parentCategories) { + for (Class parentCategory : parentCategories) { + if (!hasAssignableTo(childCategories, parentCategory)) { + return false; + } + } + return true; + } + + private static Set> categories(Description description) { + Set> categories= new HashSet>(); + Collections.addAll(categories, directCategories(description)); + Collections.addAll(categories, directCategories(parentDescription(description))); return categories; } - private Description parentDescription(Description description) { - Class testClass = description.getTestClass(); - if (testClass == null) { - return null; - } - return Description.createSuiteDescription(testClass); + private static Description parentDescription(Description description) { + Class testClass= description.getTestClass(); + return testClass == null ? null : Description.createSuiteDescription(testClass); } - private Class[] directCategories(Description description) { + private static Class[] directCategories(Description description) { if (description == null) { return new Class[0]; } - Category annotation = description.getAnnotation(Category.class); - if (annotation == null) { - return new Class[0]; + + Category annotation= description.getAnnotation(Category.class); + return annotation == null ? new Class[0] : annotation.value(); + } + + private static Set> copyAndRefine(Set> classes) { + HashSet> c= new HashSet>(); + if (classes != null) { + c.addAll(classes); + } + c.remove(null); + return c; + } + + private static boolean hasNull(Class... classes) { + if (classes == null) return false; + for (Class clazz : classes) { + if (clazz == null) { + return true; + } } - return annotation.value(); + return false; } } - public Categories(Class klass, RunnerBuilder builder) - throws InitializationError { + public Categories(Class klass, RunnerBuilder builder) throws InitializationError { super(klass, builder); try { - filter(new CategoryFilter(getIncludedCategory(klass), - getExcludedCategory(klass))); + Set> included= getIncludedCategory(klass); + Set> excluded= getExcludedCategory(klass); + boolean isAnyIncluded= isAnyIncluded(klass); + boolean isAnyExcluded= isAnyExcluded(klass); + + filter(CategoryFilter.categoryFilter(isAnyIncluded, included, isAnyExcluded, excluded)); } catch (NoTestsRemainException e) { throw new InitializationError(e); + } catch (ClassNotFoundException e) { + throw new InitializationError(e); } assertNoCategorizedDescendentsOfUncategorizeableParents(getDescription()); } - private Class getIncludedCategory(Class klass) { - IncludeCategory annotation = klass.getAnnotation(IncludeCategory.class); - return annotation == null ? null : annotation.value(); + private static Set> getIncludedCategory(Class klass) throws ClassNotFoundException { + IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class); + return createSet(annotation == null ? null : annotation.value()); + } + + private static boolean isAnyIncluded(Class klass) { + IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class); + return annotation == null || annotation.matchAny(); + } + + private static Set> getExcludedCategory(Class klass) throws ClassNotFoundException { + ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class); + return createSet(annotation == null ? null : annotation.value()); } - private Class getExcludedCategory(Class klass) { - ExcludeCategory annotation = klass.getAnnotation(ExcludeCategory.class); - return annotation == null ? null : annotation.value(); + private static boolean isAnyExcluded(Class klass) { + ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class); + return annotation == null || annotation.matchAny(); } - private void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError { + private static void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError { if (!canHaveCategorizedChildren(description)) { assertNoDescendantsHaveCategoryAnnotations(description); } @@ -183,7 +352,7 @@ private void assertNoCategorizedDescendentsOfUncategorizeableParents(Description } } - private void assertNoDescendantsHaveCategoryAnnotations(Description description) throws InitializationError { + private static void assertNoDescendantsHaveCategoryAnnotations(Description description) throws InitializationError { for (Description each : description.getChildren()) { if (each.getAnnotation(Category.class) != null) { throw new InitializationError("Category annotations on Parameterized classes are not supported on individual methods."); @@ -192,8 +361,7 @@ private void assertNoDescendantsHaveCategoryAnnotations(Description description) } } - // If children have names like [0], our current magical category code can't determine their - // parentage. + // If children have names like [0], our current magical category code can't determine their parentage. private static boolean canHaveCategorizedChildren(Description description) { for (Description each : description.getChildren()) { if (each.getTestClass() == null) { @@ -202,4 +370,21 @@ private static boolean canHaveCategorizedChildren(Description description) { } return true; } + + private static boolean hasAssignableTo(Set> assigns, Class to) { + for (final Class from : assigns) { + if (to.isAssignableFrom(from)) { + return true; + } + } + return false; + } + + private static Set> createSet(Class... t) { + final Set> set= new HashSet>(); + if (t != null) { + Collections.addAll(set, t); + } + return set; + } } \ No newline at end of file diff --git a/src/test/java/org/junit/tests/AllTests.java b/src/test/java/org/junit/tests/AllTests.java index 8bc07ce8d8c8..6bcd14225172 100644 --- a/src/test/java/org/junit/tests/AllTests.java +++ b/src/test/java/org/junit/tests/AllTests.java @@ -19,6 +19,8 @@ import org.junit.tests.experimental.MatcherTest; import org.junit.tests.experimental.categories.CategoriesAndParameterizedTest; import org.junit.tests.experimental.categories.CategoryTest; +import org.junit.tests.experimental.categories.JavadocTest; +import org.junit.tests.experimental.categories.MultiCategoryTest; import org.junit.tests.experimental.max.JUnit38SortingTest; import org.junit.tests.experimental.max.MaxStarterTest; import org.junit.tests.experimental.parallel.ParallelClassTest; @@ -154,6 +156,8 @@ VerifierRuleTest.class, CategoryTest.class, CategoriesAndParameterizedTest.class, + MultiCategoryTest.class, + JavadocTest.class, ParentRunnerFilteringTest.class, BlockJUnit4ClassRunnerOverrideTest.class, RuleFieldValidatorTest.class, diff --git a/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java b/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java index 03386fbf6860..e1a6af37235b 100644 --- a/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java +++ b/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java @@ -1,12 +1,22 @@ package org.junit.tests.experimental.categories; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.experimental.results.PrintableResult.testResult; import static org.junit.experimental.results.ResultMatchers.isSuccessful; - +import static org.junit.experimental.results.ResultMatchers.failureCountIs; +import static org.hamcrest.core.AnyOf.anyOf; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static java.lang.String.format; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Categories; import org.junit.experimental.categories.Categories.CategoryFilter; @@ -32,6 +42,28 @@ public interface SlowTests { // category marker } + public interface ReallySlowTests { + // category marker + } + + public static class OneOfEach { + + @Category(FastTests.class) + @Test + public void a() { + } + + @Category(SlowTests.class) + @Test + public void b() { + } + + @Category(ReallySlowTests.class) + @Test + public void c() { + } + } + public static class A { @Test public void a() { @@ -122,6 +154,7 @@ public static class SomeAreSlowSuite { @Test public void testCountOnAWithoutSlowTests() { Result result = JUnitCore.runClasses(SomeAreSlowSuite.class); + assertThat(testResult(SomeAreSlowSuite.class), isSuccessful()); assertEquals(2, result.getRunCount()); assertTrue(result.wasSuccessful()); } @@ -206,14 +239,24 @@ public void ifNoTestsToRunUseErrorRunner() { Result result = JUnitCore.runClasses(OneFastSuite.class); assertEquals(1, result.getRunCount()); assertEquals(1, result.getFailureCount()); + assertFalse(result.wasSuccessful()); } @Test public void describeACategoryFilter() { CategoryFilter filter = CategoryFilter.include(SlowTests.class); - assertEquals("category " + SlowTests.class, filter.describe()); + assertEquals("categories [" + SlowTests.class + "]", filter.describe()); + } + + @Test + public void describeMultipleCategoryFilter() { + CategoryFilter filter= CategoryFilter.include(FastTests.class, SlowTests.class); + String d1= format("categories [%s, %s]", FastTests.class, SlowTests.class); + String d2= format("categories [%s, %s]", SlowTests.class, FastTests.class); + assertThat(filter.describe(), is(anyOf(equalTo(d1), equalTo(d2)))); } + public static class OneThatIsBothFastAndSlow { @Category({FastTests.class, SlowTests.class}) @Test @@ -256,8 +299,71 @@ public void subclassesOfIncludedCategoriesAreRun() { assertThat(testResult(RunSlowFromVerySlow.class), isSuccessful()); } - public static class ClassAsCategory { + public interface MultiA { + } + public interface MultiB { + } + + public interface MultiC { + } + + @RunWith(Categories.class) + @IncludeCategory(value= {MultiA.class, MultiB.class}, matchAny= false) + @SuiteClasses(AllIncludedMustMatched.class) + public static class AllIncludedMustBeMatchedSuite { + } + + public static class AllIncludedMustMatched { + @Test + @Category({MultiA.class, MultiB.class}) + public void a() { + } + + @Test + @Category(MultiB.class) + public void b() { + fail("When multiple categories are included in a Suite, " + + "@Test method must match all include categories"); + } + } + + @Test + public void allIncludedSuiteCategoriesMustBeMatched() { + Result result= JUnitCore.runClasses(AllIncludedMustBeMatchedSuite.class); + assertEquals(1, result.getRunCount()); + assertEquals(0, result.getFailureCount()); + } + + @RunWith(Categories.class) + @IncludeCategory({MultiA.class, MultiB.class}) + @ExcludeCategory(MultiC.class) + @SuiteClasses(MultipleIncludesAndExcludeOnMethod.class) + public static class MultiIncludeWithExcludeCategorySuite { + } + + public static class MultipleIncludesAndExcludeOnMethod { + @Test + @Category({MultiA.class, MultiB.class}) + public void a() { + } + + @Test + @Category({ MultiA.class, MultiB.class, MultiC.class }) + public void b() { + fail("When multiple categories are included and excluded in a Suite, " + + "@Test method must match all include categories and contain non of the excluded"); + } + } + + @Test + public void anyMethodWithExcludedCategoryWillBeExcluded() { + Result result= JUnitCore.runClasses(MultiIncludeWithExcludeCategorySuite.class); + assertEquals(1, result.getRunCount()); + assertEquals(0, result.getFailureCount()); + } + + public static class ClassAsCategory { } public static class OneMoreTest { @@ -298,4 +404,145 @@ public void testInheritance() { assertEquals(1, result.getRunCount()); assertTrue(result.wasSuccessful()); } + + @RunWith(Categories.class) + @IncludeCategory(Runnable.class) + @ExcludeCategory(Runnable.class) + @SuiteClasses({}) + public static class EmptyCategoriesSuite { + } + + @Test public void emptyCategoriesSuite() { + assertThat(testResult(EmptyCategoriesSuite.class), failureCountIs(1)); + } + + @Category(Runnable.class) + public static class NoTest { + } + + @Category(Runnable.class) + public static class IgnoredTest { + + @Ignore + @Test + public void test() { + fail(); + } + } + + @RunWith(Categories.class) + @IncludeCategory(Runnable.class) + @SuiteClasses({NoTest.class, IgnoredTest.class}) + public static class IgnoredTestCategoriesSuite { + } + + @Test + public void ignoredTest() {// behaves same as Suite + Result result= JUnitCore.runClasses(IgnoredTestCategoriesSuite.class); + assertFalse(result.wasSuccessful()); + assertThat(result.getRunCount(), is(1)); + assertThat(result.getFailureCount(), is(1)); + assertThat(result.getIgnoreCount(), is(1)); + } + + @Category(Runnable.class) + public static class ExcludedTest1 { + + @Test + public void test() { + fail(); + } + } + + @Category(Runnable.class) + public static class ExcludedTest2 { + + @Test + @Category(Runnable.class) + public void test() { + fail(); + } + } + + public static class IncludedTest { + + @Test + @Category(Object.class) + public void test() { + } + } + + @RunWith(Categories.class) + @IncludeCategory({Runnable.class, Object.class}) + @ExcludeCategory(Runnable.class) + @SuiteClasses({ExcludedTest1.class, ExcludedTest2.class, IncludedTest.class}) + public static class IncludedExcludedSameSuite { + } + + @Test + public void oneRunnableOthersAvoided() { + Result result= JUnitCore.runClasses(IncludedExcludedSameSuite.class); + assertEquals(1, result.getRunCount()); + assertTrue(result.wasSuccessful()); + } + + @Test + @SuppressWarnings("unchecked") + public void testCountWithMultipleExcludeFilter() throws Throwable { + Set> exclusions= new HashSet>(2); + Collections.addAll(exclusions, SlowTests.class, FastTests.class); + CategoryFilter exclude = CategoryFilter.categoryFilter(true, null, true, exclusions); + Request baseRequest= Request.aClass(OneOfEach.class); + Result result= new JUnitCore().run(baseRequest.filterWith(exclude)); + assertTrue(result.wasSuccessful()); + assertEquals(1, result.getRunCount()); + } + + @Test + public void testCountWithMultipleIncludeFilter() throws Throwable { + CategoryFilter exclude = CategoryFilter.include(true, SlowTests.class, FastTests.class); + Request baseRequest= Request.aClass(OneOfEach.class); + Result result= new JUnitCore().run(baseRequest.filterWith(exclude)); + assertTrue(result.wasSuccessful()); + assertEquals(2, result.getRunCount()); + } + + @RunWith(Categories.class) + @Categories.ExcludeCategory(String.class) + @Suite.SuiteClasses(NoIncludeCategoryAnnotationTest.class) + public static class NoIncludeCategoryAnnotationSuite { + } + + @Category(CharSequence.class) + public static class NoIncludeCategoryAnnotationTest { + + @Test + public void test2() { + } + + @Test + @Category(String.class) public void test1() { + } + } + + @Test + public void noIncludeCategoryAnnotation() { + Result testResult= JUnitCore.runClasses(NoIncludeCategoryAnnotationSuite.class); + assertTrue(testResult.wasSuccessful()); + assertEquals(1, testResult.getRunCount()); + } + + @RunWith(Categories.class) + @Categories.IncludeCategory(CharSequence.class) + @Categories.ExcludeCategory(String.class) + @Suite.SuiteClasses(NoIncludeCategoryAnnotationTest.class) + public static class SameAsNoIncludeCategoryAnnotationSuite { + } + + @Test + public void sameAsNoIncludeCategoryAnnotation() { + Result testResult= JUnitCore.runClasses(SameAsNoIncludeCategoryAnnotationSuite.class); + assertTrue(testResult.wasSuccessful()); + assertEquals(1, testResult.getRunCount()); + } } diff --git a/src/test/java/org/junit/tests/experimental/categories/JavadocTest.java b/src/test/java/org/junit/tests/experimental/categories/JavadocTest.java new file mode 100644 index 000000000000..0f9af8e7cb26 --- /dev/null +++ b/src/test/java/org/junit/tests/experimental/categories/JavadocTest.java @@ -0,0 +1,78 @@ +package org.junit.tests.experimental.categories; + +import org.junit.Test; +import org.junit.experimental.categories.Categories; +import org.junit.experimental.categories.Category; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author tibor17 + * @version 4.12 + * @since 4.12 + */ +public class JavadocTest { + public static interface FastTests {} + public static interface SlowTests {} + public static interface SmokeTests {} + + public static class A { + public void a() { + fail(); + } + + @Category(SlowTests.class) + @Test + public void b() { + } + + @Category({FastTests.class, SmokeTests.class}) + @Test + public void c() { + } + } + + @Category({SlowTests.class, FastTests.class}) + public static class B { + @Test + public void d() { + } + } + + @RunWith(Categories.class) + @Categories.IncludeCategory(SlowTests.class) + @Suite.SuiteClasses({A.class, B.class}) + public static class SlowTestSuite { + // Will run A.b and B.d, but not A.a and A.c + } + + @RunWith(Categories.class) + @Categories.IncludeCategory({FastTests.class, SmokeTests.class}) + @Suite.SuiteClasses({A.class, B.class}) + public static class FastOrSmokeTestSuite { + // Will run A.c and B.d, but not A.b because it is not any of FastTests or SmokeTests + } + + @Test + public void slowTests() { + Result testResult= JUnitCore.runClasses(SlowTestSuite.class); + assertTrue(testResult.wasSuccessful()); + assertThat("unexpected run count", testResult.getRunCount(), is(2)); + assertThat("unexpected failure count", testResult.getFailureCount(), is(0)); + } + + @Test + public void fastSmokeTests() { + Result testResult= JUnitCore.runClasses(FastOrSmokeTestSuite.class); + assertTrue(testResult.wasSuccessful()); + assertThat("unexpected run count", testResult.getRunCount(), is(2)); + assertThat("unexpected failure count", testResult.getFailureCount(), is(0)); + } +} diff --git a/src/test/java/org/junit/tests/experimental/categories/MultiCategoryTest.java b/src/test/java/org/junit/tests/experimental/categories/MultiCategoryTest.java new file mode 100644 index 000000000000..986c5a895173 --- /dev/null +++ b/src/test/java/org/junit/tests/experimental/categories/MultiCategoryTest.java @@ -0,0 +1,171 @@ +package org.junit.tests.experimental.categories; + +import org.junit.Test; +import org.junit.experimental.categories.Categories; +import org.junit.experimental.categories.Category; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +/** + * @author tibor17 + * @version 4.12 + * @since 4.12 + */ +public final class MultiCategoryTest { + public interface A {} + public interface B {} + public interface C {} + + /** + * This test is mentioned in {@code Categories} and any changes + * must be reflected. + */ + @Test + public void runSuite() { + // Targeting Test: + Result testResult= JUnitCore.runClasses(MultiCategorySuite.class); + + assertThat("unexpected run count", testResult.getRunCount(), is(equalTo(2))); + assertThat("unexpected failure count", testResult.getFailureCount(), is(equalTo(0))); + assertThat("unexpected failure count", testResult.getIgnoreCount(), is(equalTo(0))); + } + + @RunWith(Categories.class) + @Categories.IncludeCategory({A.class, B.class}) + @Categories.ExcludeCategory(C.class) + @Suite.SuiteClasses({CategoriesTest.class}) + public static final class MultiCategorySuite {} + + public static final class CategoriesTest { + + @Test + @Category(A.class) + public void a() {} + + @Test + @Category(B.class) + public void b() {} + + @Test + @Category(C.class) + public void c() { + fail(); + } + + @Test + public void anything() { + fail(); + } + } + + @Test + public void inheritanceAnyIncluded() { + Result testResult= JUnitCore.runClasses(InheritanceAny.class); + assertThat("unexpected run count", testResult.getRunCount(), is(equalTo(3))); + assertThat("unexpected failure count", testResult.getFailureCount(), is(equalTo(0))); + assertThat("unexpected failure count", testResult.getIgnoreCount(), is(equalTo(0))); + } + + @Test + public void inheritanceAllIncluded() { + Result testResult= JUnitCore.runClasses(InheritanceAll.class); + assertThat("unexpected run count", testResult.getRunCount(), is(equalTo(1))); + assertThat("unexpected failure count", testResult.getFailureCount(), is(equalTo(0))); + assertThat("unexpected failure count", testResult.getIgnoreCount(), is(equalTo(0))); + } + + @Test + public void inheritanceAnyAll() {//any included, all excluded + Result testResult= JUnitCore.runClasses(InheritanceAnyAll.class); + assertThat("unexpected run count", testResult.getRunCount(), is(equalTo(3))); + assertThat("unexpected failure count", testResult.getFailureCount(), is(equalTo(0))); + assertThat("unexpected failure count", testResult.getIgnoreCount(), is(equalTo(0))); + } + + @Test + public void inheritanceAllAny() {//all included, any excluded + Result testResult= JUnitCore.runClasses(InheritanceAllAny.class); + assertThat("unexpected run count", testResult.getRunCount(), is(equalTo(1))); + assertThat("unexpected failure count", testResult.getFailureCount(), is(equalTo(1))); + assertThat("unexpected failure count", testResult.getIgnoreCount(), is(equalTo(0))); + assertFalse(testResult.wasSuccessful()); + } + + public static class X implements A {} + public static class Y implements B {} + public static class Z implements A, B {} + public static class W implements A, B, C {} + public static class Q implements A, C {} + + @RunWith(Categories.class) + @Categories.IncludeCategory({A.class, B.class}) + @Categories.ExcludeCategory(C.class) + @Suite.SuiteClasses({InheritanceAnyTest.class}) + public static final class InheritanceAny {} + + @RunWith(Categories.class) + @Categories.IncludeCategory(value= {A.class, B.class}, matchAny= false) + @Categories.ExcludeCategory(C.class) + @Suite.SuiteClasses({InheritanceAllTest.class}) + public static final class InheritanceAll {} + + @RunWith(Categories.class) + @Categories.IncludeCategory({A.class, B.class}) + @Categories.ExcludeCategory(value= {A.class, C.class}, matchAny= false) + @Suite.SuiteClasses({InheritanceAnyAllTest.class}) + public static final class InheritanceAnyAll {} + + @RunWith(Categories.class) + @Categories.IncludeCategory(value= {A.class, B.class}, matchAny= false) + @Categories.ExcludeCategory({A.class, C.class}) + @Suite.SuiteClasses({InheritanceAllAnyTest.class}) + public static final class InheritanceAllAny {} + + public static final class InheritanceAnyTest { + @Test @Category(X.class) public void x() {} + @Test @Category(Y.class) public void y() {} + @Test @Category(Z.class) public void z() {} + @Test @Category(W.class) public void w() { fail(); } + @Test @Category(Q.class) public void q() { fail(); } + @Test @Category(Runnable.class) public void runnable() { fail(); } + @Test public void t() { fail(); } + } + + public static final class InheritanceAllTest { + @Test @Category(X.class) public void x() { fail(); } + @Test @Category(Y.class) public void y() { fail(); } + @Test @Category(Z.class) public void z() {} + @Test @Category(W.class) public void w() { fail(); } + @Test @Category(Q.class) public void q() { fail(); } + @Test @Category(Runnable.class) public void runnable() { fail(); } + @Test public void t() { fail(); } + } + + public static final class InheritanceAnyAllTest { + @Test @Category(X.class) public void x() {} + @Test @Category(Y.class) public void y() {} + @Test @Category(Z.class) public void z() {} + @Test @Category(W.class) public void w() { fail(); } + @Test @Category(Q.class) public void q() { fail(); } + @Test @Category(Runnable.class) public void runnable() { fail(); } + @Test public void t() { fail(); } + } + + public static final class InheritanceAllAnyTest { + @Test @Category(X.class) public void x() { fail(); } + @Test @Category(Y.class) public void y() { fail(); } + @Test @Category(Z.class) public void z() { fail(); } + @Test @Category(W.class) public void w() { fail(); } + @Test @Category(Q.class) public void q() { fail(); } + @Test @Category(Runnable.class) public void runnable() { fail(); } + @Test public void t() { fail(); } + } +}