@@ -35,9 +35,11 @@ class ParallelUnitTestRunnerSuite extends AnyFunSuite {
3535 reportsDir : Path ,
3636 failFixture : Boolean ,
3737 spoofResult : Boolean = false ,
38- sparkConfs : String = " " ): Array [String ] = {
38+ sparkConfs : String = " " ,
39+ wildcardSuites : String = fixtureSuiteName,
40+ extraJvmArgs : Seq [String ] = Seq .empty): Array [String ] = {
3941 val testClasses = Paths .get(getClass.getProtectionDomain.getCodeSource.getLocation.toURI)
40- val fixtureJvmArgs = Seq (
42+ val fixtureJvmArgs = ( Seq (
4143 if (failFixture) {
4244 Some (s " -D ${ParallelUnitTestRunnerFixtureSuite .FAIL_PROPERTY }=true " )
4345 } else {
@@ -48,13 +50,13 @@ class ParallelUnitTestRunnerSuite extends AnyFunSuite {
4850 } else {
4951 None
5052 })
51- .flatten
53+ .flatten ++ extraJvmArgs)
5254 .mkString(" " )
5355 Array (
5456 s " testClasses= $testClasses" ,
5557 s " reportsDir= $reportsDir" ,
5658 " forkCount=2" ,
57- s " wildcardSuites= $fixtureSuiteName " ,
59+ s " wildcardSuites= $wildcardSuites " ,
5860 " tagsToInclude=" ,
5961 " tagsToExclude=" ,
6062 " suffixes=" ,
@@ -196,6 +198,31 @@ class ParallelUnitTestRunnerSuite extends AnyFunSuite {
196198 }
197199 }
198200
201+ test(" main runs two suites concurrently in separate child JVMs" ) {
202+ val reportsDir = Files .createTempDirectory(" parallel-unit-test-concurrent" )
203+ val barrierDir = reportsDir.resolve(" barrier" )
204+ Files .createDirectories(barrierDir)
205+ try {
206+ val fixturePrefix =
207+ classOf [ParallelUnitTestRunnerConcurrentFixtureSuiteOne ].getName.stripSuffix(" One" )
208+ ParallelUnitTestRunner .main(fixtureRunnerArgs(
209+ reportsDir,
210+ failFixture = false ,
211+ wildcardSuites = fixturePrefix,
212+ extraJvmArgs = Seq (
213+ s " -D ${ParallelUnitTestRunnerConcurrentFixture .BARRIER_DIR_PROPERTY }= $barrierDir" )))
214+
215+ Seq (
216+ classOf [ParallelUnitTestRunnerConcurrentFixtureSuiteOne ].getName,
217+ classOf [ParallelUnitTestRunnerConcurrentFixtureSuiteTwo ].getName).foreach { suite =>
218+ assert(Files .isRegularFile(
219+ reportsDir.resolve(" wave-1" ).resolve(s " TEST- $suite.xml " )))
220+ }
221+ } finally {
222+ FileUtil .fullyDelete(reportsDir.toFile)
223+ }
224+ }
225+
199226 test(" main propagates a child JVM suite failure" ) {
200227 val reportsDir = Files .createTempDirectory(" parallel-unit-test-failure" )
201228 try {
@@ -421,3 +448,31 @@ class ParallelUnitTestRunnerFixtureSuite extends AnyFunSuite {
421448 assert(! java.lang.Boolean .getBoolean(ParallelUnitTestRunnerFixtureSuite .FAIL_PROPERTY ))
422449 }
423450}
451+
452+ object ParallelUnitTestRunnerConcurrentFixture {
453+ val BARRIER_DIR_PROPERTY : String = " rapids.parallelUnitTestRunner.fixture.barrierDir"
454+ private val BARRIER_TIMEOUT_SECONDS = 10L
455+
456+ def awaitPeer (markerName : String , peerMarkerName : String ): Unit = {
457+ val barrierDir = Paths .get(System .getProperty(BARRIER_DIR_PROPERTY ))
458+ Files .createFile(barrierDir.resolve(markerName))
459+ val peerMarker = barrierDir.resolve(peerMarkerName)
460+ val deadline = System .nanoTime() + TimeUnit .SECONDS .toNanos(BARRIER_TIMEOUT_SECONDS )
461+ while (! Files .exists(peerMarker) && System .nanoTime() - deadline < 0 ) {
462+ Thread .sleep(10 )
463+ }
464+ assert(Files .exists(peerMarker), s " Timed out waiting for concurrent suite marker $peerMarker" )
465+ }
466+ }
467+
468+ class ParallelUnitTestRunnerConcurrentFixtureSuiteOne extends AnyFunSuite {
469+ test(" overlaps with the second fixture suite" ) {
470+ ParallelUnitTestRunnerConcurrentFixture .awaitPeer(" one" , " two" )
471+ }
472+ }
473+
474+ class ParallelUnitTestRunnerConcurrentFixtureSuiteTwo extends AnyFunSuite {
475+ test(" overlaps with the first fixture suite" ) {
476+ ParallelUnitTestRunnerConcurrentFixture .awaitPeer(" two" , " one" )
477+ }
478+ }
0 commit comments