Skip to content

Commit

Permalink
Test DGPv2 example projects (#3821)
Browse files Browse the repository at this point in the history
* Test DGPv2 example projects

Test the DGPv2 examples by comparing using golden testing.

The example projects are executed using Gradle TestKit. The generated Dokka output is compared against data committed into the directory
`./dokka-integration-tests/gradle/src/testExampleProjects/expectedData/`

KT-71346
  • Loading branch information
adam-enko authored Oct 31, 2024
1 parent 623ee35 commit af05202
Show file tree
Hide file tree
Showing 631 changed files with 111,195 additions and 105 deletions.
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ docs-developer/** linguist-doc
examples/** linguist-documentation
dokka-integration-tests/gradle/projects/** linguist-documentation

dokka-integration-tests/gradle/src/testExampleProjects/expectedData/** linguist-generated
dokka-runners/dokka-gradle-plugin/src/testFunctional/resources/KotlinDslAccessorsTest/** linguist-generated

# Tell Git not to export certain files or directories when generating an archive.
# Since an archive doesn't contain git data, also exclude git metadata files.
.gitattributes export-ignore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
package dokkabuild.utils

import org.gradle.api.file.Directory
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileCollection
import org.gradle.api.file.RegularFile
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskInputFilePropertyBuilder
import org.gradle.api.tasks.TaskInputPropertyBuilder
import org.gradle.api.tasks.TaskOutputFilePropertyBuilder
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.findByType
Expand Down Expand Up @@ -39,9 +39,10 @@ abstract class SystemPropertyAdder @Inject internal constructor(
) {
private val objects: ObjectFactory = task.project.objects

@JvmName("inputDirectoryProvider")
fun inputDirectory(
key: String,
value: DirectoryProperty,
value: Provider<out Directory>,
): TaskInputFilePropertyBuilder {
task.jvmArgumentProviders.add(
SystemPropertyArgumentProvider(key, value) {
Expand All @@ -52,18 +53,59 @@ abstract class SystemPropertyAdder @Inject internal constructor(
.withPropertyName("SystemProperty input directory $key")
}

@JvmName("inputDirectoryFile")
fun inputDirectory(
key: String,
value: Provider<File>,
): TaskInputFilePropertyBuilder =
inputDirectory(key, objects.directoryProperty().fileProvider(value))

fun inputDirectory(
key: String,
value: File,
): TaskInputFilePropertyBuilder =
inputDirectory(key, objects.directoryProperty().fileValue(value))

fun inputDirectory(
key: String,
value: Directory,
): TaskInputFilePropertyBuilder =
inputDirectory(key, objects.directoryProperty().apply { set(value) })


@JvmName("outputDirectoryProvider")
fun outputDirectory(
key: String,
value: Provider<out Directory>,
): TaskOutputFilePropertyBuilder {
task.jvmArgumentProviders.add(
SystemPropertyArgumentProvider(key, value) {
it.get().asFile.invariantSeparatorsPath
}
)
return task.outputs.dir(value)
.withPropertyName("SystemProperty input directory $key")
}

@JvmName("outputDirectoryFile")
fun outputDirectory(
key: String,
value: Provider<File>,
): TaskOutputFilePropertyBuilder =
outputDirectory(key, objects.directoryProperty().fileProvider(value))

fun outputDirectory(
key: String,
value: File,
): TaskOutputFilePropertyBuilder =
outputDirectory(key, objects.directoryProperty().fileValue(value))

fun outputDirectory(
key: String,
value: Directory,
): TaskOutputFilePropertyBuilder =
outputDirectory(key, objects.directoryProperty().apply { set(value) })

fun inputFile(
key: String,
file: RegularFile,
Expand Down
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ idea {
"dokka-runners/runner-cli/.kotlin",
"dokka-runners/runner-maven-plugin/.kotlin",
"dokka-runners/dokka-gradle-plugin/src/testFunctional/resources/KotlinDslAccessorsTest/",

"dokka-integration-tests/gradle/src/testExampleProjects/expectedData",
)
)
}
Expand Down
22 changes: 22 additions & 0 deletions dokka-integration-tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,28 @@ Here's how to update an external project:

4. Check that the corresponding `GradleIntegrationTest` passes locally and push

### Example projects

The [example Gradle projects for DGPv2](../examples/gradle-v2) are automatically tested.

The tests are located in [ExampleProjectsTest.kt](gradle/src/testExampleProjects/kotlin/ExampleProjectsTest.kt).
They validate that the example projects produce the expected HTML data, which is contained in the
[gradle/src/testExampleProjects/expectedData](gradle/src/testExampleProjects/expectedData) directory.

#### Updating expected data

When the Dokka HTML output is updated, the tests will fail because the files in `expectedData`
will not match the actual generated HTML.

When a test fails it will log links to directories containing the actual and expected files
(in IntelliJ the links will be clickable).

To update the expected data:

1. Verify that the new data is valid, and does not contain error messages like "Error class: Unknown class".
2. Delete the 'expected' directory.
3. Copy the actual generated files to the same location.
4. Re-run the test to verify the tests pass.

### Run integration tests with K2 (symbols)

Expand Down
45 changes: 42 additions & 3 deletions dokka-integration-tests/gradle/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ plugins {
id("dokkabuild.kotlin-jvm")
id("dokkabuild.test-integration")
id("dokkabuild.dev-maven-publish")
alias(libs.plugins.kotlinxSerialization)
}

dependencies {
val dokkaVersion = project.version.toString()

api(projects.utilities)

api(libs.jsoup)
Expand All @@ -25,7 +28,14 @@ dependencies {

api(gradleTestKit())

val dokkaVersion = project.version.toString()
api(testFixtures("org.jetbrains.dokka:dokka-gradle-plugin:$dokkaVersion"))
api(platform(libs.kotest.bom))
api(libs.kotest.assertionsCore)
api(libs.kotest.assertionsJson)

implementation(platform(libs.kotlinxSerialization.bom))
implementation(libs.kotlinxSerialization.json)

// We're using Gradle included-builds and dependency substitution, so we
// need to use the Gradle project name, *not* the published Maven artifact-id
devPublication("org.jetbrains.dokka:plugin-all-modules-page:$dokkaVersion")
Expand Down Expand Up @@ -64,6 +74,7 @@ tasks.withType<Test>().configureEach {
systemProperty("hostGradleUserHome", gradle.gradleUserHomeDir.invariantSeparatorsPath)
}


val templateSettingsGradleKts = layout.projectDirectory.file("projects/template.settings.gradle.kts")
val templateProjectsDir = layout.projectDirectory.dir("projects")

Expand Down Expand Up @@ -156,8 +167,6 @@ fun registerTestProjectSuite(
.inputFile("templateSettingsGradleKts", templateSettingsGradleKts)
.withPathSensitivity(NAME_ONLY)

devMavenPublish.configureTask(this)

if (jvm != null) {
javaLauncher = javaToolchains.launcherFor { languageVersion = jvm }
}
Expand All @@ -178,3 +187,33 @@ val checkoutKotlinxSerialization by tasks.registering(GitCheckoutTask::class) {
commitId = "ed1b05707ec27f8864c8b42235b299bdb5e0015c"
destination = templateProjectsDir.dir("serialization/kotlinx-serialization")
}

testing {
suites.withType<JvmTestSuite>().configureEach {
targets.configureEach {
testTask.configure {
devMavenPublish.configureTask(this)
}
}
}
val testExampleProjects by suites.registering(JvmTestSuite::class) {
targets.configureEach {
testTask.configure {

val exampleGradleProjectsDir = projectDir.resolve("../../examples/gradle-v2")
systemProperty
.inputDirectory("exampleGradleProjectsDir", exampleGradleProjectsDir)
.withPathSensitivity(RELATIVE)

val expectedDataDir = layout.projectDirectory.dir("src/testExampleProjects/expectedData")
systemProperty
.inputDirectory("expectedDataDir", expectedDataDir)
.withPathSensitivity(RELATIVE)

// Disable parallel on CI. TeamCity OOMs when the tests are run in parallel.
systemProperty.inputProperty("junit.jupiter.execution.parallel.enabled", dokkaBuild.isCI.map { !it })
systemProperty("junit.jupiter.execution.parallel.mode.default", "CONCURRENT")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package org.jetbrains.dokka.it.gradle

import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.jetbrains.dokka.gradle.utils.GradleProjectTest
import java.nio.file.Path
import kotlin.io.path.isRegularFile
import kotlin.io.path.readText
import kotlin.io.path.walk

/**
* Gradle creates an HTML report for each Configuration Cache result.
* This report contains JSON data that we can parse to get fine-grained details about the
* Configuration Cache result.
*
* Only the first CC report will be parsed, so make sure to clean the CC report directory
* before running any Gradle tasks to ensure only one report is found.
*/
fun GradleProjectTest.loadConfigurationCacheReportData(): ConfigurationCacheReportData? {
val ccReportFile = projectDir.resolve("build/reports/configuration-cache")
.walk()
.filter { it.isRegularFile() }
.singleOrNull()
?: return null

return parseCCReportData(ccReportFile)
}

@Serializable
data class ConfigurationCacheReportData(
val diagnostics: List<DiagnosticsItem>,
val totalProblemCount: Int,
val buildName: String,
val requestedTasks: String,
val cacheAction: String,
val documentationLink: String,
) {

@Serializable
data class DiagnosticsItem(
val trace: List<Trace>,
val input: List<Input>,
val documentationLink: String? = null,
)

@Serializable
data class Trace(
val kind: String,
val type: String? = null,
val location: String? = null,
)

@Serializable
data class Input(
val text: String? = null,
val name: String? = null,
)
}

/**
* Extract and parse the JSON data from [ccReport].
*/
private fun parseCCReportData(ccReport: Path): ConfigurationCacheReportData {
val reportData = ccReport.readText()
.substringAfter("// begin-report-data", "could not find 'begin-report-data'")
.substringBefore("// end-report-data", "could not find 'end-report-data'")

return Json.decodeFromString<ConfigurationCacheReportData>(reportData)
}

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

Loading

0 comments on commit af05202

Please sign in to comment.