-
-
Notifications
You must be signed in to change notification settings - Fork 375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add API for spawning task-futures, use it for grouping and parallelization of test classes within a single module #3478
Merged
Merged
Changes from all commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
29048c1
.
lihaoyi 1d9fb57
wip
lihaoyi 2503c31
wip
lihaoyi 8bf7299
.
lihaoyi 2b65cca
.
lihaoyi 58a059e
.
lihaoyi 68ec8e2
.
lihaoyi 5fed0e3
.
lihaoyi c5372ad
.
lihaoyi 7d76a7c
merge
lihaoyi 62ac934
.
lihaoyi b507a1f
.
lihaoyi 83a479e
.
lihaoyi e26c140
merge
lihaoyi 4adb678
filtered test classes using selectors before forking
lihaoyi 0fe0140
.
lihaoyi 2cb2ff5
.
lihaoyi 069c66b
.
lihaoyi 9e7850b
wip
lihaoyi 24432b6
.
lihaoyi bd4f778
fix
lihaoyi 722db61
merge
lihaoyi 6048490
put back final doesNotMatch check
lihaoyi 34084cd
initial merge
lihaoyi 615d57b
create simple example
lihaoyi 2f33c1c
wip
lihaoyi 28db0d3
kinda-works
lihaoyi 8fe3d5a
wip
lihaoyi 6939c74
wip
lihaoyi af38db8
.
lihaoyi 51ca042
.
lihaoyi d4866f3
.
lihaoyi 3433f03
.
lihaoyi e42d913
.
lihaoyi a3afc0c
.
lihaoyi 866d08f
.
lihaoyi 748c71f
Merge branch 'main' into test-par
lihaoyi 10c29c3
.
lihaoyi 4560377
.
lihaoyi 2a4233f
.
lihaoyi 56508c9
.
lihaoyi 6d1bbc0
.
lihaoyi 5bbf7ba
merge
lihaoyi e2c5422
.
lihaoyi 0a9865c
.
lihaoyi 4c30f76
.
lihaoyi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// Mill provides the `T.fork.async` and `T.fork.await` APIs for spawning async | ||
// futures within a task and aggregating their results later: | ||
|
||
package build | ||
|
||
import mill._ | ||
|
||
def taskSpawningFutures = Task { | ||
val f1 = T.fork.async(dest = T.dest / "future-1", key = "1", message = "First Future"){ | ||
println("Running First Future inside " + os.pwd) | ||
Thread.sleep(3000) | ||
val res = 1 | ||
println("Finished First Future") | ||
res | ||
} | ||
val f2 = T.fork.async(dest = T.dest / "future-2", key = "2", message = "Second Future"){ | ||
println("Running Second Future inside " + os.pwd) | ||
Thread.sleep(3000) | ||
val res = 2 | ||
println("Finished Second Future") | ||
res | ||
} | ||
|
||
T.fork.await(f1) + T.fork.await(f2) | ||
} | ||
|
||
/** Usage | ||
|
||
> ./mill show taskSpawningFutures | ||
[1] Running First Future inside .../out/taskSpawningFutures.dest/future-1 | ||
[2] Running Second Future inside .../out/taskSpawningFutures.dest/future-2 | ||
[1] Finished First Future | ||
[2] Finished Second Future | ||
3 | ||
|
||
*/ | ||
|
||
|
||
// `T.fork.async` takes several parameters in addition to the code block to be run: | ||
// | ||
// - `dest` is a folder for which the async future is to be run, overriding `os.pwd` | ||
// for the duration of the future | ||
// - `key` is a short prefix prepended to log lines to let you easily identify the future's | ||
// log lines and distinguish them from logs of other futures and tasks running concurrently | ||
// - `message` is a one-line description of what the future is doing | ||
// | ||
// Futures spawned by `T.fork.async` count towards Mill's `-j`/`--jobs` concurrency limit | ||
// (which defaults to one-per-core), so you can freely use `T.fork.async` without worrying | ||
// about spawning too many concurrent threads and causing CPU or memory contention. `T.fork` | ||
// uses Java's built in `ForkJoinPool` and `ManagedBlocker` infrastructure under the hood | ||
// to effectively manage the number of running threads. | ||
// | ||
// While `scala.concurrent` and `java.util.concurrent` can also be used to spawn thread | ||
// pools and run async futures, `T.fork` provides a way to do so that integrates with Mill's | ||
// existing concurrency, sandboxing and logging systems. Thus you should always prefer to | ||
// run async futures on `T.fork` whenever possible. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
|
||
//// SNIPPET:BUILD1 | ||
package build | ||
import mill._, javalib._ | ||
|
||
object foo extends JavaModule { | ||
object test extends JavaTests { | ||
def testFramework = "com.novocode.junit.JUnitFramework" | ||
def ivyDeps = Agg( | ||
ivy"com.novocode:junit-interface:0.11", | ||
ivy"org.mockito:mockito-core:4.6.1" | ||
) | ||
def testForkGrouping = discoveredTestClasses().grouped(1).toSeq | ||
} | ||
} | ||
|
||
/** See Also: foo/test/src/foo/HelloTests.java */ | ||
/** See Also: foo/test/src/foo/WorldTests.java */ | ||
|
||
//// SNIPPET:END |
11 changes: 11 additions & 0 deletions
11
example/javalib/testing/4-test-grouping/foo/src/foo/Foo.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package foo; | ||
|
||
public class Foo { | ||
public static void main(String[] args) { | ||
System.out.println(new Foo().hello()); | ||
} | ||
|
||
public String hello() { | ||
return "Hello World"; | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
example/javalib/testing/4-test-grouping/foo/test/src/foo/HelloTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package foo; | ||
|
||
import static org.junit.Assert.assertTrue; | ||
import org.junit.Test; | ||
|
||
public class HelloTests { | ||
|
||
@Test | ||
public void hello() throws Exception { | ||
System.out.println("Testing Hello"); | ||
String result = new Foo().hello(); | ||
assertTrue(result.startsWith("Hello")); | ||
Thread.sleep(1000); | ||
System.out.println("Testing Hello Completed"); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
example/javalib/testing/4-test-grouping/foo/test/src/foo/WorldTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package foo; | ||
|
||
import static org.junit.Assert.assertTrue; | ||
import org.junit.Test; | ||
|
||
public class WorldTests { | ||
@Test | ||
public void world() throws Exception { | ||
System.out.println("Testing World"); | ||
String result = new Foo().hello(); | ||
assertTrue(result.endsWith("World")); | ||
Thread.sleep(1000); | ||
System.out.println("Testing World Completed"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
//// SNIPPET:BUILD1 | ||
package build | ||
import mill._, kotlinlib._ | ||
|
||
object foo extends KotlinModule { | ||
|
||
def mainClass = Some("foo.FooKt") | ||
|
||
def kotlinVersion = "1.9.24" | ||
|
||
object test extends KotlinModuleTests { | ||
def testFramework = "com.github.sbt.junit.jupiter.api.JupiterFramework" | ||
def ivyDeps = Agg( | ||
ivy"com.github.sbt.junit:jupiter-interface:0.11.4", | ||
ivy"io.kotest:kotest-runner-junit5-jvm:5.9.1", | ||
ivy"org.mockito.kotlin:mockito-kotlin:5.4.0" | ||
) | ||
|
||
def testForkGrouping = discoveredTestClasses().grouped(1).toSeq | ||
} | ||
} | ||
|
||
/** See Also: foo/test/src/foo/HelloTests.kt */ | ||
/** See Also: foo/test/src/foo/WorldTests.kt */ | ||
|
||
//// SNIPPET:END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package foo | ||
|
||
open class Foo { | ||
|
||
fun hello(): String = "Hello World" | ||
|
||
} | ||
|
||
fun main(args: Array<String>) = println(Foo().hello()) |
14 changes: 14 additions & 0 deletions
14
example/kotlinlib/testing/4-test-grouping/foo/test/src/foo/HelloTests.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package foo | ||
|
||
import io.kotest.core.spec.style.FunSpec | ||
import io.kotest.matchers.string.shouldStartWith | ||
|
||
class HelloTests : FunSpec({ | ||
test("hello") { | ||
println("Testing Hello") | ||
val result = Foo().hello() | ||
result shouldStartWith "Hello" | ||
java.lang.Thread.sleep(1000) | ||
println("Testing Hello Completed") | ||
} | ||
}) |
14 changes: 14 additions & 0 deletions
14
example/kotlinlib/testing/4-test-grouping/foo/test/src/foo/WorldTests.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package foo | ||
|
||
import io.kotest.core.spec.style.FunSpec | ||
import io.kotest.matchers.string.shouldEndWith | ||
|
||
class WorldTests : FunSpec({ | ||
test("world") { | ||
println("Testing World") | ||
val result = Foo().hello() | ||
result shouldEndWith "World" | ||
java.lang.Thread.sleep(1000) | ||
println("Testing World Completed") | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// Test Grouping is an opt-in feature that allows you to take a single | ||
// test module and group the test classes such that each group will | ||
// execute in parallel when you call `test`. Test grouping is enabled | ||
// by overriding `def testForkGrouping`, as shown below: | ||
|
||
//// SNIPPET:BUILD1 | ||
package build | ||
import mill._, scalalib._ | ||
|
||
object foo extends ScalaModule { | ||
def scalaVersion = "2.13.8" | ||
object test extends ScalaTests { | ||
def ivyDeps = Agg(ivy"com.lihaoyi::utest:0.8.4") | ||
def testFramework = "utest.runner.Framework" | ||
def testForkGrouping = discoveredTestClasses().grouped(1).toSeq | ||
} | ||
} | ||
/** See Also: foo/test/src/HelloTests.scala */ | ||
/** See Also: foo/test/src/WorldTests.scala */ | ||
|
||
//// SNIPPET:END | ||
|
||
// In this example, we have one test module `foo.test`, and two test classes | ||
// `HelloTests` and `WorldTests`. By default, all test classes in the same | ||
// module run sequentially in the same JVM, but with `testForkGrouping` we can break up the | ||
// module and run each test class in parallel in separate JVMs, each with their own | ||
// separate `sandbox` folder and `.log` file: | ||
|
||
/** Usage | ||
|
||
> mill foo.test | ||
|
||
> find out/foo/test/test.dest | ||
... | ||
out/foo/test/test.dest/foo.HelloTests.log | ||
out/foo/test/test.dest/foo.HelloTests/sandbox | ||
out/foo/test/test.dest/foo.WorldTests.log | ||
out/foo/test/test.dest/foo.WorldTests/sandbox | ||
out/foo/test/test.dest/test-report.xml | ||
|
||
*/ | ||
|
||
// Test grouping allows you to run tests in parallel while keeping things deterministic and | ||
// debuggable: parallel test groups will not write over each others files in their | ||
// sandbox, and each one will have a separate set of logs that can be easily read | ||
// without the others mixed in | ||
// | ||
// In this example, `def testForkGrouping = discoveredTestClasses().grouped(1).toSeq` assigns | ||
// each test class to its own group, running in its own JVM. This comes with some overhead | ||
// on a per-JVM basis, so if your test classes are numerous and small you may want to assign | ||
// multiple test classes per group. You can also configure `testForkGrouping` to choose which | ||
// test classes you want to run together and which to run alone: e.g. if some test classes | ||
// are much slower than others, you may want to put the slow test classes each in its own | ||
// group to reduce latency, while grouping multiple fast test classes together to reduce the | ||
// per-group overhead of spinning up a separate JVM. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package foo | ||
object Foo { | ||
def main(args: Array[String]): Unit = { | ||
println(hello()) | ||
} | ||
def hello(): String = "Hello World" | ||
} |
14 changes: 14 additions & 0 deletions
14
example/scalalib/testing/4-test-grouping/foo/test/src/HelloTests.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package foo | ||
import utest._ | ||
object HelloTests extends TestSuite { | ||
def tests = Tests { | ||
test("hello") { | ||
println("Testing Hello") | ||
val result = Foo.hello() | ||
assert(result.startsWith("Hello")) | ||
Thread.sleep(1000) | ||
println("Testing Hello Completed") | ||
result | ||
} | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
example/scalalib/testing/4-test-grouping/foo/test/src/WorldTests.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package foo | ||
import utest._ | ||
object WorldTests extends TestSuite { | ||
def tests = Tests { | ||
test("world") { | ||
println("Testing World") | ||
val result = Foo.hello() | ||
assert(result.endsWith("World")) | ||
Thread.sleep(1000) | ||
println("Testing World Completed") | ||
result | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can turn this on properly once we re-bootstrap