diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index 364a269e5f8..19c2b374d99 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -5439,6 +5439,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { runTest("js/js.translator/testData/box/kotlin.test/ignore.kt"); } + @TestMetadata("incremental.kt") + public void testIncremental() throws Exception { + runTest("js/js.translator/testData/box/kotlin.test/incremental.kt"); + } + @TestMetadata("inherited.kt") public void testInherited() throws Exception { runTest("js/js.translator/testData/box/kotlin.test/inherited.kt"); @@ -5550,6 +5555,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/main"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("incremental.kt") + public void testIncremental() throws Exception { + runTest("js/js.translator/testData/box/main/incremental.kt"); + } + @TestMetadata("noArgs.kt") public void testNoArgs() throws Exception { runTest("js/js.translator/testData/box/main/noArgs.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java index 652d1725643..9b7e6fd8f57 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java @@ -5439,6 +5439,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { runTest("js/js.translator/testData/box/kotlin.test/ignore.kt"); } + @TestMetadata("incremental.kt") + public void testIncremental() throws Exception { + runTest("js/js.translator/testData/box/kotlin.test/incremental.kt"); + } + @TestMetadata("inherited.kt") public void testInherited() throws Exception { runTest("js/js.translator/testData/box/kotlin.test/inherited.kt"); @@ -5550,6 +5555,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/main"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS_IR, true); } + @TestMetadata("incremental.kt") + public void testIncremental() throws Exception { + runTest("js/js.translator/testData/box/main/incremental.kt"); + } + @TestMetadata("noArgs.kt") public void testNoArgs() throws Exception { runTest("js/js.translator/testData/box/main/noArgs.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt index c08450df7c8..6c638a57b62 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt @@ -66,8 +66,15 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam private fun JsProgramFragment.tryUpdateTests() { tests?.let { newTests -> - testsMap.putIfAbsent(packageFqn, newTests)?.let { oldTests -> - oldTests.decomposeTestInvocation()?.let {oldTestBody -> + testsMap.computeIfAbsent(packageFqn) { + // Copying is needed to prevent adding tests from another fragment into this one. + // The statements have to be original so that they are affected by the optimizations + // This is a temporary workaround which becomes obsolete when program construction is postponed until after IC serialization. + newTests.deepCopy().also { + it.decomposeTestInvocation()?.statements?.clear() + } + }.let { oldTests -> + oldTests.decomposeTestInvocation()?.let { oldTestBody -> newTests.decomposeTestInvocation()?.let { newTestBody -> oldTestBody.statements += newTestBody.statements } @@ -79,7 +86,7 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam private fun JsStatement.decomposeTestInvocation(): JsBlock? { return (this as? JsExpressionStatement)?.let { (it.expression as? JsInvocation)?.let { - (it.arguments[2] as? JsFunction)?.body + (it.arguments.getOrNull(2) as? JsFunction)?.body } } } @@ -133,12 +140,10 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam if (exportedPackage in exportedPackages) { nameMap[localName] = exportedPackages[exportedPackage]!! continue - } - else { + } else { exportedPackages[exportedPackage] = localName } - } - else if (statement is JsExpressionStatement) { + } else if (statement is JsExpressionStatement) { val exportedTag = statement.exportedTag if (exportedTag != null && !exportedTags.add(exportedTag)) continue } @@ -174,7 +179,7 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam fragment.mainFunction?.let { rename(it) } } - private fun Map.rename(rootNode: T): T { + private fun Map.rename(rootNode: T): T { rootNode.accept(object : RecursiveJsVisitor() { override fun visitElement(node: JsNode) { super.visitElement(node) @@ -187,8 +192,8 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam val coroutineMetadata = node.coroutineMetadata if (coroutineMetadata != null) { node.coroutineMetadata = coroutineMetadata.copy( - baseClassRef = rename(coroutineMetadata.baseClassRef), - suspendObjectRef = rename(coroutineMetadata.suspendObjectRef) + baseClassRef = rename(coroutineMetadata.baseClassRef), + suspendObjectRef = rename(coroutineMetadata.suspendObjectRef) ) } } @@ -217,8 +222,10 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam private fun MutableList.addImportForInlineDeclarationIfNecessary() { val importsForInlineName = nameTable[Namer.IMPORTS_FOR_INLINE_PROPERTY] ?: return - this += definePackageAlias(Namer.IMPORTS_FOR_INLINE_PROPERTY, importsForInlineName, Namer.IMPORTS_FOR_INLINE_PROPERTY, - JsNameRef(Namer.getRootPackageName())) + this += definePackageAlias( + Namer.IMPORTS_FOR_INLINE_PROPERTY, importsForInlineName, Namer.IMPORTS_FOR_INLINE_PROPERTY, + JsNameRef(Namer.getRootPackageName()) + ) } private fun addClassPrototypes(statements: MutableList) { @@ -229,9 +236,9 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam } private fun addClassPrototypes( - name: JsName, - visited: MutableSet, - statements: MutableList + name: JsName, + visited: MutableSet, + statements: MutableList ) { if (!visited.add(name)) return val cls = classes[name] ?: return @@ -250,9 +257,9 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam } private fun addClassPostDeclarations( - name: JsName, - visited: MutableSet, - statements: MutableList + name: JsName, + visited: MutableSet, + statements: MutableList ) { if (!visited.add(name)) return val cls = classes[name] ?: return diff --git a/js/js.translator/testData/box/kotlin.test/_common.kt b/js/js.translator/testData/box/kotlin.test/_common.kt index 3aa9734f552..e5e2fa02afc 100644 --- a/js/js.translator/testData/box/kotlin.test/_common.kt +++ b/js/js.translator/testData/box/kotlin.test/_common.kt @@ -1,3 +1,5 @@ +package common + import kotlin.test.FrameworkAdapter private val context = TestContext() @@ -98,10 +100,14 @@ class TestContext { private fun optionalIgnore(ignored: Boolean) = if (ignored) ", true" else "" } -fun checkLog(body: TestContext.() -> Unit): String { +fun checkLog(wrapInEmptySuite: Boolean = true, body: TestContext.() -> Unit): String { val expectedContext = TestContext() - expectedContext.suite("") { - body() + if (wrapInEmptySuite) { + expectedContext.suite("") { + body() + } + } else { + expectedContext.body() } if (context.log != expectedContext.log) { return "Failed test structure check. Expected: ${expectedContext.log}; actual: ${context.log}." diff --git a/js/js.translator/testData/box/kotlin.test/beforeAfter.kt b/js/js.translator/testData/box/kotlin.test/beforeAfter.kt index ebf273dae43..3d54a47996a 100644 --- a/js/js.translator/testData/box/kotlin.test/beforeAfter.kt +++ b/js/js.translator/testData/box/kotlin.test/beforeAfter.kt @@ -1,5 +1,6 @@ // IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1339 +import common.* import kotlin.test.Test import kotlin.test.BeforeTest import kotlin.test.AfterTest diff --git a/js/js.translator/testData/box/kotlin.test/ignore.kt b/js/js.translator/testData/box/kotlin.test/ignore.kt index 24beb124155..ff0577967d2 100644 --- a/js/js.translator/testData/box/kotlin.test/ignore.kt +++ b/js/js.translator/testData/box/kotlin.test/ignore.kt @@ -1,5 +1,6 @@ // IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1340 +import common.* import kotlin.test.Test import kotlin.test.Ignore diff --git a/js/js.translator/testData/box/kotlin.test/incremental.kt b/js/js.translator/testData/box/kotlin.test/incremental.kt new file mode 100644 index 00000000000..63ed88c2608 --- /dev/null +++ b/js/js.translator/testData/box/kotlin.test/incremental.kt @@ -0,0 +1,381 @@ +// IGNORE_BACKEND: JS_IR +// EXPECTED_REACHABLE_NODES: 1330 + +// FILE: a.kt +package a + +import common.* +import kotlin.test.* + +class A { + @BeforeTest + fun before() { + call("a.A.before") + } + + @AfterTest + fun after() { + call("a.A.after") + } + + @Test + fun passing() { + call("a.A.passing") + } + + @Test + fun failing() { + call("a.A.failing") + raise("a.A.failing.exception") + call("never happens") + } + + @Ignore + @Test + fun ignored() { + call("a.A.ignored") + } + + @Test + fun withException() { + call("withException") + raise("some exception") + call("never happens") + } + + inner class Inner { + @Test + fun innerTest() { + call("a.A.Inner.innerTest") + } + } + + class Nested { + @Test + fun nestedTest() { + call("a.A.Nested.nestedTest") + } + } + + companion object { + @Test + fun companionTest() { + call("a.A.companionTest") + } + } +} + + +object O { + @Test + fun test() { + call("a.O.test") + } +} + +// FILE: a_a.kt +package a.a + +import common.* +import kotlin.test.* + +class A { + @BeforeTest + fun before() { + call("a.a.A.before") + } + + @AfterTest + fun after() { + call("a.a.A.after") + } + + @Test + fun passing() { + call("a.a.A.passing") + } + + @Test + fun failing() { + call("a.a.A.failing") + raise("a.a.A.failing.exception") + call("never happens") + } + + @Ignore + @Test + fun ignored() { + call("a.a.A.ignored") + } + + @Test + fun withException() { + call("withException") + raise("some exception") + call("never happens") + } + + inner class Inner { + @Test + fun innerTest() { + call("a.a.A.Inner.innerTest") + } + } + + class Nested { + @Test + fun nestedTest() { + call("a.a.A.Nested.nestedTest") + } + } + + companion object { + @Test + fun companionTest() { + call("a.a.A.companionTest") + } + } +} + +object O { + @Test + fun test() { + call("a.a.O.test") + } +} + +// FILE: a_a2.kt +// RECOMPILE +package a.a + +import common.* +import kotlin.test.* + +class B { + @BeforeTest + fun before() { + call("a.a.B.before") + } + + @AfterTest + fun after() { + call("a.a.B.after") + } + + @Test + fun passing() { + call("a.a.B.passing") + } + + @Test + fun failing() { + call("a.a.B.failing") + raise("a.a.B.failing.exception") + call("never happens") + } + + @Ignore + @Test + fun ignored() { + call("a.a.B.ignored") + } + + @Test + fun withException() { + call("withException") + raise("some exception") + call("never happens") + } + + inner class Inner { + @Test + fun innerTest() { + call("a.a.B.Inner.innerTest") + } + } + + class Nested { + @Test + fun nestedTest() { + call("a.a.B.Nested.nestedTest") + } + } + + companion object { + @Test + fun companionTest() { + call("a.a.B.companionTest") + } + } +} + + +object O2 { + @Test + fun test() { + call("a.a.O2.test") + } +} + +// FILE: main.kt + +import common.* +import kotlin.test.Test + +class Simple { + @Test fun foo() { + call("foo") + } +} + +fun box() = checkLog(false) { + suite("a") { + suite("A") { + test("passing") { + call("a.A.before") + call("a.A.passing") + call("a.A.after") + } + test("failing") { + call("a.A.before") + call("a.A.failing") + raised("a.A.failing.exception") + call("a.A.after") + caught("a.A.failing.exception") + } + test("ignored", true) { + call("a.A.before") + call("a.A.ignored") + call("a.A.after") + } + test("withException") { + call("a.A.before") + call("withException") + raised("some exception") + call("a.A.after") + caught("some exception") + } + suite("Inner") { + test("innerTest") { + call("a.A.Inner.innerTest") + } + } + suite("Nested") { + test("nestedTest") { + call("a.A.Nested.nestedTest") + } + } + suite("Companion") { + test("companionTest") { + call("a.A.companionTest") + } + } + } + suite("O") { + test("test") { + call("a.O.test") + } + } + } + suite("a.a") { + suite("A") { + test("passing") { + call("a.a.A.before") + call("a.a.A.passing") + call("a.a.A.after") + } + test("failing") { + call("a.a.A.before") + call("a.a.A.failing") + raised("a.a.A.failing.exception") + call("a.a.A.after") + caught("a.a.A.failing.exception") + } + test("ignored", true) { + call("a.a.A.before") + call("a.a.A.ignored") + call("a.a.A.after") + } + test("withException") { + call("a.a.A.before") + call("withException") + raised("some exception") + call("a.a.A.after") + caught("some exception") + } + suite("Inner") { + test("innerTest") { + call("a.a.A.Inner.innerTest") + } + } + suite("Nested") { + test("nestedTest") { + call("a.a.A.Nested.nestedTest") + } + } + suite("Companion") { + test("companionTest") { + call("a.a.A.companionTest") + } + } + } + suite("O") { + test("test") { + call("a.a.O.test") + } + } + suite("B") { + test("passing") { + call("a.a.B.before") + call("a.a.B.passing") + call("a.a.B.after") + } + test("failing") { + call("a.a.B.before") + call("a.a.B.failing") + raised("a.a.B.failing.exception") + call("a.a.B.after") + caught("a.a.B.failing.exception") + } + test("ignored", true) { + call("a.a.B.before") + call("a.a.B.ignored") + call("a.a.B.after") + } + test("withException") { + call("a.a.B.before") + call("withException") + raised("some exception") + call("a.a.B.after") + caught("some exception") + } + suite("Inner") { + test("innerTest") { + call("a.a.B.Inner.innerTest") + } + } + suite("Nested") { + test("nestedTest") { + call("a.a.B.Nested.nestedTest") + } + } + suite("Companion") { + test("companionTest") { + call("a.a.B.companionTest") + } + } + } + suite("O2") { + test("test") { + call("a.a.O2.test") + } + } + } + suite("") { + suite("Simple") { + test("foo") { + call("foo") + } + } + } +} \ No newline at end of file diff --git a/js/js.translator/testData/box/kotlin.test/inherited.kt b/js/js.translator/testData/box/kotlin.test/inherited.kt index 088aa822131..51d1ec31379 100644 --- a/js/js.translator/testData/box/kotlin.test/inherited.kt +++ b/js/js.translator/testData/box/kotlin.test/inherited.kt @@ -1,5 +1,6 @@ // IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1351 +import common.* import kotlin.test.Test import kotlin.test.BeforeTest import kotlin.test.AfterTest diff --git a/js/js.translator/testData/box/kotlin.test/mpp.kt b/js/js.translator/testData/box/kotlin.test/mpp.kt index 88e8d9772ad..4f1c2b85414 100644 --- a/js/js.translator/testData/box/kotlin.test/mpp.kt +++ b/js/js.translator/testData/box/kotlin.test/mpp.kt @@ -10,6 +10,7 @@ expect class PlatformTest { } // FILE: main.kt +import common.* import kotlin.test.Test actual class PlatformTest { diff --git a/js/js.translator/testData/box/kotlin.test/nested.kt b/js/js.translator/testData/box/kotlin.test/nested.kt index 1f39d8227e1..fd170a2599a 100644 --- a/js/js.translator/testData/box/kotlin.test/nested.kt +++ b/js/js.translator/testData/box/kotlin.test/nested.kt @@ -1,5 +1,6 @@ // IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1367 +import common.* import kotlin.test.Test class Outer { diff --git a/js/js.translator/testData/box/kotlin.test/returnTestResult.kt b/js/js.translator/testData/box/kotlin.test/returnTestResult.kt index 76f02b3e427..4d2e54b301a 100644 --- a/js/js.translator/testData/box/kotlin.test/returnTestResult.kt +++ b/js/js.translator/testData/box/kotlin.test/returnTestResult.kt @@ -1,5 +1,6 @@ // IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1297 +import common.* import kotlin.test.Test import kotlin.test.BeforeTest import kotlin.test.AfterTest diff --git a/js/js.translator/testData/box/kotlin.test/simple.kt b/js/js.translator/testData/box/kotlin.test/simple.kt index 51a21e624ad..4b91019f36a 100644 --- a/js/js.translator/testData/box/kotlin.test/simple.kt +++ b/js/js.translator/testData/box/kotlin.test/simple.kt @@ -1,5 +1,6 @@ // IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1330 +import common.* import kotlin.test.Test class Simple { diff --git a/js/js.translator/testData/box/main/incremental.kt b/js/js.translator/testData/box/main/incremental.kt new file mode 100644 index 00000000000..4ff73580c84 --- /dev/null +++ b/js/js.translator/testData/box/main/incremental.kt @@ -0,0 +1,46 @@ +// EXPECTED_REACHABLE_NODES: 1281 +// IGNORE_BACKEND: JS_IR +// CALL_MAIN + +// FILE: ok.kt + +package ok + +var ok: String = "fail" + +// FILE: 1.kt +// RECOMPILE + +package a.a + +import ok.* + +fun main(args: Array) { + ok = "fail: b.b" +} + +// FILE: 2.kt + +package a + +import ok.* + +fun main(args: Array) { + ok = "OK" +} + +// FILE: 3.kt + +package a.b + +import ok.* + +fun main(args: Array) { + ok = "fail: a.b" +} + +// FILE: main.kt + +import ok.* + +fun box() = ok \ No newline at end of file