From 75b7cfa7e260cabbfa7eb5b43592dee18e78a76a Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Fri, 6 Sep 2024 05:53:36 +0800 Subject: [PATCH] Add `.mill.sc` as an alternative file extension to `.mill` (#3461) Took a bit of fiddling because we had to stop using `os.Path#ext` because it only takes the last segment, and also now one of the extensions is a suffix of another. There will likely be a short tail of bugs that arise because of this, but it shouldn't be too burdensome to resolve them. Otherwise this functionality should be relatively cheap to add and maintain since we already have the extensions and file names centralized in `CodeGenConstants.java`. This is a compromise between `.mill` and `.mill.sc`. I still believe long term `.mill` is better, but I can believe that short term pain of having everything not understand your `.mill` extensions is painful. This lets people use `.mill.sc` where preferable, and `.mill` otherwise by default. I expect long term `.mill` will be superior once all the tooling has caught up, but until then people can use `.mill.sc` if they want the generic `.sc` Scala script support that many tools use. Having two extensions isn't that unusual, e.g. Bazel supports both `BUILD` and `BUILD.bazel` as file extenions, supported indefinitely. The cost of adding two extensions isn't significantly more than the cost of adding one: in any case we need to send PRs to the various tools, and a PR adding two extensions isn't any more difficult than a PR adding one. Added an example test `13-helper-files-mill-sc`, variant of `11-helper-files`, which should exercise most of the file discovery/import/codegen/etc. logic that may be impacted by the extension --- .github/workflows/actions.yml | 97 ++--- .github/workflows/run-mill-action.yml | 30 +- build.mill | 2 +- ci/test-mill-dev.sh | 2 + ci/test-mill-release.sh | 2 + .../depth/large/11-helper-files/build.mill | 1 + .../13-helper-files-mill-sc/build.mill.sc | 39 ++ .../foo/package.mill.sc | 10 + .../13-helper-files-mill-sc/foo/src/Foo.scala | 8 + .../foo/versions.mill.sc | 3 + .../13-helper-files-mill-sc/src/Main.scala | 6 + .../13-helper-files-mill-sc/util.mill.sc | 9 + .../web/2-hello-spring-boot/build.mill | 2 +- .../javalib/web/3-todo-spring-boot/build.mill | 2 +- .../javalib/web/4-hello-micronaut/build.mill | 2 +- .../javalib/web/5-todo-micronaut/build.mill | 2 +- .../src/BuildFileInSubfolderTests.scala | 10 +- .../compile-error/src/CompileErrorTests.scala | 10 +- .../src/CrossCollisionsTests.scala | 10 +- .../src/InvalidMetaModuleTests.scala | 10 +- .../src/InvalidPackageDeclaration.scala | 10 +- .../src/InvalidRootModuleTests.scala | 10 +- .../src/InvalidSubfolderRootModuleTests.scala | 10 +- .../src/MisnamedRootModuleTests.scala | 10 +- .../src/MissingBuildFileTests.scala | 10 +- .../src/ModuleInitErrorTests.scala | 40 +- .../ModuleOutsideTopLevelModuleTests.scala | 9 +- .../src/NoModulesInHelperFileTests.scala | 9 +- .../src/PackageFileInRootTests.scala | 9 +- .../parse-error/src/ParseErrorTests.scala | 9 +- .../src/RootModuleCompileErrorTests.scala | 9 +- .../RootSubfolderModuleCollisionTests.scala | 9 +- .../SubfolderHelperModuleCollisionTests.scala | 9 +- .../src/SubfolderMissingBuildPrefix.scala | 9 +- .../ThingsOutsideTopLevelModuleTests.scala | 9 +- .../src/AuxiliaryClassFilesTests.scala | 92 ++--- .../src/DocAnnotationsTests.scala | 8 +- .../feature/hygiene/src/HygieneTests.scala | 9 +- .../src/ImportIvyWorkerInvalidation.scala | 8 +- .../feature/init/src/MillInitTests.scala | 8 +- .../large-project/src/LargeProjectTests.scala | 8 +- .../mill-jvm-opts/src/MillJvmOptsTests.scala | 8 +- .../src/NonIdentifierImport.scala | 8 +- .../src/MillPluginClasspathTest.scala | 58 +-- .../src/PrivateMethodsTests.scala | 8 +- .../src/ImportRepoTests.scala | 8 +- .../scoverage/src/ScoverageTests.scala | 8 +- .../src/SubprocessStdoutTests.scala | 11 +- integration/ide/bloop/src/BloopTests.scala | 28 +- .../src/BspInstallDebugTests.scala | 8 +- .../ide/bsp-install/src/BspInstallTests.scala | 8 +- .../ide/bsp-modules/src/BspModulesTests.scala | 12 +- .../gen-idea/src/GenIdeaExtendedTests.scala | 8 +- .../ide/gen-idea/src/GenIdeaTests.scala | 10 +- .../codesig-hello/src/CodeSigHelloTests.scala | 8 +- .../src/CodeSigNestedTests.scala | 12 +- .../src/CodeSigScalaModuleTests.scala | 11 +- .../src/ScriptsInvalidationTests.scala | 118 +++--- .../src/MultiLevelBuildTests.scala | 369 +++++++++++------- .../src/WatchSourceInputTests.scala | 74 ++-- .../src/ZincIncrementalCompilationTests.scala | 12 +- main/api/src/mill/api/Result.scala | 4 +- .../mill/main/client/CodeGenConstants.java | 6 +- .../main/client/FileToStreamTailerTest.java | 6 +- .../mill/main/server/ClientServerTests.scala | 7 +- main/util/src/mill/util/CoursierSupport.scala | 8 +- mill-build/src/ExampleParser.scala | 2 +- .../mill/linenumbers/LineNumberPlugin.scala | 2 +- runner/src/mill/runner/CodeGen.scala | 9 +- runner/src/mill/runner/FileImportGraph.scala | 13 +- scalalib/src/mill/scalalib/JavaModule.scala | 8 +- scalalib/src/mill/scalalib/Lib.scala | 4 +- .../scalalib/scalafmt/ScalafmtModule.scala | 4 +- .../src/mill/testkit/ExampleTestSuite.scala | 11 - .../mill/testkit/IntegrationTestSuite.scala | 9 +- .../testkit/IntegrationTestSuiteBase.scala | 17 +- .../src/mill/testkit/IntegrationTester.scala | 3 +- .../mill/testkit/IntegrationTesterBase.scala | 6 +- .../mill/testkit/UtestExampleTestSuite.scala | 15 + .../mill/testkit/IntegrationTesterTests.scala | 31 +- 80 files changed, 873 insertions(+), 640 deletions(-) create mode 100644 example/depth/large/13-helper-files-mill-sc/build.mill.sc create mode 100644 example/depth/large/13-helper-files-mill-sc/foo/package.mill.sc create mode 100644 example/depth/large/13-helper-files-mill-sc/foo/src/Foo.scala create mode 100644 example/depth/large/13-helper-files-mill-sc/foo/versions.mill.sc create mode 100644 example/depth/large/13-helper-files-mill-sc/src/Main.scala create mode 100644 example/depth/large/13-helper-files-mill-sc/util.mill.sc delete mode 100644 testkit/src/mill/testkit/ExampleTestSuite.scala create mode 100644 testkit/src/mill/testkit/UtestExampleTestSuite.scala diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 162cc87bed7..1d05e7d3e14 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -28,24 +28,30 @@ concurrency: cancel-in-progress: true jobs: - + build-linux: + uses: ./.github/workflows/run-mill-action.yml + with: + java-version: 11 + millargs: __.compile + populate_cache: true + build-windows: + uses: ./.github/workflows/run-mill-action.yml + with: + os: windows-latest + java-version: 11 + millargs: __.compile + populate_cache: true itest: + needs: build-linux strategy: fail-fast: false matrix: include: # bootstrap tests - java-version: 11 # Have one job on oldest JVM - buildcmd: ci/test-mill-dev.sh + buildcmd: ci/test-mill-dev.sh && ci/test-mill-release.sh && ./mill -i -k __.ivyDepsTree && ./mill -i -k __.ivyDepsTree --withRuntime - java-version: 17 # Have one job on default JVM - buildcmd: ci/test-mill-release.sh - - java-version: 17 buildcmd: ci/test-mill-bootstrap.sh - # Just some reporting to enable reasoning about library upgrades - - java-version: 11 - buildcmd: | - ./mill -i -k __.ivyDepsTree - ./mill -i -k __.ivyDepsTree --withRuntime uses: ./.github/workflows/run-mill-action.yml with: @@ -53,55 +59,48 @@ jobs: buildcmd: ${{ matrix.buildcmd }} linux: + needs: build-linux strategy: fail-fast: false matrix: - java-version: [11, 17] - millargs: - # Run unit and module tests on both oldest and newest Java versions - - '"{main,scalalib,testrunner,bsp}.__.test"' + include: - # For most tests, run them arbitrarily on Java 8 or Java 17 on Linux, and + # For most tests, run them arbitrarily on Java 11 or Java 17 on Linux, and # on the opposite version on Windows below, so we get decent coverage of # each test on each Java version and each operating system + # We also try to group tests together to manuaully balance out the runtimes of each jobs + - java-version: 17 + millargs: "'{main,scalalib,testrunner,bsp,testkit}.__.testCached'" - java-version: 11 - millargs: '"scalajslib.__.test"' - - java-version: 11 - millargs: '"scalanativelib.__.test"' + millargs: "'{scalajslib,scalanativelib}.__.testCached'" - java-version: 17 - millargs: "contrib._.test" + millargs: "contrib.__.testCached" - # Group these tests together to try and balance out the runtimes of each job - # Just running in `local` mode since they shouldn't depend on the mode - java-version: 17 - millargs: "'example.javalib.__.local.test'" + millargs: "'example.javalib.__.local.testCached'" - java-version: 17 - millargs: "'example.scalalib.__.local.test'" + millargs: "'example.scalalib.__.local.testCached'" - java-version: 11 - millargs: "'example.thirdparty[{mockito,acyclic,commons-io}].local.test'" + millargs: "'example.thirdparty[{mockito,acyclic,commons-io}].local.testCached'" - java-version: 17 - millargs: "'example.thirdparty[{fansi,jimfs,netty,gatling}].local.test'" + millargs: "'example.thirdparty[{fansi,jimfs,netty,gatling}].local.testCached'" - java-version: 11 - millargs: "'example.depth.__.local.test'" - - java-version: 17 - millargs: "'example.extending.__.local.test'" + millargs: "'example.{depth,extending}.__.local.testCached'" # Most of these integration tests should not depend on which mode they # are run in, so just run them in `local` - java-version: 11 - millargs: "'integration.{failure,feature,ide}[_].local.test'" + millargs: "'integration.{failure,feature,ide}.__.local.testCached'" - # These invalidation tests need to be exercised in all three execution modes + # These invalidation tests need to be exercised in both execution modes # to make sure they work with and without -i/--no-server being passed - java-version: 17 - millargs: "'integration.invalidation[_].local.test'" - - java-version: 17 - millargs: "'integration.invalidation[_].fork.test'" + millargs: "'integration.invalidation.__.fork.testCached'" - java-version: 17 - millargs: "'integration.invalidation[_].server.test'" + millargs: "'integration.invalidation.__.server.testCached'" # Check docsite compiles - - java-version: 17 + - java-version: 11 millargs: docs.githubPages @@ -111,32 +110,22 @@ jobs: millargs: ${{ matrix.millargs }} compiler-bridge: + needs: build-linux uses: ./.github/workflows/run-mill-action.yml with: java-version: '8' millargs: bridge.__.publishLocal env-bridge-versions: 'essential' - format-check: - uses: ./.github/workflows/run-mill-action.yml - with: - java-version: '11' - millargs: mill.scalalib.scalafmt.ScalafmtModule/checkFormatAll __.sources - - scalafix-check: + format-scalafix-bincompat: + needs: build-linux uses: ./.github/workflows/run-mill-action.yml with: java-version: '11' - millargs: -i -k __.fix --check - - bincompat-check: - uses: ./.github/workflows/run-mill-action.yml - with: - java-version: '11' - millargs: __.mimaReportBinaryIssues - continue-on-error: true + buildcmd: ./mill -i mill.scalalib.scalafmt.ScalafmtModule/checkFormatAll __.sources + __.mimaReportBinaryIssues + __.fix --check windows: + needs: build-windows strategy: fail-fast: false matrix: @@ -145,16 +134,12 @@ jobs: # the whole suite can take hours on windows v.s. half an hour on linux - java-version: 11 millargs: '"{main,scalalib,bsp}.__.test"' - - java-version: 17 - millargs: '"scalajslib.__.test"' - java-version: 11 - millargs: '"example.scalalib.basic.__.fork.test"' + millargs: '"example.scalalib.{basic,web}.__.fork.test"' - java-version: 17 - millargs: "'integration.feature[_].fork.test'" + millargs: "'integration.{feature,failure}[_].fork.test'" - java-version: 11 millargs: "'integration.invalidation[_].server.test'" - - java-version: 17 - millargs: "'integration.failure[_].fork.test'" - java-version: 11 millargs: "contrib.__.test" @@ -167,7 +152,7 @@ jobs: publish-sonatype: # when in master repo, publish all tags and manual runs on main if: github.repository == 'com-lihaoyi/mill' && (startsWith( github.ref, 'refs/tags/') || (github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch' ) ) - needs: [linux, windows, compiler-bridge, format-check, bincompat-check, scalafix-check, itest] + needs: [linux, windows, compiler-bridge, format-scalafix-bincompat, itest] runs-on: ubuntu-latest diff --git a/.github/workflows/run-mill-action.yml b/.github/workflows/run-mill-action.yml index 9fef5b49d4e..6d7d9266992 100644 --- a/.github/workflows/run-mill-action.yml +++ b/.github/workflows/run-mill-action.yml @@ -18,6 +18,9 @@ on: continue-on-error: default: false type: boolean + populate_cache: + default: false + type: boolean timeout-minutes: default: 60 type: number @@ -38,6 +41,16 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + if: ${{ inputs.populate_cache }} + + - uses: actions/download-artifact@v4 + if: ${{ !inputs.populate_cache }} + with: + path: . + name: ${{ inputs.os }}-artifact + + - name: chmod executable + run: "chmod -R +x ." - uses: coursier/cache-action@v6 @@ -60,5 +73,18 @@ jobs: if: inputs.millargs != '' && !startsWith(inputs.os, 'windows') - name: Run Mill (on Windows) '${{ inputs.millargs }}' - run: cmd /C %GITHUB_WORKSPACE%\ci\mill.bat -i -d -k ${{ inputs.millargs }} - if: inputs.millargs != '' && startsWith(inputs.os, 'windows') \ No newline at end of file + run: cmd /C %GITHUB_WORKSPACE%\ci\mill.bat -ij1 __.resolvedIvyDeps; cmd /C %GITHUB_WORKSPACE%\ci\mill.bat -i -j1 -k ${{ inputs.millargs }} + if: inputs.millargs != '' && startsWith(inputs.os, 'windows') + + - name: Run Mill (on Windows) Worker Cleanup + run: 'taskkill -f -im java* && rm -rf out/mill-worker-*' + if: inputs.millargs != '' && startsWith(inputs.os, 'windows') + shell: bash + continue-on-error: true + + - uses: actions/upload-artifact@v4.3.5 + with: + path: . + name: ${{ inputs.os }}-artifact + include-hidden-files: true + if: ${{ inputs.populate_cache }} \ No newline at end of file diff --git a/build.mill b/build.mill index ca4043efb91..1a02d6470eb 100644 --- a/build.mill +++ b/build.mill @@ -151,7 +151,7 @@ object Deps { val log4j2Core = ivy"org.apache.logging.log4j:log4j-core:2.23.1" val osLib = ivy"com.lihaoyi::os-lib:0.10.5" val pprint = ivy"com.lihaoyi::pprint:0.9.0" - val mainargs = ivy"com.lihaoyi::mainargs:0.7.2" + val mainargs = ivy"com.lihaoyi::mainargs:0.7.4" val millModuledefsVersion = "0.11.0-M2" val millModuledefsString = s"com.lihaoyi::mill-moduledefs:${millModuledefsVersion}" val millModuledefs = ivy"${millModuledefsString}" diff --git a/ci/test-mill-dev.sh b/ci/test-mill-dev.sh index 44828909db0..8ffede49a8c 100755 --- a/ci/test-mill-dev.sh +++ b/ci/test-mill-dev.sh @@ -16,3 +16,5 @@ test -d $EXAMPLE/out/foo/3.3.3/compile.dest ./mill -i dist.run $EXAMPLE show "bar[2.13.8].assembly" test -f $EXAMPLE/out/bar/2.13.8/assembly.dest/out.jar + +./mill -i dist.run $EXAMPLE shutdown \ No newline at end of file diff --git a/ci/test-mill-release.sh b/ci/test-mill-release.sh index 562771dff96..56fd55d69c4 100755 --- a/ci/test-mill-release.sh +++ b/ci/test-mill-release.sh @@ -19,3 +19,5 @@ test -d $EXAMPLE/out/foo/3.3.3/compile.dest (cd $EXAMPLE && ../../../../out/dist/assembly.dest/mill show "bar[2.13.8].assembly") test -f $EXAMPLE/out/bar/2.13.8/assembly.dest/out.jar + +(cd $EXAMPLE && ../../../../out/dist/assembly.dest/mill shutdown) \ No newline at end of file diff --git a/example/depth/large/11-helper-files/build.mill b/example/depth/large/11-helper-files/build.mill index 2f7cd012eb8..416c9a0cf55 100644 --- a/example/depth/large/11-helper-files/build.mill +++ b/example/depth/large/11-helper-files/build.mill @@ -8,6 +8,7 @@ object `package` extends RootModule with MyModule{ "MY_PROJECT_VERSION" -> versions.myProjectVersion, ) } + /** See Also: util.mill */ /** See Also: foo/package.mill */ /** See Also: foo/versions.mill */ diff --git a/example/depth/large/13-helper-files-mill-sc/build.mill.sc b/example/depth/large/13-helper-files-mill-sc/build.mill.sc new file mode 100644 index 00000000000..7a757d7b0ba --- /dev/null +++ b/example/depth/large/13-helper-files-mill-sc/build.mill.sc @@ -0,0 +1,39 @@ +package build +import mill._, scalalib._ +import $file.foo.versions +import $file.util.MyModule +object `package` extends RootModule with MyModule{ + def forkEnv = Map( + "MY_SCALA_VERSION" -> build.scalaVersion(), + "MY_PROJECT_VERSION" -> versions.myProjectVersion, + ) +} +///** See Also: util.mill.sc */ +///** See Also: foo/package.mill.sc */ +///** See Also: foo/versions.mill.sc */ + + +// Apart from having `package` files in subfolders to define modules, Mill +// also allows you to have helper code in any `*.mill` file in the same folder +// as your `build.mill` or a `package.mill`. +// +// Different helper scripts and ``build.mill``/``package`` files can all refer to +// each other using the `build` object, which marks the root object of your build. +// In this example: +// +// * `build.mill` can be referred to as simple `build` +// * `util.mill` can be referred to as simple `$file.util` +// * `foo/package` can be referred to as simple `build.foo` +// * `foo/versions.mill` can be referred to as simple `$file.foo.versions` + +/** Usage + +> ./mill run +Main Env build.util.myScalaVersion: 2.13.14 +Main Env build.foo.versions.myProjectVersion: 0.0.1 + +> ./mill foo.run +Foo Env build.util.myScalaVersion: 2.13.14 +Foo Env build.foo.versions.myProjectVersion: 0.0.1 + +*/ diff --git a/example/depth/large/13-helper-files-mill-sc/foo/package.mill.sc b/example/depth/large/13-helper-files-mill-sc/foo/package.mill.sc new file mode 100644 index 00000000000..a28fe0a29ce --- /dev/null +++ b/example/depth/large/13-helper-files-mill-sc/foo/package.mill.sc @@ -0,0 +1,10 @@ +package build.foo +import mill._, scalalib._ +import $file.util +import $file.foo.versions.myProjectVersion +object `package` extends RootModule with build_.util.MyModule { + def forkEnv = Map( + "MY_SCALA_VERSION" -> util.myScalaVersion, + "MY_PROJECT_VERSION" -> myProjectVersion + ) +} diff --git a/example/depth/large/13-helper-files-mill-sc/foo/src/Foo.scala b/example/depth/large/13-helper-files-mill-sc/foo/src/Foo.scala new file mode 100644 index 00000000000..08fe8028aac --- /dev/null +++ b/example/depth/large/13-helper-files-mill-sc/foo/src/Foo.scala @@ -0,0 +1,8 @@ +package foo + +object Foo { + def main(args: Array[String]): Unit = { + println("Foo Env build.util.myScalaVersion: " + sys.env("MY_SCALA_VERSION")) + println("Foo Env build.foo.versions.myProjectVersion: " + sys.env("MY_PROJECT_VERSION")) + } +} diff --git a/example/depth/large/13-helper-files-mill-sc/foo/versions.mill.sc b/example/depth/large/13-helper-files-mill-sc/foo/versions.mill.sc new file mode 100644 index 00000000000..d2d0681b67c --- /dev/null +++ b/example/depth/large/13-helper-files-mill-sc/foo/versions.mill.sc @@ -0,0 +1,3 @@ +package build.foo + +def myProjectVersion = "0.0.1" \ No newline at end of file diff --git a/example/depth/large/13-helper-files-mill-sc/src/Main.scala b/example/depth/large/13-helper-files-mill-sc/src/Main.scala new file mode 100644 index 00000000000..f8e6fdfa68f --- /dev/null +++ b/example/depth/large/13-helper-files-mill-sc/src/Main.scala @@ -0,0 +1,6 @@ +object Main { + def main(args: Array[String]): Unit = { + println("Main Env build.util.myScalaVersion: " + sys.env("MY_SCALA_VERSION")) + println("Main Env build.foo.versions.myProjectVersion: " + sys.env("MY_PROJECT_VERSION")) + } +} diff --git a/example/depth/large/13-helper-files-mill-sc/util.mill.sc b/example/depth/large/13-helper-files-mill-sc/util.mill.sc new file mode 100644 index 00000000000..0e4b102796e --- /dev/null +++ b/example/depth/large/13-helper-files-mill-sc/util.mill.sc @@ -0,0 +1,9 @@ +package build + +import mill._, scalalib._ + +def myScalaVersion = "2.13.14" + +trait MyModule extends ScalaModule { + def scalaVersion = myScalaVersion +} diff --git a/example/javalib/web/2-hello-spring-boot/build.mill b/example/javalib/web/2-hello-spring-boot/build.mill index da4b7f308c1..cc3ce13aad7 100644 --- a/example/javalib/web/2-hello-spring-boot/build.mill +++ b/example/javalib/web/2-hello-spring-boot/build.mill @@ -23,7 +23,7 @@ object `package` extends RootModule with JavaModule { > mill test ...com.example.HelloSpringBootTest#shouldReturnDefaultMessage() finished... -> mill runBackground; sleep 10 # give time for server to start +> mill runBackground; sleep 15 # give time for server to start > curl http://localhost:8086 ...

Hello, World!

... diff --git a/example/javalib/web/3-todo-spring-boot/build.mill b/example/javalib/web/3-todo-spring-boot/build.mill index 6b48e5f64af..8f0d5d12abd 100644 --- a/example/javalib/web/3-todo-spring-boot/build.mill +++ b/example/javalib/web/3-todo-spring-boot/build.mill @@ -61,7 +61,7 @@ object `package` extends RootModule with JavaModule { ...com.example.TodomvcIntegrationTests#homePageLoads() finished... ...com.example.TodomvcIntegrationTests#addNewTodoItem() finished... -> mill test.runBackground; sleep 10 # give time for server to start +> mill test.runBackground; sleep 15 # give time for server to start > curl http://localhost:8087 ...

todos

... diff --git a/example/javalib/web/4-hello-micronaut/build.mill b/example/javalib/web/4-hello-micronaut/build.mill index 3dba9169676..ff6ffb67a54 100644 --- a/example/javalib/web/4-hello-micronaut/build.mill +++ b/example/javalib/web/4-hello-micronaut/build.mill @@ -69,7 +69,7 @@ trait MicronautModule extends MavenModule{ > mill test ...example.micronaut.HelloControllerTest#testHello()... -> mill runBackground; sleep 2 # give time for server to start +> mill runBackground; sleep 5 # give time for server to start > curl http://localhost:8088/hello ...Hello World... diff --git a/example/javalib/web/5-todo-micronaut/build.mill b/example/javalib/web/5-todo-micronaut/build.mill index d2b4c180ffe..a81c48a5b88 100644 --- a/example/javalib/web/5-todo-micronaut/build.mill +++ b/example/javalib/web/5-todo-micronaut/build.mill @@ -90,7 +90,7 @@ trait MicronautModule extends MavenModule{ ...example.micronaut.TodoItemControllerTest... ...example.micronaut.HtmxWebJarsTest... -> mill runBackground; sleep 2 # give time for server to start +> mill runBackground; sleep 5 # give time for server to start > curl http://localhost:8088 ...

todos

... diff --git a/integration/failure/build-file-in-subfolder/src/BuildFileInSubfolderTests.scala b/integration/failure/build-file-in-subfolder/src/BuildFileInSubfolderTests.scala index f5d51acafd0..ea464c3864c 100644 --- a/integration/failure/build-file-in-subfolder/src/BuildFileInSubfolderTests.scala +++ b/integration/failure/build-file-in-subfolder/src/BuildFileInSubfolderTests.scala @@ -1,15 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object BuildFileInSubfolderTests extends IntegrationTestSuite { +object BuildFileInSubfolderTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("success") { - val res = eval(("resolve", "_")) + test("success") - integrationTest { tester => + val res = tester.eval(("resolve", "_")) assert(res.isSuccess == false) assert(res.err.contains("Mill build.mill files can only be in the project root")) } diff --git a/integration/failure/compile-error/src/CompileErrorTests.scala b/integration/failure/compile-error/src/CompileErrorTests.scala index 6c718d80ae4..999c258a9e9 100644 --- a/integration/failure/compile-error/src/CompileErrorTests.scala +++ b/integration/failure/compile-error/src/CompileErrorTests.scala @@ -1,15 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object CompileErrorTests extends IntegrationTestSuite { +object CompileErrorTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test { - val res = eval("foo.scalaVersion") + test - integrationTest { tester => + val res = tester.eval("foo.scalaVersion") assert(res.isSuccess == false) assert(res.err.contains("""bar.mill:15:9: not found: value doesntExist""")) diff --git a/integration/failure/cross-collisions/src/CrossCollisionsTests.scala b/integration/failure/cross-collisions/src/CrossCollisionsTests.scala index f964a019fb7..04805609ab5 100644 --- a/integration/failure/cross-collisions/src/CrossCollisionsTests.scala +++ b/integration/failure/cross-collisions/src/CrossCollisionsTests.scala @@ -1,15 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object CrossCollisionsTests extends IntegrationTestSuite { +object CrossCollisionsTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("detect-collision") { - val res = eval(("resolve", "foo._")) + test("detect-collision") - integrationTest { tester => + val res = tester.eval(("resolve", "foo._")) assert(!res.isSuccess) assert(res.err.contains("Cross module ")) assert( diff --git a/integration/failure/invalid-meta-module/src/InvalidMetaModuleTests.scala b/integration/failure/invalid-meta-module/src/InvalidMetaModuleTests.scala index 3387cc7fbeb..429d22a44c5 100644 --- a/integration/failure/invalid-meta-module/src/InvalidMetaModuleTests.scala +++ b/integration/failure/invalid-meta-module/src/InvalidMetaModuleTests.scala @@ -1,15 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object InvalidMetaModuleTests extends IntegrationTestSuite { +object InvalidMetaModuleTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("success") { - val res = eval(("resolve", "_")) + test("success") - integrationTest { tester => + val res = tester.eval(("resolve", "_")) assert(res.isSuccess == false) assert(res.err.contains("object `package` ")) assert(res.err.contains("must extend `MillBuildRootModule`")) diff --git a/integration/failure/invalid-package-declaration/src/InvalidPackageDeclaration.scala b/integration/failure/invalid-package-declaration/src/InvalidPackageDeclaration.scala index b7afb1dbd2e..f2806779c65 100644 --- a/integration/failure/invalid-package-declaration/src/InvalidPackageDeclaration.scala +++ b/integration/failure/invalid-package-declaration/src/InvalidPackageDeclaration.scala @@ -1,15 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object InvalidPackageDeclaration extends IntegrationTestSuite { +object InvalidPackageDeclaration extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("success") { - val res = eval(("resolve", "_")) + test("success") - integrationTest { tester => + val res = tester.eval(("resolve", "_")) assert(res.isSuccess == false) assert(res.err.contains( """Package declaration "package wrong" in build.mill does not match folder structure. Expected: "package build"""" diff --git a/integration/failure/invalid-root-module/src/InvalidRootModuleTests.scala b/integration/failure/invalid-root-module/src/InvalidRootModuleTests.scala index 7d1573a2044..fc0831494c9 100644 --- a/integration/failure/invalid-root-module/src/InvalidRootModuleTests.scala +++ b/integration/failure/invalid-root-module/src/InvalidRootModuleTests.scala @@ -1,15 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object InvalidRootModuleTests extends IntegrationTestSuite { +object InvalidRootModuleTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("success") { - val res = eval(("resolve", "_")) + test("success") - integrationTest { tester => + val res = tester.eval(("resolve", "_")) assert(res.isSuccess == false) assert(res.err.contains("object `package` in ")) assert(res.err.contains("must extend `RootModule`")) diff --git a/integration/failure/invalid-subfolder-root-module/src/InvalidSubfolderRootModuleTests.scala b/integration/failure/invalid-subfolder-root-module/src/InvalidSubfolderRootModuleTests.scala index 6cbd9d98f75..8d9c8b11ddc 100644 --- a/integration/failure/invalid-subfolder-root-module/src/InvalidSubfolderRootModuleTests.scala +++ b/integration/failure/invalid-subfolder-root-module/src/InvalidSubfolderRootModuleTests.scala @@ -1,15 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object InvalidSubfolderRootModuleTests extends IntegrationTestSuite { +object InvalidSubfolderRootModuleTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("success") { - val res = eval(("resolve", "_")) + test("success") - integrationTest { tester => + val res = tester.eval(("resolve", "_")) assert(res.isSuccess == false) assert(res.err.contains("object `package` ")) assert(res.err.contains("must extend `RootModule`")) diff --git a/integration/failure/misnamed-root-module/src/MisnamedRootModuleTests.scala b/integration/failure/misnamed-root-module/src/MisnamedRootModuleTests.scala index 466f32784e7..c21e8da6a32 100644 --- a/integration/failure/misnamed-root-module/src/MisnamedRootModuleTests.scala +++ b/integration/failure/misnamed-root-module/src/MisnamedRootModuleTests.scala @@ -1,15 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object MisnamedRootModuleTests extends IntegrationTestSuite { +object MisnamedRootModuleTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("success") { - val res = eval(("resolve", "_")) + test("success") - integrationTest { tester => + val res = tester.eval(("resolve", "_")) assert(!res.isSuccess) assert(res.err.contains( "Only one RootModule named `package` can be defined in a build, not: foo" diff --git a/integration/failure/missing-build-file/src/MissingBuildFileTests.scala b/integration/failure/missing-build-file/src/MissingBuildFileTests.scala index 412c1766651..2b6f625d991 100644 --- a/integration/failure/missing-build-file/src/MissingBuildFileTests.scala +++ b/integration/failure/missing-build-file/src/MissingBuildFileTests.scala @@ -1,15 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object MissingBuildFileTests extends IntegrationTestSuite { +object MissingBuildFileTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test { - val res = eval(("resolve", "_")) + test - integrationTest { tester => + val res = tester.eval(("resolve", "_")) assert(!res.isSuccess) val s"build.mill file not found in $msg. Are you in a Mill project folder?" = res.err } diff --git a/integration/failure/module-init-error/src/ModuleInitErrorTests.scala b/integration/failure/module-init-error/src/ModuleInitErrorTests.scala index a0609b6856d..c06cffab877 100644 --- a/integration/failure/module-init-error/src/ModuleInitErrorTests.scala +++ b/integration/failure/module-init-error/src/ModuleInitErrorTests.scala @@ -1,22 +1,20 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object ModuleInitErrorTests extends IntegrationTestSuite { +object ModuleInitErrorTests extends UtestIntegrationTestSuite { def captureOutErr = true val tests: Tests = Tests { - initWorkspace() - - test("resolve") { + test("resolve") - integrationTest { tester => // Ensure that resolve works even of the modules containing the resolved // tasks are broken - val res1 = eval(("resolve", "foo.fooTarget")) + val res1 = tester.eval(("resolve", "foo.fooTarget")) assert(res1.isSuccess == true) assert(res1.out.contains("foo.fooTarget")) - val res2 = eval(("resolve", "_._")) + val res2 = tester.eval(("resolve", "_._")) assert(res2.isSuccess == true) assert( res2.out.contains("bar.barCommand"), @@ -26,11 +24,11 @@ object ModuleInitErrorTests extends IntegrationTestSuite { res2.out.contains("foo.fooCommand") ) - val res3 = eval(("resolve", "__.fooTarget")) + val res3 = tester.eval(("resolve", "__.fooTarget")) assert(res3.isSuccess == true) assert(res3.out.contains("foo.fooTarget")) - val res4 = eval(("resolve", "__")) + val res4 = tester.eval(("resolve", "__")) assert(res4.isSuccess == true) assert(res4.out.contains("bar")) assert(res4.out.contains("bar.barCommand")) @@ -43,21 +41,24 @@ object ModuleInitErrorTests extends IntegrationTestSuite { assert(res4.out.contains("foo.fooTarget")) } - test("rootTarget") { + test("rootTarget") - integrationTest { tester => + import tester._ // If we specify a target in the root module, we are not // affected by the sub-modules failing to initialize val res = eval("rootTarget") assert(res.isSuccess == true) assert(res.out.contains("""Running rootTarget""")) } - test("rootCommand") { + test("rootCommand") - integrationTest { tester => + import tester._ // If we specify a target in the root module, we are not // affected by the sub-modules failing to initialize val res = eval(("rootCommand", "-s", "hello")) assert(res.isSuccess == true) assert(res.out.contains("""Running rootCommand hello""")) } - test("fooTarget") { + test("fooTarget") - integrationTest { tester => + import tester._ val res = eval("foo.fooTarget") assert(res.isSuccess == false) assert(fansi.Str(res.err).plainText.contains("""java.lang.Exception: Foo Boom""")) @@ -65,29 +66,34 @@ object ModuleInitErrorTests extends IntegrationTestSuite { // frames from the Mill launcher assert(fansi.Str(res.err).plainText.linesIterator.size < 20) } - test("fooCommand") { + test("fooCommand") - integrationTest { tester => + import tester._ val res = eval(("foo.fooCommand", "-s", "hello")) assert(res.isSuccess == false) assert(fansi.Str(res.err).plainText.contains("""java.lang.Exception: Foo Boom""")) assert(fansi.Str(res.err).plainText.linesIterator.size < 20) } - test("barTarget") { + test("barTarget") - integrationTest { tester => + import tester._ val res = eval("bar.barTarget") assert(res.isSuccess == true) assert(res.out.contains("""Running barTarget""")) } - test("barCommand") { + test("barCommand") - integrationTest { tester => + import tester._ val res = eval(("bar.barCommand", "-s", "hello")) assert(res.isSuccess == true) assert(res.out.contains("""Running barCommand hello""")) } - test("quxTarget") { + test("quxTarget") - integrationTest { tester => + import tester._ val res = eval("bar.qux.quxTarget") assert(res.isSuccess == false) assert(fansi.Str(res.err).plainText.contains("""java.lang.Exception: Qux Boom""")) assert(fansi.Str(res.err).plainText.linesIterator.size < 20) } - test("quxCommand") { + test("quxCommand") - integrationTest { tester => + import tester._ val res = eval(("bar.qux.quxCommand", "-s", "hello")) assert(res.isSuccess == false) assert(fansi.Str(res.err).plainText.contains("""java.lang.Exception: Qux Boom""")) diff --git a/integration/failure/module-outside-top-level-module/src/ModuleOutsideTopLevelModuleTests.scala b/integration/failure/module-outside-top-level-module/src/ModuleOutsideTopLevelModuleTests.scala index 0633cb930ac..28aac9aea3a 100644 --- a/integration/failure/module-outside-top-level-module/src/ModuleOutsideTopLevelModuleTests.scala +++ b/integration/failure/module-outside-top-level-module/src/ModuleOutsideTopLevelModuleTests.scala @@ -1,14 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object ModuleOutsideTopLevelModuleTests extends IntegrationTestSuite { +object ModuleOutsideTopLevelModuleTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("success") { + test("success") - integrationTest { tester => + import tester._ val res = eval(("resolve", "_")) assert(!res.isSuccess) assert( diff --git a/integration/failure/no-modules-in-helper-file/src/NoModulesInHelperFileTests.scala b/integration/failure/no-modules-in-helper-file/src/NoModulesInHelperFileTests.scala index 0b5afb37c33..f68f2b874c3 100644 --- a/integration/failure/no-modules-in-helper-file/src/NoModulesInHelperFileTests.scala +++ b/integration/failure/no-modules-in-helper-file/src/NoModulesInHelperFileTests.scala @@ -1,14 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object NoModulesInHelperFileTests extends IntegrationTestSuite { +object NoModulesInHelperFileTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("success") { + test("success") - integrationTest { tester => + import tester._ val res = eval(("resolve", "_")) assert(res.isSuccess == false) assert( diff --git a/integration/failure/package-file-in-root/src/PackageFileInRootTests.scala b/integration/failure/package-file-in-root/src/PackageFileInRootTests.scala index 87b8f4eddf2..e193c0534fa 100644 --- a/integration/failure/package-file-in-root/src/PackageFileInRootTests.scala +++ b/integration/failure/package-file-in-root/src/PackageFileInRootTests.scala @@ -1,14 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object PackageFileInRootTests extends IntegrationTestSuite { +object PackageFileInRootTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("success") { + test("success") - integrationTest { tester => + import tester._ val res = eval(("resolve", "_")) assert(res.isSuccess == false) assert(res.err.contains("Mill package.mill files can only be in subfolders")) diff --git a/integration/failure/parse-error/src/ParseErrorTests.scala b/integration/failure/parse-error/src/ParseErrorTests.scala index 772df82f476..8206a60ad60 100644 --- a/integration/failure/parse-error/src/ParseErrorTests.scala +++ b/integration/failure/parse-error/src/ParseErrorTests.scala @@ -1,14 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object ParseErrorTests extends IntegrationTestSuite { +object ParseErrorTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test { + test - integrationTest { tester => + import tester._ val res = eval("foo.scalaVersion") assert(res.isSuccess == false) diff --git a/integration/failure/root-module-compile-error/src/RootModuleCompileErrorTests.scala b/integration/failure/root-module-compile-error/src/RootModuleCompileErrorTests.scala index 97244a3f8e1..d84b9483a14 100644 --- a/integration/failure/root-module-compile-error/src/RootModuleCompileErrorTests.scala +++ b/integration/failure/root-module-compile-error/src/RootModuleCompileErrorTests.scala @@ -1,14 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object RootModuleCompileErrorTests extends IntegrationTestSuite { +object RootModuleCompileErrorTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test { + test - integrationTest { tester => + import tester._ val res = eval("foo.scalaVersion") assert(res.isSuccess == false) diff --git a/integration/failure/root-subfolder-module-collision/src/RootSubfolderModuleCollisionTests.scala b/integration/failure/root-subfolder-module-collision/src/RootSubfolderModuleCollisionTests.scala index c33cbf5ff72..2becba29e17 100644 --- a/integration/failure/root-subfolder-module-collision/src/RootSubfolderModuleCollisionTests.scala +++ b/integration/failure/root-subfolder-module-collision/src/RootSubfolderModuleCollisionTests.scala @@ -1,14 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object RootSubfolderModuleCollisionTests extends IntegrationTestSuite { +object RootSubfolderModuleCollisionTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("success") { + test("success") - integrationTest { tester => + import tester._ val res = eval(("resolve", "_")) assert(res.isSuccess == false) assert(res.err.contains("cannot override final member")) diff --git a/integration/failure/subfolder-helper-module-collision/src/SubfolderHelperModuleCollisionTests.scala b/integration/failure/subfolder-helper-module-collision/src/SubfolderHelperModuleCollisionTests.scala index d694aab1ed0..fde047ca517 100644 --- a/integration/failure/subfolder-helper-module-collision/src/SubfolderHelperModuleCollisionTests.scala +++ b/integration/failure/subfolder-helper-module-collision/src/SubfolderHelperModuleCollisionTests.scala @@ -1,14 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object SubfolderHelperModuleCollisionTests extends IntegrationTestSuite { +object SubfolderHelperModuleCollisionTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("success") { + test("success") - integrationTest { tester => + import tester._ val res = eval(("resolve", "_")) assert(res.isSuccess == false) // Not a great error message but it will have to do for now diff --git a/integration/failure/subfolder-missing-build-prefix/src/SubfolderMissingBuildPrefix.scala b/integration/failure/subfolder-missing-build-prefix/src/SubfolderMissingBuildPrefix.scala index 7711bbdc64d..02e744e219d 100644 --- a/integration/failure/subfolder-missing-build-prefix/src/SubfolderMissingBuildPrefix.scala +++ b/integration/failure/subfolder-missing-build-prefix/src/SubfolderMissingBuildPrefix.scala @@ -1,14 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object SubfolderMissingBuildPrefix extends IntegrationTestSuite { +object SubfolderMissingBuildPrefix extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("success") { + test("success") - integrationTest { tester => + import tester._ val res = eval(("resolve", "_")) assert(res.isSuccess == false) assert(res.err.contains("object y is not a member of package build_.sub")) diff --git a/integration/failure/things-outside-top-level-module/src/ThingsOutsideTopLevelModuleTests.scala b/integration/failure/things-outside-top-level-module/src/ThingsOutsideTopLevelModuleTests.scala index 564df09a65f..cf71ac33e5c 100644 --- a/integration/failure/things-outside-top-level-module/src/ThingsOutsideTopLevelModuleTests.scala +++ b/integration/failure/things-outside-top-level-module/src/ThingsOutsideTopLevelModuleTests.scala @@ -1,14 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object ThingsOutsideTopLevelModuleTests extends IntegrationTestSuite { +object ThingsOutsideTopLevelModuleTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("success") { + test("success") - integrationTest { tester => + import tester._ val res = eval(("resolve", "_")) assert(!res.isSuccess) assert( diff --git a/integration/feature/auxiliary-class-files/src/AuxiliaryClassFilesTests.scala b/integration/feature/auxiliary-class-files/src/AuxiliaryClassFilesTests.scala index 5551fa0f4e1..e481db1bff9 100644 --- a/integration/feature/auxiliary-class-files/src/AuxiliaryClassFilesTests.scala +++ b/integration/feature/auxiliary-class-files/src/AuxiliaryClassFilesTests.scala @@ -1,59 +1,60 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ // Regress test for issue https://github.com/com-lihaoyi/mill/issues/1901 -object AuxiliaryClassFilesTests extends IntegrationTestSuite { +object AuxiliaryClassFilesTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - test("tasty files are deleted together with companion class files") { - initWorkspace() - assert(eval("app.jvm.compile").isSuccess) + test("tasty files are deleted together with companion class files") - integrationTest { + tester => + import tester._ + assert(eval("app.jvm.compile").isSuccess) - val classes = workspacePath / "out" / "app" / "jvm" / "compile.dest" / "classes" - val firstRun = os.list(classes).map(_.last) + val classes = workspacePath / "out" / "app" / "jvm" / "compile.dest" / "classes" + val firstRun = os.list(classes).map(_.last) - os.remove(workspacePath / "app" / "src" / "foo.scala") + os.remove(workspacePath / "app" / "src" / "foo.scala") - assert(eval("app.jvm.compile").isSuccess) + assert(eval("app.jvm.compile").isSuccess) - val secondRun = os.list(classes).map(_.last) + val secondRun = os.list(classes).map(_.last) - assert(firstRun == Seq("foo$.class", "foo.class", "foo.tasty")) - assert(secondRun == Seq.empty) + assert(firstRun == Seq("foo$.class", "foo.class", "foo.tasty")) + assert(secondRun == Seq.empty) } - test("compilation fails when deleting a class used by other files") { - initWorkspace() + test("compilation fails when deleting a class used by other files") - integrationTest { + tester => + import tester._ + os.write(workspacePath / "app" / "src" / "bar.scala", "object bar { println(foo) }") + val firstRunSuccessful = eval("app.jvm.compile") + assert(firstRunSuccessful.isSuccess) - os.write(workspacePath / "app" / "src" / "bar.scala", "object bar { println(foo) }") - val firstRunSuccessful = eval("app.jvm.compile") - assert(firstRunSuccessful.isSuccess) + val classes = workspacePath / "out" / "app" / "jvm" / "compile.dest" / "classes" + val firstRun = os.list(classes).map(_.last) - val classes = workspacePath / "out" / "app" / "jvm" / "compile.dest" / "classes" - val firstRun = os.list(classes).map(_.last) + os.remove(workspacePath / "app" / "src" / "foo.scala") - os.remove(workspacePath / "app" / "src" / "foo.scala") + val secondRunSuccessful = eval("app.jvm.compile") + assert(!secondRunSuccessful.isSuccess) - val secondRunSuccessful = eval("app.jvm.compile") - assert(!secondRunSuccessful.isSuccess) + val secondRun = os.list(classes).map(_.last) - val secondRun = os.list(classes).map(_.last) - - assert(firstRun == Seq( - "bar$.class", - "bar.class", - "bar.tasty", - "foo$.class", - "foo.class", - "foo.tasty" - )) - assert(secondRun == Seq.empty) + assert(firstRun == Seq( + "bar$.class", + "bar.class", + "bar.tasty", + "foo$.class", + "foo.class", + "foo.tasty" + )) + assert(secondRun == Seq.empty) } - test("nir files are deleted together with companion class files") { - initWorkspace() + test("nir files are deleted together with companion class files") - integrationTest { tester => + import tester._ assert(eval("app.native.compile").isSuccess) val classes = workspacePath / "out" / "app" / "native" / "compile.dest" / "classes" @@ -69,21 +70,22 @@ object AuxiliaryClassFilesTests extends IntegrationTestSuite { assert(secondRun == Seq.empty) } - test("sjsir files are deleted together with companion class files") { - initWorkspace() - assert(eval("app.js.compile").isSuccess) + test("sjsir files are deleted together with companion class files") - integrationTest { + tester => + import tester._ + assert(eval("app.js.compile").isSuccess) - val classes = workspacePath / "out" / "app" / "js" / "compile.dest" / "classes" - val firstRun = os.list(classes).map(_.last) + val classes = workspacePath / "out" / "app" / "js" / "compile.dest" / "classes" + val firstRun = os.list(classes).map(_.last) - os.remove(workspacePath / "app" / "src" / "foo.scala") + os.remove(workspacePath / "app" / "src" / "foo.scala") - assert(eval("app.js.compile").isSuccess) + assert(eval("app.js.compile").isSuccess) - val secondRun = os.list(classes).map(_.last) + val secondRun = os.list(classes).map(_.last) - assert(firstRun == Seq("foo$.class", "foo$.sjsir", "foo.class", "foo.tasty")) - assert(secondRun == Seq.empty) + assert(firstRun == Seq("foo$.class", "foo$.sjsir", "foo.class", "foo.tasty")) + assert(secondRun == Seq.empty) } } } diff --git a/integration/feature/docannotations/src/DocAnnotationsTests.scala b/integration/feature/docannotations/src/DocAnnotationsTests.scala index 8c4948e75be..b23fdeb9b36 100644 --- a/integration/feature/docannotations/src/DocAnnotationsTests.scala +++ b/integration/feature/docannotations/src/DocAnnotationsTests.scala @@ -1,10 +1,10 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object DocAnnotationsTests extends IntegrationTestSuite { +object DocAnnotationsTests extends UtestIntegrationTestSuite { def globMatches(glob: String, input: String): Boolean = { StringContext .glob( @@ -16,8 +16,8 @@ object DocAnnotationsTests extends IntegrationTestSuite { } val tests: Tests = Tests { - initWorkspace() - test("test") - { + test("test") - integrationTest { tester => + import tester._ val res = eval(("inspect", "core.test.ivyDeps")) assert(res.isSuccess == true) diff --git a/integration/feature/hygiene/src/HygieneTests.scala b/integration/feature/hygiene/src/HygieneTests.scala index f805400b1ec..c6094f5f421 100644 --- a/integration/feature/hygiene/src/HygieneTests.scala +++ b/integration/feature/hygiene/src/HygieneTests.scala @@ -1,14 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object HygieneTests extends IntegrationTestSuite { +object HygieneTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test { + test - integrationTest { tester => + import tester._ val res = eval("scala.foo") assert(res.isSuccess == true) val output = out("scala.foo").text diff --git a/integration/feature/import-ivy-worker-invalidation/src/ImportIvyWorkerInvalidation.scala b/integration/feature/import-ivy-worker-invalidation/src/ImportIvyWorkerInvalidation.scala index 999015576c9..41c1ec01022 100644 --- a/integration/feature/import-ivy-worker-invalidation/src/ImportIvyWorkerInvalidation.scala +++ b/integration/feature/import-ivy-worker-invalidation/src/ImportIvyWorkerInvalidation.scala @@ -1,14 +1,14 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object ImportIvyWorkerInvalidation extends IntegrationTestSuite { +object ImportIvyWorkerInvalidation extends UtestIntegrationTestSuite { val tests: Tests = Tests { - test { - initWorkspace() + test - integrationTest { tester => + import tester._ assert(eval("app.compile").isSuccess) modifyFile( workspacePath / "build.mill", diff --git a/integration/feature/init/src/MillInitTests.scala b/integration/feature/init/src/MillInitTests.scala index c7558d05340..7305917fe21 100644 --- a/integration/feature/init/src/MillInitTests.scala +++ b/integration/feature/init/src/MillInitTests.scala @@ -1,13 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object MillInitTests extends IntegrationTestSuite { +object MillInitTests extends UtestIntegrationTestSuite { def tests: Tests = Tests { - test("Mill init works") { - initWorkspace() + test("Mill init works") - integrationTest { tester => + import tester._ eval(("init", "com-lihaoyi/mill-scala-hello.g8", "--name=example")).isSuccess ==> true val projFile = workspacePath / "example" / "build.sc" assert(os.exists(projFile)) diff --git a/integration/feature/large-project/src/LargeProjectTests.scala b/integration/feature/large-project/src/LargeProjectTests.scala index 7bc1585af7b..ad3bba0a3e1 100644 --- a/integration/feature/large-project/src/LargeProjectTests.scala +++ b/integration/feature/large-project/src/LargeProjectTests.scala @@ -1,13 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object LargeProjectTests extends IntegrationTestSuite { +object LargeProjectTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - test("test") - { + test("test") - integrationTest { tester => + import tester._ assert(eval("foo.common.one.compile").isSuccess) } diff --git a/integration/feature/mill-jvm-opts/src/MillJvmOptsTests.scala b/integration/feature/mill-jvm-opts/src/MillJvmOptsTests.scala index b3379e10eb4..4391a7d1322 100644 --- a/integration/feature/mill-jvm-opts/src/MillJvmOptsTests.scala +++ b/integration/feature/mill-jvm-opts/src/MillJvmOptsTests.scala @@ -1,13 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object MillJvmOptsTests extends IntegrationTestSuite { +object MillJvmOptsTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - test("JVM options from file .mill-jvm-opts are properly read") { + test("JVM options from file .mill-jvm-opts are properly read") - integrationTest { tester => + import tester._ assert(eval("checkJvmOpts").isSuccess) } } diff --git a/integration/feature/non-identifier-import/src/NonIdentifierImport.scala b/integration/feature/non-identifier-import/src/NonIdentifierImport.scala index 29fd1117160..daec95905ab 100644 --- a/integration/feature/non-identifier-import/src/NonIdentifierImport.scala +++ b/integration/feature/non-identifier-import/src/NonIdentifierImport.scala @@ -1,13 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object NonIdentifierImport extends IntegrationTestSuite { +object NonIdentifierImport extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - test("test") - { + test("test") - integrationTest { tester => + import tester._ assert(eval("foo-bar-module.compile").isSuccess) } } diff --git a/integration/feature/plugin-classpath/src/MillPluginClasspathTest.scala b/integration/feature/plugin-classpath/src/MillPluginClasspathTest.scala index c7062ff0a35..e6589c451a3 100644 --- a/integration/feature/plugin-classpath/src/MillPluginClasspathTest.scala +++ b/integration/feature/plugin-classpath/src/MillPluginClasspathTest.scala @@ -1,11 +1,10 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object MillPluginClasspathTest extends IntegrationTestSuite { - initWorkspace() +object MillPluginClasspathTest extends UtestIntegrationTestSuite { val embeddedModules: Seq[(String, String)] = Seq( ("com.lihaoyi", "mill-main-client"), @@ -31,37 +30,44 @@ object MillPluginClasspathTest extends IntegrationTestSuite { ) val tests: Tests = Tests { - test("exclusions") - { - val res1 = eval(("--meta-level", "1", "resolveDepsExclusions")) - assert(res1.isSuccess) - val exclusions = out("mill-build.resolveDepsExclusions").value[Seq[(String, String)]] - val expectedExclusions = embeddedModules + test("exclusions") - integrationTest { tester => + import tester._ + retry(3) { + val res1 = eval(("--meta-level", "1", "resolveDepsExclusions")) + assert(res1.isSuccess) - val diff = expectedExclusions.toSet.diff(exclusions.toSet) - assert(diff.isEmpty) + val exclusions = out("mill-build.resolveDepsExclusions").value[Seq[(String, String)]] + val expectedExclusions = embeddedModules + val diff = expectedExclusions.toSet.diff(exclusions.toSet) + assert(diff.isEmpty) + } } - test("runClasspath") - { - // We expect Mill core transitive dependencies to be filtered out - val res1 = eval(("--meta-level", "1", "runClasspath")) - assert(res1.isSuccess) + test("runClasspath") - integrationTest { tester => + import tester._ + retry(3) { + // We expect Mill core transitive dependencies to be filtered out + val res1 = eval(("--meta-level", "1", "runClasspath")) + assert(res1.isSuccess) - val runClasspath = out("mill-build.runClasspath").value[Seq[String]] + val runClasspath = out("mill-build.runClasspath").value[Seq[String]] - val unexpectedArtifacts = embeddedModules.map { - case (o, n) => s"${o.replaceAll("[.]", "/")}/${n}" - } + val unexpectedArtifacts = embeddedModules.map { + case (o, n) => s"${o.replaceAll("[.]", "/")}/${n}" + } - val unexpected = unexpectedArtifacts.flatMap { a => - runClasspath.find(p => p.toString.contains(a)).map((a, _)) - }.toMap - assert(unexpected.isEmpty) + val unexpected = unexpectedArtifacts.flatMap { a => + runClasspath.find(p => p.toString.contains(a)).map((a, _)) + }.toMap + assert(unexpected.isEmpty) - val expected = Seq("com/disneystreaming/smithy4s/smithy4s-mill-codegen-plugin_mill0.11_2.13") - assert(expected.forall(a => - runClasspath.exists(p => p.toString().replace('\\', '/').contains(a)) - )) + val expected = + Seq("com/disneystreaming/smithy4s/smithy4s-mill-codegen-plugin_mill0.11_2.13") + assert(expected.forall(a => + runClasspath.exists(p => p.toString().replace('\\', '/').contains(a)) + )) + } } } diff --git a/integration/feature/private-methods/src/PrivateMethodsTests.scala b/integration/feature/private-methods/src/PrivateMethodsTests.scala index 122e444bfed..51e1ff35374 100644 --- a/integration/feature/private-methods/src/PrivateMethodsTests.scala +++ b/integration/feature/private-methods/src/PrivateMethodsTests.scala @@ -1,13 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object PrivateMethodsTests extends IntegrationTestSuite { +object PrivateMethodsTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - test("simple") { + test("simple") - integrationTest { tester => + import tester._ // Simple public target depending on private target works val pub = eval(("show", "pub")) assert(pub.out == "\"priv\"") diff --git a/integration/feature/repo-config-via-import/src/ImportRepoTests.scala b/integration/feature/repo-config-via-import/src/ImportRepoTests.scala index 9a3d233a26c..99b89760b29 100644 --- a/integration/feature/repo-config-via-import/src/ImportRepoTests.scala +++ b/integration/feature/repo-config-via-import/src/ImportRepoTests.scala @@ -1,13 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object ImportRepoTests extends IntegrationTestSuite { +object ImportRepoTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - test("test") - { + test("test") - integrationTest { tester => + import tester._ // Make sure, we propery parse a line: // ``` // import $repo.`file:///tmp/testrepo` diff --git a/integration/feature/scoverage/src/ScoverageTests.scala b/integration/feature/scoverage/src/ScoverageTests.scala index 8b2497e0414..75c78521f9c 100644 --- a/integration/feature/scoverage/src/ScoverageTests.scala +++ b/integration/feature/scoverage/src/ScoverageTests.scala @@ -1,13 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object ScoverageTests extends IntegrationTestSuite { +object ScoverageTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - test("test") - { + test("test") - integrationTest { tester => + import tester._ assert(eval("__.compile").isSuccess) assert(eval("core[2.13.11].scoverage.xmlReport").isSuccess) } diff --git a/integration/feature/subprocess-stdout/src/SubprocessStdoutTests.scala b/integration/feature/subprocess-stdout/src/SubprocessStdoutTests.scala index cbe745ef2b2..bfdb7219ef4 100644 --- a/integration/feature/subprocess-stdout/src/SubprocessStdoutTests.scala +++ b/integration/feature/subprocess-stdout/src/SubprocessStdoutTests.scala @@ -1,14 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object SubprocessStdoutTests extends IntegrationTestSuite { +object SubprocessStdoutTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test { + test - integrationTest { tester => + import tester._ val res1 = eval("inheritInterleaved", mergeErrIntoOut = true).out // Make sure that when a lot of printed/inherited stdout/stderr is printed // in quick succession, the output ordering is preserved and it doesn't get @@ -62,7 +61,7 @@ object SubprocessStdoutTests extends IntegrationTestSuite { // up in the console somewhere and not disappear // val res2 = eval("inheritRaw", mergeErrIntoOut = true).out - if (!clientServerMode) { + if (!tester.clientServerMode) { // For `fork` tests, which represent `-i`/`--interactive`/`--no-server`, the output should // be properly ordered since it all comes directly from the stdout/stderr of the same process assert( diff --git a/integration/ide/bloop/src/BloopTests.scala b/integration/ide/bloop/src/BloopTests.scala index fe4e7afb5b6..6b342f93453 100644 --- a/integration/ide/bloop/src/BloopTests.scala +++ b/integration/ide/bloop/src/BloopTests.scala @@ -1,28 +1,34 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object BloopTests extends IntegrationTestSuite { - initWorkspace() - - val installResult: Boolean = eval("mill.contrib.bloop.Bloop/install").isSuccess +object BloopTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { test("test") - { - assert(installResult) - test("root module bloop config should be created") { + test("root module bloop config should be created") - integrationTest { tester => + import tester._ + val installResult: Boolean = eval("mill.contrib.bloop.Bloop/install").isSuccess + assert(installResult) assert(os.exists(workspacePath / ".bloop" / "root-module.json")) } - val millBuildJsonFile = workspacePath / ".bloop" / "mill-build-.json" - test("mill-build module bloop config should be created") { + test("mill-build module bloop config should be created") - integrationTest { tester => + import tester._ + val installResult: Boolean = eval("mill.contrib.bloop.Bloop/install").isSuccess + val millBuildJsonFile = workspacePath / ".bloop" / "mill-build-.json" + assert(installResult) assert(os.exists(millBuildJsonFile)) } - val config = ujson.read(os.read.stream(millBuildJsonFile)) - test("mill-build config should contain build.mill source") { + test("mill-build config should contain build.mill source") - integrationTest { tester => + import tester._ + val millBuildJsonFile = workspacePath / ".bloop" / "mill-build-.json" + val installResult: Boolean = eval("mill.contrib.bloop.Bloop/install").isSuccess + val config = ujson.read(os.read.stream(millBuildJsonFile)) + assert(installResult) assert(config("project")("sources").arr.exists(path => os.Path(path.str).last == "build.mill" )) diff --git a/integration/ide/bsp-install/src/BspInstallDebugTests.scala b/integration/ide/bsp-install/src/BspInstallDebugTests.scala index 543c2f6929d..2294dab3571 100644 --- a/integration/ide/bsp-install/src/BspInstallDebugTests.scala +++ b/integration/ide/bsp-install/src/BspInstallDebugTests.scala @@ -1,18 +1,18 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import mill.bsp.Constants import utest._ -object BspInstallDebugTests extends IntegrationTestSuite { +object BspInstallDebugTests extends UtestIntegrationTestSuite { val bsp4jVersion: String = sys.props.getOrElse("BSP4J_VERSION", ???) // we purposely enable debugging in this simulated test env override val debugLog: Boolean = true def tests: Tests = Tests { - test("BSP install forwards --debug option to server") { - initWorkspace() + test("BSP install forwards --debug option to server") - integrationTest { tester => + import tester._ eval("mill.bsp.BSP/install").isSuccess ==> true val jsonFile = workspacePath / Constants.bspDir / s"${Constants.serverName}.json" assert(os.exists(jsonFile)) diff --git a/integration/ide/bsp-install/src/BspInstallTests.scala b/integration/ide/bsp-install/src/BspInstallTests.scala index afeea1d53a1..40f9547da61 100644 --- a/integration/ide/bsp-install/src/BspInstallTests.scala +++ b/integration/ide/bsp-install/src/BspInstallTests.scala @@ -1,15 +1,15 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import mill.bsp.Constants import utest._ -object BspInstallTests extends IntegrationTestSuite { +object BspInstallTests extends UtestIntegrationTestSuite { val bsp4jVersion: String = sys.props.getOrElse("BSP4J_VERSION", ???) def tests: Tests = Tests { - test("BSP install") { - initWorkspace() + test("BSP install") - integrationTest { tester => + import tester._ assert(eval("mill.bsp.BSP/install").isSuccess) val jsonFile = workspacePath / Constants.bspDir / s"${Constants.serverName}.json" assert(os.exists(jsonFile)) diff --git a/integration/ide/bsp-modules/src/BspModulesTests.scala b/integration/ide/bsp-modules/src/BspModulesTests.scala index 8ffb3d10b28..cadc7e31e98 100644 --- a/integration/ide/bsp-modules/src/BspModulesTests.scala +++ b/integration/ide/bsp-modules/src/BspModulesTests.scala @@ -1,21 +1,21 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import mill.bsp.Constants import utest._ -object BspModulesTests extends IntegrationTestSuite { +object BspModulesTests extends UtestIntegrationTestSuite { val bsp4jVersion: String = sys.props.getOrElse("BSP4J_VERSION", ???) def tests: Tests = Tests { test("BSP module with foreign modules") { - test("can be installed") { - initWorkspace() + test("can be installed") - integrationTest { tester => + import tester._ assert(eval("mill.bsp.BSP/install").isSuccess) os.exists(workspacePath / Constants.bspDir / s"${Constants.serverName}.json") ==> true } - test("ModuleUtils resolves all referenced transitive modules") { - initWorkspace() + test("ModuleUtils resolves all referenced transitive modules") - integrationTest { tester => + import tester._ val res = eval("validate") assert(res.isSuccess) val file = workspacePath / "out" / "validate.dest" / "transitive-modules.json" diff --git a/integration/ide/gen-idea/src/GenIdeaExtendedTests.scala b/integration/ide/gen-idea/src/GenIdeaExtendedTests.scala index f60401bd313..ec4d78de0b3 100644 --- a/integration/ide/gen-idea/src/GenIdeaExtendedTests.scala +++ b/integration/ide/gen-idea/src/GenIdeaExtendedTests.scala @@ -1,18 +1,18 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ import scala.util.Try import os.Path -object GenIdeaExtendedTests extends IntegrationTestSuite { +object GenIdeaExtendedTests extends UtestIntegrationTestSuite { override def workspaceSourcePath: Path = super.workspaceSourcePath / "extended" def tests: Tests = Tests { - test("genIdeaTests") { - initWorkspace() + test("genIdeaTests") - integrationTest { tester => + import tester._ val expectedBase = workspacePath / "idea" val resources = os.walk(expectedBase).filter(os.isFile).map(_.subRelativeTo(expectedBase)) diff --git a/integration/ide/gen-idea/src/GenIdeaTests.scala b/integration/ide/gen-idea/src/GenIdeaTests.scala index 913b3903995..fa128c045ad 100644 --- a/integration/ide/gen-idea/src/GenIdeaTests.scala +++ b/integration/ide/gen-idea/src/GenIdeaTests.scala @@ -3,16 +3,16 @@ package mill.integration import utest.{Tests, assert, _} import scala.util.Try -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import GenIdeaUtils._ import os.Path -object GenIdeaTests extends IntegrationTestSuite { +object GenIdeaTests extends UtestIntegrationTestSuite { override def workspaceSourcePath: Path = super.workspaceSourcePath / "hello-idea" def tests: Tests = Tests { - test("helper assertPartialContentMatches works") { + test("helper assertPartialContentMatches works") - integrationTest { tester => val testContent = s"""line 1 |line 2 @@ -51,8 +51,8 @@ object GenIdeaTests extends IntegrationTestSuite { () } - test("genIdeaTests") { - initWorkspace() + test("genIdeaTests") - integrationTest { tester => + import tester._ val expectedBase = workspacePath / "idea" val resources = os.walk(expectedBase).filter(os.isFile).map(_.subRelativeTo(expectedBase)) diff --git a/integration/invalidation/codesig-hello/src/CodeSigHelloTests.scala b/integration/invalidation/codesig-hello/src/CodeSigHelloTests.scala index 25066443a7d..38ff50c4a79 100644 --- a/integration/invalidation/codesig-hello/src/CodeSigHelloTests.scala +++ b/integration/invalidation/codesig-hello/src/CodeSigHelloTests.scala @@ -1,13 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object CodeSigHelloTests extends IntegrationTestSuite { +object CodeSigHelloTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - test("simple") { + test("simple") - integrationTest { tester => + import tester._ // Make sure the simplest case where we have a single target calling a single helper // method is properly invalidated when either the target body, or the helper method's body // is changed, or something changed in the constructor diff --git a/integration/invalidation/codesig-nested/src/CodeSigNestedTests.scala b/integration/invalidation/codesig-nested/src/CodeSigNestedTests.scala index 04a72f94e7b..3e973af3cb8 100644 --- a/integration/invalidation/codesig-nested/src/CodeSigNestedTests.scala +++ b/integration/invalidation/codesig-nested/src/CodeSigNestedTests.scala @@ -1,14 +1,13 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object CodeSigNestedTests extends IntegrationTestSuite { +object CodeSigNestedTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - - test("nested") { + test("nested") - integrationTest { tester => + import tester._ // Make sure the code-change invalidation works in more complex cases: multi-step // target graphs, targets inside module objects, targets inside module traits @@ -190,7 +189,8 @@ object CodeSigNestedTests extends IntegrationTestSuite { assert(addedNewlinesInsideCurlies.out == "") } - test("trait") { + test("trait") - integrationTest { tester => + import tester._ val initial = eval("traitOuter.traitInner.inner") assert( initial.out.linesIterator.toSet == Set( diff --git a/integration/invalidation/codesig-scalamodule/src/CodeSigScalaModuleTests.scala b/integration/invalidation/codesig-scalamodule/src/CodeSigScalaModuleTests.scala index a14ef8d9603..05e8c68a3dd 100644 --- a/integration/invalidation/codesig-scalamodule/src/CodeSigScalaModuleTests.scala +++ b/integration/invalidation/codesig-scalamodule/src/CodeSigScalaModuleTests.scala @@ -1,16 +1,16 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ -object CodeSigScalaModuleTests extends IntegrationTestSuite { +object CodeSigScalaModuleTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { def filterLines(out: String) = { out.linesIterator.filter(!_.contains("[info]")).toSet } - initWorkspace() - test("single") { + test("single") - integrationTest { tester => + import tester._ // Tests for fine-grained method-based invalidation within a single ScalaModule // Check normal behavior for initial run and subsequent fully-cached run @@ -120,7 +120,8 @@ object CodeSigScalaModuleTests extends IntegrationTestSuite { ) } - test("multiple") { + test("multiple") - integrationTest { tester => + import tester._ // Tests for fine-grained method-based invalidation between multiple ScalaModules, // some related and some not diff --git a/integration/invalidation/invalidation/src/ScriptsInvalidationTests.scala b/integration/invalidation/invalidation/src/ScriptsInvalidationTests.scala index ee3d42890d0..895ec42b096 100644 --- a/integration/invalidation/invalidation/src/ScriptsInvalidationTests.scala +++ b/integration/invalidation/invalidation/src/ScriptsInvalidationTests.scala @@ -1,118 +1,108 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.{UtestIntegrationTestSuite, IntegrationTester} import utest._ -object ScriptsInvalidationTests extends IntegrationTestSuite { +object ScriptsInvalidationTests extends UtestIntegrationTestSuite { - def runTask(task: String): Set[String] = { - val res = eval(task) + def runTask(tester: IntegrationTester, task: String): Set[String] = { + val res = tester.eval(task) assert(res.isSuccess) res.out.linesIterator.map(_.trim).toSet } val tests: Tests = Tests { - test("should not invalidate tasks in different untouched sc files") { - test("first run") { - initWorkspace() - - val result = runTask("task") + test("should not invalidate tasks in different untouched sc files") - integrationTest { + tester => + import tester._ + // first run + val result = runTask(tester, "task") val expected = Set("a", "d", "b", "c") assert(result == expected) - } - test("second run modifying script") { + // second run modifying script modifyFile( workspacePath / "build.mill", _.replace("""println("task")""", """System.out.println("task2")""") ) - val stdout = runTask("task") + val stdout = runTask(tester, "task") assert(stdout.isEmpty) - } } - test("should invalidate tasks if leaf file is changed") { - test("first run") { - initWorkspace() - val result = runTask("task") - val expected = Set("a", "d", "b", "c") + test("should invalidate tasks if leaf file is changed") - integrationTest { tester => + import tester._ + // first run - assert(result == expected) - } + val result = runTask(tester, "task") + val expected = Set("a", "d", "b", "c") - test("second run modifying script") { - modifyFile( - workspacePath / "b" / "inputD.mill", - _.replace("""println("d")""", """System.out.println("d2")""") - ) + assert(result == expected) - val result = runTask("task") - val expected = Set("d2", "b") + // second run modifying script + modifyFile( + workspacePath / "b" / "inputD.mill", + _.replace("""println("d")""", """System.out.println("d2")""") + ) - assert(result == expected) - } - } - test("should handle submodules in scripts") { - test("first run") { - initWorkspace() + val result2 = runTask(tester, "task") + val expected2 = Set("d2", "b") - val result = runTask("module.task") - val expected = Set("a", "d", "b", "c", "task") + assert(result2 == expected2) - assert(result == expected) - } + } + test("should handle submodules in scripts") - integrationTest { tester => + import tester._ + // first run + val result = runTask(tester, "module.task") + val expected = Set("a", "d", "b", "c", "task") - test("second run modifying script") { - modifyFile( - workspacePath / "build.mill", - _.replace("""println("task")""", """System.out.println("task2")""") - ) + assert(result == expected) - val result = runTask("module.task") - val expected = Set("task2") + // second run modifying script + modifyFile( + workspacePath / "build.mill", + _.replace("""println("task")""", """System.out.println("task2")""") + ) - assert(result == expected) - } - } - test("should handle ammonite ^ imports") { - test("first run") { - initWorkspace() + val result2 = runTask(tester, "module.task") + val expected2 = Set("task2") - val result = runTask("taskE") + assert(result2 == expected2) + } + test("should handle ammonite ^ imports") - integrationTest { tester => + import tester._ + retry(3) { + // first run + val result = runTask(tester, "taskE") val expected = Set("a", "e", "taskE") assert(result == expected) - } - test("second run modifying script") { + // second run modifying script modifyFile( workspacePath / "build.mill", _.replace("""println("taskE")""", """System.out.println("taskE2")""") ) - val result = runTask("taskE") - val expected = Set("taskE2") + val result2 = runTask(tester, "taskE") + val expected2 = Set("taskE2") - assert(result == expected) + assert(result2 == expected2) } } - test("should handle ammonite paths with symbols") { - initWorkspace() - - val result = runTask("taskSymbols") + test("should handle ammonite paths with symbols") - integrationTest { tester => + val result = runTask(tester, "taskSymbols") val expected = Set("taskSymbols") assert(result == expected) } - test("should handle ammonite files with symbols") { - initWorkspace() - - val result = runTask("taskSymbolsInFile") + test("should handle ammonite files with symbols") - integrationTest { tester => + val result = runTask(tester, "taskSymbolsInFile") val expected = Set("taskSymbolsInFile") assert(result == expected) diff --git a/integration/invalidation/multi-level-editing/src/MultiLevelBuildTests.scala b/integration/invalidation/multi-level-editing/src/MultiLevelBuildTests.scala index 53c3628e0b3..954223c7068 100644 --- a/integration/invalidation/multi-level-editing/src/MultiLevelBuildTests.scala +++ b/integration/invalidation/multi-level-editing/src/MultiLevelBuildTests.scala @@ -1,6 +1,6 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.{UtestIntegrationTestSuite, IntegrationTester} import mill.main.client.OutFiles._ import mill.runner.RunnerState @@ -13,45 +13,44 @@ import scala.util.matching.Regex // that the proper messages are reported, proper build classloaders are // re-used or invalidated, and the proper files end up getting watched // in all cases. -object MultiLevelBuildTests extends IntegrationTestSuite { +object MultiLevelBuildTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - def runAssertSuccess(expected: String) = { - val res = eval("foo.run") + def runAssertSuccess(tester: IntegrationTester, expected: String) = { + val res = tester.eval("foo.run") assert(res.isSuccess == true) assert(res.out.contains(expected)) } - val fooPaths = Seq( - workspacePath / "foo" / "compile-resources", - workspacePath / "foo" / "resources", - workspacePath / "foo" / "src" + def fooPaths(tester: IntegrationTester) = Seq( + tester.workspacePath / "foo" / "compile-resources", + tester.workspacePath / "foo" / "resources", + tester.workspacePath / "foo" / "src" ) - val buildPaths = Seq( - workspacePath / "build.mill", - workspacePath / "mill-build" / "compile-resources", - workspacePath / "mill-build" / "resources", - workspacePath / "mill-build" / "src" + def buildPaths(tester: IntegrationTester) = Seq( + tester.workspacePath / "build.mill", + tester.workspacePath / "mill-build" / "compile-resources", + tester.workspacePath / "mill-build" / "resources", + tester.workspacePath / "mill-build" / "src" ) - val buildPaths2 = Seq( - workspacePath / "mill-build" / "build.mill", - workspacePath / "mill-build" / "mill-build" / "compile-resources", - workspacePath / "mill-build" / "mill-build" / "resources", - workspacePath / "mill-build" / "mill-build" / "src" + def buildPaths2(tester: IntegrationTester) = Seq( + tester.workspacePath / "mill-build" / "build.mill", + tester.workspacePath / "mill-build" / "mill-build" / "compile-resources", + tester.workspacePath / "mill-build" / "mill-build" / "resources", + tester.workspacePath / "mill-build" / "mill-build" / "src" ) - val buildPaths3 = Seq( - workspacePath / "mill-build" / "mill-build" / "build.mill", - workspacePath / "mill-build" / "mill-build" / "mill-build" / "compile-resources", - workspacePath / "mill-build" / "mill-build" / "mill-build" / "resources", - workspacePath / "mill-build" / "mill-build" / "mill-build" / "src" + def buildPaths3(tester: IntegrationTester) = Seq( + tester.workspacePath / "mill-build" / "mill-build" / "build.mill", + tester.workspacePath / "mill-build" / "mill-build" / "mill-build" / "compile-resources", + tester.workspacePath / "mill-build" / "mill-build" / "mill-build" / "resources", + tester.workspacePath / "mill-build" / "mill-build" / "mill-build" / "src" ) - def loadFrames(n: Int) = { + def loadFrames(tester: IntegrationTester, n: Int) = { for (depth <- Range(0, n)) yield { val path = - workspacePath / "out" / Seq.fill(depth)(millBuild) / millRunnerState + tester.workspacePath / "out" / Seq.fill(depth)(millBuild) / millRunnerState if (os.exists(path)) upickle.default.read[RunnerState.Frame.Logged](os.read(path)) -> path else RunnerState.Frame.Logged(Map(), Seq(), Seq(), None, Seq(), 0) -> path } @@ -61,12 +60,14 @@ object MultiLevelBuildTests extends IntegrationTestSuite { * Verify that each level of the multi-level build ends upcausing the * appropriate files to get watched */ - def checkWatchedFiles(expected0: Seq[os.Path]*) = { - for ((expectedWatched0, (frame, path)) <- expected0.zip(loadFrames(expected0.length))) { + def checkWatchedFiles(tester: IntegrationTester, expected0: Seq[os.Path]*) = { + for ( + (expectedWatched0, (frame, path)) <- expected0.zip(loadFrames(tester, expected0.length)) + ) { val frameWatched = frame .evalWatched .map(_.path) - .filter(_.startsWith(workspacePath)) + .filter(_.startsWith(tester.workspacePath)) .filter(!_.segments.contains("mill-launcher")) .sorted @@ -75,14 +76,14 @@ object MultiLevelBuildTests extends IntegrationTestSuite { } } - def evalCheckErr(expectedSnippets: String*) = { + def evalCheckErr(tester: IntegrationTester, expectedSnippets: String*) = { // Wipe out stale state files to make sure they don't get picked up when // Mill aborts early and fails to generate a new one - os.walk(workspacePath / "out").filter(_.last == "mill-runner-state.json").foreach( + os.walk(tester.workspacePath / "out").filter(_.last == "mill-runner-state.json").foreach( os.remove(_) ) - val res = eval("foo.run") + val res = tester.eval("foo.run") assert(res.isSuccess == false) // Prepend a "\n" to allow callsites to use "\n" to test for start of // line, even though the first line doesn't have a "\n" at the start @@ -100,9 +101,12 @@ object MultiLevelBuildTests extends IntegrationTestSuite { * the previous classloader was re-used, `null` means there is no * classloader at that level */ - def checkChangedClassloaders(expectedChanged0: java.lang.Boolean*) = { + def checkChangedClassloaders( + tester: IntegrationTester, + expectedChanged0: java.lang.Boolean* + ) = { val currentClassLoaderIds = - for ((frame, path) <- loadFrames(expectedChanged0.length)) + for ((frame, path) <- loadFrames(tester, expectedChanged0.length)) yield frame.classLoaderIdentity val changed = currentClassLoaderIds @@ -124,197 +128,273 @@ object MultiLevelBuildTests extends IntegrationTestSuite { savedClassLoaderIds = currentClassLoaderIds } - test("validEdits") { - runAssertSuccess("

hello

world

0.8.2

!") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) + test("validEdits") - integrationTest { tester => + import tester._ + runAssertSuccess(tester, "

hello

world

0.8.2

!") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) // First run all classloaders are new, except level 0 running user code // which doesn't need generate a classloader which never changes - checkChangedClassloaders(null, true, true, true) + checkChangedClassloaders(tester, null, true, true, true) modifyFile(workspacePath / "foo" / "src" / "Example.scala", _.replace("!", "?")) - runAssertSuccess("

hello

world

0.8.2

?") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) + runAssertSuccess(tester, "

hello

world

0.8.2

?") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) // Second run with no build changes, all classloaders are unchanged - checkChangedClassloaders(null, false, false, false) + checkChangedClassloaders(tester, null, false, false, false) modifyFile(workspacePath / "build.mill", _.replace("hello", "HELLO")) - runAssertSuccess("

HELLO

world

0.8.2

?") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, true, false, false) + runAssertSuccess(tester, "

HELLO

world

0.8.2

?") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) + checkChangedClassloaders(tester, null, true, false, false) modifyFile( workspacePath / "mill-build" / "build.mill", _.replace("def scalatagsVersion = ", "def scalatagsVersion = \"changed-\" + ") ) - runAssertSuccess("

HELLO

world

changed-0.8.2

?") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, true, true, false) + runAssertSuccess(tester, "

HELLO

world

changed-0.8.2

?") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) + checkChangedClassloaders(tester, null, true, true, false) modifyFile( workspacePath / "mill-build" / "mill-build" / "build.mill", _.replace("0.8.2", "0.12.0") ) - runAssertSuccess("

HELLO

world

changed-0.12.0

?") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, true, true, true) + runAssertSuccess(tester, "

HELLO

world

changed-0.12.0

?") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) + checkChangedClassloaders(tester, null, true, true, true) modifyFile( workspacePath / "mill-build" / "mill-build" / "build.mill", _.replace("0.12.0", "0.8.2") ) - runAssertSuccess("

HELLO

world

changed-0.8.2

?") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, true, true, true) + runAssertSuccess(tester, "

HELLO

world

changed-0.8.2

?") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) + checkChangedClassloaders(tester, null, true, true, true) modifyFile( workspacePath / "mill-build" / "build.mill", _.replace("def scalatagsVersion = \"changed-\" + ", "def scalatagsVersion = ") ) - runAssertSuccess("

HELLO

world

0.8.2

?") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, true, true, false) + runAssertSuccess(tester, "

HELLO

world

0.8.2

?") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) + checkChangedClassloaders(tester, null, true, true, false) modifyFile(workspacePath / "build.mill", _.replace("HELLO", "hello")) - runAssertSuccess("

hello

world

0.8.2

?") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, true, false, false) + runAssertSuccess(tester, "

hello

world

0.8.2

?") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) + checkChangedClassloaders(tester, null, true, false, false) modifyFile(workspacePath / "foo" / "src" / "Example.scala", _.replace("?", "!")) - runAssertSuccess("

hello

world

0.8.2

!") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, false, false, false) + runAssertSuccess(tester, "

hello

world

0.8.2

!") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) + checkChangedClassloaders(tester, null, false, false, false) } - test("parseErrorEdits") { + test("parseErrorEdits") - integrationTest { tester => + import tester._ def causeParseError(p: os.Path) = modifyFile(p, _.replace("extends", "extendx")) def fixParseError(p: os.Path) = modifyFile(p, _.replace("extendx", "extends")) - runAssertSuccess("

hello

world

0.8.2

!") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, true, true, true) + runAssertSuccess(tester, "

hello

world

0.8.2

!") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) + checkChangedClassloaders(tester, null, true, true, true) causeParseError(workspacePath / "build.mill") - evalCheckErr( - "\n1 targets failed", - "\ngenerateScriptSources build.mill" - ) - checkWatchedFiles(Nil, buildPaths, Nil, Nil) + evalCheckErr(tester, "\n1 targets failed", "\ngenerateScriptSources build.mill") + checkWatchedFiles(tester, Nil, buildPaths(tester), Nil, Nil) // When one of the meta-builds still has parse errors, all classloaders // remain null, because none of the meta-builds can evaluate. Only once // all of them parse successfully do we get a new set of classloaders for // every level of the meta-build - checkChangedClassloaders(null, null, null, null) + checkChangedClassloaders(tester, null, null, null, null) fixParseError(workspacePath / "build.mill") causeParseError(workspacePath / "mill-build" / "build.mill") - evalCheckErr( - "\n1 targets failed", - "\ngenerateScriptSources mill-build/build.mill" - ) - checkWatchedFiles(Nil, Nil, buildPaths2, Nil) - checkChangedClassloaders(null, null, null, null) + evalCheckErr(tester, "\n1 targets failed", "\ngenerateScriptSources mill-build/build.mill") + checkWatchedFiles(tester, Nil, Nil, buildPaths2(tester), Nil) + checkChangedClassloaders(tester, null, null, null, null) fixParseError(workspacePath / "mill-build" / "build.mill") causeParseError(workspacePath / "mill-build" / "mill-build" / "build.mill") evalCheckErr( + tester, "\n1 targets failed", "\ngenerateScriptSources mill-build/mill-build/build.mill" ) - checkWatchedFiles(Nil, Nil, Nil, buildPaths3) - checkChangedClassloaders(null, null, null, null) + checkWatchedFiles(tester, Nil, Nil, Nil, buildPaths3(tester)) + checkChangedClassloaders(tester, null, null, null, null) fixParseError(workspacePath / "mill-build" / "mill-build" / "build.mill") causeParseError(workspacePath / "mill-build" / "build.mill") - evalCheckErr( - "\n1 targets failed", - "\ngenerateScriptSources mill-build/build.mill" - ) - checkWatchedFiles(Nil, Nil, buildPaths2, Nil) - checkChangedClassloaders(null, null, null, null) + evalCheckErr(tester, "\n1 targets failed", "\ngenerateScriptSources mill-build/build.mill") + checkWatchedFiles(tester, Nil, Nil, buildPaths2(tester), Nil) + checkChangedClassloaders(tester, null, null, null, null) fixParseError(workspacePath / "mill-build" / "build.mill") causeParseError(workspacePath / "build.mill") - evalCheckErr( - "\n1 targets failed", - "\ngenerateScriptSources build.mill" - ) - checkWatchedFiles(Nil, buildPaths, Nil, Nil) - checkChangedClassloaders(null, null, null, null) + evalCheckErr(tester, "\n1 targets failed", "\ngenerateScriptSources build.mill") + checkWatchedFiles(tester, Nil, buildPaths(tester), Nil, Nil) + checkChangedClassloaders(tester, null, null, null, null) fixParseError(workspacePath / "build.mill") - runAssertSuccess("

hello

world

0.8.2

!") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, true, true, true) + runAssertSuccess(tester, "

hello

world

0.8.2

!") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) + checkChangedClassloaders(tester, null, true, true, true) } - test("compileErrorEdits") { + test("compileErrorEdits") - integrationTest { tester => + import tester._ def causeCompileError(p: os.Path) = modifyFile(p, _ + "\nimport doesnt.exist") def fixCompileError(p: os.Path) = modifyFile(p, _.replace("import doesnt.exist", "")) - runAssertSuccess("

hello

world

0.8.2

!") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, true, true, true) + runAssertSuccess(tester, "

hello

world

0.8.2

!") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) + checkChangedClassloaders(tester, null, true, true, true) causeCompileError(workspacePath / "build.mill") evalCheckErr( + tester, "\n1 targets failed", // Ensure the file path in the compile error is properly adjusted to point // at the original source file and not the generated file (workspacePath / "build.mill").toString, "not found: value doesnt" ) - checkWatchedFiles(Nil, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, null, false, false) + checkWatchedFiles(tester, Nil, buildPaths(tester), buildPaths2(tester), buildPaths3(tester)) + checkChangedClassloaders(tester, null, null, false, false) causeCompileError(workspacePath / "mill-build" / "build.mill") evalCheckErr( + tester, "\n1 targets failed", (workspacePath / "mill-build" / "build.mill").toString, "not found: object doesnt" ) - checkWatchedFiles(Nil, Nil, buildPaths2, buildPaths3) - checkChangedClassloaders(null, null, null, false) + checkWatchedFiles(tester, Nil, Nil, buildPaths2(tester), buildPaths3(tester)) + checkChangedClassloaders(tester, null, null, null, false) causeCompileError(workspacePath / "mill-build" / "mill-build" / "build.mill") evalCheckErr( + tester, "\n1 targets failed", (workspacePath / "mill-build" / "mill-build" / "build.mill").toString, "not found: object doesnt" ) - checkWatchedFiles(Nil, Nil, Nil, buildPaths3) - checkChangedClassloaders(null, null, null, null) + checkWatchedFiles(tester, Nil, Nil, Nil, buildPaths3(tester)) + checkChangedClassloaders(tester, null, null, null, null) fixCompileError(workspacePath / "mill-build" / "mill-build" / "build.mill") evalCheckErr( + tester, "\n1 targets failed", (workspacePath / "mill-build" / "build.mill").toString, "not found: object doesnt" ) - checkWatchedFiles(Nil, Nil, buildPaths2, buildPaths3) - checkChangedClassloaders(null, null, null, true) + checkWatchedFiles(tester, Nil, Nil, buildPaths2(tester), buildPaths3(tester)) + checkChangedClassloaders(tester, null, null, null, true) fixCompileError(workspacePath / "mill-build" / "build.mill") evalCheckErr( + tester, "\n1 targets failed", (workspacePath / "build.mill").toString, "not found: value doesnt" ) - checkWatchedFiles(Nil, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, null, true, false) + checkWatchedFiles(tester, Nil, buildPaths(tester), buildPaths2(tester), buildPaths3(tester)) + checkChangedClassloaders(tester, null, null, true, false) fixCompileError(workspacePath / "build.mill") - runAssertSuccess("

hello

world

0.8.2

!") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, true, false, false) + runAssertSuccess(tester, "

hello

world

0.8.2

!") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) + checkChangedClassloaders(tester, null, true, false, false) } - test("runtimeErrorEdits") { + test("runtimeErrorEdits") - integrationTest { tester => + import tester._ val runErrorSnippet = """{ |override def runClasspath = T{ | throw new Exception("boom") @@ -327,58 +407,83 @@ object MultiLevelBuildTests extends IntegrationTestSuite { def fixRuntimeError(p: os.Path) = modifyFile(p, _.replaceFirst(Regex.quote(runErrorSnippet), "\\{")) - runAssertSuccess("

hello

world

0.8.2

!") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, true, true, true) + runAssertSuccess(tester, "

hello

world

0.8.2

!") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) + checkChangedClassloaders(tester, null, true, true, true) causeRuntimeError(workspacePath / "build.mill") - evalCheckErr( - "\n1 targets failed", - "foo.runClasspath java.lang.Exception: boom" + evalCheckErr(tester, "\n1 targets failed", "foo.runClasspath java.lang.Exception: boom") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) ) - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, true, false, false) + checkChangedClassloaders(tester, null, true, false, false) causeRuntimeError(workspacePath / "mill-build" / "build.mill") evalCheckErr( + tester, "\n1 targets failed", "build.mill", "runClasspath java.lang.Exception: boom" ) - checkWatchedFiles(Nil, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, null, true, false) + checkWatchedFiles(tester, Nil, buildPaths(tester), buildPaths2(tester), buildPaths3(tester)) + checkChangedClassloaders(tester, null, null, true, false) causeRuntimeError(workspacePath / "mill-build" / "mill-build" / "build.mill") evalCheckErr( + tester, "\n1 targets failed", "build.mill", "runClasspath java.lang.Exception: boom" ) - checkWatchedFiles(Nil, Nil, buildPaths2, buildPaths3) - checkChangedClassloaders(null, null, null, true) + checkWatchedFiles(tester, Nil, Nil, buildPaths2(tester), buildPaths3(tester)) + checkChangedClassloaders(tester, null, null, null, true) fixRuntimeError(workspacePath / "mill-build" / "mill-build" / "build.mill") evalCheckErr( + tester, "\n1 targets failed", "build.mill", "runClasspath java.lang.Exception: boom" ) - checkWatchedFiles(Nil, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, null, true, true) + checkWatchedFiles(tester, Nil, buildPaths(tester), buildPaths2(tester), buildPaths3(tester)) + checkChangedClassloaders(tester, null, null, true, true) fixRuntimeError(workspacePath / "mill-build" / "build.mill") evalCheckErr( + tester, "\n1 targets failed", "build.mill", "foo.runClasspath java.lang.Exception: boom" ) - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, true, true, false) + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) + checkChangedClassloaders(tester, null, true, true, false) fixRuntimeError(workspacePath / "build.mill") - runAssertSuccess("

hello

world

0.8.2

!") - checkWatchedFiles(fooPaths, buildPaths, buildPaths2, buildPaths3) - checkChangedClassloaders(null, true, false, false) + runAssertSuccess(tester, "

hello

world

0.8.2

!") + checkWatchedFiles( + tester, + fooPaths(tester), + buildPaths(tester), + buildPaths2(tester), + buildPaths3(tester) + ) + checkChangedClassloaders(tester, null, true, false, false) } } diff --git a/integration/invalidation/watch-source-input/src/WatchSourceInputTests.scala b/integration/invalidation/watch-source-input/src/WatchSourceInputTests.scala index 6230a43a2fd..09a2205fb05 100644 --- a/integration/invalidation/watch-source-input/src/WatchSourceInputTests.scala +++ b/integration/invalidation/watch-source-input/src/WatchSourceInputTests.scala @@ -1,6 +1,6 @@ package mill.integration -import mill.testkit.{IntegrationTestSuite, IntegrationTester} +import mill.testkit.{UtestIntegrationTestSuite, IntegrationTester} import mill.main.client.Util import utest._ @@ -20,15 +20,13 @@ import scala.concurrent.ExecutionContext.Implicits.global * 4. `interp.watchValue` * 5. Implicitly watched files, like `build.mill` */ -object WatchSourceInputTests extends IntegrationTestSuite { +object WatchSourceInputTests extends UtestIntegrationTestSuite { val maxDuration = 60000 val tests: Tests = Tests { - initWorkspace() - - def awaitCompletionMarker(name: String) = { + def awaitCompletionMarker(tester: IntegrationTester, name: String) = { val maxTime = System.currentTimeMillis() + maxDuration - while (!os.exists(workspacePath / "out" / name)) { + while (!os.exists(tester.workspacePath / "out" / name)) { if (System.currentTimeMillis() > maxTime) { sys.error(s"awaitCompletionMarker($name) timed out") } @@ -63,14 +61,14 @@ object WatchSourceInputTests extends IntegrationTestSuite { if (show) assert(shows == expectedShows.map('"' + _ + '"')) } - def testWatchSource(show: Boolean) = + def testWatchSource(tester: IntegrationTester, show: Boolean) = testBase(show) { (expectedOut, expectedErr, expectedShows) => val showArgs = if (show) Seq("show") else Nil - + import tester._ val evalResult = Future { eval(("--watch", showArgs, "qux"), timeout = maxDuration) } - awaitCompletionMarker("initialized0") - awaitCompletionMarker("quxRan0") + awaitCompletionMarker(tester, "initialized0") + awaitCompletionMarker(tester, "quxRan0") expectedOut.append( "Setting up build.mill" ) @@ -83,7 +81,7 @@ object WatchSourceInputTests extends IntegrationTestSuite { ) os.write.over(workspacePath / "foo1.txt", "edited-foo1") - awaitCompletionMarker("quxRan1") + awaitCompletionMarker(tester, "quxRan1") expectedErr.append( "Running qux foo contents edited-foo1 initial-foo2", "Running qux bar contents initial-bar" @@ -93,7 +91,7 @@ object WatchSourceInputTests extends IntegrationTestSuite { ) os.write.over(workspacePath / "foo2.txt", "edited-foo2") - awaitCompletionMarker("quxRan2") + awaitCompletionMarker(tester, "quxRan2") expectedErr.append( "Running qux foo contents edited-foo1 edited-foo2", "Running qux bar contents initial-bar" @@ -103,7 +101,7 @@ object WatchSourceInputTests extends IntegrationTestSuite { ) os.write.over(workspacePath / "bar.txt", "edited-bar") - awaitCompletionMarker("quxRan3") + awaitCompletionMarker(tester, "quxRan3") expectedErr.append( "Running qux foo contents edited-foo1 edited-foo2", "Running qux bar contents edited-bar" @@ -113,7 +111,7 @@ object WatchSourceInputTests extends IntegrationTestSuite { ) os.write.append(workspacePath / "build.mill", "\ndef unrelated = true") - awaitCompletionMarker("initialized1") + awaitCompletionMarker(tester, "initialized1") expectedOut.append( "Setting up build.mill" // These targets do not re-evaluate, because the change to the build @@ -126,7 +124,7 @@ object WatchSourceInputTests extends IntegrationTestSuite { ) os.write.over(workspacePath / "watchValue.txt", "exit") - awaitCompletionMarker("initialized2") + awaitCompletionMarker(tester, "initialized2") expectedOut.append("Setting up build.mill") Await.result(evalResult, Duration.apply(maxDuration, SECONDS)) @@ -135,18 +133,30 @@ object WatchSourceInputTests extends IntegrationTestSuite { test("sources") { // Make sure we clean up the workspace between retries - test("noshow") - retry(3) { if (!Util.isWindows) { initWorkspace(); testWatchSource(false) } } - test("show") - retry(3) { if (!Util.isWindows) { initWorkspace(); testWatchSource(true) } } + test("noshow") - retry(3) { + integrationTest { tester => + if (!Util.isWindows) { + testWatchSource(tester, false) + } + } + } + test("show") - retry(3) { + integrationTest { tester => + if (!Util.isWindows) { + testWatchSource(tester, true) + } + } + } } - def testWatchInput(show: Boolean) = + def testWatchInput(tester: IntegrationTester, show: Boolean) = testBase(show) { (expectedOut, expectedErr, expectedShows) => val showArgs = if (show) Seq("show") else Nil - + import tester._ val evalResult = Future { eval(("--watch", showArgs, "lol"), timeout = maxDuration) } - awaitCompletionMarker("initialized0") - awaitCompletionMarker("lolRan0") + awaitCompletionMarker(tester, "initialized0") + awaitCompletionMarker(tester, "lolRan0") expectedOut.append( "Setting up build.mill" ) @@ -156,17 +166,17 @@ object WatchSourceInputTests extends IntegrationTestSuite { expectedShows.append("Running lol baz contents initial-baz") os.write.over(workspacePath / "baz.txt", "edited-baz") - awaitCompletionMarker("lolRan1") + awaitCompletionMarker(tester, "lolRan1") expectedErr.append("Running lol baz contents edited-baz") expectedShows.append("Running lol baz contents edited-baz") os.write.over(workspacePath / "watchValue.txt", "edited-watchValue") - awaitCompletionMarker("initialized1") + awaitCompletionMarker(tester, "initialized1") expectedOut.append("Setting up build.mill") expectedShows.append("Running lol baz contents edited-baz") os.write.over(workspacePath / "watchValue.txt", "exit") - awaitCompletionMarker("initialized2") + awaitCompletionMarker(tester, "initialized2") expectedOut.append("Setting up build.mill") Await.result(evalResult, Duration.apply(maxDuration, SECONDS)) @@ -175,8 +185,20 @@ object WatchSourceInputTests extends IntegrationTestSuite { test("input") { // Make sure we clean up the workspace between retries - test("noshow") - retry(3) { if (!Util.isWindows) { initWorkspace(); testWatchInput(false) } } - test("show") - retry(3) { if (!Util.isWindows) { initWorkspace(); testWatchInput(true) } } + test("noshow") - retry(3) { + integrationTest { tester => + if (!Util.isWindows) { + testWatchInput(tester, false) + } + } + } + test("show") - retry(3) { + integrationTest { tester => + if (!Util.isWindows) { + testWatchInput(tester, true) + } + } + } } } } diff --git a/integration/invalidation/zinc-incremental-compilation/src/ZincIncrementalCompilationTests.scala b/integration/invalidation/zinc-incremental-compilation/src/ZincIncrementalCompilationTests.scala index cf61225ff9f..a0f902721e2 100644 --- a/integration/invalidation/zinc-incremental-compilation/src/ZincIncrementalCompilationTests.scala +++ b/integration/invalidation/zinc-incremental-compilation/src/ZincIncrementalCompilationTests.scala @@ -1,15 +1,15 @@ package mill.integration -import mill.testkit.IntegrationTestSuite +import mill.testkit.UtestIntegrationTestSuite import utest._ // Regress test for issue https://github.com/com-lihaoyi/mill/issues/1901 -object ZincIncrementalCompilationTests extends IntegrationTestSuite { +object ZincIncrementalCompilationTests extends UtestIntegrationTestSuite { val tests: Tests = Tests { - initWorkspace() - test("incremental compilation only compiles changed files") { - val successful = eval("app.compile") + test("incremental compilation only compiles changed files") - integrationTest { tester => + import tester._ + val successful = tester.eval("app.compile") assert(successful.isSuccess) val appSrc = workspacePath / "app" / "src" / "main" / "scala" / "App.scala" @@ -25,7 +25,7 @@ object ZincIncrementalCompilationTests extends IntegrationTestSuite { println("** second run **") os.write.append(appSrc, "\n ") - val succ2nd = eval("app.compile") + val succ2nd = tester.eval("app.compile") assert(succ2nd.isSuccess) val appSrcInfo2 = os.stat(appSrc) diff --git a/main/api/src/mill/api/Result.scala b/main/api/src/mill/api/Result.scala index a06cd191c99..ad7af1e051b 100644 --- a/main/api/src/mill/api/Result.scala +++ b/main/api/src/mill/api/Result.scala @@ -74,8 +74,10 @@ object Result { */ case class Failure[T](msg: String, value: Option[T] = None) extends Failing[T] { def map[V](f: T => V): Failure[V] = Result.Failure(msg, value.map(f(_))) - def flatMap[V](f: T => Result[V]): Failure[V] = + def flatMap[V](f: T => Result[V]): Failure[V] = { Failure(msg, value.flatMap(f(_).asSuccess.map(_.value))) + } + override def toString: String = s"Failure($msg, $value)" } /** diff --git a/main/client/src/mill/main/client/CodeGenConstants.java b/main/client/src/mill/main/client/CodeGenConstants.java index 1245cfc75ff..71136e2d1dd 100644 --- a/main/client/src/mill/main/client/CodeGenConstants.java +++ b/main/client/src/mill/main/client/CodeGenConstants.java @@ -21,17 +21,17 @@ public class CodeGenConstants { /** * The name of the root build file */ - final public static String[] rootBuildFileNames = {"build.mill", "build.sc"}; + final public static String[] rootBuildFileNames = {"build.mill", "build.mill.sc", "build.sc"}; /** * The name of any sub-folder build files */ - final public static String[] nestedBuildFileNames = {"package.mill", "package.sc"}; + final public static String[] nestedBuildFileNames = {"package.mill", "package.mill.sc", "package.sc"}; /** * The extensions used by build files */ - final public static String[] buildFileExtensions = {"mill", "sc"}; + final public static String[] buildFileExtensions = {"mill", "mill.sc", "sc"}; /** * The user-facing name for the root of the module tree. diff --git a/main/client/test/src/mill/main/client/FileToStreamTailerTest.java b/main/client/test/src/mill/main/client/FileToStreamTailerTest.java index bb960cafc4a..0c68b5e12fe 100644 --- a/main/client/test/src/mill/main/client/FileToStreamTailerTest.java +++ b/main/client/test/src/mill/main/client/FileToStreamTailerTest.java @@ -99,12 +99,12 @@ public FileToStreamTailerTest() { final FileToStreamTailer tailer = new FileToStreamTailer(file, ps, 10); ) { tailer.start(); - Thread.sleep(100); + Thread.sleep(500); expectEquals(bas.toString(), ""); out.println("log line"); expectTrue(file.exists()); - Thread.sleep(100); - expectEquals(bas.toString(), "log line" + System.lineSeparator()); + Thread.sleep(500); + expectEquals(bas.toString().trim(), "log line"); } } }); diff --git a/main/server/test/src/mill/main/server/ClientServerTests.scala b/main/server/test/src/mill/main/server/ClientServerTests.scala index d9b14286f5c..e6a0a090696 100644 --- a/main/server/test/src/mill/main/server/ClientServerTests.scala +++ b/main/server/test/src/mill/main/server/ClientServerTests.scala @@ -121,9 +121,9 @@ object ClientServerTests extends TestSuite { } def tests = Tests { - val tester = new Tester - test("hello") - retry(3) { + test("hello") - retry(3) { + val tester = new Tester val res1 = tester(args = Array("world")) assert( @@ -163,6 +163,7 @@ object ClientServerTests extends TestSuite { } test("concurrency") { + val tester = new Tester // Make sure concurrently running client commands results in multiple processes // being spawned, running in different folders import concurrent._ @@ -187,6 +188,7 @@ object ClientServerTests extends TestSuite { } test("clientLockReleasedOnFailure") { + val tester = new Tester // When the client gets interrupted via Ctrl-C, we exit the server immediately. This // is because Mill ends up executing arbitrary JVM code, and there is no generic way // to interrupt such an execution. The two options are to leave the server running @@ -212,6 +214,7 @@ object ClientServerTests extends TestSuite { } test("envVars") - retry(3) { + val tester = new Tester // Make sure the simple "have the client start a server and // exchange one message" workflow works from end to end. diff --git a/main/util/src/mill/util/CoursierSupport.scala b/main/util/src/mill/util/CoursierSupport.scala index 2fa9c7783e3..9fa405f0282 100644 --- a/main/util/src/mill/util/CoursierSupport.scala +++ b/main/util/src/mill/util/CoursierSupport.scala @@ -38,8 +38,12 @@ trait CoursierSupport { )(f: () => T): T = { val tried = Try(f()) tried match { - case Failure(e: NoSuchFileException) - if retryCount > 0 && e.getMessage.contains("__sha1.computed") => + case Failure(e) + if retryCount > 0 + && e.getMessage.contains("__sha1.computed") + && (e.isInstanceOf[NoSuchFileException] || e.isInstanceOf[ + java.nio.file.AccessDeniedException + ]) => // this one is not detected by coursier itself, so we try-catch handle it // I assume, this happens when another coursier thread already moved or rename dthe temporary file ctx.foreach(_.log.debug( diff --git a/mill-build/src/ExampleParser.scala b/mill-build/src/ExampleParser.scala index 55db14aa90a..39988ee2a86 100644 --- a/mill-build/src/ExampleParser.scala +++ b/mill-build/src/ExampleParser.scala @@ -6,7 +6,7 @@ object ExampleParser { val states = collection.mutable.Buffer("scala") val chunks = collection.mutable.Buffer(collection.mutable.Buffer.empty[String]) - val rootBuildFileNames = Seq("build.sc", "build.mill") + val rootBuildFileNames = Seq("build.sc", "build.mill", "build.mill.sc") val buildFile = rootBuildFileNames.map(testRepoRoot / _).find(os.exists) for (line <- os.read.lines(buildFile.get)) { val (newState, restOpt) = line match { diff --git a/runner/linenumbers/src/mill/linenumbers/LineNumberPlugin.scala b/runner/linenumbers/src/mill/linenumbers/LineNumberPlugin.scala index 591023f796c..b8f0aeee877 100644 --- a/runner/linenumbers/src/mill/linenumbers/LineNumberPlugin.scala +++ b/runner/linenumbers/src/mill/linenumbers/LineNumberPlugin.scala @@ -30,7 +30,7 @@ class LineNumberPlugin(val global: Global) extends Plugin { object LineNumberPlugin { def apply(g: Global)(unit: g.CompilationUnit): Unit = { - if (buildFileExtensions.exists(g.currentSource.file.hasExtension(_))) { + if (buildFileExtensions.exists(ex => g.currentSource.file.name.endsWith(s".$ex"))) { val str = new String(g.currentSource.content) val lines = str.linesWithSeparators.toVector diff --git a/runner/src/mill/runner/CodeGen.scala b/runner/src/mill/runner/CodeGen.scala index 3e8c64c4fe5..dbe14739c82 100644 --- a/runner/src/mill/runner/CodeGen.scala +++ b/runner/src/mill/runner/CodeGen.scala @@ -18,18 +18,17 @@ object CodeGen { millTopLevelProjectRoot: os.Path ): Unit = { for (scriptSource <- scriptSources) { - // pprint.log(scriptSource) val scriptPath = scriptSource.path val specialNames = (nestedBuildFileNames ++ rootBuildFileNames).toSet val isBuildScript = specialNames(scriptPath.last) val scriptFolderPath = scriptPath / os.up - if (scriptFolderPath == projectRoot && scriptPath.baseName == "package") { + if (scriptFolderPath == projectRoot && scriptPath.last.split('.').head == "package") { throw Result.Failure(s"Mill ${scriptPath.last} files can only be in subfolders") } - if (scriptFolderPath != projectRoot && scriptPath.baseName == "build") { + if (scriptFolderPath != projectRoot && scriptPath.last.split('.').head == "build") { throw Result.Failure(s"Mill ${scriptPath.last} files can only be in the project root") } @@ -86,7 +85,7 @@ object CodeGen { if (!isBuildScript) { s"""$pkgLine |$aliasImports - |object ${backtickWrap(scriptPath.baseName)} { + |object ${backtickWrap(scriptPath.last.split('.').head)} { |$markerComment |$scriptCode |}""".stripMargin @@ -105,7 +104,7 @@ object CodeGen { ) } - os.write(dest, parts, createFolders = true) + os.write.over(dest, parts, createFolders = true) } } diff --git a/runner/src/mill/runner/FileImportGraph.scala b/runner/src/mill/runner/FileImportGraph.scala index 89199a69f3b..fae30b35174 100644 --- a/runner/src/mill/runner/FileImportGraph.scala +++ b/runner/src/mill/runner/FileImportGraph.scala @@ -51,7 +51,6 @@ object FileImportGraph { var millImport = false def processScript(s: os.Path, useDummy: Boolean = false): Unit = { - val readFileEither = scala.util.Try { val content = if (useDummy) "" else os.read(s) val fileName = s.relativeTo(topLevelProjectRoot).toString @@ -67,7 +66,7 @@ object FileImportGraph { val expectedImportSegments = expectedImportSegments0.map(backtickWrap).mkString(".") if ( // Legacy `.sc` files have their package build be optional - s.ext == "mill" && + (s.last.endsWith(".mill") || s.last.endsWith(".mill.sc")) && expectedImportSegments != importSegments && // Root build.mill file has its `package build` be optional !(importSegments == "" && rootBuildFileNames.contains(s.last)) @@ -137,7 +136,7 @@ object FileImportGraph { case ImportTree(Seq(("$file", end0), rest @ _*), mapping, start, end) => // Only recursively explore imports from legacy `.sc` files, as new `.mill` files // do file discovery via scanning folders containing `package.mill` files - if (s.ext == "sc") { + if (s.last.endsWith(".sc") && !s.last.endsWith(".mill.sc")) { val nextPaths = mapping.map { case (lhs, rhs) => nextPathFor(s, rest.map(_._1) :+ lhs) } @@ -167,8 +166,10 @@ object FileImportGraph { val useDummy = rootBuildFiles.isEmpty val foundRootBuildFileName: String = rootBuildFiles.getOrElse(rootBuildFileNames.head) - val buildFileExtension = buildFileExtensions.find(foundRootBuildFileName.endsWith).get - val nestedBuildFileName = nestedBuildFileNames.find(_.endsWith(buildFileExtension)).get + val buildFileExtension = + buildFileExtensions.find(ex => foundRootBuildFileName.endsWith(s".$ex")).get + + val nestedBuildFileName = s"package.$buildFileExtension" processScript(projectRoot / foundRootBuildFileName, useDummy) val buildFiles = os @@ -184,7 +185,7 @@ object FileImportGraph { val adjacentScripts = (projectRoot +: buildFiles.map(_ / os.up)) .flatMap(os.list(_)) - .filter(_.ext == buildFileExtension) + .filter(_.last.endsWith(s".$buildFileExtension")) (buildFiles ++ adjacentScripts).foreach(processScript(_)) diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala index 98fdc303a40..508793a2664 100644 --- a/scalalib/src/mill/scalalib/JavaModule.scala +++ b/scalalib/src/mill/scalalib/JavaModule.scala @@ -533,7 +533,13 @@ trait JavaModule * Resolved dependencies based on [[transitiveIvyDeps]] and [[transitiveCompileIvyDeps]]. */ def resolvedIvyDeps: T[Agg[PathRef]] = T { - defaultResolver().resolveDeps(transitiveCompileIvyDeps() ++ transitiveIvyDeps()) + def resolvedIvyDeps0() = + defaultResolver().resolveDeps(transitiveCompileIvyDeps() ++ transitiveIvyDeps()) + try resolvedIvyDeps0() + catch { + case e: java.nio.file.AccessDeniedException => + resolvedIvyDeps0() // this is caused by a coursier race condition on windows, just retry + } } /** diff --git a/scalalib/src/mill/scalalib/Lib.scala b/scalalib/src/mill/scalalib/Lib.scala index 556f138a01d..664cf7f52be 100644 --- a/scalalib/src/mill/scalalib/Lib.scala +++ b/scalalib/src/mill/scalalib/Lib.scala @@ -132,7 +132,9 @@ object Lib { root <- sources if os.exists(root.path) path <- (if (os.isDir(root.path)) os.walk(root.path) else Seq(root.path)) - if os.isFile(path) && (extensions.exists(path.ext == _) && !isHiddenFile(path)) + if os.isFile(path) && (extensions.exists(ex => path.last.endsWith(s".$ex")) && !isHiddenFile( + path + )) } yield path } diff --git a/scalalib/src/mill/scalalib/scalafmt/ScalafmtModule.scala b/scalalib/src/mill/scalalib/scalafmt/ScalafmtModule.scala index f10a0595bc6..f28a17624ea 100644 --- a/scalalib/src/mill/scalalib/scalafmt/ScalafmtModule.scala +++ b/scalalib/src/mill/scalalib/scalafmt/ScalafmtModule.scala @@ -57,7 +57,9 @@ trait ScalafmtModule extends JavaModule { file <- { if (os.isDir(pathRef.path)) { os.walk(pathRef.path).filter(file => - os.isFile(file) && (file.ext == "scala" || buildFileExtensions.contains(file.ext)) + os.isFile(file) && (file.ext == "scala" || buildFileExtensions.exists(ex => + file.last.endsWith(s".$ex") + )) ) } else { Seq(pathRef.path) diff --git a/testkit/src/mill/testkit/ExampleTestSuite.scala b/testkit/src/mill/testkit/ExampleTestSuite.scala deleted file mode 100644 index 7d15b65d553..00000000000 --- a/testkit/src/mill/testkit/ExampleTestSuite.scala +++ /dev/null @@ -1,11 +0,0 @@ -package mill.testkit -import utest._ - -object ExampleTestSuite extends IntegrationTestSuiteBase { - val tests: Tests = Tests { - - test("exampleTest") { - new ExampleTester(clientServerMode, workspaceSourcePath, millExecutable).run() - } - } -} diff --git a/testkit/src/mill/testkit/IntegrationTestSuite.scala b/testkit/src/mill/testkit/IntegrationTestSuite.scala index 9ad34228288..e488a51542f 100644 --- a/testkit/src/mill/testkit/IntegrationTestSuite.scala +++ b/testkit/src/mill/testkit/IntegrationTestSuite.scala @@ -1,7 +1,8 @@ package mill.testkit -abstract class IntegrationTestSuite extends IntegrationTestSuiteBase with IntegrationTester.Impl { - override def utestAfterEach(path: Seq[String]): Unit = { - if (clientServerMode) close() - } +abstract class UtestIntegrationTestSuite extends utest.TestSuite with IntegrationTestSuite { + protected def workspaceSourcePath: os.Path = os.Path(sys.env("MILL_TEST_RESOURCE_FOLDER")) + protected def clientServerMode: Boolean = sys.env("MILL_INTEGRATION_SERVER_MODE").toBoolean + protected def millExecutable: os.Path = + os.Path(System.getenv("MILL_INTEGRATION_LAUNCHER"), os.pwd) } diff --git a/testkit/src/mill/testkit/IntegrationTestSuiteBase.scala b/testkit/src/mill/testkit/IntegrationTestSuiteBase.scala index b3a06993be1..24abc745b62 100644 --- a/testkit/src/mill/testkit/IntegrationTestSuiteBase.scala +++ b/testkit/src/mill/testkit/IntegrationTestSuiteBase.scala @@ -1,11 +1,18 @@ package mill.testkit import os.Path -import utest._ -abstract class IntegrationTestSuiteBase extends TestSuite { - def workspaceSourcePath: os.Path = os.Path(sys.env("MILL_TEST_RESOURCE_FOLDER")) - val clientServerMode: Boolean = sys.env("MILL_INTEGRATION_SERVER_MODE").toBoolean +trait IntegrationTestSuite { + protected def workspaceSourcePath: os.Path + protected def clientServerMode: Boolean - def millExecutable: Path = os.Path(System.getenv("MILL_INTEGRATION_LAUNCHER"), os.pwd) + protected def millExecutable: Path + + def debugLog: Boolean = false + def integrationTest[T](t: IntegrationTester => T): T = { + val tester = + new IntegrationTester(clientServerMode, workspaceSourcePath, millExecutable, debugLog) + try t(tester) + finally tester.close() + } } diff --git a/testkit/src/mill/testkit/IntegrationTester.scala b/testkit/src/mill/testkit/IntegrationTester.scala index d6bece57248..9ddc8350f88 100644 --- a/testkit/src/mill/testkit/IntegrationTester.scala +++ b/testkit/src/mill/testkit/IntegrationTester.scala @@ -23,7 +23,8 @@ import scala.util.control.NonFatal class IntegrationTester( val clientServerMode: Boolean, val workspaceSourcePath: os.Path, - val millExecutable: os.Path + val millExecutable: os.Path, + override val debugLog: Boolean = false ) extends IntegrationTester.Impl { initWorkspace() } diff --git a/testkit/src/mill/testkit/IntegrationTesterBase.scala b/testkit/src/mill/testkit/IntegrationTesterBase.scala index a67f5a7b27d..523fc2728a9 100644 --- a/testkit/src/mill/testkit/IntegrationTesterBase.scala +++ b/testkit/src/mill/testkit/IntegrationTesterBase.scala @@ -9,15 +9,17 @@ trait IntegrationTesterBase { * Mill build being tested. Contains the `build.mill` file, any application code, and * the `out/` folder containing the build output * - * Typically just `pwd`, which is a sandbox directory for test suites run using Mill. + * Typically a temp folder inside `pwd`, just in case there's some leftover + * files/processes from previous integration tests that may interfere with the current one */ - val workspacePath: os.Path = os.pwd + val workspacePath: os.Path = os.temp.dir(dir = os.pwd) /** * Initializes the workspace in preparation for integration testing */ def initWorkspace(): Unit = { println(s"Copying integration test sources from $workspaceSourcePath to $workspacePath") + os.makeDir.all(workspacePath) os.list(workspacePath).foreach(os.remove.all(_)) os.list(workspaceSourcePath).filter(_.last != out).foreach(os.copy.into(_, workspacePath)) os.remove.all(workspacePath / "out") diff --git a/testkit/src/mill/testkit/UtestExampleTestSuite.scala b/testkit/src/mill/testkit/UtestExampleTestSuite.scala new file mode 100644 index 00000000000..994a4995a04 --- /dev/null +++ b/testkit/src/mill/testkit/UtestExampleTestSuite.scala @@ -0,0 +1,15 @@ +package mill.testkit +import utest._ + +object UtestExampleTestSuite extends TestSuite { + val workspaceSourcePath: os.Path = os.Path(sys.env("MILL_TEST_RESOURCE_FOLDER")) + val clientServerMode: Boolean = sys.env("MILL_INTEGRATION_SERVER_MODE").toBoolean + + val millExecutable: os.Path = os.Path(System.getenv("MILL_INTEGRATION_LAUNCHER"), os.pwd) + val tests: Tests = Tests { + + test("exampleTest") { + new ExampleTester(clientServerMode, workspaceSourcePath, millExecutable).run() + } + } +} diff --git a/testkit/test/src/mill/testkit/IntegrationTesterTests.scala b/testkit/test/src/mill/testkit/IntegrationTesterTests.scala index 13ed31e1a3e..9172a036427 100644 --- a/testkit/test/src/mill/testkit/IntegrationTesterTests.scala +++ b/testkit/test/src/mill/testkit/IntegrationTesterTests.scala @@ -2,28 +2,27 @@ package mill.testkit import utest._ -object IntegrationTesterTests extends TestSuite { - +object IntegrationTesterTests extends TestSuite with IntegrationTestSuite { + def clientServerMode = true + def workspaceSourcePath = + os.Path(sys.env("MILL_TEST_RESOURCE_FOLDER")) / "integration-test-example-project" + def millExecutable = os.Path(sys.env("MILL_EXECUTABLE_PATH")) def tests: Tests = Tests { test("integration") { - val tester = new IntegrationTester( - clientServerMode = true, - workspaceSourcePath = - os.Path(sys.env("MILL_TEST_RESOURCE_FOLDER")) / "integration-test-example-project", - millExecutable = os.Path(sys.env("MILL_EXECUTABLE_PATH")) - ) + integrationTest { tester => + val res1 = tester.eval("testTask") + assert(res1.isSuccess) + assert(res1.err.contains("compiling 1 Scala source")) // compiling the `build.mill` + assert(tester.out("testTask").value[String] == "HELLO WORLD SOURCE FILE") - val res1 = tester.eval("testTask") - assert(res1.isSuccess) - assert(res1.err.contains("compiling 1 Scala source")) // compiling the `build.mill` - assert(tester.out("testTask").value[String] == "HELLO WORLD SOURCE FILE") + tester.modifyFile(tester.workspacePath / "source-file.txt", _ + "!!!") - tester.modifyFile(tester.workspacePath / "source-file.txt", _ + "!!!") + val res2 = tester.eval("testTask") + assert(!res2.err.contains("compiling 1 Scala source")) // no need to re-compile `build.mill` + assert(tester.out("testTask").value[String] == "HELLO WORLD SOURCE FILE!!!") + } - val res2 = tester.eval("testTask") - assert(!res2.err.contains("compiling 1 Scala source")) // no need to re-compile `build.mill` - assert(tester.out("testTask").value[String] == "HELLO WORLD SOURCE FILE!!!") } } }