@@ -13,122 +13,126 @@ import scala.util.matching.Regex
1313// that the proper messages are reported, proper build classloaders are
1414// re-used or invalidated, and the proper files end up getting watched
1515// in all cases.
16- object MultiLevelBuildTests extends UtestIntegrationTestSuite {
17- val tests : Tests = Tests {
16+ trait MultiLevelBuildTests extends UtestIntegrationTestSuite {
17+ var savedClassLoaderIds : Seq [Option [Int ]] = Nil
18+ val retryCount : Int = if (sys.env.contains(" CI" )) 2 else 0
19+ def runAssertSuccess (tester : IntegrationTester , expected : String ): Unit = {
20+ val res = tester.eval(" foo.run" )
21+ assert(res.isSuccess == true )
22+ assert(res.out.contains(expected))
23+ }
24+
25+ def fooPaths (tester : IntegrationTester ): Seq [os.Path ] = Seq (
26+ tester.workspacePath / " foo/compile-resources" ,
27+ tester.workspacePath / " foo/resources" ,
28+ tester.workspacePath / " foo/src"
29+ )
30+ def buildPaths (tester : IntegrationTester ): Seq [os.Path ] = Seq (
31+ tester.workspacePath / " build.mill" ,
32+ tester.workspacePath / " mill-build/compile-resources" ,
33+ tester.workspacePath / " mill-build/resources" ,
34+ tester.workspacePath / " mill-build/src"
35+ )
36+ def buildPaths2 (tester : IntegrationTester ): Seq [os.Path ] = Seq (
37+ tester.workspacePath / " mill-build/build.mill" ,
38+ tester.workspacePath / " mill-build/mill-build/compile-resources" ,
39+ tester.workspacePath / " mill-build/mill-build/resources" ,
40+ tester.workspacePath / " mill-build/mill-build/src"
41+ )
42+ def buildPaths3 (tester : IntegrationTester ): Seq [os.Path ] = Seq (
43+ tester.workspacePath / " mill-build/mill-build/build.mill" ,
44+ tester.workspacePath / " mill-build/mill-build/mill-build/compile-resources" ,
45+ tester.workspacePath / " mill-build/mill-build/mill-build/resources" ,
46+ tester.workspacePath / " mill-build/mill-build/mill-build/src"
47+ )
48+
49+ def loadFrames (
50+ tester : IntegrationTester ,
51+ n : Int
52+ ): IndexedSeq [(RunnerState .Frame .Logged , os.Path )] = {
53+ for (depth <- Range (0 , n))
54+ yield {
55+ val path =
56+ tester.workspacePath / " out" / Seq .fill(depth)(millBuild) / millRunnerState
57+ if (os.exists(path)) upickle.default.read[RunnerState .Frame .Logged ](os.read(path)) -> path
58+ else RunnerState .Frame .Logged (Map (), Seq (), Seq (), None , Seq (), 0 ) -> path
59+ }
60+ }
1861
19- def runAssertSuccess (tester : IntegrationTester , expected : String ) = {
20- val res = tester.eval(" foo.run" )
21- assert(res.isSuccess == true )
22- assert(res.out.contains(expected))
62+ /**
63+ * Verify that each level of the multi-level build ends upcausing the
64+ * appropriate files to get watched
65+ */
66+ def checkWatchedFiles (tester : IntegrationTester , expected0 : Seq [os.Path ]* ): Unit = {
67+ for ((expectedWatched0, (frame, path)) <- expected0.zip(loadFrames(tester, expected0.length))) {
68+ val frameWatched = frame
69+ .evalWatched
70+ .map(_.path)
71+ .filter(_.startsWith(tester.workspacePath))
72+ .filter(! _.segments.contains(" mill-launcher" ))
73+ .sorted
74+
75+ val expectedWatched = expectedWatched0.sorted
76+ assert(frameWatched == expectedWatched)
2377 }
78+ }
2479
25- def fooPaths (tester : IntegrationTester ) = Seq (
26- tester.workspacePath / " foo/compile-resources" ,
27- tester.workspacePath / " foo/resources" ,
28- tester.workspacePath / " foo/src"
29- )
30- def buildPaths (tester : IntegrationTester ) = Seq (
31- tester.workspacePath / " build.mill" ,
32- tester.workspacePath / " mill-build/compile-resources" ,
33- tester.workspacePath / " mill-build/resources" ,
34- tester.workspacePath / " mill-build/src"
35- )
36- def buildPaths2 (tester : IntegrationTester ) = Seq (
37- tester.workspacePath / " mill-build/build.mill" ,
38- tester.workspacePath / " mill-build/mill-build/compile-resources" ,
39- tester.workspacePath / " mill-build/mill-build/resources" ,
40- tester.workspacePath / " mill-build/mill-build/src"
41- )
42- def buildPaths3 (tester : IntegrationTester ) = Seq (
43- tester.workspacePath / " mill-build/mill-build/build.mill" ,
44- tester.workspacePath / " mill-build/mill-build/mill-build/compile-resources" ,
45- tester.workspacePath / " mill-build/mill-build/mill-build/resources" ,
46- tester.workspacePath / " mill-build/mill-build/mill-build/src"
80+ def evalCheckErr (tester : IntegrationTester , expectedSnippets : String * ): Unit = {
81+ // Wipe out stale state files to make sure they don't get picked up when
82+ // Mill aborts early and fails to generate a new one
83+ os.walk(tester.workspacePath / " out" ).filter(_.last == " mill-runner-state.json" ).foreach(
84+ os.remove(_)
4785 )
4886
49- def loadFrames (tester : IntegrationTester , n : Int ) = {
50- for (depth <- Range (0 , n))
51- yield {
52- val path =
53- tester.workspacePath / " out" / Seq .fill(depth)(millBuild) / millRunnerState
54- if (os.exists(path)) upickle.default.read[RunnerState .Frame .Logged ](os.read(path)) -> path
55- else RunnerState .Frame .Logged (Map (), Seq (), Seq (), None , Seq (), 0 ) -> path
56- }
87+ val res = tester.eval(" foo.run" )
88+ assert(res.isSuccess == false )
89+ // Prepend a "\n" to allow callsites to use "\n" to test for start of
90+ // line, even though the first line doesn't have a "\n" at the start
91+ val err = " ```\n " + res.err + " \n ```"
92+ for (expected <- expectedSnippets) {
93+ assert(err.contains(expected))
5794 }
95+ }
5896
59- /**
60- * Verify that each level of the multi-level build ends upcausing the
61- * appropriate files to get watched
62- */
63- def checkWatchedFiles (tester : IntegrationTester , expected0 : Seq [os.Path ]* ) = {
64- for (
65- (expectedWatched0, (frame, path)) <- expected0.zip(loadFrames(tester, expected0.length))
66- ) {
67- val frameWatched = frame
68- .evalWatched
69- .map(_.path)
70- .filter(_.startsWith(tester.workspacePath))
71- .filter(! _.segments.contains(" mill-launcher" ))
72- .sorted
73-
74- val expectedWatched = expectedWatched0.sorted
75- assert(frameWatched == expectedWatched)
97+ /**
98+ * Check whether the classloaders of the nested meta-builds are changing as
99+ * expected. `true` means a new classloader was created, `false` means
100+ * the previous classloader was re-used, `null` means there is no
101+ * classloader at that level
102+ */
103+ def checkChangedClassloaders (
104+ tester : IntegrationTester ,
105+ expectedChanged0 : java.lang.Boolean *
106+ ): Unit = {
107+ val currentClassLoaderIds =
108+ for ((frame, path) <- loadFrames(tester, expectedChanged0.length))
109+ yield frame.classLoaderIdentity
110+
111+ val changed = currentClassLoaderIds
112+ .zipAll(savedClassLoaderIds, None , None )
113+ .map { case (cur, old) =>
114+ if (cur.isEmpty) null
115+ else cur != old
76116 }
77- }
78117
79- def evalCheckErr (tester : IntegrationTester , expectedSnippets : String * ) = {
80- // Wipe out stale state files to make sure they don't get picked up when
81- // Mill aborts early and fails to generate a new one
82- os.walk(tester.workspacePath / " out" ).filter(_.last == " mill-runner-state.json" ).foreach(
83- os.remove(_)
84- )
85-
86- val res = tester.eval(" foo.run" )
87- assert(res.isSuccess == false )
88- // Prepend a "\n" to allow callsites to use "\n" to test for start of
89- // line, even though the first line doesn't have a "\n" at the start
90- val err = " ```\n " + res.err + " \n ```"
91- for (expected <- expectedSnippets) {
92- assert(err.contains(expected))
118+ val expectedChanged =
119+ if (clientServerMode) expectedChanged0
120+ else expectedChanged0.map {
121+ case java.lang.Boolean .FALSE => true
122+ case n => n
93123 }
94- }
95124
96- var savedClassLoaderIds = Seq .empty[Option [Int ]]
97-
98- /**
99- * Check whether the classloaders of the nested meta-builds are changing as
100- * expected. `true` means a new classloader was created, `false` means
101- * the previous classloader was re-used, `null` means there is no
102- * classloader at that level
103- */
104- def checkChangedClassloaders (
105- tester : IntegrationTester ,
106- expectedChanged0 : java.lang.Boolean *
107- ) = {
108- val currentClassLoaderIds =
109- for ((frame, path) <- loadFrames(tester, expectedChanged0.length))
110- yield frame.classLoaderIdentity
111-
112- val changed = currentClassLoaderIds
113- .zipAll(savedClassLoaderIds, None , None )
114- .map { case (cur, old) =>
115- if (cur.isEmpty) null
116- else cur != old
117- }
118-
119- val expectedChanged =
120- if (clientServerMode) expectedChanged0
121- else expectedChanged0.map {
122- case java.lang.Boolean .FALSE => true
123- case n => n
124- }
125-
126- assert(changed == expectedChanged)
127-
128- savedClassLoaderIds = currentClassLoaderIds
129- }
125+ assert(changed == expectedChanged)
126+
127+ savedClassLoaderIds = currentClassLoaderIds
128+ }
129+
130+ }
131+
132+ object MultiLevelBuildTestsValidEdits extends MultiLevelBuildTests {
133+ val tests : Tests = Tests {
134+ savedClassLoaderIds = Seq .empty[Option [Int ]]
130135
131- val retryCount = if (sys.env.contains(" CI" )) 2 else 0
132136 test(" validEdits" ) - retry(retryCount) {
133137 integrationTest { tester =>
134138 import tester ._
@@ -246,7 +250,11 @@ object MultiLevelBuildTests extends UtestIntegrationTestSuite {
246250 checkChangedClassloaders(tester, null , false , false , false )
247251 }
248252 }
249-
253+ }
254+ }
255+ object MultiLevelBuildTestsParseErrorEdits extends MultiLevelBuildTests {
256+ val tests : Tests = Tests {
257+ savedClassLoaderIds = Seq .empty[Option [Int ]]
250258 test(" parseErrorEdits" ) - retry(retryCount) {
251259 integrationTest { tester =>
252260 import tester ._
@@ -315,7 +323,11 @@ object MultiLevelBuildTests extends UtestIntegrationTestSuite {
315323 checkChangedClassloaders(tester, null , true , true , true )
316324 }
317325 }
318-
326+ }
327+ }
328+ object MultiLevelBuildTestsCompileErrorEdits extends MultiLevelBuildTests {
329+ val tests : Tests = Tests {
330+ savedClassLoaderIds = Seq .empty[Option [Int ]]
319331 test(" compileErrorEdits" ) - retry(retryCount) {
320332 integrationTest { tester =>
321333 import tester ._
@@ -399,7 +411,11 @@ object MultiLevelBuildTests extends UtestIntegrationTestSuite {
399411 checkChangedClassloaders(tester, null , true , false , false )
400412 }
401413 }
402-
414+ }
415+ }
416+ object MultiLevelBuildTestsRuntimeErrorEdits extends MultiLevelBuildTests {
417+ val tests : Tests = Tests {
418+ savedClassLoaderIds = Seq .empty[Option [Int ]]
403419 test(" runtimeErrorEdits" ) - retry(retryCount) {
404420 integrationTest { tester =>
405421 import tester ._
0 commit comments