Skip to content

Commit 0972a52

Browse files
committed
Enabling Surefire to run test classes and test methods in any order specified by a runOrder
1 parent d5beee5 commit 0972a52

File tree

10 files changed

+323
-9
lines changed

10 files changed

+323
-9
lines changed

surefire-api/src/main/java/org/apache/maven/surefire/api/testset/TestListResolver.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
import java.util.ArrayList;
2323
import java.util.Collection;
2424
import java.util.Collections;
25+
import java.util.HashMap;
2526
import java.util.LinkedHashSet;
27+
import java.util.Map;
2628
import java.util.Set;
2729

2830
import static java.util.Collections.unmodifiableSet;
@@ -61,6 +63,8 @@ public class TestListResolver
6163

6264
private final boolean hasExcludedMethodPatterns;
6365

66+
private final Map<String, Integer> patternMapper = new HashMap<String, Integer>();
67+
6468
public TestListResolver( Collection<String> tests )
6569
{
6670
final IncludedExcludedPatterns patterns = new IncludedExcludedPatterns();
@@ -208,6 +212,7 @@ public boolean shouldRun( String testClassFile, String methodName )
208212
else
209213
{
210214
boolean shouldRun = false;
215+
ResolvedTest matchedFilter = null;
211216

212217
if ( getIncludedPatterns().isEmpty() )
213218
{
@@ -220,6 +225,7 @@ public boolean shouldRun( String testClassFile, String methodName )
220225
if ( filter.matchAsInclusive( testClassFile, methodName ) )
221226
{
222227
shouldRun = true;
228+
matchedFilter = filter;
223229
break;
224230
}
225231
}
@@ -236,6 +242,15 @@ public boolean shouldRun( String testClassFile, String methodName )
236242
}
237243
}
238244
}
245+
246+
if ( shouldRun )
247+
{
248+
String test = testClassFile + "#" + methodName;
249+
if ( ! this.patternMapper.containsKey( test ) )
250+
{
251+
this.patternMapper.put( test, new ArrayList<>( this.includedPatterns ).indexOf( matchedFilter ) );
252+
}
253+
}
239254
return shouldRun;
240255
}
241256
}
@@ -514,4 +529,31 @@ private static boolean haveMethodPatterns( Set<ResolvedTest> patterns )
514529
}
515530
return false;
516531
}
532+
533+
public Integer testOrderComparator( String className1, String className2, String methodName1, String methodName2 )
534+
{
535+
String classFileName1 = toClassFileName( className1 );
536+
String classFileName2 = toClassFileName( className2 );
537+
boolean shouldRunMethodName1 = shouldRun( classFileName1 , methodName1 );
538+
boolean shouldRunMethodName2 = shouldRun( classFileName2 , methodName2 );
539+
if ( ! shouldRunMethodName1 )
540+
{
541+
return -1;
542+
}
543+
if ( ! shouldRunMethodName2 )
544+
{
545+
return 1;
546+
}
547+
548+
String test1 = classFileName1 + "#" + methodName1;
549+
String test2 = classFileName2 + "#" + methodName2;
550+
if ( patternMapper.get( test1 ) < patternMapper.get( test2 ) )
551+
{
552+
return -1;
553+
}
554+
else
555+
{
556+
return 1;
557+
}
558+
}
517559
}

surefire-api/src/main/java/org/apache/maven/surefire/api/util/DefaultRunOrderCalculator.java

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,14 @@
2121

2222
import org.apache.maven.surefire.api.runorder.RunEntryStatisticsMap;
2323
import org.apache.maven.surefire.api.testset.RunOrderParameters;
24+
import org.apache.maven.surefire.api.testset.TestListResolver;
2425

26+
import java.io.File;
27+
import java.io.IOException;
28+
import java.nio.charset.Charset;
29+
import java.nio.file.Files;
2530
import java.util.ArrayList;
31+
import java.util.Arrays;
2632
import java.util.Calendar;
2733
import java.util.Collections;
2834
import java.util.Comparator;
@@ -48,6 +54,8 @@ public class DefaultRunOrderCalculator
4854

4955
private final Random random;
5056

57+
private final TestListResolver testListResolver;
58+
5159
public DefaultRunOrderCalculator( RunOrderParameters runOrderParameters, int threadCount )
5260
{
5361
this.runOrderParameters = runOrderParameters;
@@ -61,6 +69,14 @@ public DefaultRunOrderCalculator( RunOrderParameters runOrderParameters, int thr
6169
runOrderParameters.setRunOrderRandomSeed( runOrderRandomSeed );
6270
}
6371
this.random = new Random( runOrderRandomSeed );
72+
if ( RunOrder.TESTORDER.equals( getRunOrderMethod() ) )
73+
{
74+
this.testListResolver = getTestListResolver();
75+
}
76+
else
77+
{
78+
this.testListResolver = null;
79+
}
6480
}
6581

6682
@Override
@@ -78,9 +94,80 @@ public TestsToRun orderTestClasses( TestsToRun scannedClasses )
7894
return new TestsToRun( new LinkedHashSet<>( result ) );
7995
}
8096

97+
@Override
98+
public Comparator<String> comparatorForTestMethods()
99+
{
100+
RunOrder methodRunOrder = getRunOrderMethod();
101+
if ( RunOrder.TESTORDER.equals( methodRunOrder ) )
102+
{
103+
return new Comparator<String>()
104+
{
105+
@Override
106+
public int compare( String o1, String o2 )
107+
{
108+
String[] classAndMethod1 = getClassAndMethod( o1 );
109+
String className1 = classAndMethod1[0];
110+
String methodName1 = classAndMethod1[1];
111+
String[] classAndMethod2 = getClassAndMethod( o2 );
112+
String className2 = classAndMethod2[0];
113+
String methodName2 = classAndMethod2[1];
114+
return testListResolver.testOrderComparator( className1, className2, methodName1, methodName2 );
115+
}
116+
};
117+
}
118+
else
119+
{
120+
return null;
121+
}
122+
}
123+
124+
public TestListResolver getTestListResolver()
125+
{
126+
String orderParam = parseTestOrder( System.getProperty( "test" ) );
127+
if ( orderParam == null )
128+
{
129+
throw new IllegalStateException( "TestListResolver in RunOrderCalculator should be used only when "
130+
+ "system property -Dtest is set and runOrder is testorder" );
131+
}
132+
return new TestListResolver( Arrays.asList( orderParam.split( "," ) ) );
133+
}
134+
135+
public String[] getClassAndMethod( String request )
136+
{
137+
String[] classAndMethod = { request, request };
138+
if ( request.contains( "(" ) )
139+
{
140+
String[] nameSplit1 = request.split( "\\(" );
141+
classAndMethod[0] = nameSplit1[1].substring( 0, nameSplit1[1].length() - 1 );
142+
classAndMethod[1] = nameSplit1[0];
143+
}
144+
return classAndMethod;
145+
}
146+
147+
private RunOrder getRunOrderMethod()
148+
{
149+
if ( runOrder.length > 1 && Arrays.asList( runOrder ).contains( RunOrder.TESTORDER ) )
150+
{
151+
// Use of testorder and other runOrders are currently not supported
152+
throw new IllegalStateException( "Expected only testorder. Got: " + runOrder.length );
153+
}
154+
return runOrder[0];
155+
}
156+
81157
private void orderTestClasses( List<Class<?>> testClasses, RunOrder runOrder )
82158
{
83-
if ( RunOrder.RANDOM.equals( runOrder ) )
159+
if ( RunOrder.TESTORDER.equals( runOrder ) )
160+
{
161+
Collections.sort( testClasses, new Comparator<Class<?>>()
162+
{
163+
@Override
164+
public int compare( Class<?> o1, Class<?> o2 )
165+
{
166+
return testListResolver.testOrderComparator( o1.getName(), o2.getName(), null, null );
167+
}
168+
} );
169+
}
170+
else if ( RunOrder.RANDOM.equals( runOrder ) )
84171
{
85172
Collections.shuffle( testClasses, random );
86173
}
@@ -106,6 +193,33 @@ else if ( sortOrder != null )
106193
}
107194
}
108195

196+
private String parseTestOrder( String s )
197+
{
198+
// if s is a file, then parse each line of the file as a test
199+
if ( s != null && s != "" )
200+
{
201+
File f = new File( s );
202+
if ( f.exists() && !f.isDirectory ( ) )
203+
{
204+
try
205+
{
206+
List<String> l = Files.readAllLines( f.toPath(), Charset.defaultCharset( ) );
207+
StringBuilder sb = new StringBuilder( );
208+
for ( String sd : l )
209+
{
210+
sb.append( sd + "," );
211+
}
212+
String sd = sb.toString( );
213+
return sd.substring( 0 , sd.length( ) - 1 );
214+
}
215+
catch ( IOException e )
216+
{
217+
}
218+
}
219+
}
220+
return s;
221+
}
222+
109223
private Comparator<Class> getSortOrderComparator( RunOrder runOrder )
110224
{
111225
if ( RunOrder.ALPHABETICAL.equals( runOrder ) )

surefire-api/src/main/java/org/apache/maven/surefire/api/util/RunOrder.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public class RunOrder
4444

4545
public static final RunOrder FAILEDFIRST = new RunOrder( "failedfirst" );
4646

47+
public static final RunOrder TESTORDER = new RunOrder( "testorder" );
48+
4749
public static final RunOrder[] DEFAULT = new RunOrder[]{ FILESYSTEM };
4850

4951
/**
@@ -108,7 +110,8 @@ private static String createMessageForMissingRunOrder( String name )
108110

109111
private static RunOrder[] values()
110112
{
111-
return new RunOrder[]{ ALPHABETICAL, FILESYSTEM, HOURLY, RANDOM, REVERSE_ALPHABETICAL, BALANCED, FAILEDFIRST };
113+
return new RunOrder[]{ ALPHABETICAL, FILESYSTEM, HOURLY, RANDOM, REVERSE_ALPHABETICAL, BALANCED, FAILEDFIRST,
114+
TESTORDER };
112115
}
113116

114117
public static String asString( RunOrder[] runOrder )

surefire-api/src/main/java/org/apache/maven/surefire/api/util/RunOrderCalculator.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,14 @@
1919
* under the License.
2020
*/
2121

22+
import java.util.Comparator;
23+
2224
/**
2325
* @author Kristian Rosenvold
2426
*/
2527
public interface RunOrderCalculator
2628
{
2729
TestsToRun orderTestClasses( TestsToRun scannedClasses );
30+
31+
Comparator<String> comparatorForTestMethods();
2832
}

surefire-api/src/test/java/org/apache/maven/surefire/api/testset/TestListResolverTest.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.HashSet;
2828
import java.util.Iterator;
2929
import java.util.LinkedHashSet;
30+
import java.util.List;
3031
import java.util.Set;
3132

3233
import static java.util.Collections.addAll;
@@ -502,4 +503,65 @@ private static Set<ResolvedTest> resolveClass( String patterns )
502503
}
503504
return resolved;
504505
}
506+
507+
public void testOrderComparatorTest()
508+
{
509+
List<String> orderParamList = new ArrayList<String>();
510+
orderParamList.add( "TestClass1#testa2d" );
511+
orderParamList.add( "TestClass1#testabc" );
512+
orderParamList.add( "TestClass1#testa1b" );
513+
orderParamList.add( "TestClass2#testa1b" );
514+
orderParamList.add( "TestClass2#testaBc" );
515+
TestListResolver tlr = new TestListResolver( orderParamList );
516+
String className = "TestClass1";
517+
String className2 = "TestClass2";
518+
assertEquals( ( int ) tlr.testOrderComparator( className, className, "testa2d", "testa1b" ), -1 );
519+
assertEquals( ( int ) tlr.testOrderComparator( className, className, "testa2d", "testabc" ), -1 );
520+
assertEquals( ( int ) tlr.testOrderComparator( className, className, "testa1b", "testabc" ), 1 );
521+
assertEquals( ( int ) tlr.testOrderComparator( className, className, "testa2d", "testaBc" ), 1 );
522+
assertEquals( ( int ) tlr.testOrderComparator( className, className, "testa3d", "testa1b" ), -1 );
523+
assertEquals( ( int ) tlr.testOrderComparator( className, className2, "testa2d", "testa1b" ), -1 );
524+
assertEquals( ( int ) tlr.testOrderComparator( className2, className, "testaBc", "testa1b" ), 1 );
525+
assertEquals( ( int ) tlr.testOrderComparator( className, className2, "testa3d", "testa1b" ), -1 );
526+
assertEquals( ( int ) tlr.testOrderComparator( className, className2, "testa2d", "testabc" ), 1 );
527+
}
528+
529+
public void testRegexMethodOrderComparator()
530+
{
531+
List<String> orderParamList = new ArrayList<String>();
532+
orderParamList.add( "TestClass1#testa?c" );
533+
orderParamList.add( "TestClass1#testa?b" );
534+
orderParamList.add( "TestClass2#test?1*" );
535+
orderParamList.add( "!TestClass1#testa4b" );
536+
orderParamList.add( "!TestClass2#test11MyTest" );
537+
TestListResolver tlr = new TestListResolver( orderParamList );
538+
String className = "TestClass1";
539+
String className2 = "TestClass2";
540+
assertEquals( ( int ) tlr.testOrderComparator( className, className, "testabc", "testa1b" ), -1 );
541+
assertEquals( ( int ) tlr.testOrderComparator( className, className, "testaBc", "testa2b" ), -1 );
542+
assertEquals( ( int ) tlr.testOrderComparator( className, className, "testa1b", "testa3c" ), 1 );
543+
assertEquals( ( int ) tlr.testOrderComparator( className, className, "testa1b", "testa4b" ), 1 );
544+
assertEquals( ( int ) tlr.testOrderComparator( className, className, "testa4b", "testabc" ), -1 );
545+
assertEquals( ( int ) tlr.testOrderComparator( className, className2, "testa1b", "test1123" ), -1 );
546+
assertEquals( ( int ) tlr.testOrderComparator( className2, className, "testa1b", "testa1b" ), 1 );
547+
assertEquals( ( int ) tlr.testOrderComparator( className2, className2, "testa1b", "test1123" ), 1 );
548+
assertEquals( ( int ) tlr.testOrderComparator( className2, className2, "test1123", "test11MyTest" ), 1 );
549+
assertEquals( ( int ) tlr.testOrderComparator( className2, className2, "test11MyTest", "test456" ), -1 );
550+
}
551+
552+
public void testRegexClassOrderComparator()
553+
{
554+
List<String> orderParamList = new ArrayList<String>();
555+
orderParamList.add( "My2*Test.java" );
556+
orderParamList.add( "???My1*Test" );
557+
orderParamList.add( "!abcMy1PeaceTest" );
558+
TestListResolver tlr = new TestListResolver( orderParamList );
559+
String className = "My2ConnectTest";
560+
String className2 = "456My1ConnectTest";
561+
String className3 = "abcMy1PeaceTest";
562+
assertEquals( ( int ) tlr.testOrderComparator( className, className2, null, null ), -1 );
563+
assertEquals( ( int ) tlr.testOrderComparator( className2, className, null, null ), 1 );
564+
assertEquals( ( int ) tlr.testOrderComparator( className3, className2, null, null ), -1 );
565+
assertEquals( ( int ) tlr.testOrderComparator( className, className3, null, null ), 1 );
566+
}
505567
}

0 commit comments

Comments
 (0)