From d467e4209a48ee98a200790edd9849ed8fc73d63 Mon Sep 17 00:00:00 2001 From: Sergey Rostov Date: Wed, 20 Feb 2019 15:14:05 +0300 Subject: [PATCH] Gradle: Basic Kotlin NodeJS tests runner #KT-30531 Fixed #KT-30528 Fixed --- .gitignore | 3 + .../build.gradle.kts | 1 + .../jetbrains/kotlin/gradle/BaseGradleIT.kt | 88 +- .../kotlin/gradle/MultiplatformGradleIT.kt | 79 ++ .../new-mpp-js-tests/TEST-CommonTest-2.xml | 32 + .../new-mpp-js-tests/TEST-CommonTest.xml | 29 + .../new-mpp-js-tests/build.gradle.kts | 42 + .../new-mpp-js-tests/settings.gradle.kts | 8 + .../src/commonMain/kotlin/common.kt | 1 + .../src/commonTest/kotlin/CommonTest.kt | 31 + .../kotlin-gradle-plugin/build.gradle.kts | 17 +- .../testing/TCServiceMessagesClient.kt | 310 +++++ .../testing/TCServiceMessagesTestExecutor.kt | 86 ++ .../KotlinJsCompilationTestsConfigurator.kt | 118 ++ .../targets/js/KotlinJsTargetConfigurator.kt | 69 +- .../gradle/targets/js/NodeJsTestFailure.kt | 15 + .../gradle/targets/js/nodejs/NodeJsEnv.kt | 17 + .../targets/js/nodejs/NodeJsExtension.kt | 68 ++ .../targets/js/nodejs/NodeJsPlatform.kt | 49 + .../gradle/targets/js/nodejs/NodeJsPlugin.kt | 29 + .../targets/js/nodejs/NodeJsSetupTask.kt | 85 ++ .../js/tasks/KotlinJsNodeModulesTask.kt | 50 + .../js/tasks/KotlinNodeJsTestRuntime.kt | 67 + .../targets/js/tasks/KotlinNodeJsTestTask.kt | 125 ++ .../kotlin/gradle/testing/options.kt | 18 + .../jetbrains/kotlin/gradle/utils/injected.kt | 11 + ...etbrains.kotlin.platform.nodejs.properties | 1 + .../testing/RecordingTestResultProcessor.kt | 40 + .../internal/testing/tcsmc/AppendLeaf.kt | 116 ++ .../internal/testing/tcsmc/AppendRoot.kt | 58 + .../gradle/internal/testing/tcsmc/Complex.kt | 197 +++ .../internal/testing/tcsmc/ReplaceRoot.kt | 66 + .../gradle/internal/testing/tcsmc/SkipRoot.kt | 30 + .../tcsmc/TCServiceMessagesClientTest.kt | 43 + .../internal/testing/tcsmc/TestNameParsing.kt | 65 + .../kotlin-test-nodejs-runner/.yarnclean | 45 + .../tools/kotlin-test-nodejs-runner/README.md | 6 + .../build.gradle.kts | 49 + .../tools/kotlin-test-nodejs-runner/cli.ts | 65 + .../tools/kotlin-test-nodejs-runner/env.ts | 4 + .../kotlin-test-nodejs-runner/package.json | 30 + .../rollup.config.js | 59 + .../src/CliArgsParser.ts | 115 ++ .../src/CliFiltertingConfiguration.ts | 52 + .../src/KotlinTestRunner.ts | 14 + .../src/KotlinTestTeamCityReporter.ts | 104 ++ .../src/KotlinTestsFilter.ts | 168 +++ .../src/KotlinTestsFilterDebug.ts | 32 + .../src/TeamCityMessagesFlow.ts | 23 + .../kotlin-test-nodejs-runner/src/Timer.ts | 16 + .../kotlin-test-nodejs-runner/src/utils.ts | 96 ++ .../kotlin-test-nodejs-runner/tsconfig.json | 15 + .../tools/kotlin-test-nodejs-runner/yarn.lock | 1073 +++++++++++++++++ settings.gradle | 2 + 54 files changed, 3968 insertions(+), 64 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/TEST-CommonTest-2.xml create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/TEST-CommonTest.xml create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/build.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/settings.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/src/commonMain/kotlin/common.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/src/commonTest/kotlin/CommonTest.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesClient.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesTestExecutor.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsCompilationTestsConfigurator.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/NodeJsTestFailure.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsEnv.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsExtension.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsPlatform.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsPlugin.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsSetupTask.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinJsNodeModulesTask.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinNodeJsTestRuntime.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinNodeJsTestTask.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/options.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/injected.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.kotlin.platform.nodejs.properties create mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/RecordingTestResultProcessor.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendLeaf.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendRoot.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/Complex.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/ReplaceRoot.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/SkipRoot.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/TCServiceMessagesClientTest.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/TestNameParsing.kt create mode 100644 libraries/tools/kotlin-test-nodejs-runner/.yarnclean create mode 100644 libraries/tools/kotlin-test-nodejs-runner/README.md create mode 100644 libraries/tools/kotlin-test-nodejs-runner/build.gradle.kts create mode 100755 libraries/tools/kotlin-test-nodejs-runner/cli.ts create mode 100644 libraries/tools/kotlin-test-nodejs-runner/env.ts create mode 100644 libraries/tools/kotlin-test-nodejs-runner/package.json create mode 100644 libraries/tools/kotlin-test-nodejs-runner/rollup.config.js create mode 100644 libraries/tools/kotlin-test-nodejs-runner/src/CliArgsParser.ts create mode 100644 libraries/tools/kotlin-test-nodejs-runner/src/CliFiltertingConfiguration.ts create mode 100644 libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestRunner.ts create mode 100644 libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestTeamCityReporter.ts create mode 100644 libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestsFilter.ts create mode 100644 libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestsFilterDebug.ts create mode 100644 libraries/tools/kotlin-test-nodejs-runner/src/TeamCityMessagesFlow.ts create mode 100644 libraries/tools/kotlin-test-nodejs-runner/src/Timer.ts create mode 100644 libraries/tools/kotlin-test-nodejs-runner/src/utils.ts create mode 100644 libraries/tools/kotlin-test-nodejs-runner/tsconfig.json create mode 100644 libraries/tools/kotlin-test-nodejs-runner/yarn.lock diff --git a/.gitignore b/.gitignore index badd5c65115..117f6dccdfb 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ build/ .idea/.name .idea/artifacts/dist_auto_* kotlin-ultimate/ +node_modules/ +.rpt2_cache/ +libraries/tools/kotlin-test-nodejs-runner/lib/ \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts index d9ac9c0b303..4c601a111fe 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts @@ -23,6 +23,7 @@ dependencies { testCompile(project(":kotlin-test:kotlin-test-jvm")) testCompile(projectRuntimeJar(":kotlin-compiler-embeddable")) + testCompile(intellijCoreDep()) { includeJars("jdom") } // testCompileOnly dependency on non-shaded artifacts is needed for IDE support // testRuntime on shaded artifact is needed for running tests with shaded compiler testCompileOnly(project(path = ":kotlin-gradle-plugin-test-utils-embeddable", configuration = "compile")) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt index bf424e676e8..e3b1e1913bb 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt @@ -1,9 +1,13 @@ package org.jetbrains.kotlin.gradle +import com.intellij.testFramework.TestDataFile import org.gradle.api.logging.LogLevel import org.gradle.tooling.GradleConnector import org.gradle.util.GradleVersion -import org.gradle.util.VersionNumber +import org.jdom.Element +import org.jdom.input.SAXBuilder +import org.jdom.output.Format +import org.jdom.output.XMLOutputter import org.jetbrains.kotlin.gradle.model.ModelContainer import org.jetbrains.kotlin.gradle.model.ModelFetcherBuildAction import org.jetbrains.kotlin.gradle.util.* @@ -495,6 +499,23 @@ abstract class BaseGradleIT { } } + fun CompiledProject.assertTasksRegistered(vararg tasks: String) { + for (task in tasks) { + assertContains("'Register task $task'") + } + } + + fun CompiledProject.assertTasksNotRealized(vararg tasks: String) { + for (task in tasks) { + assertNotContains("'Realize task $task'") + } + } + + fun CompiledProject.assertTasksRegisteredAndNotRealized(vararg tasks: String) { + assertTasksRegistered(*tasks) + assertTasksNotRealized(*tasks) + } + fun CompiledProject.getOutputForTask(taskName: String): String { val taskOutputRegex = ("(?:\\[LIFECYCLE] \\[class org\\.gradle(?:\\.internal\\.buildevents)?\\.TaskExecutionLogger] :$taskName|" + "\\[org\\.gradle\\.execution\\.plan\\.DefaultPlanExecutor\\] :$taskName.*?started)" + @@ -575,6 +596,71 @@ abstract class BaseGradleIT { File(projectDir, it).takeIf(File::exists) }.single() + /** + * @param assertionFileName path to xml with expected test results, relative to test resources root + */ + fun CompiledProject.assertTestResults( + @TestDataFile assertionFileName: String, + testReportName: String + ) { + val projectDir = project.projectDir + val testReportDir = projectDir.resolve("build/test-results/$testReportName") + + if (!testReportDir.isDirectory) { + error("Test report dir was not created") + } + + val actualTestResults = readAndCleanupTestResults(testReportDir, projectDir) + val expectedTestResults = resourcesRootFile.resolve(assertionFileName).readText() + + assertEquals( + prettyPrintXml(expectedTestResults), + prettyPrintXml(actualTestResults) + ) + } + + private fun readAndCleanupTestResults(testReportDir: File, projectDir: File): String { + val files = testReportDir + .listFiles() + .filter { it.isFile && it.name.endsWith(".xml") } + .sortedBy { + // let containing test suite be first + it.name.replace(".xml", ".A.xml") + } + + val xmlString = "\n\n" + + files.joinToString("") { + it.readText() + .replace(projectDir.absolutePath, "/\$PROJECT_DIR$") + .replace(projectDir.name, "\$PROJECT_NAME$") + .replace("\n", "") + } + + "" + + val doc = SAXBuilder().build(xmlString.reader()) + val skipAttrs = setOf("timestamp", "hostname", "time", "message") + val skipContentsOf = setOf("failure") + + fun cleanup(e: Element) { + if (e.name in skipContentsOf) e.text = "..." + e.attributes.forEach { + if (it.name in skipAttrs) { + it.value = "..." + } + } + + e.children.forEach { + cleanup(it) + } + } + + cleanup(doc.rootElement) + return XMLOutputter(Format.getPrettyFormat()).outputString(doc) + } + + private fun prettyPrintXml(uglyXml: String): String = + XMLOutputter(Format.getPrettyFormat()).outputString(SAXBuilder().build(uglyXml.reader())) + private fun Project.createGradleTailParameters(options: BuildOptions, params: Array = arrayOf()): List = params.toMutableList().apply { add("--stacktrace") diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/MultiplatformGradleIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/MultiplatformGradleIT.kt index dd0d55fef9b..ae00024774f 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/MultiplatformGradleIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/MultiplatformGradleIT.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.gradle +import com.intellij.testFramework.TestDataPath import org.jetbrains.kotlin.gradle.plugin.EXPECTED_BY_CONFIG_NAME import org.jetbrains.kotlin.gradle.plugin.IMPLEMENT_CONFIG_NAME import org.jetbrains.kotlin.gradle.plugin.IMPLEMENT_DEPRECATION_WARNING @@ -25,6 +26,7 @@ import org.junit.Test import java.io.File import kotlin.test.assertTrue +@TestDataPath("\$CONTENT_ROOT/resources") class MultiplatformGradleIT : BaseGradleIT() { @Test @@ -293,4 +295,81 @@ class MultiplatformGradleIT : BaseGradleIT() { assertTasksExecuted(customSourceSetCompileTasks) } } + + @Test + fun testMppNodeJsTestRun() = with(Project("new-mpp-js-tests", GradleVersionRequired.AtLeast("4.10.2"))) { + setupWorkingDir() + gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) + gradleSettingsScript().modify(::transformBuildScriptWithPluginsDsl) + + // just build without running tests to check configuration avoidance + build("assemble") { + assertSuccessful() + + assertTasksRegisteredAndNotRealized( + ":clientKotlinJsNodeModules", + ":clientKotlinJsNodeModulesTestRuntime", + ":clientTest", + + ":serverKotlinJsNodeModules", + ":serverKotlinJsNodeModulesTestRuntime", + ":serverTest" + ) + } + + build("check") { + assertSuccessful() + + assertTasksExecuted( + ":clientKotlinJsNodeModules", + ":clientKotlinJsNodeModulesTestRuntime", + ":clientTest", + + ":serverKotlinJsNodeModules", + ":serverKotlinJsNodeModulesTestRuntime", + ":serverTest" + ) + + assertTestResults("testProject/new-mpp-js-tests/TEST-CommonTest.xml", "clientTest") + assertTestResults("testProject/new-mpp-js-tests/TEST-CommonTest.xml", "serverTest") + } + + // test all is up-to-date when no changes + build("check") { + assertSuccessful() + + assertTasksUpToDate( + ":clientKotlinJsNodeModules", + ":clientKotlinJsNodeModulesTestRuntime", + ":clientTest", + + ":serverKotlinJsNodeModules", + ":serverKotlinJsNodeModulesTestRuntime", + ":serverTest" + ) + } + + // change common file and check that all tasks executed + projectDir.resolve("src/commonMain/kotlin/common.kt").writeText("fun common() = 777") + + build("check") { + assertSuccessful() + + assertTasksUpToDate( + ":clientKotlinJsNodeModulesTestRuntime", + ":serverKotlinJsNodeModulesTestRuntime" + ) + + assertTasksExecuted( + ":clientKotlinJsNodeModules", + ":clientTest", + + ":serverKotlinJsNodeModules", + ":serverTest" + ) + + assertTestResults("testProject/new-mpp-js-tests/TEST-CommonTest-2.xml", "clientTest") + assertTestResults("testProject/new-mpp-js-tests/TEST-CommonTest-2.xml", "serverTest") + } + } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/TEST-CommonTest-2.xml b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/TEST-CommonTest-2.xml new file mode 100644 index 00000000000..b70866724b4 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/TEST-CommonTest-2.xml @@ -0,0 +1,32 @@ + + + + + + ... + + + + + + ... + + + + + + + + + + + + + + + + + + + + diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/TEST-CommonTest.xml b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/TEST-CommonTest.xml new file mode 100644 index 00000000000..1e5a796318d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/TEST-CommonTest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + ... + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/build.gradle.kts new file mode 100644 index 00000000000..cc0819b599c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/build.gradle.kts @@ -0,0 +1,42 @@ +import org.jetbrains.kotlin.gradle.targets.js.tasks.KotlinNodeJsTestTask + +plugins { + kotlin("multiplatform").version("") +} + +repositories { + mavenLocal() + mavenCentral() + jcenter() +} + +kotlin { + dependencies { + commonMainImplementation(kotlin("stdlib-common")) + commonTestApi(kotlin("test-common")) + } + + val jsCommon = js("jsCommon") { + dependencies { + commonMainImplementation(kotlin("stdlib-js")) + commonTestApi(kotlin("test-js")) + } + } + + js("server") + js("client") +} + +tasks { + "jsCommonTest"(KotlinNodeJsTestTask::class) { + ignoreFailures = true + } + + "clientTest"(KotlinNodeJsTestTask::class) { + ignoreFailures = true + } + + "serverTest"(KotlinNodeJsTestTask::class) { + ignoreFailures = true + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/settings.gradle.kts new file mode 100644 index 00000000000..93b60c43717 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/settings.gradle.kts @@ -0,0 +1,8 @@ +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + } +} + +rootProject.name = "new-mpp-js-tests" \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/src/commonMain/kotlin/common.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/src/commonMain/kotlin/common.kt new file mode 100644 index 00000000000..57ebd30cce6 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/src/commonMain/kotlin/common.kt @@ -0,0 +1 @@ +fun common() = 1 \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/src/commonTest/kotlin/CommonTest.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/src/commonTest/kotlin/CommonTest.kt new file mode 100644 index 00000000000..651c855d4f9 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-js-tests/src/commonTest/kotlin/CommonTest.kt @@ -0,0 +1,31 @@ +package my.pack.name + +import common +import kotlin.test.Ignore +import kotlin.test.Test +import kotlin.test.assertEquals + +class CommonTest { + @Test + fun test1() = assertEquals(common(), 1) + + @Test + @Ignore + fun test2() = assertEquals(common(), 2) + + @Test + fun test3() = assertEquals(common(), 3) + + @Ignore + class InnerIgnored { + @Test + fun test4() = assertEquals(common(), 1) + + @Test + @Ignore + fun test5() = assertEquals(common(), 2) + + @Test + fun test6() = assertEquals(common(), 3) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin/build.gradle.kts index 1d2086abacb..8d95877c241 100644 --- a/libraries/tools/kotlin-gradle-plugin/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin/build.gradle.kts @@ -15,13 +15,14 @@ publish() // todo: make lazy val jar: Jar by tasks -runtimeJar(rewriteDepsToShadedCompiler(jar)) +val jarContents by configurations.creating sourcesJar() javadocJar() repositories { google() + maven(url = "https://plugins.gradle.org/m2/") } pill { @@ -61,6 +62,9 @@ dependencies { runtime(projectRuntimeJar(":kotlin-scripting-compiler-embeddable")) runtime(project(":kotlin-reflect")) + jarContents(compileOnly(intellijDep()) { includeJars("serviceMessages") }) + jarContents(projectArchives(":kotlin-test-nodejs-runner")) + // com.android.tools.build:gradle has ~50 unneeded transitive dependencies compileOnly("com.android.tools.build:gradle:3.0.0") { isTransitive = false } compileOnly("com.android.tools.build:gradle-core:3.0.0") { isTransitive = false } @@ -77,6 +81,17 @@ dependencies { testCompileOnly(project(":kotlin-annotation-processing-gradle")) } +runtimeJar(rewriteDepsToShadedCompiler(jar)) { + dependsOn(jarContents) + + from { + jarContents.asFileTree.map { + if (it.endsWith(".jar")) zipTree(it) + else it + } + } +} + tasks { withType { kotlinOptions.jdkHome = rootProject.extra["JDK_18"] as String diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesClient.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesClient.kt new file mode 100644 index 00000000000..a290ce8e5ee --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesClient.kt @@ -0,0 +1,310 @@ +package org.jetbrains.kotlin.gradle.internal.testing + +import jetbrains.buildServer.messages.serviceMessages.* +import org.gradle.api.internal.tasks.testing.* +import org.gradle.api.tasks.testing.TestOutputEvent.Destination.StdErr +import org.gradle.api.tasks.testing.TestOutputEvent.Destination.StdOut +import org.gradle.api.tasks.testing.TestResult +import org.gradle.api.tasks.testing.TestResult.ResultType.* +import org.gradle.internal.operations.OperationIdentifier +import org.jetbrains.kotlin.gradle.logging.kotlinDebug +import org.jetbrains.kotlin.gradle.targets.js.NodeJsTestFailure +import org.slf4j.Logger +import java.text.ParseException +import java.lang.System.currentTimeMillis as currentTimeMillis1 + +data class TCServiceMessagesClientSettings( + val rootNodeName: String, + val nameOfRootSuiteToAppend: String? = null, + val nameOfRootSuiteToReplace: String? = null, + val nameOfLeafTestToAppend: String? = null, + val skipRoots: Boolean = false +) { + init { + if (skipRoots) { + check(nameOfRootSuiteToReplace == null) { "nameOfRootSuiteToReplace makes no sense when skipRoots is set" } + check(nameOfRootSuiteToAppend == null) { "nameOfRootSuiteToAppend cannot work with skipRoots" } + } + } +} + +internal class TCServiceMessagesClient( + private val results: TestResultProcessor, + val settings: TCServiceMessagesClientSettings, + val log: Logger +) : ServiceMessageParserCallback { + inline fun root(operation: OperationIdentifier, actions: () -> Unit) { + RootNode(operation.id).open { root -> + if (settings.nameOfRootSuiteToAppend != null) { + SuiteNode(root, settings.nameOfRootSuiteToAppend).open { + actions() + } + } else { + actions() + } + } + } + + override fun parseException(e: ParseException, text: String) { + log.error("Failed to parse test process messages: \"$text\"", e) + } + + override fun serviceMessage(message: ServiceMessage) { + log.kotlinDebug { "TCSM: $message" } + + when (message) { + is TestSuiteStarted -> open(message.ts, SuiteNode(leaf, hookSuiteName(leaf, message.suiteName))) + is TestStarted -> beginTest(message.ts, message.testName) + is TestStdOut -> results.output(requireLeafTest().descriptor, DefaultTestOutputEvent(StdOut, message.stdOut)) + is TestStdErr -> results.output(requireLeafTest().descriptor, DefaultTestOutputEvent(StdErr, message.stdErr)) + is TestFailed -> requireLeafTest().failure(message) + is TestFinished -> endTest(message.ts, message.testName) + is TestIgnored -> { + if (message.attributes["suite"] == "true") { + // non standard property for dealing with ignored test suites without visiting all inner tests + SuiteNode(requireLeaf(), message.testName).open(message.ts) { message.ts } + } else { + beginTest(message.ts, message.testName, isIgnored = true) + endTest(message.ts, message.testName) + } + } + is TestSuiteFinished -> close(message.ts, hookSuiteName(leaf?.parent, message.suiteName)) + else -> Unit + } + } + + private fun hookSuiteName(parent: Node?, originalName: String) = + if (parent?.parent == null && settings.nameOfRootSuiteToReplace != null) settings.nameOfRootSuiteToReplace + else originalName + + override fun regularText(text: String) { + log.kotlinDebug { "TCSM stdout captured: $text" } + + val test = leaf as? TestNode + if (test != null) { + results.output(test.descriptor, DefaultTestOutputEvent(StdOut, text)) + } else { + println(text) + } + } + + private fun beginTest(ts: Long, testName: String, isIgnored: Boolean = false) { + val parent = requireLeafGroup() + + val parsedName = ParsedTestName(testName, parent.localId) + + if (settings.nameOfLeafTestToAppend != null) { + val group = open(ts, SuiteNode(parent, parsedName.methodName)) + open(ts, TestNode( + group, parsedName.className, parsedName.classDisplayName, parsedName.methodName, + displayName = "${parsedName.methodName}.${settings.nameOfLeafTestToAppend}", + localId = "$testName.${settings.nameOfLeafTestToAppend}", + ignored = isIgnored + )) + } else { + open(ts, TestNode( + parent, parsedName.className, parsedName.classDisplayName, parsedName.methodName, + displayName = parsedName.methodName, + localId = testName, + ignored = isIgnored + )) + } + } + + private fun endTest(ts: Long, testName: String) { + val parsedName = ParsedTestName(testName, leaf!!.parent!!.localId) + + if (settings.nameOfLeafTestToAppend != null) { + close(ts, "$testName.${settings.nameOfLeafTestToAppend}") + close(ts, parsedName.methodName) + } else { + close(ts, testName) + } + } + + private fun Node.failure( + message: TestFailed + ) { + hasFailures = true + results.failure(descriptor.id, NodeJsTestFailure(message.messageName, message.stacktrace)) + } + + private inline fun NodeType.open(contents: (NodeType) -> Unit) = open(System.currentTimeMillis()) { + contents(it) + System.currentTimeMillis() + } + + + private inline fun NodeType.open(tsStart: Long, contents: (NodeType) -> Long) { + val child = open(tsStart, this@open) + val tsEnd = contents(child) + assert(close(tsEnd, child.localId) === child) + } + + private fun open(ts: Long, new: NodeType): NodeType = new.also { + log.kotlinDebug { "Test node opened: $it" } + + if (!it.isSkippedRoot) { + results.started(it.descriptor, TestStartEvent(ts, it.reportingParent?.descriptor?.id)) + } + push(it) + } + + private fun close(ts: Long, assertLocalId: String?) = pop().also { + if (assertLocalId != null) { + check(it.localId == assertLocalId) { + "Bad TCSM: unexpected node to close: ${it.localId}, stack: ${ + collectParents().joinToString("") { item -> "\n - ${item.localId}" } + }\n" + } + } + + log.kotlinDebug { "Test node closed: $it" } + + if (!it.isSkippedRoot) { + results.completed(it.descriptor.id, TestCompleteEvent(ts, it.resultType)) + } + } + + private fun collectParents(): MutableList { + var i = leaf + val items = mutableListOf() + while (i != null) { + items.add(i) + i = i.parent + } + return items + } + + class ParsedTestName(testName: String, parentName: String) { + val hasClassName: Boolean + val className: String + val classDisplayName: String + val methodName: String + + init { + val methodNameCut = testName.lastIndexOf('.') + hasClassName = methodNameCut != -1 + + if (hasClassName) { + className = testName.substring(0, methodNameCut) + classDisplayName = className.substringAfterLast('.') + methodName = testName.substring(methodNameCut + 1) + } else { + className = parentName + classDisplayName = parentName + methodName = testName + } + } + } + + + /** + * Node of tests tree + */ + abstract inner class Node( + var parent: Node? = null, + val localId: String + ) { + val reportingParent: Node? + get() = when { + parent == null -> null + parent!!.isSkippedRoot -> parent!!.reportingParent + else -> parent + } + + val isSkippedRoot: Boolean + get() = settings.skipRoots && parent != null && parent!!.parent == null + + val id: String = if (parent != null) "${reportingParent?.descriptor?.id}.$localId" else localId + + abstract val descriptor: TestDescriptorInternal + + var hasFailures: Boolean = false + set(value) { + // traverse parents only on first failure + if (!field) { + field = value + parent?.hasFailures = true + } + } + + /** + * If all tests in group are ignored, then group marked as skipped. + * This is workaround for absence of ignored test suite flag in TC service messages protocol. + */ + var containsNotIgnored: Boolean = false + set(value) { + // traverse parents only on first test + if (!field) { + field = value + parent?.containsNotIgnored = true + } + } + + val resultType: TestResult.ResultType + get() = when { + containsNotIgnored -> when { + hasFailures -> FAILURE + else -> SUCCESS + } + else -> SKIPPED + } + + override fun toString(): String = descriptor.toString() + } + + inner class RootNode(val ownerBuildOperationId: Any) : Node(null, settings.rootNodeName) { + override val descriptor = + object : DefaultTestSuiteDescriptor(settings.rootNodeName, localId) { + override fun getOwnerBuildOperationId(): Any? = this@RootNode.ownerBuildOperationId + } + } + + inner class SuiteNode(parent: Node? = null, name: String) : Node(parent, name) { + override val descriptor = object : DefaultTestSuiteDescriptor(id, name) { + override fun getParent(): TestDescriptorInternal? = this@SuiteNode.parent?.descriptor + } + } + + inner class TestNode( + parent: Node, + className: String, + classDisplayName: String, + methodName: String, + displayName: String, + localId: String, + ignored: Boolean = false + ) : Node(parent, localId) { + override val descriptor = object : DefaultTestDescriptor(id, className, methodName, classDisplayName, displayName) { + override fun getParent(): TestDescriptorInternal? = this@TestNode.parent?.descriptor + } + + init { + if (!ignored) containsNotIgnored = true + } + } + + private var leaf: Node? = null + + private val ServiceMessage.ts: Long + get() = creationTimestamp?.timestamp?.time ?: System.currentTimeMillis() + + private fun push(node: Node) = node.also { leaf = node } + private fun pop() = leaf!!.also { leaf = it.parent } + + fun closeAll() { + val ts = System.currentTimeMillis() + + while (leaf != null) { + close(ts, leaf!!.localId) + } + } + + private fun requireLeaf() = leaf ?: error("test out of group") + private fun requireLeafGroup() = requireLeaf().also { + check(it !is TestNode) { "previous test `$it` not finished"} + } + private fun requireLeafTest() = leaf as? TestNode + ?: error("no running test") +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesTestExecutor.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesTestExecutor.kt new file mode 100644 index 00000000000..09a38da17b2 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesTestExecutor.kt @@ -0,0 +1,86 @@ +package org.jetbrains.kotlin.gradle.internal.testing + +import jetbrains.buildServer.messages.serviceMessages.ServiceMessage +import org.gradle.api.internal.tasks.testing.TestExecuter +import org.gradle.api.internal.tasks.testing.TestExecutionSpec +import org.gradle.api.internal.tasks.testing.TestResultProcessor +import org.gradle.internal.operations.BuildOperationExecutor +import org.gradle.process.ProcessForkOptions +import org.gradle.process.internal.ExecHandle +import org.gradle.process.internal.ExecHandleFactory +import org.slf4j.LoggerFactory +import java.io.PipedInputStream +import java.io.PipedOutputStream +import kotlin.concurrent.thread + +data class TCServiceMessagesTestExecutionSpec( + val forkOptions: ProcessForkOptions, + val args: List, + val clientSettings: TCServiceMessagesClientSettings +) : TestExecutionSpec + +private val log = LoggerFactory.getLogger("org.jetbrains.kotlin.gradle.tasks.testing") + +class TCServiceMessagesTestExecutor( + val execHandleFactory: ExecHandleFactory, + val buildOperationExecutor: BuildOperationExecutor +) : TestExecuter { + var execHandle: ExecHandle? = null + var outputReaderThread: Thread? = null + var shouldStop = false + + override fun execute(spec: TCServiceMessagesTestExecutionSpec, testResultProcessor: TestResultProcessor) { + val stdInPipe = PipedInputStream() + + val rootOperation = buildOperationExecutor.currentOperation.parentId + + outputReaderThread = thread(name = "${spec.forkOptions} output reader") { + try { + val client = TCServiceMessagesClient(testResultProcessor, spec.clientSettings, log) + + client.root(rootOperation) { + stdInPipe.reader().useLines { lines -> + lines.forEach { + if (shouldStop) { + client.closeAll() + return@thread + } + + try { + ServiceMessage.parse(it, client) + } catch (e: Exception) { + log.error( + "Error while processing test process output message \"$it\"", + e + ) + } + } + } + } + } catch (t: Throwable) { + log.error("Error creating TCServiceMessagesClient", t) + } + } + + val exec = execHandleFactory.newExec() + spec.forkOptions.copyTo(exec) + exec.args = spec.args + exec.standardOutput = PipedOutputStream(stdInPipe) + + execHandle = exec.build() + + execHandle!!.start() + val result = execHandle!!.waitForFinish() + outputReaderThread!!.join() + + if (result.exitValue != 0) { + error("$execHandle exited with errors (exit code: ${result.exitValue})") + } + } + + override fun stopNow() { + shouldStop = true + execHandle?.abort() + outputReaderThread?.join() + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsCompilationTestsConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsCompilationTestsConfigurator.kt new file mode 100644 index 00000000000..ecc8554773e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsCompilationTestsConfigurator.kt @@ -0,0 +1,118 @@ +package org.jetbrains.kotlin.gradle.targets.js + +import org.gradle.api.Project +import org.gradle.api.internal.plugins.DslObject +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.testing.base.plugins.TestingBasePlugin +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExtension +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsSetupTask +import org.jetbrains.kotlin.gradle.targets.js.tasks.KotlinJsNodeModulesTask +import org.jetbrains.kotlin.gradle.targets.js.tasks.KotlinNodeJsTestRuntimeToNodeModulesTask +import org.jetbrains.kotlin.gradle.targets.js.tasks.KotlinNodeJsTestTask +import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile +import org.jetbrains.kotlin.gradle.tasks.registerTask +import java.io.File + +internal class KotlinJsCompilationTestsConfigurator( + val compilation: KotlinCompilationToRunnableFiles<*> +) { + private val target get() = compilation.target + private val project get() = target.project + private val compileTestKotlin2Js get() = compilation.compileKotlinTask as Kotlin2JsCompile + private val isSinglePlatformProject get() = target is KotlinWithJavaTarget<*> + private val testTaskName: String + get() = if (isSinglePlatformProject) "testJs" else camelCaseTargetName("test") + + private fun camelCaseTargetName(prefix: String): String { + return if (isSinglePlatformProject) prefix + else target.name + prefix.capitalize() + } + + @Suppress("SameParameterValue") + private fun underscoredCompilationName(prefix: String): String { + return if (isSinglePlatformProject) prefix + else "${target.name}_${compilation.name}_$prefix" + } + + private val nodeModulesDir + get() = project.buildDir.resolve(underscoredCompilationName("node_modules")) + + @Suppress("UnstableApiUsage") + private val Project.testResults: File + get() = project.buildDir.resolve(TestingBasePlugin.TEST_RESULTS_DIR_NAME) + + @Suppress("UnstableApiUsage") + private val Project.testReports: File + get() = project.buildDir.resolve(TestingBasePlugin.TESTS_DIR_NAME) + + private val compileTask: Kotlin2JsCompile + get() = project.tasks.findByName(compileTestKotlin2Js.name) as Kotlin2JsCompile + + private val Kotlin2JsCompile.jsRuntimeClasspath: FileCollection + get() = classpath.plus(project.files(destinationDir)) + + fun configure() { + val nodeModulesTask = registerTask( + project, + camelCaseTargetName("kotlinJsNodeModules"), + KotlinJsNodeModulesTask::class.java + ) { + it.dependsOn(compileTestKotlin2Js) + + it.nodeModulesDir = nodeModulesDir + it.classpath = compileTask.jsRuntimeClasspath + } + + val nodeModulesTestRuntimeTask = registerTask( + project, + camelCaseTargetName("kotlinJsNodeModulesTestRuntime"), + KotlinNodeJsTestRuntimeToNodeModulesTask::class.java + ) { + it.nodeModulesDir = nodeModulesDir + } + + val projectWithNodeJsPlugin = NodeJsPlugin.ensureAppliedInHierarchy(target.project) + + val testTask = registerTask(project, testTaskName, KotlinNodeJsTestTask::class.java) { testJs -> + testJs.group = "verification" + + testJs.dependsOn( + nodeModulesTask.getTaskOrProvider(), + nodeModulesTestRuntimeTask.getTaskOrProvider() + ) + + if (!isSinglePlatformProject) { + testJs.targetName = target.name + } + + testJs.nodeJsProcessOptions.workingDir = project.projectDir + + testJs.nodeModulesDir = nodeModulesDir + testJs.nodeModulesToLoad = setOf(compileTestKotlin2Js.outputFile.name) + + val htmlReport = DslObject(testJs.reports.html) + val xmlReport = DslObject(testJs.reports.junitXml) + + xmlReport.conventionMapping.map("destination") { project.testResults.resolve(testJs.name) } + htmlReport.conventionMapping.map("destination") { project.testReports.resolve(testJs.name) } + testJs.conventionMapping.map("binResultsDir") { project.testResults.resolve(testJs.name + "/binary") } + } + + project.afterEvaluate { + project.tasks.findByName(JavaBasePlugin.CHECK_TASK_NAME)?.dependsOn(testTask.getTaskOrProvider()) + + // defer nodeJs executable setup, as nodejs project settings may change during configuration + testTask.configure { + val nodeJsSetupTask = projectWithNodeJsPlugin.tasks.findByName(NodeJsSetupTask.NAME) + it.dependsOn(nodeJsSetupTask) + + if (it.nodeJsProcessOptions.executable == null) { + it.nodeJsProcessOptions.executable = NodeJsExtension[projectWithNodeJsPlugin].buildEnv().nodeExec + } + } + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt index 2ccfa15e0f9..4cbfc278876 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt @@ -5,21 +5,14 @@ package org.jetbrains.kotlin.gradle.targets.js -import org.gradle.api.plugins.JavaBasePlugin -import org.jetbrains.kotlin.gradle.plugin.Kotlin2JsSourceSetProcessor -import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles -import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProcessor -import org.jetbrains.kotlin.gradle.plugin.KotlinTargetConfigurator +import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget -import org.jetbrains.kotlin.gradle.targets.js.tasks.KotlinJsNodeModulesTask -import org.jetbrains.kotlin.gradle.targets.js.tasks.KotlinNodeJsTestRuntimeToNodeModulesTask -import org.jetbrains.kotlin.gradle.targets.js.tasks.KotlinNodeJsTestTask -import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider class KotlinJsTargetConfigurator(kotlinPluginVersion: String) : - KotlinTargetConfigurator(true, true, kotlinPluginVersion) { + KotlinTargetConfigurator(true, true, kotlinPluginVersion) { override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> { val tasksProvider = KotlinTasksProvider(compilation.target.targetName) @@ -28,59 +21,11 @@ class KotlinJsTargetConfigurator(kotlinPluginVersion: String) : override fun configureTest(target: KotlinOnlyTarget) { target.compilations.all { - configureTest(it) - } - } + it.compileKotlinTask.kotlinOptions.moduleKind = "umd" - private fun configureTest(compilation: KotlinCompilationToRunnableFiles<*>) { - val target = compilation.target - val project = target.project - val name = compilation.name - val compileTestKotlin2Js = compilation.compileKotlinTask as Kotlin2JsCompile - val isDefaultTarget = compilation.name.isBlank() - - fun camelCaseName(prefix: String): String { - return if (isDefaultTarget) prefix - else name + prefix.capitalize() - } - - fun underscoredName(prefix: String): String { - return if (isDefaultTarget) prefix - else "${name}_$prefix" - } - - val nodeModulesDir = project.buildDir.resolve(underscoredName("node_modules")) - val nodeModulesTask = project.tasks.create( - camelCaseName("kotlinJsNodeModules"), - KotlinJsNodeModulesTask::class.java - ) { - it.dependsOn(compileTestKotlin2Js) - - it.nodeModulesDir = nodeModulesDir - it.compile = compileTestKotlin2Js - } - - val nodeModulesTestRuntimeTask = project.tasks.create( - camelCaseName("kotlinJsNodeModulesTestRuntime"), - KotlinNodeJsTestRuntimeToNodeModulesTask::class.java - ) { - it.nodeModulesDir = nodeModulesDir - } - - val testJs: KotlinNodeJsTestTask = - if (isDefaultTarget) project.tasks.create("testJs", KotlinNodeJsTestTask::class.java) - else project.tasks.replace(camelCaseName("test"), KotlinNodeJsTestTask::class.java) - - testJs.also { - it.group = "verification" - it.dependsOn(nodeModulesTask, nodeModulesTestRuntimeTask) - - it.nodeModulesDir = nodeModulesDir - it.nodeModulesToLoad = setOf(compileTestKotlin2Js.outputFile.name) - } - - project.afterEvaluate { - project.tasks.findByName(JavaBasePlugin.CHECK_TASK_NAME)?.dependsOn(testJs) + if (it.name == KotlinCompilation.TEST_COMPILATION_NAME) { + KotlinJsCompilationTestsConfigurator(it).configure() + } } } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/NodeJsTestFailure.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/NodeJsTestFailure.kt new file mode 100644 index 00000000000..d71da37be43 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/NodeJsTestFailure.kt @@ -0,0 +1,15 @@ +package org.jetbrains.kotlin.gradle.targets.js + +/** + * Class to be shown in default Gradle tests console reporter. + * + * Example console output: + * ``` + * clientTest.CommonTest.test1 FAILED + * org.jetbrains.kotlin.gradle.targets.js.NodeJsTestFailure + * ``` + */ +class NodeJsTestFailure(message: String, val stackTrace: String?) : Throwable(message) { + override fun fillInStackTrace(): Throwable = this + override fun toString(): String = stackTrace ?: "" +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsEnv.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsEnv.kt new file mode 100644 index 00000000000..223f097d5ac --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsEnv.kt @@ -0,0 +1,17 @@ +package org.jetbrains.kotlin.gradle.targets.js.nodejs + +import java.io.* + +internal data class NodeJsEnv( + val nodeDir: File, + val nodeBinDir: File, + val nodeExec: String, + val npmExec: String, + + val platformName: String, + val architectureName: String, + val ivyDependency: String +) { + val isWindows: Boolean + get() = platformName == "win" +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsExtension.kt new file mode 100644 index 00000000000..c40c04ed88a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsExtension.kt @@ -0,0 +1,68 @@ +package org.jetbrains.kotlin.gradle.targets.js.nodejs + +import org.gradle.api.GradleException +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.logging.kotlinInfo +import java.io.File + +open class NodeJsExtension(project: Project) { + private val gradleHome = project.gradle.gradleUserHomeDir.also { + project.logger.kotlinInfo("Storing cached files in $it") + } + + var installationDir = gradleHome.resolve("nodejs") + + var distBaseUrl = "https://nodejs.org/dist" + var version = "10.15.3" + var npmVersion = "" + + var nodeCommand = "node" + var npmCommand = "npm" + + var download = true + + internal fun buildEnv(): NodeJsEnv { + val platform = NodeJsPlatform.name + val architecture = NodeJsPlatform.architecture + + val nodeDir = installationDir.resolve("node-v$version-$platform-$architecture") + val isWindows = NodeJsPlatform.name == NodeJsPlatform.WIN + val nodeBinDir = if (isWindows) nodeDir else nodeDir.resolve("bin") + + fun getExecutable(command: String, customCommand: String, windowsExtension: String): String { + val finalCommand = if (isWindows && customCommand == command) "$command.$windowsExtension" else customCommand + return if (download) File(nodeBinDir, finalCommand).absolutePath else finalCommand + } + + fun getIvyDependency(): String { + val type = if (isWindows) "zip" else "tar.gz" + return "org.nodejs:node:$version:$platform-$architecture@$type" + } + + return NodeJsEnv( + nodeDir = nodeDir, + nodeBinDir = nodeBinDir, + nodeExec = getExecutable("node", nodeCommand, "exe"), + npmExec = getExecutable("npm", npmCommand, "cmd"), + platformName = platform, + architectureName = architecture, + ivyDependency = getIvyDependency() + ) + } + + companion object { + const val NODE_JS: String = "nodeJs" + + operator fun get(project: Project): NodeJsExtension { + val extension = project.extensions.findByType(NodeJsExtension::class.java) + if (extension != null) + return extension + + val parentProject = project.parent + if (parentProject != null) + return get(parentProject) + + throw GradleException("NodeJsExtension is not installed") + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsPlatform.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsPlatform.kt new file mode 100644 index 00000000000..8d25659b6b0 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsPlatform.kt @@ -0,0 +1,49 @@ +package org.jetbrains.kotlin.gradle.targets.js.nodejs + +/** + * Provides platform and architecture names that is used to download NodeJs. + * See [NodeJsEnv.ivyDependency] that is filled in [NodeJsExtension.buildEnv]. + */ +internal object NodeJsPlatform { + private val props = System.getProperties() + private fun property(name: String) = props.getProperty(name) ?: System.getProperty(name) + + const val WIN = "win" + const val DARWIN = "darwin" + const val LINUX = "linux" + const val SUNOS = "sunos" + + val name: String = run { + val name = property("os.name").toLowerCase() + when { + name.contains("windows") -> WIN + name.contains("mac") -> DARWIN + name.contains("linux") -> LINUX + name.contains("freebsd") -> LINUX + name.contains("sunos") -> SUNOS + else -> throw IllegalArgumentException("Unsupported OS: $name") + } + } + + const val X64 = "x64" + const val X86 = "x86" + const val ARM64 = "arm64" + + val architecture: String = run { + val arch = property("os.arch").toLowerCase() + when { + arch.contains("64") -> X64 + arch == "arm" -> { + // as Java just returns "arm" on all ARM variants, we need a system call to determine the exact arch + // the node binaries for 'armv8l' are called 'arm64', so we need to distinguish here + val process = Runtime.getRuntime().exec("uname -m") + val systemArch = process.inputStream.bufferedReader().use { it.readText() } + when (systemArch.trim()) { + "armv8l" -> ARM64 + else -> systemArch + } + } + else -> X86 + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsPlugin.kt new file mode 100644 index 00000000000..02cbced2834 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsPlugin.kt @@ -0,0 +1,29 @@ +package org.jetbrains.kotlin.gradle.targets.js.nodejs + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExtension.Companion.NODE_JS + +open class NodeJsPlugin : Plugin { + override fun apply(project: Project): Unit = project.run { + this.extensions.create(NODE_JS, NodeJsExtension::class.java, this) + tasks.create(NodeJsSetupTask.NAME, NodeJsSetupTask::class.java) + } + + companion object { + fun ensureAppliedInHierarchy(myProject: Project): Project = + findInHeirarchy(myProject) ?: myProject.also { + it.pluginManager.apply(NodeJsPlugin::class.java) + } + + fun findInHeirarchy(myProject: Project): Project? { + var project: Project? = myProject + while (project != null) { + if (myProject.plugins.hasPlugin(NodeJsPlugin::class.java)) return project + project = project.parent + } + + return null + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsSetupTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsSetupTask.kt new file mode 100644 index 00000000000..9a9c0fec74d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsSetupTask.kt @@ -0,0 +1,85 @@ +package org.jetbrains.kotlin.gradle.targets.js.nodejs + +import org.gradle.api.DefaultTask +import org.gradle.api.artifacts.repositories.IvyPatternRepositoryLayout +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import org.jetbrains.kotlin.gradle.logging.kotlinInfo +import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast +import java.io.File +import java.net.URI + +open class NodeJsSetupTask : DefaultTask() { + private val settings = NodeJsExtension[project] + private val env by lazy { settings.buildEnv() } + + init { + group = NodeJsExtension.NODE_JS + description = "Download and install a local node/npm version." + } + + val input: Set + @Input get() = setOf(settings.download.toString(), env.ivyDependency) + + val destination: File + @OutputDirectory get() = env.nodeDir + + @Suppress("unused") + @TaskAction + fun exec() { + if (!settings.download) + return + + @Suppress("UnstableApiUsage", "DEPRECATION") + val repo = project.repositories.ivy { repo -> + repo.name = "Node Distributions at ${settings.distBaseUrl}" + repo.url = URI(settings.distBaseUrl) + repo.layout("pattern") { layout -> + layout as IvyPatternRepositoryLayout + layout.artifact("v[revision]/[artifact](-v[revision]-[classifier]).[ext]") + layout.ivy("v[revision]/ivy.xml") + } + repo.metadataSources { it.artifact() } + + if (isGradleVersionAtLeast(5, 1)) { + repo.content { it.includeModule("org.nodejs", "node") } + } + } + + val dep = this.project.dependencies.create(env.ivyDependency) + val conf = this.project.configurations.detachedConfiguration(dep) + conf.isTransitive = false + val result = conf.resolve().single() + project.repositories.remove(repo) + + project.logger.kotlinInfo("Using node distribution from '$result'") + + unpackNodeArchive(result, destination.parentFile) // parent because archive contains name already + + if (!env.isWindows) { + File(env.nodeExec).setExecutable(true) + } + } + + private fun unpackNodeArchive(archive: File, destination: File) { + project.logger.kotlinInfo("Unpacking $archive to $destination") + + when { + archive.name.endsWith("zip") -> project.copy { + it.from(project.zipTree(archive)) + it.into(destination) + } + else -> { + project.copy { + it.from(project.tarTree(archive)) + it.into(destination) + } + } + } + } + + companion object { + const val NAME: String = "nodeJsSetup" + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinJsNodeModulesTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinJsNodeModulesTask.kt new file mode 100644 index 00000000000..ee40faa172e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinJsNodeModulesTask.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. 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.gradle.targets.js.tasks + +import org.gradle.api.DefaultTask +import org.gradle.api.file.FileCollection +import org.gradle.api.tasks.* +import java.io.File + +open class KotlinJsNodeModulesTask : DefaultTask() { + @OutputDirectory + @SkipWhenEmpty + lateinit var nodeModulesDir: File + + @Classpath + @InputFiles + @SkipWhenEmpty + lateinit var classpath: FileCollection + + @TaskAction + fun copyFromRuntimeClasspath() { + project.copy { copy -> + copy.includeEmptyDirs = false + + classpath.forEach { + if (it.isZip) copy.from(project.zipTree(it)) + else copy.from(it) + } + + copy.include { fileTreeElement -> + isKotlinJsRuntimeFile(fileTreeElement.file) + } + + copy.into(nodeModulesDir) + } + } +} + +val File.isZip + get() = isFile && name.endsWith(".jar") + +fun isKotlinJsRuntimeFile(file: File): Boolean { + if (!file.isFile) return false + val name = file.name + return (name.endsWith(".js") && !name.endsWith(".meta.js")) + || name.endsWith(".js.map") +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinNodeJsTestRuntime.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinNodeJsTestRuntime.kt new file mode 100644 index 00000000000..a4ce1042c22 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinNodeJsTestRuntime.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. 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.gradle.targets.js.tasks + +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.* +import java.io.File +import java.nio.file.Files +import java.nio.file.StandardCopyOption + +const val kotlinNodeJsTestRuntimeBin: String = "kotlin-js-test" + +open class KotlinNodeJsTestRuntimeToNodeModulesTask : DefaultTask() { + @Input + public var resourceName: String = "/kotlin-js-test.js" + + @OutputDirectory + @SkipWhenEmpty + public lateinit var nodeModulesDir: File + + @TaskAction + fun copyRuntime() { + val testsRuntime = javaClass.getResourceAsStream(resourceName) ?: error("Cannot find `$resourceName` in resources") + val bin = nodeModulesDir.resolve(".bin").resolve(kotlinNodeJsTestRuntimeBin) + bin.parentFile.mkdirs() + Files.copy( + testsRuntime, + bin.toPath(), + StandardCopyOption.REPLACE_EXISTING + ) + } +} + +data class KotlinNodeJsTestRunnerCliArgs( + val moduleNames: List, + val include: Collection = listOf(), + val exclude: Collection = listOf(), + val ignoredTestSuites: IgnoredTestSuitesReporting = IgnoredTestSuitesReporting.reportAllInnerTestsAsIgnored +) { + fun toList(): List = mutableListOf().also { args -> + if (include.isNotEmpty()) { + args.add("--include") + args.add(include.joinToString(",")) + } + + if (exclude.isNotEmpty()) { + args.add("--exclude") + args.add(exclude.joinToString(",")) + } + + if (ignoredTestSuites !== IgnoredTestSuitesReporting.reportAllInnerTestsAsIgnored) { + args.add("--ignoredTestSuites") + args.add(ignoredTestSuites.name) + } + + args.addAll(moduleNames) + } + + @Suppress("EnumEntryName") + enum class IgnoredTestSuitesReporting { + skip, reportAsIgnoredTest, reportAllInnerTestsAsIgnored + } +} + diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinNodeJsTestTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinNodeJsTestTask.kt new file mode 100644 index 00000000000..41383717e4b --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinNodeJsTestTask.kt @@ -0,0 +1,125 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. 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.gradle.targets.js.tasks + +import org.gradle.api.internal.file.FileResolver +import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter +import org.gradle.api.tasks.* +import org.gradle.api.tasks.testing.AbstractTestTask +import org.gradle.process.ProcessForkOptions +import org.gradle.process.internal.DefaultProcessForkOptions +import org.gradle.process.internal.ExecHandleFactory +import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings +import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec +import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutor +import org.jetbrains.kotlin.gradle.testing.IgnoredTestSuites +import org.jetbrains.kotlin.gradle.testing.TestsGrouping +import org.jetbrains.kotlin.gradle.utils.injected +import java.io.File +import javax.inject.Inject + +open class KotlinNodeJsTestTask : AbstractTestTask() { + @Input + var ignoredTestSuites: IgnoredTestSuites = + IgnoredTestSuites.showWithContents + + @Input + var testsGrouping: TestsGrouping = + TestsGrouping.root + + @Input + @Optional + var targetName: String? = null + + @Input + var excludes = mutableSetOf() + + @InputDirectory + var nodeModulesDir: File? = null + + @Input + @SkipWhenEmpty + var nodeModulesToLoad: Set = setOf() + + @InputFile + @Optional + var testRuntimeNodeModule: File? = null + + @Suppress("UnstableApiUsage") + private val filterExt: DefaultTestFilter + get() = filter as DefaultTestFilter + + init { + filterExt.isFailOnNoMatchingTests = false + } + + @get:Inject + open val fileResolver: FileResolver + get() = injected + + @Suppress("LeakingThis") + @Internal + val nodeJsProcessOptions: ProcessForkOptions = DefaultProcessForkOptions(fileResolver) + + val nodeJsExecutable: String + @Input get() = nodeJsProcessOptions.executable + + val nodeJsWorkingDirCanonicalPath: String + @Input get() = nodeJsProcessOptions.workingDir.canonicalPath + + fun nodeJs(options: ProcessForkOptions.() -> Unit) { + options(nodeJsProcessOptions) + } + + override fun createTestExecutionSpec(): TCServiceMessagesTestExecutionSpec { + val extendedForkOptions = DefaultProcessForkOptions(fileResolver) + nodeJsProcessOptions.copyTo(extendedForkOptions) + + extendedForkOptions.environment.addPath("NODE_PATH", nodeModulesDir!!.canonicalPath) + + val cliArgs = KotlinNodeJsTestRunnerCliArgs( + nodeModulesToLoad.toList(), + filterExt.includePatterns + filterExt.commandLineIncludePatterns, + excludes, + ignoredTestSuites.cli + ) + + val clientSettings = when (testsGrouping) { + TestsGrouping.none -> TCServiceMessagesClientSettings(rootNodeName = name, skipRoots = true) + TestsGrouping.root -> TCServiceMessagesClientSettings(rootNodeName = name, nameOfRootSuiteToReplace = targetName) + TestsGrouping.leaf -> TCServiceMessagesClientSettings( + rootNodeName = name, + skipRoots = true, + nameOfLeafTestToAppend = targetName + ) + } + + return TCServiceMessagesTestExecutionSpec( + extendedForkOptions, + listOf(finalTestRuntimeNodeModule.absolutePath) + cliArgs.toList(), + clientSettings + ) + } + + @get:Inject + open val execHandleFactory: ExecHandleFactory + get() = injected + + private val finalTestRuntimeNodeModule: File + get() = testRuntimeNodeModule + ?: nodeModulesDir!!.resolve(".bin").resolve(kotlinNodeJsTestRuntimeBin) + + override fun createTestExecuter() = TCServiceMessagesTestExecutor( + execHandleFactory, + buildOperationExecutor + ) +} + +private fun MutableMap.addPath(key: String, path: String) { + val prev = get(key) + if (prev == null) set(key, path) + else set(key, prev as String + File.pathSeparator + path) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/options.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/options.kt new file mode 100644 index 00000000000..22790140ce1 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/options.kt @@ -0,0 +1,18 @@ +package org.jetbrains.kotlin.gradle.testing + +import org.jetbrains.kotlin.gradle.targets.js.tasks.KotlinNodeJsTestRunnerCliArgs + +@Suppress("EnumEntryName") +enum class IgnoredTestSuites(val cli: KotlinNodeJsTestRunnerCliArgs.IgnoredTestSuitesReporting) { + hide(KotlinNodeJsTestRunnerCliArgs.IgnoredTestSuitesReporting.skip), + showWithContents(KotlinNodeJsTestRunnerCliArgs.IgnoredTestSuitesReporting.reportAllInnerTestsAsIgnored), + showWithoutContents(KotlinNodeJsTestRunnerCliArgs.IgnoredTestSuitesReporting.reportAsIgnoredTest) +} + +@Suppress("EnumEntryName") +enum class TestsGrouping { + none, + root, + leaf; +} + diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/injected.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/injected.kt new file mode 100644 index 00000000000..c23f4b9ec02 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/injected.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. 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.gradle.utils + +/** + * Utility for functions annotated with @Inject + */ +val injected: Nothing get() = error("Should be injected"); diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.kotlin.platform.nodejs.properties b/libraries/tools/kotlin-gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.kotlin.platform.nodejs.properties new file mode 100644 index 00000000000..c5b1feac01b --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.kotlin.platform.nodejs.properties @@ -0,0 +1 @@ +implementation-class=org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/RecordingTestResultProcessor.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/RecordingTestResultProcessor.kt new file mode 100644 index 00000000000..3914023fed8 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/RecordingTestResultProcessor.kt @@ -0,0 +1,40 @@ +package org.jetbrains.kotlin.gradle.internal.testing + +import org.gradle.api.internal.tasks.testing.* +import org.gradle.api.tasks.testing.TestOutputEvent + +class RecordingTestResultProcessor : TestResultProcessor { + val output = StringBuilder() + var indent = 0 + + fun line(line: String) { + repeat(indent) { output.append(" ") } + output.appendln(line) + } + + override fun started(test: TestDescriptorInternal, event: TestStartEvent) { + val description = when (test) { + is DefaultTestSuiteDescriptor -> "SUITE ${test.displayName}" + is DefaultTestDescriptor -> "TEST displayName: ${test.displayName}, " + + "classDisplayName: ${test.classDisplayName}, " + + "className: ${test.className}, " + + "name: ${test.name}" + else -> error("Unknown test descriptor $test") + } + line("STARTED $description // ${test.id}") + indent++ + } + + override fun output(testId: Any, event: TestOutputEvent) { + line("${event.destination}[${event.message}] // $testId") + } + + override fun failure(testId: Any, result: Throwable) { + line("FAILURE ${result.message} // $testId") + } + + override fun completed(testId: Any, event: TestCompleteEvent) { + indent-- + line("COMPLETED ${event.resultType} // $testId") + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendLeaf.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendLeaf.kt new file mode 100644 index 00000000000..e2aa4de4f86 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendLeaf.kt @@ -0,0 +1,116 @@ +package org.jetbrains.kotlin.gradle.internal.testing.tcsmc + +import jetbrains.buildServer.messages.serviceMessages.TestFinished +import jetbrains.buildServer.messages.serviceMessages.TestStarted +import jetbrains.buildServer.messages.serviceMessages.TestSuiteFinished +import jetbrains.buildServer.messages.serviceMessages.TestSuiteStarted +import org.junit.Test + +class AppendLeaf : TCServiceMessagesClientTest() { + @Test + fun appendLeafWithFqn() { + skipRoots = true + nameOfLeafTestToAppend = "js" + + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE myMethod // root.myMethod + STARTED TEST displayName: myMethod.js, classDisplayName: MyTest, className: my.company.product.MyTest, name: myMethod // root.myMethod.my.company.product.MyTest.myMethod.js + COMPLETED SUCCESS // root.myMethod.my.company.product.MyTest.myMethod.js + COMPLETED SUCCESS // root.myMethod +COMPLETED SUCCESS // root + """) { + serviceMessage(TestSuiteStarted("")) + serviceMessage(TestStarted("my.company.product.MyTest.myMethod", false, null)) + serviceMessage(TestFinished("my.company.product.MyTest.myMethod", 1)) + serviceMessage(TestSuiteFinished("")) + } + } + + @Test + fun appendLeafWithClassName() { + skipRoots = true + nameOfLeafTestToAppend = "js" + + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE myMethod // root.myMethod + STARTED TEST displayName: myMethod.js, classDisplayName: MyTest, className: MyTest, name: myMethod // root.myMethod.MyTest.myMethod.js + COMPLETED SUCCESS // root.myMethod.MyTest.myMethod.js + COMPLETED SUCCESS // root.myMethod +COMPLETED SUCCESS // root + """) { + serviceMessage(TestSuiteStarted("")) + serviceMessage(TestStarted("MyTest.myMethod", false, null)) + serviceMessage(TestFinished("MyTest.myMethod", 1)) + serviceMessage(TestSuiteFinished("")) + } + } + + @Test + fun replaceRootWithSuite() { + nameOfRootSuiteToReplace = "js" + + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE js // root.js + STARTED SUITE MyTest // root.js.MyTest + STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: MyTest, name: myMethod // root.js.MyTest.myMethod + COMPLETED SUCCESS // root.js.MyTest.myMethod + COMPLETED SUCCESS // root.js.MyTest + COMPLETED SUCCESS // root.js +COMPLETED SUCCESS // root + """) { + serviceMessage(TestSuiteStarted("")) + serviceMessage(TestSuiteStarted("MyTest")) + serviceMessage(TestStarted("myMethod", false, null)) + serviceMessage(TestFinished("myMethod", 1)) + serviceMessage(TestSuiteFinished("MyTest")) + serviceMessage(TestSuiteFinished("")) + } + } + + @Test + fun appendLeaf() { + skipRoots = true + nameOfLeafTestToAppend = "js" + + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE myMethod // root.myMethod + STARTED TEST displayName: myMethod.js, classDisplayName: , className: , name: myMethod // root.myMethod.myMethod.js + COMPLETED SUCCESS // root.myMethod.myMethod.js + COMPLETED SUCCESS // root.myMethod +COMPLETED SUCCESS // root + """) { + serviceMessage(TestSuiteStarted("")) + serviceMessage(TestStarted("myMethod", false, null)) + serviceMessage(TestFinished("myMethod", 1)) + serviceMessage(TestSuiteFinished("")) + } + } + + @Test + fun appendLeafWithSuite() { + skipRoots = true + nameOfLeafTestToAppend = "js" + + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE MyTest // root.MyTest + STARTED SUITE myMethod // root.MyTest.myMethod + STARTED TEST displayName: myMethod.js, classDisplayName: MyTest, className: MyTest, name: myMethod // root.MyTest.myMethod.myMethod.js + COMPLETED SUCCESS // root.MyTest.myMethod.myMethod.js + COMPLETED SUCCESS // root.MyTest.myMethod + COMPLETED SUCCESS // root.MyTest +COMPLETED SUCCESS // root + """) { + serviceMessage(TestSuiteStarted("")) + serviceMessage(TestSuiteStarted("MyTest")) + serviceMessage(TestStarted("myMethod", false, null)) + serviceMessage(TestFinished("myMethod", 1)) + serviceMessage(TestSuiteFinished("MyTest")) + serviceMessage(TestSuiteFinished("")) + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendRoot.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendRoot.kt new file mode 100644 index 00000000000..a0e7bf25140 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendRoot.kt @@ -0,0 +1,58 @@ +package org.jetbrains.kotlin.gradle.internal.testing.tcsmc + +import jetbrains.buildServer.messages.serviceMessages.TestFinished +import jetbrains.buildServer.messages.serviceMessages.TestStarted +import org.junit.Test + +class AppendRoot : TCServiceMessagesClientTest() { + @Test + fun testWithFqn() { + nameOfRootSuiteToAppend = "ios" + + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE ios // root.ios + STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: my.company.product.MyTest, name: myMethod // root.ios.my.company.product.MyTest.myMethod + COMPLETED SUCCESS // root.ios.my.company.product.MyTest.myMethod + COMPLETED SUCCESS // root.ios +COMPLETED SUCCESS // root + """) { + serviceMessage(TestStarted("my.company.product.MyTest.myMethod", false, null)) + serviceMessage(TestFinished("my.company.product.MyTest.myMethod", 1)) + } + } + + @Test + fun testWithClassName() { + nameOfRootSuiteToAppend = "ios" + + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE ios // root.ios + STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: MyTest, name: myMethod // root.ios.MyTest.myMethod + COMPLETED SUCCESS // root.ios.MyTest.myMethod + COMPLETED SUCCESS // root.ios +COMPLETED SUCCESS // root + """) { + serviceMessage(TestStarted("MyTest.myMethod", false, null)) + serviceMessage(TestFinished("MyTest.myMethod", 1)) + } + } + + @Test + fun testRoot() { + nameOfRootSuiteToAppend = "ios" + + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE ios // root.ios + STARTED TEST displayName: myMethod, classDisplayName: ios, className: ios, name: myMethod // root.ios.myMethod + COMPLETED SUCCESS // root.ios.myMethod + COMPLETED SUCCESS // root.ios +COMPLETED SUCCESS // root + """) { + serviceMessage(TestStarted("myMethod", false, null)) + serviceMessage(TestFinished("myMethod", 1)) + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/Complex.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/Complex.kt new file mode 100644 index 00000000000..703117bec18 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/Complex.kt @@ -0,0 +1,197 @@ +package org.jetbrains.kotlin.gradle.internal.testing.tcsmc + +import jetbrains.buildServer.messages.serviceMessages.* +import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClient +import org.junit.Test + +class Complex: TCServiceMessagesClientTest() { + @Test + fun testComplexJs() { + nameOfRootSuiteToReplace = "js" + + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE js // root.js + STARTED SUITE MyTest // root.js.MyTest + STARTED TEST displayName: myTest1, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest1 // root.js.MyTest.my.company.product.MyTest.myTest1 + COMPLETED SUCCESS // root.js.MyTest.my.company.product.MyTest.myTest1 + STARTED TEST displayName: myTest2, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest2 // root.js.MyTest.my.company.product.MyTest.myTest2 + FAILURE testFailed // root.js.MyTest.my.company.product.MyTest.myTest2 + COMPLETED FAILURE // root.js.MyTest.my.company.product.MyTest.myTest2 + STARTED TEST displayName: myTest3, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest3 // root.js.MyTest.my.company.product.MyTest.myTest3 + COMPLETED SKIPPED // root.js.MyTest.my.company.product.MyTest.myTest3 + STARTED SUITE MyTestNested // root.js.MyTest.MyTestNested + STARTED TEST displayName: myTest4, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest4 // root.js.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest4 + COMPLETED SUCCESS // root.js.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest4 + STARTED TEST displayName: myTest5, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest5 // root.js.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest5 + FAILURE testFailed // root.js.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest5 + COMPLETED FAILURE // root.js.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest5 + STARTED TEST displayName: myTest6, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest6 // root.js.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest6 + COMPLETED SKIPPED // root.js.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest6 + STARTED SUITE MyTestNestedNested // root.js.MyTest.MyTestNested.MyTestNestedNested + STARTED TEST displayName: myTest7, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest7 // root.js.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest7 + COMPLETED SUCCESS // root.js.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest7 + STARTED TEST displayName: myTest8, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest8 // root.js.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest8 + FAILURE testFailed // root.js.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest8 + COMPLETED FAILURE // root.js.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest8 + STARTED TEST displayName: myTest9, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest9 // root.js.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest9 + COMPLETED SKIPPED // root.js.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest9 + COMPLETED FAILURE // root.js.MyTest.MyTestNested.MyTestNestedNested + COMPLETED FAILURE // root.js.MyTest.MyTestNested + COMPLETED FAILURE // root.js.MyTest + STARTED SUITE MyTest2 // root.js.MyTest2 + STARTED TEST displayName: myTest10, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest10 // root.js.MyTest2.my.company.product.MyTest2.myTest10 + COMPLETED SUCCESS // root.js.MyTest2.my.company.product.MyTest2.myTest10 + STARTED TEST displayName: myTest11, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest11 // root.js.MyTest2.my.company.product.MyTest2.myTest11 + FAILURE testFailed // root.js.MyTest2.my.company.product.MyTest2.myTest11 + COMPLETED FAILURE // root.js.MyTest2.my.company.product.MyTest2.myTest11 + STARTED TEST displayName: myTest12, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest12 // root.js.MyTest2.my.company.product.MyTest2.myTest12 + COMPLETED SKIPPED // root.js.MyTest2.my.company.product.MyTest2.myTest12 + STARTED SUITE MyTest2Nested // root.js.MyTest2.MyTest2Nested + STARTED TEST displayName: myTest13, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest13 // root.js.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest13 + COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest13 + STARTED TEST displayName: myTest14, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest14 // root.js.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest14 + COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest14 + STARTED TEST displayName: myTest15, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest15 // root.js.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest15 + COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest15 + STARTED SUITE MyTest2NestedNested // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested + STARTED TEST displayName: myTest16, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest16 // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest16 + COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest16 + STARTED TEST displayName: myTest17, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest17 // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest17 + COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest17 + STARTED TEST displayName: myTest18, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest18 // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest18 + COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest18 + COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested + COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested + COMPLETED FAILURE // root.js.MyTest2 + COMPLETED FAILURE // root.js +COMPLETED FAILURE // root + """) { + serviceMessage(TestSuiteStarted("")) + complexFixture() + serviceMessage(TestSuiteFinished("")) + } + } + + @Test + fun testComplexIos() { + nameOfRootSuiteToAppend = "ios" + + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE ios // root.ios + STARTED SUITE MyTest // root.ios.MyTest + STARTED TEST displayName: myTest1, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest1 // root.ios.MyTest.my.company.product.MyTest.myTest1 + COMPLETED SUCCESS // root.ios.MyTest.my.company.product.MyTest.myTest1 + STARTED TEST displayName: myTest2, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest2 // root.ios.MyTest.my.company.product.MyTest.myTest2 + FAILURE testFailed // root.ios.MyTest.my.company.product.MyTest.myTest2 + COMPLETED FAILURE // root.ios.MyTest.my.company.product.MyTest.myTest2 + STARTED TEST displayName: myTest3, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest3 // root.ios.MyTest.my.company.product.MyTest.myTest3 + COMPLETED SKIPPED // root.ios.MyTest.my.company.product.MyTest.myTest3 + STARTED SUITE MyTestNested // root.ios.MyTest.MyTestNested + STARTED TEST displayName: myTest4, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest4 // root.ios.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest4 + COMPLETED SUCCESS // root.ios.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest4 + STARTED TEST displayName: myTest5, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest5 // root.ios.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest5 + FAILURE testFailed // root.ios.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest5 + COMPLETED FAILURE // root.ios.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest5 + STARTED TEST displayName: myTest6, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest6 // root.ios.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest6 + COMPLETED SKIPPED // root.ios.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest6 + STARTED SUITE MyTestNestedNested // root.ios.MyTest.MyTestNested.MyTestNestedNested + STARTED TEST displayName: myTest7, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest7 // root.ios.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest7 + COMPLETED SUCCESS // root.ios.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest7 + STARTED TEST displayName: myTest8, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest8 // root.ios.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest8 + FAILURE testFailed // root.ios.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest8 + COMPLETED FAILURE // root.ios.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest8 + STARTED TEST displayName: myTest9, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest9 // root.ios.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest9 + COMPLETED SKIPPED // root.ios.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest9 + COMPLETED FAILURE // root.ios.MyTest.MyTestNested.MyTestNestedNested + COMPLETED FAILURE // root.ios.MyTest.MyTestNested + COMPLETED FAILURE // root.ios.MyTest + STARTED SUITE MyTest2 // root.ios.MyTest2 + STARTED TEST displayName: myTest10, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest10 // root.ios.MyTest2.my.company.product.MyTest2.myTest10 + COMPLETED SUCCESS // root.ios.MyTest2.my.company.product.MyTest2.myTest10 + STARTED TEST displayName: myTest11, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest11 // root.ios.MyTest2.my.company.product.MyTest2.myTest11 + FAILURE testFailed // root.ios.MyTest2.my.company.product.MyTest2.myTest11 + COMPLETED FAILURE // root.ios.MyTest2.my.company.product.MyTest2.myTest11 + STARTED TEST displayName: myTest12, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest12 // root.ios.MyTest2.my.company.product.MyTest2.myTest12 + COMPLETED SKIPPED // root.ios.MyTest2.my.company.product.MyTest2.myTest12 + STARTED SUITE MyTest2Nested // root.ios.MyTest2.MyTest2Nested + STARTED TEST displayName: myTest13, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest13 // root.ios.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest13 + COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest13 + STARTED TEST displayName: myTest14, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest14 // root.ios.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest14 + COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest14 + STARTED TEST displayName: myTest15, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest15 // root.ios.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest15 + COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest15 + STARTED SUITE MyTest2NestedNested // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested + STARTED TEST displayName: myTest16, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest16 // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest16 + COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest16 + STARTED TEST displayName: myTest17, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest17 // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest17 + COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest17 + STARTED TEST displayName: myTest18, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest18 // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest18 + COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest18 + COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested + COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested + COMPLETED FAILURE // root.ios.MyTest2 + COMPLETED FAILURE // root.ios +COMPLETED FAILURE // root + """) { + complexFixture() + } + } + + private fun TCServiceMessagesClient.complexFixture() { + serviceMessage(TestSuiteStarted("MyTest")) + + serviceMessage(TestStarted("my.company.product.MyTest.myTest1", false, null)) + serviceMessage(TestFinished("my.company.product.MyTest.myTest1", 1)) + serviceMessage(TestStarted("my.company.product.MyTest.myTest2", false, null)) + serviceMessage(TestFailed("my.company.product.MyTest.myTest2", null)) + serviceMessage(TestFinished("my.company.product.MyTest.myTest2", 1)) + serviceMessage(TestIgnored("my.company.product.MyTest.myTest3", "")) + + serviceMessage(TestSuiteStarted("MyTestNested")) + serviceMessage(TestStarted("my.company.product.MyTest.MyTestNested.myTest4", false, null)) + serviceMessage(TestFinished("my.company.product.MyTest.MyTestNested.myTest4", 1)) + serviceMessage(TestStarted("my.company.product.MyTest.MyTestNested.myTest5", false, null)) + serviceMessage(TestFailed("my.company.product.MyTest.MyTestNested.myTest5", null)) + serviceMessage(TestFinished("my.company.product.MyTest.MyTestNested.myTest5", 1)) + serviceMessage(TestIgnored("my.company.product.MyTest.MyTestNested.myTest6", "")) + + serviceMessage(TestSuiteStarted("MyTestNestedNested")) + serviceMessage(TestStarted("my.company.product.MyTest.MyTestNestedNested.myTest7", false, null)) + serviceMessage(TestFinished("my.company.product.MyTest.MyTestNestedNested.myTest7", 1)) + serviceMessage(TestStarted("my.company.product.MyTest.MyTestNestedNested.myTest8", false, null)) + serviceMessage(TestFailed("my.company.product.MyTest.MyTestNestedNested.myTest8", null)) + serviceMessage(TestFinished("my.company.product.MyTest.MyTestNestedNested.myTest8", 1)) + serviceMessage(TestIgnored("my.company.product.MyTest.MyTestNestedNested.myTest9", "")) + serviceMessage(TestSuiteFinished("MyTestNestedNested")) + + serviceMessage(TestSuiteFinished("MyTestNested")) + + serviceMessage(TestSuiteFinished("MyTest")) + + serviceMessage(TestSuiteStarted("MyTest2")) + + serviceMessage(TestStarted("my.company.product.MyTest2.myTest10", false, null)) + serviceMessage(TestFinished("my.company.product.MyTest2.myTest10", 1)) + serviceMessage(TestStarted("my.company.product.MyTest2.myTest11", false, null)) + serviceMessage(TestFailed("my.company.product.MyTest2.myTest11", null)) + serviceMessage(TestFinished("my.company.product.MyTest2.myTest11", 1)) + serviceMessage(TestIgnored("my.company.product.MyTest2.myTest12", "")) + + serviceMessage(TestSuiteStarted("MyTest2Nested")) + serviceMessage(TestIgnored("my.company.product.MyTest2.MyTestNested.myTest13", "")) + serviceMessage(TestIgnored("my.company.product.MyTest2.MyTestNested.myTest14", "")) + serviceMessage(TestIgnored("my.company.product.MyTest2.MyTestNested.myTest15", "")) + + serviceMessage(TestSuiteStarted("MyTest2NestedNested")) + serviceMessage(TestIgnored("my.company.product.MyTest2.MyTest2NestedNested.myTest16", "")) + serviceMessage(TestIgnored("my.company.product.MyTest2.MyTest2NestedNested.myTest17", "")) + serviceMessage(TestIgnored("my.company.product.MyTest2.MyTest2NestedNested.myTest18", "")) + serviceMessage(TestSuiteFinished("MyTest2NestedNested")) + + serviceMessage(TestSuiteFinished("MyTest2Nested")) + + serviceMessage(TestSuiteFinished("MyTest2")) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/ReplaceRoot.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/ReplaceRoot.kt new file mode 100644 index 00000000000..2b7362f0731 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/ReplaceRoot.kt @@ -0,0 +1,66 @@ +package org.jetbrains.kotlin.gradle.internal.testing.tcsmc + +import jetbrains.buildServer.messages.serviceMessages.TestFinished +import jetbrains.buildServer.messages.serviceMessages.TestStarted +import jetbrains.buildServer.messages.serviceMessages.TestSuiteFinished +import jetbrains.buildServer.messages.serviceMessages.TestSuiteStarted +import org.junit.Test + +class ReplaceRoot : TCServiceMessagesClientTest() { + @Test + fun replaceRootWithFqn() { + nameOfRootSuiteToReplace = "js" + + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE js // root.js + STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: my.company.product.MyTest, name: myMethod // root.js.my.company.product.MyTest.myMethod + COMPLETED SUCCESS // root.js.my.company.product.MyTest.myMethod + COMPLETED SUCCESS // root.js +COMPLETED SUCCESS // root + """) { + serviceMessage(TestSuiteStarted("")) + serviceMessage(TestStarted("my.company.product.MyTest.myMethod", false, null)) + serviceMessage(TestFinished("my.company.product.MyTest.myMethod", 1)) + serviceMessage(TestSuiteFinished("")) + } + } + + @Test + fun replaceRootWithClassName() { + nameOfRootSuiteToReplace = "js" + + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE js // root.js + STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: MyTest, name: myMethod // root.js.MyTest.myMethod + COMPLETED SUCCESS // root.js.MyTest.myMethod + COMPLETED SUCCESS // root.js +COMPLETED SUCCESS // root + """) { + serviceMessage(TestSuiteStarted("")) + serviceMessage(TestStarted("MyTest.myMethod", false, null)) + serviceMessage(TestFinished("MyTest.myMethod", 1)) + serviceMessage(TestSuiteFinished("")) + } + } + + @Test + fun replaceRoot() { + nameOfRootSuiteToReplace = "js" + + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE js // root.js + STARTED TEST displayName: myMethod, classDisplayName: js, className: js, name: myMethod // root.js.myMethod + COMPLETED SUCCESS // root.js.myMethod + COMPLETED SUCCESS // root.js +COMPLETED SUCCESS // root + """) { + serviceMessage(TestSuiteStarted("")) + serviceMessage(TestStarted("myMethod", false, null)) + serviceMessage(TestFinished("myMethod", 1)) + serviceMessage(TestSuiteFinished("")) + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/SkipRoot.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/SkipRoot.kt new file mode 100644 index 00000000000..2ad631cea44 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/SkipRoot.kt @@ -0,0 +1,30 @@ +package org.jetbrains.kotlin.gradle.internal.testing.tcsmc + +import jetbrains.buildServer.messages.serviceMessages.TestFinished +import jetbrains.buildServer.messages.serviceMessages.TestStarted +import jetbrains.buildServer.messages.serviceMessages.TestSuiteFinished +import jetbrains.buildServer.messages.serviceMessages.TestSuiteStarted +import org.junit.Test + +class SkipRoot: TCServiceMessagesClientTest() { + @Test + fun testSkipRoot() { + skipRoots = true + + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE MyTest // root.MyTest + STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: MyTest, name: myMethod // root.MyTest.myMethod + COMPLETED SUCCESS // root.MyTest.myMethod + COMPLETED SUCCESS // root.MyTest +COMPLETED SUCCESS // root + """) { + serviceMessage(TestSuiteStarted("")) + serviceMessage(TestSuiteStarted("MyTest")) + serviceMessage(TestStarted("myMethod", false, null)) + serviceMessage(TestFinished("myMethod", 1)) + serviceMessage(TestSuiteFinished("MyTest")) + serviceMessage(TestSuiteFinished("")) + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/TCServiceMessagesClientTest.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/TCServiceMessagesClientTest.kt new file mode 100644 index 00000000000..f3abd228ba8 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/TCServiceMessagesClientTest.kt @@ -0,0 +1,43 @@ +package org.jetbrains.kotlin.gradle.internal.testing.tcsmc + +import org.gradle.internal.operations.OperationIdentifier +import org.jetbrains.kotlin.gradle.internal.testing.RecordingTestResultProcessor +import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClient +import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings +import org.slf4j.LoggerFactory +import kotlin.test.assertEquals + +open class TCServiceMessagesClientTest { + protected var nameOfRootSuiteToAppend: String? = null + protected var nameOfRootSuiteToReplace: String? = null + protected var nameOfLeafTestToAppend: String? = null + protected var skipRoots: Boolean = false + + internal fun assertEvents(assertion: String, produceServiceMessage: TCServiceMessagesClient.() -> Unit) { + val results = RecordingTestResultProcessor() + val client = createClient(results) + + client.root(OperationIdentifier(1)) { + client.produceServiceMessage() + } + + assertEquals( + assertion.trim(), + results.output.toString().trim() + ) + } + + internal open fun createClient(results: RecordingTestResultProcessor): TCServiceMessagesClient { + return TCServiceMessagesClient( + results, + TCServiceMessagesClientSettings( + "root", + nameOfRootSuiteToAppend, + nameOfRootSuiteToReplace, + nameOfLeafTestToAppend, + skipRoots + ), + LoggerFactory.getLogger("test") + ) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/TestNameParsing.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/TestNameParsing.kt new file mode 100644 index 00000000000..19afc3d8a6c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/TestNameParsing.kt @@ -0,0 +1,65 @@ +package org.jetbrains.kotlin.gradle.internal.testing.tcsmc + +import jetbrains.buildServer.messages.serviceMessages.TestFinished +import jetbrains.buildServer.messages.serviceMessages.TestStarted +import jetbrains.buildServer.messages.serviceMessages.TestSuiteFinished +import jetbrains.buildServer.messages.serviceMessages.TestSuiteStarted +import org.junit.Test + +class TestNameParsing: TCServiceMessagesClientTest() { + @Test + fun testFqn() { + assertEvents(""" +STARTED SUITE root // root + STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: my.company.product.MyTest, name: myMethod // root.my.company.product.MyTest.myMethod + COMPLETED SUCCESS // root.my.company.product.MyTest.myMethod +COMPLETED SUCCESS // root + """) { + serviceMessage(TestStarted("my.company.product.MyTest.myMethod", false, null)) + serviceMessage(TestFinished("my.company.product.MyTest.myMethod", 1)) + } + } + + @Test + fun testSimpleClassName() { + assertEvents(""" +STARTED SUITE root // root + STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: MyTest, name: myMethod // root.MyTest.myMethod + COMPLETED SUCCESS // root.MyTest.myMethod +COMPLETED SUCCESS // root + """) { + serviceMessage(TestStarted("MyTest.myMethod", false, null)) + serviceMessage(TestFinished("MyTest.myMethod", 1)) + } + } + + @Test + fun testParentSuite() { + assertEvents(""" +STARTED SUITE root // root + STARTED SUITE MyTest // root.MyTest + STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: MyTest, name: myMethod // root.MyTest.myMethod + COMPLETED SUCCESS // root.MyTest.myMethod + COMPLETED SUCCESS // root.MyTest +COMPLETED SUCCESS // root + """) { + serviceMessage(TestSuiteStarted("MyTest")) + serviceMessage(TestStarted("myMethod", false, null)) + serviceMessage(TestFinished("myMethod", 1)) + serviceMessage(TestSuiteFinished("MyTest")) + } + } + + @Test + fun testAlone() { + assertEvents(""" +STARTED SUITE root // root + STARTED TEST displayName: myMethod, classDisplayName: root, className: root, name: myMethod // root.myMethod + COMPLETED SUCCESS // root.myMethod +COMPLETED SUCCESS // root + """) { + serviceMessage(TestStarted("myMethod", false, null)) + serviceMessage(TestFinished("myMethod", 1)) + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-test-nodejs-runner/.yarnclean b/libraries/tools/kotlin-test-nodejs-runner/.yarnclean new file mode 100644 index 00000000000..b591611ea7a --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/.yarnclean @@ -0,0 +1,45 @@ +# test directories +__tests__ +test +tests +powered-test + +# asset directories +docs +doc +website +images +assets + +# examples +example +examples + +# code coverage directories +coverage +.nyc_output + +# build scripts +Makefile +Gulpfile.js +Gruntfile.js + +# configs +appveyor.yml +circle.yml +codeship-services.yml +codeship-steps.yml +wercker.yml +.tern-project +.gitattributes +.editorconfig +.*ignore +.eslintrc +.jshintrc +.flowconfig +.documentup.json +.yarn-metadata.json +.travis.yml + +# misc +*.md diff --git a/libraries/tools/kotlin-test-nodejs-runner/README.md b/libraries/tools/kotlin-test-nodejs-runner/README.md new file mode 100644 index 00000000000..023c5242bd4 --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/README.md @@ -0,0 +1,6 @@ +Kotlin/JS TeamCity tests results reporter. + +### Usage + +`yarn add @kotlin/js-tests-teamcity --dev` +`yarn run kotlin-js-tests` \ No newline at end of file diff --git a/libraries/tools/kotlin-test-nodejs-runner/build.gradle.kts b/libraries/tools/kotlin-test-nodejs-runner/build.gradle.kts new file mode 100644 index 00000000000..22dcb85571b --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/build.gradle.kts @@ -0,0 +1,49 @@ +import com.moowork.gradle.node.yarn.YarnTask + +plugins { + id("base") + id("com.moowork.node") version "1.2.0" +} + +node { + version = "11.9.0" + download = true + nodeModulesDir = projectDir +} + +tasks { + "yarn" { + outputs.upToDateWhen { + projectDir.resolve("node_modules").isDirectory + } + } + + create("yarnBuild") { + group = "build" + + dependsOn("yarn") + setWorkingDir(projectDir) + args = listOf("build") + + inputs.dir(projectDir.resolve("src")) + outputs.file(projectDir.resolve("lib/kotlin-js-test.js")) + } + + create("cleanYarn") { + group = "build" + + delete = setOf( + projectDir.resolve("node_modules"), + projectDir.resolve("lib"), + projectDir.resolve(".rpt2_cache") + ) + } + + getByName("clean").dependsOn("cleanYarn") +} + +artifacts { + add("archives", projectDir.resolve("lib/kotlin-js-test.js")) { + builtBy("yarnBuild") + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-test-nodejs-runner/cli.ts b/libraries/tools/kotlin-test-nodejs-runner/cli.ts new file mode 100755 index 00000000000..c6909f3c41e --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/cli.ts @@ -0,0 +1,65 @@ +import {TeamCityMessagesFlow} from "./src/TeamCityMessagesFlow"; +import {directRunner, KotlinTestRunner} from "./src/KotlinTestRunner"; +import {IgnoredTestSuitesReporting, runWithTeamCityReporter} from "./src/KotlinTestTeamCityReporter"; +import {CliArgsParser} from "./src/CliArgsParser"; +import {configureFiltering} from "./src/CliFiltertingConfiguration"; +import {hrTimer} from "./src/Timer"; + +const kotlin_test = require('kotlin-test'); + +const parser = new CliArgsParser({ + version: VERSION, + bin: BIN, + description: DESCRIPTION, + usage: "[-t --tests] [-e --exclude] , , ..", + args: { + include: { + keys: ['--tests', '--include'], + help: "Tests to include. Example: MySuite.test1,MySuite.MySubSuite.*,*unix*,!*windows*", + default: "*" + + }, + exclude: { + keys: ['--exclude'], + help: "Tests to exclude. Example: MySuite.test1,MySuite.MySubSuite.*,*unix*" + }, + ignoredTestSuites: { + keys: ['--ignoredTestSuites'], + help: "How to deal with ignored test suites", + single: true, + values: [ + IgnoredTestSuitesReporting.skip, + IgnoredTestSuitesReporting.reportAsIgnoredTest, + IgnoredTestSuitesReporting.reportAllInnerTestsAsIgnored + ], + valuesHelp: [ + "don't report ignored test suites", + "useful to speedup large ignored test suites", + "will cause visiting all inner tests", + ], + default: IgnoredTestSuitesReporting.reportAllInnerTestsAsIgnored + } + }, + freeArgsTitle: "module_name" +}); + +const processArgs = process.argv.slice(2); +const untypedArgs = parser.parse(processArgs); +const args = { + onIgnoredTestSuites: (untypedArgs.ignoredTestSuites + || IgnoredTestSuitesReporting.reportAllInnerTestsAsIgnored) as IgnoredTestSuitesReporting, + include: untypedArgs.include as string[], + exclude: untypedArgs.exclude as string[], +}; + +const teamCity = new TeamCityMessagesFlow(null, (payload) => console.log(payload)); + +let runner: KotlinTestRunner = directRunner; +runner = runWithTeamCityReporter(runner, args.onIgnoredTestSuites, teamCity, hrTimer); +runner = configureFiltering(runner, args.include, args.exclude); + +kotlin_test.setAdapter(runner); + +untypedArgs.free.forEach((arg: string) => { + require(arg); +}); \ No newline at end of file diff --git a/libraries/tools/kotlin-test-nodejs-runner/env.ts b/libraries/tools/kotlin-test-nodejs-runner/env.ts new file mode 100644 index 00000000000..7040622143f --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/env.ts @@ -0,0 +1,4 @@ +declare const DEBUG: boolean; +declare const VERSION: string; +declare const BIN: string; +declare const DESCRIPTION: string; \ No newline at end of file diff --git a/libraries/tools/kotlin-test-nodejs-runner/package.json b/libraries/tools/kotlin-test-nodejs-runner/package.json new file mode 100644 index 00000000000..0cb73042e2c --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/package.json @@ -0,0 +1,30 @@ +{ + "name": "@kotlin/js-tests-teamcity", + "version": "0.0.1", + "description": "Simple Kotlin/JS tests runner with TeamCity reporter", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "bin": { + "kotlin-js-tests": "lib/kotlin-js-test.js" + }, + "files": [ + "lib/**/*" + ], + "scripts": { + "build": "rimraf lib/* && rollup -c rollup.config.js" + }, + "dependencies": { + "@types/node": "^10.12.21" + }, + "devDependencies": { + "copyfiles": "^2.1.0", + "rimraf": "^2.6.3", + "rollup": "^1.1.2", + "rollup-plugin-commonjs": "^9.2.0", + "rollup-plugin-node-resolve": "^4.0.0", + "rollup-plugin-sourcemaps": "^0.4.2", + "rollup-plugin-typescript2": "^0.19.2", + "rollup-plugin-uglify": "^6.0.2", + "typescript": "^3.3.1" + } +} diff --git a/libraries/tools/kotlin-test-nodejs-runner/rollup.config.js b/libraries/tools/kotlin-test-nodejs-runner/rollup.config.js new file mode 100644 index 00000000000..559b32e320d --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/rollup.config.js @@ -0,0 +1,59 @@ +import typescript from 'rollup-plugin-typescript2'; +import {uglify} from "rollup-plugin-uglify"; +import nodeResolve from 'rollup-plugin-node-resolve'; +import commonjs from 'rollup-plugin-commonjs'; + +const pckg = require('./package.json'); + +export default [ + { + input: './cli.ts', + output: { + file: 'lib/kotlin-js-test.js', + format: 'cjs', + banner: '#!/usr/bin/env node' + }, + plugins: [ + nodeResolve({ + jsnext: true, + main: true + }), + commonjs(), + typescript({ + tsconfig: "tsconfig.json" + }), + uglify({ + sourcemap: true, + compress: { + // hoist_funs: true, + // hoist_vars: true, + toplevel: true, + unsafe: true, + dead_code: true, + global_defs: { + DEBUG: false, + VERSION: pckg.version, + BIN: Object.keys(pckg.bin)[0], + DESCRIPTION: pckg.description + } + }, + mangle: { + properties: { + keep_quoted: true, + reserved: [ + "argv", "hrtime", + "kotlin_test", "kotlin", "setAdapter", "setAssertHook_4duqou$", + "suite", "test", + "stack" + ] + }, + toplevel: true, + }, + // output: { + // beautify: true + // } + }), + // sourceMaps() + ] + } +] \ No newline at end of file diff --git a/libraries/tools/kotlin-test-nodejs-runner/src/CliArgsParser.ts b/libraries/tools/kotlin-test-nodejs-runner/src/CliArgsParser.ts new file mode 100644 index 00000000000..367cb66a15e --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/src/CliArgsParser.ts @@ -0,0 +1,115 @@ +import {println, startsWith} from "./utils"; + +export type CliDescription = { + version: string, + bin: string, + description: string, + usage: string, + args: { + [k: string]: CliArgDescription, + }, + freeArgsTitle: string +} + +export type CliArgValues = { + [k: string]: string[] | string, + free: string[] +} + +export type CliArgDescription = { + keys: string[], + help: string, + values?: string[], + valuesHelp?: string[], + default?: string, + single?: true +} + +export class CliArgsParser { + constructor(private description: CliDescription) { + } + + printUsage() { + const description = this.description; + + println(`${description.bin} v${description.version} - ${description.description}`); + println(); + println(`Usage: ${description.bin} ${description.usage}`); + println(); + for (let key in description.args) { + const data = description.args[key]; + println(' ' + data.keys.join(', ')); + const indent = ' '; + println(`${indent}${data.help}`); + if (data.values && data.valuesHelp) { + println(`${indent}Possible values:`); + for (let i = 0; i < data.values.length; i++) { + const value = data.values[i]; + const help = data.valuesHelp[i]; + println(`${indent} - "${value}": ${help}`) + } + } + if (data.default) println(`${indent}By default: ${data.default}`); + println('') + } + } + + badArgsExit(message: string) { + println(message); + println(); + this.printUsage(); + process.exit(1) + } + + parse(args: string[]): CliArgValues { + const description = this.description; + + const result: CliArgValues = { + free: [] + }; + for (let key in description.args) { + if (!description.args[key].single) { + result[key] = []; + } + } + + // process all arguments from left to right + args: while (args.length != 0) { + const arg = args.shift() as string; + + if (startsWith(arg, '--')) { + for (let argName in description.args) { + const argDescription = description.args[argName]; + if (argDescription.keys.indexOf(arg) != -1) { + if (args.length == 0) { + this.badArgsExit("Missed value after option " + arg); + } + + const value = args.shift() as string; + if (argDescription.values && argDescription.values.indexOf(value) == -1) { + this.badArgsExit("Unsupported value for option " + arg); + } + + if (argDescription.single) { + result[argName] = value; + } else { + (result[argName] as string[]).push(value); + } + + continue args; + } + } + + this.badArgsExit("Unknown option: " + arg); + } else { + result.free.push(arg) + } + } + + if (result.free.length == 0) { + this.badArgsExit(`At least one ${description.freeArgsTitle} should be provided`) + } + + return result + } +} diff --git a/libraries/tools/kotlin-test-nodejs-runner/src/CliFiltertingConfiguration.ts b/libraries/tools/kotlin-test-nodejs-runner/src/CliFiltertingConfiguration.ts new file mode 100644 index 00000000000..3aba5daa009 --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/src/CliFiltertingConfiguration.ts @@ -0,0 +1,52 @@ +import {KotlinTestRunner} from "./KotlinTestRunner"; +import { + allTest, + CompositeTestFilter, + KotlinTestsFilter, + newKotlinTestsFilter, + runWithFilter +} from "./KotlinTestsFilter"; +import {flatMap, println, pushIfNotNull} from "./utils"; + +export function configureFiltering( + runner: KotlinTestRunner, + includeWildcards: string[], + excludeWildcards: string[] +): KotlinTestRunner { + const include: KotlinTestsFilter[] = []; + const exclude: KotlinTestsFilter[] = []; + + function collectWildcards( + value: string[], + positive: KotlinTestsFilter[], + negative: KotlinTestsFilter[] + ) { + flatMap(value, (t: string) => t.split(',')) + .map(t => { + if (t.length && t[0] == '!') { + pushIfNotNull(negative, newKotlinTestsFilter(t.substring(1))) + } else { + pushIfNotNull(positive, newKotlinTestsFilter(t)) + } + }) + } + + collectWildcards(includeWildcards, include, exclude); + collectWildcards(excludeWildcards, exclude, include); + + if (include.length == 0 && exclude.length == 0) { + return runner + } else { + if (include.length == 0) { + include.push(allTest) + } + + const filter = new CompositeTestFilter(include, exclude); + + if (DEBUG) { + println(filter.toString()); + } + + return runWithFilter(runner, filter) + } +} diff --git a/libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestRunner.ts b/libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestRunner.ts new file mode 100644 index 00000000000..46f58e1aa85 --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestRunner.ts @@ -0,0 +1,14 @@ +export interface KotlinTestRunner { + suite(name: string, isIgnored: boolean, fn: () => void): void + + test(name: string, isIgnored: boolean, fn: () => void): void +} + +export const directRunner: KotlinTestRunner = { + suite(name: string, isIgnored: boolean, fn: () => void): void { + if (!isIgnored) fn() + }, + test(name: string, isIgnored: boolean, fn: () => void): void { + if (!isIgnored) fn() + } +}; \ No newline at end of file diff --git a/libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestTeamCityReporter.ts b/libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestTeamCityReporter.ts new file mode 100644 index 00000000000..155128092d5 --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestTeamCityReporter.ts @@ -0,0 +1,104 @@ +import {KotlinTestRunner} from "./KotlinTestRunner"; +import {TeamCityMessageData, TeamCityMessagesFlow} from "./TeamCityMessagesFlow"; +import {Timer} from "./Timer"; + +const kotlin_test = require('kotlin-test'); + +// don't use enum as it is not minified by uglify +export type IgnoredTestSuitesReporting + = "skip" | "reportAsIgnoredTest" | "reportAllInnerTestsAsIgnored" +export const IgnoredTestSuitesReporting: { [key: string]: IgnoredTestSuitesReporting } = { + skip: "skip", + reportAsIgnoredTest: "reportAsIgnoredTest", + reportAllInnerTestsAsIgnored: "reportAllInnerTestsAsIgnored" +}; + +// to reduce minified code size +function withName(name: string, data?: TeamCityMessageData): TeamCityMessageData { + data = data || {}; + data["name"] = name; + return data +} + +export function runWithTeamCityReporter( + runner: KotlinTestRunner, + ignoredTestSuites: IgnoredTestSuitesReporting, + teamCity: TeamCityMessagesFlow, + timer: Timer | null +): KotlinTestRunner { + let inIgnoredSuite = false; + let currentAssertionResult: { expected: any, actual: any } | null = null; + + kotlin_test.kotlin.test.setAssertHook_4duqou$(function (assertionResult: { expected: any, actual: any }) { + currentAssertionResult = assertionResult; + }); + + return { + suite: function (name: string, isIgnored: boolean, fn: () => void) { + if (isIgnored) { + if (ignoredTestSuites == IgnoredTestSuitesReporting.skip) return; + else if (ignoredTestSuites == IgnoredTestSuitesReporting.reportAsIgnoredTest) { + teamCity.sendMessage('testIgnored', withName(name, {"suite": true})); + return + } + } + + teamCity.sendMessage('testSuiteStarted', withName(name)); + + // noinspection UnnecessaryLocalVariableJS + const alreadyInIgnoredSuite = inIgnoredSuite; + if (!alreadyInIgnoredSuite && isIgnored) { + inIgnoredSuite = true; + } + + try { + if (isIgnored && ignoredTestSuites == IgnoredTestSuitesReporting.reportAllInnerTestsAsIgnored) { + fn(); + } else { + runner.suite(name, isIgnored, fn) + } + } finally { + if (isIgnored && !alreadyInIgnoredSuite) { + inIgnoredSuite = false; + } + + const data: TeamCityMessageData = withName(name); + + // extension only for Gradle + if (isIgnored) data["ignored"] = true; + + teamCity.sendMessage('testSuiteFinished', data); + } + }, + test: function (name: string, isIgnored: boolean, fn: () => void) { + if (inIgnoredSuite || isIgnored) { + teamCity.sendMessage('testIgnored', withName(name)); + } else { + const startTime = timer ? timer.start() : null; + teamCity.sendMessage('testStarted', withName(name)); + try { + runner.test(name, isIgnored, fn); + } catch (e) { + const data: TeamCityMessageData = withName(name, { + "message": e.message, + "details": e.stack + }); + + if (currentAssertionResult) { + data["type"] = 'comparisonFailure'; + data["expected"] = currentAssertionResult.expected; + data["actual"] = currentAssertionResult.actual; + } + teamCity.sendMessage('testFailed', data); + } finally { + currentAssertionResult = null; + const data: TeamCityMessageData = withName(name); + if (startTime) { + data["duration"] = timer!!.end(startTime); // ns to ms + } + teamCity.sendMessage('testFinished', data); + } + } + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestsFilter.ts b/libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestsFilter.ts new file mode 100644 index 00000000000..e755c164259 --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestsFilter.ts @@ -0,0 +1,168 @@ +import {escapeRegExp, startsWith, trim} from "./utils"; +import {KotlinTestRunner} from "./KotlinTestRunner"; + +export interface KotlinTestsFilter { + mayContainTestsFromSuite(fqn: string): boolean; + + containsTest(fqn: string): boolean; +} + +export function runWithFilter( + runner: KotlinTestRunner, + filter: KotlinTestsFilter, +): KotlinTestRunner { + let path: string[] = []; + + function pathString() { + // skip root + return path.slice(1).join('.') + } + + return { + suite: function (name: string, isIgnored: boolean, fn: () => void) { + path.push(name); + + try { + if (path.length > 1 && !filter.mayContainTestsFromSuite(pathString())) return; + + runner.suite(name, isIgnored, fn); + } finally { + path.pop() + } + }, + + test: function (name: string, isIgnored: boolean, fn: () => void) { + path.push(name); + + try { + if (!filter.containsTest(pathString())) return; + + runner.test(name, isIgnored, fn); + } finally { + path.pop() + } + } + }; +} + +export function newKotlinTestsFilter(wildcard: string | null): KotlinTestsFilter | null { + if (wildcard == null) return null; + wildcard = trim(wildcard); + wildcard = wildcard.replace(/\*+/, '*'); // ** => * + if (wildcard.length == 0) return null; + else if (wildcard == '*') return allTest; + else if (wildcard.indexOf('*') == -1) return new ExactFilter(wildcard); + else if (startsWith(wildcard, '*')) return new RegExpKotlinTestsFilter(wildcard); + else { + // optimize for cases like "Something*", "Something*a*b" and so on. + // by adding explicit prefix matcher to not visit unneeded suites + // (RegExpKotlinTestsFilter doesn't support suites matching) + const [prefix, rest] = wildcard.split('*', 2); + return new StartsWithFilter(prefix, rest ? new RegExpKotlinTestsFilter(wildcard) : null) + } +} + +export const allTest = new class implements KotlinTestsFilter { + mayContainTestsFromSuite(fqn: string): boolean { + return true; + } + + containsTest(fqn: string): boolean { + return true; + } +}; + +export class StartsWithFilter implements KotlinTestsFilter { + constructor( + public readonly prefix: string, + public readonly filter: RegExpKotlinTestsFilter | null + ) { + } + + isPrefixMatched(fqn: string): boolean { + return startsWith(fqn + ".", this.prefix); + } + + mayContainTestsFromSuite(fqn: string): boolean { + return this.isPrefixMatched(fqn); + } + + containsAllTestsFromSuite(fqn: string): boolean { + return this.filter == null && this.isPrefixMatched(fqn); + } + + containsTest(fqn: string): boolean { + return startsWith(fqn, this.prefix) + && (this.filter == null || this.filter.containsTest(fqn)); + } +} + +export class ExactFilter implements KotlinTestsFilter { + constructor(public fqn: string) { + } + + mayContainTestsFromSuite(fqn: string): boolean { + return startsWith(fqn, this.fqn); + } + + containsTest(fqn: string): boolean { + return fqn === this.fqn; + } +} + +export class RegExpKotlinTestsFilter implements KotlinTestsFilter { + public readonly regexp: RegExp; + + constructor(wildcard: string) { + this.regexp = RegExp(wildcard + .split('*') + .map(it => escapeRegExp(it)) + .join('.*')); + } + + mayContainTestsFromSuite(fqn: string): boolean { + return true + } + + containsTest(fqn: string): boolean { + return this.regexp!.test(fqn) + } + + toString(): string { + return this.regexp.toString() + } +} + +export class CompositeTestFilter implements KotlinTestsFilter { + private readonly excludePrefix: StartsWithFilter[] = []; + + constructor( + public include: KotlinTestsFilter[], + public exclude: KotlinTestsFilter[] + ) { + this.exclude.forEach(it => { + if (it instanceof StartsWithFilter && it.filter == null) + this.excludePrefix.push(it) + }) + } + + mayContainTestsFromSuite(fqn: string): boolean { + for (const excl of this.excludePrefix) { + if (excl.containsAllTestsFromSuite(fqn)) return false + } + for (const incl of this.include) { + if (incl.mayContainTestsFromSuite(fqn)) return true + } + return false; + } + + containsTest(fqn: string): boolean { + for (const excl of this.exclude) { + if (excl.containsTest(fqn)) return false + } + for (const incl of this.include) { + if (incl.containsTest(fqn)) return true + } + return false + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestsFilterDebug.ts b/libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestsFilterDebug.ts new file mode 100644 index 00000000000..738fcc39074 --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/src/KotlinTestsFilterDebug.ts @@ -0,0 +1,32 @@ +import { + allTest, + CompositeTestFilter, + ExactFilter, + RegExpKotlinTestsFilter, + StartsWithFilter +} from "./KotlinTestsFilter"; + +declare const DEBUG: boolean; + +if (DEBUG) { + allTest.toString = function () { + return "(ALL)" + }; + + StartsWithFilter.prototype.toString = function (): string { + return "(STARTS WITH " + this.prefix + (this.filter ? (" AND " + this.filter) : "") + ")" + }; + + RegExpKotlinTestsFilter.prototype.toString = function (): string { + return "(REGEXP " + this.regexp + ")" + }; + + ExactFilter.prototype.toString = function (): string { + return "(EXACT " + this.fqn + ")" + }; + + CompositeTestFilter.prototype.toString = function (): string { + return this.include.map(it => it.toString()).join(" OR ") + + "\n NOT (" + this.exclude.map(it => it.toString()).join(" OR ") + ")" + }; +} \ No newline at end of file diff --git a/libraries/tools/kotlin-test-nodejs-runner/src/TeamCityMessagesFlow.ts b/libraries/tools/kotlin-test-nodejs-runner/src/TeamCityMessagesFlow.ts new file mode 100644 index 00000000000..fdeb9d7248d --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/src/TeamCityMessagesFlow.ts @@ -0,0 +1,23 @@ +import {dateTimeWithoutTimeZone, newFlowId, tcEscape} from "./utils" + +export type TeamCityMessageData = { [key: string]: any } + +export class TeamCityMessagesFlow { + public readonly id: number; + + constructor(id: number | null, private readonly send: (payload: string) => void) { + this.id = id || newFlowId() + } + + sendMessage(type: string, args: TeamCityMessageData) { + args['flowId'] = this.id; + args['timestamp'] = dateTimeWithoutTimeZone(); + + const serializedArgs = Object + .keys(args) + .map((key) => `${key}='${tcEscape(args[key])}'`) + .join(' '); + + this.send(`##teamcity[${type} ${serializedArgs}]`) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-test-nodejs-runner/src/Timer.ts b/libraries/tools/kotlin-test-nodejs-runner/src/Timer.ts new file mode 100644 index 00000000000..fd577604ed7 --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/src/Timer.ts @@ -0,0 +1,16 @@ +export interface Timer { + start(): T + + end(start: T): number +} + +export const hrTimer: Timer<[number, number]> = { + start(): [number, number] { + return process.hrtime(); + }, + end(start: [number, number]): number { + const elapsedHr = process.hrtime(start); + return elapsedHr[0] + (elapsedHr[1] / 1e6); // ns to ms + } +}; + diff --git a/libraries/tools/kotlin-test-nodejs-runner/src/utils.ts b/libraries/tools/kotlin-test-nodejs-runner/src/utils.ts new file mode 100644 index 00000000000..1714c914b21 --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/src/utils.ts @@ -0,0 +1,96 @@ + +/** + * from teamcity-service-messages + * Copyright (c) 2013 Aaron Forsander + * + * Escape string for TeamCity output. + * @see https://confluence.jetbrains.com/display/TCD65/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-servMsgsServiceMessages + */ +export function tcEscape(str: string): string { + if (!str) { + return ''; + } + + return str + .toString() + .replace(/\|/g, '||') + .replace(/\n/g, '|n') + .replace(/\r/g, '|r') + .replace(/\[/g, '|[') + .replace(/\]/g, '|]') + .replace(/\u0085/g, '|x') // next line + .replace(/\u2028/g, '|l') // line separator + .replace(/\u2029/g, '|p') // paragraph separator + .replace(/'/g, '|\''); +} + +/** + * From teamcity-service-messages. + * Copyright 2013 Aaron Forsander + */ +export function newFlowId(): number { + return Math.floor(Math.random() * (1e10 - 1e6 + 1)) + 1e6; +} + +/** + * From teamcity-service-messages. + * Copyright 2013 Aaron Forsander + */ +export function dateTimeWithoutTimeZone(): string { + // TeamCity not fully support ISO 8601 (see TW-36173) so we need to cut off 'Z' at the end. + return new Date().toISOString().slice(0, -1); +} + +/** + * From lodash. + * Copyright JS Foundation and other contributors + */ +export function startsWith(string: string, target: string) { + return string.slice(0, target.length) == target; +} + +/** + * From lodash. + * Copyright JS Foundation and other contributors + */ +export function trim(str: string): string { + return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); +} + +/** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ +const reRegExpChar = /[\\^$.*+?()[\]{}|]/g, + reHasRegExpChar = RegExp(reRegExpChar.source); + +/** + * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+", + * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. + * + * From lodash. + * Copyright JS Foundation and other contributors + */ +export function escapeRegExp(string: string) { + return (string && reHasRegExpChar.test(string)) + ? string.replace(reRegExpChar, '\\$&') + : string; +} + +export function pushIfNotNull(list: T[], value: T) { + if (value !== null) list.push(value) +} + +export function flatMap(arr: T[], f: (item: T) => T[]): T[] { + const result: T[] = []; + arr.forEach(item => { + f(item).forEach(x => { + result.push(x) + }) + }); + return result; +} + +export function println(message ?: string) { + console.log(message) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-test-nodejs-runner/tsconfig.json b/libraries/tools/kotlin-test-nodejs-runner/tsconfig.json new file mode 100644 index 00000000000..19433724709 --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "ESNext", +// "declaration": true, +// "outDir": "./lib", + "strict": true, +// "sourceMap": true, + "allowSyntheticDefaultImports": true, + "inlineSourceMap": true + }, + "exclude": [ + "**/*Debug.ts" + ] +} \ No newline at end of file diff --git a/libraries/tools/kotlin-test-nodejs-runner/yarn.lock b/libraries/tools/kotlin-test-nodejs-runner/yarn.lock new file mode 100644 index 00000000000..e0db42584cc --- /dev/null +++ b/libraries/tools/kotlin-test-nodejs-runner/yarn.lock @@ -0,0 +1,1073 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" + integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== + dependencies: + "@babel/highlight" "^7.0.0" + +"@babel/highlight@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" + integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw== + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + +"@types/node@*", "@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== + +acorn@^6.0.5: + version "6.0.6" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.6.tgz#cd75181670d5b99bdb1b1c993941d3a239ab1f56" + integrity sha512-5M3G/A4uBSMIlfJ+h9W125vJvPFH/zirISsW5qfxF5YzEvXJCtolLoQvM5yZft0DvMcUrPGKPOlgEu55I6iUtA== + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= + +atob@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +builtin-modules@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.0.0.tgz#1e587d44b006620d90286cc7a9238bbc6129cab1" + integrity sha512-hMIeU4K2ilbXV6Uv93ZZ0Avg/M91RaKXucQ+4me2Do1txxBDyDZWCBa5bJSLqoNTRpXTLwEzIk1KmloenDDjhg== + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= + +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +cliui@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" + integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi "^2.0.0" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +commander@~2.17.1: + version "2.17.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +copyfiles@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.1.0.tgz#0e2a4188162d6b2f3c5adfe34e9c0bd564d23164" + integrity sha512-cAeDE0vL/koE9WSEGxqPpSyvU638Kgfu6wfrnj7kqp9FWa1CWsU54Coo6sdYZP4GstWa39tL/wIVJWfXcujgNA== + dependencies: + glob "^7.0.5" + minimatch "^3.0.3" + mkdirp "^0.5.1" + noms "0.0.0" + through2 "^2.0.1" + yargs "^11.0.0" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +decamelize@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +estree-walker@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.2.tgz#d3850be7529c9580d815600b53126515e146dd39" + integrity sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig== + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= + dependencies: + fill-range "^2.1.0" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= + dependencies: + is-extglob "^1.0.0" + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= + +fill-range@^2.1.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" + integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^3.0.0" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +for-in@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= + dependencies: + for-in "^1.0.1" + +fs-extra@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= + dependencies: + is-glob "^2.0.0" + +glob@^7.0.5, glob@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.1.15" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" + integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= + dependencies: + is-extglob "^1.0.0" + +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= + +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +jest-worker@^24.0.0: + version "24.0.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.0.0.tgz#3d3483b077bf04f412f47654a27bba7e947f8b6d" + integrity sha512-s64/OThpfQvoCeHG963MiEZOAAxu8kHsaL/rCMF7lpdzo7vgF0CtPml9hfguOMgykgH/eOm4jFP4ibfHLruytg== + dependencies: + merge-stream "^1.0.1" + supports-color "^6.1.0" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +kind-of@^3.0.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= + dependencies: + invert-kv "^1.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +magic-string@^0.25.1: + version "0.25.2" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.2.tgz#139c3a729515ec55e96e69e82a11fe890a293ad9" + integrity sha512-iLs9mPjh9IuTtRsqqhNGYcZXGei0Nh/A4xirrsqW7c+QhKVFL2vm7U09ru6cHRD22azaP/wMDgI+HCqbETMTtg== + dependencies: + sourcemap-codec "^1.4.4" + +math-random@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" + integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== + +mem@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y= + dependencies: + mimic-fn "^1.0.0" + +merge-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" + integrity sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE= + dependencies: + readable-stream "^2.0.1" + +micromatch@^2.3.11: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +minimatch@^3.0.3, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +noms@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/noms/-/noms-0.0.0.tgz#da8ebd9f3af9d6760919b27d9cdc8092a7332859" + integrity sha1-2o69nzr51nYJGbJ9nNyAkqczKFk= + dependencies: + inherits "^2.0.1" + readable-stream "~1.0.31" + +normalize-path@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +os-locale@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" + integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA== + dependencies: + execa "^0.7.0" + lcid "^1.0.0" + mem "^1.1.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.5, path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +randomatic@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" + integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== + dependencies: + is-number "^4.0.0" + kind-of "^6.0.0" + math-random "^1.0.1" + +readable-stream@^2.0.1, readable-stream@~2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@~1.0.31: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== + dependencies: + is-equal-shallow "^0.1.3" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== + dependencies: + path-parse "^1.0.5" + +resolve@^1.8.1: + version "1.10.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" + integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg== + dependencies: + path-parse "^1.0.6" + +rimraf@^2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rollup-plugin-commonjs@^9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.2.0.tgz#4604e25069e0c78a09e08faa95dc32dec27f7c89" + integrity sha512-0RM5U4Vd6iHjL6rLvr3lKBwnPsaVml+qxOGaaNUWN1lSq6S33KhITOfHmvxV3z2vy9Mk4t0g4rNlVaJJsNQPWA== + dependencies: + estree-walker "^0.5.2" + magic-string "^0.25.1" + resolve "^1.8.1" + rollup-pluginutils "^2.3.3" + +rollup-plugin-node-resolve@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-4.0.0.tgz#9bc6b8205e9936cc0e26bba2415f1ecf1e64d9b2" + integrity sha512-7Ni+/M5RPSUBfUaP9alwYQiIKnKeXCOHiqBpKUl9kwp3jX5ZJtgXAait1cne6pGEVUUztPD6skIKH9Kq9sNtfw== + dependencies: + builtin-modules "^3.0.0" + is-module "^1.0.0" + resolve "^1.8.1" + +rollup-plugin-sourcemaps@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.4.2.tgz#62125aa94087aadf7b83ef4dfaf629b473135e87" + integrity sha1-YhJaqUCHqt97g+9N+vYptHMTXoc= + dependencies: + rollup-pluginutils "^2.0.1" + source-map-resolve "^0.5.0" + +rollup-plugin-typescript2@^0.19.2: + version "0.19.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.19.2.tgz#87d9c799cd6e02efbedbba25af12753a1e92b6c2" + integrity sha512-DRG7SaYX0QzBIz6rII5nm1UkiceS95r8mJjujugybyIueNF3auvzGTHMK62O7As/0q5RHjXsOguWOUv+KJKLFA== + dependencies: + fs-extra "7.0.1" + resolve "1.8.1" + rollup-pluginutils "2.3.3" + tslib "1.9.3" + +rollup-plugin-uglify@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-uglify/-/rollup-plugin-uglify-6.0.2.tgz#681042cfdf7ea4e514971946344e1a95bc2772fe" + integrity sha512-qwz2Tryspn5QGtPUowq5oumKSxANKdrnfz7C0jm4lKxvRDsNe/hSGsB9FntUul7UeC4TsZEWKErVgE1qWSO0gw== + dependencies: + "@babel/code-frame" "^7.0.0" + jest-worker "^24.0.0" + serialize-javascript "^1.6.1" + uglify-js "^3.4.9" + +rollup-pluginutils@2.3.3, rollup-pluginutils@^2.0.1, rollup-pluginutils@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.3.3.tgz#3aad9b1eb3e7fe8262820818840bf091e5ae6794" + integrity sha512-2XZwja7b6P5q4RZ5FhyX1+f46xi1Z3qBKigLRZ6VTZjwbN0K1IFGMlwm06Uu0Emcre2Z63l77nq/pzn+KxIEoA== + dependencies: + estree-walker "^0.5.2" + micromatch "^2.3.11" + +rollup@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.1.2.tgz#8d094b85683b810d0c05a16bd7618cf70d48eba7" + integrity sha512-OkdMxqMl8pWoQc5D8y1cIinYQPPLV8ZkfLgCzL6SytXeNA2P7UHynEQXI9tYxuAjAMsSyvRaWnyJDLHMxq0XAg== + dependencies: + "@types/estree" "0.0.39" + "@types/node" "*" + acorn "^6.0.5" + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +serialize-javascript@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.6.1.tgz#4d1f697ec49429a847ca6f442a2a755126c4d879" + integrity sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw== + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sourcemap-codec@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz#c63ea927c029dd6bd9a2b7fa03b3fec02ad56e9f" + integrity sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg== + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.0.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +through2@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +tslib@1.9.3: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== + +typescript@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b" + integrity sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA== + +uglify-js@^3.4.9: + version "3.4.9" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" + integrity sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q== + dependencies: + commander "~2.17.1" + source-map "~0.6.1" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +xtend@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yargs-parser@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" + integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc= + dependencies: + camelcase "^4.1.0" + +yargs@^11.0.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" + integrity sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A== + dependencies: + cliui "^4.0.0" + decamelize "^1.1.1" + find-up "^2.1.0" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^9.0.2" diff --git a/settings.gradle b/settings.gradle index 37de5db713e..bb40211b124 100644 --- a/settings.gradle +++ b/settings.gradle @@ -122,6 +122,7 @@ include ":kotlin-build-common", ":kotlin-test:kotlin-test-testng", ":kotlin-test:kotlin-test-js", ":kotlin-test:kotlin-test-js:kotlin-test-js-it", + ":kotlin-test-nodejs-runner", ":kotlinx-metadata", ":kotlinx-metadata-jvm", ":prepare:build.version", @@ -263,6 +264,7 @@ project(':kotlin-test:kotlin-test-junit5').projectDir = "$rootDir/libraries/kotl project(':kotlin-test:kotlin-test-testng').projectDir = "$rootDir/libraries/kotlin.test/testng" as File project(':kotlin-test:kotlin-test-js').projectDir = "$rootDir/libraries/kotlin.test/js" as File project(':kotlin-test:kotlin-test-js:kotlin-test-js-it').projectDir = "$rootDir/libraries/kotlin.test/js/it" as File +project(':kotlin-test-nodejs-runner').projectDir = "$rootDir/libraries/tools/kotlin-test-nodejs-runner" as File project(':kotlin-reflect').projectDir = "$rootDir/libraries/reflect" as File project(':kotlin-reflect-api').projectDir = "$rootDir/libraries/reflect/api" as File project(':kotlinx-metadata').projectDir = "$rootDir/libraries/kotlinx-metadata" as File