JS: test kotlin-test as box tests, support nested, fix mpp
Support tests inside nested classes and companion objects (KT-21850 fixed). Don't launch multiplatform tests twice (KT-21567 fixed).
This commit is contained in:
@@ -114,6 +114,7 @@ abstract class BasicBoxTest(
|
||||
|
||||
val outputFileName = module.outputFileName(outputDir) + ".js"
|
||||
generateJavaScriptFile(file.parent, module, outputFileName, dependencies, friends, modules.size > 1,
|
||||
ENABLE_MULTIPLATFORM.matcher(fileContent).find(),
|
||||
outputPrefixFile, outputPostfixFile, mainCallParameters)
|
||||
|
||||
if (!module.name.endsWith(OLD_MODULE_SUFFIX)) Pair(outputFileName, module) else null
|
||||
@@ -280,6 +281,7 @@ abstract class BasicBoxTest(
|
||||
dependencies: List<String>,
|
||||
friends: List<String>,
|
||||
multiModule: Boolean,
|
||||
multiplatform: Boolean,
|
||||
outputPrefixFile: File?,
|
||||
outputPostfixFile: File?,
|
||||
mainCallParameters: MainCallParameters
|
||||
@@ -298,7 +300,7 @@ abstract class BasicBoxTest(
|
||||
val psiFiles = createPsiFiles(allSourceFiles.sortedBy { it.canonicalPath }.map { it.canonicalPath })
|
||||
|
||||
val sourceDirs = (testFiles + additionalFiles).map { File(it).parent }.distinct()
|
||||
val config = createConfig(sourceDirs, module, dependencies, friends, multiModule, incrementalData = null)
|
||||
val config = createConfig(sourceDirs, module, dependencies, friends, multiModule, multiplatform, incrementalData = null)
|
||||
val outputFile = File(outputFileName)
|
||||
|
||||
val incrementalData = IncrementalData()
|
||||
@@ -306,7 +308,7 @@ abstract class BasicBoxTest(
|
||||
mainCallParameters, incrementalData)
|
||||
|
||||
if (module.hasFilesToRecompile) {
|
||||
checkIncrementalCompilation(sourceDirs, module, kotlinFiles, dependencies, friends, multiModule, outputFile,
|
||||
checkIncrementalCompilation(sourceDirs, module, kotlinFiles, dependencies, friends, multiModule, multiplatform, outputFile,
|
||||
outputPrefixFile, outputPostfixFile, mainCallParameters, incrementalData)
|
||||
}
|
||||
}
|
||||
@@ -318,6 +320,7 @@ abstract class BasicBoxTest(
|
||||
dependencies: List<String>,
|
||||
friends: List<String>,
|
||||
multiModule: Boolean,
|
||||
multiplatform: Boolean,
|
||||
outputFile: File,
|
||||
outputPrefixFile: File?,
|
||||
outputPostfixFile: File?,
|
||||
@@ -339,7 +342,7 @@ abstract class BasicBoxTest(
|
||||
.sortedBy { it.canonicalPath }
|
||||
.map { sourceToTranslationUnit[it]!! }
|
||||
|
||||
val recompiledConfig = createConfig(sourceDirs, module, dependencies, friends, multiModule, incrementalData)
|
||||
val recompiledConfig = createConfig(sourceDirs, module, dependencies, friends, multiModule, multiplatform, incrementalData)
|
||||
val recompiledOutputFile = File(outputFile.parentFile, outputFile.nameWithoutExtension + "-recompiled.js")
|
||||
|
||||
translateFiles(translationUnits, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile,
|
||||
@@ -516,7 +519,7 @@ abstract class BasicBoxTest(
|
||||
|
||||
private fun createConfig(
|
||||
sourceDirs: List<String>,module: TestModule, dependencies: List<String>, friends: List<String>,
|
||||
multiModule: Boolean, incrementalData: IncrementalData?
|
||||
multiModule: Boolean, multiplatform: Boolean, incrementalData: IncrementalData?
|
||||
): JsConfig {
|
||||
val configuration = environment.configuration.copy()
|
||||
|
||||
@@ -550,6 +553,15 @@ abstract class BasicBoxTest(
|
||||
|
||||
configuration.put(JSConfigurationKeys.TYPED_ARRAYS_ENABLED, typedArraysEnabled)
|
||||
|
||||
if (multiplatform) {
|
||||
val defaultLanguageVersionSettings = configuration.languageVersionSettings
|
||||
configuration.languageVersionSettings = object : LanguageVersionSettings by defaultLanguageVersionSettings {
|
||||
override fun getFeatureSupport(feature: LanguageFeature): LanguageFeature.State =
|
||||
if (feature == LanguageFeature.MultiPlatformProjects) LanguageFeature.State.ENABLED
|
||||
else defaultLanguageVersionSettings.getFeatureSupport(feature)
|
||||
}
|
||||
}
|
||||
|
||||
return JsConfig(project, configuration, METADATA_CACHE, (JsConfig.JS_STDLIB + JsConfig.JS_KOTLIN_TEST).toSet())
|
||||
}
|
||||
|
||||
@@ -699,6 +711,7 @@ abstract class BasicBoxTest(
|
||||
private val EXPECTED_REACHABLE_NODES = Pattern.compile("^// *$EXPECTED_REACHABLE_NODES_DIRECTIVE: *([0-9]+) *$", Pattern.MULTILINE)
|
||||
private val RECOMPILE_PATTERN = Pattern.compile("^// *RECOMPILE *$", Pattern.MULTILINE)
|
||||
private val SOURCE_MAP_SOURCE_EMBEDDING = Regex("^// *SOURCE_MAP_EMBED_SOURCES: ([A-Z]+)*\$", RegexOption.MULTILINE)
|
||||
private val ENABLE_MULTIPLATFORM = Pattern.compile("^// *MULTIPLATFORM *$", Pattern.MULTILINE)
|
||||
|
||||
val TEST_MODULE = "JS_TESTS"
|
||||
private val DEFAULT_MODULE = "main"
|
||||
|
||||
@@ -5886,6 +5886,51 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("js/js.translator/testData/box/kotlin.test")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Kotlin_test extends AbstractBoxJsTest {
|
||||
public void testAllFilesPresentInKotlin_test() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/kotlin.test"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true);
|
||||
}
|
||||
|
||||
@TestMetadata("beforeAfter.kt")
|
||||
public void testBeforeAfter() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/kotlin.test/beforeAfter.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ignore.kt")
|
||||
public void testIgnore() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/kotlin.test/ignore.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inherited.kt")
|
||||
public void testInherited() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/kotlin.test/inherited.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("mpp.kt")
|
||||
public void testMpp() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/kotlin.test/mpp.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nested.kt")
|
||||
public void testNested() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/kotlin.test/nested.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/kotlin.test/simple.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("js/js.translator/testData/box/labels")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -58,20 +58,21 @@ class JSTestGenerator(val context: TranslationContext) {
|
||||
}
|
||||
|
||||
private fun generateTestFunctions(classDescriptor: ClassDescriptor, parentFun: JsFunction) {
|
||||
if (classDescriptor.modality === Modality.ABSTRACT) return
|
||||
if (classDescriptor.modality === Modality.ABSTRACT || classDescriptor.isExpect) return
|
||||
|
||||
val suiteFunction = JsFunction(context.scope(), JsBlock(), "suite function")
|
||||
|
||||
val descriptors = classDescriptor.unsubstitutedMemberScope
|
||||
.getContributedDescriptors(DescriptorKindFilter.FUNCTIONS, MemberScope.ALL_NAME_FILTER)
|
||||
.filterIsInstance<FunctionDescriptor>()
|
||||
|
||||
val beforeFunctions = descriptors.filter { it.isBefore }
|
||||
val afterFunctions = descriptors.filter { it.isAfter }
|
||||
val beforeFunctions = descriptors.filterIsInstance<FunctionDescriptor>().filter { it.isBefore }
|
||||
val afterFunctions = descriptors.filterIsInstance<FunctionDescriptor>().filter { it.isAfter }
|
||||
|
||||
descriptors.forEach {
|
||||
if (it.isTest) {
|
||||
generateCodeForTestMethod(it, beforeFunctions, afterFunctions, classDescriptor, suiteFunction)
|
||||
when {
|
||||
it is ClassDescriptor -> generateTestFunctions(it, suiteFunction)
|
||||
it is FunctionDescriptor && it.isTest ->
|
||||
generateCodeForTestMethod(it, beforeFunctions, afterFunctions, classDescriptor, suiteFunction)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,9 +102,7 @@ class JSTestGenerator(val context: TranslationContext) {
|
||||
val functionToTest = JsFunction(scope, JsBlock(), "test function")
|
||||
val innerContext = context.contextWithScope(functionToTest)
|
||||
|
||||
val expression = ReferenceTranslator.translateAsTypeReference(classDescriptor, innerContext)
|
||||
val testClass = JsNew(expression)
|
||||
val classVal = innerContext.defineTemporary(testClass)
|
||||
val classVal = innerContext.defineTemporary(classDescriptor.instance(innerContext))
|
||||
|
||||
fun FunctionDescriptor.buildCall() = CallTranslator.buildCall(context, this, emptyList(), classVal).makeStmt()
|
||||
|
||||
@@ -119,6 +118,16 @@ class JSTestGenerator(val context: TranslationContext) {
|
||||
return functionToTest
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.instance(context: TranslationContext): JsExpression {
|
||||
return if (kind == ClassKind.OBJECT) {
|
||||
ReferenceTranslator.translateAsValueReference(this, context)
|
||||
}
|
||||
else {
|
||||
val args = if (isInner) listOf((containingDeclaration as ClassDescriptor).instance(context)) else emptyList()
|
||||
JsNew(ReferenceTranslator.translateAsTypeReference(this, context), args)
|
||||
}
|
||||
}
|
||||
|
||||
private val suiteRef: JsExpression by lazy { findFunction("suite") }
|
||||
private val testRef: JsExpression by lazy { findFunction("test") }
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1114
|
||||
package foo
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
var log = ""
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// EXPECTED_REACHABLE_NODES: 4643
|
||||
// EXPECTED_REACHABLE_NODES: 5107
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import kotlin.test.FrameworkAdapter
|
||||
|
||||
private val context = TestContext()
|
||||
|
||||
fun call(name: String) = context.call(name)
|
||||
|
||||
fun raise(name: String): Nothing {
|
||||
context.raised(name)
|
||||
throw Exception(name)
|
||||
}
|
||||
|
||||
@Suppress("INVISIBLE_MEMBER")
|
||||
val underscore = kotlin.test.setAdapter(object : FrameworkAdapter {
|
||||
override fun suite(name: String, ignored: Boolean, suiteFn: () -> Unit) {
|
||||
context.suite(name, ignored) { suiteFn() }
|
||||
}
|
||||
|
||||
override fun test(name: String, ignored: Boolean, testFn: () -> Unit) {
|
||||
context.test(name, ignored) { testFn() }
|
||||
}
|
||||
})
|
||||
|
||||
class TestContext {
|
||||
val log: String
|
||||
get() = logHead + (lastRecord ?: "")
|
||||
|
||||
var indentation = ""
|
||||
|
||||
fun suite(name: String, ignored: Boolean = false, body: TestContext.() -> Unit) = indent {
|
||||
record("suite(\"$name\"${optionalIgnore(ignored)}) {")
|
||||
body.runSafely()
|
||||
record("}")
|
||||
}
|
||||
|
||||
|
||||
fun test(name: String, ignored: Boolean = false, body: TestContext.() -> Unit = {}) = indent {
|
||||
val num = record("test(\"$name\"${optionalIgnore(ignored)}) {")
|
||||
|
||||
body.runSafely()
|
||||
|
||||
if (!writtenSince(num)) {
|
||||
record("test(\"$name\"${optionalIgnore(ignored)})", replaceLast = true)
|
||||
}
|
||||
else {
|
||||
record("}")
|
||||
}
|
||||
}
|
||||
|
||||
fun call(name: String) = indent {
|
||||
record("call(\"$name\")")
|
||||
}
|
||||
|
||||
fun raised(msg: String) = indent {
|
||||
record("raised(\"$msg\")")
|
||||
}
|
||||
|
||||
fun caught(msg: String) = indent {
|
||||
record("caught(\"$msg\")")
|
||||
}
|
||||
|
||||
private fun (TestContext.() -> Unit).runSafely() {
|
||||
try {
|
||||
this()
|
||||
}
|
||||
catch (t: Throwable) {
|
||||
caught(t.message ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
private fun indent(body: () -> Unit) {
|
||||
val prevIndentation = indentation
|
||||
indentation += " "
|
||||
body()
|
||||
indentation = prevIndentation
|
||||
}
|
||||
|
||||
|
||||
private var logHead: String = ""
|
||||
private var lastRecord: String? = null
|
||||
private var counter = 0
|
||||
|
||||
private fun writtenSince(num: Int) = counter > num
|
||||
|
||||
private fun record(s: String, replaceLast: Boolean = false): Int {
|
||||
if (!replaceLast && lastRecord != null) {
|
||||
logHead += lastRecord
|
||||
}
|
||||
|
||||
lastRecord = indentation + s + "\n"
|
||||
|
||||
return ++counter
|
||||
}
|
||||
|
||||
private fun optionalIgnore(ignored: Boolean) = if (ignored) ", true" else ""
|
||||
}
|
||||
|
||||
fun checkLog(body: TestContext.() -> Unit): String {
|
||||
val expectedContext = TestContext()
|
||||
expectedContext.suite("") {
|
||||
body()
|
||||
}
|
||||
if (context.log != expectedContext.log) {
|
||||
return "Failed test structure check. Expected: ${expectedContext.log}; actual: ${context.log}."
|
||||
}
|
||||
else {
|
||||
return "OK"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1183
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.AfterTest
|
||||
|
||||
class Simple {
|
||||
@BeforeTest
|
||||
fun before() {
|
||||
call("before")
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
fun after() {
|
||||
call("after")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun foo() {
|
||||
call("foo")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bar() {
|
||||
call("bar")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun withException() {
|
||||
call("withException")
|
||||
raise("some exception")
|
||||
call("never happens")
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = checkLog {
|
||||
suite("Simple") {
|
||||
test("foo") {
|
||||
call("before")
|
||||
call("foo")
|
||||
call("after")
|
||||
}
|
||||
test("bar") {
|
||||
call("before")
|
||||
call("bar")
|
||||
call("after")
|
||||
}
|
||||
test("withException") {
|
||||
call("before")
|
||||
call("withException")
|
||||
raised("some exception")
|
||||
call("after")
|
||||
caught("some exception")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1192
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.Ignore
|
||||
|
||||
class A {
|
||||
@Test
|
||||
fun foo() {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
fun bar() {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
class B {
|
||||
@Test
|
||||
fun foo() {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
fun bar() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore
|
||||
class C {
|
||||
@Test
|
||||
fun foo() {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
fun bar() {
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = checkLog {
|
||||
suite("A") {
|
||||
test("foo")
|
||||
test("bar", true)
|
||||
suite("B", true) {
|
||||
test("foo")
|
||||
test("bar", true)
|
||||
}
|
||||
}
|
||||
suite("C", true) {
|
||||
test("foo")
|
||||
test("bar", true)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1197
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.AfterTest
|
||||
|
||||
interface TestyInterface {
|
||||
@Test
|
||||
fun someVarTest() {
|
||||
call("TestyInterface.someVarTest")
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractTest : TestyInterface {
|
||||
@Test abstract fun abstractTest()
|
||||
|
||||
@Test
|
||||
fun someTest() {
|
||||
call("AbstractTest.someTest")
|
||||
}
|
||||
}
|
||||
|
||||
interface BeforeAfterInterface {
|
||||
@BeforeTest
|
||||
@AfterTest
|
||||
fun beforeAfter() {
|
||||
call("beforeAfter")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class InheritedTest : AbstractTest(), BeforeAfterInterface {
|
||||
@Test override fun abstractTest() {
|
||||
call("InheritedTest.abstractTest")
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = checkLog() {
|
||||
suite("InheritedTest") {
|
||||
test("abstractTest") {
|
||||
call("beforeAfter")
|
||||
call("InheritedTest.abstractTest")
|
||||
call("beforeAfter")
|
||||
}
|
||||
test("someTest") {
|
||||
call("beforeAfter")
|
||||
call("AbstractTest.someTest")
|
||||
call("beforeAfter")
|
||||
}
|
||||
test("someVarTest") {
|
||||
call("beforeAfter")
|
||||
call("TestyInterface.someVarTest")
|
||||
call("beforeAfter")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1173
|
||||
// MULTIPLATFORM
|
||||
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
import kotlin.test.Test
|
||||
|
||||
expect class PlatformTest {
|
||||
@Test fun platformTest()
|
||||
}
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: main.kt
|
||||
import kotlin.test.Test
|
||||
|
||||
actual class PlatformTest {
|
||||
@Test actual fun platformTest() {}
|
||||
|
||||
@Test fun someOtherTest() {}
|
||||
}
|
||||
|
||||
fun box() = checkLog {
|
||||
suite("PlatformTest") {
|
||||
test("platformTest")
|
||||
test("someOtherTest")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1200
|
||||
import kotlin.test.Test
|
||||
|
||||
class Outer {
|
||||
|
||||
val prop = "prop"
|
||||
|
||||
@Test
|
||||
fun test1() {
|
||||
}
|
||||
|
||||
inner class Inner {
|
||||
|
||||
@Test fun innerTest() {
|
||||
call(prop + "Inner")
|
||||
}
|
||||
|
||||
inner class Inneer {
|
||||
@Test fun inneerTest() {
|
||||
call(prop + "Inneer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Nested {
|
||||
@Test
|
||||
fun a() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun b() {
|
||||
}
|
||||
|
||||
class EvenDeeper {
|
||||
|
||||
@Test
|
||||
fun c() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test2() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Test
|
||||
fun companionTest() {
|
||||
}
|
||||
|
||||
object InnerCompanion {
|
||||
@Test
|
||||
fun innerCompanionTest() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = checkLog {
|
||||
suite("Outer") {
|
||||
test("test1")
|
||||
suite("Inner") {
|
||||
test("innerTest") {
|
||||
call("propInner")
|
||||
}
|
||||
suite("Inneer") {
|
||||
test("inneerTest") {
|
||||
call("propInneer")
|
||||
}
|
||||
}
|
||||
}
|
||||
suite("Nested") {
|
||||
test("a")
|
||||
test("b")
|
||||
suite("EvenDeeper") {
|
||||
test("c")
|
||||
}
|
||||
}
|
||||
test("test2")
|
||||
suite("Companion") {
|
||||
test("companionTest")
|
||||
suite("InnerCompanion") {
|
||||
test("innerCompanionTest")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1173
|
||||
import kotlin.test.Test
|
||||
|
||||
class Simple {
|
||||
@Test fun foo() {
|
||||
call("foo")
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = checkLog {
|
||||
suite("Simple") {
|
||||
test("foo") {
|
||||
call("foo")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user