From b13ff5993eb6a24adea0d17d989a594ae2e1e062 Mon Sep 17 00:00:00 2001 From: Szymon Świstun Date: Tue, 14 Jan 2020 10:34:36 +0100 Subject: Add testing utils and examples --- .../test/kotlin/generator/DokkaTestGenerator.kt | 78 ++++++++++ core/src/test/kotlin/generator/TestRunner.kt | 157 +++++++++++++++++++++ .../test/kotlin/generator/test/GeneratorTest.kt | 62 ++++++++ .../kotlin/generator/test/MultiplatformTest.kt | 52 +++++++ core/src/test/kotlin/utils/Builders.kt | 78 ++++++++++ 5 files changed, 427 insertions(+) create mode 100644 core/src/test/kotlin/generator/DokkaTestGenerator.kt create mode 100644 core/src/test/kotlin/generator/TestRunner.kt create mode 100644 core/src/test/kotlin/generator/test/GeneratorTest.kt create mode 100644 core/src/test/kotlin/generator/test/MultiplatformTest.kt create mode 100644 core/src/test/kotlin/utils/Builders.kt (limited to 'core/src/test/kotlin') diff --git a/core/src/test/kotlin/generator/DokkaTestGenerator.kt b/core/src/test/kotlin/generator/DokkaTestGenerator.kt new file mode 100644 index 00000000..9ceeeadf --- /dev/null +++ b/core/src/test/kotlin/generator/DokkaTestGenerator.kt @@ -0,0 +1,78 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.model.Module +import org.jetbrains.dokka.pages.ModulePageNode +import org.jetbrains.dokka.pages.PlatformData +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.single +import org.jetbrains.dokka.renderers.FileWriter +import org.jetbrains.dokka.utilities.DokkaLogger + +class DokkaTestGenerator( + private val configuration: DokkaConfiguration, + private val logger: DokkaLogger, + private val setupTest: (Map) -> Unit?, + private val pluginInitTest: (DokkaContext) -> Unit?, + private val documentablesCreationTest: (List) -> Unit?, + private val documentablesMergingTest: (Module) -> Unit?, + private val documentablesTransformationTest: (Module) -> Unit?, + private val pagesCreationTest: (ModulePageNode) -> Unit?, + private val pagesTransformationTest: (ModulePageNode) -> Unit?, + private val finalTest: (DokkaConfiguration) -> Unit? +) : DokkaGenerator(configuration, logger) { + + override fun generate() { + logger.debug("Setting up analysis environments") + val platforms = configuration.passesConfigurations.map { + PlatformData(it.moduleName, it.analysisPlatform, it.targets) to createEnvironmentAndFacade(it) + }.toMap() + + setupTest(platforms) + + logger.debug("Initializing plugins") + val context = DokkaContext.create(configuration.pluginsClasspath, logger, platforms) + + pluginInitTest(context) + + logger.debug("Creating documentation models") + val modulesFromPlatforms = platforms.map { (pdata, _) -> translateDescriptors(pdata, context) } + + documentablesCreationTest(modulesFromPlatforms) + + logger.debug("Merging documentation models") + val documentationModel = context.single(CoreExtensions.documentationMerger) + .invoke(modulesFromPlatforms, context) + + documentablesMergingTest(documentationModel) + + logger.debug("Transforming documentation model") + val transformedDocumentation = context[CoreExtensions.documentationTransformer] + .fold(documentationModel) { acc, t -> t(acc, context) } + + documentablesTransformationTest(transformedDocumentation) + + logger.debug("Creating pages") + val pages = context.single(CoreExtensions.documentationToPageTranslator) + .invoke(transformedDocumentation, context) + + pagesCreationTest(pages) + + logger.debug("Transforming pages") + val transformedPages = context[CoreExtensions.pageTransformer] + .fold(pages) { acc, t -> t(acc, context) } + + pagesTransformationTest(transformedPages) + + logger.debug("Rendering") + val fileWriter = FileWriter(configuration.outputDir, "") + val locationProvider = context.single(CoreExtensions.locationProviderFactory) + .invoke(transformedPages, configuration, context) + val renderer = context.single(CoreExtensions.rendererFactory) + .invoke(fileWriter, locationProvider, context) + + renderer.render(transformedPages) + + finalTest(configuration) + } + +} \ No newline at end of file diff --git a/core/src/test/kotlin/generator/TestRunner.kt b/core/src/test/kotlin/generator/TestRunner.kt new file mode 100644 index 00000000..305996f8 --- /dev/null +++ b/core/src/test/kotlin/generator/TestRunner.kt @@ -0,0 +1,157 @@ +package generator + +import com.intellij.util.io.createDirectories +import com.intellij.util.io.exists +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaTestGenerator +import org.jetbrains.dokka.EnvironmentAndFacade +import org.jetbrains.dokka.SourceRootImpl +import org.jetbrains.dokka.model.Module +import org.jetbrains.dokka.pages.ModulePageNode +import org.jetbrains.dokka.pages.PlatformData +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.utilities.DokkaConsoleLogger +import org.jetbrains.dokka.utilities.DokkaLogger +import utils.Builders +import java.io.File +import java.nio.charset.Charset +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths + +data class ConfigParams(val name: String, val root: Path, val out: Path) + +object TestRunner { + + private val createdDirs: MutableList = mutableListOf() + + fun cleanup() { + createdDirs.forEach { f -> f.takeIf { it.exists() }?.deleteRecursively() } + createdDirs.clear() + } + + fun srcSetFromName(name: String) = + ClassLoader.getSystemResource("sourceSets/$name").takeIf { it != null } + ?.let { Paths.get(it.toURI()).toString() } + + fun testFromSourceSets( + name: String, + configBuilder: Builders.ConfigBuilder, + setupTest: (Map) -> Unit? = {}, + pluginInitTest: (DokkaContext) -> Unit? = {}, + documentablesCreationTest: (List) -> Unit? = {}, + documentablesMergingTest: (Module) -> Unit? = {}, + documentablesTransformationTest: (Module) -> Unit? = {}, + pagesCreationTest: (ModulePageNode) -> Unit? = {}, + pagesTransformationTest: (ModulePageNode) -> Unit? = {}, + finalTest: (DokkaConfiguration) -> Unit? = {}, + logger: DokkaLogger = DokkaConsoleLogger + ) { + val (out, _) = getTestDirs(name) + DokkaTestGenerator( + configBuilder(out.toString()), + logger, + setupTest, + pluginInitTest, + documentablesCreationTest, + documentablesMergingTest, + documentablesTransformationTest, + pagesCreationTest, + pagesTransformationTest, + finalTest + ).generate() + } + + fun testInline( + name: String, + query: String, + config: (ConfigParams) -> DokkaConfiguration, + setupTest: (Map) -> Unit? = {}, + pluginInitTest: (DokkaContext) -> Unit? = {}, + documentablesCreationTest: (List) -> Unit? = {}, + documentablesMergingTest: (Module) -> Unit? = {}, + documentablesTransformationTest: (Module) -> Unit? = {}, + pagesCreationTest: (ModulePageNode) -> Unit? = {}, + pagesTransformationTest: (ModulePageNode) -> Unit? = {}, + finalTest: (DokkaConfiguration) -> Unit? = {}, + logger: DokkaLogger = DokkaConsoleLogger + ) { + + val (root, out) = getTestDirs(name) + query.toFileMap().materializeFiles(root) + DokkaTestGenerator( + config(ConfigParams(name, root, out)), + logger, + setupTest, + pluginInitTest, + documentablesCreationTest, + documentablesMergingTest, + documentablesTransformationTest, + pagesCreationTest, + pagesTransformationTest, + finalTest + ).generate() + } + + fun generatePassesForPlatforms( + testName: String, + platforms: List, + passBuilder: Builders.PassBuilder + ): List { + fun File.nameLower() = this.name.toLowerCase() + fun File.isCommon() = this.nameLower().contains("common") + fun File.getPlatforms(platforms: List) = + platforms.filter { this.nameLower().contains(it.toLowerCase()) } + + + val testSrcDirs = + srcSetFromName(testName)?.let { fName -> + File(fName).listFiles()?.filter { it.isDirectory } + } ?: emptyList() + + val dirs = + platforms.associateWith { platform -> + testSrcDirs.filter { file -> + platform == "common" && file.isCommon() && file.getPlatforms(platforms).size == 1 || + platform != "common" && file.nameLower().contains(platform.toLowerCase()) + }.map { it.path } + }.filter { (_, value) -> value.isNotEmpty() } + + return dirs.map { (platform, srcs) -> + passBuilder.copy(moduleName = "$testName-$platform", analysisPlatform = platform, sourceRoots = srcs) + } + } + + fun getTestDirs(name: String) = + Files.createTempDirectory(name).also { it.takeIf { it.exists() }?.deleteContents() } + .let { it to it.resolve("out").also { it.createDirectories() } } + .also { createdDirs += it.first.toFile() } + + fun String.toFileMap(): Map = this.replace("\r\n", "\n") + .split("\n/") + .map {fileString -> + fileString.split("\n", limit = 2) + .let { + it.first().trim().removePrefix("/") to it.last().trim() + } + } + .toMap() + + 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)) + } + + fun File.deleteContents() { + this.listFiles()?.forEach { it.deleteRecursively() } + } + + fun Path.deleteContents() { + this.toFile().deleteContents() + } + +} \ No newline at end of file diff --git a/core/src/test/kotlin/generator/test/GeneratorTest.kt b/core/src/test/kotlin/generator/test/GeneratorTest.kt new file mode 100644 index 00000000..97eb8b92 --- /dev/null +++ b/core/src/test/kotlin/generator/test/GeneratorTest.kt @@ -0,0 +1,62 @@ +package generator.test + +import generator.ConfigParams +import generator.TestRunner +import org.jetbrains.dokka.* +import org.jetbrains.dokka.utilities.DokkaConsoleLogger +import org.junit.AfterClass +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestName +import utils.Builders + +class GeneratorTest { + + companion object { + @AfterClass @JvmStatic + fun cleanup() = TestRunner.cleanup() + } + + val builder = Builders.ConfigBuilder( + format = "html", + generateIndexPages = true + ) + + val name: TestName = TestName() + @Rule + fun name(): TestName = name + + val logger = DokkaConsoleLogger + fun config(): (ConfigParams) -> DokkaConfiguration = { (name, root, out) -> + builder.copy( + passesConfigurations = listOf( + Builders.PassBuilder( + moduleName = name, + sourceRoots = listOf(root.toString()), + analysisPlatform = "jvm", + targets = listOf("jvm") + ) + ) + )(out.toString()) + } + + @Test + fun test1() { + TestRunner.testInline( + name = name.methodName, + query = """ + |/src/main/kotlin/test/Test.kt + |package test + | + |object Test { + | fun test2(str: String): Unit {println(str)} + |} + """.trimMargin(), + pagesCreationTest = { pages -> + val test = pages.parentMap.size == 7 + if (!test) println(pages.parentMap.size) + assert(test) + }, config = config(), logger = logger + ) + } +} \ No newline at end of file diff --git a/core/src/test/kotlin/generator/test/MultiplatformTest.kt b/core/src/test/kotlin/generator/test/MultiplatformTest.kt new file mode 100644 index 00000000..2e28797d --- /dev/null +++ b/core/src/test/kotlin/generator/test/MultiplatformTest.kt @@ -0,0 +1,52 @@ +package generator.test + +import generator.TestRunner +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.utilities.DokkaConsoleLogger +import org.junit.AfterClass +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestName +import utils.Builders + +class MultiplatformTest { + + companion object { + @AfterClass @JvmStatic + fun cleanup() = + TestRunner.cleanup() + } + + val platforms = Platform.values().map { it.name } + + val configBuilder = Builders.ConfigBuilder( + format = "html", + generateIndexPages = true + ) + val passBuilder = Builders.PassBuilder(targets = platforms) + + val name: TestName = TestName() + @Rule + fun name(): TestName = name + + val logger = DokkaConsoleLogger + + @Test + fun example() { + val testName = name.methodName + + val passBuilders = + TestRunner.generatePassesForPlatforms(testName, platforms, passBuilder) + + TestRunner.testFromSourceSets( + name = testName, + configBuilder = configBuilder.copy(passesConfigurations = passBuilders), + pagesTransformationTest = { assert(it.children.size == 2) }, + finalTest = { + assert(it.passesConfigurations.size == 3) + }, + logger = logger + ) + } + +} \ No newline at end of file diff --git a/core/src/test/kotlin/utils/Builders.kt b/core/src/test/kotlin/utils/Builders.kt new file mode 100644 index 00000000..e881082f --- /dev/null +++ b/core/src/test/kotlin/utils/Builders.kt @@ -0,0 +1,78 @@ +package utils + +import org.jetbrains.dokka.DokkaConfigurationImpl +import org.jetbrains.dokka.PassConfigurationImpl +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.SourceRootImpl +import java.io.File + +object Builders { + data class ConfigBuilder( + val format: String = "html", + val generateIndexPages: Boolean = true, + val cacheRoot: String? = null, + val impliedPlatforms: List = emptyList(), + val passesConfigurations: List = emptyList(), + var pluginsClasspath: List = emptyList() + ) { + operator fun invoke(out: String) = + DokkaConfigurationImpl( + outputDir = out, + format = format, + generateIndexPages = generateIndexPages, + cacheRoot = cacheRoot, + impliedPlatforms = impliedPlatforms, + passesConfigurations = passesConfigurations.map { it() }, + pluginsClasspath = pluginsClasspath.map { File(it) } + ) + } + + data class PassBuilder( + val moduleName: String = "", + val classpath: List = emptyList(), + val sourceRoots: List = emptyList(), + val samples: List = emptyList(), + val includes: List = emptyList(), + val includeNonPublic: Boolean = true, + val includeRootPackage: Boolean = true, + val reportUndocumented: Boolean = false, + val skipEmptyPackages: Boolean = false, + val skipDeprecated: Boolean = false, + val jdkVersion: Int = 6, + val languageVersion: String? = null, + val apiVersion: String? = null, + val noStdlibLink: Boolean = false, + val noJdkLink: Boolean = false, + val suppressedFiles: List = emptyList(), + val collectInheritedExtensionsFromLibraries: Boolean = true, + val analysisPlatform: String = "", + val targets: List = emptyList(), + val sinceKotlin: String? = null + ) { + operator fun invoke() = + PassConfigurationImpl( + moduleName = moduleName, + classpath = classpath, + sourceRoots = sourceRoots.map{ SourceRootImpl(it) }, + samples = samples, + includes = includes, + includeNonPublic = includeNonPublic, + includeRootPackage = includeRootPackage, + reportUndocumented = reportUndocumented, + skipEmptyPackages = skipEmptyPackages, + skipDeprecated = skipDeprecated, + jdkVersion = jdkVersion, + languageVersion = languageVersion, + apiVersion = apiVersion, + noStdlibLink = noStdlibLink, + noJdkLink = noJdkLink, + suppressedFiles = suppressedFiles, + collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries, + analysisPlatform = Platform.fromString(analysisPlatform), + targets = targets, + sinceKotlin = sinceKotlin + ) + } + + +} \ No newline at end of file -- cgit