diff --git a/example/kotlinlib/publishing/2-publish-module/build.mill b/example/kotlinlib/publishing/2-publish-module/build.mill new file mode 100644 index 00000000000..e1dd89b66b1 --- /dev/null +++ b/example/kotlinlib/publishing/2-publish-module/build.mill @@ -0,0 +1,34 @@ +//// SNIPPET:BUILD +package build +import mill._, kotlinlib._, publish._ + +object foo extends KotlinModule with PublishModule { + + def mainClass = Some("foo.FooKt") + + def kotlinVersion = "1.9.24" + + def publishVersion = "0.0.1" + + def pomSettings = PomSettings( + description = "Hello", + organization = "com.lihaoyi", + url = "https://github.com/lihaoyi/example", + licenses = Seq(License.MIT), + versionControl = VersionControl.github("lihaoyi", "example"), + developers = Seq(Developer("lihaoyi", "Li Haoyi", "https://github.com/lihaoyi")) + ) +} + +// This is an example `KotlinModule` with added publishing capabilities via +// `PublishModule`. This requires that you define an additional +// `publishVersion` and `pomSettings` with the relevant metadata, and provides +// the `.publishLocal` and `publishSigned` tasks for publishing locally to the +// machine or to the central maven repository + +/** Usage + +> mill foo.publishLocal +Publishing Artifact(com.lihaoyi,foo,0.0.1) to ivy repo... + +*/ diff --git a/example/kotlinlib/publishing/2-publish-module/foo/src/foo/Foo.kt b/example/kotlinlib/publishing/2-publish-module/foo/src/foo/Foo.kt new file mode 100644 index 00000000000..9e2b3bf4abe --- /dev/null +++ b/example/kotlinlib/publishing/2-publish-module/foo/src/foo/Foo.kt @@ -0,0 +1,3 @@ +package foo + +fun main(args: Array) = println("Hello World") diff --git a/example/kotlinlib/testing/1-test-suite/bar/src/bar/Bar.kt b/example/kotlinlib/testing/1-test-suite/bar/src/bar/Bar.kt new file mode 100644 index 00000000000..a844d340397 --- /dev/null +++ b/example/kotlinlib/testing/1-test-suite/bar/src/bar/Bar.kt @@ -0,0 +1,9 @@ +package bar + +open class Bar { + + fun hello(): String = "Hello World" + +} + +fun main(args: Array) = println(Bar().hello()) diff --git a/example/kotlinlib/testing/1-test-suite/bar/test/src/bar/BarTests.kt b/example/kotlinlib/testing/1-test-suite/bar/test/src/bar/BarTests.kt new file mode 100644 index 00000000000..6b7b4587da1 --- /dev/null +++ b/example/kotlinlib/testing/1-test-suite/bar/test/src/bar/BarTests.kt @@ -0,0 +1,34 @@ +package bar + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldStartWith +import io.kotest.matchers.string.shouldEndWith +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +class BarTests : FunSpec({ + + test("hello") { + val result = Bar().hello() + result shouldStartWith "Hello" + } + + test("world") { + val result = Bar().hello() + result shouldEndWith "World" + } + + test("mockito") { + val mockBar = mock() + + whenever(mockBar.hello()) doReturn "Hello Mockito World" + + val result = mockBar.hello() + + result shouldBe "Hello Mockito World" + verify(mockBar).hello() + } +}) diff --git a/example/kotlinlib/testing/1-test-suite/build.mill b/example/kotlinlib/testing/1-test-suite/build.mill new file mode 100644 index 00000000000..04edd594051 --- /dev/null +++ b/example/kotlinlib/testing/1-test-suite/build.mill @@ -0,0 +1,44 @@ +//// 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" + ) + + // This is needed because of the "mockito-kotlin" + def kotlincOptions = super.kotlincOptions() ++ Seq("-jvm-target", "11") + } +} +// This build defines a single module with a test suite, configured to use +// "JUnit" + "Kotest" as the testing framework, along with Mockito. Test suites are themselves +// ``KotlinModule``s, nested within the enclosing module, +//// SNIPPET:BUILD2 + +object bar extends KotlinModule { + + def mainClass = Some("bar.BarKt") + + def kotlinVersion = "1.9.24" + + object test extends KotlinModuleTests with TestModule.Junit5 { + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"io.kotest:kotest-runner-junit5-jvm:5.9.1", + ivy"org.mockito.kotlin:mockito-kotlin:5.4.0" + ) + + // This is needed because of the "mockito-kotlin" + def kotlincOptions = super.kotlincOptions() ++ Seq("-jvm-target", "11") + } +} + diff --git a/example/kotlinlib/testing/1-test-suite/foo/src/foo/Foo.kt b/example/kotlinlib/testing/1-test-suite/foo/src/foo/Foo.kt new file mode 100644 index 00000000000..4a44de60879 --- /dev/null +++ b/example/kotlinlib/testing/1-test-suite/foo/src/foo/Foo.kt @@ -0,0 +1,9 @@ +package foo + +open class Foo { + + fun hello(): String = "Hello World" + +} + +fun main(args: Array) = println(Foo().hello()) diff --git a/example/kotlinlib/testing/1-test-suite/foo/test/src/foo/FooTests.kt b/example/kotlinlib/testing/1-test-suite/foo/test/src/foo/FooTests.kt new file mode 100644 index 00000000000..def318b8b69 --- /dev/null +++ b/example/kotlinlib/testing/1-test-suite/foo/test/src/foo/FooTests.kt @@ -0,0 +1,34 @@ +package foo + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldStartWith +import io.kotest.matchers.string.shouldEndWith +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +class FooTests : FunSpec({ + + test("hello") { + val result = Foo().hello() + result shouldStartWith "Hello" + } + + test("world") { + val result = Foo().hello() + result shouldEndWith "World" + } + + test("mockito") { + val mockFoo = mock() + + whenever(mockFoo.hello()) doReturn "Hello Mockito World" + + val result = mockFoo.hello() + + result shouldBe "Hello Mockito World" + verify(mockFoo).hello() + } +}) diff --git a/example/kotlinlib/testing/2-test-deps/baz/src/baz/Baz.kt b/example/kotlinlib/testing/2-test-deps/baz/src/baz/Baz.kt new file mode 100644 index 00000000000..bedea67556d --- /dev/null +++ b/example/kotlinlib/testing/2-test-deps/baz/src/baz/Baz.kt @@ -0,0 +1,5 @@ +package baz + +object Baz { + const val VALUE = 123 +} diff --git a/example/kotlinlib/testing/2-test-deps/baz/test/src/baz/BazTestUtils.kt b/example/kotlinlib/testing/2-test-deps/baz/test/src/baz/BazTestUtils.kt new file mode 100644 index 00000000000..fa152807c01 --- /dev/null +++ b/example/kotlinlib/testing/2-test-deps/baz/test/src/baz/BazTestUtils.kt @@ -0,0 +1,11 @@ +package baz + +object BazTestUtils { + + fun bazAssertEquals(x: Any, y: Any) { + println("Using BazTestUtils.bazAssertEquals") + if (x != y) { + throw AssertionError("Expected $y, but got $x") + } + } +} diff --git a/example/kotlinlib/testing/2-test-deps/baz/test/src/baz/BazTests.kt b/example/kotlinlib/testing/2-test-deps/baz/test/src/baz/BazTests.kt new file mode 100644 index 00000000000..0903068f0a7 --- /dev/null +++ b/example/kotlinlib/testing/2-test-deps/baz/test/src/baz/BazTests.kt @@ -0,0 +1,12 @@ +package baz + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import com.google.common.math.IntMath + +class BazTests : FunSpec({ + + test("simple") { + BazTestUtils.bazAssertEquals(Baz.VALUE, IntMath.mean(122, 124)) + } +}) diff --git a/example/kotlinlib/testing/2-test-deps/build.mill b/example/kotlinlib/testing/2-test-deps/build.mill new file mode 100644 index 00000000000..66447b34c97 --- /dev/null +++ b/example/kotlinlib/testing/2-test-deps/build.mill @@ -0,0 +1,30 @@ +//// SNIPPET:BUILD +package build +import mill._, kotlinlib._ + +object qux extends KotlinModule { + + def kotlinVersion = "1.9.24" + + def moduleDeps = Seq(baz) + + object test extends KotlinModuleTests with TestModule.Junit5 { + def moduleDeps = super.moduleDeps ++ Seq(baz.test) + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"io.kotest:kotest-runner-junit5-jvm:5.9.1", + ivy"com.google.guava:guava:33.3.0-jre" + ) + } +} + +object baz extends KotlinModule { + + def kotlinVersion = "1.9.24" + + object test extends KotlinModuleTests with TestModule.Junit5 { + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"io.kotest:kotest-runner-junit5-jvm:5.9.1", + ivy"com.google.guava:guava:33.3.0-jre" + ) + } +} diff --git a/example/kotlinlib/testing/2-test-deps/qux/src/qux/Qux.kt b/example/kotlinlib/testing/2-test-deps/qux/src/qux/Qux.kt new file mode 100644 index 00000000000..0dca6258092 --- /dev/null +++ b/example/kotlinlib/testing/2-test-deps/qux/src/qux/Qux.kt @@ -0,0 +1,5 @@ +package qux + +object Qux { + const val VALUE = "xyz" +} diff --git a/example/kotlinlib/testing/2-test-deps/qux/test/src/qux/QuxTests.kt b/example/kotlinlib/testing/2-test-deps/qux/test/src/qux/QuxTests.kt new file mode 100644 index 00000000000..758a6b98fd1 --- /dev/null +++ b/example/kotlinlib/testing/2-test-deps/qux/test/src/qux/QuxTests.kt @@ -0,0 +1,12 @@ +package qux + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import com.google.common.base.Ascii + +class QuxTests : FunSpec({ + + test("simple") { + baz.BazTestUtils.bazAssertEquals(Ascii.toLowerCase("XYZ"), Qux.VALUE) + } +}) diff --git a/example/kotlinlib/testing/3-integration-suite/build.mill b/example/kotlinlib/testing/3-integration-suite/build.mill new file mode 100644 index 00000000000..ae202119f16 --- /dev/null +++ b/example/kotlinlib/testing/3-integration-suite/build.mill @@ -0,0 +1,21 @@ +//// SNIPPET:BUILD3 +package build +import mill._, kotlinlib._ +object qux extends KotlinModule { + + def kotlinVersion = "1.9.24" + + object test extends KotlinModuleTests with TestModule.Junit5 { + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"io.kotest:kotest-runner-junit5-jvm:5.9.1" + ) + } + object integration extends KotlinModuleTests with TestModule.Junit5 { + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"io.kotest:kotest-runner-junit5-jvm:5.9.1" + ) + } +} + +// The integration suite is just another regular test module within the parent KotlinModule +// (This example also demonstrates using Junit 5 instead of Junit 4) diff --git a/example/kotlinlib/testing/3-integration-suite/qux/integration/src/qux/QuxIntegrationTests.kt b/example/kotlinlib/testing/3-integration-suite/qux/integration/src/qux/QuxIntegrationTests.kt new file mode 100644 index 00000000000..eea56e6d65a --- /dev/null +++ b/example/kotlinlib/testing/3-integration-suite/qux/integration/src/qux/QuxIntegrationTests.kt @@ -0,0 +1,12 @@ +package qux + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe + +class QuxIntegrationTests : FunSpec({ + + test("helloworld") { + val result = Qux.hello() + result shouldBe "Hello World" + } +}) diff --git a/example/kotlinlib/testing/3-integration-suite/qux/src/qux/Qux.kt b/example/kotlinlib/testing/3-integration-suite/qux/src/qux/Qux.kt new file mode 100644 index 00000000000..22f67ee7f70 --- /dev/null +++ b/example/kotlinlib/testing/3-integration-suite/qux/src/qux/Qux.kt @@ -0,0 +1,7 @@ +package qux + +object Qux { + fun main(args: Array) = println(hello()) + + fun hello(): String = "Hello World" +} diff --git a/example/kotlinlib/testing/3-integration-suite/qux/test/src/qux/QuxTests.kt b/example/kotlinlib/testing/3-integration-suite/qux/test/src/qux/QuxTests.kt new file mode 100644 index 00000000000..554df690eec --- /dev/null +++ b/example/kotlinlib/testing/3-integration-suite/qux/test/src/qux/QuxTests.kt @@ -0,0 +1,19 @@ +package qux + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldStartWith +import io.kotest.matchers.string.shouldEndWith + +class QuxTests : FunSpec({ + + test("hello") { + val result = Qux.hello() + result shouldStartWith "Hello" + } + + test("world") { + val result = Qux.hello() + result shouldEndWith "World" + } +}) diff --git a/example/package.mill b/example/package.mill index a00c8b22495..385eed474bb 100644 --- a/example/package.mill +++ b/example/package.mill @@ -42,6 +42,8 @@ object `package` extends RootModule with Module { object builds extends Cross[ExampleCrossModuleKotlin](build.listIn(millSourcePath / "builds")) object linting extends Cross[ExampleCrossModuleKotlin](build.listIn(millSourcePath / "linting")) object module extends Cross[ExampleCrossModuleKotlin](build.listIn(millSourcePath / "module")) + object publishing extends Cross[ExampleCrossModuleKotlin](build.listIn(millSourcePath / "publishing")) + object testing extends Cross[ExampleCrossModuleKotlin](build.listIn(millSourcePath / "testing")) } object scalalib extends Module { object basic extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "basic")) @@ -77,6 +79,16 @@ object `package` extends RootModule with Module { .replace("bar.BarTests.escaping", "bar.BarTestsescaping") case "4-builtin-commands" => line.replace("compile.dest/zinc", "compile.dest/kotlin.analysis.dummy") case "5-resources" => line.replace("FooTests.simple", "FooTestssimple") + case "1-test-suite" => line + .replace("mill bar.test bar.BarTests.hello", "kotest_filter_tests='hello' kotest_filter_specs='bar.BarTests' ./mill bar.test") + .replace("FooTests.hello", "FooTestshello") + .replace("FooTests.world", "FooTestsworld") + .replace("BarTests.hello", "BarTestshello") + .replace("BarTests.world", "BarTestsworld") + .replace("compiling 1 ... source...", "Compiling 1 ... source...") + case "2-test-deps" => line + .replace("qux.QuxTests.simple", "qux.QuxTestssimple") + .replace("baz.BazTests.simple", "baz.BazTestssimple") case _ => line } }