[Wasm] Add box and stdlib tests in wasi mode

This commit is contained in:
Igor Yakovlev
2023-07-31 15:46:48 +02:00
committed by Zalim Bashorov
parent f42d0b1ed4
commit 983991d46c
37 changed files with 560 additions and 93 deletions
@@ -0,0 +1,25 @@
package kotlin
fun <T> assertArrayEquals(expected: Array<out T>, actual: Array<out T>, message: String? = null) {
if (!arraysEqual(expected, actual)) {
val msg = if (message == null) "" else ", message = '$message'"
fail("Unexpected array: expected = '$expected', actual = '$actual'$msg")
}
}
private fun <T> arraysEqual(first: Array<out T>, second: Array<out T>): Boolean {
if (first === second) return true
if (first.size != second.size) return false
for (index in 0..first.size - 1) {
if (!equal(first[index], second[index])) return false
}
return true
}
private fun equal(first: Any?, second: Any?) =
if (first is Array<*> && second is Array<*>) {
arraysEqual(first, second)
}
else {
first == second
}
+45
View File
@@ -0,0 +1,45 @@
package kotlin
// This file should be excluded from tests using StdLib, as these methods conflict with corresponding methods from kotlin.test
// see StdLibTestBase.removeAdHocAssertions
fun <T> assertEquals(expected: T, actual: T, message: String? = null) {
if (expected != actual) {
val msg = if (message == null) "" else ", message = '$message'"
fail("Unexpected value: expected = '$expected', actual = '$actual'$msg")
}
}
fun <T> assertNotEquals(illegal: T, actual: T, message: String? = null) {
if (illegal == actual) {
val msg = if (message == null) "" else ", message = '$message'"
fail("Illegal value: illegal = '$illegal', actual = '$actual'$msg")
}
}
fun <T> assertSame(expected: T, actual: T, message: String? = null) {
if (expected !== actual) {
val msg = if (message == null) "" else ", message = '$message'"
fail("Expected same instances: expected = '$expected', actual = '$actual'$msg")
}
}
fun assertTrue(actual: Boolean, message: String? = null) = assertEquals(true, actual, message)
fun assertFalse(actual: Boolean, message: String? = null) = assertEquals(false, actual, message)
fun testTrue(f: () -> Boolean) {
assertTrue(f(), f.toString())
}
fun testFalse(f: () -> Boolean) {
assertFalse(f(), f.toString())
}
fun assertFails(block: () -> Unit): Throwable {
try {
block()
} catch (t: Throwable) {
return t
}
fail("Expected an exception to be thrown, but was completed successfully.")
}
+7
View File
@@ -0,0 +1,7 @@
package kotlin
// This file should be excluded from tests using StdLib, as these methods conflict with corresponding methods from kotlin.test
// see StdLibTestBase.removeAdHocAssertions
fun fail(message: String? = null): Nothing {
throw Throwable(message)
}
+9 -6
View File
@@ -76,6 +76,7 @@ dependencies {
val generationRoot = projectDir.resolve("tests-gen")
useD8Plugin()
useNodeJsPlugin()
optInToExperimentalCompilerApi()
sourceSets {
@@ -86,11 +87,11 @@ sourceSets {
}
}
fun Test.setupWasmStdlib() {
dependsOn(":kotlin-stdlib-wasm-js:compileKotlinWasm")
systemProperty("kotlin.wasm.stdlib.path", "libraries/stdlib/wasm/js/build/classes/kotlin/wasm/main")
dependsOn(":kotlin-test:kotlin-test-wasm-js:compileKotlinWasm")
systemProperty("kotlin.wasm.kotlin.test.path", "libraries/kotlin.test/wasm/js/build/classes/kotlin/wasm/main")
fun Test.setupWasmStdlib(target: String) {
dependsOn(":kotlin-stdlib-wasm-$target:compileKotlinWasm")
systemProperty("kotlin.wasm-$target.stdlib.path", "libraries/stdlib/wasm/$target/build/classes/kotlin/wasm/main")
dependsOn(":kotlin-test:kotlin-test-wasm-$target:compileKotlinWasm")
systemProperty("kotlin.wasm-$target.kotlin.test.path", "libraries/kotlin.test/wasm/$target/build/classes/kotlin/wasm/main")
}
fun Test.setupGradlePropertiesForwarding() {
@@ -144,9 +145,11 @@ fun Project.wasmProjectTest(
) {
workingDir = rootDir
setupV8()
setupNodeJs()
setupSpiderMonkey()
useJUnitPlatform()
setupWasmStdlib()
setupWasmStdlib("js")
setupWasmStdlib("wasi")
setupGradlePropertiesForwarding()
systemProperty("kotlin.wasm.test.root.out.dir", "$buildDir/")
body()
@@ -80,6 +80,10 @@ fun main(args: Array<String>) {
testClass<AbstractK1WasmCodegenWasmJsInteropTest> {
model("codegen/boxWasmJsInterop")
}
testClass<AbstractK1WasmWasiCodegenBoxTest> {
model("codegen/boxWasmWasi")
}
}
}
}
@@ -17,8 +17,11 @@ import org.jetbrains.kotlin.test.frontend.fir.*
import org.jetbrains.kotlin.test.frontend.fir.handlers.*
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.runners.codegen.commonFirHandlersForCodegenTest
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
import org.jetbrains.kotlin.test.services.configuration.WasmEnvironmentConfiguratorJs
import org.jetbrains.kotlin.wasm.test.converters.FirWasmKlibBackendFacade
import org.jetbrains.kotlin.wasm.test.converters.WasmBackendFacade
import org.jetbrains.kotlin.wasm.test.handlers.WasmBoxRunner
open class AbstractFirWasmTest(
@@ -39,6 +42,12 @@ open class AbstractFirWasmTest(
override val afterBackendFacade: Constructor<AbstractTestFacade<BinaryArtifacts.KLib, BinaryArtifacts.Wasm>>
get() = ::WasmBackendFacade
override val wasmBoxTestRunner: Constructor<AnalysisHandler<BinaryArtifacts.Wasm>>
get() = ::WasmBoxRunner
override val wasmEnvironmentConfigurator: Constructor<EnvironmentConfigurator>
get() = ::WasmEnvironmentConfiguratorJs
override fun configure(builder: TestConfigurationBuilder) {
super.configure(builder)
with(builder) {
@@ -12,8 +12,11 @@ import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontend2IrConverter
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendFacade
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
import org.jetbrains.kotlin.test.services.configuration.WasmEnvironmentConfiguratorJs
import org.jetbrains.kotlin.wasm.test.converters.FirWasmKlibBackendFacade
import org.jetbrains.kotlin.wasm.test.converters.WasmBackendFacade
import org.jetbrains.kotlin.wasm.test.handlers.WasmBoxRunner
abstract class AbstractK1WasmTest(
pathToTestDir: String,
@@ -32,6 +35,12 @@ abstract class AbstractK1WasmTest(
override val afterBackendFacade: Constructor<AbstractTestFacade<BinaryArtifacts.KLib, BinaryArtifacts.Wasm>>
get() = ::WasmBackendFacade
override val wasmBoxTestRunner: Constructor<AnalysisHandler<BinaryArtifacts.Wasm>>
get() = ::WasmBoxRunner
override val wasmEnvironmentConfigurator: Constructor<EnvironmentConfigurator>
get() = ::WasmEnvironmentConfiguratorJs
}
open class AbstractK1WasmCodegenBoxTest : AbstractK1WasmTest(
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2023 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 org.jetbrains.kotlin.wasm.test
import org.jetbrains.kotlin.test.Constructor
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontend2IrConverter
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendFacade
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.services.AdditionalSourceProvider
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
import org.jetbrains.kotlin.test.services.configuration.WasmEnvironmentConfiguratorWasi
import org.jetbrains.kotlin.wasm.test.converters.FirWasmKlibBackendFacade
import org.jetbrains.kotlin.wasm.test.converters.WasmBackendFacade
import org.jetbrains.kotlin.wasm.test.handlers.WasiBoxRunner
abstract class AbstractK1WasmWasiTest(
pathToTestDir: String,
testGroupOutputDirPrefix: String,
) : AbstractWasmBlackBoxCodegenTestBase<ClassicFrontendOutputArtifact, IrBackendInput, BinaryArtifacts.KLib>(
FrontendKinds.ClassicFrontend, TargetBackend.WASM, pathToTestDir, testGroupOutputDirPrefix
) {
override val frontendFacade: Constructor<FrontendFacade<ClassicFrontendOutputArtifact>>
get() = ::ClassicFrontendFacade
override val frontendToBackendConverter: Constructor<Frontend2BackendConverter<ClassicFrontendOutputArtifact, IrBackendInput>>
get() = ::ClassicFrontend2IrConverter
override val backendFacade: Constructor<BackendFacade<IrBackendInput, BinaryArtifacts.KLib>>
get() = ::FirWasmKlibBackendFacade
override val afterBackendFacade: Constructor<AbstractTestFacade<BinaryArtifacts.KLib, BinaryArtifacts.Wasm>>
get() = ::WasmBackendFacade
override val wasmBoxTestRunner: Constructor<AnalysisHandler<BinaryArtifacts.Wasm>>
get() = ::WasiBoxRunner
override val wasmEnvironmentConfigurator: Constructor<EnvironmentConfigurator>
get() = ::WasmEnvironmentConfiguratorWasi
override val additionalSourceProvider: Constructor<AdditionalSourceProvider>?
get() = ::WasmWasiBoxTestHelperSourceProvider
}
open class AbstractK1WasmWasiCodegenBoxTest : AbstractK1WasmWasiTest(
"compiler/testData/codegen/boxWasmWasi/",
"codegen/k1WasmWasiBox"
)
@@ -21,10 +21,11 @@ import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerWithTargetBackendTest
import org.jetbrains.kotlin.test.runners.codegen.actualizersAndPluginsFacadeStepIfNeeded
import org.jetbrains.kotlin.test.runners.codegen.commonClassicFrontendHandlersForCodegenTest
import org.jetbrains.kotlin.test.services.AdditionalSourceProvider
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
import org.jetbrains.kotlin.test.services.LibraryProvider
import org.jetbrains.kotlin.test.services.configuration.WasmEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
import org.jetbrains.kotlin.wasm.test.handlers.WasmBoxRunner
abstract class AbstractWasmBlackBoxCodegenTestBase<R : ResultingArtifact.FrontendOutput<R>, I : ResultingArtifact.BackendInput<I>, A : ResultingArtifact.Binary<A>>(
private val targetFrontend: FrontendKind<R>,
@@ -36,6 +37,9 @@ abstract class AbstractWasmBlackBoxCodegenTestBase<R : ResultingArtifact.Fronten
abstract val frontendToBackendConverter: Constructor<Frontend2BackendConverter<R, I>>
abstract val backendFacade: Constructor<BackendFacade<I, A>>
abstract val afterBackendFacade: Constructor<AbstractTestFacade<A, BinaryArtifacts.Wasm>>
abstract val wasmBoxTestRunner: Constructor<AnalysisHandler<BinaryArtifacts.Wasm>>
abstract val wasmEnvironmentConfigurator: Constructor<EnvironmentConfigurator>
open val additionalSourceProvider: Constructor<AdditionalSourceProvider>? = null
override fun TestConfigurationBuilder.configuration() {
globalDefaults {
@@ -63,7 +67,7 @@ abstract class AbstractWasmBlackBoxCodegenTestBase<R : ResultingArtifact.Fronten
}
useConfigurators(
::WasmEnvironmentConfigurator,
wasmEnvironmentConfigurator,
)
useAdditionalSourceProviders(
@@ -71,6 +75,10 @@ abstract class AbstractWasmBlackBoxCodegenTestBase<R : ResultingArtifact.Fronten
::CoroutineHelpersSourceFilesProvider,
)
additionalSourceProvider?.let {
useAdditionalSourceProviders(it)
}
useAdditionalService(::LibraryProvider)
useAfterAnalysisCheckers(
@@ -106,7 +114,7 @@ abstract class AbstractWasmBlackBoxCodegenTestBase<R : ResultingArtifact.Fronten
}
wasmArtifactsHandlersStep {
useHandlers(::WasmBoxRunner)
useHandlers(wasmBoxTestRunner)
}
}
}
@@ -15,6 +15,13 @@ import org.jetbrains.kotlin.test.services.TestServices
import java.io.File
import java.io.FileFilter
class WasmWasiBoxTestHelperSourceProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
override fun produceAdditionalFiles(globalDirectives: RegisteredDirectives, module: TestModule): List<TestFile> {
val boxTestRunFile = File("wasm/wasm.tests/wasiBoxTestRun.kt")
return listOf(boxTestRunFile.toTestFile())
}
}
class WasmAdditionalSourceProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
override fun produceAdditionalFiles(globalDirectives: RegisteredDirectives, module: TestModule): List<TestFile> {
if (JsEnvironmentConfigurationDirectives.NO_COMMON_FILES in module.directives) return emptyList()
@@ -24,7 +31,7 @@ class WasmAdditionalSourceProvider(testServices: TestServices) : AdditionalSourc
companion object {
private const val COMMON_FILES_NAME = "_common"
private const val COMMON_FILES_DIR = "_commonFiles/"
private const val COMMON_FILES_DIR_PATH = "js/js.translator/testData/$COMMON_FILES_DIR"
private const val COMMON_FILES_DIR_PATH = "wasm/wasm.tests/$COMMON_FILES_DIR"
private fun getFilesInDirectoryByExtension(directory: String, extension: String): List<String> {
val dir = File(directory)
@@ -14,6 +14,8 @@ import org.jetbrains.kotlin.ir.backend.js.JsFactories
import org.jetbrains.kotlin.ir.backend.js.resolverLogger
import org.jetbrains.kotlin.ir.backend.js.serializeModuleIntoKlib
import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.WasmTarget
import org.jetbrains.kotlin.library.KotlinAbiVersion
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.test.backend.ir.IrBackendFacade
@@ -52,7 +54,8 @@ class FirWasmKlibBackendFacade(
val outputFile = WasmEnvironmentConfigurator.getWasmKlibArtifactPath(testServices, module.name)
// TODO: consider avoiding repeated libraries resolution
val libraries = resolveLibraries(configuration, getAllWasmDependenciesPaths(module, testServices))
val target = configuration.get(JSConfigurationKeys.WASM_TARGET, WasmTarget.JS)
val libraries = resolveLibraries(configuration, getAllWasmDependenciesPaths(module, testServices, target))
if (firstTimeCompilation) {
serializeModuleIntoKlib(
@@ -77,7 +80,7 @@ class FirWasmKlibBackendFacade(
// TODO: consider avoiding repeated libraries resolution
val lib = CommonKLibResolver.resolve(
getAllWasmDependenciesPaths(module, testServices) + listOf(outputFile),
getAllWasmDependenciesPaths(module, testServices, target) + listOf(outputFile),
configuration.resolverLogger
).getFullResolvedList().last().library
@@ -20,6 +20,8 @@ import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageConfig
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageLogLevel
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageMode
import org.jetbrains.kotlin.ir.linkage.partial.setupPartialLinkageConfig
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.WasmTarget
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.test.DebugMode
@@ -62,9 +64,15 @@ class WasmBackendFacade(
PhaseConfig(wasmPhases)
}
val suffix = when (configuration.get(JSConfigurationKeys.WASM_TARGET, WasmTarget.JS)) {
WasmTarget.JS -> "-js"
WasmTarget.WASI -> "-wasi"
else -> error("Unexpected wasi target")
}
val libraries = listOf(
System.getProperty("kotlin.wasm.stdlib.path")!!,
System.getProperty("kotlin.wasm.kotlin.test.path")!!
System.getProperty("kotlin.wasm$suffix.stdlib.path")!!,
System.getProperty("kotlin.wasm$suffix.kotlin.test.path")!!
) + WasmEnvironmentConfigurator.getAllRecursiveLibrariesFor(module, testServices).map { it.key.libraryFile.canonicalPath }
val friendLibraries = emptyList<String>()
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerTest
import org.jetbrains.kotlin.test.services.LibraryProvider
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.configuration.WasmEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.configuration.WasmEnvironmentConfiguratorJs
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
@@ -37,7 +38,7 @@ abstract class AbstractDiagnosticsWasmTest : AbstractKotlinCompilerTest() {
useConfigurators(
::CommonEnvironmentConfigurator,
::WasmEnvironmentConfigurator,
::WasmEnvironmentConfiguratorJs,
)
useMetaInfoProcessors(::OldNewInferenceMetaInfoProcessor)
@@ -0,0 +1,103 @@
/*
* Copyright 2010-2023 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 org.jetbrains.kotlin.wasm.test.handlers
import org.jetbrains.kotlin.backend.wasm.WasmCompilerResult
import org.jetbrains.kotlin.backend.wasm.writeCompilationResult
import org.jetbrains.kotlin.js.JavaScript
import org.jetbrains.kotlin.test.DebugMode
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.directives.WasmEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.directives.WasmEnvironmentConfigurationDirectives.RUN_UNIT_TESTS
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.moduleStructure
import org.jetbrains.kotlin.wasm.test.tools.WasmVM
import java.io.File
class WasiBoxRunner(
testServices: TestServices
) : AbstractWasmArtifactsCollector(testServices) {
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
if (!someAssertionWasFailed) {
runWasmCode()
}
}
private fun runWasmCode() {
val artifacts = modulesToArtifact.values.single()
val baseFileName = "index"
val outputDirBase = testServices.getWasmTestOutputDirectory()
val originalFile = testServices.moduleStructure.originalTestDataFiles.first()
val debugMode = DebugMode.fromSystemProperty("kotlin.wasm.debugMode")
val startUnitTests = RUN_UNIT_TESTS in testServices.moduleStructure.allDirectives
val testWasiQuiet = """
let boxTestPassed = false;
try {
let jsModule = await import('./index.mjs');
let wasmExports = jsModule.default;
${if (startUnitTests) "wasmExports.startUnitTests();" else ""}
boxTestPassed = wasmExports.runBoxTest();
} catch(e) {
console.log('Failed with exception!');
console.log(e);
}
if (!boxTestPassed)
process.exit(1);
""".trimIndent()
val testWasiVerbose = testWasiQuiet + """
console.log('test passed');
""".trimIndent()
val testWasi = if (debugMode >= DebugMode.DEBUG) testWasiVerbose else testWasiQuiet
fun writeToFilesAndRunTest(mode: String, res: WasmCompilerResult) {
val dir = File(outputDirBase, mode)
dir.mkdirs()
writeCompilationResult(res, dir, baseFileName)
File(dir, "test.mjs").writeText(testWasi)
if (debugMode >= DebugMode.DEBUG) {
val path = dir.absolutePath
println(" ------ $mode Wat file://$path/index.wat")
println(" ------ $mode Wasm file://$path/index.wasm")
println(" ------ $mode JS file://$path/index.mjs")
println(" ------ $mode Test file://$path/test.mjs")
}
val testFileText = originalFile.readText()
val failsIn: List<String> = InTextDirectivesUtils.findListWithPrefixes(testFileText, "// WASM_FAILS_IN: ")
val exception = WasmVM.NodeJs.runWithCathedExceptions(
debugMode = debugMode,
disableExceptions = false,
failsIn = failsIn,
entryMjs = "test.mjs",
jsFilePaths = emptyList(),
workingDirectory = dir
)
if (exception != null) {
throw exception
}
if (mode == "dce") {
checkExpectedOutputSize(debugMode, testFileText, dir)
}
}
writeToFilesAndRunTest("dev", artifacts.compilerResult)
writeToFilesAndRunTest("dce", artifacts.compilerResultWithDCE)
}
}
@@ -92,48 +92,13 @@ class WasmBoxRunner(
""".trimIndent()
val testJsVerbose = testJsQuiet + """
console.log('test passed');
""".trimIndent()
val testJs = if (debugMode >= DebugMode.DEBUG) testJsVerbose else testJsQuiet
fun checkExpectedOutputSize(testFileContent: String, testDir: File) {
val expectedSizes =
InTextDirectivesUtils.findListWithPrefixes(testFileContent, "// WASM_DCE_EXPECTED_OUTPUT_SIZE: ")
.map {
val i = it.indexOf(' ')
val extension = it.substring(0, i)
val size = it.substring(i + 1)
extension.trim().lowercase() to size.filter(Char::isDigit).toInt()
}
val filesByExtension = testDir.listFiles()?.groupBy { it.extension }.orEmpty()
val errors = expectedSizes.mapNotNull { (extension, expectedSize) ->
val totalSize = filesByExtension[extension].orEmpty().sumOf { it.length() }
val thresholdPercent = 1
val thresholdInBytes = expectedSize * thresholdPercent / 100
val expectedMinSize = expectedSize - thresholdInBytes
val expectedMaxSize = expectedSize + thresholdInBytes
val diff = totalSize - expectedSize
val message = "Total size of $extension files is $totalSize," +
" but expected $expectedSize$thresholdInBytes [$expectedMinSize .. $expectedMaxSize]." +
" Diff: $diff (${diff * 100 / expectedSize}%)"
if (debugMode >= DebugMode.DEBUG) {
println(" ------ $message")
}
if (totalSize !in expectedMinSize..expectedMaxSize) message else null
}
if (errors.isNotEmpty()) throw AssertionError(errors.joinToString("\n"))
}
fun writeToFilesAndRunTest(mode: String, res: WasmCompilerResult) {
val dir = File(outputDirBase, mode)
dir.mkdirs()
@@ -194,26 +159,15 @@ class WasmBoxRunner(
val disableExceptions = DISABLE_WASM_EXCEPTION_HANDLING in testServices.moduleStructure.allDirectives
val exceptions = listOf(WasmVM.V8, WasmVM.SpiderMonkey).mapNotNull map@{ vm ->
try {
if (debugMode >= DebugMode.DEBUG) {
println(" ------ Run in ${vm.name}" + if (vm.shortName in failsIn) " (expected to fail)" else "")
}
vm.run(
"./${entryMjs}",
jsFilePaths,
workingDirectory = dir,
disableExceptionHandlingIfPossible = disableExceptions
)
if (vm.shortName in failsIn) {
return@map AssertionError("The test expected to fail in ${vm.name}. Please update the testdata.")
}
} catch (e: Throwable) {
if (vm.shortName !in failsIn) {
return@map e
}
}
null
val exceptions = listOf(WasmVM.V8, WasmVM.SpiderMonkey).mapNotNull { vm ->
vm.runWithCathedExceptions(
debugMode = debugMode,
disableExceptions = disableExceptions,
failsIn = failsIn,
entryMjs = entryMjs,
jsFilePaths = jsFilePaths,
workingDirectory = dir,
)
}
when (exceptions.size) {
@@ -229,7 +183,7 @@ class WasmBoxRunner(
}
if (mode == "dce") {
checkExpectedOutputSize(testFileText, dir)
checkExpectedOutputSize(debugMode, testFileText, dir)
}
}
@@ -240,6 +194,35 @@ class WasmBoxRunner(
private class AdditionalFile(val name: String, val content: String)
}
internal fun WasmVM.runWithCathedExceptions(
debugMode: DebugMode,
disableExceptions: Boolean,
failsIn: List<String>,
entryMjs: String?,
jsFilePaths: List<String>,
workingDirectory: File,
): Throwable? {
try {
if (debugMode >= DebugMode.DEBUG) {
println(" ------ Run in ${name}" + if (shortName in failsIn) " (expected to fail)" else "")
}
run(
"./${entryMjs}",
jsFilePaths,
workingDirectory = workingDirectory,
disableExceptionHandlingIfPossible = disableExceptions,
)
if (shortName in failsIn) {
return AssertionError("The test expected to fail in ${name}. Please update the testdata.")
}
} catch (e: Throwable) {
if (shortName !in failsIn) {
return e
}
}
return null
}
fun TestServices.getWasmTestOutputDirectory(): File {
val originalFile = moduleStructure.originalTestDataFiles.first()
val allDirectives = moduleStructure.allDirectives
@@ -256,4 +239,41 @@ fun TestServices.getWasmTestOutputDirectory(): File {
.toList().asReversed()
.fold(testGroupOutputDir, ::File)
.let { File(it, originalFile.nameWithoutExtension) }
}
}
fun checkExpectedOutputSize(debugMode: DebugMode, testFileContent: String, testDir: File) {
val expectedSizes =
InTextDirectivesUtils.findListWithPrefixes(testFileContent, "// WASM_DCE_EXPECTED_OUTPUT_SIZE: ")
.map {
val i = it.indexOf(' ')
val extension = it.substring(0, i)
val size = it.substring(i + 1)
extension.trim().lowercase() to size.filter(Char::isDigit).toInt()
}
val filesByExtension = testDir.listFiles()?.groupBy { it.extension }.orEmpty()
val errors = expectedSizes.mapNotNull { (extension, expectedSize) ->
val totalSize = filesByExtension[extension].orEmpty().sumOf { it.length() }
val thresholdPercent = 1
val thresholdInBytes = expectedSize * thresholdPercent / 100
val expectedMinSize = expectedSize - thresholdInBytes
val expectedMaxSize = expectedSize + thresholdInBytes
val diff = totalSize - expectedSize
val message = "Total size of $extension files is $totalSize," +
" but expected $expectedSize$thresholdInBytes [$expectedMinSize .. $expectedMaxSize]." +
" Diff: $diff (${diff * 100 / expectedSize}%)"
if (debugMode >= DebugMode.DEBUG) {
println(" ------ $message")
}
if (totalSize !in expectedMinSize..expectedMaxSize) message else null
}
if (errors.isNotEmpty()) throw AssertionError(errors.joinToString("\n"))
}
@@ -46,6 +46,17 @@ internal sealed class WasmVM(val shortName: String) {
)
}
}
object NodeJs : WasmVM("NodeJs") {
override fun run(entryMjs: String, jsFiles: List<String>, workingDirectory: File?, disableExceptionHandlingIfPossible: Boolean) {
tool.run(
"--experimental-wasm-gc",
*jsFiles.flatMap { listOf("-f", it) }.toTypedArray(),
entryMjs,
workingDirectory = workingDirectory
)
}
}
}
internal class ExternalTool(val path: String) {
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2023 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 org.jetbrains.kotlin.wasm.test;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateWasmTestsKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("compiler/testData/codegen/boxWasmWasi")
@TestDataPath("$PROJECT_ROOT")
public class K1WasmWasiCodegenBoxTestGenerated extends AbstractK1WasmWasiCodegenBoxTest {
@Test
public void testAllFilesPresentInBoxWasmWasi() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxWasmWasi"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.WASM, true);
}
@Test
@TestMetadata("simpleWasi.kt")
public void testSimpleWasi() throws Exception {
runTest("compiler/testData/codegen/boxWasmWasi/simpleWasi.kt");
}
}
+14
View File
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2023 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.
*/
@kotlin.wasm.WasmExport
fun runBoxTest(): Boolean {
val boxResult = box() //TODO: Support non-root package box functions
val isOk = boxResult == "OK"
if (!isOk) {
println("Wrong box result '${boxResult}'; Expected 'OK'")
}
return isOk
}