From 245409dede405582ee4b02f719571179a73df512 Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Wed, 24 Jan 2024 12:43:51 +0000 Subject: [PATCH] [JS] Support custom Promise declarations for kotlin.test @AfterTest ^KT-63359 fixed Merge-request: KT-MR-14001 Merged-by: Anton Bannykh --- .../ir/backend/js/lower/TestGenerator.kt | 35 +++++++++++++------ .../kotlin.test/js/it/js/expected-outcomes.js | 1 + .../js/it/src/test/kotlin/MainTest.kt | 26 ++++++++++---- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt index 470bfac39a4..395a707b29b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt @@ -14,19 +14,26 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder +import org.jetbrains.kotlin.ir.backend.js.utils.getJsModule +import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.irString import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrBlockBody +import org.jetbrains.kotlin.ir.expressions.IrDynamicOperator import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrDynamicMemberExpressionImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrDynamicOperatorExpressionImpl +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.asSimpleType import org.jetbrains.kotlin.utils.filterIsInstanceAnd import org.jetbrains.kotlin.utils.findIsInstanceAnd import org.jetbrains.kotlin.utils.memoryOptimizedMap @@ -65,7 +72,7 @@ class TestGenerator(val context: JsCommonBackendContext, val groupByPackage: Boo private fun IrSimpleFunctionSymbol.createInvocation( name: String, parentFunction: IrSimpleFunction, - ignored: Boolean = false + ignored: Boolean = false, ): IrSimpleFunction { val body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET, emptyList()) @@ -134,7 +141,7 @@ class TestGenerator(val context: JsCommonBackendContext, val groupByPackage: Boo beforeFuns: List, afterFuns: List, irClass: IrClass, - parentFunction: IrSimpleFunction + parentFunction: IrSimpleFunction, ) { val fn = context.testFun!!.createInvocation(testFun.name.asString(), parentFunction, testFun.isIgnored) val body = fn.body as IrBlockBody @@ -179,10 +186,8 @@ class TestGenerator(val context: JsCommonBackendContext, val groupByPackage: Boo return } - if (context is JsIrBackendContext && (testFun.returnType as? IrSimpleType)?.classifier == context.intrinsics.promiseClassSymbol) { - val finally = context.intrinsics.promiseClassSymbol.owner.declarations - .findIsInstanceAnd { it.name.asString() == "finally" }!! + if (context is JsIrBackendContext && (testFun.returnType as? IrSimpleType)?.isPromise == true) { val refType = IrSimpleTypeImpl(context.ir.symbols.functionN(0), false, emptyList(), emptyList()) val afterFunction = context.irFactory.buildFun { @@ -204,12 +209,14 @@ class TestGenerator(val context: JsCommonBackendContext, val groupByPackage: Boo val finallyLambda = JsIrBuilder.buildFunctionExpression(refType, afterFunction) - val returnValue = JsIrBuilder.buildCall( - finally.symbol - ).apply { - this.dispatchReceiver = returnStatement.value - putValueArgument(0, finallyLambda) - } + val finally = + IrDynamicMemberExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.dynamicType, "finally", returnStatement.value) + + val returnValue = + IrDynamicOperatorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.dynamicType, IrDynamicOperator.INVOKE).also { + it.receiver = finally + it.arguments.add(finallyLambda) + } body.statements += JsIrBuilder.buildReturn( fn.symbol, @@ -261,4 +268,10 @@ class TestGenerator(val context: JsCommonBackendContext, val groupByPackage: Boo private fun IrAnnotationContainer.hasAnnotation(fqName: String) = annotations.any { it.symbol.owner.fqNameWhenAvailable?.parent()?.asString() == fqName } + + private val IrSimpleType.isPromise: Boolean + get() { + val klass = classifier.owner as? IrClass ?: return false + return klass.isExternal && klass.getJsModule() == null && klass.getJsNameOrKotlinName().asString() == "Promise" + } } diff --git a/libraries/kotlin.test/js/it/js/expected-outcomes.js b/libraries/kotlin.test/js/it/js/expected-outcomes.js index dd44289b2fc..d439415ca6c 100644 --- a/libraries/kotlin.test/js/it/js/expected-outcomes.js +++ b/libraries/kotlin.test/js/it/js/expected-outcomes.js @@ -4,6 +4,7 @@ module.exports = { 'SimpleTest testFooWrong': 'pending', 'TestTest emptyTest': 'pending', 'AsyncTest checkAsyncOrder': 'pass', + 'AsyncTest checkCustomPromise': 'pass', 'AsyncTest asyncPassing': 'pass', 'AsyncTest asyncFailing': 'fail', 'org OrgTest test': 'pass', diff --git a/libraries/kotlin.test/js/it/src/test/kotlin/MainTest.kt b/libraries/kotlin.test/js/it/src/test/kotlin/MainTest.kt index e73a1bf4d91..c37a9ca1913 100644 --- a/libraries/kotlin.test/js/it/src/test/kotlin/MainTest.kt +++ b/libraries/kotlin.test/js/it/src/test/kotlin/MainTest.kt @@ -46,28 +46,33 @@ class AsyncTest { var afterLog = "" + var expectedAfterLog = "" + @BeforeTest fun before() { log = "" + afterLog = "" + expectedAfterLog = "" } - // Until bootstrap update @AfterTest fun after() { - assertEquals(afterLog, "after") + assertEquals(afterLog, expectedAfterLog) } - fun promise(v: Int) = Promise { resolve, reject -> + fun promise(v: Int, after: String = "") = Promise { resolve, reject -> log += "a" - js("setTimeout")({ log += "c"; afterLog += "after"; resolve(v) }, 100) + js("setTimeout")({ log += "c"; afterLog += after; resolve(v) }, 100) log += "b" + }.also { + expectedAfterLog += after } @Test fun checkAsyncOrder(): Promise { log += 1 - val p1 = promise(10) + val p1 = promise(10, "after") log += 2 @@ -80,9 +85,18 @@ class AsyncTest { return p2 } + @Test + fun checkCustomPromise(): CustomPromise { +// return promise(10, "afterCustom") as CustomPromise + return promise(10, "") as CustomPromise + } + @Test fun asyncPassing() = promise(10).then { assertEquals(10, it) } @Test fun asyncFailing() = promise(20).then { assertEquals(10, it) } -} \ No newline at end of file +} + +@JsName("Promise") +external class CustomPromise {} \ No newline at end of file