Skip to content

Commit

Permalink
html log link tests and tidying (#124)
Browse files Browse the repository at this point in the history
- add tests for log link task
- minor tidying
  • Loading branch information
aSemy authored Nov 30, 2023
1 parent 1a73111 commit a1023bf
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 21 deletions.
9 changes: 9 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ kotlin = "1.9.0" # should match Gradle's embedded Kotlin version https://docs.gr
kotlin-dokka = "1.9.0"
kotlinx-serialization = "1.6.0"

ktor = "2.3.6"

kotest = "5.6.2"

gradlePlugin-android = "8.0.2"
Expand All @@ -24,6 +26,13 @@ kotlinxSerialization-bom = { module = "org.jetbrains.kotlinx:kotlinx-serializati
kotlinxSerialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json" }
#kotlinxSerialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }

##region ktor
ktor-bom = { group = "io.ktor", name = "ktor-bom", version.ref = "ktor" }

ktorServer-core = { group = "io.ktor", name = "ktor-server-core" }
ktorServer-cio = { group = "io.ktor", name = "ktor-server-cio" }
##endregion


### Test libraries ###

Expand Down
6 changes: 6 additions & 0 deletions modules/dokkatoo-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ testing.suites {
shouldRunAfter(test)
}
}

dependencies {
implementation(project.dependencies.platform(libs.ktor.bom))
implementation(libs.ktorServer.core)
implementation(libs.ktorServer.cio)
}
}

tasks.check { dependsOn(test, testFunctional) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import dev.adamko.dokkatoo.DokkatooExtension
import dev.adamko.dokkatoo.adapters.DokkatooAndroidAdapter
import dev.adamko.dokkatoo.adapters.DokkatooJavaAdapter
import dev.adamko.dokkatoo.adapters.DokkatooKotlinAdapter
import dev.adamko.dokkatoo.internal.*
import dev.adamko.dokkatoo.internal.DokkatooInternalApi
import javax.inject.Inject
import org.gradle.api.Plugin
import org.gradle.api.Project
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ constructor() : DokkatooFormatPlugin(formatName = "html") {
private fun DokkatooFormatPluginContext.registerLogHtmlUrlTask():
TaskProvider<LogHtmlPublicationLinkTask> {

val indexHtmlFile = dokkatooTasks.generatePublication
val generatePublicationTask = dokkatooTasks.generatePublication

val indexHtmlFile = generatePublicationTask
.flatMap { it.outputDirectory.file("index.html") }

val indexHtmlPath = indexHtmlFile.map { indexHtml ->
Expand All @@ -63,8 +65,10 @@ constructor() : DokkatooFormatPlugin(formatName = "html") {
}

return project.tasks.register<LogHtmlPublicationLinkTask>(
"logLink" + dokkatooTasks.generatePublication.name.uppercaseFirstChar()
"logLink" + generatePublicationTask.name.uppercaseFirstChar()
) {
// default port of IntelliJ built-in server is defined in the docs
// https://www.jetbrains.com/help/idea/settings-debugger.html#24aabda8
serverUri.convention("http://localhost:63342")
this.indexHtmlPath.convention(indexHtmlPath)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.model.ReplacedBy
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,24 @@ import org.gradle.api.tasks.Console
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.*
import org.gradle.work.DisableCachingByDefault
import org.slf4j.LoggerFactory

/**
* Prints an HTTP link in the console when the HTML publication is generated.
*
* The HTML publication requires a web server, since it loads resources via javascript.
*
* By default, it uses
* [IntelliJ's built-in server](https://www.jetbrains.com/help/idea/php-built-in-web-server.html)
* [IntelliJ's built-in server](https://www.jetbrains.com/help/phpstorm/php-built-in-web-server.html#ws_html_preview_output_built_in_browser)†
* to host the file.
*
*
* This task can be disabled using the [ENABLE_TASK_PROPERTY_NAME] project property.
*
* ---
*
* † For some reason there only doc page for the built-in server I could find is for PhpStorm,
* but the built-in server is also available in IntelliJ IDEA.)
*/
@DisableCachingByDefault(because = "logging-only task")
abstract class LogHtmlPublicationLinkTask
Expand Down Expand Up @@ -56,7 +63,7 @@ constructor(
* ```
* /Users/rachel/projects/my-project/docs/build/dokka/html/index.html
* ````
* * then IntelliJ requires the [indexHtmlPath] is
* * then IntelliJ requires [indexHtmlPath] is
* ```
* my-project/docs/build/dokka/html/index.html
* ```
Expand Down Expand Up @@ -85,18 +92,30 @@ constructor(
super.onlyIf("task is enabled via property") {
logHtmlPublicationLinkTaskEnabled.get()
}

super.onlyIf("${::serverUri.name} is present") {
!serverUri.orNull.isNullOrBlank()
}

super.onlyIf("${::indexHtmlPath.name} is present") {
!indexHtmlPath.orNull.isNullOrBlank()
}
}

@TaskAction
fun exec() {
val serverUri = serverUri.orNull
val filePath = indexHtmlPath.orNull
val serverUri = serverUri.get()
val indexHtmlPath = indexHtmlPath.get()

if (serverUri != null && !filePath.isNullOrBlank()) {
val link = URI(serverUri).appendPath(filePath).toString()
logger.info(
"LogHtmlPublicationLinkTask received variables " +
"serverUri:$serverUri, " +
"indexHtmlPath:$indexHtmlPath"
)

logger.lifecycle("Generated Dokka HTML publication: $link")
}
val link = URI(serverUri).appendPath(indexHtmlPath)

logger.lifecycle("Generated Dokka HTML publication: $link")
}

/**
Expand All @@ -110,8 +129,13 @@ constructor(
*/
internal abstract class ServerActiveCheck : ValueSource<Boolean, ServerActiveCheck.Parameters> {

private val logger = LoggerFactory.getLogger(ServerActiveCheck::class.java)

interface Parameters : ValueSourceParameters {
/** E.g. `http://localhost:63342` */
/**
* IntelliJ built-in server's default address is `http://localhost:63342`
* See https://www.jetbrains.com/help/idea/settings-debugger.html
*/
val uri: Property<String>
}

Expand All @@ -126,11 +150,12 @@ constructor(
.GET()
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())

// don't care about the status - only if the server is available
logger.info("got ${response.statusCode()} from $uri")
return response.statusCode() > 0
} catch (ex: Exception) {
return false
logger.info("could not reach URI ${parameters.uri.get()}: $ex")
return false
}
}
}
Expand All @@ -140,8 +165,10 @@ constructor(
* Control whether the [LogHtmlPublicationLinkTask] task is enabled. Useful for disabling the
* task locally, or in CI/CD, or for tests.
*
* It can be set in any `gradle.properties` file. For example, on a specific machine:
*
* ```properties
* #$GRADLE_USER_HOME/gradle.properties
* # $GRADLE_USER_HOME/gradle.properties
* dev.adamko.dokkatoo.tasks.logHtmlPublicationLinkEnabled=false
* ```
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,19 @@ class GradleProjectTest(
baseDir: Path = funcTestTempDir,
) : this(projectDir = baseDir.resolve(testProjectName))

/** Args that will be added to every [runner] */
val defaultRunnerArgs: MutableList<String> = mutableListOf(
// disable the logging task so the tests work consistently on local machines and CI/CD
"-P" + "dev.adamko.dokkatoo.tasks.logHtmlPublicationLinkEnabled=false"
)

val runner: GradleRunner
get() = GradleRunner.create()
.withProjectDir(projectDir.toFile())
.withJvmArguments(
"-XX:MaxMetaspaceSize=512m",
"-XX:+AlwaysPreTouch", // https://github.com/gradle/gradle/issues/3093#issuecomment-387259298
).addArguments(
// disable the logging task so the tests work consistently on local machines and CI/CD
"-P" + "dev.adamko.dokkatoo.tasks.logHtmlPublicationLinkEnabled=false"
)
).addArguments(*defaultRunnerArgs.toTypedArray())

val testMavenRepoRelativePath: String =
projectDir.relativize(testMavenRepoDir).toFile().invariantSeparatorsPath
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class DokkatooPluginFunctionalTest : FunSpec({
}

test("expect Dokka Plugin creates Dokka outgoing variants") {
val build = testProject.runner
testProject.runner
.addArguments("outgoingVariants", "-q")
.build {
val variants = output.invariantNewlines().replace('\\', '/')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package dev.adamko.dokkatoo.tasks

import dev.adamko.dokkatoo.internal.DokkatooConstants
import dev.adamko.dokkatoo.tasks.LogHtmlPublicationLinkTask.Companion.ENABLE_TASK_PROPERTY_NAME
import dev.adamko.dokkatoo.utils.*
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.string.shouldNotContain
import io.ktor.server.cio.CIO
import io.ktor.server.engine.embeddedServer
import org.gradle.testkit.runner.TaskOutcome.SKIPPED
import org.gradle.testkit.runner.TaskOutcome.SUCCESS

class LogHtmlPublicationLinkTaskTest : FunSpec({

context("given an active file-host server") {
val server = embeddedServer(CIO, port = 0) { }
server.start(wait = false)
val serverPort = server.resolvedConnectors().first().port

val validServerUri = "http://localhost:$serverPort"
val validServerUriParam = `-P`("testServerUri=$validServerUri")

context("and a Kotlin project") {
val project = initDokkatooProject()

context("when generate task is run with correct server URI") {
project.runner
.addArguments(
"clean",
"dokkatooGeneratePublicationHtml",
"--stacktrace",
"--info",
validServerUriParam,
)
.forwardOutput()
.build {
test("expect project builds successfully") {
output shouldContain "BUILD SUCCESSFUL"
}
test("LogHtmlPublicationLinkTask should run") {
shouldHaveTasksWithAnyOutcome(
":logLinkDokkatooGeneratePublicationHtml" to listOf(SUCCESS)
)
}
test("expect link is logged") {
output shouldContain "Generated Dokka HTML publication: $validServerUri/log-html-publication-link-task/build/dokka/html/index.html"
}
}
}

context("and the server is down") {
// stop the server immediately
server.stop(gracePeriodMillis = 0, timeoutMillis = 0)

context("when running the generate task") {
project.runner
.addArguments(
"clean",
"dokkatooGeneratePublicationHtml",
"--stacktrace",
"--info",
validServerUriParam,
)
.forwardOutput()
.build {
test("expect project builds successfully") {
output shouldContain "BUILD SUCCESSFUL"
}
test("LogHtmlPublicationLinkTask should be skipped") {
shouldHaveTasksWithAnyOutcome(
":logLinkDokkatooGeneratePublicationHtml" to listOf(SKIPPED)
)
output shouldContain "Skipping task ':logLinkDokkatooGeneratePublicationHtml' as task onlyIf 'server URL is reachable' is false"
}
test("expect link is not logged") {
output shouldNotContain "Generated Dokka HTML publication"
}
}
}
}
}
}
}) {
companion object {
@Suppress("SpellCheckingInspection")
/**
* prefix [param] with `-P`.
*
* (this exists to avoid annoying typo warnings, e.g. `-Pserver=localhost` -> `Typo: In word 'Pserver'`)
*/
private fun `-P`(param: String): String = "-P$param"
}
}


private fun initDokkatooProject(
config: GradleProjectTest.() -> Unit = {},
): GradleProjectTest {
return gradleKtsProjectTest("log-html-publication-link-task") {
buildGradleKts = """
|plugins {
| kotlin("jvm") version "1.8.22"
| id("dev.adamko.dokkatoo") version "${DokkatooConstants.DOKKATOO_VERSION}"
|}
|
|dependencies {
| dokkatooPluginHtml(
| dokkatoo.versions.jetbrainsDokka.map { dokkaVersion ->
| "org.jetbrains.dokka:all-modules-page-plugin:${'$'}dokkaVersion"
| }
| )
|}
|
|tasks.withType<dev.adamko.dokkatoo.tasks.LogHtmlPublicationLinkTask>().configureEach {
| serverUri.set(providers.gradleProperty("testServerUri"))
|}
""".trimMargin()

createKotlinFile(
"src/main/kotlin/Hello.kt",
"""
|package com.project.hello
|
|/** The Hello class */
|class Hello {
| /** prints `Hello` to the console */
| fun sayHello() = println("Hello")
|}
|
""".trimMargin()
)

// remove the flag that disables the logging task, since this test wants the logger to run
defaultRunnerArgs.removeIf { ENABLE_TASK_PROPERTY_NAME in it }

config()
}
}

0 comments on commit a1023bf

Please sign in to comment.