View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.surefire.common.junit48;
20  
21  import java.lang.annotation.Inherited;
22  import java.util.ArrayList;
23  import java.util.HashSet;
24  import java.util.Set;
25  
26  import org.apache.maven.surefire.group.match.AndGroupMatcher;
27  import org.apache.maven.surefire.group.match.GroupMatcher;
28  import org.apache.maven.surefire.group.match.InverseGroupMatcher;
29  import org.junit.experimental.categories.Category;
30  import org.junit.runner.Description;
31  import org.junit.runner.manipulation.Filter;
32  
33  import static java.util.Collections.addAll;
34  import static org.junit.runner.Description.createSuiteDescription;
35  
36  final class GroupMatcherCategoryFilter extends Filter {
37      /**
38       * Only traverse the tree if <code>@Category</code> annotation is inherited (since <code>junit 4.12</code>).
39       */
40      private static final boolean IS_CATEGORY_INHERITED = Category.class.isAnnotationPresent(Inherited.class);
41  
42      private final AndGroupMatcher matcher;
43  
44      GroupMatcherCategoryFilter(GroupMatcher included, GroupMatcher excluded) {
45          GroupMatcher invertedExclude = excluded == null ? null : new InverseGroupMatcher(excluded);
46          if (included != null || invertedExclude != null) {
47              matcher = new AndGroupMatcher();
48              if (included != null) {
49                  matcher.addMatcher(included);
50              }
51  
52              if (invertedExclude != null) {
53                  matcher.addMatcher(invertedExclude);
54              }
55          } else {
56              matcher = null;
57          }
58      }
59  
60      @Override
61      public boolean shouldRun(Description description) {
62          if (invalidTestClass(description)) {
63              return shouldRun(description, null, null);
64          }
65  
66          if (describesTestClass(description)) // is a test class
67          {
68              Class<?> testClass = description.getTestClass();
69              return shouldRun(description, null, testClass);
70          } else
71          // is a test method
72          {
73              Class<?> testClass = description.getTestClass();
74              return shouldRun(description, createSuiteDescription(testClass), testClass);
75          }
76      }
77  
78      private boolean describesTestClass(Description description) {
79          String methodName = description.getMethodName();
80          // Description parser in Junit 4.8 can return "null" String.
81          return methodName == null || methodName.equals("null");
82      }
83  
84      private boolean invalidTestClass(Description description) {
85          return description.getTestClass() == null;
86      }
87  
88      private static void findSuperclassCategories(Set<Class<?>> cats, Class<?> clazz) {
89          if (IS_CATEGORY_INHERITED && hasSuperclass(clazz)) {
90              Category cat = clazz.getSuperclass().getAnnotation(Category.class);
91              if (cat != null) {
92                  // Found categories in current superclass
93                  addAll(cats, cat.value());
94              }
95              // Search the hierarchy
96              findSuperclassCategories(cats, clazz.getSuperclass());
97          }
98      }
99  
100     private static boolean hasSuperclass(Class<?> clazz) {
101         return clazz != null && clazz.getSuperclass() != null;
102     }
103 
104     private boolean shouldRun(Description description, Description parent, Class<?> parentClass) {
105         if (matcher == null) {
106             return true;
107         } else {
108             Set<Class<?>> cats = new HashSet<>();
109             Category cat = description.getAnnotation(Category.class);
110             if (cat != null) {
111                 // Found categories in current description
112                 addAll(cats, cat.value());
113             }
114 
115             if (parent != null) {
116                 cat = parent.getAnnotation(Category.class);
117                 if (cat != null) {
118                     // Found categories in current parent
119                     addAll(cats, cat.value());
120                 }
121             }
122             if (parentClass != null) {
123                 findSuperclassCategories(cats, parentClass);
124             }
125 
126             Class<?> testClass = description.getTestClass();
127             if (testClass != null) {
128                 cat = testClass.getAnnotation(Category.class);
129                 if (cat != null) {
130                     // Found categories in current testClass
131                     addAll(cats, cat.value());
132                 }
133             }
134 
135             cats.remove(null);
136             boolean result = matcher.enabled(cats.toArray(new Class<?>[cats.size()]));
137 
138             if (!result) {
139                 ArrayList<Description> children = description.getChildren();
140                 if (children != null) {
141                     for (Description child : children) {
142                         if (shouldRun(child, description, null)) {
143                             result = true;
144                             break;
145                         }
146                     }
147                 }
148             }
149 
150             return result;
151         }
152     }
153 
154     @Override
155     public String describe() {
156         return matcher == null ? "ANY" : matcher.toString();
157     }
158 }