From 694bafe357393ff575af4c3c81ac39f95318ce92 Mon Sep 17 00:00:00 2001 From: Henning Gross Date: Tue, 4 Dec 2012 22:21:27 +0100 Subject: [PATCH 1/6] Enables Categories.java to handle optional @Category - inheritance --- .../experimental/categories/Categories.java | 63 +++++++++++++----- .../experimental/categories/CategoryTest.java | 2 +- ...nIncludeCategoryAndExludeCategoryTest.java | 65 +++++++++++++++++++ 3 files changed, 111 insertions(+), 19 deletions(-) create mode 100644 src/test/java/org/junit/tests/experimental/categories/MultipleClassesInIncludeCategoryAndExludeCategoryTest.java diff --git a/src/main/java/org/junit/experimental/categories/Categories.java b/src/main/java/org/junit/experimental/categories/Categories.java index 49a4433b5e9e..67ad29ac43a5 100644 --- a/src/main/java/org/junit/experimental/categories/Categories.java +++ b/src/main/java/org/junit/experimental/categories/Categories.java @@ -66,32 +66,40 @@ public class Categories extends Suite { @Retention(RetentionPolicy.RUNTIME) public @interface IncludeCategory { - public Class value(); + public Class[] value(); } @Retention(RetentionPolicy.RUNTIME) public @interface ExcludeCategory { - public Class value(); + public Class[] value(); } public static class CategoryFilter extends Filter { + public static CategoryFilter include(Class categoryType) { + return new CategoryFilter(new Class[]{categoryType}, null); + } + + public static CategoryFilter include(Class[] categoryType) { return new CategoryFilter(categoryType, null); } - private final Class fIncluded; + private final Class[] includedCategories; - private final Class fExcluded; + private final Class[] excludedCategories; - public CategoryFilter(Class includedCategory, - Class excludedCategory) { - fIncluded = includedCategory; - fExcluded = excludedCategory; + public CategoryFilter(Class[] includedCategories, + Class[] excludedCategories) { + this.includedCategories = includedCategories; + this.excludedCategories = excludedCategories; } @Override public String describe() { - return "category " + fIncluded; + Integer numIncludedCategories = includedCategories == null ? 0 : includedCategories.length; + Integer numExcludedCategories = excludedCategories == null ? 0 : excludedCategories.length; + return "category. contains: " + numIncludedCategories + " included categories and " + + numExcludedCategories + " excluded categories."; } @Override @@ -107,18 +115,37 @@ public boolean shouldRun(Description description) { return false; } + /** + * Checks if the given array of classes (eg includedCategories) contains a given class + * This is used to determine if a specified class in a {@link Category} annotation is represented in the + * class-array specified in a {@link IncludeCategory} or {@link ExcludeCategory} annotation of a suite ran with + * {@link Categories} + * + * @param categories array of categories (specified value of {@link IncludeCategory} or {@link ExcludeCategory}) + * @param category defined category (one of the classes specified in {@link Category}) + * @return true if the given category class is assignable from any of the given classes in categories + */ + private boolean containsCategory(Class[] categories, Class category){ + for(Class cat : categories){ + if(cat.isAssignableFrom(category)){ + return true; + } + } + return false; + } + private boolean hasCorrectCategoryAnnotation(Description description) { List> categories = categories(description); if (categories.isEmpty()) { - return fIncluded == null; + return includedCategories == null; } - for (Class each : categories) { - if (fExcluded != null && fExcluded.isAssignableFrom(each)) { + for (Class category : categories) { + if (excludedCategories != null && containsCategory(excludedCategories, category)) { return false; } } - for (Class each : categories) { - if (fIncluded == null || fIncluded.isAssignableFrom(each)) { + for (Class category : categories) { + if (includedCategories == null || containsCategory(includedCategories, category)) { return true; } } @@ -156,20 +183,20 @@ 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) { + private Class[] getIncludedCategories(Class klass) { IncludeCategory annotation = klass.getAnnotation(IncludeCategory.class); return annotation == null ? null : annotation.value(); } - private Class getExcludedCategory(Class klass) { + private Class[] getExcludedCategories(Class klass) { ExcludeCategory annotation = klass.getAnnotation(ExcludeCategory.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 8083bbc52740..6b6e0e1577b9 100644 --- a/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java +++ b/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java @@ -211,7 +211,7 @@ public void ifNoTestsToRunUseErrorRunner() { @Test public void describeACategoryFilter() { CategoryFilter filter = CategoryFilter.include(SlowTests.class); - assertEquals("category " + SlowTests.class, filter.describe()); + assertEquals("category. contains: 1 included categories and 0 excluded categories.", filter.describe()); } public static class OneThatIsBothFastAndSlow { diff --git a/src/test/java/org/junit/tests/experimental/categories/MultipleClassesInIncludeCategoryAndExludeCategoryTest.java b/src/test/java/org/junit/tests/experimental/categories/MultipleClassesInIncludeCategoryAndExludeCategoryTest.java new file mode 100644 index 000000000000..68be899b715d --- /dev/null +++ b/src/test/java/org/junit/tests/experimental/categories/MultipleClassesInIncludeCategoryAndExludeCategoryTest.java @@ -0,0 +1,65 @@ +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 junit.framework.Assert.assertEquals; + +/** + * Author: Henning Gross + * Date: 04.12.12 + */ +public class MultipleClassesInIncludeCategoryAndExludeCategoryTest { + + // category markers + public interface IntegrationTests { + } + + public interface SlowTests { + } + + public static class A { + @Category(IntegrationTests.class) + @Test + public void a() { + } + + @Category(SlowTests.class) + @Test + public void b() { + } + + @Test + public void c() { + } + } + + @RunWith(Categories.class) + @Categories.IncludeCategory({SlowTests.class, IntegrationTests.class}) + @Suite.SuiteClasses({A.class}) + public static class IncludeCategorySuite { + } + + @Test + public void testMultipleClassesInIncludeCategory() { + Result result = JUnitCore.runClasses(IncludeCategorySuite.class); + assertEquals(2, result.getRunCount()); + } + + @RunWith(Categories.class) + @Categories.ExcludeCategory({SlowTests.class, IntegrationTests.class}) + @Suite.SuiteClasses({A.class}) + public static class ExcludeCategorySuite { + } + + @Test + public void testMultipleClassesInExcludeCategory() { + Result result = JUnitCore.runClasses(ExcludeCategorySuite.class); + assertEquals(1, result.getRunCount()); + } +} From 42ff514b7e8f21a61190a573de72830b5d996fb0 Mon Sep 17 00:00:00 2001 From: Henning Gross Date: Tue, 4 Dec 2012 22:41:45 +0100 Subject: [PATCH 2/6] Minor: JavaDoc --- .../junit/experimental/categories/Categories.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/org/junit/experimental/categories/Categories.java b/src/main/java/org/junit/experimental/categories/Categories.java index 67ad29ac43a5..665346fd5246 100644 --- a/src/main/java/org/junit/experimental/categories/Categories.java +++ b/src/main/java/org/junit/experimental/categories/Categories.java @@ -64,11 +64,23 @@ public class Categories extends Suite { // someday enable a better new implementation. // https://github.com/KentBeck/junit/issues/issue/172 + /** + * {@link IncludeCategory} determines which Tests are ran by {@link Categories}. + *

+ * If specified all Tests annotated with classes specified in the value of this annotation will be ran by + * {@link Categories} as long they are not specified using {@link ExcludeCategory} + */ @Retention(RetentionPolicy.RUNTIME) public @interface IncludeCategory { public Class[] value(); } + /** + * {@link ExcludeCategory} determines which Tests are skipped by {@link Categories}. + *

+ * If specified all Tests annotated with classes specified in the value of this annotation will be skipped by + * {@link Categories} regardless of being mentioned in {@link IncludeCategory} + */ @Retention(RetentionPolicy.RUNTIME) public @interface ExcludeCategory { public Class[] value(); From 2a434174e9721aafe6ad65fc4d4c2654e9c8d3c2 Mon Sep 17 00:00:00 2001 From: Henning Gross Date: Tue, 4 Dec 2012 22:48:44 +0100 Subject: [PATCH 3/6] Another test for behaviour descriped in JavaDoc --- ...esInIncludeCategoryAndExludeCategoryTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/test/java/org/junit/tests/experimental/categories/MultipleClassesInIncludeCategoryAndExludeCategoryTest.java b/src/test/java/org/junit/tests/experimental/categories/MultipleClassesInIncludeCategoryAndExludeCategoryTest.java index 68be899b715d..2a3f73c68e79 100644 --- a/src/test/java/org/junit/tests/experimental/categories/MultipleClassesInIncludeCategoryAndExludeCategoryTest.java +++ b/src/test/java/org/junit/tests/experimental/categories/MultipleClassesInIncludeCategoryAndExludeCategoryTest.java @@ -47,6 +47,7 @@ public static class IncludeCategorySuite { @Test public void testMultipleClassesInIncludeCategory() { + // a and b will be ran Result result = JUnitCore.runClasses(IncludeCategorySuite.class); assertEquals(2, result.getRunCount()); } @@ -59,6 +60,21 @@ public static class ExcludeCategorySuite { @Test public void testMultipleClassesInExcludeCategory() { + // only c will be ran (not annotated, due to no @IncludeCategory specified) + Result result = JUnitCore.runClasses(ExcludeCategorySuite.class); + assertEquals(1, result.getRunCount()); + } + + @RunWith(Categories.class) + @Categories.IncludeCategory(SlowTests.class) + @Categories.ExcludeCategory({SlowTests.class}) + @Suite.SuiteClasses({A.class}) + public static class OverrideIncludeCategorySuite { + } + + @Test + public void testOverrideCategory() { + // only a will be ran (IntegrationTest) Result result = JUnitCore.runClasses(ExcludeCategorySuite.class); assertEquals(1, result.getRunCount()); } From dc2349af622e3e000ff81cea2c5fde7c98c1a951 Mon Sep 17 00:00:00 2001 From: Henning Gross Date: Wed, 5 Dec 2012 22:40:39 +0100 Subject: [PATCH 4/6] Implements suggested optimizations and addresses formatting issues. --- .../experimental/categories/Categories.java | 62 ++++++++++++++----- .../experimental/categories/CategoryTest.java | 2 +- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/junit/experimental/categories/Categories.java b/src/main/java/org/junit/experimental/categories/Categories.java index 665346fd5246..3f8eefa25e5f 100644 --- a/src/main/java/org/junit/experimental/categories/Categories.java +++ b/src/main/java/org/junit/experimental/categories/Categories.java @@ -92,26 +92,54 @@ public static CategoryFilter include(Class categoryType) { return new CategoryFilter(new Class[]{categoryType}, null); } - public static CategoryFilter include(Class[] categoryType) { + public static CategoryFilter include(Class... categoryType) { return new CategoryFilter(categoryType, null); } - private final Class[] includedCategories; + private Class[] fIncludedCategories = new Class[]{}; - private final Class[] excludedCategories; + private Class[] fExcludedCategories = new Class[]{}; + + public CategoryFilter(Class includedCategory, Class excludedCategory) { + if (includedCategory != null){ + fIncludedCategories = new Class[]{includedCategory}; + } + if (excludedCategory != null){ + fExcludedCategories = new Class[]{excludedCategory}; + } + } public CategoryFilter(Class[] includedCategories, Class[] excludedCategories) { - this.includedCategories = includedCategories; - this.excludedCategories = excludedCategories; + if (includedCategories != null){ + fIncludedCategories = includedCategories; + } + if (excludedCategories != null){ + fExcludedCategories = excludedCategories; + } } @Override public String describe() { - Integer numIncludedCategories = includedCategories == null ? 0 : includedCategories.length; - Integer numExcludedCategories = excludedCategories == null ? 0 : excludedCategories.length; - return "category. contains: " + numIncludedCategories + " included categories and " - + numExcludedCategories + " excluded categories."; + StringBuilder description = new StringBuilder("Categories: "); + if (fIncludedCategories.length > 0){ + description.append("Included categories: "); + for (Class each : fIncludedCategories){ + description.append(each.getSimpleName()).append(" "); + } + } else{ + description.append("No included categories "); + } + description.append("| "); + if (fExcludedCategories.length > 0){ + description.append("Excluded categories: "); + for (Class each : fExcludedCategories){ + description.append(each.getSimpleName()).append(" "); + } + } else{ + description.append("No excluded categories"); + } + return description.toString(); } @Override @@ -128,7 +156,7 @@ public boolean shouldRun(Description description) { } /** - * Checks if the given array of classes (eg includedCategories) contains a given class + * Checks if the given array of classes (eg fIncludedCategories) contains a given class * This is used to determine if a specified class in a {@link Category} annotation is represented in the * class-array specified in a {@link IncludeCategory} or {@link ExcludeCategory} annotation of a suite ran with * {@link Categories} @@ -138,8 +166,8 @@ public boolean shouldRun(Description description) { * @return true if the given category class is assignable from any of the given classes in categories */ private boolean containsCategory(Class[] categories, Class category){ - for(Class cat : categories){ - if(cat.isAssignableFrom(category)){ + for (Class each : categories){ + if (each.isAssignableFrom(category)){ return true; } } @@ -149,15 +177,15 @@ private boolean containsCategory(Class[] categories, Class category){ private boolean hasCorrectCategoryAnnotation(Description description) { List> categories = categories(description); if (categories.isEmpty()) { - return includedCategories == null; + return fIncludedCategories.length == 0; } - for (Class category : categories) { - if (excludedCategories != null && containsCategory(excludedCategories, category)) { + for (Class each : categories) { + if (fExcludedCategories.length > 0 && containsCategory(fExcludedCategories, each)) { return false; } } - for (Class category : categories) { - if (includedCategories == null || containsCategory(includedCategories, category)) { + for (Class each : categories) { + if (fIncludedCategories.length == 0 || containsCategory(fIncludedCategories, each)) { return true; } } 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 6b6e0e1577b9..3b4ad4b22e54 100644 --- a/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java +++ b/src/test/java/org/junit/tests/experimental/categories/CategoryTest.java @@ -211,7 +211,7 @@ public void ifNoTestsToRunUseErrorRunner() { @Test public void describeACategoryFilter() { CategoryFilter filter = CategoryFilter.include(SlowTests.class); - assertEquals("category. contains: 1 included categories and 0 excluded categories.", filter.describe()); + assertEquals("Categories: Included categories: SlowTests | No excluded categories", filter.describe()); } public static class OneThatIsBothFastAndSlow { From 7dbfb2baa46da5e7a4532d472734cbf5dd2e0ebc Mon Sep 17 00:00:00 2001 From: Henning Gross Date: Thu, 6 Dec 2012 18:46:20 +0100 Subject: [PATCH 5/6] Introduces suggested optimzations. --- .../java/org/junit/experimental/categories/Categories.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/junit/experimental/categories/Categories.java b/src/main/java/org/junit/experimental/categories/Categories.java index 3f8eefa25e5f..6ac73efdef2e 100644 --- a/src/main/java/org/junit/experimental/categories/Categories.java +++ b/src/main/java/org/junit/experimental/categories/Categories.java @@ -72,7 +72,7 @@ public class Categories extends Suite { */ @Retention(RetentionPolicy.RUNTIME) public @interface IncludeCategory { - public Class[] value(); + public Class[] value() default {}; } /** @@ -83,7 +83,7 @@ public class Categories extends Suite { */ @Retention(RetentionPolicy.RUNTIME) public @interface ExcludeCategory { - public Class[] value(); + public Class[] value() default {}; } public static class CategoryFilter extends Filter { @@ -180,7 +180,7 @@ private boolean hasCorrectCategoryAnnotation(Description description) { return fIncludedCategories.length == 0; } for (Class each : categories) { - if (fExcludedCategories.length > 0 && containsCategory(fExcludedCategories, each)) { + if (containsCategory(fExcludedCategories, each)) { return false; } } From 548c905f2bc850622d16f78208d46a610a0d9e07 Mon Sep 17 00:00:00 2001 From: Henning Gross Date: Thu, 6 Dec 2012 21:43:38 +0100 Subject: [PATCH 6/6] minor: spaces before brackets --- .../experimental/categories/Categories.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/junit/experimental/categories/Categories.java b/src/main/java/org/junit/experimental/categories/Categories.java index 6ac73efdef2e..d8c99d020773 100644 --- a/src/main/java/org/junit/experimental/categories/Categories.java +++ b/src/main/java/org/junit/experimental/categories/Categories.java @@ -96,25 +96,25 @@ public static CategoryFilter include(Class... categoryType) { return new CategoryFilter(categoryType, null); } - private Class[] fIncludedCategories = new Class[]{}; + private Class[] fIncludedCategories = new Class[] {}; - private Class[] fExcludedCategories = new Class[]{}; + private Class[] fExcludedCategories = new Class[] {}; public CategoryFilter(Class includedCategory, Class excludedCategory) { - if (includedCategory != null){ - fIncludedCategories = new Class[]{includedCategory}; + if (includedCategory != null) { + fIncludedCategories = new Class[] {includedCategory}; } - if (excludedCategory != null){ - fExcludedCategories = new Class[]{excludedCategory}; + if (excludedCategory != null) { + fExcludedCategories = new Class[] {excludedCategory}; } } public CategoryFilter(Class[] includedCategories, Class[] excludedCategories) { - if (includedCategories != null){ + if (includedCategories != null) { fIncludedCategories = includedCategories; } - if (excludedCategories != null){ + if (excludedCategories != null) { fExcludedCategories = excludedCategories; } } @@ -122,21 +122,21 @@ public CategoryFilter(Class[] includedCategories, @Override public String describe() { StringBuilder description = new StringBuilder("Categories: "); - if (fIncludedCategories.length > 0){ + if (fIncludedCategories.length > 0) { description.append("Included categories: "); - for (Class each : fIncludedCategories){ + for (Class each : fIncludedCategories) { description.append(each.getSimpleName()).append(" "); } - } else{ + } else { description.append("No included categories "); } description.append("| "); - if (fExcludedCategories.length > 0){ + if (fExcludedCategories.length > 0) { description.append("Excluded categories: "); - for (Class each : fExcludedCategories){ + for (Class each : fExcludedCategories) { description.append(each.getSimpleName()).append(" "); } - } else{ + } else { description.append("No excluded categories"); } return description.toString(); @@ -165,9 +165,9 @@ public boolean shouldRun(Description description) { * @param category defined category (one of the classes specified in {@link Category}) * @return true if the given category class is assignable from any of the given classes in categories */ - private boolean containsCategory(Class[] categories, Class category){ - for (Class each : categories){ - if (each.isAssignableFrom(category)){ + private boolean containsCategory(Class[] categories, Class category) { + for (Class each : categories) { + if (each.isAssignableFrom(category)) { return true; } }