diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
deleted file mode 100644
index 797acea53eb..00000000000
--- a/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/libraries/kotlin.test/build.gradle.kts b/libraries/kotlin.test/build.gradle.kts
index fc7659823e8..074e9d9a39c 100644
--- a/libraries/kotlin.test/build.gradle.kts
+++ b/libraries/kotlin.test/build.gradle.kts
@@ -55,6 +55,24 @@ val jsRuntimeVariant by configurations.creating {
extendsFrom(jsApiVariant)
}
+val wasmApiVariant by configurations.creating {
+ isCanBeConsumed = true
+ isCanBeResolved = false
+ attributes {
+ attribute(Usage.USAGE_ATTRIBUTE, objects.named("kotlin-api"))
+ attribute(KotlinPlatformType.attribute, KotlinPlatformType.wasm)
+ }
+}
+val wasmRuntimeVariant by configurations.creating {
+ isCanBeConsumed = true
+ isCanBeResolved = false
+ attributes {
+ attribute(Usage.USAGE_ATTRIBUTE, objects.named("kotlin-runtime"))
+ attribute(KotlinPlatformType.attribute, KotlinPlatformType.wasm)
+ }
+ extendsFrom(wasmApiVariant)
+}
+
val nativeApiVariant by configurations.creating {
isCanBeConsumed = true
isCanBeResolved = false
@@ -76,6 +94,7 @@ val commonVariant by configurations.creating {
dependencies {
jvmApi(project(":kotlin-stdlib"))
jsApiVariant("$group:kotlin-test-js:$version")
+ wasmApiVariant("$group:kotlin-test-wasm:$version")
commonVariant("$group:kotlin-test-common:$version")
commonVariant("$group:kotlin-test-annotations-common:$version")
}
@@ -114,6 +133,8 @@ val rootComponent = componentFactory.adhoc("root").apply {
}
addVariantsFromConfiguration(jsApiVariant) { mapToOptional() }
addVariantsFromConfiguration(jsRuntimeVariant) { mapToOptional() }
+ addVariantsFromConfiguration(wasmApiVariant) { mapToOptional() }
+ addVariantsFromConfiguration(wasmRuntimeVariant) { mapToOptional() }
addVariantsFromConfiguration(nativeApiVariant) { mapToOptional() }
addVariantsFromConfiguration(commonVariant) { mapToOptional() }
}
@@ -236,6 +257,38 @@ val jsComponent = componentFactory.adhoc("js").apply {
}
}
+val (wasmApi, wasmRuntime) = listOf("api", "runtime").map { usage ->
+ configurations.create("wasm${usage.capitalize()}") {
+ isCanBeConsumed = true
+ isCanBeResolved = false
+ attributes {
+ attribute(Usage.USAGE_ATTRIBUTE, objects.named("kotlin-$usage"))
+ attribute(KotlinPlatformType.attribute, KotlinPlatformType.js)
+ }
+ }
+}
+wasmRuntime.extendsFrom(wasmApi)
+
+dependencies {
+ wasmApi(project(":kotlin-stdlib-wasm"))
+}
+
+artifacts {
+ val wasmJar = tasks.getByPath(":kotlin-test:kotlin-test-wasm:jsJar")
+ add(wasmApi.name, wasmJar)
+ add(wasmRuntime.name, wasmJar)
+}
+
+val wasmComponent = componentFactory.adhoc("wasm").apply {
+ addVariantsFromConfiguration(wasmApi) {
+ mapToMavenScope("compile")
+ }
+ addVariantsFromConfiguration(wasmRuntime) {
+ mapToMavenScope("runtime")
+ }
+}
+
+
val commonMetadata by configurations.creating {
isCanBeConsumed = true
isCanBeResolved = false
@@ -306,6 +359,12 @@ publishing {
artifact(tasks.getByPath(":kotlin-test:kotlin-test-js:sourcesJar") as Jar)
configureKotlinPomAttributes(project, "Kotlin Test for JS")
}
+ create("wasm", MavenPublication::class) {
+ artifactId = "kotlin-test-wasm"
+ from(wasmComponent)
+ // TODO: artifact(tasks.getByPath(":kotlin-test:kotlin-test-wasm:jsSourcesJar"))
+ configureKotlinPomAttributes(project, "Kotlin Test for WASM")
+ }
create("common", MavenPublication::class) {
artifactId = "kotlin-test-common"
from(commonMetadataComponent)
diff --git a/libraries/kotlin.test/wasm/build.gradle.kts b/libraries/kotlin.test/wasm/build.gradle.kts
new file mode 100644
index 00000000000..3cb45389269
--- /dev/null
+++ b/libraries/kotlin.test/wasm/build.gradle.kts
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
+
+plugins {
+ kotlin("multiplatform")
+}
+
+val commonMainSources by task {
+ from(
+ "$rootDir/libraries/kotlin.test/common/src",
+ "$rootDir/libraries/kotlin.test/annotations-common/src"
+ )
+ into("$buildDir/commonMainSources")
+}
+
+//val commonTestSources by task {
+// from("$rootDir/libraries/kotlin.test/common/src/test/kotlin")
+// into("$buildDir/commonTestSources")
+//}
+
+kotlin {
+ js(IR) {
+ nodejs()
+ }
+
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ api(project(":kotlin-stdlib-wasm"))
+ }
+ kotlin.srcDir(commonMainSources.get().destinationDir)
+ }
+// val commonTest by getting {
+// kotlin.srcDir(commonTestSources.get().destinationDir)
+// }
+ val jsMain by getting {
+ dependencies {
+ api(project(":kotlin-stdlib-wasm"))
+ }
+ kotlin.srcDir("$rootDir/libraries/kotlin.test/wasm/src")
+ }
+ }
+}
+
+tasks.withType>().configureEach {
+ kotlinOptions.freeCompilerArgs += listOf(
+ "-Xallow-kotlin-package",
+ "-opt-in=kotlin.ExperimentalMultiplatform",
+ "-opt-in=kotlin.contracts.ExperimentalContracts"
+ )
+}
+
+tasks.named("compileKotlinJs") {
+ (this as KotlinCompile<*>).kotlinOptions.freeCompilerArgs += "-Xir-module-name=kotlin-test"
+ dependsOn(commonMainSources)
+}
+
+//tasks.named("compileTestKotlinJs") {
+// dependsOn(commonTestSources)
+//}
diff --git a/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/Annotations.kt b/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/Annotations.kt
new file mode 100644
index 00000000000..cf4cc2bda6a
--- /dev/null
+++ b/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/Annotations.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package kotlin.test
+
+/**
+ * Marks a function as a test.
+ */
+@Target(AnnotationTarget.FUNCTION)
+public actual annotation class Test
+
+/**
+ * Marks a test or a suite as ignored.
+ */
+@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
+public actual annotation class Ignore
+
+/**
+ * Marks a function to be invoked before each test.
+ */
+@Target(AnnotationTarget.FUNCTION)
+public actual annotation class BeforeTest
+
+/**
+ * Marks a function to be invoked after each test.
+ */
+@Target(AnnotationTarget.FUNCTION)
+public actual annotation class AfterTest
diff --git a/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/BareAdapter.kt b/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/BareAdapter.kt
new file mode 100644
index 00000000000..0787038140d
--- /dev/null
+++ b/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/BareAdapter.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package kotlin.test
+
+/**
+ * A fallback adapter for the case when no framework is detected.
+ */
+internal open class BareAdapter : FrameworkAdapter {
+
+ override fun suite(name: String, ignored: Boolean, suiteFn: () -> Unit) {
+ if (!ignored) {
+ suiteFn()
+ }
+ }
+
+ override fun test(name: String, ignored: Boolean, testFn: () -> Any?) {
+ if (!ignored) {
+ testFn()
+ }
+ }
+}
diff --git a/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/DefaultWasmAsserter.kt b/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/DefaultWasmAsserter.kt
new file mode 100644
index 00000000000..19a16ab6fe9
--- /dev/null
+++ b/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/DefaultWasmAsserter.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package kotlin.test
+
+/**
+ * Describes the result of an assertion execution.
+ */
+public interface AssertionResult {
+ val result: Boolean
+ val expected: Any?
+ val actual: Any?
+ val lazyMessage: () -> String?
+}
+
+internal var assertHook: (AssertionResult) -> Unit = { _ -> }
+
+internal object DefaultWasmAsserter : Asserter {
+ private var e: Any? = null
+ private var a: Any? = null
+
+ override fun assertEquals(message: String?, expected: Any?, actual: Any?) {
+ e = expected
+ a = actual
+ super.assertEquals(message, expected, actual)
+ }
+
+ override fun assertNotEquals(message: String?, illegal: Any?, actual: Any?) {
+ e = illegal
+ a = actual
+ super.assertNotEquals(message, illegal, actual)
+ }
+
+ override fun assertSame(message: String?, expected: Any?, actual: Any?) {
+ e = expected
+ a = actual
+ super.assertSame(message, expected, actual)
+ }
+
+ override fun assertNotSame(message: String?, illegal: Any?, actual: Any?) {
+ e = illegal
+ a = actual
+ super.assertNotSame(message, illegal, actual)
+ }
+
+ override fun assertNull(message: String?, actual: Any?) {
+ a = actual
+ super.assertNull(message, actual)
+ }
+
+ override fun assertNotNull(message: String?, actual: Any?) {
+ a = actual
+ super.assertNotNull(message, actual)
+ }
+
+ override fun assertTrue(lazyMessage: () -> String?, actual: Boolean) {
+ if (!actual) {
+ failWithMessage(lazyMessage, null)
+ } else {
+ invokeHook(true, lazyMessage)
+ }
+ }
+
+ override fun assertTrue(message: String?, actual: Boolean) {
+ assertTrue({ message }, actual)
+ }
+
+ override fun fail(message: String?): Nothing {
+ fail(message, null)
+ }
+
+ @SinceKotlin("1.4")
+ override fun fail(message: String?, cause: Throwable?): Nothing {
+ failWithMessage({ message }, cause)
+ }
+
+ private inline fun failWithMessage(lazyMessage: () -> String?, cause: Throwable?): Nothing {
+ val message = lazyMessage()
+ invokeHook(false) { message }
+ throw AssertionErrorWithCause(message, cause)
+ }
+
+ private fun invokeHook(result: Boolean, lazyMessage: () -> String?) {
+ try {
+ assertHook(object : AssertionResult {
+ override val result: Boolean = result
+ override val expected: Any? = e
+ override val actual: Any? = a
+ override val lazyMessage: () -> String? = lazyMessage
+ })
+ } finally {
+ e = null
+ a = null
+ }
+ }
+}
\ No newline at end of file
diff --git a/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/FrameworkAdapter.kt b/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/FrameworkAdapter.kt
new file mode 100644
index 00000000000..6ed7616b493
--- /dev/null
+++ b/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/FrameworkAdapter.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package kotlin.test
+
+/**
+ * Serves as a bridge to a testing framework.
+ *
+ * The tests structure is defined using internal functions suite and test, which delegate to corresponding functions of a [FrameworkAdapter].
+ * Sample test layout:
+ *
+ * ```
+ * suite('a suite', false, function() {
+ * suite('a subsuite', false, function() {
+ * test('a test', false, function() {...});
+ * test('an ignored/pending test', true, function() {...});
+ * });
+ * suite('an ignored/pending test', true, function() {...});
+ * });
+ * ```
+ *
+ */
+public interface FrameworkAdapter {
+
+ /**
+ * Declares a test suite.
+ *
+ * @param name the name of the test suite, e.g. a class name
+ * @param ignored whether the test suite is ignored, e.g. marked with [Ignore] annotation
+ * @param suiteFn defines nested suites by calling [kotlin.test.suite] and tests by calling [kotlin.test.test]
+ */
+ fun suite(name: String, ignored: Boolean, suiteFn: () -> Unit)
+
+ /**
+ * Declares a test.
+ *
+ * @param name the test name.
+ * @param ignored whether the test is ignored
+ * @param testFn contains test body invocation
+ */
+ fun test(name: String, ignored: Boolean, testFn: () -> Any?)
+}
diff --git a/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/TestApi.kt b/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/TestApi.kt
new file mode 100644
index 00000000000..c5636268ebc
--- /dev/null
+++ b/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/TestApi.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package kotlin.test
+
+import kotlin.js.JsExport
+
+/**
+ * Overrides current framework adapter with a provided instance of [FrameworkAdapter]. Use in order to support custom test frameworks.
+ *
+ * If this function is not called, the test framework will be detected automatically.
+ *
+ */
+internal fun setAdapter(adapter: FrameworkAdapter) {
+ currentAdapter = adapter
+}
+
+/**
+ * The functions below are used by the compiler to describe the tests structure, e.g.
+ *
+ * suite('a suite', false, function() {
+ * suite('a subsuite', false, function() {
+ * test('a test', false, function() {...});
+ * test('an ignored/pending test', true, function() {...});
+ * });
+ * suite('an ignored/pending test', true, function() {...});
+ * });
+ */
+
+internal fun suite(name: String, ignored: Boolean, suiteFn: () -> Unit) {
+ currentAdapter.suite(name, ignored, suiteFn)
+}
+
+internal fun test(name: String, ignored: Boolean, testFn: () -> Any?) {
+ currentAdapter.test(name, ignored, testFn)
+}
+
+internal var currentAdapter: FrameworkAdapter = BareAdapter()
+
+// This is called from the js-launcher alongside wasm start function
+@JsExport
+internal fun startUnitTests() {
+ // This will be filled with the corresponding code during lowering
+}
diff --git a/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/WasmImpl.kt b/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/WasmImpl.kt
new file mode 100644
index 00000000000..f6f6fddc77e
--- /dev/null
+++ b/libraries/kotlin.test/wasm/src/main/kotlin/kotlin/test/WasmImpl.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package kotlin.test
+
+import kotlin.reflect.KClass
+
+/**
+ * Takes the given [block] of test code and _doesn't_ execute it.
+ *
+ * This keeps the code under test referenced, but doesn't actually test it until it is implemented.
+ */
+actual fun todo(block: () -> Unit) {
+ println("TODO at " + block)
+}
+
+/** Platform-specific construction of AssertionError with cause */
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun AssertionErrorWithCause(message: String?, cause: Throwable?): AssertionError =
+ AssertionError(message, cause)
+
+
+@PublishedApi
+internal actual fun checkResultIsFailure(exceptionClass: KClass, message: String?, blockResult: Result): T {
+ blockResult.fold(
+ onSuccess = {
+ asserter.fail(messagePrefix(message) + "Expected an exception of $exceptionClass to be thrown, but was completed successfully.")
+ },
+ onFailure = { e ->
+ if (exceptionClass.isInstance(e)) {
+ @Suppress("UNCHECKED_CAST")
+ return e as T
+ }
+ asserter.fail(messagePrefix(message) + "Expected an exception of $exceptionClass to be thrown, but was $e", e)
+ }
+ )
+}
+
+
+/**
+ * Provides the JS implementation of asserter
+ */
+internal actual fun lookupAsserter(): Asserter = DefaultWasmAsserter
\ No newline at end of file
diff --git a/libraries/stdlib/wasm/src/kotlin/Exceptions.kt b/libraries/stdlib/wasm/src/kotlin/Exceptions.kt
index 9ad26c4090a..bb7d647f830 100644
--- a/libraries/stdlib/wasm/src/kotlin/Exceptions.kt
+++ b/libraries/stdlib/wasm/src/kotlin/Exceptions.kt
@@ -66,7 +66,7 @@ public actual open class ClassCastException actual constructor(message: String?)
actual constructor() : this(null)
}
-public actual open class AssertionError private constructor(message: String?, cause: Throwable?) : Error(message, cause) {
+public actual open class AssertionError constructor(message: String?, cause: Throwable?) : Error(message, cause) {
actual constructor() : this(null)
constructor(message: String?) : this(message, null)
actual constructor(message: Any?) : this(message.toString(), message as? Throwable)
diff --git a/settings.gradle b/settings.gradle
index df59aca57ea..baa62e7f04b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -500,7 +500,8 @@ if (buildProperties.inJpsBuildIdeaSync) {
":kotlin-test",
":kotlin-test:kotlin-test-js",
":kotlin-test:kotlin-test-js-ir",
- ":kotlin-test:kotlin-test-js:kotlin-test-js-it"
+ ":kotlin-test:kotlin-test-js:kotlin-test-js-it",
+ ":kotlin-test:kotlin-test-wasm"
project(':kotlin-stdlib-common').projectDir = "$rootDir/libraries/stdlib/common" as File
project(':kotlin-stdlib').projectDir = "$rootDir/libraries/stdlib/jvm" as File
@@ -520,6 +521,7 @@ if (buildProperties.inJpsBuildIdeaSync) {
project(':kotlin-test:kotlin-test-js').projectDir = "$rootDir/libraries/kotlin.test/js" as File
project(':kotlin-test:kotlin-test-js-ir').projectDir = "$rootDir/libraries/kotlin.test/js-ir" as File
project(':kotlin-test:kotlin-test-js:kotlin-test-js-it').projectDir = "$rootDir/libraries/kotlin.test/js/it" as File
+ project(':kotlin-test:kotlin-test-wasm').projectDir = "$rootDir/libraries/kotlin.test/wasm" as File
}
include ":compiler:android-tests"