Add tests for source maps in JS BE
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
* Copyright 2010-2017 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.
|
||||
@@ -145,6 +145,7 @@ import org.jetbrains.kotlin.jps.build.*
|
||||
import org.jetbrains.kotlin.jps.build.android.AbstractAndroidJpsTestCase
|
||||
import org.jetbrains.kotlin.jps.incremental.AbstractProtoComparisonTest
|
||||
import org.jetbrains.kotlin.js.test.AbstractDceTest
|
||||
import org.jetbrains.kotlin.js.test.AbstractJsLineNumberTest
|
||||
import org.jetbrains.kotlin.js.test.semantics.*
|
||||
import org.jetbrains.kotlin.jvm.compiler.*
|
||||
import org.jetbrains.kotlin.jvm.runtime.AbstractJvm8RuntimeDescriptorLoaderTest
|
||||
@@ -1356,6 +1357,10 @@ fun main(args: Array<String>) {
|
||||
testClass<AbstractDceTest> {
|
||||
model("dce/", pattern = "(.+)\\.js", targetBackend = TargetBackend.JS)
|
||||
}
|
||||
|
||||
testClass<AbstractJsLineNumberTest> {
|
||||
model("lineNumbers/", pattern = "^([^_](.+))\\.kt$", targetBackend = TargetBackend.JS)
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("js/js.tests/test", "compiler/testData") {
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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 com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.psi.PsiManager
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
|
||||
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.js.config.EcmaVersion
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.js.config.JsConfig
|
||||
import org.jetbrains.kotlin.js.facade.K2JSTranslator
|
||||
import org.jetbrains.kotlin.js.facade.MainCallParameters
|
||||
import org.jetbrains.kotlin.js.facade.TranslationResult
|
||||
import org.jetbrains.kotlin.js.facade.TranslationUnit
|
||||
import org.jetbrains.kotlin.js.test.utils.LineCollector
|
||||
import org.jetbrains.kotlin.js.test.utils.LineOutputToStringVisitor
|
||||
import org.jetbrains.kotlin.js.util.TextOutputImpl
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestWithEnvironment
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
import java.nio.charset.Charset
|
||||
|
||||
abstract class AbstractJsLineNumberTest : KotlinTestWithEnvironment() {
|
||||
fun doTest(filePath: String) {
|
||||
val translator = K2JSTranslator(createConfig())
|
||||
val unit = TranslationUnit.SourceFile(createPsiFile(filePath))
|
||||
val translationResult = translator.translateUnits(listOf(unit), MainCallParameters.noCall())
|
||||
|
||||
if (translationResult !is TranslationResult.Success) {
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
val collector = PrintingMessageCollector(PrintStream(outputStream), MessageRenderer.PLAIN_FULL_PATHS, true)
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(translationResult.diagnostics, collector)
|
||||
val messages = outputStream.toByteArray().toString(Charset.forName("UTF-8"))
|
||||
throw AssertionError("The following errors occurred compiling test:\n" + messages)
|
||||
}
|
||||
|
||||
val lineCollector = LineCollector()
|
||||
lineCollector.accept(translationResult.program)
|
||||
|
||||
val programOutput = TextOutputImpl()
|
||||
translationResult.program.globalBlock.accept(LineOutputToStringVisitor(programOutput, lineCollector))
|
||||
val generatedCode = programOutput.toString()
|
||||
val relativePath = File(filePath).relativeTo(File(BASE_PATH)).path.removeSuffix(".kt")
|
||||
with(File(File(OUT_PATH), relativePath + ".js")) {
|
||||
parentFile.mkdirs()
|
||||
writeText(generatedCode)
|
||||
}
|
||||
|
||||
val sourceCode = FileUtil.loadFile(File(filePath))
|
||||
val linesMatcher = LINES_PATTERN.find(sourceCode) ?:
|
||||
error("'// LINES: ' comment was not found in source file. Generated code is:\n$generatedCode")
|
||||
|
||||
val expectedLines = linesMatcher.groups[1]!!.value
|
||||
val actualLines = lineCollector.lines
|
||||
.dropLastWhile { it == null }
|
||||
.joinToString(" ") { if (it == null) "*" else (it + 1).toString() }
|
||||
|
||||
TestCase.assertEquals(generatedCode, expectedLines, actualLines)
|
||||
}
|
||||
|
||||
override fun createEnvironment(): KotlinCoreEnvironment {
|
||||
return KotlinCoreEnvironment.createForTests(testRootDisposable, CompilerConfiguration(), EnvironmentConfigFiles.JS_CONFIG_FILES)
|
||||
}
|
||||
|
||||
private fun createConfig(): JsConfig {
|
||||
val configuration = environment.configuration.copy()
|
||||
|
||||
configuration.put(JSConfigurationKeys.LIBRARIES, JsConfig.JS_STDLIB + JsConfig.JS_KOTLIN_TEST)
|
||||
|
||||
configuration.put(CommonConfigurationKeys.MODULE_NAME, "test")
|
||||
configuration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN)
|
||||
configuration.put(JSConfigurationKeys.TARGET, EcmaVersion.v5)
|
||||
|
||||
configuration.put(JSConfigurationKeys.SOURCE_MAP, true)
|
||||
|
||||
return JsConfig(project, configuration)
|
||||
}
|
||||
|
||||
private fun createPsiFile(fileName: String): KtFile {
|
||||
val psiManager = PsiManager.getInstance(project)
|
||||
val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
|
||||
|
||||
return psiManager.findFile(fileSystem.findFileByPath(fileName)!!) as KtFile
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DIR_NAME = "lineNumbers"
|
||||
private val LINES_PATTERN = Regex("^ *// *LINES: *(.*)$", RegexOption.MULTILINE)
|
||||
private val BASE_PATH = "${BasicBoxTest.TEST_DATA_DIR_PATH}/$DIR_NAME"
|
||||
private val OUT_PATH = "${BasicBoxTest.TEST_DATA_DIR_PATH}/out/$DIR_NAME"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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 com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TargetBackend;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("js/js.translator/testData/lineNumbers")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class JsLineNumberTestGenerated extends AbstractJsLineNumberTest {
|
||||
public void testAllFilesPresentInLineNumbers() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/lineNumbers"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/lineNumbers/simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.utils
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
|
||||
class LineCollector : RecursiveJsVisitor() {
|
||||
val lines = mutableListOf<Int?>()
|
||||
private var currentStatement: JsStatement? = null
|
||||
val lineNumbersByStatement = mutableMapOf<JsStatement, MutableList<Int>>()
|
||||
val statementsWithoutLineNumbers = mutableSetOf<JsStatement>()
|
||||
|
||||
override fun visitElement(node: JsNode) {
|
||||
handleNodeLocation(node)
|
||||
super.visitElement(node)
|
||||
}
|
||||
|
||||
private fun handleNodeLocation(node: JsNode) {
|
||||
(node.source as? PsiElement)?.let { source ->
|
||||
val file = source.containingFile
|
||||
val offset = source.node.startOffset
|
||||
val document = file.viewProvider.document!!
|
||||
val line = document.getLineNumber(offset)
|
||||
currentStatement?.let {
|
||||
val linesByStatement = lineNumbersByStatement.getOrPut(it, ::mutableListOf)
|
||||
if (linesByStatement.lastOrNull() != line) {
|
||||
linesByStatement += line
|
||||
lines += line
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitExpressionStatement(x: JsExpressionStatement) {
|
||||
val expression = x.expression
|
||||
if (expression is JsFunction) {
|
||||
expression.accept(this)
|
||||
}
|
||||
else {
|
||||
withStatement(x) {
|
||||
super.visitExpressionStatement(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitIf(x: JsIf) {
|
||||
withStatement(x) {
|
||||
x.ifExpression.accept(this)
|
||||
}
|
||||
x.thenStatement.accept(this)
|
||||
x.elseStatement?.accept(this)
|
||||
}
|
||||
|
||||
override fun visitWhile(x: JsWhile) {
|
||||
withStatement(x) {
|
||||
x.condition.accept(this)
|
||||
}
|
||||
x.body.accept(this)
|
||||
}
|
||||
|
||||
override fun visitDoWhile(x: JsDoWhile) {
|
||||
withStatement(x) {
|
||||
x.condition.accept(this)
|
||||
}
|
||||
x.body.accept(this)
|
||||
}
|
||||
|
||||
override fun visitFor(x: JsFor) {
|
||||
withStatement(x) {
|
||||
x.initExpression?.accept(this)
|
||||
x.initVars?.accept(this)
|
||||
x.condition?.accept(this)
|
||||
x.incrementExpression?.accept(this)
|
||||
}
|
||||
x.body?.accept(this)
|
||||
}
|
||||
|
||||
override fun visitBreak(x: JsBreak) {
|
||||
withStatement(x) {
|
||||
super.visitBreak(x)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitContinue(x: JsContinue) {
|
||||
withStatement(x) {
|
||||
super.visitContinue(x)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitReturn(x: JsReturn) {
|
||||
withStatement(x) {
|
||||
super.visitReturn(x)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitVars(x: JsVars) {
|
||||
withStatement(x) {
|
||||
super.visitVars(x)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(x: JsSwitch) {
|
||||
withStatement(x) {
|
||||
x.expression.accept(this)
|
||||
}
|
||||
x.cases.forEach { accept(it) }
|
||||
}
|
||||
|
||||
override fun visitThrow(x: JsThrow) {
|
||||
withStatement(x) {
|
||||
super.visitThrow(x)
|
||||
}
|
||||
}
|
||||
|
||||
private fun withStatement(statement: JsStatement, action: () -> Unit) {
|
||||
val oldStatement = currentStatement
|
||||
currentStatement = statement
|
||||
|
||||
action()
|
||||
|
||||
if (statement !in lineNumbersByStatement && lines.lastOrNull() != null) {
|
||||
lines.add(null)
|
||||
statementsWithoutLineNumbers += statement
|
||||
}
|
||||
currentStatement = oldStatement
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.utils
|
||||
|
||||
import org.jetbrains.kotlin.js.backend.JsToStringGenerationVisitor
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.js.util.TextOutput
|
||||
|
||||
class LineOutputToStringVisitor(output: TextOutput, val lineCollector: LineCollector) :
|
||||
JsToStringGenerationVisitor(output) {
|
||||
|
||||
override fun visitIf(x: JsIf) {
|
||||
printLineNumbers(x)
|
||||
super.visitIf(x)
|
||||
}
|
||||
|
||||
override fun visitExpressionStatement(x: JsExpressionStatement) {
|
||||
printLineNumbers(x)
|
||||
super.visitExpressionStatement(x)
|
||||
}
|
||||
|
||||
override fun visitWhile(x: JsWhile) {
|
||||
printLineNumbers(x)
|
||||
super.visitWhile(x)
|
||||
}
|
||||
|
||||
override fun visitDoWhile(x: JsDoWhile) {
|
||||
printLineNumbers(x)
|
||||
super.visitDoWhile(x)
|
||||
}
|
||||
|
||||
override fun visitBreak(x: JsBreak) {
|
||||
printLineNumbers(x)
|
||||
super.visitBreak(x)
|
||||
}
|
||||
|
||||
override fun visitContinue(x: JsContinue) {
|
||||
printLineNumbers(x)
|
||||
super.visitContinue(x)
|
||||
}
|
||||
|
||||
override fun visitFor(x: JsFor) {
|
||||
printLineNumbers(x)
|
||||
super.visitFor(x)
|
||||
}
|
||||
|
||||
override fun visitVars(x: JsVars) {
|
||||
printLineNumbers(x)
|
||||
super.visitVars(x)
|
||||
}
|
||||
|
||||
override fun visitThrow(x: JsThrow) {
|
||||
printLineNumbers(x)
|
||||
super.visitThrow(x)
|
||||
}
|
||||
|
||||
override fun visitReturn(x: JsReturn) {
|
||||
printLineNumbers(x)
|
||||
super.visitReturn(x)
|
||||
}
|
||||
|
||||
override fun visit(x: JsSwitch) {
|
||||
printLineNumbers(x)
|
||||
super.visit(x)
|
||||
}
|
||||
|
||||
private fun printLineNumbers(statement: JsStatement) {
|
||||
if (statement in lineCollector.statementsWithoutLineNumbers) {
|
||||
p.print("/* no-line */ ")
|
||||
}
|
||||
else if (statement in lineCollector.lineNumbersByStatement) {
|
||||
p.print("/* ")
|
||||
p.print(lineCollector.lineNumbersByStatement[statement]!!.joinToString(" ") { (it + 1).toString() })
|
||||
p.print(" */ ")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun box() {
|
||||
println("foo")
|
||||
println("bar")
|
||||
}
|
||||
|
||||
// LINES: 2 3
|
||||
Reference in New Issue
Block a user