Move JS tests generator to :js:js.tests, run it in "Generate Compiler Tests"

Also cleanup generateTestDataForReservedWords and make everything there
private
This commit is contained in:
Alexander Udalov
2017-11-02 12:03:41 +01:00
parent 8c6ed3e872
commit f52ba44e90
5 changed files with 124 additions and 93 deletions
@@ -1130,63 +1130,4 @@ fun main(args: Array<String>) {
model("android", recursive = false, extension = null)
}
}
// TODO: repair these tests
//generateTestDataForReservedWords()
testGroup("js/js.tests/test", "js/js.translator/testData") {
testClass<AbstractBoxJsTest> {
model("box/", pattern = "^([^_](.+))\\.kt$", targetBackend = TargetBackend.JS)
}
testClass<AbstractSourceMapGenerationSmokeTest> {
model("sourcemap/", pattern = "^([^_](.+))\\.kt$", targetBackend = TargetBackend.JS)
}
testClass<AbstractOutputPrefixPostfixTest> {
model("outputPrefixPostfix/", pattern = "^([^_](.+))\\.kt$", targetBackend = TargetBackend.JS)
}
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") {
testClass<AbstractJsCodegenBoxTest> {
model("codegen/box", targetBackend = TargetBackend.JS)
}
testClass<AbstractNonLocalReturnsTest> {
model("codegen/boxInline/nonLocalReturns/", targetBackend = TargetBackend.JS)
}
testClass<AbstractPropertyAccessorsInlineTests> {
model("codegen/boxInline/property/", targetBackend = TargetBackend.JS)
}
testClass<AbstractNoInlineTests> {
model("codegen/boxInline/noInline/", targetBackend = TargetBackend.JS)
}
testClass<AbstractCallableReferenceInlineTests> {
model("codegen/boxInline/callableReference/", targetBackend = TargetBackend.JS)
}
testClass<AbstractEnumValuesInlineTests> {
model("codegen/boxInline/enum/", targetBackend = TargetBackend.JS)
}
testClass<AbstractInlineDefaultValuesTests> {
model("codegen/boxInline/defaultValues/", targetBackend = TargetBackend.JS)
}
testClass<AbstractJsLegacyPrimitiveArraysBoxTest> {
model("codegen/box/arrays", targetBackend = TargetBackend.JS)
}
}
}
@@ -1,398 +0,0 @@
/*
* Copyright 2010-2015 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.generators.tests
import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil
import org.jetbrains.kotlin.js.backend.ast.JsDeclarationScope
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.renderer.KeywordStringsGenerated
import java.io.File
val MODIFIER_KEYWORDS = KtTokens.MODIFIER_KEYWORDS_ARRAY.map { it.value }.toSet()
val commonCases: CaseBuilder.(String, String) -> Unit = { testByName, testByRef ->
case("val", "val $KEYWORD_MARKER: Int", " = 0", testByName)
case("var", "var $KEYWORD_MARKER: Int", " = 0", testByName)
case("fun", "fun $KEYWORD_MARKER()", " { $KEYWORD_MARKER() }", testByRef)
case("funParam",
"fun foo($KEYWORD_MARKER: String)", """ {
assertEquals("123", $KEYWORD_MARKER)
$testRenamedByName
}""", "foo(\"123\")")
case("label", "val t: Int", " = 0", "testRenamed(\"$KEYWORD_MARKER\", { $KEYWORD_MARKER@ while (false) {} })")
}
val commonCasesWithTestNotRenamed: CaseBuilder.() -> Unit = { commonCases(testNotRenamedByName, testNotRenamedByRef) }
fun main(args: Array<String>) {
generateTestDataForReservedWords()
}
val TEST_DATA_DIR_FOR_RESERVED_WORDS = "js/js.translator/testData/box/reservedWords"
fun generateTestDataForReservedWords() {
generate(TEST_DATA_DIR_FOR_RESERVED_WORDS) {
suite("toplevel",
"""
$DEFINITION_MARKER
fun box(): String {
$TEST_BLOCK_MARKER
return "OK"
}"""
) {
commonCasesWithTestNotRenamed()
case("class", "class $KEYWORD_MARKER { companion object {} }", "", testNotRenamedByName)
case("interface", "interface $KEYWORD_MARKER { companion object {} }", "", testNotRenamedByName)
case("enum", "enum class $KEYWORD_MARKER { foo }", "", testNotRenamed("$KEYWORD_MARKER.foo"))
case("object", "object $KEYWORD_MARKER {}", "", testNotRenamedByName)
}
// -------------------------
suite("local",
"""
fun box(): String {
$DEFINITION_MARKER
$TEST_BLOCK_MARKER
return "OK"
}"""
) {
commonCases(testRenamedByName, testRenamedByRef)
case("catch", "", "",
"""
try {
throw Exception()
}
catch($KEYWORD_MARKER: Exception) {
$testRenamedByName
}""")
}
// -------------------------
suite("insideClass",
"""
class TestClass {
$DEFINITION_MARKER
fun test() {
$TEST_BLOCK_MARKER
}
}
fun box(): String {
TestClass().test()
return "OK"
}""", commonCasesWithTestNotRenamed)
// -------------------------
suite("insideClassObject",
"""
class TestClass {
companion object {
$DEFINITION_MARKER
fun test() {
$TEST_BLOCK_MARKER
}
}
}
fun box(): String {
TestClass.test()
return "OK"
}""", commonCasesWithTestNotRenamed)
// -------------------------
suite("insideObject",
"""
object TestObject {
$DEFINITION_MARKER
fun test() {
$TEST_BLOCK_MARKER
}
}
fun box(): String {
TestObject.test()
return "OK"
}""", commonCasesWithTestNotRenamed)
// -------------------------
suite("dataClass",
"""
data class DataClass($DEFINITION_MARKER: String) {
init {
$TEST_BLOCK_MARKER
}
}
fun box(): String {
DataClass("123")
return "OK"
}"""
) {
case("val", "val $KEYWORD_MARKER", "", testNotRenamedByName)
case("var", "var $KEYWORD_MARKER", "", testNotRenamedByName)
case("param", "$KEYWORD_MARKER", "", testRenamedByName, ignore = true)
}
// -------------------------
suite("delegated",
"""
interface Trait {
$DECLARATION_MARKER
}
class TraitImpl : Trait {
override $DEFINITION_MARKER
}
class TestDelegate : Trait by TraitImpl() {
fun test() {
$TEST_BLOCK_MARKER
}
}
fun box(): String {
TestDelegate().test()
return "OK"
}""", commonCasesWithTestNotRenamed)
// -------------------------
suite("enum",
"""
enum class Foo {
BAR;
$DEFINITION_MARKER
fun test() {
$TEST_BLOCK_MARKER
}
}
fun box(): String {
Foo.BAR.test()
return "OK"
}""", commonCasesWithTestNotRenamed)
// -------------------------
suite("enum",
"""
enum class Foo {
$KEYWORD_MARKER
}
fun box(): String {
${testNotRenamed("Foo.$KEYWORD_MARKER")}
return "OK"
}"""
) {
case("entry", "", "", "", additionalShouldBeEscaped = MODIFIER_KEYWORDS)
}
// -------------------------
}
}
// DSL
class Case(
val name: String,
val testDeclaration: String,
val testDeclarationInit: String,
val testBlock: String,
val ignore: Boolean,
val additionalShouldBeEscaped: Set<String>
)
class Suite(
val name: String,
val code: String,
val cases: List<Case>
)
class CaseBuilder {
val cases = arrayListOf<Case>()
fun case(name: String, testDeclaration: String, testDeclarationInit: String, testBlock: String,
ignore: Boolean = false, additionalShouldBeEscaped: Set<String> = setOf()) {
cases.add(Case(name, testDeclaration, testDeclarationInit, testBlock, ignore, additionalShouldBeEscaped))
}
}
class TestDataBuilder() {
val suites = arrayListOf<Suite>()
fun suite(name: String, code: String, f: CaseBuilder.() -> Unit) {
val builder = CaseBuilder()
builder.f()
suites.add(Suite(name, PREAMBLE + code, builder.cases))
}
fun generate(testDataDirPath: String) {
fun File.readLinesOrNull() = if (!exists()) null else readLines()
val testDataDir = File(testDataDirPath)
val cases = suites.flatMap { suite -> suite.cases.map { case -> suite.name + " / " + case.name } }.sorted()
val shouldBeEscapedFile = File("$testDataDirPath/SHOULD_BE_ESCAPED.txt")
val shouldNotBeEscapedFile = File("$testDataDirPath/SHOULD_NOT_BE_ESCAPED.txt")
val casesFile = File("$testDataDirPath/CASES.txt")
val shouldBeEscapedFromFile = shouldBeEscapedFile.readLinesOrNull()?.drop(1)
val shouldNotBeEscapedFromFile = shouldNotBeEscapedFile.readLinesOrNull()?.drop(1)
val casesFromFile = casesFile.readLinesOrNull()?.drop(1)
val isCreatingFromScratch = shouldBeEscapedFromFile != SHOULD_BE_ESCAPED || shouldNotBeEscapedFromFile != SHOULD_NOT_BE_ESCAPED || casesFromFile != cases
if (!testDataDir.exists() && !testDataDir.mkdirs()) {
error("Unable to find or create test data directory: '$testDataDirPath'.")
}
else if (isCreatingFromScratch) {
if (testDataDir.listFiles()?.all { it.delete() } ?: true) {
println("Create testdata files from scratch.")
}
else {
error("Can not clean testdata directory.")
}
}
for (suite in suites) {
for (case in suite.cases) {
// Uses small portions of keywords instead of ALL_KEYWORDS to avoid a combinatorial explosion
// Each portion contains at least one keyword which should be escaped and at least one which should not.
for (keyword in nextKeywordPortion()) {
if (case.ignore) {
continue
}
val shouldBeEscaped = keyword in SHOULD_BE_ESCAPED || keyword in case.additionalShouldBeEscaped
val keywordWithEscapeIfNeed = if (shouldBeEscaped) "`$keyword`" else keyword
val out = suite.code
.replace(DEFINITION_MARKER, case.testDeclaration + case.testDeclarationInit)
.replace(DECLARATION_MARKER, case.testDeclaration)
.replace(DECLARATION_INIT_MARKER, case.testDeclarationInit)
.replace(TEST_BLOCK_MARKER, case.testBlock)
.replace("\"$KEYWORD_MARKER\"", "\"$keyword\"")
.replace(KEYWORD_MARKER, keywordWithEscapeIfNeed)
val fileName = "${suite.name.decapitalize()}${case.name.capitalize()}${keyword.capitalize()}.kt"
val testDataFile = File(testDataDirPath + "/" + fileName)
if (testDataFile.exists()) {
if (isCreatingFromScratch) {
error("The file '$fileName' unexpectedly exists when create test data from scratch.")
}
} else if (!isCreatingFromScratch) {
error("Unexpected new testdata file: '$fileName'. It may cause for example because of bug in stdlib.\n" +
"If a new keyword has been added, delete SHOULD_BE_ESCAPED.txt and SHOULD_NOT_BE_ESCAPED.txt")
}
GeneratorsFileUtil.writeFileIfContentChanged(testDataFile, out, false)
}
}
}
if (isCreatingFromScratch) {
shouldBeEscapedFile.writeText("$PREAMBLE_MESSAGE\n${SHOULD_BE_ESCAPED.joinToString("\n")}")
shouldNotBeEscapedFile.writeText("$PREAMBLE_MESSAGE\n${SHOULD_NOT_BE_ESCAPED.joinToString("\n")}")
casesFile.writeText("$PREAMBLE_MESSAGE\n${cases.joinToString("\n")}")
}
}
}
fun generate(testDataDirPath: String, f: TestDataBuilder.() -> Unit) {
val builder = TestDataBuilder()
builder.f()
builder.generate(testDataDirPath)
}
val DEFINITION_MARKER = "DEFINITION"
val DECLARATION_MARKER = "DECLARATION"
val DECLARATION_INIT_MARKER = "DECLARATION_INIT"
val TEST_BLOCK_MARKER = "TEST_BLOCK"
val KEYWORD_MARKER = "KEYWORD"
val PREAMBLE_MESSAGE = "NOTE THIS FILE IS AUTO-GENERATED by the generateTestDataForReservedWords.kt. DO NOT EDIT!"
val PREAMBLE = """package foo
// $PREAMBLE_MESSAGE
"""
fun testRenamed(reference: String = "$KEYWORD_MARKER") = "testRenamed(\"$KEYWORD_MARKER\", { $reference })"
fun testNotRenamed(reference: String = "$KEYWORD_MARKER") = "testNotRenamed(\"$KEYWORD_MARKER\", { $reference })"
val testRenamedByName = testRenamed()
val testRenamedByRef = testRenamed("$KEYWORD_MARKER()")
val testNotRenamedByName = testNotRenamed()
val testNotRenamedByRef = testNotRenamed("$KEYWORD_MARKER()")
// KEYWORDS
val SHOULD_BE_ESCAPED = JsDeclarationScope.RESERVED_WORDS.filter { it in KeywordStringsGenerated.KEYWORDS }.sorted()
val SHOULD_NOT_BE_ESCAPED = JsDeclarationScope.RESERVED_WORDS.filter { it !in SHOULD_BE_ESCAPED }.sorted()
// all keywords by portions
// cyclic keyword streams
val s1 = SHOULD_BE_ESCAPED.cyclicSequence()
val s2 = SHOULD_NOT_BE_ESCAPED.cyclicSequence()
val PORTION_PART_SIZE = 2
fun nextKeywordPortion() = s1.take(PORTION_PART_SIZE).toList() + s2.take(PORTION_PART_SIZE).toList()
// CyclicStream
fun <T> List<T>.cyclicSequence() = CyclicSequence(this)
class CyclicSequence<T>(val c: List<T>) : Sequence<T> {
var i = 0
val iterator = object : Iterator<T> {
override fun next(): T {
i = if (i >= c.size) 0 else i
return c[i++]
}
override fun hasNext(): Boolean = true
}
override fun iterator(): Iterator<T> = iterator
}