View Javadoc

1   package org.apache.maven.surefire.common.junit48;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Properties;
29  import java.util.Set;
30  
31  import org.apache.maven.shared.utils.io.SelectorUtils;
32  import org.apache.maven.surefire.booter.ProviderParameterNames;
33  import org.apache.maven.surefire.group.match.AndGroupMatcher;
34  import org.apache.maven.surefire.group.match.GroupMatcher;
35  import org.apache.maven.surefire.group.match.InverseGroupMatcher;
36  import org.apache.maven.surefire.group.parse.GroupMatcherParser;
37  import org.apache.maven.surefire.group.parse.ParseException;
38  import org.junit.experimental.categories.Category;
39  import org.junit.runner.Description;
40  import org.junit.runner.manipulation.Filter;
41  
42  /**
43   * @author Todd Lipcon
44   */
45  public class FilterFactory
46  {
47      private final ClassLoader testClassLoader;
48  
49      public FilterFactory( ClassLoader testClassLoader )
50      {
51          this.testClassLoader = testClassLoader;
52      }
53  
54      public Filter createGroupFilter( Properties providerProperties )
55      {
56          String groups = providerProperties.getProperty( ProviderParameterNames.TESTNG_GROUPS_PROP );
57          String excludedGroups = providerProperties.getProperty( ProviderParameterNames.TESTNG_EXCLUDEDGROUPS_PROP );
58  
59          GroupMatcher included = null;
60          if ( groups != null && groups.trim().length() > 0 )
61          {
62              try
63              {
64                  included = new GroupMatcherParser( groups ).parse();
65              }
66              catch ( ParseException e )
67              {
68                  throw new IllegalArgumentException( "Invalid group expression: '" + groups + "'. Reason: "
69                      + e.getMessage(), e );
70              }
71          }
72  
73          GroupMatcher excluded = null;
74          if ( excludedGroups != null && excludedGroups.trim().length() > 0 )
75          {
76              try
77              {
78                  excluded = new GroupMatcherParser( excludedGroups ).parse();
79              }
80              catch ( ParseException e )
81              {
82                  throw new IllegalArgumentException( "Invalid group expression: '" + excludedGroups + "'. Reason: "
83                      + e.getMessage(), e );
84              }
85          }
86  
87          if ( included != null && testClassLoader != null )
88          {
89              included.loadGroupClasses( testClassLoader );
90          }
91  
92          if ( excluded != null && testClassLoader != null )
93          {
94              excluded.loadGroupClasses( testClassLoader );
95          }
96  
97          return new GroupMatcherCategoryFilter( included, excluded );
98      }
99  
100     public Filter createMethodFilter( String requestedTestMethod )
101     {
102         return new MethodFilter( requestedTestMethod );
103     }
104 
105     public Filter and( Filter filter1, Filter filter2 )
106     {
107         return new AndFilter( filter1, filter2 );
108     }
109 
110     private static class MethodFilter
111         extends Filter
112     {
113         private final String requestedTestMethod;
114 
115         public MethodFilter( String requestedTestMethod )
116         {
117             this.requestedTestMethod = requestedTestMethod;
118         }
119 
120         @Override
121         public boolean shouldRun( Description description )
122         {
123             for ( Description o : description.getChildren() )
124             {
125                 if ( isDescriptionMatch( o ) )
126                 {
127                     return true;
128                 }
129 
130             }
131             return isDescriptionMatch( description );
132         }
133 
134         private boolean isDescriptionMatch( Description description )
135         {
136             return description.getMethodName() != null
137                 && SelectorUtils.match( requestedTestMethod, description.getMethodName() );
138         }
139 
140         @Override
141         public String describe()
142         {
143             return "By method" + requestedTestMethod;
144         }
145     }
146 
147     private static class GroupMatcherCategoryFilter
148         extends Filter
149     {
150 
151         private AndGroupMatcher matcher;
152 
153         public GroupMatcherCategoryFilter( GroupMatcher included, GroupMatcher excluded )
154         {
155             GroupMatcher invertedExclude = excluded == null ? null : new InverseGroupMatcher( excluded );
156             if ( included != null || invertedExclude != null )
157             {
158                 matcher = new AndGroupMatcher();
159                 if ( included != null )
160                 {
161                     matcher.addMatcher( included );
162                 }
163 
164                 if ( invertedExclude != null )
165                 {
166                     matcher.addMatcher( invertedExclude );
167                 }
168             }
169         }
170 
171         @Override
172         public boolean shouldRun( Description description )
173         {
174             if ( description.getMethodName() == null || description.getTestClass() == null )
175             {
176                 return shouldRun( description, null, null );
177             }
178             else
179             {
180                 return shouldRun( description, Description.createSuiteDescription( description.getTestClass() ),
181                                   description.getTestClass() );
182             }
183         }
184 
185         private Collection<Class<?>> findSuperclassCategories( Class<?> clazz )
186         {
187             if ( clazz != null && clazz.getSuperclass() != null )
188             {
189                 Category cat = clazz.getSuperclass().getAnnotation( Category.class );
190                 if ( cat != null )
191                 {
192                     return new HashSet<Class<?>>( Arrays.asList( cat.value() ) );
193                 }
194                 else
195                 {
196                     return findSuperclassCategories( clazz.getSuperclass() );
197                 }
198             }
199 
200             return Collections.emptySet();
201         }
202 
203         private boolean shouldRun( Description description, Description parent, Class<?> parentClass )
204         {
205             if ( matcher == null )
206             {
207                 return true;
208             }
209 
210             Set<Class<?>> cats = new HashSet<Class<?>>();
211             Category cat = description.getAnnotation( Category.class );
212             if ( cat != null )
213             {
214                 cats.addAll( Arrays.asList( cat.value() ) );
215             }
216 
217             if ( parent != null )
218             {
219                 cat = parent.getAnnotation( Category.class );
220                 if ( cat != null )
221                 {
222                     cats.addAll( Arrays.asList( cat.value() ) );
223                 }
224             }
225 
226             if ( parentClass != null )
227             {
228                 cats.addAll( findSuperclassCategories( parentClass ) );
229             }
230 
231             boolean result = matcher.enabled( cats.toArray( new Class<?>[] {} ) );
232 
233             if ( parent == null )
234             {
235                 if ( cats.size() == 0 )
236                 {
237                     result = true;
238                 }
239                 else if ( !result )
240                 {
241                     ArrayList<Description> children = description.getChildren();
242                     if ( children != null )
243                     {
244                         for ( Description child : children )
245                         {
246                             if ( shouldRun( child, description, null ) )
247                             {
248                                 result = true;
249                                 break;
250                             }
251                         }
252                     }
253                 }
254             }
255 
256             return result;
257         }
258 
259         @Override
260         public String describe()
261         {
262             return matcher == null ? "ANY" : matcher.toString();
263         }
264 
265     }
266 
267     private static class AndFilter
268         extends Filter
269     {
270         private final Filter filter1;
271 
272         private final Filter filter2;
273 
274         public AndFilter( Filter filter1, Filter filter2 )
275         {
276             this.filter1 = filter1;
277             this.filter2 = filter2;
278         }
279 
280         @Override
281         public boolean shouldRun( Description description )
282         {
283             return filter1.shouldRun( description ) && filter2.shouldRun( description );
284         }
285 
286         @Override
287         public String describe()
288         {
289             return filter1.describe() + " AND " + filter2.describe();
290         }
291     }
292 
293     @SuppressWarnings( "unused" )
294     private static class CombinedCategoryFilter
295         extends Filter
296     {
297         private final List<Filter> includedFilters;
298 
299         private final List<Filter> excludedFilters;
300 
301         public CombinedCategoryFilter( List<Filter> includedFilters, List<Filter> excludedFilters )
302         {
303             this.includedFilters = includedFilters;
304             this.excludedFilters = excludedFilters;
305         }
306 
307         @Override
308         public boolean shouldRun( Description description )
309         {
310             return ( includedFilters.isEmpty() || inOneOfFilters( includedFilters, description ) )
311                 && ( excludedFilters.isEmpty() || !inOneOfFilters( excludedFilters, description ) );
312         }
313 
314         private boolean inOneOfFilters( List<Filter> filters, Description description )
315         {
316             for ( Filter f : filters )
317             {
318                 if ( f.shouldRun( description ) )
319                 {
320                     return true;
321                 }
322             }
323             return false;
324         }
325 
326         @Override
327         public String describe()
328         {
329             StringBuilder sb = new StringBuilder();
330             if ( !includedFilters.isEmpty() )
331             {
332                 sb.append( "(" );
333                 sb.append( joinFilters( includedFilters, " OR " ) );
334                 sb.append( ")" );
335                 if ( !excludedFilters.isEmpty() )
336                 {
337                     sb.append( " AND " );
338                 }
339             }
340             if ( !excludedFilters.isEmpty() )
341             {
342                 sb.append( "NOT (" );
343                 sb.append( joinFilters( includedFilters, " OR " ) );
344                 sb.append( ")" );
345             }
346 
347             return sb.toString();
348         }
349 
350         private String joinFilters( List<Filter> filters, String sep )
351         {
352             int i = 0;
353             StringBuilder sb = new StringBuilder();
354             for ( Filter f : filters )
355             {
356                 if ( i++ > 0 )
357                 {
358                     sb.append( sep );
359                 }
360                 sb.append( f.describe() );
361             }
362             return sb.toString();
363         }
364     }
365 
366 }