[JS] Support custom Promise declarations for kotlin.test @AfterTest

^KT-63359 fixed


Merge-request: KT-MR-14001
Merged-by: Anton Bannykh <Anton.Bannykh@jetbrains.com>
This commit is contained in:
Anton Bannykh
2024-01-24 12:43:51 +00:00
committed by Space Team
parent 9a74a5fcdd
commit 245409dede
3 changed files with 45 additions and 17 deletions
@@ -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.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder 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.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irString import org.jetbrains.kotlin.ir.builders.irString
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBlockBody 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.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl 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.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name 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.filterIsInstanceAnd
import org.jetbrains.kotlin.utils.findIsInstanceAnd import org.jetbrains.kotlin.utils.findIsInstanceAnd
import org.jetbrains.kotlin.utils.memoryOptimizedMap import org.jetbrains.kotlin.utils.memoryOptimizedMap
@@ -65,7 +72,7 @@ class TestGenerator(val context: JsCommonBackendContext, val groupByPackage: Boo
private fun IrSimpleFunctionSymbol.createInvocation( private fun IrSimpleFunctionSymbol.createInvocation(
name: String, name: String,
parentFunction: IrSimpleFunction, parentFunction: IrSimpleFunction,
ignored: Boolean = false ignored: Boolean = false,
): IrSimpleFunction { ): IrSimpleFunction {
val body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET, emptyList()) val body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET, emptyList())
@@ -134,7 +141,7 @@ class TestGenerator(val context: JsCommonBackendContext, val groupByPackage: Boo
beforeFuns: List<IrSimpleFunction>, beforeFuns: List<IrSimpleFunction>,
afterFuns: List<IrSimpleFunction>, afterFuns: List<IrSimpleFunction>,
irClass: IrClass, irClass: IrClass,
parentFunction: IrSimpleFunction parentFunction: IrSimpleFunction,
) { ) {
val fn = context.testFun!!.createInvocation(testFun.name.asString(), parentFunction, testFun.isIgnored) val fn = context.testFun!!.createInvocation(testFun.name.asString(), parentFunction, testFun.isIgnored)
val body = fn.body as IrBlockBody val body = fn.body as IrBlockBody
@@ -179,10 +186,8 @@ class TestGenerator(val context: JsCommonBackendContext, val groupByPackage: Boo
return return
} }
if (context is JsIrBackendContext && (testFun.returnType as? IrSimpleType)?.classifier == context.intrinsics.promiseClassSymbol) {
val finally = context.intrinsics.promiseClassSymbol.owner.declarations
.findIsInstanceAnd<IrSimpleFunction> { 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 refType = IrSimpleTypeImpl(context.ir.symbols.functionN(0), false, emptyList(), emptyList())
val afterFunction = context.irFactory.buildFun { val afterFunction = context.irFactory.buildFun {
@@ -204,12 +209,14 @@ class TestGenerator(val context: JsCommonBackendContext, val groupByPackage: Boo
val finallyLambda = JsIrBuilder.buildFunctionExpression(refType, afterFunction) val finallyLambda = JsIrBuilder.buildFunctionExpression(refType, afterFunction)
val returnValue = JsIrBuilder.buildCall( val finally =
finally.symbol IrDynamicMemberExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.dynamicType, "finally", returnStatement.value)
).apply {
this.dispatchReceiver = returnStatement.value val returnValue =
putValueArgument(0, finallyLambda) IrDynamicOperatorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.dynamicType, IrDynamicOperator.INVOKE).also {
} it.receiver = finally
it.arguments.add(finallyLambda)
}
body.statements += JsIrBuilder.buildReturn( body.statements += JsIrBuilder.buildReturn(
fn.symbol, fn.symbol,
@@ -261,4 +268,10 @@ class TestGenerator(val context: JsCommonBackendContext, val groupByPackage: Boo
private fun IrAnnotationContainer.hasAnnotation(fqName: String) = private fun IrAnnotationContainer.hasAnnotation(fqName: String) =
annotations.any { it.symbol.owner.fqNameWhenAvailable?.parent()?.asString() == fqName } 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"
}
} }
@@ -4,6 +4,7 @@ module.exports = {
'SimpleTest testFooWrong': 'pending', 'SimpleTest testFooWrong': 'pending',
'TestTest emptyTest': 'pending', 'TestTest emptyTest': 'pending',
'AsyncTest checkAsyncOrder': 'pass', 'AsyncTest checkAsyncOrder': 'pass',
'AsyncTest checkCustomPromise': 'pass',
'AsyncTest asyncPassing': 'pass', 'AsyncTest asyncPassing': 'pass',
'AsyncTest asyncFailing': 'fail', 'AsyncTest asyncFailing': 'fail',
'org OrgTest test': 'pass', 'org OrgTest test': 'pass',
@@ -46,28 +46,33 @@ class AsyncTest {
var afterLog = "" var afterLog = ""
var expectedAfterLog = ""
@BeforeTest @BeforeTest
fun before() { fun before() {
log = "" log = ""
afterLog = ""
expectedAfterLog = ""
} }
// Until bootstrap update
@AfterTest @AfterTest
fun after() { fun after() {
assertEquals(afterLog, "after") assertEquals(afterLog, expectedAfterLog)
} }
fun promise(v: Int) = Promise<Int> { resolve, reject -> fun promise(v: Int, after: String = "") = Promise<Int> { resolve, reject ->
log += "a" log += "a"
js("setTimeout")({ log += "c"; afterLog += "after"; resolve(v) }, 100) js("setTimeout")({ log += "c"; afterLog += after; resolve(v) }, 100)
log += "b" log += "b"
}.also {
expectedAfterLog += after
} }
@Test @Test
fun checkAsyncOrder(): Promise<Unit> { fun checkAsyncOrder(): Promise<Unit> {
log += 1 log += 1
val p1 = promise(10) val p1 = promise(10, "after")
log += 2 log += 2
@@ -80,9 +85,18 @@ class AsyncTest {
return p2 return p2
} }
@Test
fun checkCustomPromise(): CustomPromise {
// return promise(10, "afterCustom") as CustomPromise
return promise(10, "") as CustomPromise
}
@Test @Test
fun asyncPassing() = promise(10).then { assertEquals(10, it) } fun asyncPassing() = promise(10).then { assertEquals(10, it) }
@Test @Test
fun asyncFailing() = promise(20).then { assertEquals(10, it) } fun asyncFailing() = promise(20).then { assertEquals(10, it) }
} }
@JsName("Promise")
external class CustomPromise {}