diff --git a/src/main/java/org/junit/experimental/categories/Categories.java b/src/main/java/org/junit/experimental/categories/Categories.java
index 49a4433b5e9e..46f1c12fcc1d 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);
+ }
- private final Class> fExcluded;
+ 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);
+ }
+
+ 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 "[] - []", 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,157 @@ 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;
+ }
+ }
+ }
+
+ // Couldn't be excluded, and with no suite's included categories treated as should run.
+ if (fIncluded.isEmpty()) {
+ return true;
+ }
+
+ if (!fIncluded.isEmpty()) {
+ if (fIncludedAny) {
+ return matchesAnyParentCategories(childCategories, fIncluded);
+ } else {
+ return matchesAllParentCategories(childCategories, fIncluded);
}
}
- for (Class> each : categories) {
- if (fIncluded == null || fIncluded.isAssignableFrom(each)) {
+
+ return false;
+ }
+
+ /**
+ * @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);
}
- return annotation.value();
+ 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 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 Class> getExcludedCategory(Class> klass) {
- ExcludeCategory annotation = klass.getAnnotation(ExcludeCategory.class);
- return annotation == null ? null : annotation.value();
+ private static boolean isAnyIncluded(Class> klass) {
+ IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class);
+ return annotation == null || annotation.matchAny();
}
- private void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError {
+ private static Set> getExcludedCategory(Class> klass) throws ClassNotFoundException {
+ ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class);
+ return createSet(annotation == null ? null : annotation.value());
+ }
+
+ private static boolean isAnyExcluded(Class> klass) {
+ ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class);
+ return annotation == null || annotation.matchAny();
+ }
+
+ private static void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError {
if (!canHaveCategorizedChildren(description)) {
assertNoDescendantsHaveCategoryAnnotations(description);
}
@@ -183,7 +356,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 +365,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 +374,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 8083bbc52740..bca240cca8bf 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 {
@@ -277,4 +383,145 @@ public static class RunClassAsCategory {
public void classesCanBeCategories() {
assertThat(testResult(RunClassAsCategory.class), isSuccessful());
}
+
+ @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(); }
+ }
+}