@@ -89,13 +89,11 @@ object DynamicTestRunner {
89
89
def listClasses (classPath : Seq [Path ], keepJars : Boolean ): Iterator [String ] =
90
90
classPath.iterator.flatMap(listClasses(_, keepJars))
91
91
92
- def findFrameworkService (loader : ClassLoader ): Option [Framework ] =
92
+ def findFrameworkServices (loader : ClassLoader ): Seq [Framework ] =
93
93
ServiceLoader .load(classOf [Framework ], loader)
94
94
.iterator()
95
95
.asScala
96
- .take(1 )
97
- .toList
98
- .headOption
96
+ .toSeq
99
97
100
98
def loadFramework (
101
99
loader : ClassLoader ,
@@ -106,11 +104,11 @@ object DynamicTestRunner {
106
104
constructor.newInstance().asInstanceOf [Framework ]
107
105
}
108
106
109
- def findFramework (
107
+ def findFrameworks (
110
108
classPath : Seq [Path ],
111
109
loader : ClassLoader ,
112
110
preferredClasses : Seq [String ]
113
- ): Option [Framework ] = {
111
+ ): Seq [Framework ] = {
114
112
val frameworkCls = classOf [Framework ]
115
113
(preferredClasses.iterator ++ listClasses(classPath, true ))
116
114
.flatMap { name =>
@@ -144,9 +142,7 @@ object DynamicTestRunner {
144
142
case _ : NoSuchMethodException => Iterator .empty
145
143
}
146
144
}
147
- .take(1 )
148
- .toList
149
- .headOption
145
+ .toSeq
150
146
}
151
147
152
148
/** Based on junit-interface [GlobFilter.
@@ -220,15 +216,83 @@ object DynamicTestRunner {
220
216
221
217
val classLoader = Thread .currentThread().getContextClassLoader
222
218
val classPath0 = TestRunner .classPath(classLoader)
223
- val framework = testFrameworkOpt.map(loadFramework(classLoader, _))
224
- .orElse(findFrameworkService (classLoader))
225
- .orElse(findFramework(classPath0, classLoader, TestRunner .commonTestFrameworks ))
219
+ val frameworks = testFrameworkOpt
220
+ .map(loadFramework (classLoader, _ ))
221
+ .map( Seq (_ ))
226
222
.getOrElse {
227
- if (verbosity >= 2 )
228
- sys.error(" No test framework found" )
229
- else {
230
- System .err.println(" No test framework found" )
231
- sys.exit(1 )
223
+ // needed for Scala 2.12
224
+ def distinctBy [A , B ](seq : Seq [A ])(f : A => B ): Seq [A ] = {
225
+ @ annotation.tailrec
226
+ def loop (remaining : Seq [A ], seen : Set [B ], acc : Vector [A ]): Vector [A ] =
227
+ if (remaining.isEmpty) acc
228
+ else {
229
+ val head = remaining.head
230
+ val tail = remaining.tail
231
+ val key = f(head)
232
+ if (seen(key)) loop(tail, seen, acc)
233
+ else loop(tail, seen + key, acc :+ head)
234
+ }
235
+ loop(seq, Set .empty, Vector .empty)
236
+ }
237
+
238
+ def getFrameworkDescription (f : Framework ): String =
239
+ s " ${f.name()} ( ${Option (f.getClass.getCanonicalName).getOrElse(f.toString)}) "
240
+
241
+ val foundFrameworkServices = findFrameworkServices(classLoader)
242
+ if (verbosity >= 2 && foundFrameworkServices.nonEmpty)
243
+ System .err.println(
244
+ s """ Found test framework services:
245
+ | - ${foundFrameworkServices.map(getFrameworkDescription).mkString(" \n - " )}
246
+ | """ .stripMargin
247
+ )
248
+
249
+ val foundFrameworks =
250
+ findFrameworks(classPath0, classLoader, TestRunner .commonTestFrameworks)
251
+ if (verbosity >= 2 && foundFrameworks.nonEmpty)
252
+ System .err.println(
253
+ s """ Found test frameworks:
254
+ | - ${foundFrameworks.map(getFrameworkDescription).mkString(" \n - " )}
255
+ | """ .stripMargin
256
+ )
257
+
258
+ val distinctFrameworks = distinctBy(foundFrameworkServices ++ foundFrameworks)(_.name())
259
+ if (verbosity >= 2 && distinctFrameworks.nonEmpty)
260
+ System .err.println(
261
+ s """ Distinct test frameworks found (by framework name):
262
+ | - ${distinctFrameworks.map(getFrameworkDescription).mkString(" \n - " )}
263
+ | """ .stripMargin
264
+ )
265
+
266
+ val finalFrameworks =
267
+ distinctFrameworks
268
+ .filter(f1 =>
269
+ ! distinctFrameworks
270
+ .filter(_ != f1)
271
+ .exists(f2 =>
272
+ f1.getClass.isAssignableFrom(f2.getClass)
273
+ )
274
+ )
275
+ if (verbosity >= 1 && finalFrameworks.nonEmpty)
276
+ System .err.println(
277
+ s """ Final list of test frameworks found:
278
+ | - ${finalFrameworks.map(getFrameworkDescription).mkString(" \n - " )}
279
+ | """ .stripMargin
280
+ )
281
+
282
+ val skippedInheritedFrameworks = distinctFrameworks.diff(finalFrameworks)
283
+ if (verbosity >= 1 && skippedInheritedFrameworks.nonEmpty)
284
+ System .err.println(
285
+ s """ The following test frameworks have been filtered out, as they're being inherited from by others:
286
+ | - ${skippedInheritedFrameworks.map(getFrameworkDescription).mkString(" \n - " )}
287
+ | """ .stripMargin
288
+ )
289
+
290
+ finalFrameworks match {
291
+ case f if f.nonEmpty => f
292
+ case _ if verbosity >= 2 => sys.error(" No test framework found" )
293
+ case _ =>
294
+ System .err.println(" No test framework found" )
295
+ sys.exit(1 )
232
296
}
233
297
}
234
298
def classes = {
@@ -237,41 +301,55 @@ object DynamicTestRunner {
237
301
}
238
302
val out = System .out
239
303
240
- val fingerprints = framework.fingerprints()
241
- val runner = framework.runner(args0.toArray, Array (), classLoader)
242
- def clsFingerprints = classes.flatMap { cls =>
243
- matchFingerprints(classLoader, cls, fingerprints)
244
- .map((cls, _))
245
- .iterator
246
- }
247
- val taskDefs = clsFingerprints
248
- .filter {
249
- case (cls, _) =>
250
- testOnly.forall(pattern =>
251
- globPattern(pattern).matcher(cls.getName.stripSuffix(" $" )).matches()
252
- )
253
- }
254
- .map {
255
- case (cls, fp) =>
256
- new TaskDef (cls.getName.stripSuffix(" $" ), fp, false , Array (new SuiteSelector ))
257
- }
258
- .toVector
259
- val initialTasks = runner.tasks(taskDefs.toArray)
260
- val events = TestRunner .runTasks(initialTasks, out)
261
- val failed = events.exists { ev =>
262
- ev.status == Status .Error ||
263
- ev.status == Status .Failure ||
264
- ev.status == Status .Canceled
265
- }
266
- val doneMsg = runner.done()
267
- if (doneMsg.nonEmpty)
268
- out.println(doneMsg)
269
- if (requireTests && events.isEmpty) {
270
- System .err.println(" Error: no tests were run." )
271
- sys.exit(1 )
272
- }
273
- if (failed)
274
- sys.exit(1 )
304
+ val exitCodes =
305
+ frameworks
306
+ .map { framework =>
307
+ if (verbosity >= 1 ) System .err.println(s " Running test framework: ${framework.name}" )
308
+ val fingerprints = framework.fingerprints()
309
+ val runner = framework.runner(args0.toArray, Array (), classLoader)
310
+
311
+ def clsFingerprints = classes.flatMap { cls =>
312
+ matchFingerprints(classLoader, cls, fingerprints)
313
+ .map((cls, _))
314
+ .iterator
315
+ }
316
+
317
+ val taskDefs = clsFingerprints
318
+ .filter {
319
+ case (cls, _) =>
320
+ testOnly.forall(pattern =>
321
+ globPattern(pattern).matcher(cls.getName.stripSuffix(" $" )).matches()
322
+ )
323
+ }
324
+ .map {
325
+ case (cls, fp) =>
326
+ new TaskDef (cls.getName.stripSuffix(" $" ), fp, false , Array (new SuiteSelector ))
327
+ }
328
+ .toVector
329
+ val initialTasks = runner.tasks(taskDefs.toArray)
330
+ val events = TestRunner .runTasks(initialTasks, out)
331
+ val failed = events.exists { ev =>
332
+ ev.status == Status .Error ||
333
+ ev.status == Status .Failure ||
334
+ ev.status == Status .Canceled
335
+ }
336
+ val doneMsg = runner.done()
337
+ if (doneMsg.nonEmpty) out.println(doneMsg)
338
+ if (requireTests && events.isEmpty) {
339
+ System .err.println(s " Error: no tests were run for ${framework.name()}. " )
340
+ 1
341
+ }
342
+ else if (failed) {
343
+ System .err.println(s " Error: ${framework.name()} tests failed. " )
344
+ 1
345
+ }
346
+ else {
347
+ if (verbosity >= 1 ) System .err.println(s " ${framework.name()} tests ran successfully. " )
348
+ 0
349
+ }
350
+ }
351
+ if (exitCodes.contains(1 )) sys.exit(1 )
352
+ else sys.exit(0 )
275
353
}
276
354
}
277
355
0 commit comments