From e38b83880fe3d55da1455bd90e93d88dc866adde Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 20 Apr 2016 12:33:09 +0300 Subject: [PATCH] KT-3008 Add tests for module wrappers --- js/js.tests/js.tests.iml | 1 + .../jetbrains/kotlin/js/test/BasicTest.java | 7 ++ .../js/test/MultipleModulesTranslationTest.kt | 41 ++++++++++-- .../kotlin/js/test/WithModuleKind.kt | 23 +++++++ .../rhino/RhinoFunctionResultChecker.java | 4 +- .../test/semantics/MultiModuleWrappersTest.kt | 66 +++++++++++++++++++ js/js.translator/testData/moduleEmulation.js | 30 +++++++++ .../cases/simple/dependencies.txt | 2 + .../cases/simple/main/main.kt | 4 ++ .../cases/simple/module1/module1.kt | 1 + 10 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 js/js.tests/test/org/jetbrains/kotlin/js/test/WithModuleKind.kt create mode 100644 js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/MultiModuleWrappersTest.kt create mode 100644 js/js.translator/testData/moduleEmulation.js create mode 100644 js/js.translator/testData/multiModuleWrappers/cases/simple/dependencies.txt create mode 100644 js/js.translator/testData/multiModuleWrappers/cases/simple/main/main.kt create mode 100644 js/js.translator/testData/multiModuleWrappers/cases/simple/module1/module1.kt diff --git a/js/js.tests/js.tests.iml b/js/js.tests/js.tests.iml index 5a346486074..be90c8dbeab 100644 --- a/js/js.tests/js.tests.iml +++ b/js/js.tests/js.tests.iml @@ -16,5 +16,6 @@ + \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicTest.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicTest.java index a3145e3ff3e..a07a65e7204 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicTest.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicTest.java @@ -81,6 +81,7 @@ public abstract class BasicTest extends KotlinTestWithEnvironment { private static final String OUT = "out/"; private static final String EXPECTED = "expected/"; private static final String COMMON_FILES_DIR = "_commonFiles/"; + public static final String MODULE_EMULATION_FILE = TEST_DATA_DIR_PATH + "/moduleEmulation.js"; public static final String TEST_MODULE = "JS_TESTS"; public static final String TEST_PACKAGE = "foo"; @@ -346,6 +347,8 @@ public abstract class BasicTest extends KotlinTestWithEnvironment { configuration.put(JSConfigurationKeys.UNIT_TEST_CONFIG, shouldBeTranslateAsUnitTestClass()); + setupConfig(configBuilder); + return new LibrarySourcesConfig(project, configuration); } @@ -359,6 +362,10 @@ public abstract class BasicTest extends KotlinTestWithEnvironment { return false; } + protected void setupConfig(@NotNull CompilerConfiguration configuration) { + // Do nothing by default, expect inheritors to implement this method + } + @NotNull protected String getOutputPath() { return pathToTestDir() + OUT; diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/MultipleModulesTranslationTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/MultipleModulesTranslationTest.kt index c96dc2d0a48..d44fd6f17c7 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/MultipleModulesTranslationTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/MultipleModulesTranslationTest.kt @@ -16,22 +16,24 @@ package org.jetbrains.kotlin.js.test -import com.google.dart.compiler.backend.js.ast.JsProgram +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.util.text.StringUtil +import org.jetbrains.kotlin.js.config.Config import org.jetbrains.kotlin.js.config.EcmaVersion +import org.jetbrains.kotlin.js.config.LibrarySourcesConfig import org.jetbrains.kotlin.js.facade.MainCallParameters import org.jetbrains.kotlin.js.test.rhino.RhinoFunctionResultChecker -import org.jetbrains.kotlin.js.test.utils.DirectiveTestUtils -import org.jetbrains.kotlin.js.test.utils.JsTestUtils import org.jetbrains.kotlin.js.test.utils.JsTestUtils.getAllFilesInDir +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.serialization.js.ModuleKind import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils import java.io.File -import java.util.ArrayList import java.util.LinkedHashMap abstract class MultipleModulesTranslationTest(main: String) : BasicTest(main) { - private val MAIN_MODULE_NAME: String = "main" private var dependencies: Map>? = null + protected var moduleKind = ModuleKind.PLAIN override fun checkFooBoxIsOkByPath(filePath: String) { val dirName = getTestName(true) @@ -68,10 +70,23 @@ abstract class MultipleModulesTranslationTest(main: String) : BasicTest(main) { private fun getMetaFileOutputPath(moduleDirectoryName: String, version: EcmaVersion) = KotlinJavascriptMetadataUtils.replaceSuffix(getOutputFilePath(moduleDirectoryName, version)) + override fun setupConfig(builder: LibrarySourcesConfig.Builder) { + val method = try { + javaClass.getMethod(name) + } + catch (e: NoSuchMethodException) { + return + } + + method.getAnnotation(WithModuleKind::class.java)?.let { moduleKind = it.value } + builder.moduleKind(moduleKind) + } + override fun shouldGenerateMetaInfo() = true override fun additionalJsFiles(ecmaVersion: EcmaVersion): List { - val result = super.additionalJsFiles(ecmaVersion) + val result = mutableListOf(MODULE_EMULATION_FILE) + result += super.additionalJsFiles(ecmaVersion) val dirName = getTestName(true) assert(dependencies != null) { "dependencies should not be null" } @@ -84,6 +99,20 @@ abstract class MultipleModulesTranslationTest(main: String) : BasicTest(main) { return result } + override fun translateFiles(jetFiles: MutableList, outputFile: File, mainCallParameters: MainCallParameters, + config: Config) { + super.translateFiles(jetFiles, outputFile, mainCallParameters, config) + + if (config.moduleKind == ModuleKind.COMMON_JS) { + val content = FileUtil.loadFile(outputFile, true) + val wrappedContent = "__beginModule__();\n" + + "$content\n" + + "__endModule__(\"${StringUtil.escapeStringCharacters(config.moduleId)}\");" + FileUtil.writeToFile(outputFile, wrappedContent) + // TODO: it would be better to wrap output before JS file is actually written + } + } + private fun readModuleDependencies(testDataDir: String): Map> { val dependenciesTxt = upsearchFile(testDataDir, "dependencies.txt") assert(dependenciesTxt.isFile) { "moduleDependencies should not be null" } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/WithModuleKind.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/WithModuleKind.kt new file mode 100644 index 00000000000..c4dfb3dbd2d --- /dev/null +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/WithModuleKind.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.js.test + +import org.jetbrains.kotlin.serialization.js.ModuleKind + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class WithModuleKind(val value: ModuleKind) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/rhino/RhinoFunctionResultChecker.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/rhino/RhinoFunctionResultChecker.java index 55ef5293396..c420e9aa147 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/rhino/RhinoFunctionResultChecker.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/rhino/RhinoFunctionResultChecker.java @@ -55,10 +55,10 @@ public class RhinoFunctionResultChecker implements RhinoResultChecker { return cx.evaluateString(scope, functionCallString(), "function call", 0, null); } - protected String functionCallString() { + private String functionCallString() { StringBuilder sb = new StringBuilder(); if (packageName != null) { - sb.append("this"); + sb.append("kotlin.modules"); if (moduleId.contains(".")) { sb.append("['").append(moduleId).append("']"); } else { diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/MultiModuleWrappersTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/MultiModuleWrappersTest.kt new file mode 100644 index 00000000000..9f450130746 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/MultiModuleWrappersTest.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.js.test.semantics + +import org.jetbrains.kotlin.js.test.MultipleModulesTranslationTest +import org.jetbrains.kotlin.js.test.WithModuleKind +import org.jetbrains.kotlin.serialization.js.ModuleKind + +class MultiModuleWrappersTest() : MultipleModulesTranslationTest("multiModuleWrappers/") { + private var overridenTestName = "" + + @WithModuleKind(ModuleKind.AMD) fun testAmd() { + runTest("simple") + } + + @WithModuleKind(ModuleKind.COMMON_JS) fun testCommonJs() { + runTest("simple") + } + + @WithModuleKind(ModuleKind.UMD) fun testUmd() { + runTest("simple") + } + + @WithModuleKind(ModuleKind.UMD) fun testPlain() { + runTest("simple") + } + + fun runTest(name: String) { + overridenTestName = name + doTest("${pathToTestDir()}/cases/$name") + } + + override fun getTestName(lowercaseFirstLetter: Boolean) = overridenTestName + + override fun getOutputPath() = "${super.getOutputPath()}/${moduleKind.name.toLowerCase()}/" +} diff --git a/js/js.translator/testData/moduleEmulation.js b/js/js.translator/testData/moduleEmulation.js new file mode 100644 index 00000000000..eefbca5ca49 --- /dev/null +++ b/js/js.translator/testData/moduleEmulation.js @@ -0,0 +1,30 @@ +(function(global) { + var modules = { kotlin: kotlin }; + var module = { exports: {} }; + + function require(moduleId) { + return modules[moduleId]; + } + + function beginModule() { + module.exports = {}; + } + + function endModule(moduleId) { + modules[moduleId] = module.exports; + } + + function define(moduleId, dependencies, body) { + var resolvedDependencies = []; + for (var i = 0; i < dependencies.length; ++i) { + resolvedDependencies.push(modules[dependencies[i]]); + } + modules[moduleId] = body.apply(null, resolvedDependencies); + } + + global.require = require; + global.define = define; + global.__beginModule__ = beginModule; + global.__endModule__ = endModule; + global.module = module; +})(this); \ No newline at end of file diff --git a/js/js.translator/testData/multiModuleWrappers/cases/simple/dependencies.txt b/js/js.translator/testData/multiModuleWrappers/cases/simple/dependencies.txt new file mode 100644 index 00000000000..6db4872a51a --- /dev/null +++ b/js/js.translator/testData/multiModuleWrappers/cases/simple/dependencies.txt @@ -0,0 +1,2 @@ +module1-> +main->module1 \ No newline at end of file diff --git a/js/js.translator/testData/multiModuleWrappers/cases/simple/main/main.kt b/js/js.translator/testData/multiModuleWrappers/cases/simple/main/main.kt new file mode 100644 index 00000000000..7b089fa128c --- /dev/null +++ b/js/js.translator/testData/multiModuleWrappers/cases/simple/main/main.kt @@ -0,0 +1,4 @@ +fun box(): String { + assertEquals("bar", bar()) + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/multiModuleWrappers/cases/simple/module1/module1.kt b/js/js.translator/testData/multiModuleWrappers/cases/simple/module1/module1.kt new file mode 100644 index 00000000000..7ebd3013b6b --- /dev/null +++ b/js/js.translator/testData/multiModuleWrappers/cases/simple/module1/module1.kt @@ -0,0 +1 @@ +fun bar() = "bar" \ No newline at end of file