diff options
author | sebastian.sellmair <sebastian.sellmair@jetbrains.com> | 2020-08-24 14:02:07 +0200 |
---|---|---|
committer | Paweł Marks <Kordyjan@users.noreply.github.com> | 2020-08-25 16:21:32 +0200 |
commit | 7196323582dce7ca3f9b07262a1f94ecd8514539 (patch) | |
tree | 0c326aee84d4727d5cd5bc5c8198b992d5b58de6 /test-tools/src/main | |
parent | 219e2c98f5d03fc8581fd6ce9dd870919523be44 (diff) | |
download | dokka-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.kt | 120 | ||||
-rw-r--r-- | test-tools/src/main/kotlin/matchers/content/contentMatchers.kt | 183 |
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 |