Skip to content

Commit

Permalink
Add global settings to JSON dokka cli input
Browse files Browse the repository at this point in the history
  • Loading branch information
BarkingBad committed Dec 27, 2021
1 parent 1f82c97 commit 2ace166
Show file tree
Hide file tree
Showing 11 changed files with 336 additions and 12 deletions.
3 changes: 3 additions & 0 deletions integration-tests/cli/build.gradle.kts
Expand Up @@ -12,6 +12,8 @@ evaluationDependsOn(":plugins:base")
dependencies {
implementation(kotlin("stdlib"))
implementation(kotlin("test-junit"))
val jackson_version: String by project
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version")
}

/* Create a fat base plugin jar for cli tests */
Expand All @@ -23,6 +25,7 @@ val basePluginShadow: Configuration by configurations.creating {

dependencies {
basePluginShadow(project(":plugins:base"))

}
val basePluginShadowJar by tasks.register("basePluginShadowJar", ShadowJar::class) {
configurations = listOf(basePluginShadow)
Expand Down
Expand Up @@ -2,6 +2,8 @@ package org.jetbrains.dokka.it.cli

import org.jetbrains.dokka.it.awaitProcessResult
import java.io.File
import java.io.PrintWriter
import java.lang.IllegalStateException
import kotlin.test.*

class CliIntegrationTest : AbstractCliIntegrationTest() {
Expand Down Expand Up @@ -193,7 +195,7 @@ class CliIntegrationTest : AbstractCliIntegrationTest() {
}

@Test
fun `logging level should be respected`(){
fun `logging level should be respected`() {
val dokkaOutputDir = File(projectDir, "output")
assertTrue(dokkaOutputDir.mkdirs())
val process = ProcessBuilder(
Expand Down Expand Up @@ -259,4 +261,111 @@ class CliIntegrationTest : AbstractCliIntegrationTest() {
)
)
}


@Test
fun json() {
val dokkaOutputDir = File(projectDir, "output")
assertTrue(dokkaOutputDir.mkdirs())
val jsonPath = javaClass.getResource("/my-file.json")?.path ?: throw IllegalStateException("No JSON found!")
PrintWriter(jsonPath).run {
write(jsonBuilder(dokkaOutputDir.path, basePluginJarFile.path, File(projectDir, "src").path, reportUndocumented = true))
close()
}

val process = ProcessBuilder(
"java", "-jar", cliJarFile.path, jsonPath
).redirectErrorStream(true).start()

val result = process.awaitProcessResult()
assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)")

val extensionLoadedRegex = Regex("""Extension: org\.jetbrains\.dokka\.base\.DokkaBase""")
val amountOfExtensionsLoaded = extensionLoadedRegex.findAll(result.output).count()

assertTrue(
amountOfExtensionsLoaded > 10,
"Expected more than 10 extensions being present (found $amountOfExtensionsLoaded)"
)

val undocumentedReportRegex = Regex("""Undocumented:""")
val amountOfUndocumentedReports = undocumentedReportRegex.findAll(result.output).count()
assertTrue(
amountOfUndocumentedReports > 0,
"Expected at least one report of undocumented code (found $amountOfUndocumentedReports)"
)

assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory")
}

/**
* This test disables global `reportUndocumneted` property and set `reportUndocumented` via perPackageOptions to
* make sure that global settings apply to dokka context.
*/
@Test
fun jsonWithGlobals() {
val dokkaOutputDir = File(projectDir, "output")
assertTrue(dokkaOutputDir.mkdirs())
val jsonPath = javaClass.getResource("/my-file.json")?.path ?: throw IllegalStateException("No JSON found!")
PrintWriter(jsonPath).run {
write(
jsonBuilder(
outputPath = dokkaOutputDir.path,
pluginsClasspath = basePluginJarFile.path,
projectPath = File(projectDir, "src").path,
globalSourceLinks = """
{
"localDirectory": "/home/Vadim.Mishenev/dokka/examples/cli/src/main/kotlin",
"remoteUrl": "https://github.com/Kotlin/dokka/tree/master/examples/gradle/dokka-gradle-example/src/main/kotlin",
"remoteLineSuffix": "#L"
}
""".trimIndent(),
globalExternalDocumentationLinks = """
{
"url": "https://docs.oracle.com/javase/8/docs/api/",
"packageListUrl": "https://docs.oracle.com/javase/8/docs/api/package-list"
},
{
"url": "https://kotlinlang.org/api/latest/jvm/stdlib/",
"packageListUrl": "https://kotlinlang.org/api/latest/jvm/stdlib/package-list"
}
""".trimIndent(),
globalPerPackageOptions = """
{
"matchingRegex": ".*",
"skipDeprecated": "true",
"reportUndocumented": "true",
"includeNonPublic": "true"
}
""".trimIndent(),
reportUndocumented = false
),
)
close()
}

val process = ProcessBuilder(
"java", "-jar", cliJarFile.path, jsonPath
).redirectErrorStream(true).start()

val result = process.awaitProcessResult()
assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)")

val extensionLoadedRegex = Regex("""Extension: org\.jetbrains\.dokka\.base\.DokkaBase""")
val amountOfExtensionsLoaded = extensionLoadedRegex.findAll(result.output).count()

assertTrue(
amountOfExtensionsLoaded > 10,
"Expected more than 10 extensions being present (found $amountOfExtensionsLoaded)"
)

val undocumentedReportRegex = Regex("""Undocumented:""")
val amountOfUndocumentedReports = undocumentedReportRegex.findAll(result.output).count()
assertTrue(
amountOfUndocumentedReports > 0,
"Expected at least one report of undocumented code (found $amountOfUndocumentedReports)"
)

assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory")
}
}
@@ -0,0 +1,52 @@
package org.jetbrains.dokka.it.cli

fun jsonBuilder(
outputPath: String,
pluginsClasspath: String,
projectPath: String,
globalSourceLinks: String = "",
globalExternalDocumentationLinks: String = "",
globalPerPackageOptions: String = "",
reportUndocumented: Boolean = false

): String {
return """{
"moduleName": "Dokka Example",
"moduleVersion": null,
"outputDir": "$outputPath",
"pluginsClasspath": ["$pluginsClasspath"],
"cacheRoot": null,
"offlineMode": false,
"sourceLinks": [$globalSourceLinks],
"externalDocumentationLinks": [$globalExternalDocumentationLinks],
"perPackageOptions": [$globalPerPackageOptions],
"sourceSets": [
{
"displayName": "jvm",
"sourceSetID": {
"scopeId": ":dokkaHtml",
"sourceSetName": "main"
},
"sourceRoots": [
"$projectPath"
],
"dependentSourceSets": [],
"samples": [],
"includes": [],
"includeNonPublic": false,
"reportUndocumented": $reportUndocumented,
"skipEmptyPackages": true,
"skipDeprecated": false,
"jdkVersion": 8,
"sourceLinks": [],
"perPackageOptions": [],
"externalDocumentationLinks": [],
"noStdlibLink": false,
"noJdkLink": false,
"suppressedFiles": [],
"analysisPlatform": "jvm"
}
]
}
"""
}
Empty file.
10 changes: 10 additions & 0 deletions plugins/all-modules-page/out/index.md
@@ -0,0 +1,10 @@
/

# Sample project

Sample documentation with [external link](https://www.google.pl)

## All modules:

| Name |
|---|
2 changes: 1 addition & 1 deletion plugins/base/frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions runners/cli/build.gradle.kts
Expand Up @@ -10,6 +10,10 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.3")
implementation(project(":core"))
implementation(kotlin("stdlib"))
val jackson_version: String by project
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version")

testImplementation(kotlin("test-junit"))
}

tasks {
Expand Down
48 changes: 48 additions & 0 deletions runners/cli/src/main/kotlin/cli/JsonMapperForCLI.kt
@@ -0,0 +1,48 @@
package org.jetbrains.dokka

import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
import java.io.File

// THIS IS COPIED FROM BASE SINCE IT NEEDS TO BE INSTANTIATED ON THE SAME CLASS LOADER AS PLUGINS

private val objectMapper = run {
val module = SimpleModule().apply {
addSerializer(FileSerializer)
}
jacksonObjectMapper()
.registerModule(module)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
}

@PublishedApi
internal class TypeReference<T> private constructor(
internal val jackson: com.fasterxml.jackson.core.type.TypeReference<T>
) {
companion object {
internal inline operator fun <reified T> invoke(): TypeReference<T> = TypeReference(jacksonTypeRef())
}
}

inline fun <reified T : Any> parseJson(json: String): T = parseJson(json, TypeReference())

@PublishedApi
internal fun <T : Any> parseJson(json: String, typeReference: TypeReference<T>): T =
objectMapper.readValue(json, typeReference.jackson)

private object FileSerializer : StdScalarSerializer<File>(File::class.java) {
override fun serialize(value: File, g: JsonGenerator, provider: SerializerProvider) {
g.writeString(value.path)
}
}

data class GlobalDokkaConfiguration(
val perPackageOptions: List<PackageOptionsImpl>?,
val externalDocumentationLinks: List<ExternalDocumentationLinkImpl>?,
val sourceLinks: List<SourceLinkDefinitionImpl>?
)
39 changes: 29 additions & 10 deletions runners/cli/src/main/kotlin/cli/main.kt
Expand Up @@ -2,10 +2,7 @@ package org.jetbrains.dokka

import kotlinx.cli.*
import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink
import org.jetbrains.dokka.utilities.DokkaConsoleLogger
import org.jetbrains.dokka.utilities.DokkaLogger
import org.jetbrains.dokka.utilities.LoggingLevel
import org.jetbrains.dokka.utilities.cast
import org.jetbrains.dokka.utilities.*
import java.io.*
import java.net.MalformedURLException
import java.net.URL
Expand Down Expand Up @@ -80,6 +77,7 @@ class GlobalArguments(args: Array<String>) : DokkaConfiguration {

private val _includes by parser.option(
ArgTypeFile,
fullName = "includes",
description = "Markdown files that would be displayed in multi-module page separated by the semicolon `;`)"
).delimiter(";")

Expand Down Expand Up @@ -406,14 +404,35 @@ fun parseLinks(links: List<String>): List<ExternalDocumentationLink> {
}
}

fun initializeConfiguration(globalArguments: GlobalArguments): DokkaConfiguration = if (globalArguments.json != null) {
val jsonContent = Paths.get(checkNotNull(globalArguments.json)).toFile().readText()
val globals: GlobalDokkaConfiguration = parseJson(jsonContent)
val dokkaConfigurationImpl = DokkaConfigurationImpl(jsonContent)
dokkaConfigurationImpl.run {
sourceSets.forEach {
it.perPackageOptions.cast<MutableList<DokkaConfiguration.PackageOptions>>().addAll(globals.perPackageOptions ?: emptyList())
}

sourceSets.forEach {
it.externalDocumentationLinks.cast<MutableSet<ExternalDocumentationLink>>().addAll(globals.externalDocumentationLinks ?: emptyList())
}

sourceSets.forEach {
it.sourceLinks.cast<MutableSet<SourceLinkDefinitionImpl>>().addAll(globals.sourceLinks ?: emptyList())
}

sourceSets.forEach {
it.externalDocumentationLinks.cast<MutableSet<ExternalDocumentationLink>>().addAll(defaultLinks(it))
}
}
dokkaConfigurationImpl
} else {
globalArguments
}

fun main(args: Array<String>) {
val globalArguments = GlobalArguments(args)
val configuration = if (globalArguments.json != null)
DokkaConfigurationImpl(
Paths.get(checkNotNull(globalArguments.json)).toFile().readText()
)
else
globalArguments
val configuration = initializeConfiguration(globalArguments)
DokkaGenerator(configuration, globalArguments.logger).generate()
}

29 changes: 29 additions & 0 deletions runners/cli/src/test/kotlin/cli/CliTest.kt
@@ -0,0 +1,29 @@
package org.jetbrains.dokka

import junit.framework.Assert.assertTrue
import org.junit.Test
import java.lang.IllegalStateException
import kotlin.test.assertEquals

class CliIntegrationTest {

@Test
fun testGlobalArgs() {
val jsonPath = javaClass.getResource("/my-file.json")?.path ?: throw IllegalStateException("No JSON found!")
val globalArguments = GlobalArguments(arrayOf(jsonPath))

val configuration = initializeConfiguration(globalArguments)

configuration.sourceSets.forEach {
assertTrue(it.perPackageOptions.isNotEmpty())
assertTrue(it.sourceLinks.isNotEmpty())
assertTrue(it.externalDocumentationLinks.isNotEmpty())

assertTrue(it.externalDocumentationLinks.any { it.url.toString() == "https://docs.oracle.com/javase/8/docs/api/" })
assertEquals(it.sourceLinks.single().localDirectory, "/home/Vadim.Mishenev/dokka/examples/cli/src/main/kotlin")
assertEquals(it.perPackageOptions.single().matchingRegex, "my-custom-regex")
}

}

}

0 comments on commit 2ace166

Please sign in to comment.