[K/N][Tests] Fix ComplexCInteropTest.kt to respect 2-stage compilation

^KT-66014
This commit is contained in:
Vladimir Sukharev
2024-02-21 19:15:08 +01:00
committed by Space Team
parent 5e81850bb1
commit 98c3147d19
9 changed files with 122 additions and 87 deletions
@@ -54,6 +54,8 @@ abstract class AbstractNativeCInteropKT39120Test : AbstractNativeCInteropBaseTes
val module = TestModule.Exclusive(DEFAULT_MODULE_NAME, emptySet(), emptySet(), emptySet()).apply {
files += TestFile.createCommitted(ktFile, this)
}
// KT-66014: TODO convert compileToExecutable() to TestCompilationFactory.testCasesToExecutable(),
// to respect possible `mode=TWO_STAGE_MULTI_MODULE`: factory would then add intermediate LibraryCompilation(kt->klib).
val compilationResult = compileToExecutable(
createTestCaseNoTestRun(module, TestCompilerArgs.EMPTY),
klib1.asLibraryDependency(),
@@ -78,6 +78,8 @@ class CInteropPackagesTest : AbstractNativeSimpleTest() {
freeCompilerArgs = TestCompilerArgs.EMPTY
).assertSuccess().resultingArtifact
// KT-66014: TODO convert compileToExecutable() to TestCompilationFactory.testCasesToExecutable(),
// to respect possible `mde=TWO_STAGE_MULTI_MODULE`: factory would then add intermediate LibraryCompilation(kt->klib).
compileToExecutable(
generateTestCaseWithSingleFile(
sourceFile = ktFile,
@@ -44,6 +44,7 @@ class FirComplexCInteropTest : ComplexCInteropTestBase()
abstract class ComplexCInteropTestBase : AbstractNativeSimpleTest() {
private val interopDir = File("native/native.tests/testData/interop")
private val interopObjCDir = interopDir.resolve("objc")
private val testCompilationFactory = TestCompilationFactory()
@Test
@TestMetadata("embedStaticLibraries.kt")
@@ -79,26 +80,20 @@ abstract class ComplexCInteropTestBase : AbstractNativeSimpleTest() {
)
}
}
val cinteropKlib = cinteropToLibrary(
targets = targets,
val (testCase, compilationResult) = compileDefAndKtToExecutable(
testName = "embedStaticLibraries",
defFile = defFile,
outputDir = buildDir,
ktFiles = listOf(embedStaticLibrariesDir.resolve("embedStaticLibraries.kt")),
freeCompilerArgs = TestCompilerArgs(
emptyList(), cinteropArgs = listOf(
compilerArgs = listOf("-opt-in=kotlinx.cinterop.ExperimentalForeignApi"),
cinteropArgs = listOf(
"-header", embedStaticLibrariesDir.resolve("embedStaticLibraries.h").absolutePath,
"-staticLibrary", "1.a",
"-staticLibrary", "2.a",
)
)
).assertSuccess().resultingArtifact
val testCase = generateTestCaseWithSingleFile(
sourceFile = embedStaticLibrariesDir.resolve("embedStaticLibraries.kt"),
testKind = TestKind.STANDALONE_NO_TR,
),
extras = TestCase.NoTestRunnerExtras("main"),
freeCompilerArgs = TestCompilerArgs("-opt-in=kotlinx.cinterop.ExperimentalForeignApi"),
)
val compilationResult = compileToExecutable(testCase, cinteropKlib.asLibraryDependency()).assertSuccess()
val testExecutable = TestExecutable(
compilationResult.resultingArtifact,
compilationResult.loggedData,
@@ -132,30 +127,21 @@ abstract class ComplexCInteropTestBase : AbstractNativeSimpleTest() {
if (testRunSettings.configurables.targetTriple.isSimulator)
codesign(dylib.resultingArtifact.path)
val stringsdict = interopObjCDir.resolve("Localizable.stringsdict")
stringsdict.copyTo(buildDir.resolve("en.lproj/Localizable.stringsdict"), overwrite = true)
stringsdict.copyTo(buildDir.resolve("$ktFilePrefix/en.lproj/Localizable.stringsdict"), overwrite = true)
val cinteropKlib = cinteropToLibrary(
targets = targets,
val (testCase, success) = compileDefAndKtToExecutable(
testName = ktFilePrefix,
defFile = interopObjCDir.resolve("objcSmoke.def"),
outputDir = buildDir,
freeCompilerArgs = TestCompilerArgs(emptyList(), cinteropArgs = listOf("-header", "smoke.h"))
).assertSuccess().resultingArtifact
val testCase = generateTestCaseWithSingleFile(
sourceFile = interopObjCDir.resolve("$ktFilePrefix.kt"),
moduleName = "smoke",
ktFiles = listOf(interopObjCDir.resolve("$ktFilePrefix.kt")),
freeCompilerArgs = TestCompilerArgs(
"-opt-in=kotlinx.cinterop.ExperimentalForeignApi",
"-linker-option",
"-L${buildDir.absolutePath}"
compilerArgs = listOf("-opt-in=kotlinx.cinterop.ExperimentalForeignApi", "-linker-option", "-L${buildDir.absolutePath}"),
cinteropArgs = listOf("-header", "smoke.h")
),
testKind = TestKind.STANDALONE_NO_TR,
extras = TestCase.NoTestRunnerExtras("main"),
checks = TestRunChecks.Default(testRunSettings.get<Timeouts>().executionTimeout).copy(
outputDataFile = TestRunCheck.OutputDataFile(file = interopObjCDir.resolve("$ktFilePrefix.out"))
)
),
)
val success = compileToExecutable(testCase, cinteropKlib.asLibraryDependency()).assertSuccess()
val testExecutable = TestExecutable(
success.resultingArtifact,
success.loggedData,
@@ -167,20 +153,17 @@ abstract class ComplexCInteropTestBase : AbstractNativeSimpleTest() {
@Test
@TestMetadata("tests")
fun testInteropObjCTests() {
fun fileList(subFolder: String, extension: String) =
interopObjCDir.resolve(subFolder).walk().filter { it.isFile && it.extension == extension }.toList()
Assumptions.assumeTrue(targets.testTarget.family.isAppleFamily)
val mSources = interopObjCDir.resolve("tests").listFiles { file: File -> file.name.endsWith(".m") }!!.toList()
val dylib = compileDylib("objctests", mSources)
val ktFiles = fileList("tests", "kt")
val hFiles = fileList("tests", "h")
val mFiles = fileList("tests", "m")
val dylib = compileDylib("objctests", mFiles)
if (testRunSettings.configurables.targetTriple.isSimulator)
codesign(dylib.resultingArtifact.path)
val hFiles = interopObjCDir.resolve("tests").listFiles { file: File -> file.name.endsWith(".h") }!!.toList()
val cinteropKlib = cinteropToLibrary(
targets = targets,
defFile = interopObjCDir.resolve("objcTests.def"),
outputDir = buildDir,
freeCompilerArgs = TestCompilerArgs(emptyList(), cinteropArgs = hFiles.flatMap { listOf("-header", "tests/${it.name}") })
).assertSuccess().resultingArtifact
val ignoredTestGTestPatterns = if (testRunSettings.get<GCType>() != GCType.NOOP) emptySet() else setOf(
"Kt41811Kt.*",
"CustomStringKt.testCustomString",
@@ -188,17 +171,21 @@ abstract class ComplexCInteropTestBase : AbstractNativeSimpleTest() {
"ObjcWeakRefsKt.testObjCWeakRef",
"WeakRefsKt.testWeakRefs",
)
val testCase = generateTestCaseWithSingleModule(
moduleDir = interopObjCDir.resolve("tests"),
val (testCase, success) = compileDefAndKtToExecutable(
testName = "embedStaticLibraries",
defFile = interopObjCDir.resolve("objcTests.def"),
ktFiles = ktFiles,
freeCompilerArgs = TestCompilerArgs(
"-opt-in=kotlinx.cinterop.ExperimentalForeignApi",
"-opt-in=kotlin.native.internal.InternalForKotlinNative",
"-XXLanguage:+ImplicitSignedToUnsignedIntegerConversion",
"-tr", "-e", "main", "-linker-option", "-L${buildDir.absolutePath}"
compilerArgs = listOf(
"-opt-in=kotlinx.cinterop.ExperimentalForeignApi",
"-opt-in=kotlin.native.internal.InternalForKotlinNative",
"-XXLanguage:+ImplicitSignedToUnsignedIntegerConversion",
"-tr", "-e", "main", "-linker-option", "-L${buildDir.absolutePath}"
),
cinteropArgs = hFiles.flatMap { listOf("-header", "tests/${it.name}") }
),
extras = TestCase.WithTestRunnerExtras(TestRunnerType.DEFAULT, ignoredTestGTestPatterns),
)
val success = compileToExecutable(testCase, cinteropKlib.asLibraryDependency()).assertSuccess()
val testExecutable = TestExecutable(
success.resultingArtifact,
success.loggedData,
@@ -257,7 +244,7 @@ abstract class ComplexCInteropTestBase : AbstractNativeSimpleTest() {
extraClangOpts = listOf("-framework", "AppKit", "-fobjc-arc"),
extraCompilerOpts = listOf("-tr"),
)
assertTrue(execResult.stdout.contains("[ PASSED ] 8 tests"))
assertTrue(execResult.stdout.contains("[ PASSED ] 8 tests"), execResult.stdout)
}
@Test
@@ -362,34 +349,21 @@ abstract class ComplexCInteropTestBase : AbstractNativeSimpleTest() {
if (testRunSettings.configurables.targetTriple.isSimulator)
codesign(dylib.resultingArtifact.path)
val cinteropKlib = cinteropToLibrary(
targets = targets,
val (_, success) = compileDefAndKtToExecutable(
testName = testName,
defFile = srcDir.resolve(defFile),
outputDir = buildDir,
ktFiles = listOf(srcDir.resolve(ktFile)),
freeCompilerArgs = TestCompilerArgs(
compilerArgs = emptyList(),
compilerArgs = listOf(
"-opt-in=kotlinx.cinterop.ExperimentalForeignApi",
"-opt-in=kotlin.native.internal.InternalForKotlinNative",
"-linker-option", "-L${buildDir.absolutePath}",
*extraCompilerOpts.toTypedArray<String>()
),
cinteropArgs = extraCinteropArgs + listOf("-header", srcDir.resolve(hFile).absolutePath)
)
).assertSuccess().resultingArtifact
val testKind = when (extras) {
is TestCase.NoTestRunnerExtras -> TestKind.STANDALONE_NO_TR
is TestCase.WithTestRunnerExtras -> TestKind.STANDALONE
}
val testCase = generateTestCaseWithSingleFile(
sourceFile = srcDir.resolve(ktFile),
moduleName = testName,
testKind = testKind,
),
extras = extras,
freeCompilerArgs = TestCompilerArgs(
"-opt-in=kotlinx.cinterop.ExperimentalForeignApi",
"-opt-in=kotlin.native.internal.InternalForKotlinNative",
"-linker-option", "-L${buildDir.absolutePath}",
*extraCompilerOpts.toTypedArray()
)
)
val success = compileToExecutable(testCase, cinteropKlib.asLibraryDependency()).assertSuccess()
return testRunSettings.executor.runProcess(success.resultingArtifact.executableFile.absolutePath)
}
@@ -418,22 +392,15 @@ abstract class ComplexCInteropTestBase : AbstractNativeSimpleTest() {
)
}
}
val cinteropKlib = cinteropToLibrary(
targets = targets,
defFile = withSpacesDef,
outputDir = buildDir,
freeCompilerArgs = TestCompilerArgs.EMPTY
).assertSuccess().resultingArtifact
val testCase = generateTestCaseWithSingleFile(
sourceFile = srcDir.resolve("withSpaces.kt"),
freeCompilerArgs = TestCompilerArgs(
"-opt-in=kotlinx.cinterop.ExperimentalForeignApi",
),
testKind = TestKind.STANDALONE_NO_TR,
val (testCase, success) = compileDefAndKtToExecutable(
testName = "withSpaces",
defFile = withSpacesDef,
ktFiles = listOf(srcDir.resolve("withSpaces.kt")),
freeCompilerArgs = TestCompilerArgs("-opt-in=kotlinx.cinterop.ExperimentalForeignApi"),
extras = TestCase.NoTestRunnerExtras("main"),
)
val success = compileToExecutable(testCase, cinteropKlib.asLibraryDependency()).assertSuccess()
val testExecutable = TestExecutable(
success.resultingArtifact,
success.loggedData,
@@ -443,6 +410,40 @@ abstract class ComplexCInteropTestBase : AbstractNativeSimpleTest() {
assertTrue(mapfile.exists())
}
private fun compileDefAndKtToExecutable(
testName: String,
defFile: File,
ktFiles: Collection<File>,
freeCompilerArgs: TestCompilerArgs,
extras: TestCase.Extras,
checks: TestRunChecks = TestRunChecks.Default(testRunSettings.get<Timeouts>().executionTimeout),
): Pair<TestCase, TestCompilationResult.Success<out TestCompilationArtifact.Executable>> {
val cinteropModule = TestModule.Exclusive("cinterop", emptySet(), emptySet(), emptySet())
cinteropModule.files += TestFile.createCommitted(defFile, cinteropModule)
val ktModule = TestModule.Exclusive("main", setOf("cinterop"), emptySet(), emptySet())
ktFiles.forEach { ktModule.files += TestFile.createCommitted(it, ktModule) }
val testKind = when (extras) {
is TestCase.NoTestRunnerExtras -> TestKind.STANDALONE_NO_TR
is TestCase.WithTestRunnerExtras -> TestKind.STANDALONE
}
val testCase = TestCase(
id = TestCaseId.Named(testName),
kind = testKind,
modules = setOf(cinteropModule, ktModule),
freeCompilerArgs = freeCompilerArgs,
nominalPackageName = PackageName(testName),
checks = checks,
extras = extras
).apply {
initialize(null, null)
}
// Compile test, respecting possible `mode=TWO_STAGE_MULTI_MODULE`: add intermediate LibraryCompilation(kt->klib), if needed.
val success = testCompilationFactory.testCasesToExecutable(listOf(testCase), testRunSettings).result.assertSuccess()
return Pair(testCase, success)
}
private fun compileDylib(name: String, mSources: List<File>, extraClangOpts: List<String> = listOf("-fobjc-arc")): TestCompilationResult.Success<out TestCompilationArtifact.Executable> {
val clangResult = compileWithClang(
clangDistribution = ClangDistribution.Llvm,
@@ -498,6 +498,8 @@ class IncrementalCompilationTest : AbstractNativeSimpleTest() {
build()
}
// Compile test, NOT respecting possible `mode=TWO_STAGE_MULTI_MODULE`: don't add intermediate LibraryCompilation(kt->klib).
// KT-66014: Extract this test from usual Native test run, and run it in scope of new test module
inline fun compileToExecutable(
targetSrc: String,
vararg dependencies: TestCompilationArtifact.KLIB,
@@ -54,6 +54,8 @@ class InfrastructureDumpedTestListingTest : AbstractNativeSimpleTest() {
val (executableTestCase: TestCase, executableCompilationResult: TestCompilationResult<out Executable>) =
if (fromSources) {
// Compile test, NOT respecting possible `mode=TWO_STAGE_MULTI_MODULE`: don't add intermediate LibraryCompilation(kt->klib).
// KT-66014: Extract this test from usual Native test run, and run it in scope of new test module
barTestCase to compileToExecutable(barTestCase, fooLibrary.asLibraryDependency())
} else {
val barCompilationResult: Success<out KLIB> = compileToLibrary(barTestCase, fooLibrary.asLibraryDependency())
@@ -58,6 +58,8 @@ class KT59030WorkaroundTest : AbstractNativeSimpleTest() {
).assertSuccess().resultingArtifact
spoilDeprecatedAnnotationsInLibrary(library)
// Compile test, NOT respecting possible `mode=TWO_STAGE_MULTI_MODULE`: don't add intermediate LibraryCompilation(kt->klib).
// KT-66014: Extract this test from usual Native test run, and run it in scope of new test module
compileToExecutable(
generateTestCaseWithSingleFile(
sourceFile = File(MAIN_FILE_PATH),
@@ -102,6 +102,8 @@ class KlibResolverTest : AbstractNativeSimpleTest() {
testKind = TestKind.STANDALONE_NO_TR,
extras = TestCase.NoTestRunnerExtras("main"),
)
// Compile test, NOT respecting possible `mode=TWO_STAGE_MULTI_MODULE`: don't add intermediate LibraryCompilation(kt->klib).
// KT-66014: Extract this test from usual Native test run, and run it in scope of new test module
val executableResult =
compileToExecutable(testCase, klibResult.resultingArtifact.asLibraryDependency()).assertSuccess()
val testExecutable = TestExecutable(
@@ -78,6 +78,7 @@ internal class LibraryBuilder(
)
}
// WARNING: compiles in one-stage mode (sources->executable) even when `mode=TWO_STAGE_MULTI_MODULE`
internal class ExecutableBuilder(
test: AbstractNativeSimpleTest,
rootDir: File,
@@ -91,6 +92,7 @@ internal class ExecutableBuilder(
freeCompilerArgs.add(this)
}
// WARNING: compiles in one-stage mode (sources->executable) even when `mode=TWO_STAGE_MULTI_MODULE`
override fun build(sourcesDir: File, outputDir: File, dependencies: List<TestCompilationArtifact.KLIB>) =
test.compileToExecutable(
sourcesDir,
@@ -161,6 +163,7 @@ internal class CompiledExecutable(
val testExecutable by lazy { TestExecutable.fromCompilationResult(testCase, compilationResult) }
}
// WARNING: compiles in one-stage mode (sources->executable) even when `mode=TWO_STAGE_MULTI_MODULE`
internal fun AbstractNativeSimpleTest.compileToExecutable(
sourcesDir: File,
tryPassSystemCacheDirectory: Boolean,
@@ -168,6 +171,7 @@ internal fun AbstractNativeSimpleTest.compileToExecutable(
vararg dependencies: TestCompilationArtifact.KLIB
) = compileToExecutable(sourcesDir, tryPassSystemCacheDirectory, freeCompilerArgs, dependencies.asList())
// WARNING: compiles in one-stage mode (sources->executable) even when `mode=TWO_STAGE_MULTI_MODULE`
internal fun AbstractNativeSimpleTest.compileToExecutable(
sourcesDir: File,
tryPassSystemCacheDirectory: Boolean,
@@ -179,9 +183,11 @@ internal fun AbstractNativeSimpleTest.compileToExecutable(
return CompiledExecutable(testCase, compilationResult.assertSuccess())
}
// WARNING: compiles in one-stage mode (sources->executable) even when `mode=TWO_STAGE_MULTI_MODULE`
internal fun AbstractNativeSimpleTest.compileToExecutable(testCase: TestCase, vararg dependencies: TestCompilationDependency<*>) =
compileToExecutable(testCase, true, dependencies.asList())
// WARNING: compiles in one-stage mode (sources->static cache) even when `mode=TWO_STAGE_MULTI_MODULE`
internal fun AbstractNativeSimpleTest.compileToStaticCache(
klib: TestCompilationArtifact.KLIB,
cacheDir: File,
@@ -296,7 +296,7 @@ internal class ObjCFrameworkCompilation(
gcType = settings.get(),
gcScheduler = settings.get(),
allocator = settings.get(),
pipelineType = settings.getStageDependentPipelineType(),
pipelineType = settings.getStageDependentPipelineType(sourceModules),
cacheMode = settings.get(),
freeCompilerArgs = freeCompilerArgs,
compilerPlugins = settings.get(),
@@ -341,7 +341,7 @@ internal class BinaryLibraryCompilation(
gcType = settings.get(),
gcScheduler = settings.get(),
allocator = settings.get(),
pipelineType = settings.getStageDependentPipelineType(),
pipelineType = settings.getStageDependentPipelineType(sourceModules),
cacheMode = settings.get(),
freeCompilerArgs = freeCompilerArgs,
compilerPlugins = settings.get(),
@@ -508,7 +508,7 @@ internal class ExecutableCompilation(
gcType = settings.get(),
gcScheduler = settings.get(),
allocator = settings.get(),
pipelineType = settings.getStageDependentPipelineType(),
pipelineType = settings.getStageDependentPipelineType(sourceModules),
cacheMode = settings.get(),
freeCompilerArgs = freeCompilerArgs,
compilerPlugins = settings.get(),
@@ -712,8 +712,24 @@ private object BinaryOptions {
}
}
internal fun Settings.getStageDependentPipelineType(): PipelineType =
// Calculates PipelineType to be used for compilations involving native backend or C/ObjC export.
// Second stage of TWO_STAGE_MULTI_MODULE must receive PipelineType.DEFAULT to be unaware of the language version used before, during first stage.
internal fun Settings.getStageDependentPipelineType(sourceModules: Collection<TestModule>): PipelineType =
when (get<TestMode>()) {
TestMode.ONE_STAGE_MULTI_MODULE -> get<PipelineType>()
TestMode.TWO_STAGE_MULTI_MODULE -> PipelineType.DEFAULT // Don't pass "-language_version" option to the second stage
TestMode.TWO_STAGE_MULTI_MODULE -> {
if (sourceModules.isEmpty())
PipelineType.DEFAULT // KT-56182: Don't pass "-language_version" option to pure second compilation stage.
else {
println( // KT-66014: TODO change println() to fail{} if all testsuites in KT-66014 would be changed
"WARNING: Wrong testing approach for `mode=TWO_STAGE_MULTI_MODULE`: test explicitly uses one-stage compilation for sources:\n" +
"${sourceModules.map { it.files.map { it.location.name } }}\n" +
"Please re-implement test to split compilation to two stages, when `mode=TWO_STAGE_MULTI_MODULE` is specified.\n" +
"TestCompilationFactory provides some tooling for this."
)
// Provided source modules must be compiled with proper frontend version,
// even if this version would be then wrongly passed to backend or C/ObjC generator
get<PipelineType>()
}
}
}