All Downloads are FREE. Search and download functionalities are using the official Maven repository.

testApi.testRunner.TestRunner.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
package org.jetbrains.dokka.testApi.testRunner

import com.intellij.openapi.application.PathManager
import org.jetbrains.dokka.*
import org.jetbrains.dokka.model.DModule
import org.jetbrains.dokka.pages.RootPageNode
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.DokkaPlugin
import org.jetbrains.dokka.testApi.logger.TestLogger
import org.jetbrains.dokka.utilities.DokkaLogger
import org.junit.rules.TemporaryFolder
import testApi.testRunner.TestDokkaConfigurationBuilder
import java.io.File
import java.net.URL
import java.nio.charset.Charset
import java.nio.file.Files
import java.nio.file.InvalidPathException
import java.nio.file.Path
import java.nio.file.Paths

// TODO: take dokka configuration from file
abstract class AbstractTest, D : DokkaTestGenerator>(
    protected val testBuilder: () -> T,
    protected val dokkaTestGenerator: (DokkaConfiguration, DokkaLogger, M, List) -> D,
    protected val logger: TestLogger,
) {
    protected fun getTestDataDir(name: String) =
        File("src/test/resources/$name").takeIf { it.exists() }?.toPath()
            ?: throw InvalidPathException(name, "Cannot be found")

    protected fun testFromData(
        configuration: DokkaConfigurationImpl,
        cleanupOutput: Boolean = true,
        preserveOutputLocation: Boolean = false,
        pluginOverrides: List = emptyList(),
        block: T.() -> Unit
    ) {
        val testMethods = testBuilder().apply(block).build()
        val configurationToUse = if (!preserveOutputLocation) {
            val tempDir = getTempDir(cleanupOutput)
            if (!cleanupOutput)
                logger.info("Output generated under: ${tempDir.root.absolutePath}")
            configuration.copy(
                outputDir = tempDir.root
            )
        } else configuration

        dokkaTestGenerator(
            configurationToUse,
            logger,
            testMethods,
            pluginOverrides
        ).generate()
    }

    protected fun testInline(
        query: String,
        configuration: DokkaConfigurationImpl,
        cleanupOutput: Boolean = true,
        pluginOverrides: List = emptyList(),
        loggerForTest: DokkaLogger = logger,
        block: T.() -> Unit
    ) {
        val testMethods = testBuilder().apply(block).build()
        val testDirPath = getTempDir(cleanupOutput).root.toPath()
        val fileMap = query.toFileMap()
        fileMap.materializeFiles(testDirPath.toAbsolutePath())
        if (!cleanupOutput)
            loggerForTest.info("Output generated under: ${testDirPath.toAbsolutePath()}")
        val newConfiguration = configuration.copy(
            outputDir = testDirPath.toFile(),
            sourceSets = configuration.sourceSets.map { sourceSet ->
                sourceSet.copy(
                    sourceRoots = sourceSet.sourceRoots.map { file ->
                        testDirPath.toFile().resolve(file)
                    }.toSet(),
                    suppressedFiles = sourceSet.suppressedFiles.map { file ->
                        testDirPath.toFile().resolve(file)
                    }.toSet(),
                    sourceLinks = sourceSet.sourceLinks.map { link ->
                        link.copy(
                            localDirectory = testDirPath.toFile().resolve(link.localDirectory).canonicalPath
                        )
                    }.toSet()
                )
            }
        )
        dokkaTestGenerator(
            newConfiguration,
            loggerForTest,
            testMethods,
            pluginOverrides
        ).generate()
    }


    private fun String.toFileMap(): Map {
        return this.trimIndent().trimMargin()
            .replace("\r\n", "\n")
            .sliceAt(filePathRegex)
            .filter { it.isNotEmpty() && it.isNotBlank() && "\n" in it }
            .map { fileDeclaration -> fileDeclaration.trim() }
            .map { fileDeclaration ->
                val filePathAndContent = fileDeclaration.split("\n", limit = 2)
                val filePath = filePathAndContent.first().removePrefix("/").trim()
                val content = filePathAndContent.last().trim()
                filePath to content
            }
            .toMap()
    }

    private fun String.sliceAt(regex: Regex): List {
        val matchesStartIndices = regex.findAll(this).toList().map { match -> match.range.first }
        return sequence {
            yield(0)
            yieldAll(matchesStartIndices)
            yield([email protected])
        }
            .zipWithNext { startIndex: Int, endIndex: Int -> substring(startIndex, endIndex) }
            .toList()
            .also { slices ->
                /* Post-condition verifying that no character is lost */
                check(slices.sumBy { it.length } == length)
            }
    }

    private fun Map.materializeFiles(
        root: Path = Paths.get("."),
        charset: Charset = Charset.forName("utf-8")
    ) = this.map { (path, content) ->
        val file = root.resolve(path)
        Files.createDirectories(file.parent)
        Files.write(file, content.toByteArray(charset))
    }

    private fun getTempDir(cleanupOutput: Boolean) = if (cleanupOutput) {
        TemporaryFolder().apply { create() }
    } else {
        object : TemporaryFolder() {
            override fun after() {}
        }.apply { create() }
    }

    protected fun dokkaConfiguration(block: TestDokkaConfigurationBuilder.() -> Unit): DokkaConfigurationImpl =
        testApi.testRunner.dokkaConfiguration(block)


    protected val jvmStdlibPath: String? by lazy {
        PathManager.getResourceRoot(Strictfp::class.java, "/kotlin/jvm/Strictfp.class")
    }

    protected val jsStdlibPath: String? by lazy {
        PathManager.getResourceRoot(Any::class.java, "/kotlin/jquery")
    }

    protected val commonStdlibPath: String? by lazy {
        // TODO: feels hacky, find a better way to do it
        ClassLoader.getSystemResource("kotlin/UInt.kotlin_metadata")
            ?.file
            ?.replace("file:", "")
            ?.replaceAfter(".jar", "")
    }

    protected val stdlibExternalDocumentationLink = ExternalDocumentationLinkImpl(
        URL("https://kotlinlang.org/api/latest/jvm/stdlib/"),
        URL("https://kotlinlang.org/api/latest/jvm/stdlib/package-list")
    )

    companion object {
        private val filePathRegex = Regex("""[\n^](\/[\w|\-]+)+(\.\w+)?\s*\n""")
    }
}

interface TestMethods

open class CoreTestMethods(
    open val pluginsSetupStage: (DokkaContext) -> Unit,
    open val verificationStage: (() -> Unit) -> Unit,
    open val documentablesCreationStage: (List) -> Unit,
    open val documentablesMergingStage: (DModule) -> Unit,
    open val documentablesTransformationStage: (DModule) -> Unit,
    open val pagesGenerationStage: (RootPageNode) -> Unit,
    open val pagesTransformationStage: (RootPageNode) -> Unit,
    open val renderingStage: (RootPageNode, DokkaContext) -> Unit
) : TestMethods

abstract class TestBuilder {
    abstract fun build(): M
}

abstract class DokkaTestGenerator(
    protected val configuration: DokkaConfiguration,
    protected val logger: DokkaLogger,
    protected val testMethods: T,
    protected val additionalPlugins: List = emptyList()
) {
    abstract fun generate()
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy