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.HashMap;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Properties;
29  import java.util.Set;
30  import org.apache.maven.shared.utils.io.SelectorUtils;
31  import org.apache.maven.surefire.booter.ProviderParameterNames;
32  import org.apache.maven.surefire.group.match.AndGroupMatcher;
33  import org.apache.maven.surefire.group.match.GroupMatcher;
34  import org.apache.maven.surefire.group.match.InverseGroupMatcher;
35  import org.apache.maven.surefire.group.parse.GroupMatcherParser;
36  import org.apache.maven.surefire.group.parse.ParseException;
37  
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          // GroupMatcher included = commaSeparatedListToFilters( groups );
88          // GroupMatcher excluded = commaSeparatedListToFilters( excludedGroups
89          // );
90  
91          if ( included != null && testClassLoader != null )
92          {
93              included.loadGroupClasses( testClassLoader );
94          }
95  
96          if ( excluded != null && testClassLoader != null )
97          {
98              excluded.loadGroupClasses( testClassLoader );
99          }
100 
101         return new GroupMatcherCategoryFilter( included, excluded );
102     }
103 
104     // private GroupMatcher commaSeparatedListToFilters( String str )
105     // {
106     // List<GroupMatcher> included = new ArrayList<GroupMatcher>();
107     // if ( str != null )
108     // {
109     // for ( String group : str.split( "," ) )
110     // {
111     // group = group.trim();
112     // if ( group == null || group.length() == 0)
113     // {
114     // continue;
115     // }
116     //
117     // try
118     // {
119     // GroupMatcher matcher = new GroupMatcherParser( group ).parse();
120     // included.add( matcher );
121     // }
122     // catch ( ParseException e )
123     // {
124     // throw new IllegalArgumentException( "Invalid group expression: '" + group
125     // + "'. Reason: "
126     // + e.getMessage(), e );
127     // }
128     //
129     // // Class<?> categoryType = classloadCategory( group );
130     // // included.add( Categories.CategoryFilter.include( categoryType ) );
131     // }
132     // }
133     //
134     // return included.isEmpty() ? null : new OrGroupMatcher( included );
135     // }
136 
137     public Filter createMethodFilter( String requestedTestMethod )
138     {
139         return new MethodFilter( requestedTestMethod );
140     }
141 
142     public Filter and( Filter filter1, Filter filter2 )
143     {
144         return new AndFilter( filter1, filter2 );
145     }
146 
147     private static class MethodFilter
148         extends Filter
149     {
150         private final String requestedTestMethod;
151 
152         public MethodFilter( String requestedTestMethod )
153         {
154             this.requestedTestMethod = requestedTestMethod;
155         }
156 
157         @Override
158         public boolean shouldRun( Description description )
159         {
160             for ( Description o : description.getChildren() )
161             {
162                 if ( isDescriptionMatch( o ) )
163                 {
164                     return true;
165                 }
166 
167             }
168             return isDescriptionMatch( description );
169         }
170 
171         private boolean isDescriptionMatch( Description description )
172         {
173             return description.getMethodName() != null
174                 && SelectorUtils.match( requestedTestMethod, description.getMethodName() );
175         }
176 
177         @Override
178         public String describe()
179         {
180             return "By method" + requestedTestMethod;
181         }
182     }
183 
184     private static class GroupMatcherCategoryFilter
185         extends Filter
186     {
187 
188         private AndGroupMatcher matcher;
189 
190         private Map<Description, Boolean> shouldRunAnswers = new HashMap<Description, Boolean>();
191 
192         public GroupMatcherCategoryFilter( GroupMatcher included, GroupMatcher excluded )
193         {
194             GroupMatcher invertedExclude = excluded == null ? null : new InverseGroupMatcher( excluded );
195             if ( included != null || invertedExclude != null )
196             {
197                 matcher = new AndGroupMatcher();
198                 if ( included != null )
199                 {
200                     matcher.addMatcher( included );
201                 }
202 
203                 if ( invertedExclude != null )
204                 {
205                     matcher.addMatcher( invertedExclude );
206                 }
207             }
208         }
209 
210         @Override
211         public boolean shouldRun( Description description )
212         {
213             return shouldRun( description,
214                               ( description.getMethodName() == null ? null
215                                               : Description.createSuiteDescription( description.getTestClass() ) ) );
216         }
217 
218         private boolean shouldRun( Description description, Description parent )
219         {
220             Boolean result = shouldRunAnswers.get( description );
221             if ( result != null )
222             {
223                 return result;
224             }
225 
226             if ( matcher == null )
227             {
228                 return true;
229             }
230 
231             // System.out.println( "\n\nMatcher: " + matcher );
232             // System.out.println( "Checking: " + description.getClassName()
233             // + ( parent == null ? "" : "#" + description.getMethodName() ) );
234 
235             Set<Class<?>> cats = new HashSet<Class<?>>();
236             Category cat = description.getAnnotation( Category.class );
237             if ( cat != null )
238             {
239                 // System.out.println( "Adding categories: " + Arrays.toString(
240                 // cat.value() ) );
241                 cats.addAll( Arrays.asList( cat.value() ) );
242             }
243 
244             if ( parent != null )
245             {
246                 cat = parent.getAnnotation( Category.class );
247                 if ( cat != null )
248                 {
249                     // System.out.println( "Adding class-level categories: " +
250                     // Arrays.toString( cat.value() ) );
251                     cats.addAll( Arrays.asList( cat.value() ) );
252                 }
253             }
254 
255             // System.out.println( "Checking " + cats.size() + " categories..."
256             // );
257             //
258             // System.out.println( "Enabled? " + ( matcher.enabled(
259             // cats.toArray( new Class<?>[] {} ) ) ) + "\n\n" );
260             result = matcher.enabled( cats.toArray( new Class<?>[] {} ) );
261 
262             if ( parent == null )
263             {
264                 if ( cats.size() == 0 )
265                 {
266                     // System.out.println(
267                     // "Allow method-level filtering by PASSing class-level shouldRun() test..."
268                     // );
269                     result = true;
270                 }
271                 else if ( !result )
272                 {
273                     ArrayList<Description> children = description.getChildren();
274                     if ( children != null )
275                     {
276                         for ( Description child : children )
277                         {
278                             if ( shouldRun( child, description ) )
279                             {
280                                 result = true;
281                                 break;
282                             }
283                         }
284                     }
285                 }
286             }
287 
288             shouldRunAnswers.put( description, result );
289             return result == null ? false : result;
290         }
291 
292         @Override
293         public String describe()
294         {
295             return matcher == null ? "ANY" : matcher.toString();
296         }
297 
298     }
299 
300     private static class AndFilter
301         extends Filter
302     {
303         private final Filter filter1;
304 
305         private final Filter filter2;
306 
307         public AndFilter( Filter filter1, Filter filter2 )
308         {
309             this.filter1 = filter1;
310             this.filter2 = filter2;
311         }
312 
313         @Override
314         public boolean shouldRun( Description description )
315         {
316             return filter1.shouldRun( description ) && filter2.shouldRun( description );
317         }
318 
319         @Override
320         public String describe()
321         {
322             return filter1.describe() + " AND " + filter2.describe();
323         }
324     }
325 
326     @SuppressWarnings( "unused" )
327     private static class CombinedCategoryFilter
328         extends Filter
329     {
330         private final List<Filter> includedFilters;
331 
332         private final List<Filter> excludedFilters;
333 
334         public CombinedCategoryFilter( List<Filter> includedFilters, List<Filter> excludedFilters )
335         {
336             this.includedFilters = includedFilters;
337             this.excludedFilters = excludedFilters;
338         }
339 
340         @Override
341         public boolean shouldRun( Description description )
342         {
343             return ( includedFilters.isEmpty() || inOneOfFilters( includedFilters, description ) )
344                 && ( excludedFilters.isEmpty() || !inOneOfFilters( excludedFilters, description ) );
345         }
346 
347         private boolean inOneOfFilters( List<Filter> filters, Description description )
348         {
349             for ( Filter f : filters )
350             {
351                 if ( f.shouldRun( description ) )
352                 {
353                     return true;
354                 }
355             }
356             return false;
357         }
358 
359         @Override
360         public String describe()
361         {
362             StringBuilder sb = new StringBuilder();
363             if ( !includedFilters.isEmpty() )
364             {
365                 sb.append( "(" );
366                 sb.append( joinFilters( includedFilters, " OR " ) );
367                 sb.append( ")" );
368                 if ( !excludedFilters.isEmpty() )
369                 {
370                     sb.append( " AND " );
371                 }
372             }
373             if ( !excludedFilters.isEmpty() )
374             {
375                 sb.append( "NOT (" );
376                 sb.append( joinFilters( includedFilters, " OR " ) );
377                 sb.append( ")" );
378             }
379 
380             return sb.toString();
381         }
382 
383         private String joinFilters( List<Filter> filters, String sep )
384         {
385             int i = 0;
386             StringBuilder sb = new StringBuilder();
387             for ( Filter f : filters )
388             {
389                 if ( i++ > 0 )
390                 {
391                     sb.append( sep );
392                 }
393                 sb.append( f.describe() );
394             }
395             return sb.toString();
396         }
397     }
398 
399 }