From a434eee09b210c049e58ef31722ec68e418f5866 Mon Sep 17 00:00:00 2001 From: Andrew Matheny Date: Tue, 11 Oct 2011 10:59:49 -0400 Subject: [PATCH 1/5] Added two new annotations, @IncludeCategories and @ExcludeCategories, that allow for filtering the items in a test suite by more than one category --- .../experimental/categories/Categories.java | 76 ++++++++++++++----- .../experimental/categories/CategoryTest.java | 34 +++++++-- 2 files changed, 85 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/junit/experimental/categories/Categories.java b/src/main/java/org/junit/experimental/categories/Categories.java index d57b4d3bd905..554ef0f09c0f 100644 --- a/src/main/java/org/junit/experimental/categories/Categories.java +++ b/src/main/java/org/junit/experimental/categories/Categories.java @@ -71,25 +71,39 @@ public class Categories extends Suite { public @interface IncludeCategory { public Class value(); } + + @Retention(RetentionPolicy.RUNTIME) + public @interface IncludeCategories { + public Class[] value(); + } @Retention(RetentionPolicy.RUNTIME) public @interface ExcludeCategory { public Class value(); } + + @Retention(RetentionPolicy.RUNTIME) + public @interface ExcludeCategories { + public Class[] value(); + } public static class CategoryFilter extends Filter { - public static CategoryFilter include(Class categoryType) { - return new CategoryFilter(categoryType, null); + public static CategoryFilter include(Class[] categoryTypes) { + return new CategoryFilter(categoryTypes, null); + } + + public static CategoryFilter exclude(Class[] categoryTypes) { + return new CategoryFilter(null, categoryTypes); } - private final Class fIncluded; - - private final Class fExcluded; + private final Class[] fIncluded; - public CategoryFilter(Class includedCategory, - Class excludedCategory) { - fIncluded= includedCategory; - fExcluded= excludedCategory; + private final Class[] fExcluded; + + public CategoryFilter(Class[] includedCategories, + Class[] excludedCategories) { + fIncluded= includedCategories; + fExcluded= excludedCategories; } @Override @@ -112,10 +126,28 @@ private boolean hasCorrectCategoryAnnotation(Description description) { if (categories.isEmpty()) return fIncluded == null; for (Class each : categories) - if (fExcluded != null && fExcluded.isAssignableFrom(each)) + if (shouldExclude(each)) return false; for (Class each : categories) - if (fIncluded == null || fIncluded.isAssignableFrom(each)) + if (shouldInclude(each)) + return true; + return false; + } + + private boolean shouldInclude(Class category) { + if (fIncluded == null) + return true; + for(Class includeCat : fIncluded) + if (includeCat.isAssignableFrom(category)) + return true; + return false; + } + + private boolean shouldExclude(Class category) { + if (fExcluded == null) + return false; + for(Class each : fExcluded) + if(each.isAssignableFrom(category)) return true; return false; } @@ -148,22 +180,30 @@ public Categories(Class klass, RunnerBuilder builder) throws InitializationError { super(klass, builder); try { - filter(new CategoryFilter(getIncludedCategory(klass), - getExcludedCategory(klass))); + filter(new CategoryFilter(getIncludedCategories(klass), + getExcludedCategories(klass))); } catch (NoTestsRemainException 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 Class[] getIncludedCategories(Class klass) { + IncludeCategories pluralAnnotation= klass.getAnnotation(IncludeCategories.class); + if (pluralAnnotation != null) + return pluralAnnotation.value(); + + IncludeCategory annotation = klass.getAnnotation(IncludeCategory.class); + return annotation == null ? null : new Class[] {annotation.value()}; } - private Class getExcludedCategory(Class klass) { + private Class[] getExcludedCategories(Class klass) { + ExcludeCategories pluralAnnotation= klass.getAnnotation(ExcludeCategories.class); + if (pluralAnnotation != null) + return pluralAnnotation.value(); + ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class); - return annotation == null ? null : annotation.value(); + return annotation == null ? null : new Class[] {annotation.value()}; } private void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError { 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 b316731d7475..bca8d405a7cf 100644 --- a/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java +++ b/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java @@ -30,6 +30,10 @@ public interface FastTests { public interface SlowTests { // category marker } + + public interface ReallySlowTests { + // category marker + } public static class A { @Test @@ -127,23 +131,32 @@ public static class TestSuiteWithNoCategories { @Test public void testCountWithExplicitFilter() throws Throwable { - CategoryFilter include= CategoryFilter.include(SlowTests.class); + CategoryFilter include= CategoryFilter.include(new Class[] {SlowTests.class}); Request baseRequest= Request.aClass(TestSuiteWithNoCategories.class); Result result= new JUnitCore().run(baseRequest.filterWith(include)); assertTrue(result.wasSuccessful()); assertEquals(2, result.getRunCount()); } + + @Test + public void testCountWithExplicitExcludeFilter() throws Throwable { + CategoryFilter include= CategoryFilter.exclude(new Class[] {SlowTests.class, FastTests.class}); + Request baseRequest= Request.aClass(OneOfEach.class); + Result result= new JUnitCore().run(baseRequest.filterWith(include)); + assertTrue(result.wasSuccessful()); + assertEquals(1, result.getRunCount()); + } @Test public void categoryFilterLeavesOnlyMatchingMethods() throws InitializationError, NoTestsRemainException { - CategoryFilter filter= CategoryFilter.include(SlowTests.class); + CategoryFilter filter= CategoryFilter.include(new Class[] {SlowTests.class}); BlockJUnit4ClassRunner runner= new BlockJUnit4ClassRunner(A.class); filter.apply(runner); assertEquals(1, runner.testCount()); } - public static class OneFastOneSlow { + public static class OneOfEach { @Category(FastTests.class) @Test public void a() { @@ -155,14 +168,20 @@ public void a() { public void b() { } + + @Category(ReallySlowTests.class) + @Test + public void c() { + + } } @Test public void categoryFilterRejectsIncompatibleCategory() throws InitializationError, NoTestsRemainException { - CategoryFilter filter= CategoryFilter.include(SlowTests.class); + CategoryFilter filter= CategoryFilter.include(new Class[] {SlowTests.class}); BlockJUnit4ClassRunner runner= new BlockJUnit4ClassRunner( - OneFastOneSlow.class); + OneOfEach.class); filter.apply(runner); assertEquals(1, runner.testCount()); } @@ -190,8 +209,9 @@ public void ifNoTestsToRunUseErrorRunner() { @Test public void describeACategoryFilter() { - CategoryFilter filter= CategoryFilter.include(SlowTests.class); - assertEquals("category " + SlowTests.class, filter.describe()); + Class[] includeCats = new Class[] {SlowTests.class}; + CategoryFilter filter= CategoryFilter.include(includeCats); + assertEquals("category " + includeCats, filter.describe()); } public static class OneThatIsBothFastAndSlow { From 83ac907e9a72a81c4f95549003c0d4eca85f7533 Mon Sep 17 00:00:00 2001 From: Andrew Matheny Date: Tue, 11 Oct 2011 11:14:02 -0400 Subject: [PATCH 2/5] Removed IncludeCategories and ExcludeCategories in favor of having the original annotations take arrays instead of single values --- .../experimental/categories/Categories.java | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/junit/experimental/categories/Categories.java b/src/main/java/org/junit/experimental/categories/Categories.java index 554ef0f09c0f..ef5bb7f7d6e7 100644 --- a/src/main/java/org/junit/experimental/categories/Categories.java +++ b/src/main/java/org/junit/experimental/categories/Categories.java @@ -69,21 +69,11 @@ public class Categories extends Suite { @Retention(RetentionPolicy.RUNTIME) public @interface IncludeCategory { - public Class value(); - } - - @Retention(RetentionPolicy.RUNTIME) - public @interface IncludeCategories { public Class[] value(); } @Retention(RetentionPolicy.RUNTIME) public @interface ExcludeCategory { - public Class value(); - } - - @Retention(RetentionPolicy.RUNTIME) - public @interface ExcludeCategories { public Class[] value(); } @@ -189,21 +179,13 @@ public Categories(Class klass, RunnerBuilder builder) } private Class[] getIncludedCategories(Class klass) { - IncludeCategories pluralAnnotation= klass.getAnnotation(IncludeCategories.class); - if (pluralAnnotation != null) - return pluralAnnotation.value(); - IncludeCategory annotation = klass.getAnnotation(IncludeCategory.class); - return annotation == null ? null : new Class[] {annotation.value()}; + return annotation == null ? null : annotation.value(); } private Class[] getExcludedCategories(Class klass) { - ExcludeCategories pluralAnnotation= klass.getAnnotation(ExcludeCategories.class); - if (pluralAnnotation != null) - return pluralAnnotation.value(); - ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class); - return annotation == null ? null : new Class[] {annotation.value()}; + return annotation == null ? null : annotation.value(); } private void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError { From 56ed0d7411d59a29976177238f188d8d28e22abd Mon Sep 17 00:00:00 2001 From: Andrew Matheny Date: Tue, 11 Oct 2011 11:29:32 -0400 Subject: [PATCH 3/5] Updated describe method to perform as it did previously in the case where there is only one category. Added new unit test that verifies description of multi category filters --- .../experimental/categories/Categories.java | 21 ++++++++++++++++--- .../experimental/categories/CategoryTest.java | 21 ++++++++++++------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/junit/experimental/categories/Categories.java b/src/main/java/org/junit/experimental/categories/Categories.java index ef5bb7f7d6e7..614a711e733b 100644 --- a/src/main/java/org/junit/experimental/categories/Categories.java +++ b/src/main/java/org/junit/experimental/categories/Categories.java @@ -78,11 +78,11 @@ public class Categories extends Suite { } public static class CategoryFilter extends Filter { - public static CategoryFilter include(Class[] categoryTypes) { + public static CategoryFilter include(Class... categoryTypes) { return new CategoryFilter(categoryTypes, null); } - public static CategoryFilter exclude(Class[] categoryTypes) { + public static CategoryFilter exclude(Class... categoryTypes) { return new CategoryFilter(null, categoryTypes); } @@ -98,9 +98,24 @@ public CategoryFilter(Class[] includedCategories, @Override public String describe() { - return "category " + fIncluded; + return ((fIncluded == null || fIncluded.length == 1) ? "category ":"categories ") + join(", ", fIncluded); } + private String join(String seperator, Class... values) + { + if(values == null || values.length == 0) + { + return ""; + } + StringBuilder sb = new StringBuilder(values[0].toString()); + for(int i = 1; i < values.length; i++) + { + sb.append(seperator).append(values[i].toString()); + } + return sb.toString(); + } + + @Override public boolean shouldRun(Description description) { if (hasCorrectCategoryAnnotation(description)) 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 bca8d405a7cf..fb2e85dd0b84 100644 --- a/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java +++ b/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java @@ -131,7 +131,7 @@ public static class TestSuiteWithNoCategories { @Test public void testCountWithExplicitFilter() throws Throwable { - CategoryFilter include= CategoryFilter.include(new Class[] {SlowTests.class}); + CategoryFilter include= CategoryFilter.include(SlowTests.class); Request baseRequest= Request.aClass(TestSuiteWithNoCategories.class); Result result= new JUnitCore().run(baseRequest.filterWith(include)); assertTrue(result.wasSuccessful()); @@ -140,9 +140,9 @@ public void testCountWithExplicitFilter() throws Throwable { @Test public void testCountWithExplicitExcludeFilter() throws Throwable { - CategoryFilter include= CategoryFilter.exclude(new Class[] {SlowTests.class, FastTests.class}); + CategoryFilter exclude = CategoryFilter.exclude(SlowTests.class, FastTests.class); Request baseRequest= Request.aClass(OneOfEach.class); - Result result= new JUnitCore().run(baseRequest.filterWith(include)); + Result result= new JUnitCore().run(baseRequest.filterWith(exclude)); assertTrue(result.wasSuccessful()); assertEquals(1, result.getRunCount()); } @@ -150,7 +150,7 @@ public void testCountWithExplicitExcludeFilter() throws Throwable { @Test public void categoryFilterLeavesOnlyMatchingMethods() throws InitializationError, NoTestsRemainException { - CategoryFilter filter= CategoryFilter.include(new Class[] {SlowTests.class}); + CategoryFilter filter= CategoryFilter.include(SlowTests.class); BlockJUnit4ClassRunner runner= new BlockJUnit4ClassRunner(A.class); filter.apply(runner); assertEquals(1, runner.testCount()); @@ -179,7 +179,7 @@ public void c() { @Test public void categoryFilterRejectsIncompatibleCategory() throws InitializationError, NoTestsRemainException { - CategoryFilter filter= CategoryFilter.include(new Class[] {SlowTests.class}); + CategoryFilter filter= CategoryFilter.include(SlowTests.class); BlockJUnit4ClassRunner runner= new BlockJUnit4ClassRunner( OneOfEach.class); filter.apply(runner); @@ -209,9 +209,14 @@ public void ifNoTestsToRunUseErrorRunner() { @Test public void describeACategoryFilter() { - Class[] includeCats = new Class[] {SlowTests.class}; - CategoryFilter filter= CategoryFilter.include(includeCats); - assertEquals("category " + includeCats, filter.describe()); + CategoryFilter filter= CategoryFilter.include(SlowTests.class); + assertEquals("category " + SlowTests.class, filter.describe()); + } + + @Test + public void describeAMultiCategoryFilter() { + CategoryFilter filter= CategoryFilter.include(SlowTests.class, FastTests.class); + assertEquals("categories " + SlowTests.class + ", " + FastTests.class, filter.describe()); } public static class OneThatIsBothFastAndSlow { From 5fc05a033ef42363113c3e8b18f7ea5a51c8b590 Mon Sep 17 00:00:00 2001 From: Andrew Matheny Date: Tue, 18 Oct 2011 14:33:07 -0400 Subject: [PATCH 4/5] Addressed issues put forth by dsaff in pull request diff https://github.com/KentBeck/junit/pull/340/files#r176473 --- .../experimental/categories/Categories.java | 54 ++++++++----------- .../experimental/categories/CategoryTest.java | 11 +++- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/junit/experimental/categories/Categories.java b/src/main/java/org/junit/experimental/categories/Categories.java index 614a711e733b..772f27cafa90 100644 --- a/src/main/java/org/junit/experimental/categories/Categories.java +++ b/src/main/java/org/junit/experimental/categories/Categories.java @@ -90,8 +90,7 @@ public static CategoryFilter exclude(Class... categoryTypes) { private final Class[] fExcluded; - public CategoryFilter(Class[] includedCategories, - Class[] excludedCategories) { + public CategoryFilter(Class[] includedCategories, Class[] excludedCategories) { fIncluded= includedCategories; fExcluded= excludedCategories; } @@ -101,18 +100,13 @@ public String describe() { return ((fIncluded == null || fIncluded.length == 1) ? "category ":"categories ") + join(", ", fIncluded); } - private String join(String seperator, Class... values) - { + private String join(String seperator, Class... values) { if(values == null || values.length == 0) - { return ""; - } - StringBuilder sb = new StringBuilder(values[0].toString()); - for(int i = 1; i < values.length; i++) - { - sb.append(seperator).append(values[i].toString()); - } - return sb.toString(); + StringBuilder sb= new StringBuilder(); + for(Class each :values) + sb.append(each.toString()).append(seperator); + return sb.substring(0, sb.length() - seperator.length()); } @@ -130,29 +124,23 @@ private boolean hasCorrectCategoryAnnotation(Description description) { List> categories= categories(description); if (categories.isEmpty()) return fIncluded == null; - for (Class each : categories) - if (shouldExclude(each)) - return false; - for (Class each : categories) - if (shouldInclude(each)) - return true; - return false; + return categoryListPassesFilters(categories); } - private boolean shouldInclude(Class category) { - if (fIncluded == null) - return true; - for(Class includeCat : fIncluded) - if (includeCat.isAssignableFrom(category)) - return true; - return false; + private boolean categoryListPassesFilters(List> categories) { + boolean hasIncludeCategory= false; + for (Class each : categories) { + if ((fExcluded != null) && classIsAssignableFromClassInArray(each, fExcluded)) + return false; + hasIncludeCategory= (fIncluded == null) || hasIncludeCategory || + classIsAssignableFromClassInArray(each, fIncluded); + } + return hasIncludeCategory; } - - private boolean shouldExclude(Class category) { - if (fExcluded == null) - return false; - for(Class each : fExcluded) - if(each.isAssignableFrom(category)) + + private boolean classIsAssignableFromClassInArray(Class clazz, Class[] classes) { + for(Class each : classes) + if (each.isAssignableFrom(clazz)) return true; return false; } @@ -194,7 +182,7 @@ public Categories(Class klass, RunnerBuilder builder) } private Class[] getIncludedCategories(Class klass) { - IncludeCategory annotation = klass.getAnnotation(IncludeCategory.class); + IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class); return annotation == null ? null : annotation.value(); } 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 fb2e85dd0b84..e5fef4cecd13 100644 --- a/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java +++ b/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java @@ -139,13 +139,22 @@ public void testCountWithExplicitFilter() throws Throwable { } @Test - public void testCountWithExplicitExcludeFilter() throws Throwable { + public void testCountWithMultipleExcludeFilter() throws Throwable { CategoryFilter exclude = CategoryFilter.exclude(SlowTests.class, FastTests.class); 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(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()); + } @Test public void categoryFilterLeavesOnlyMatchingMethods() From 942e89f94eaf8e3b754923cf90167fb4ded1780d Mon Sep 17 00:00:00 2001 From: Andrew Matheny Date: Thu, 27 Oct 2011 18:02:50 -0400 Subject: [PATCH 5/5] Fixed minor style issues --- .../org/junit/experimental/categories/Categories.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/junit/experimental/categories/Categories.java b/src/main/java/org/junit/experimental/categories/Categories.java index 772f27cafa90..ee46c8d4717d 100644 --- a/src/main/java/org/junit/experimental/categories/Categories.java +++ b/src/main/java/org/junit/experimental/categories/Categories.java @@ -101,10 +101,10 @@ public String describe() { } private String join(String seperator, Class... values) { - if(values == null || values.length == 0) + if (values == null || values.length == 0) return ""; StringBuilder sb= new StringBuilder(); - for(Class each :values) + for(Class each : values) sb.append(each.toString()).append(seperator); return sb.substring(0, sb.length() - seperator.length()); } @@ -129,7 +129,7 @@ private boolean hasCorrectCategoryAnnotation(Description description) { private boolean categoryListPassesFilters(List> categories) { boolean hasIncludeCategory= false; - for (Class each : categories) { + for (Class each: categories) { if ((fExcluded != null) && classIsAssignableFromClassInArray(each, fExcluded)) return false; hasIncludeCategory= (fIncluded == null) || hasIncludeCategory || @@ -139,7 +139,7 @@ private boolean categoryListPassesFilters(List> categories) { } private boolean classIsAssignableFromClassInArray(Class clazz, Class[] classes) { - for(Class each : classes) + for(Class each: classes) if (each.isAssignableFrom(clazz)) return true; return false;