View Javadoc
1   package org.apache.maven.surefire.testset;
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 org.apache.maven.shared.utils.StringUtils;
23  import org.apache.maven.shared.utils.io.MatchPatterns;
24  import org.apache.maven.shared.utils.io.SelectorUtils;
25  
26  import java.io.File;
27  
28  /**
29   * Single pattern test filter resolved from multi pattern filter -Dtest=MyTest#test,AnotherTest#otherTest.
30   */
31  public final class ResolvedTest
32  {
33      /**
34       * Type of patterns in ResolvedTest constructor.
35       */
36      public static enum Type
37      {
38          CLASS, METHOD
39      }
40  
41      private static final String CLASS_FILE_EXTENSION = ".class";
42  
43      private static final String JAVA_FILE_EXTENSION = ".java";
44  
45      private static final String WILDCARD_PATH_PREFIX = "**/";
46  
47      private static final String WILDCARD_FILENAME_POSTFIX = ".*";
48  
49      private final String classPattern;
50  
51      private final String methodPattern;
52  
53      private final boolean isRegexTestClassPattern;
54  
55      private final boolean isRegexTestMethodPattern;
56  
57      private final String description;
58  
59      /**
60       * '*' means zero or more characters<br>
61       * '?' means one and only one character
62       * The pattern %regex[] prefix and suffix does not appear. The regex <code>pattern</code> is always
63       * unwrapped by the caller.
64       *
65       * @param classPattern     test class file pattern
66       * @param methodPattern    test method
67       * @param isRegex          {@code true} if regex
68       */
69      public ResolvedTest( String classPattern, String methodPattern, boolean isRegex )
70      {
71          classPattern = tryBlank( classPattern );
72          methodPattern = tryBlank( methodPattern );
73          description = description( classPattern, methodPattern, isRegex );
74  
75          if ( isRegex && classPattern != null )
76          {
77              classPattern = wrapRegex( classPattern );
78          }
79  
80          if ( isRegex && methodPattern != null )
81          {
82              methodPattern = wrapRegex( methodPattern );
83          }
84  
85          this.classPattern = reformatClassPattern( classPattern, isRegex );
86          this.methodPattern = methodPattern;
87          isRegexTestClassPattern = isRegex;
88          isRegexTestMethodPattern = isRegex;
89      }
90  
91      /**
92       * The regex <code>pattern</code> is always unwrapped.
93       */
94      public ResolvedTest( Type type, String pattern, boolean isRegex )
95      {
96          pattern = tryBlank( pattern );
97          final boolean isClass = type == Type.CLASS;
98          description = description( isClass ? pattern : null, !isClass ? pattern : null, isRegex );
99          if ( isRegex && pattern != null )
100         {
101             pattern = wrapRegex( pattern );
102         }
103         classPattern = isClass ? reformatClassPattern( pattern, isRegex ) : null;
104         methodPattern = !isClass ? pattern : null;
105         isRegexTestClassPattern = isRegex && isClass;
106         isRegexTestMethodPattern = isRegex && !isClass;
107     }
108 
109     /**
110      * Test class file pattern, e.g. org&#47;**&#47;Cat*.class<br/>
111      * Other examples: org&#47;animals&#47;Cat*, org&#47;animals&#47;Ca?.class, %regex[Cat.class|Dog.*]<br/>
112      * <br/>
113      * '*' means zero or more characters<br>
114      * '?' means one and only one character
115      */
116     public String getTestClassPattern()
117     {
118         return classPattern;
119     }
120 
121     public boolean hasTestClassPattern()
122     {
123         return classPattern != null;
124     }
125 
126     /**
127      * Test method, e.g. "realTestMethod".<br/>
128      * Other examples: test* or testSomethin? or %regex[testOne|testTwo] or %ant[testOne|testTwo]<br/>
129      * <br/>
130      * '*' means zero or more characters<br>
131      * '?' means one and only one character
132      */
133     public String getTestMethodPattern()
134     {
135         return methodPattern;
136     }
137 
138     public boolean hasTestMethodPattern()
139     {
140         return methodPattern != null;
141     }
142 
143     public boolean isRegexTestClassPattern()
144     {
145         return isRegexTestClassPattern;
146     }
147 
148     public boolean isRegexTestMethodPattern()
149     {
150         return isRegexTestMethodPattern;
151     }
152 
153     public boolean isEmpty()
154     {
155         return classPattern == null && methodPattern == null;
156     }
157 
158     public boolean shouldRun( String testClassFile, String methodName )
159     {
160         if ( isEmpty() )
161         {
162             return true;
163         }
164 
165         boolean matchedMethodPattern = false;
166 
167         if ( methodPattern != null && methodName != null )
168         {
169             if ( SelectorUtils.matchPath( methodPattern, methodName ) )
170             {
171                 matchedMethodPattern = true;
172             }
173             else
174             {
175                 return false;
176             }
177         }
178 
179         if ( classPattern != null )
180         {
181             return isRegexTestClassPattern ? matchClassRegexPatter( testClassFile ) : matchClassPatter( testClassFile );
182         }
183         else
184         {
185             return matchedMethodPattern;
186         }
187     }
188 
189     @Override
190     public boolean equals( Object o )
191     {
192         if ( this == o )
193         {
194             return true;
195         }
196         if ( o == null || getClass() != o.getClass() )
197         {
198             return false;
199         }
200 
201         ResolvedTest that = (ResolvedTest) o;
202 
203         return ( classPattern == null ? that.classPattern == null : classPattern.equals( that.classPattern ) )
204             && ( methodPattern == null ? that.methodPattern == null : methodPattern.equals( that.methodPattern ) );
205     }
206 
207     @Override
208     public int hashCode()
209     {
210         int result = classPattern != null ? classPattern.hashCode() : 0;
211         result = 31 * result + ( methodPattern != null ? methodPattern.hashCode() : 0 );
212         return result;
213     }
214 
215     @Override
216     public String toString()
217     {
218         return isEmpty() ? null : description;
219     }
220 
221     private static String description( String clazz, String method, boolean isRegex )
222     {
223         String description;
224         if ( clazz == null && method == null )
225         {
226             description = null;
227         }
228         else if ( clazz == null )
229         {
230             description = "#" + method;
231         }
232         else if ( method == null )
233         {
234             description = clazz;
235         }
236         else
237         {
238             description = clazz + "#" + method;
239         }
240         return isRegex && description != null ? wrapRegex( description ) : description;
241     }
242 
243     private boolean matchClassPatter( String testClassFile )
244     {
245         //@todo We have to use File.separator only because the MatchPatterns is using it internally - cannot override.
246         String classPattern = this.classPattern;
247         if ( File.separatorChar != '/' )
248         {
249             testClassFile = testClassFile.replace( '/', File.separatorChar );
250             classPattern = classPattern.replace( '/', File.separatorChar );
251         }
252 
253         if ( classPattern.endsWith( WILDCARD_FILENAME_POSTFIX ) || classPattern.endsWith( CLASS_FILE_EXTENSION ) )
254         {
255             return MatchPatterns.from( classPattern ).matches( testClassFile, true );
256         }
257         else
258         {
259             String[] classPatterns = { classPattern + CLASS_FILE_EXTENSION, classPattern };
260             return MatchPatterns.from( classPatterns ).matches( testClassFile, true );
261         }
262     }
263 
264     private boolean matchClassRegexPatter( String testClassFile )
265     {
266         if ( File.separatorChar != '/' )
267         {
268             testClassFile = testClassFile.replace( '/', File.separatorChar );
269         }
270         return MatchPatterns.from( classPattern ).matches( testClassFile, true );
271     }
272 
273     private static String tryBlank( String s )
274     {
275         if ( s == null )
276         {
277             return null;
278         }
279         else
280         {
281             s = s.trim();
282             return StringUtils.isEmpty( s ) ? null : s;
283         }
284     }
285 
286     private static String reformatClassPattern( String s, boolean isRegex )
287     {
288         if ( s != null && !isRegex )
289         {
290             s = convertToPath( s );
291             if ( s != null && !s.startsWith( WILDCARD_PATH_PREFIX ) )
292             {
293                 s = WILDCARD_PATH_PREFIX + s;
294             }
295         }
296         return s;
297     }
298 
299     private static String convertToPath( String className )
300     {
301         if ( StringUtils.isBlank( className ) )
302         {
303             return null;
304         }
305         else
306         {
307             if ( className.endsWith( JAVA_FILE_EXTENSION ) )
308             {
309                 className = className.substring( 0, className.length() - JAVA_FILE_EXTENSION.length() );
310                 className += CLASS_FILE_EXTENSION;
311             }
312             return className;
313         }
314     }
315 
316     static String wrapRegex( String unwrapped )
317     {
318         return SelectorUtils.REGEX_HANDLER_PREFIX + unwrapped + SelectorUtils.PATTERN_HANDLER_SUFFIX;
319     }
320 }