@@ -2,150 +2,13 @@ package scala.build.testrunner
2
2
3
3
import sbt .testing .{Logger => _ , _ }
4
4
5
- import java .lang .annotation .Annotation
6
- import java .lang .reflect .Modifier
7
- import java .nio .file .{Files , Path }
8
- import java .util .ServiceLoader
9
5
import java .util .regex .Pattern
10
6
11
7
import scala .annotation .tailrec
12
8
import scala .build .testrunner .FrameworkUtils ._
13
- import scala .jdk .CollectionConverters ._
14
9
15
10
object DynamicTestRunner {
16
11
17
- // adapted from https://github.com/com-lihaoyi/mill/blob/ab4d61a50da24fb7fac97c4453dd8a770d8ac62b/scalalib/src/Lib.scala#L156-L172
18
- private def matchFingerprints (
19
- loader : ClassLoader ,
20
- cls : Class [_],
21
- fingerprints : Array [Fingerprint ]
22
- ): Option [Fingerprint ] = {
23
- val isModule = cls.getName.endsWith(" $" )
24
- val publicConstructorCount = cls.getConstructors.count(c => Modifier .isPublic(c.getModifiers))
25
- val noPublicConstructors = publicConstructorCount == 0
26
- val definitelyNoTests = Modifier .isAbstract(cls.getModifiers) ||
27
- cls.isInterface ||
28
- publicConstructorCount > 1 ||
29
- isModule != noPublicConstructors
30
- if (definitelyNoTests)
31
- None
32
- else
33
- fingerprints.find {
34
- case f : SubclassFingerprint =>
35
- f.isModule == isModule &&
36
- loader.loadClass(f.superclassName())
37
- .isAssignableFrom(cls)
38
-
39
- case f : AnnotatedFingerprint =>
40
- val annotationCls = loader.loadClass(f.annotationName())
41
- .asInstanceOf [Class [Annotation ]]
42
- f.isModule == isModule && (
43
- cls.isAnnotationPresent(annotationCls) ||
44
- cls.getDeclaredMethods.exists(_.isAnnotationPresent(annotationCls)) ||
45
- cls.getMethods.exists { m =>
46
- m.isAnnotationPresent(annotationCls) &&
47
- Modifier .isPublic(m.getModifiers())
48
- }
49
- )
50
- }
51
- }
52
-
53
- def listClasses (classPathEntry : Path , keepJars : Boolean ): Iterator [String ] =
54
- if (Files .isDirectory(classPathEntry)) {
55
- var stream : java.util.stream.Stream [Path ] = null
56
- try {
57
- stream = Files .walk(classPathEntry, Int .MaxValue )
58
- stream
59
- .iterator
60
- .asScala
61
- .filter(_.getFileName.toString.endsWith(" .class" ))
62
- .map(classPathEntry.relativize(_))
63
- .map { p =>
64
- val count = p.getNameCount
65
- (0 until count).map(p.getName).mkString(" ." )
66
- }
67
- .map(_.stripSuffix(" .class" ))
68
- .toVector // fully consume stream before closing it
69
- .iterator
70
- }
71
- finally if (stream != null ) stream.close()
72
- }
73
- else if (keepJars && Files .isRegularFile(classPathEntry)) {
74
- import java .util .zip ._
75
- var zf : ZipFile = null
76
- try {
77
- zf = new ZipFile (classPathEntry.toFile)
78
- zf.entries
79
- .asScala
80
- // FIXME Check if these are files too
81
- .filter(_.getName.endsWith(" .class" ))
82
- .map(ent => ent.getName.stripSuffix(" .class" ).replace(" /" , " ." ))
83
- .toVector // full consume ZipFile before closing it
84
- .iterator
85
- }
86
- finally if (zf != null ) zf.close()
87
- }
88
- else Iterator .empty
89
-
90
- def listClasses (classPath : Seq [Path ], keepJars : Boolean ): Iterator [String ] =
91
- classPath.iterator.flatMap(listClasses(_, keepJars))
92
-
93
- def findFrameworkServices (loader : ClassLoader ): Seq [Framework ] =
94
- ServiceLoader .load(classOf [Framework ], loader)
95
- .iterator()
96
- .asScala
97
- .toSeq
98
-
99
- def loadFramework (
100
- loader : ClassLoader ,
101
- className : String
102
- ): Framework = {
103
- val cls = loader.loadClass(className)
104
- val constructor = cls.getConstructor()
105
- constructor.newInstance().asInstanceOf [Framework ]
106
- }
107
-
108
- def findFrameworks (
109
- classPath : Seq [Path ],
110
- loader : ClassLoader ,
111
- preferredClasses : Seq [String ]
112
- ): Seq [Framework ] = {
113
- val frameworkCls = classOf [Framework ]
114
- (preferredClasses.iterator ++ listClasses(classPath, true ))
115
- .flatMap { name =>
116
- val it : Iterator [Class [_]] =
117
- try Iterator (loader.loadClass(name))
118
- catch {
119
- case _ : ClassNotFoundException | _ : UnsupportedClassVersionError | _ : NoClassDefFoundError | _ : IncompatibleClassChangeError =>
120
- Iterator .empty
121
- }
122
- it
123
- }
124
- .flatMap { cls =>
125
- def isAbstract = Modifier .isAbstract(cls.getModifiers)
126
- def publicConstructorCount =
127
- cls.getConstructors.count { c =>
128
- Modifier .isPublic(c.getModifiers) && c.getParameterCount() == 0
129
- }
130
- val it : Iterator [Class [_]] =
131
- if (frameworkCls.isAssignableFrom(cls) && ! isAbstract && publicConstructorCount == 1 )
132
- Iterator (cls)
133
- else
134
- Iterator .empty
135
- it
136
- }
137
- .flatMap { cls =>
138
- try {
139
- val constructor = cls.getConstructor()
140
- Iterator (constructor.newInstance().asInstanceOf [Framework ])
141
- }
142
- catch {
143
- case _ : NoSuchMethodException => Iterator .empty
144
- }
145
- }
146
- .toSeq
147
- }
148
-
149
12
/** Based on junit-interface [GlobFilter.
150
13
* compileGlobPattern](https://github.com/sbt/junit-interface/blob/f8c6372ed01ce86f15393b890323d96afbe6d594/src/main/java/com/novocode/junit/GlobFilter.java#L37)
151
14
*
@@ -223,71 +86,10 @@ object DynamicTestRunner {
223
86
.map(loadFramework(classLoader, _))
224
87
.map(Seq (_))
225
88
.getOrElse {
226
- // needed for Scala 2.12
227
- def distinctBy [A , B ](seq : Seq [A ])(f : A => B ): Seq [A ] = {
228
- @ annotation.tailrec
229
- def loop (remaining : Seq [A ], seen : Set [B ], acc : Vector [A ]): Vector [A ] =
230
- if (remaining.isEmpty) acc
231
- else {
232
- val head = remaining.head
233
- val tail = remaining.tail
234
- val key = f(head)
235
- if (seen(key)) loop(tail, seen, acc)
236
- else loop(tail, seen + key, acc :+ head)
237
- }
238
- loop(seq, Set .empty, Vector .empty)
239
- }
240
-
241
- val foundFrameworkServices = findFrameworkServices(classLoader)
242
- if (foundFrameworkServices.nonEmpty)
243
- logger.debug(
244
- s """ Found test framework services:
245
- | - ${foundFrameworkServices.map(_.description).mkString(" \n - " )}
246
- | """ .stripMargin
247
- )
248
-
249
- val foundFrameworks =
250
- findFrameworks(classPath0, classLoader, TestRunner .commonTestFrameworks)
251
- if (foundFrameworks.nonEmpty)
252
- logger.debug(
253
- s """ Found test frameworks:
254
- | - ${foundFrameworks.map(_.description).mkString(" \n - " )}
255
- | """ .stripMargin
256
- )
257
-
258
- val distinctFrameworks = distinctBy(foundFrameworkServices ++ foundFrameworks)(_.name())
259
- if (distinctFrameworks.nonEmpty)
260
- logger.debug(
261
- s """ Distinct test frameworks found (by framework name):
262
- | - ${distinctFrameworks.map(_.description).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 (finalFrameworks.nonEmpty)
276
- logger.log(
277
- s """ Final list of test frameworks found:
278
- | - ${finalFrameworks.map(_.description).mkString(" \n - " )}
279
- | """ .stripMargin
280
- )
281
-
282
- val skippedInheritedFrameworks = distinctFrameworks.diff(finalFrameworks)
283
- if (skippedInheritedFrameworks.nonEmpty)
284
- logger.log(
285
- s """ The following test frameworks have been filtered out, as they're being inherited from by others:
286
- | - ${skippedInheritedFrameworks.map(_.description).mkString(" \n - " )}
287
- | """ .stripMargin
288
- )
289
-
290
- finalFrameworks match {
89
+ getFrameworksToRun(
90
+ frameworkServices = findFrameworkServices(classLoader),
91
+ frameworks = findFrameworks(classPath0, classLoader, TestRunner .commonTestFrameworks)
92
+ )(logger) match {
291
93
case f if f.nonEmpty => f
292
94
case _ if verbosity >= 2 => sys.error(" No test framework found" )
293
95
case _ =>
0 commit comments