All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.junit.experimental.categories.Categories Maven / Gradle / Ivy

Go to download

JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.

There is a newer version: 4.13.2
Show newest version
package org.junit.experimental.categories;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;

import org.junit.runner.Description;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.NoTestsRemainException;
import org.junit.runners.Suite;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerBuilder;

/**
 * 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 {
 * }
 *
 * public interface SlowTests {
 * }
 *
 * public interface SmokeTests
 * }
 *
 * public static class A {
 *     @Test
 *     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)
 * @IncludeCategory(SlowTests.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 { @Retention(RetentionPolicy.RUNTIME) public @interface IncludeCategory { /** * 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}. */ 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. */ boolean matchAny() default true; } @Retention(RetentionPolicy.RUNTIME) public @interface ExcludeCategory { /** * 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()}. */ 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. */ boolean matchAny() default true; } public static class CategoryFilter extends Filter { private final Set> included; private final Set> excluded; private final boolean includedAny; private final boolean excludedAny; public static CategoryFilter include(boolean matchAny, Class... categories) { return new CategoryFilter(matchAny, categories, true, null); } 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) { return new CategoryFilter(true, null, matchAny, 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); } @Deprecated public CategoryFilter(Class includedCategory, Class excludedCategory) { includedAny = true; excludedAny = true; included = nullableClassToSet(includedCategory); excluded = nullableClassToSet(excludedCategory); } protected CategoryFilter(boolean matchAnyIncludes, Set> includes, boolean matchAnyExcludes, Set> excludes) { includedAny = matchAnyIncludes; excludedAny = matchAnyExcludes; included = copyAndRefine(includes); excluded = copyAndRefine(excludes); } private CategoryFilter(boolean matchAnyIncludes, Class[] inclusions, boolean matchAnyExcludes, Class[] exclusions) { includedAny = matchAnyIncludes; excludedAny = matchAnyExcludes; included = createSet(inclusions); excluded = createSet(exclusions); } /** * @see #toString() */ @Override public String describe() { 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(included.isEmpty() ? "[all]" : included); if (!excluded.isEmpty()) { description.append(" - ").append(excluded); } return description.toString(); } @Override 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) { final Set> childCategories= categories(description); // If a child has no categories, immediately return. if (childCategories.isEmpty()) { return included.isEmpty(); } if (!excluded.isEmpty()) { if (excludedAny) { if (matchesAnyParentCategories(childCategories, excluded)) { return false; } } else { if (matchesAllParentCategories(childCategories, excluded)) { return false; } } } if (included.isEmpty()) { // Couldn't be excluded, and with no suite's included categories treated as should run. return true; } else { if (includedAny) { return matchesAnyParentCategories(childCategories, included); } else { return matchesAllParentCategories(childCategories, included); } } } /** * @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; } /** * @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 static Description parentDescription(Description description) { Class testClass= description.getTestClass(); return testClass == null ? null : Description.createSuiteDescription(testClass); } private static Class[] directCategories(Description description) { if (description == 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) { Set> c= new LinkedHashSet>(); if (classes != null) { c.addAll(classes); } c.remove(null); return c; } } public Categories(Class klass, RunnerBuilder builder) throws InitializationError { super(klass, builder); try { 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); } } private static Set> getIncludedCategory(Class klass) { 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) { 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 boolean hasAssignableTo(Set> assigns, Class to) { for (final Class from : assigns) { if (to.isAssignableFrom(from)) { return true; } } return false; } private static Set> createSet(Class[] classes) { // Not throwing a NPE if t is null is a bad idea, but it's the behavior from JUnit 4.12 // for include(boolean, Class...) and exclude(boolean, Class...) if (classes == null || classes.length == 0) { return Collections.emptySet(); } for (Class category : classes) { if (category == null) { throw new NullPointerException("has null category"); } } return classes.length == 1 ? Collections.>singleton(classes[0]) : new LinkedHashSet>(Arrays.asList(classes)); } private static Set> nullableClassToSet(Class nullableClass) { // Not throwing a NPE if t is null is a bad idea, but it's the behavior from JUnit 4.11 // for CategoryFilter(Class includedCategory, Class excludedCategory) return nullableClass == null ? Collections.>emptySet() : Collections.>singleton(nullableClass); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy