Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add warning if the html log link 404s
Browse files Browse the repository at this point in the history
- add tests for log link task
- minor tidying
aSemy committed Nov 29, 2023
1 parent 1a73111 commit 9619e4d
Showing 8 changed files with 258 additions and 28 deletions.
9 changes: 9 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -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"
@@ -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 ###

6 changes: 6 additions & 0 deletions modules/dokkatoo-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -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) }
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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 ->
@@ -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)
}
Original file line number Diff line number Diff line change
@@ -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
@@ -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
* ```
@@ -85,17 +92,37 @@ 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")

val statusCode = httpGet(link).statusCode()
if (statusCode !in 200..299) {
logger.warn(
"Warning: $link returned unsuccessful status code $statusCode. " +
"Does the index.html file exist, or is the server misconfigured?"
)
}
}

@@ -110,27 +137,27 @@ 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>
}

override fun obtain(): Boolean {
try {
return try {
val uri = URI.create(parameters.uri.get())
val client = HttpClient.newHttpClient()
val request = HttpRequest
.newBuilder()
.uri(uri)
.timeout(Duration.ofSeconds(1))
.GET()
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
val response = httpGet(uri)

// don't care about the status - only if the server is available
return response.statusCode() > 0
logger.info("got ${response.statusCode()} from $uri")
response.statusCode() > 0
} catch (ex: Exception) {
return false
logger.info("could not reach URI ${parameters.uri.get()}: $ex")
false
}
}
}
@@ -140,8 +167,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
* ```
*
@@ -152,5 +181,16 @@ constructor(
* ```
*/
const val ENABLE_TASK_PROPERTY_NAME = "dev.adamko.dokkatoo.tasks.logHtmlPublicationLinkEnabled"

private fun httpGet(uri: URI): HttpResponse<String> {
val client = HttpClient.newHttpClient()
val request = HttpRequest
.newBuilder()
.uri(uri)
.timeout(Duration.ofSeconds(1))
.GET()
.build()
return client.send(request, HttpResponse.BodyHandlers.ofString())
}
}
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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('\\', '/')
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
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 invalidServerUri = "http://localhost:$serverPort/wrong/path/"
val validServerUriParam = `-P`("testServerUri=$validServerUri")
val invalidServerUriParam = `-P`("testServerUri=$invalidServerUri")

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

context("when generate task is run with incorrect server URI") {
project.runner
.addArguments(
"clean",
"dokkatooGeneratePublicationHtml",
"--stacktrace",
"--info",
invalidServerUriParam,
)
.forwardOutput()
.build {
test("expect project builds successfully") {
output shouldContain "BUILD SUCCESSFUL"
}
test("LogHtmlPublicationLinkTask should run") {
shouldHaveTasksWithAnyOutcome(
":logLinkDokkatooGeneratePublicationHtml" to listOf(SUCCESS)
)
}
test("expect invalid link is logged, with warning") {
output shouldContain "Generated Dokka HTML publication: $invalidServerUri"
output shouldContain "Warning: ${invalidServerUri}log-html-publication-link-task/build/dokka/html/index.html returned unsuccessful status code 404"
output shouldContain "Does the index.html file exist, or is the server misconfigured?"
}
}
}

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 9619e4d

Please sign in to comment.