From 7810da701850a459d3f4315127e4d4f9e7f8088b Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Thu, 13 Jul 2017 07:07:01 +0300 Subject: [PATCH] Add IC compiler tests with Java --- .../incremental-compilation-impl.iml | 1 + ...bstractIncrementalJvmCompilerRunnerTest.kt | 46 +++- ...ementalJvmCompilerRunnerTestGenerated.java | 243 ++++++++++++++++++ .../kotlin/generators/tests/GenerateTests.kt | 1 + .../javaToKotlinAndRemove/gradle-build.log | 14 + .../constantUnchanged/gradle-build.log | 7 + .../enumEntryAdded/gradle-build.log | 29 +-- .../enumEntryRemoved/gradle-build.log | 22 ++ .../gradle-build.log | 7 + .../notChangeSignature/gradle-build.log | 6 + 10 files changed, 353 insertions(+), 23 deletions(-) create mode 100644 jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/javaToKotlinAndRemove/gradle-build.log create mode 100644 jps-plugin/testData/incremental/withJava/javaUsedInKotlin/constantUnchanged/gradle-build.log create mode 100644 jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryRemoved/gradle-build.log create mode 100644 jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaFieldNullabilityChanged/gradle-build.log create mode 100644 jps-plugin/testData/incremental/withJava/javaUsedInKotlin/notChangeSignature/gradle-build.log diff --git a/compiler/incremental-compilation-impl/incremental-compilation-impl.iml b/compiler/incremental-compilation-impl/incremental-compilation-impl.iml index c317050d542..c6d4241d727 100644 --- a/compiler/incremental-compilation-impl/incremental-compilation-impl.iml +++ b/compiler/incremental-compilation-impl/incremental-compilation-impl.iml @@ -21,5 +21,6 @@ + \ No newline at end of file diff --git a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalJvmCompilerRunnerTest.kt b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalJvmCompilerRunnerTest.kt index 24cf6f980f1..b219f00704f 100644 --- a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalJvmCompilerRunnerTest.kt +++ b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalJvmCompilerRunnerTest.kt @@ -16,24 +16,66 @@ package org.jetbrains.kotlin.incremental +import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.incremental.utils.TestCompilationResult import org.jetbrains.kotlin.incremental.utils.TestICReporter import org.jetbrains.kotlin.incremental.utils.TestMessageCollector +import java.io.ByteArrayOutputStream import java.io.File +import javax.tools.ToolProvider abstract class AbstractIncrementalJvmCompilerRunnerTest : AbstractIncrementalCompilerRunnerTestBase() { override fun make(cacheDir: File, sourceRoots: Iterable, args: K2JVMCompilerArguments): TestCompilationResult { val reporter = TestICReporter() val messageCollector = TestMessageCollector() makeIncrementally(cacheDir, sourceRoots, args, reporter = reporter, messageCollector = messageCollector) - return TestCompilationResult(reporter, messageCollector) + val kotlinCompileResult = TestCompilationResult(reporter, messageCollector) + if (kotlinCompileResult.exitCode != ExitCode.OK) return kotlinCompileResult + + val (javaExitCode, _, javaErrors) = compileJava(sourceRoots, args.destination!!) + return when (javaExitCode) { + ExitCode.OK -> kotlinCompileResult + else -> kotlinCompileResult.copy(exitCode = javaExitCode, compileErrors = javaErrors) + } + } + + private fun compileJava(sourceRoots: Iterable, kotlinClassesPath: String): TestCompilationResult { + val javaSources = arrayListOf() + for (root in sourceRoots) { + javaSources.addAll(root.walk().filter { it.isFile && it.extension == "java" }) + } + if (javaSources.isEmpty()) return TestCompilationResult(ExitCode.OK, emptyList(), emptyList()) + + val javaClasspath = compileClasspath + File.pathSeparator + kotlinClassesPath + val javaDestinationDir = File(workingDir, "java-classes").apply { + if (exists()) { + deleteRecursively() + } + mkdirs() + } + val args = arrayOf("-cp", javaClasspath, + "-d", javaDestinationDir.canonicalPath, + *javaSources.map { it.canonicalPath }.toTypedArray()) + + val err = ByteArrayOutputStream() + val javac = ToolProvider.getSystemJavaCompiler() + val rc = javac.run(null, null, err, *args) + + val exitCode = if (rc == 0) ExitCode.OK else ExitCode.COMPILATION_ERROR + val errors = err.toString().split("\n") + return TestCompilationResult(exitCode, javaSources, errors) } override fun createCompilerArguments(destinationDir: File, testDir: File): K2JVMCompilerArguments = K2JVMCompilerArguments().apply { moduleName = testDir.name destination = destinationDir.path - classpathAsList = listOf(File(bootstrapKotlincLib, "kotlin-stdlib.jar")) + classpath = compileClasspath } + + private val compileClasspath = + listOf(File(bootstrapKotlincLib, "kotlin-stdlib.jar")) + .map { it.canonicalPath } + .joinToString(File.pathSeparator) } \ No newline at end of file diff --git a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunnerTestGenerated.java b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunnerTestGenerated.java index d26babc592b..38a966140fe 100644 --- a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunnerTestGenerated.java +++ b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunnerTestGenerated.java @@ -941,4 +941,247 @@ public class IncrementalJvmCompilerRunnerTestGenerated extends AbstractIncrement doTest(fileName); } } + + @TestMetadata("jps-plugin/testData/incremental/withJava") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class WithJava extends AbstractIncrementalJvmCompilerRunnerTest { + public void testAllFilesPresentInWithJava() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/incremental/withJava"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true); + } + + @TestMetadata("jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConvertBetweenJavaAndKotlin extends AbstractIncrementalJvmCompilerRunnerTest { + public void testAllFilesPresentInConvertBetweenJavaAndKotlin() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true); + } + + @TestMetadata("javaToKotlin") + public void testJavaToKotlin() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/javaToKotlin/"); + doTest(fileName); + } + + @TestMetadata("javaToKotlinAndBack") + public void testJavaToKotlinAndBack() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/javaToKotlinAndBack/"); + doTest(fileName); + } + + @TestMetadata("javaToKotlinAndRemove") + public void testJavaToKotlinAndRemove() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/javaToKotlinAndRemove/"); + doTest(fileName); + } + + @TestMetadata("kotlinToJava") + public void testKotlinToJava() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/kotlinToJava/"); + doTest(fileName); + } + } + + @TestMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JavaUsedInKotlin extends AbstractIncrementalJvmCompilerRunnerTest { + public void testAllFilesPresentInJavaUsedInKotlin() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/incremental/withJava/javaUsedInKotlin"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true); + } + + @TestMetadata("changeNotUsedSignature") + public void testChangeNotUsedSignature() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/changeNotUsedSignature/"); + doTest(fileName); + } + + @TestMetadata("changeSignature") + public void testChangeSignature() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/changeSignature/"); + doTest(fileName); + } + + @TestMetadata("constantChanged") + public void testConstantChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/constantChanged/"); + doTest(fileName); + } + + @TestMetadata("constantUnchanged") + public void testConstantUnchanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/constantUnchanged/"); + doTest(fileName); + } + + @TestMetadata("enumEntryAdded") + public void testEnumEntryAdded() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryAdded/"); + doTest(fileName); + } + + @TestMetadata("enumEntryRemoved") + public void testEnumEntryRemoved() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryRemoved/"); + doTest(fileName); + } + + @TestMetadata("javaAndKotlinChangedSimultaneously") + public void testJavaAndKotlinChangedSimultaneously() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaAndKotlinChangedSimultaneously/"); + doTest(fileName); + } + + @TestMetadata("javaFieldNullabilityChanged") + public void testJavaFieldNullabilityChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaFieldNullabilityChanged/"); + doTest(fileName); + } + + @TestMetadata("javaMethodParamNullabilityChanged") + public void testJavaMethodParamNullabilityChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaMethodParamNullabilityChanged/"); + doTest(fileName); + } + + @TestMetadata("javaMethodReturnTypeNullabilityChanged") + public void testJavaMethodReturnTypeNullabilityChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaMethodReturnTypeNullabilityChanged/"); + doTest(fileName); + } + + @TestMetadata("methodAddedInSuper") + public void testMethodAddedInSuper() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/methodAddedInSuper/"); + doTest(fileName); + } + + @TestMetadata("methodRenamed") + public void testMethodRenamed() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/methodRenamed/"); + doTest(fileName); + } + + @TestMetadata("notChangeSignature") + public void testNotChangeSignature() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/notChangeSignature/"); + doTest(fileName); + } + + @TestMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/samConversions") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SamConversions extends AbstractIncrementalJvmCompilerRunnerTest { + public void testAllFilesPresentInSamConversions() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/samConversions"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true); + } + + @TestMetadata("methodAdded") + public void testMethodAdded() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/samConversions/methodAdded/"); + doTest(fileName); + } + + @TestMetadata("methodSignatureChanged") + public void testMethodSignatureChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/samConversions/methodSignatureChanged/"); + doTest(fileName); + } + } + } + + @TestMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class KotlinUsedInJava extends AbstractIncrementalJvmCompilerRunnerTest { + @TestMetadata("addOptionalParameter") + public void testAddOptionalParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/addOptionalParameter/"); + doTest(fileName); + } + + public void testAllFilesPresentInKotlinUsedInJava() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/incremental/withJava/kotlinUsedInJava"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true); + } + + @TestMetadata("changeNotUsedSignature") + public void testChangeNotUsedSignature() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/changeNotUsedSignature/"); + doTest(fileName); + } + + @TestMetadata("changeSignature") + public void testChangeSignature() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/changeSignature/"); + doTest(fileName); + } + + @TestMetadata("constantChanged") + public void testConstantChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/constantChanged/"); + doTest(fileName); + } + + @TestMetadata("constantUnchanged") + public void testConstantUnchanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/constantUnchanged/"); + doTest(fileName); + } + + @TestMetadata("funRenamed") + public void testFunRenamed() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/funRenamed/"); + doTest(fileName); + } + + @TestMetadata("jvmFieldChanged") + public void testJvmFieldChanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/jvmFieldChanged/"); + doTest(fileName); + } + + @TestMetadata("jvmFieldUnchanged") + public void testJvmFieldUnchanged() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/jvmFieldUnchanged/"); + doTest(fileName); + } + + @TestMetadata("methodAddedInSuper") + public void testMethodAddedInSuper() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/methodAddedInSuper/"); + doTest(fileName); + } + + @TestMetadata("notChangeSignature") + public void testNotChangeSignature() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/notChangeSignature/"); + doTest(fileName); + } + + @TestMetadata("onlyTopLevelFunctionInFileRemoved") + public void testOnlyTopLevelFunctionInFileRemoved() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/onlyTopLevelFunctionInFileRemoved/"); + doTest(fileName); + } + + @TestMetadata("packageFileAdded") + public void testPackageFileAdded() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/packageFileAdded/"); + doTest(fileName); + } + + @TestMetadata("privateChanges") + public void testPrivateChanges() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/privateChanges/"); + doTest(fileName); + } + + @TestMetadata("propertyRenamed") + public void testPropertyRenamed() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/propertyRenamed/"); + doTest(fileName); + } + } + } } diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 1925152c62a..96143ada344 100755 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -1250,6 +1250,7 @@ fun main(args: Array) { testClass { model("incremental/pureKotlin", extension = null, recursive = false) model("incremental/inlineFunCallSite", extension = null, excludeParentDirs = true) + model("incremental/withJava", extension = null, excludeParentDirs = true) } testClass { diff --git a/jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/javaToKotlinAndRemove/gradle-build.log b/jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/javaToKotlinAndRemove/gradle-build.log new file mode 100644 index 00000000000..b43d089d40e --- /dev/null +++ b/jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/javaToKotlinAndRemove/gradle-build.log @@ -0,0 +1,14 @@ +================ Step #1 ================= +Compiling files: + src/TheClass.kt + src/other.kt +End of files +Exit code: OK +------------------------------------------ + +================ Step #2 ================= + +Compiling files: +End of files +Exit code: OK +------------------------------------------ diff --git a/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/constantUnchanged/gradle-build.log b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/constantUnchanged/gradle-build.log new file mode 100644 index 00000000000..6d26dbae105 --- /dev/null +++ b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/constantUnchanged/gradle-build.log @@ -0,0 +1,7 @@ +================ Step #1 ================= + +Compiling files: + src/JavaClass.java + src/usage.kt +End of files +Exit code: OK diff --git a/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryAdded/gradle-build.log b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryAdded/gradle-build.log index 6d78e93fdff..739c085f5d9 100644 --- a/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryAdded/gradle-build.log +++ b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryAdded/gradle-build.log @@ -1,35 +1,22 @@ ================ Step #1 ================= -Cleaning output files: - out/production/module/Enum.class -End of files -Exit code: NOTHING_DONE ------------------------------------------- Compiling files: src/Enum.java -End of files -Cleaning output files: - out/production/module/META-INF/module.kotlin_module - out/production/module/UseKt.class -End of files -Compiling files: + src/getRandomEnumEntry.kt src/use.kt + src/useEnumImplicitly.kt End of files Exit code: ABORT ------------------------------------------ COMPILATION FAILED -When expression must be exhaustive, add necessary 'C' branch or 'else' branch instead +'when' expression must be exhaustive, add necessary 'C' branch or 'else' branch instead ================ Step #2 ================= -Cleaning output files: - out/production/module/Enum.class -End of files -Compiling files: - src/use.kt -End of files -Exit code: OK ------------------------------------------- Compiling files: src/Enum.java -End of files \ No newline at end of file + src/getRandomEnumEntry.kt + src/use.kt + src/useEnumImplicitly.kt +End of files +Exit code: OK \ No newline at end of file diff --git a/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryRemoved/gradle-build.log b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryRemoved/gradle-build.log new file mode 100644 index 00000000000..b0607ab07a5 --- /dev/null +++ b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryRemoved/gradle-build.log @@ -0,0 +1,22 @@ +================ Step #1 ================= + +Compiling files: + src/Enum.java + src/getRandomEnumEntry.kt + src/use.kt + src/useEnumImplicitly.kt +End of files +Exit code: ABORT +------------------------------------------ +COMPILATION FAILED +Unresolved reference: C + +================ Step #2 ================= + +Compiling files: + src/Enum.java + src/getRandomEnumEntry.kt + src/use.kt + src/useEnumImplicitly.kt +End of files +Exit code: OK \ No newline at end of file diff --git a/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaFieldNullabilityChanged/gradle-build.log b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaFieldNullabilityChanged/gradle-build.log new file mode 100644 index 00000000000..5ada4a700d5 --- /dev/null +++ b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaFieldNullabilityChanged/gradle-build.log @@ -0,0 +1,7 @@ +================ Step #1 ================= +Compiling files: + src/useA.kt + src/AChild.kt +End of files +Exit code: OK +------------------------------------------ \ No newline at end of file diff --git a/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/notChangeSignature/gradle-build.log b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/notChangeSignature/gradle-build.log new file mode 100644 index 00000000000..5b9396ca2b1 --- /dev/null +++ b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/notChangeSignature/gradle-build.log @@ -0,0 +1,6 @@ +================ Step #1 ================= + +Compiling files: + src/JavaClass.java + src/usage.kt +End of files