@@ -21,6 +21,7 @@ import mill.util.Jvm
21
21
import os .Path
22
22
import scala .util .Try
23
23
import scala .collection .mutable
24
+
24
25
/**
25
26
* Core configuration required to compile a single Java compilation target
26
27
*/
@@ -95,116 +96,122 @@ trait JavaModule
95
96
}
96
97
}
97
98
98
- def testQuick (args : String * ): Command [(String , Seq [TestResult ])] = Task .Command (persistent = true ) {
99
- val quicktestFailedClassesLog = Task .dest / " quickTestFailedClasses.log"
100
- val (analysisFolder, previousAnalysisFolderOpt) = callGraphAnalysis()
101
- val testClasses = testForkGrouping()
102
- val quickTestClassLists = previousAnalysisFolderOpt.fold {
103
- // no previous analysis folder, all classes need to be tested
104
- testClasses
105
- } { _ =>
106
- // get spanning invalidation tree content
107
- val spanningInvalidationTreeObj = Try {
108
- upickle.default.read[ujson.Obj ](os.read.stream(analysisFolder / " spanningInvalidationTree.json" ))
109
- }.getOrElse(ujson.Obj ())
110
-
111
- // we cannot parse the json back to its concrete type, we only know that all of them are
112
- // encoded with class name as prefix.
113
- // so we can do a prefix checking to get the affected class.
114
- // naive implementation won't work well as it has loop through all `testClasses` and check prefix
115
- // for all of them, so we employ a prefix trie to speed up the prefix checking.
116
-
117
- val jsonValueQueue = mutable.ArrayDeque [(String , ujson.Value )]()
118
- val prefixTrie = new PrefixTrie [String ]()
119
- jsonValueQueue.appendAll(spanningInvalidationTreeObj.value)
120
- while (jsonValueQueue.nonEmpty) {
121
- val (nodeStr, value) = jsonValueQueue.removeHead()
122
- val parts = nodeStr
123
- .stripPrefix(" def " )
124
- .stripPrefix(" call " )
125
- .stripPrefix(" external " )
126
- .split(" [\\ .\\ $\\ !]" )
127
- .map(_.trim())
128
- .toSeq
129
- prefixTrie.insert(parts)
130
- value match {
131
- case ujson.Obj (fieldMap) => jsonValueQueue.appendAll(fieldMap)
132
- case _ => ()
99
+ def testQuick (args : String * ): Command [(String , Seq [TestResult ])] =
100
+ Task .Command (persistent = true ) {
101
+ val quicktestFailedClassesLog = Task .dest / " quickTestFailedClasses.log"
102
+ val (analysisFolder, previousAnalysisFolderOpt) = callGraphAnalysis()
103
+ val testClasses = testForkGrouping()
104
+ val quickTestClassLists = previousAnalysisFolderOpt.fold {
105
+ // no previous analysis folder, all classes need to be tested
106
+ testClasses
107
+ } { _ =>
108
+ // get spanning invalidation tree content
109
+ val spanningInvalidationTreeObj = Try {
110
+ upickle.default.read[ujson.Obj ](
111
+ os.read.stream(analysisFolder / " spanningInvalidationTree.json" )
112
+ )
113
+ }.getOrElse(ujson.Obj ())
114
+
115
+ // we cannot parse the json back to its concrete type, we only know that all of them are
116
+ // encoded with class name as prefix.
117
+ // so we can do a prefix checking to get the affected class.
118
+ // naive implementation won't work well as it has loop through all `testClasses` and check prefix
119
+ // for all of them, so we employ a prefix trie to speed up the prefix checking.
120
+
121
+ val jsonValueQueue = mutable.ArrayDeque [(String , ujson.Value )]()
122
+ val prefixTrie = new PrefixTrie [String ]()
123
+ jsonValueQueue.appendAll(spanningInvalidationTreeObj.value)
124
+ while (jsonValueQueue.nonEmpty) {
125
+ val (nodeStr, value) = jsonValueQueue.removeHead()
126
+ val parts = nodeStr
127
+ .stripPrefix(" def " )
128
+ .stripPrefix(" call " )
129
+ .stripPrefix(" external " )
130
+ .split(" [\\ .\\ $\\ !]" )
131
+ .map(_.trim())
132
+ .toSeq
133
+ prefixTrie.insert(parts)
134
+ value match {
135
+ case ujson.Obj (fieldMap) => jsonValueQueue.appendAll(fieldMap)
136
+ case _ => ()
137
+ }
133
138
}
139
+
140
+ val failedTestClasses =
141
+ if (! os.exists(quicktestFailedClassesLog)) {
142
+ Set .empty[String ]
143
+ } else {
144
+ Try {
145
+ upickle.default.read[Seq [String ]](os.read.stream(quicktestFailedClassesLog))
146
+ }.getOrElse(Seq .empty[String ]).toSet
147
+ }
148
+
149
+ val result = testClasses.map(_.filter { testClassName =>
150
+ failedTestClasses.contains(testClassName) || {
151
+ val parts = testClassName
152
+ .split(" \\ ." )
153
+ .map(_.trim())
154
+ .toSeq
155
+ prefixTrie.contains(parts)
156
+ }
157
+ }).filter(_.nonEmpty)
158
+ // help gc
159
+ prefixTrie.clear()
160
+ result
134
161
}
135
162
136
- val failedTestClasses =
137
- if (! os.exists(quicktestFailedClassesLog)) {
138
- Set .empty[String ]
139
- } else {
140
- Try {
141
- upickle.default.read[Seq [String ]](os.read.stream(quicktestFailedClassesLog))
142
- }.getOrElse(Seq .empty[String ]).toSet
143
- }
163
+ // Clean up the directory for test runners
164
+ os.walk(Task .dest).foreach { subPath => os.remove.all(subPath) }
165
+
166
+ val quickTestReportXml = testReportXml()
167
+
168
+ val testModuleUtil = new TestModuleUtil (
169
+ testUseArgsFile(),
170
+ forkArgs(),
171
+ Seq .empty,
172
+ zincWorker().scalalibClasspath(),
173
+ resources(),
174
+ testFramework(),
175
+ runClasspath(),
176
+ testClasspath(),
177
+ args.toSeq,
178
+ quickTestClassLists,
179
+ zincWorker().testrunnerEntrypointClasspath(),
180
+ forkEnv(),
181
+ testSandboxWorkingDir(),
182
+ forkWorkingDir(),
183
+ quickTestReportXml,
184
+ zincWorker().javaHome().map(_.path),
185
+ testParallelism()
186
+ )
144
187
145
- val result = testClasses.map(_.filter { testClassName =>
146
- failedTestClasses.contains(testClassName) || {
147
- val parts = testClassName
148
- .split(" \\ ." )
149
- .map(_.trim())
150
- .toSeq
151
- prefixTrie.contains(parts)
152
- }
153
- }).filter(_.nonEmpty)
154
- // help gc
155
- prefixTrie.clear()
156
- result
157
- }
188
+ val results = testModuleUtil.runTests()
189
+
190
+ val badTestClasses = results match {
191
+ case Result .Failure (_) =>
192
+ // Consider all quick testing classes as failed
193
+ quickTestClassLists.flatten
194
+ case Result .Success ((_, results)) =>
195
+ // Get all test classes that failed
196
+ results
197
+ .filter(testResult => Set (" Error" , " Failure" ).contains(testResult.status))
198
+ .map(_.fullyQualifiedName)
199
+ }
158
200
159
- // Clean up the directory for test runners
160
- os.walk(Task .dest).foreach { subPath => os.remove.all(subPath) }
161
-
162
- val quickTestReportXml = testReportXml()
163
-
164
- val testModuleUtil = new TestModuleUtil (
165
- testUseArgsFile(),
166
- forkArgs(),
167
- Seq .empty,
168
- zincWorker().scalalibClasspath(),
169
- resources(),
170
- testFramework(),
171
- runClasspath(),
172
- testClasspath(),
173
- args.toSeq,
174
- quickTestClassLists,
175
- zincWorker().testrunnerEntrypointClasspath(),
176
- forkEnv(),
177
- testSandboxWorkingDir(),
178
- forkWorkingDir(),
179
- quickTestReportXml,
180
- zincWorker().javaHome().map(_.path),
181
- testParallelism()
182
- )
201
+ os.write.over(
202
+ quicktestFailedClassesLog,
203
+ upickle.default.write[Seq [String ]](badTestClasses.distinct)
204
+ )
183
205
184
- val results = testModuleUtil.runTests()
185
-
186
- val badTestClasses = results match {
187
- case Result .Failure (_) =>
188
- // Consider all quick testing classes as failed
189
- quickTestClassLists.flatten
190
- case Result .Success ((_, results)) =>
191
- // Get all test classes that failed
192
- results
193
- .filter(testResult => Set (" Error" , " Failure" ).contains(testResult.status))
194
- .map(_.fullyQualifiedName)
195
- }
196
-
197
- os.write.over(quicktestFailedClassesLog, upickle.default.write[Seq [String ]](badTestClasses.distinct))
198
-
199
- results match {
200
- case Result .Failure (errMsg) => Result .Failure (errMsg)
201
- case Result .Success ((doneMsg, results)) =>
202
- try TestModule .handleResults(doneMsg, results, Task .ctx(), quickTestReportXml)
203
- catch {
204
- case e : Throwable => Result .Failure (" Test reporting failed: " + e)
205
- }
206
+ results match {
207
+ case Result .Failure (errMsg) => Result .Failure (errMsg)
208
+ case Result .Success ((doneMsg, results)) =>
209
+ try TestModule .handleResults(doneMsg, results, Task .ctx(), quickTestReportXml)
210
+ catch {
211
+ case e : Throwable => Result .Failure (" Test reporting failed: " + e)
212
+ }
213
+ }
206
214
}
207
- }
208
215
}
209
216
210
217
def defaultCommandName (): String = " run"
@@ -1496,8 +1503,8 @@ trait JavaModule
1496
1503
1497
1504
@ internal
1498
1505
protected def callGraphAnalysisIgnoreCalls (
1499
- callSiteOpt : Option [mill.codesig.JvmModel .MethodDef ],
1500
- calledSig : mill.codesig.JvmModel .MethodSig
1506
+ callSiteOpt : Option [mill.codesig.JvmModel .MethodDef ],
1507
+ calledSig : mill.codesig.JvmModel .MethodSig
1501
1508
): Boolean = {
1502
1509
// We can ignore all calls to methods that look like Targets when traversing
1503
1510
// the call graph. We can do this because we assume `def` Targets are pure,
@@ -1534,10 +1541,10 @@ trait JavaModule
1534
1541
isSimpleTarget(callSiteSig.desc) && calledSig.name.contains(" $anonfun" )
1535
1542
)
1536
1543
}
1537
-
1544
+
1538
1545
isSimpleTarget(calledSig.desc) && ! isForwarderCallsiteOrLambda
1539
1546
}
1540
-
1547
+
1541
1548
// Return the directory containing the current call graph analysis results, and previous one too if it exists
1542
1549
def callGraphAnalysis : T [(Path , Option [Path ])] = Task (persistent = true ) {
1543
1550
os.remove.all(Task .dest / " previous" )
@@ -1548,7 +1555,8 @@ trait JavaModule
1548
1555
.compute(
1549
1556
classFiles = os.walk(compile().classes.path).filter(_.ext == " class" ),
1550
1557
upstreamClasspath = compileClasspath().toSeq.map(_.path),
1551
- ignoreCall = (callSiteOpt, calledSig) => callGraphAnalysisIgnoreCalls(callSiteOpt, calledSig),
1558
+ ignoreCall =
1559
+ (callSiteOpt, calledSig) => callGraphAnalysisIgnoreCalls(callSiteOpt, calledSig),
1552
1560
logger = new mill.codesig.Logger (
1553
1561
Task .dest / " current" ,
1554
1562
Option .when(debugEnabled)(Task .dest / " current" )
@@ -1560,7 +1568,7 @@ trait JavaModule
1560
1568
)
1561
1569
)
1562
1570
)
1563
-
1571
+
1564
1572
(Task .dest / " current" , Option .when(os.exists(Task .dest / " previous" ))(Task .dest / " previous" ))
1565
1573
}
1566
1574
}
0 commit comments