[K/N][Tests] Migrate objcSmoke & objcTests tests
^KT-61259
This commit is contained in:
committed by
Space Team
parent
58f7dd1c83
commit
524f0d335e
+1
-1
@@ -1,5 +1,5 @@
|
||||
language = Objective-C
|
||||
headers = Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h Foundation/NSStream.h Foundation/NSBundle.h
|
||||
headerFilter = **/smoke.h Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h Foundation/NSStream.h \
|
||||
headerFilter = smoke.h Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h Foundation/NSStream.h \
|
||||
objc/objc.h Foundation/NSBundle.h
|
||||
linkerOpts = -lobjcsmoke
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
language = Objective-C
|
||||
headers = Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h Foundation/NSStream.h Foundation/NSBundle.h \
|
||||
objc/runtime.h
|
||||
headerFilter = **/interop/objc/tests/**.h \
|
||||
headerFilter = tests/**.h \
|
||||
Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h Foundation/NSStream.h \
|
||||
objc/objc.h Foundation/NSBundle.h \
|
||||
objc/runtime.h
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.konan.test.blackbox
|
||||
|
||||
import com.intellij.testFramework.TestDataPath
|
||||
import org.jetbrains.kotlin.konan.target.isSimulator
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.*
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.TestCase
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.TestCompilerArgs
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.compilation.*
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.compilation.TestCompilationResult.Companion.assertSuccess
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.group.FirPipeline
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.runner.TestExecutable
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.runner.TestRunCheck
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.runner.TestRunChecks
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.settings.*
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.settings.Timeouts
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.util.ClangDistribution
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.util.compileWithClang
|
||||
import org.jetbrains.kotlin.test.TestMetadata
|
||||
import org.junit.jupiter.api.Assumptions
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.File
|
||||
|
||||
@TestDataPath("\$PROJECT_ROOT")
|
||||
class ClassicComplexCInteropTest : ComplexCInteropTestBase()
|
||||
|
||||
@FirPipeline
|
||||
@Tag("frontend-fir")
|
||||
@TestDataPath("\$PROJECT_ROOT")
|
||||
class FirComplexCInteropTest : ComplexCInteropTestBase()
|
||||
|
||||
abstract class ComplexCInteropTestBase : AbstractNativeSimpleTest() {
|
||||
private val interopObjCDir = File("native/native.tests/testData/interop/objc")
|
||||
|
||||
@Test
|
||||
@TestMetadata("smoke.kt")
|
||||
fun testInteropObjCSmokeGC() {
|
||||
Assumptions.assumeTrue(
|
||||
testRunSettings.get<GCType>() != GCType.NOOP
|
||||
|| testRunSettings.get<CacheMode>() != CacheMode.WithoutCache // TODO: Remove line after fix of KT-63944
|
||||
)
|
||||
testInteropObjCSmoke("smoke")
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("smoke_noopgc.kt")
|
||||
fun testInteropObjCSmokeNoopGC() {
|
||||
Assumptions.assumeTrue(testRunSettings.get<GCType>() == GCType.NOOP)
|
||||
Assumptions.assumeTrue(testRunSettings.get<CacheMode>() == CacheMode.WithoutCache) // TODO: Remove line after fix of KT-63944
|
||||
testInteropObjCSmoke("smoke_noopgc")
|
||||
}
|
||||
|
||||
private fun testInteropObjCSmoke(ktFilePrefix: String) {
|
||||
Assumptions.assumeTrue(targets.testTarget.family.isAppleFamily)
|
||||
val mSources = listOf(interopObjCDir.resolve("smoke.m"))
|
||||
val dylib = compileDylib("objcsmoke", mSources)
|
||||
println(dylib)
|
||||
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)
|
||||
|
||||
val cinteropKlib = cinteropToLibrary(
|
||||
targets = targets,
|
||||
defFile = interopObjCDir.resolve("objcSmoke.def"),
|
||||
outputDir = buildDir,
|
||||
freeCompilerArgs = TestCompilerArgs(emptyList(), cinteropArgs = listOf("-header", "smoke.h"))
|
||||
).assertSuccess().resultingArtifact
|
||||
println(cinteropKlib)
|
||||
|
||||
val testCase = generateTestCaseWithSingleFile(
|
||||
sourceFile = interopObjCDir.resolve("$ktFilePrefix.kt"),
|
||||
moduleName = "smoke",
|
||||
freeCompilerArgs = TestCompilerArgs(
|
||||
"-opt-in=kotlinx.cinterop.ExperimentalForeignApi",
|
||||
"-linker-option",
|
||||
"-L${buildDir.absolutePath}"
|
||||
),
|
||||
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()
|
||||
println(success)
|
||||
val testExecutable = TestExecutable(
|
||||
success.resultingArtifact,
|
||||
success.loggedData,
|
||||
listOf(TestName("smoke"))
|
||||
)
|
||||
runExecutableAndVerify(testCase, testExecutable)
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("tests")
|
||||
fun testInteropObjCTests() {
|
||||
Assumptions.assumeTrue(targets.testTarget.family.isAppleFamily)
|
||||
val mSources = interopObjCDir.resolve("tests").listFiles { file: File -> file.name.endsWith(".m") }!!.toList()
|
||||
val dylib = compileDylib("objctests", mSources)
|
||||
if (testRunSettings.configurables.targetTriple.isSimulator)
|
||||
codesign(dylib.resultingArtifact.path)
|
||||
println(dylib)
|
||||
|
||||
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
|
||||
println(cinteropKlib)
|
||||
|
||||
val ignoredTests = if (testRunSettings.get<GCType>() != GCType.NOOP) emptySet() else setOf(
|
||||
"Kt41811Kt.*",
|
||||
"CustomStringKt.testCustomString",
|
||||
"Kt42482Kt.testKT42482",
|
||||
"ObjcWeakRefsKt.testObjCWeakRef",
|
||||
"WeakRefsKt.testWeakRefs",
|
||||
)
|
||||
val testCase = generateTestCaseWithSingleModule(
|
||||
moduleDir = interopObjCDir.resolve("tests"),
|
||||
freeCompilerArgs = TestCompilerArgs(
|
||||
"-opt-in=kotlinx.cinterop.ExperimentalForeignApi",
|
||||
"-opt-in=kotlin.native.internal.InternalForKotlinNative",
|
||||
"-XXLanguage:+ImplicitSignedToUnsignedIntegerConversion",
|
||||
"-tr", "-e", "main", "-linker-option", "-L${buildDir.absolutePath}"
|
||||
),
|
||||
extras = TestCase.WithTestRunnerExtras(TestRunnerType.DEFAULT, ignoredTests),
|
||||
)
|
||||
val success = compileToExecutable(testCase, cinteropKlib.asLibraryDependency()).assertSuccess()
|
||||
println(success)
|
||||
val testExecutable = TestExecutable(
|
||||
success.resultingArtifact,
|
||||
success.loggedData,
|
||||
listOf(TestName("interop_objc_tests"))
|
||||
)
|
||||
runExecutableAndVerify(testCase, testExecutable)
|
||||
}
|
||||
|
||||
private fun compileDylib(name: String, mSources: List<File>): TestCompilationResult.Success<out TestCompilationArtifact.Executable> {
|
||||
val clangResult = compileWithClang(
|
||||
clangDistribution = ClangDistribution.Llvm,
|
||||
sourceFiles = mSources,
|
||||
includeDirectories = emptyList(),
|
||||
outputFile = buildDir.resolve("lib$name.dylib"),
|
||||
libraryDirectories = emptyList(),
|
||||
libraries = emptyList(),
|
||||
additionalClangFlags = listOf(
|
||||
"-DNS_FORMAT_ARGUMENT(A)=", "-lobjc", "-fobjc-arc", "-fPIC", "-shared",
|
||||
// Enable ARC optimizations to prevent some objects from leaking in Obj-C code due to exceptions:
|
||||
"-O2"
|
||||
),
|
||||
fmodules = false, // with `-fmodules`, ld cannot find symbol `_assert`
|
||||
).assertSuccess()
|
||||
return clangResult
|
||||
}
|
||||
}
|
||||
+6
-4
@@ -202,7 +202,8 @@ internal fun AbstractNativeSimpleTest.compileToStaticCache(
|
||||
|
||||
internal fun AbstractNativeSimpleTest.generateTestCaseWithSingleModule(
|
||||
moduleDir: File?,
|
||||
freeCompilerArgs: TestCompilerArgs = TestCompilerArgs.EMPTY
|
||||
freeCompilerArgs: TestCompilerArgs = TestCompilerArgs.EMPTY,
|
||||
extras: TestCase.Extras = TestCase.WithTestRunnerExtras(TestRunnerType.DEFAULT),
|
||||
): TestCase {
|
||||
val moduleName: String = moduleDir?.name ?: LAUNCHER_MODULE_NAME
|
||||
val module = TestModule.Exclusive(moduleName, emptySet(), emptySet(), emptySet())
|
||||
@@ -218,7 +219,7 @@ internal fun AbstractNativeSimpleTest.generateTestCaseWithSingleModule(
|
||||
freeCompilerArgs = freeCompilerArgs,
|
||||
nominalPackageName = PackageName.EMPTY,
|
||||
checks = TestRunChecks.Default(testRunSettings.get<Timeouts>().executionTimeout),
|
||||
extras = TestCase.WithTestRunnerExtras(TestRunnerType.DEFAULT)
|
||||
extras = extras
|
||||
).apply {
|
||||
initialize(null, null)
|
||||
}
|
||||
@@ -229,7 +230,8 @@ internal fun AbstractNativeSimpleTest.generateTestCaseWithSingleFile(
|
||||
moduleName: String = sourceFile.name,
|
||||
freeCompilerArgs: TestCompilerArgs = TestCompilerArgs.EMPTY,
|
||||
testKind: TestKind = TestKind.STANDALONE,
|
||||
extras: TestCase.Extras = TestCase.WithTestRunnerExtras(TestRunnerType.DEFAULT)
|
||||
extras: TestCase.Extras = TestCase.WithTestRunnerExtras(TestRunnerType.DEFAULT),
|
||||
checks: TestRunChecks = TestRunChecks.Default(testRunSettings.get<Timeouts>().executionTimeout),
|
||||
): TestCase {
|
||||
val module = TestModule.Exclusive(moduleName, emptySet(), emptySet(), emptySet())
|
||||
module.files += TestFile.createCommitted(sourceFile, module)
|
||||
@@ -240,7 +242,7 @@ internal fun AbstractNativeSimpleTest.generateTestCaseWithSingleFile(
|
||||
modules = setOf(module),
|
||||
freeCompilerArgs = freeCompilerArgs,
|
||||
nominalPackageName = PackageName.EMPTY,
|
||||
checks = TestRunChecks.Default(testRunSettings.get<Timeouts>().executionTimeout),
|
||||
checks = checks,
|
||||
extras = extras
|
||||
).apply {
|
||||
initialize(null, null)
|
||||
|
||||
+34
@@ -10,8 +10,10 @@ import org.jetbrains.kotlin.cli.common.messages.*
|
||||
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
|
||||
import org.jetbrains.kotlin.compilerRunner.processCompilerOutput
|
||||
import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.NativeTestSupport
|
||||
import org.jetbrains.kotlin.konan.test.blackbox.support.settings.KotlinNativeTargets
|
||||
import org.junit.jupiter.api.Assumptions
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
@@ -167,3 +169,35 @@ internal fun invokeCInterop(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class HostExecutionOutput(val executablePath: String, var exitCode: Int, var duration: Duration, val stdOut: String, val stdErr: String)
|
||||
|
||||
private fun executeHostProcess(executable: String, args: List<String> = listOf(), env: Map<String, String> = emptyMap()): HostExecutionOutput {
|
||||
val processBuilder = ProcessBuilder(executable, *args.toTypedArray())
|
||||
.redirectOutput(ProcessBuilder.Redirect.PIPE)
|
||||
.redirectError(ProcessBuilder.Redirect.PIPE)
|
||||
processBuilder.environment().putAll(env)
|
||||
val process = processBuilder.start()
|
||||
val (exitCode, duration) = measureTimedValue {
|
||||
process.waitFor()
|
||||
}
|
||||
return HostExecutionOutput(
|
||||
executable,
|
||||
exitCode,
|
||||
duration,
|
||||
process.inputStream.bufferedReader().readText(),
|
||||
process.errorStream.bufferedReader().readText(),
|
||||
)
|
||||
}
|
||||
|
||||
internal fun codesign(path: String) {
|
||||
Assumptions.assumeTrue(HostManager.hostIsMac) { "Apple specific code signing is needed" }
|
||||
val processOutput = executeHostProcess(executable = "/usr/bin/codesign", args = listOf("--verbose", "-s", "-", path))
|
||||
check(processOutput.exitCode == 0) {
|
||||
"""
|
||||
|Codesign failed with exitCode: ${processOutput.exitCode}
|
||||
|stdout: ${processOutput.stdOut}
|
||||
|stderr: ${processOutput.stdErr}
|
||||
""".trimMargin()
|
||||
}
|
||||
}
|
||||
|
||||
+8
-1
@@ -43,7 +43,14 @@ internal open class BaseTestRunProvider {
|
||||
}
|
||||
TestKind.STANDALONE -> {
|
||||
add(TestRunParameter.WithTCTestLogger)
|
||||
addIfNotNull(testName?.let(TestRunParameter::WithTestFilter))
|
||||
if (testName != null)
|
||||
add(TestRunParameter.WithTestFilter(testName))
|
||||
else {
|
||||
val ignoredTests = (testCase.extras as TestCase.WithTestRunnerExtras).ignoredTests
|
||||
if (ignoredTests.isNotEmpty()) {
|
||||
add(TestRunParameter.WithRegexFilter("(${ignoredTests.joinToString("|")})", positive = false))
|
||||
}
|
||||
}
|
||||
}
|
||||
TestKind.REGULAR -> {
|
||||
add(TestRunParameter.WithTCTestLogger)
|
||||
|
||||
+11
@@ -86,6 +86,17 @@ internal sealed interface TestRunParameter {
|
||||
override fun testMatches(testName: TestName) = this.testName == testName
|
||||
}
|
||||
|
||||
class WithRegexFilter(val pattern: String, val positive: Boolean) : WithFilter() {
|
||||
override fun applyTo(programArgs: MutableList<String>) {
|
||||
programArgs += if (positive)
|
||||
"--ktest_regex_filter=$pattern"
|
||||
else
|
||||
"--ktest_negative_regex_filter=$pattern"
|
||||
}
|
||||
|
||||
override fun testMatches(testName: TestName) = Regex(pattern).matches(testName.toString()) == positive
|
||||
}
|
||||
|
||||
object WithTCTestLogger : TestRunParameter {
|
||||
override fun applyTo(programArgs: MutableList<String>) {
|
||||
programArgs += "--ktest_logger=TEAMCITY"
|
||||
|
||||
+3
-1
@@ -50,6 +50,7 @@ internal fun AbstractNativeSimpleTest.compileWithClang(
|
||||
libraryDirectories: List<File> = emptyList(),
|
||||
libraries: List<String> = emptyList(),
|
||||
additionalClangFlags: List<String> = emptyList(),
|
||||
fmodules: Boolean = true
|
||||
): TestCompilationResult<out TestCompilationArtifact.Executable> {
|
||||
val configurables = testRunSettings.configurables
|
||||
val host = testRunSettings.get<KotlinNativeTargets>().hostTarget
|
||||
@@ -71,7 +72,8 @@ internal fun AbstractNativeSimpleTest.compileWithClang(
|
||||
*clangArgs,
|
||||
*sourceFiles.map { it.absolutePath }.toTypedArray(),
|
||||
*includeDirectories.flatMap { listOf("-I", it.absolutePath) }.toTypedArray(),
|
||||
"-g", "-fmodules",
|
||||
"-g",
|
||||
*(if (fmodules) arrayOf("-fmodules") else arrayOf()),
|
||||
*frameworkDirectories.flatMap { listOf("-F", it.absolutePath) }.toTypedArray(),
|
||||
*libraryDirectories.flatMap { listOf("-L", it.absolutePath) }.toTypedArray(),
|
||||
*libraries.map { "-l$it" }.toTypedArray(),
|
||||
|
||||
Reference in New Issue
Block a user