[K2/N] Add ObjCExport test for kdoc strings

^KT-56090

Merge-request: KT-MR-9926
Merged-by: Vladimir Sukharev <Vladimir.Sukharev@jetbrains.com>
This commit is contained in:
Vladimir Sukharev
2023-05-04 19:17:00 +00:00
committed by Space Team
parent d24eaf13b5
commit 79599ab69e
11 changed files with 333 additions and 23 deletions
@@ -0,0 +1 @@
EXPORT_KDOC
@@ -0,0 +1,14 @@
* `Summary class` [KDocExport]. // EXPORT_KDOC
* @property xyzzy Doc for property xyzzy // EXPORT_KDOC
* @property zzz See below. // EXPORT_KDOC
/** Non-primary ctor KDoc: // EXPORT_KDOC */
/** @property xyzzy KDoc for foo? // EXPORT_KDOC */
* @param xyzzy is documented. // EXPORT_KDOC
* This is multi-line KDoc. See a blank line above. // EXPORT_KDOC
/** @property foo KDoc for yxxyz? // EXPORT_KDOC */
* Kdoc for a property // EXPORT_KDOC
* Useless function [whatever] // EXPORT_KDOC
* This kdoc has some additional formatting. // EXPORT_KDOC
* @param a keep intact and return // EXPORT_KDOC
* @return value of [a] // EXPORT_KDOC
* Check for additional comment (note) below // EXPORT_KDOC
@@ -0,0 +1,54 @@
// KT-56090: [K2/N] Emit DocString klib extensions for ObjCExport
// MUTED_WHEN: K2
/*
* Copyright 2010-2023 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 kdocExport
/**
* `Summary class` [KDocExport]. // EXPORT_KDOC
*
* @property xyzzy Doc for property xyzzy // EXPORT_KDOC
* @property zzz See below. // EXPORT_KDOC
*/
// Expected: this comment shall not affect KDoc (i.e. kdoc above is still OK)
class KDocExport() {
/**
* @param xyzzy is documented. // EXPORT_KDOC
*
* This is multi-line KDoc. See a blank line above. // EXPORT_KDOC
*/
val xyzzy: String = "String example"
/** Non-primary ctor KDoc: // EXPORT_KDOC */
constructor(name: String) : this() {
println(name)
}
/** @property xyzzy KDoc for foo? // EXPORT_KDOC */
val foo = "foo"
/** @property foo KDoc for yxxyz? // EXPORT_KDOC */
var yxxyz = 0;
}
/**
* Useless function [whatever] // EXPORT_KDOC
*
* This kdoc has some additional formatting. // EXPORT_KDOC
* @param a keep intact and return // EXPORT_KDOC
* @return value of [a] // EXPORT_KDOC
* Check for additional comment (note) below // EXPORT_KDOC
*/
fun whatever(a:String) = a
public abstract class SomeClassWithProperty
{
/**
* Kdoc for a property // EXPORT_KDOC
*/
public abstract val heavyFormattedKDocFoo: SomeClassWithProperty
}
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2023 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.blackboxtest;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.junit.jupiter.api.Tag;
import org.jetbrains.kotlin.konan.blackboxtest.support.group.FirPipeline;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateNativeTestsKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("native/native.tests/testData/ObjCExport")
@TestDataPath("$PROJECT_ROOT")
@Tag("frontend-fir")
@FirPipeline()
public class FirObjCExportTestGenerated extends AbstractNativeObjCExportTest {
@Test
public void testAllFilesPresentInObjCExport() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("native/native.tests/testData/ObjCExport"), Pattern.compile("^([^_](.+))$"), null, false);
}
@Test
@TestMetadata("kdocExport")
public void testKdocExport() throws Exception {
runTest("native/native.tests/testData/ObjCExport/kdocExport/");
}
}
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2023 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.blackboxtest;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateNativeTestsKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("native/native.tests/testData/ObjCExport")
@TestDataPath("$PROJECT_ROOT")
public class ObjCExportTestGenerated extends AbstractNativeObjCExportTest {
@Test
public void testAllFilesPresentInObjCExport() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("native/native.tests/testData/ObjCExport"), Pattern.compile("^([^_](.+))$"), null, false);
}
@Test
@TestMetadata("kdocExport")
public void testKdocExport() throws Exception {
runTest("native/native.tests/testData/ObjCExport/kdocExport/");
}
}
@@ -165,6 +165,23 @@ fun main() {
}
}
// ObjCExport tests.
testGroup("native/native.tests/tests-gen", "native/native.tests/testData") {
testClass<AbstractNativeObjCExportTest>(
suiteTestClassName = "ObjCExportTestGenerated"
) {
model("ObjCExport", pattern = "^([^_](.+))$", recursive = false)
}
testClass<AbstractNativeObjCExportTest>(
suiteTestClassName = "FirObjCExportTestGenerated",
annotations = listOf(
*frontendFir()
),
) {
model("ObjCExport", pattern = "^([^_](.+))$", recursive = false)
}
}
// Klib contents tests
testGroup("native/native.tests/tests-gen", "native/native.tests/testData") {
testClass<AbstractNativeKlibContentsTest>(
@@ -0,0 +1,76 @@
/*
* Copyright 2010-2022 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.blackboxtest
import com.intellij.testFramework.TestDataFile
import org.jetbrains.kotlin.konan.blackboxtest.support.*
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.*
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.*
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Companion.assertSuccess
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.*
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.*
import org.jetbrains.kotlin.konan.blackboxtest.support.util.getAbsoluteFile
import org.jetbrains.kotlin.test.services.JUnit5Assertions
import org.junit.jupiter.api.Assumptions
import org.junit.jupiter.api.Tag
import java.io.File
@Tag("objcexport")
abstract class AbstractNativeObjCExportTest : AbstractNativeSimpleTest() {
private val targets: KotlinNativeTargets get() = testRunSettings.get<KotlinNativeTargets>()
private val testCompilationFactory = TestCompilationFactory()
protected fun runTest(@TestDataFile testDir: String) {
Assumptions.assumeTrue(targets.testTarget.family.isAppleFamily)
val testPathFull = getAbsoluteFile(testDir)
val ktSources = testPathFull.list()!!
.filter { it.endsWith(".kt")}
.map { testPathFull.resolve(it) }
ktSources.forEach { muteTestIfNecessary(it) }
val testCase: TestCase = generateObjCFrameworkTestCase(testPathFull, ktSources)
val objCFramework: ObjCFramework = testCase.toObjCFramework().assertSuccess().resultingArtifact
val mainHeaderContents = objCFramework.mainHeader.readText()
val regexFilter = testPathFull.resolve("${testPathFull.name}.filter.txt").readText()
val actualFilteredOutput = filterContentsOutput(mainHeaderContents, regexFilter)
val expectedFilteredOutput = testPathFull.resolve("${testPathFull.name}.gold.txt").readText()
// Compare to goldfile, ignoring lines order. This is needed to be stable against possible declaration order fluctuations.
// TODO Investigate why ObjCExport declarations order may vary
JUnit5Assertions.assertEquals(
expectedFilteredOutput.lines().sorted().joinToString("\n"),
actualFilteredOutput.lines().sorted().joinToString("\n")
)
}
private fun filterContentsOutput(contents: String, pattern: String) =
contents.split("\n").filter {
it.contains(Regex(pattern))
}.joinToString(separator = "\n")
private fun TestCase.toObjCFramework(): TestCompilationResult<out ObjCFramework> {
return testCompilationFactory.testCaseToObjCFrameworkCompilation(this, testRunSettings).result
}
private fun generateObjCFrameworkTestCase(testPathFull: File, sources: List<File>): TestCase {
val moduleName: String = testPathFull.name
val module = TestModule.Exclusive(moduleName, emptySet(), emptySet(), emptySet())
sources.forEach { module.files += TestFile.createCommitted(it, module) }
return TestCase(
id = TestCaseId.Named(moduleName),
kind = TestKind.STANDALONE,
modules = setOf(module),
freeCompilerArgs = TestCompilerArgs(listOf("-Xexport-kdoc")),
nominalPackageName = PackageName(moduleName),
checks = TestRunChecks.Default(testRunSettings.get<Timeouts>().executionTimeout),
extras = TestCase.WithTestRunnerExtras(TestRunnerType.DEFAULT)
).apply {
initialize(null, null)
}
}
}
@@ -207,6 +207,7 @@ private object NativeTestSupport {
output += computePipelineType(testClass.get())
output += computeUsedPartialLinkageConfig(enclosingTestClass)
output += computeCompilerOutputInterceptor(enforcedProperties)
output += computeBinariesDirs(getOrCreateTestProcessSettings().get(), nativeTargets, enclosingTestClass)
return nativeTargets
}
@@ -337,7 +338,6 @@ private object NativeTestSupport {
// Put settings that are always required:
this += computedTestConfiguration
this += computeBinariesDirs(testProcessSettings.get(), nativeTargets, enclosingTestClass)
// Add custom settings:
computedTestConfiguration.configuration.requiredSettings.forEach { clazz ->
@@ -213,6 +213,40 @@ internal class LibraryCompilation(
}
}
internal class ObjCFrameworkCompilation(
settings: Settings,
freeCompilerArgs: TestCompilerArgs,
sourceModules: Collection<TestModule>,
dependencies: Iterable<TestCompilationDependency<*>>,
expectedArtifact: ObjCFramework
) : SourceBasedCompilation<ObjCFramework>(
targets = settings.get(),
home = settings.get(),
classLoader = settings.get(),
optimizationMode = settings.get(),
compilerOutputInterceptor = settings.get(),
memoryModel = settings.get(),
threadStateChecker = settings.get(),
sanitizer = settings.get(),
gcType = settings.get(),
gcScheduler = settings.get(),
pipelineType = settings.get(),
freeCompilerArgs = freeCompilerArgs,
sourceModules = sourceModules,
dependencies = CategorizedDependencies(dependencies),
expectedArtifact = expectedArtifact
) {
override val binaryOptions get() = BinaryOptions.RuntimeAssertionsMode.defaultForTesting
override fun applySpecificArgs(argsBuilder: ArgsBuilder) = with(argsBuilder) {
add(
"-produce", "framework",
"-output", expectedArtifact.frameworkDir.absolutePath
)
super.applySpecificArgs(argsBuilder)
}
}
internal class GivenLibraryCompilation(givenArtifact: KLIB) : TestCompilation<KLIB>() {
override val result = TestCompilationResult.Success(givenArtifact, LoggedData.NoopCompilerCall(givenArtifact.klibFile))
}
@@ -24,4 +24,11 @@ internal sealed interface TestCompilationArtifact {
override val logFile: File get() = executableFile.resolveSibling("${executableFile.name}.log")
val testDumpFile: File get() = executableFile.resolveSibling("${executableFile.name}.dump")
}
data class ObjCFramework(private val buildDir: File, val frameworkName: String) : TestCompilationArtifact {
override val logFile: File get() = buildDir.resolve("$frameworkName.log")
val frameworkDir = buildDir.resolve("$frameworkName.framework")
val headersDir: File = frameworkDir.resolve("Headers")
val mainHeader: File = headersDir.resolve("$frameworkName.h")
}
}
@@ -25,9 +25,11 @@ import java.io.File
internal class TestCompilationFactory {
private val cachedKlibCompilations = ThreadSafeCache<KlibCacheKey, KlibCompilations>()
private val cachedExecutableCompilations = ThreadSafeCache<ExecutableCacheKey, TestCompilation<Executable>>()
private val cachedObjCFrameworkCompilations = ThreadSafeCache<ObjCFrameworkCacheKey, ObjCFrameworkCompilation>()
private data class KlibCacheKey(val sourceModules: Set<TestModule>, val freeCompilerArgs: TestCompilerArgs)
private data class ExecutableCacheKey(val sourceModules: Set<TestModule>)
private data class ObjCFrameworkCacheKey(val sourceModules: Set<TestModule>)
// A pair of compilations for a KLIB itself and for its static cache that are created together.
private data class KlibCompilations(val klib: TestCompilation<KLIB>, val staticCache: TestCompilation<KLIBStaticCache>?)
@@ -80,6 +82,31 @@ internal class TestCompilationFactory {
}
}
fun testCaseToObjCFrameworkCompilation(testCase: TestCase, settings: Settings): ObjCFrameworkCompilation {
val cacheKey = ObjCFrameworkCacheKey(testCase.rootModules)
cachedObjCFrameworkCompilations[cacheKey]?.let { return it }
val (
dependencies: Iterable<CompiledDependency<*>>,
sourceModules: Set<TestModule.Exclusive>
) = getDependenciesAndSourceModules(settings, testCase.rootModules, testCase.freeCompilerArgs) {
ProduceStaticCache.No
}
return cachedObjCFrameworkCompilations.computeIfAbsent(cacheKey) {
ObjCFrameworkCompilation(
settings = settings,
freeCompilerArgs = testCase.freeCompilerArgs,
sourceModules = sourceModules,
dependencies = dependencies,
expectedArtifact = ObjCFramework(
settings.artifactDirForPackageName(testCase.nominalPackageName),
testCase.nominalPackageName.compressedPackageName
)
)
}
}
fun testCasesToExecutable(testCases: Collection<TestCase>, settings: Settings): TestCompilation<Executable> {
val rootModules = testCases.flatMapToSet { testCase -> testCase.rootModules }
val cacheKey = ExecutableCacheKey(rootModules)
@@ -92,28 +119,11 @@ internal class TestCompilationFactory {
val extras = testCases.first().extras // Should be identical inside the same test case group.
val executableArtifact = Executable(settings.artifactFileForExecutable(rootModules))
val sourceModulesToCompileExecutable: Set<TestModule.Exclusive>
val dependenciesToCompileExecutable: Iterable<CompiledDependency<*>>
when (settings.get<TestMode>()) {
TestMode.ONE_STAGE_MULTI_MODULE -> {
// Collect dependencies of root modules. Compile root modules directly to executable.
sourceModulesToCompileExecutable = rootModules
dependenciesToCompileExecutable = collectDependencies(rootModules, freeCompilerArgs, settings).forOneStageExecutable()
}
TestMode.TWO_STAGE_MULTI_MODULE -> {
// Compile root modules to KLIB. Pass this KLIB as included dependency to executable compilation.
val produceStaticCache = ProduceStaticCache.decideForIncludedKlib(settings, executableArtifact, extras)
val klibCompilations = modulesToKlib(rootModules, freeCompilerArgs, produceStaticCache, settings)
sourceModulesToCompileExecutable = emptySet() // No sources.
// Include just compiled KLIB as -Xinclude dependency.
dependenciesToCompileExecutable = collectDependencies(rootModules, freeCompilerArgs, settings).forTwoStageExecutable(
includedKlib = klibCompilations.klib.asKlibDependency(IncludedLibrary),
includedKlibStaticCache = klibCompilations.staticCache?.asStaticCacheDependency()
)
}
val (
dependenciesToCompileExecutable: Iterable<CompiledDependency<*>>,
sourceModulesToCompileExecutable: Set<TestModule.Exclusive>
) = getDependenciesAndSourceModules(settings, rootModules, freeCompilerArgs) {
ProduceStaticCache.decideForIncludedKlib(settings, executableArtifact, extras)
}
return cachedExecutableCompilations.computeIfAbsent(cacheKey) {
@@ -128,6 +138,35 @@ internal class TestCompilationFactory {
}
}
private fun getDependenciesAndSourceModules(
settings: Settings,
rootModules: Set<TestModule.Exclusive>,
freeCompilerArgs: TestCompilerArgs,
produceStaticCache: () -> ProduceStaticCache,
): Pair<Iterable<CompiledDependency<*>>, Set<TestModule.Exclusive>> =
when (settings.get<TestMode>()) {
TestMode.ONE_STAGE_MULTI_MODULE -> {
Pair(
// Collect dependencies of root modules. Compile root modules directly to executable.
collectDependencies(rootModules, freeCompilerArgs, settings).forOneStageExecutable(),
rootModules
)
}
TestMode.TWO_STAGE_MULTI_MODULE -> {
// Compile root modules to KLIB. Pass this KLIB as included dependency to executable compilation.
val klibCompilations = modulesToKlib(rootModules, freeCompilerArgs, produceStaticCache(), settings)
Pair(
// Include just compiled KLIB as -Xinclude dependency.
collectDependencies(rootModules, freeCompilerArgs, settings).forTwoStageExecutable(
includedKlib = klibCompilations.klib.asKlibDependency(IncludedLibrary),
includedKlibStaticCache = klibCompilations.staticCache?.asStaticCacheDependency()
),
emptySet() // No sources.
)
}
}
private fun modulesToKlib(
sourceModules: Set<TestModule>,
freeCompilerArgs: TestCompilerArgs,