aboutsummaryrefslogtreecommitdiff
path: root/test-tools/src/main
diff options
context:
space:
mode:
authorsebastian.sellmair <sebastian.sellmair@jetbrains.com>2020-08-24 14:02:07 +0200
committerPaweł Marks <Kordyjan@users.noreply.github.com>2020-08-25 16:21:32 +0200
commit7196323582dce7ca3f9b07262a1f94ecd8514539 (patch)
tree0c326aee84d4727d5cd5bc5c8198b992d5b58de6 /test-tools/src/main
parent219e2c98f5d03fc8581fd6ce9dd870919523be44 (diff)
downloaddokka-7196323582dce7ca3f9b07262a1f94ecd8514539.tar.gz
dokka-7196323582dce7ca3f9b07262a1f94ecd8514539.tar.bz2
dokka-7196323582dce7ca3f9b07262a1f94ecd8514539.zip
- Move `test` projects into semantic parent projects
- Implement new `:test-utils` project - Resolve requirement for Android SDK installation
Diffstat (limited to 'test-tools/src/main')
-rw-r--r--test-tools/src/main/kotlin/matchers/content/ContentMatchersDsl.kt120
-rw-r--r--test-tools/src/main/kotlin/matchers/content/contentMatchers.kt183
2 files changed, 0 insertions, 303 deletions
diff --git a/test-tools/src/main/kotlin/matchers/content/ContentMatchersDsl.kt b/test-tools/src/main/kotlin/matchers/content/ContentMatchersDsl.kt
deleted file mode 100644
index 67c0e692..00000000
--- a/test-tools/src/main/kotlin/matchers/content/ContentMatchersDsl.kt
+++ /dev/null
@@ -1,120 +0,0 @@
-package matchers.content
-
-import assertk.assertThat
-import assertk.assertions.contains
-import assertk.assertions.isEqualTo
-import assertk.assertions.matches
-import org.jetbrains.dokka.model.withDescendants
-import org.jetbrains.dokka.pages.*
-import org.jetbrains.dokka.test.tools.matchers.content.*
-import kotlin.reflect.KClass
-
-// entry point:
-fun ContentNode.assertNode(block: ContentMatcherBuilder<ContentComposite>.() -> Unit) {
- val matcher = ContentMatcherBuilder(ContentComposite::class).apply(block).build()
- try {
- matcher.tryMatch(this)
- } catch (e: MatcherError) {
- throw AssertionError(e.message + "\n" + matcher.toDebugString(e.anchor, e.anchorAfter))
- }
-}
-
-
-// DSL:
-@DslMarker
-annotation class ContentMatchersDsl
-
-@ContentMatchersDsl
-class ContentMatcherBuilder<T : ContentComposite> @PublishedApi internal constructor(private val kclass: KClass<T>) {
- @PublishedApi
- internal val children = mutableListOf<MatcherElement>()
- internal val assertions = mutableListOf<T.() -> Unit>()
-
- fun build() = CompositeMatcher(kclass, childrenOrSkip()) { assertions.forEach { it() } }
-
- // part of DSL that cannot be defined as an extension
- operator fun String.unaryPlus() {
- children += TextMatcher(this)
- }
-
- private fun childrenOrSkip() = if (children.isEmpty() && assertions.isNotEmpty()) listOf(Anything) else children
-}
-
-fun <T : ContentComposite> ContentMatcherBuilder<T>.check(assertion: T.() -> Unit) {
- assertions += assertion
-}
-
-private val ContentComposite.extractedText
- get() = withDescendants().filterIsInstance<ContentText>().joinToString(separator = "") { it.text }
-
-fun <T : ContentComposite> ContentMatcherBuilder<T>.hasExactText(expected: String) {
- assertions += {
- assertThat(this::extractedText).isEqualTo(expected)
- }
-}
-
-fun <T : ContentComposite> ContentMatcherBuilder<T>.textMatches(pattern: Regex) {
- assertions += {
- assertThat(this::extractedText).matches(pattern)
- }
-}
-
-inline fun <reified S : ContentComposite> ContentMatcherBuilder<*>.composite(
- block: ContentMatcherBuilder<S>.() -> Unit
-) {
- children += ContentMatcherBuilder(S::class).apply(block).build()
-}
-
-inline fun <reified S : ContentNode> ContentMatcherBuilder<*>.node(noinline assertions: S.() -> Unit = {}) {
- children += NodeMatcher(S::class, assertions)
-}
-
-fun ContentMatcherBuilder<*>.skipAllNotMatching() {
- children += Anything
-}
-
-
-// Convenience functions:
-fun ContentMatcherBuilder<*>.group(block: ContentMatcherBuilder<ContentGroup>.() -> Unit) = composite(block)
-
-fun ContentMatcherBuilder<*>.header(expectedLevel: Int? = null, block: ContentMatcherBuilder<ContentHeader>.() -> Unit) =
- composite<ContentHeader> {
- block()
- check { if (expectedLevel != null) assertThat(this::level).isEqualTo(expectedLevel) }
- }
-
-fun ContentMatcherBuilder<*>.p(block: ContentMatcherBuilder<ContentGroup>.() -> Unit) =
- composite<ContentGroup> {
- block()
- check { assertThat(this::style).contains(TextStyle.Paragraph) }
- }
-
-fun ContentMatcherBuilder<*>.link(block: ContentMatcherBuilder<ContentLink>.() -> Unit) = composite(block)
-
-fun ContentMatcherBuilder<*>.table(block: ContentMatcherBuilder<ContentTable>.() -> Unit) = composite(block)
-
-fun ContentMatcherBuilder<*>.platformHinted(block: ContentMatcherBuilder<ContentGroup>.() -> Unit) =
- composite<PlatformHintedContent> { group(block) }
-
-fun ContentMatcherBuilder<*>.br() = node<ContentBreakLine>()
-
-fun ContentMatcherBuilder<*>.somewhere(block: ContentMatcherBuilder<*>.() -> Unit) {
- skipAllNotMatching()
- block()
- skipAllNotMatching()
-}
-
-fun ContentMatcherBuilder<*>.divergentGroup(block: ContentMatcherBuilder<ContentDivergentGroup>.() -> Unit) =
- composite(block)
-
-fun ContentMatcherBuilder<ContentDivergentGroup>.divergentInstance(block: ContentMatcherBuilder<ContentDivergentInstance>.() -> Unit) =
- composite(block)
-
-fun ContentMatcherBuilder<ContentDivergentInstance>.before(block: ContentMatcherBuilder<ContentComposite>.() -> Unit) =
- composite(block)
-
-fun ContentMatcherBuilder<ContentDivergentInstance>.divergent(block: ContentMatcherBuilder<ContentComposite>.() -> Unit) =
- composite(block)
-
-fun ContentMatcherBuilder<ContentDivergentInstance>.after(block: ContentMatcherBuilder<ContentComposite>.() -> Unit) =
- composite(block) \ No newline at end of file
diff --git a/test-tools/src/main/kotlin/matchers/content/contentMatchers.kt b/test-tools/src/main/kotlin/matchers/content/contentMatchers.kt
deleted file mode 100644
index f42e28f3..00000000
--- a/test-tools/src/main/kotlin/matchers/content/contentMatchers.kt
+++ /dev/null
@@ -1,183 +0,0 @@
-package org.jetbrains.dokka.test.tools.matchers.content
-
-import org.jetbrains.dokka.model.asPrintableTree
-import org.jetbrains.dokka.pages.ContentComposite
-import org.jetbrains.dokka.pages.ContentNode
-import org.jetbrains.dokka.pages.ContentText
-import kotlin.reflect.KClass
-import kotlin.reflect.full.cast
-import kotlin.reflect.full.safeCast
-
-sealed class MatcherElement
-
-class TextMatcher(val text: String) : MatcherElement()
-
-open class NodeMatcher<T : ContentNode>(
- val kclass: KClass<T>,
- val assertions: T.() -> Unit = {}
-) : MatcherElement() {
- open fun tryMatch(node: ContentNode) {
- kclass.safeCast(node)?.apply {
- try {
- assertions()
- } catch (e: AssertionError) {
- throw MatcherError(
- "${e.message.orEmpty()}\nin node:\n${node.debugRepresentation()}",
- this@NodeMatcher,
- cause = e
- )
- }
- } ?: throw MatcherError("Expected ${kclass.simpleName} but got:\n${node.debugRepresentation()}", this)
- }
-}
-
-class CompositeMatcher<T : ContentComposite>(
- kclass: KClass<T>,
- private val children: List<MatcherElement>,
- assertions: T.() -> Unit = {}
-) : NodeMatcher<T>(kclass, assertions) {
- internal val normalizedChildren: List<MatcherElement> by lazy {
- children.fold(listOf<MatcherElement>()) { acc, e ->
- when {
- acc.lastOrNull() is Anything && e is Anything -> acc
- acc.lastOrNull() is TextMatcher && e is TextMatcher ->
- acc.dropLast(1) + TextMatcher((acc.lastOrNull() as TextMatcher).text + e.text)
- else -> acc + e
- }
- }
- }
-
- override fun tryMatch(node: ContentNode) {
- super.tryMatch(node)
- kclass.cast(node).children.asSequence()
- .filter { it !is ContentText || it.text.isNotBlank() }
- .fold(FurtherSiblings(normalizedChildren, this).pop()) { acc, n -> acc.next(n) }.finish()
- }
-}
-
-object Anything : MatcherElement()
-
-private sealed class MatchWalkerState {
- abstract fun next(node: ContentNode): MatchWalkerState
- abstract fun finish()
-}
-
-private class TextMatcherState(
- val text: String,
- val rest: FurtherSiblings,
- val anchor: TextMatcher
-) : MatchWalkerState() {
- override fun next(node: ContentNode): MatchWalkerState {
- node as? ContentText ?: throw MatcherError("Expected text: \"$text\" but got\n${node.debugRepresentation()}", anchor)
- val trimmed = node.text.trim()
- return when {
- text == trimmed -> rest.pop()
- text.startsWith(trimmed) -> TextMatcherState(text.removePrefix(node.text).trim(), rest, anchor)
- else -> throw MatcherError("Expected text: \"$text\", but got: \"${node.text}\"", anchor)
- }
- }
-
- override fun finish() = throw MatcherError("\"$text\" was not found" + rest.messageEnd, anchor)
-}
-
-private class EmptyMatcherState(val parent: CompositeMatcher<*>) : MatchWalkerState() {
- override fun next(node: ContentNode): MatchWalkerState {
- throw MatcherError("Unexpected node:\n${node.debugRepresentation()}", parent, anchorAfter = true)
- }
-
- override fun finish() = Unit
-}
-
-private class NodeMatcherState(
- val matcher: NodeMatcher<*>,
- val rest: FurtherSiblings
-) : MatchWalkerState() {
- override fun next(node: ContentNode): MatchWalkerState {
- matcher.tryMatch(node)
- return rest.pop()
- }
-
- override fun finish() =
- throw MatcherError("Content of type ${matcher.kclass} was not found" + rest.messageEnd, matcher)
-}
-
-private class SkippingMatcherState(
- val innerState: MatchWalkerState
-) : MatchWalkerState() {
- override fun next(node: ContentNode): MatchWalkerState = runCatching { innerState.next(node) }.getOrElse { this }
-
- override fun finish() = innerState.finish()
-}
-
-private class FurtherSiblings(val list: List<MatcherElement>, val parent: CompositeMatcher<*>) {
- fun pop(): MatchWalkerState = when (val head = list.firstOrNull()) {
- is TextMatcher -> TextMatcherState(head.text.trim(), drop(), head)
- is NodeMatcher<*> -> NodeMatcherState(head, drop())
- is Anything -> SkippingMatcherState(drop().pop())
- null -> EmptyMatcherState(parent)
- }
-
- fun drop() = FurtherSiblings(list.drop(1), parent)
-
- val messageEnd: String
- get() = list.filter { it !is Anything }
- .count().takeIf { it > 0 }
- ?.let { " and $it further matchers were not satisfied" } ?: ""
-}
-
-
-internal fun MatcherElement.toDebugString(anchor: MatcherElement?, anchorAfter: Boolean): String {
- fun Appendable.append(element: MatcherElement, ownPrefix: String, childPrefix: String) {
- if (anchor != null) {
- if (element != anchor || anchorAfter) append(" ".repeat(4))
- else append("--> ")
- }
-
- append(ownPrefix)
- when (element) {
- is Anything -> append("skipAllNotMatching\n")
- is TextMatcher -> append("\"${element.text}\"\n")
- is CompositeMatcher<*> -> {
- append("${element.kclass.simpleName.toString()}\n")
- if (element.normalizedChildren.isNotEmpty()) {
- val newOwnPrefix = childPrefix + '\u251c' + '\u2500' + ' '
- val lastOwnPrefix = childPrefix + '\u2514' + '\u2500' + ' '
- val newChildPrefix = childPrefix + '\u2502' + ' ' + ' '
- val lastChildPrefix = childPrefix + ' ' + ' ' + ' '
- element.normalizedChildren.forEachIndexed { n, e ->
- if (n != element.normalizedChildren.lastIndex) append(e, newOwnPrefix, newChildPrefix)
- else append(e, lastOwnPrefix, lastChildPrefix)
- }
- }
- if (element == anchor && anchorAfter) {
- append("--> $childPrefix\n")
- }
- }
- is NodeMatcher<*> -> append("${element.kclass.simpleName}\n")
- }
- }
-
- return buildString { append(this@toDebugString, "", "") }
-}
-
-private fun ContentNode.debugRepresentation() = asPrintableTree { element ->
- append(if (element is ContentText) """"${element.text}"""" else element::class.simpleName)
- append(
- " { " +
- "kind=${element.dci.kind}, " +
- "dri=${element.dci.dri}, " +
- "style=${element.style}, " +
- "sourceSets=${element.sourceSets} " +
- "}"
- )
-}
-
-data class MatcherError(
- override val message: String,
- val anchor: MatcherElement,
- val anchorAfter: Boolean = false,
- override val cause: Throwable? = null
-) : AssertionError(message, cause)
-
-// Creating this whole mechanism was most scala-like experience I had since I stopped using scala.
-// I don't know how I should feel about it. \ No newline at end of file