├── .github
└── workflows
│ └── gradle-wrapper-validation.yml
├── .gitignore
├── .java-version
├── .travis.yml
├── LICENSE
├── README.md
├── b
├── build.gradle
├── docs
├── describe.md
├── dokka
│ ├── hamkrest
│ │ ├── com.natpryce.hamkrest.assertion
│ │ │ ├── assert-that.html
│ │ │ └── index.html
│ │ ├── com.natpryce.hamkrest
│ │ │ ├── -case-sensitivity
│ │ │ │ ├── -case-insensitive
│ │ │ │ │ └── index.html
│ │ │ │ ├── -case-sensitive
│ │ │ │ │ └── index.html
│ │ │ │ └── index.html
│ │ │ ├── -match-result
│ │ │ │ ├── -match
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── to-string.html
│ │ │ │ ├── -mismatch
│ │ │ │ │ ├── -mismatch.html
│ │ │ │ │ ├── description.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── to-string.html
│ │ │ │ └── index.html
│ │ │ ├── -matcher
│ │ │ │ ├── -companion
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── invoke.html
│ │ │ │ ├── -conjunction
│ │ │ │ │ ├── -conjunction.html
│ │ │ │ │ ├── description.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── invoke.html
│ │ │ │ ├── -disjunction
│ │ │ │ │ ├── -disjunction.html
│ │ │ │ │ ├── description.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── invoke.html
│ │ │ │ ├── -negation
│ │ │ │ │ ├── -negation.html
│ │ │ │ │ ├── description.html
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── invoke.html
│ │ │ │ │ ├── negated-description.html
│ │ │ │ │ └── not.html
│ │ │ │ ├── -primitive
│ │ │ │ │ ├── -primitive.html
│ │ │ │ │ └── index.html
│ │ │ │ ├── as-predicate.html
│ │ │ │ ├── description.html
│ │ │ │ ├── index.html
│ │ │ │ ├── invoke.html
│ │ │ │ ├── negated-description.html
│ │ │ │ └── not.html
│ │ │ ├── -self-describing
│ │ │ │ ├── description.html
│ │ │ │ └── index.html
│ │ │ ├── -string-matcher
│ │ │ │ ├── -companion
│ │ │ │ │ ├── index.html
│ │ │ │ │ └── invoke.html
│ │ │ │ ├── -string-matcher.html
│ │ │ │ └── index.html
│ │ │ ├── -value-description
│ │ │ │ ├── describe.html
│ │ │ │ └── index.html
│ │ │ ├── absent.html
│ │ │ ├── all-elements.html
│ │ │ ├── all-of.html
│ │ │ ├── and.html
│ │ │ ├── any-element.html
│ │ │ ├── any-of.html
│ │ │ ├── anything.html
│ │ │ ├── case-insensitive.html
│ │ │ ├── case-sensitive.html
│ │ │ ├── cast.html
│ │ │ ├── close-to.html
│ │ │ ├── contains-substring.html
│ │ │ ├── contains.html
│ │ │ ├── describe.html
│ │ │ ├── described-by.html
│ │ │ ├── ends-with.html
│ │ │ ├── equal-to-ignoring-case.html
│ │ │ ├── equal-to.html
│ │ │ ├── greater-than-or-equal-to.html
│ │ │ ├── greater-than.html
│ │ │ ├── has-element.html
│ │ │ ├── has-size.html
│ │ │ ├── has.html
│ │ │ ├── identifier-to-words.html
│ │ │ ├── index.html
│ │ │ ├── is-a.html
│ │ │ ├── is-blank.html
│ │ │ ├── is-empty-string.html
│ │ │ ├── is-empty.html
│ │ │ ├── is-in.html
│ │ │ ├── is-null-or-blank.html
│ │ │ ├── is-null-or-empty-string.html
│ │ │ ├── is-within.html
│ │ │ ├── less-than-or-equal-to.html
│ │ │ ├── less-than.html
│ │ │ ├── matches.html
│ │ │ ├── nothing.html
│ │ │ ├── or.html
│ │ │ ├── present.html
│ │ │ ├── same-instance.html
│ │ │ ├── starts-with.html
│ │ │ └── throws.html
│ │ └── package-list
│ ├── images
│ │ ├── anchor-copy-button.svg
│ │ ├── arrow_down.svg
│ │ ├── burger.svg
│ │ ├── copy-icon.svg
│ │ ├── copy-successful-icon.svg
│ │ ├── footer-go-to-link.svg
│ │ ├── go-to-top-icon.svg
│ │ ├── homepage.svg
│ │ ├── logo-icon.svg
│ │ ├── nav-icons
│ │ │ ├── abstract-class-kotlin.svg
│ │ │ ├── abstract-class.svg
│ │ │ ├── annotation-kotlin.svg
│ │ │ ├── annotation.svg
│ │ │ ├── class-kotlin.svg
│ │ │ ├── class.svg
│ │ │ ├── enum-kotlin.svg
│ │ │ ├── enum.svg
│ │ │ ├── exception-class.svg
│ │ │ ├── field-value.svg
│ │ │ ├── field-variable.svg
│ │ │ ├── function.svg
│ │ │ ├── interface-kotlin.svg
│ │ │ ├── interface.svg
│ │ │ ├── object.svg
│ │ │ └── typealias-kotlin.svg
│ │ └── theme-toggle.svg
│ ├── index.html
│ ├── navigation.html
│ ├── scripts
│ │ ├── clipboard.js
│ │ ├── main.js
│ │ ├── navigation-loader.js
│ │ ├── pages.json
│ │ ├── platform-content-handler.js
│ │ ├── prism.js
│ │ ├── sourceset_dependencies.js
│ │ └── symbol-parameters-wrapper_deferred.js
│ └── styles
│ │ ├── font-jb-sans-auto.css
│ │ ├── logo-styles.css
│ │ ├── main.css
│ │ ├── prism.css
│ │ └── style.css
├── index.md
└── matchers
│ ├── equalTo.md
│ └── index.md
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── publish-release
├── settings.gradle
├── src
├── main
│ └── kotlin
│ │ └── com
│ │ └── natpryce
│ │ └── hamkrest
│ │ ├── SelfDescribing.kt
│ │ ├── ValueDescription.kt
│ │ ├── assertion
│ │ └── assert.kt
│ │ ├── collection_matchers.kt
│ │ ├── core_matchers.kt
│ │ ├── describe.kt
│ │ ├── internal.kt
│ │ ├── matching.kt
│ │ ├── number_matchers.kt
│ │ └── string_matchers.kt
└── test
│ ├── kotlin
│ └── com
│ │ └── natpryce
│ │ └── hamkrest
│ │ ├── assertion
│ │ └── assert_tests.kt
│ │ ├── assertions.kt
│ │ ├── collection_matchers_tests.kt
│ │ ├── core_matchers_tests.kt
│ │ ├── describe_tests.kt
│ │ ├── internal_tests.kt
│ │ ├── matching_tests.kt
│ │ ├── number_matchers.kt
│ │ └── string_matchers_tests.kt
│ └── resources
│ └── META-INF
│ └── services
│ └── com.natpryce.hamkrest.ValueDescription
└── tag-release
/.github/workflows/gradle-wrapper-validation.yml:
--------------------------------------------------------------------------------
1 | name: "Validate Gradle Wrapper"
2 | on: [push, pull_request]
3 |
4 | jobs:
5 | validation:
6 | name: "Validation"
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | - uses: gradle/wrapper-validation-action@v1
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | *.iml
3 | .gradle/
4 | build/
5 | out/
6 | *.class
7 |
--------------------------------------------------------------------------------
/.java-version:
--------------------------------------------------------------------------------
1 | 1.8
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
3 | script: ./b clean build
4 |
5 | sudo: false
6 |
7 | jdk:
8 | - openjdk8
9 | - openjdk11
10 |
11 | cache:
12 | directories:
13 | - $HOME/.m2
14 | - $HOME/.gradle
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | HamKrest - Hamcrest for Kotlin
2 | ==============================
3 |
4 | A reimplementation of Hamcrest to take advantage of [Kotlin](https://kotlinlang.org/) language features.
5 |
6 | [](http://kotlinlang.org)
7 | [](https://travis-ci.org/npryce/hamkrest)
8 | [](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.natpryce%22%20AND%20a%3A%22hamkrest%22)
9 |
10 | Note: as of version 1.4.0.0, you must add kotlin-reflect to the classpath to use HamKrest's reflective features.
11 |
12 |
13 | When working in Kotlin, Hamkrest provides these benefits over using the Java Hamcrest library:
14 |
15 | * Kotlin's type system means that developers don't have to worry about getting the variance of generic signatures right. Variance is defined on the abstract Matcher type and Kotlin makes sure composition and subtyping work together the way you expect.
16 |
17 | * Syntactic sugar. You can negate a matcher with the ! operator and compose matchers with infix `and` and `or` functions:
18 |
19 | ``` kotlin
20 | import com.natpryce.hamkrest.assertion.assert
21 |
22 | ...
23 |
24 | assertThat("xyzzy", startsWith("x") and endsWith("y") and !containsSubstring("a"))
25 | ```
26 |
27 | * Easier to extend. You can convert named unary predicates into matchers.
28 |
29 | ``` kotlin
30 | val isBlank = Matcher(String::isBlank)
31 |
32 | assertThat(input, isBlank)
33 | ```
34 |
35 | As a shortcut, you can pass named functions to the `assertThat`, `and`, `or` and many other functions that take a matcher.
36 |
37 | ``` kotlin
38 | assertThat(input, String::isBlank)
39 | ```
40 |
41 | You can also convert a named binary predicate and the second argument to a matcher for first argument, which works well for extension methods.
42 |
43 | ``` kotlin
44 | fun String.hasLength(n: Int): Boolean = this.length == n
45 |
46 | val isTheRightLength = Matcher(String::hasLength, 8)
47 |
48 | assertThat(secretCode, isTheRightLength)
49 | ```
50 |
51 | You can use function and property references to match features of a value:
52 |
53 | ``` kotlin
54 | val isLongEnough = has(String::length, greaterThan(8))
55 |
56 | assertThat(password, isLongEnough)
57 | ```
58 |
59 | All of these shortcuts produce good, human-readable diagnostics.
60 |
61 | You can customise how diagnostics are generated by creating a project-specific `assert` object.
62 |
63 |
64 | ## More documentation
65 |
66 | [More detailed documentation of specific library features is in the docs/ directory](docs/).
67 |
--------------------------------------------------------------------------------
/b:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 | version=`git describe --always --tags --match '*.*.*.*' --dirty="_patched"`
4 | `dirname $0`/gradlew -P-version=${version} "$@"
5 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "org.jetbrains.kotlin.jvm" version "1.4.10"
3 | id 'maven'
4 | id 'signing'
5 | id("org.jetbrains.dokka") version "1.9.20"
6 | }
7 |
8 | group 'com.natpryce'
9 |
10 | version(hasProperty("-version") ? property("-version") : "SNAPSHOT")
11 | println "building version ${version}"
12 |
13 | repositories {
14 | mavenCentral()
15 | }
16 |
17 | dependencies {
18 | compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
19 | compileOnly "org.jetbrains.kotlin:kotlin-reflect"
20 |
21 | testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
22 | testImplementation "org.jetbrains.kotlin:kotlin-reflect"
23 | testImplementation "org.jetbrains.kotlin:kotlin-test"
24 | testImplementation 'junit:junit:4.12'
25 | }
26 |
27 | jar {
28 | manifest {
29 | attributes 'Implementation-Title': 'hamkrest',
30 | 'Implementation-Vendor': 'com.natpryce',
31 | 'Implementation-Version': version
32 | }
33 | }
34 |
35 | task sourcesJar(type: Jar) {
36 | archiveClassifier = 'sources'
37 | from sourceSets.main.allSource
38 | }
39 |
40 | artifacts {
41 | archives sourcesJar
42 | }
43 |
44 | task javadocJar(type: Jar) {
45 | archiveClassifier = 'javadoc'
46 | from 'build/javadoc'
47 | }
48 |
49 | test {
50 | include 'com/natpryce/hamkrest/**'
51 | scanForTestClasses true
52 | reports {
53 | junitXml.enabled = true
54 | html.enabled = true
55 | }
56 |
57 | beforeTest { desc ->
58 | print "${desc.className.substring("com.natpryce.hamkrest.".length())}: ${desc.name.replace("_", " ")}"
59 | }
60 | afterTest { desc, result ->
61 | println " -> ${result.resultType}"
62 | }
63 | }
64 |
65 | artifacts {
66 | archives sourcesJar, javadocJar
67 | }
68 |
69 | signing {
70 | required { hasProperty("sign") || gradle.taskGraph.hasTask("uploadArchives") }
71 | sign configurations.archives
72 | }
73 |
74 | tasks.dokkaHtml {
75 | outputDirectory.set(layout.projectDirectory.dir("docs/dokka"))
76 | }
77 |
78 | task ossrhAuthentication {
79 | doFirst {
80 | if (!(project.hasProperty('ossrh.username') && project.hasProperty('ossrh.password'))) {
81 | throw new InvalidUserDataException("no OSSRH username and/or password!")
82 | }
83 | }
84 | }
85 |
86 | uploadArchives {
87 | dependsOn ossrhAuthentication
88 |
89 | repositories {
90 | mavenDeployer {
91 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
92 |
93 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
94 | authentication(userName: project.properties["ossrh.username"],
95 | password: project.properties["ossrh.password"])
96 | }
97 |
98 | snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") {
99 | authentication(userName: project.properties["ossrh.username"],
100 | password: project.properties["ossrh.password"])
101 | }
102 |
103 | pom.project {
104 | name 'HamKrest'
105 | packaging 'jar'
106 | description 'Matcher library for Kotlin. A port of Hamcrest, made more Kotlin-esque.'
107 | url 'https://github.com/npryce/hamkrest'
108 |
109 | scm {
110 | connection 'git@github.com:npryce/hamkrest.git'
111 | url 'https://github.com/npryce/hamkrest'
112 | }
113 |
114 | licenses {
115 | license {
116 | name 'Apache 2.0'
117 | url 'http://opensource.org/licenses/Apache-2.0'
118 | }
119 | }
120 |
121 | developers {
122 | developer {
123 | id 'npryce'
124 | name 'Nat Pryce'
125 | }
126 | developer {
127 | id 'dmcg'
128 | name 'Duncan McGregor'
129 | }
130 | }
131 | }
132 | }
133 | }
134 | }
135 |
136 |
137 |
--------------------------------------------------------------------------------
/docs/describe.md:
--------------------------------------------------------------------------------
1 | # Extending how Hamkrest Describes Values
2 |
3 | Hamkrest provides a function, called [describe][], that generates readable representations of values for inclusion in
4 | assertion failure messages and the descriptions of matchers.
5 |
6 | For example:
7 |
8 | ```
9 | describe(10)
10 | >>> 10
11 |
12 | describe(listOf("a","b","c"))
13 | >>> ["a", "b", "c"]
14 |
15 | describe(null)
16 | >>> null
17 | ```
18 |
19 | The `describe` function generates reasonable representations of primitive types, null, Strings, Pairs,
20 | Triples and Collections, and falls back to calling `toString` when it has no specialised representation for
21 | a value.
22 |
23 | If the built-in representation is not suitable for your types, you can plug new representations into the `describe` function by registering a [JVM service][] of type [com.natpryce.hamkrest.ValueDescription][].
24 |
25 | The ValueDescription service interface implements a single method, also called `describe`, which maps a value either to
26 | a String representation, or to `null` if the service cannot generate a representation for the value. When a
27 | ValueDescription service returns `null`, Hamkrest will pass the value to other registered ValueDescription services and,
28 | if no services can describe the value, generate a description using the default rules described above.
29 |
30 | **Tip**: You can define ValueDescription services in each module of your project, meaning they can use internal features
31 | of your modules to generate representations.
32 |
33 | [describe]: https://github.com/npryce/hamkrest/blob/master/src/main/kotlin/com/natpryce/hamkrest/describe.kt
34 | [JVM service]: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ServiceLoader.html
35 | [com.natpryce.hamkrest.ValueDescription]: https://github.com/npryce/hamkrest/blob/master/src/main/kotlin/com/natpryce/hamkrest/ValueDescription.kt
36 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-case-sensitivity/-case-insensitive/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CaseInsensitive
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
CaseInsensitive
68 |
69 |
70 |
74 |
75 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-case-sensitivity/-case-sensitive/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CaseSensitive
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
CaseSensitive
68 |
69 |
70 |
74 |
75 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-match-result/-match/to-string.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | toString
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
toString
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-match-result/-mismatch/to-string.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | toString
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
toString
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-matcher/-conjunction/description.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | description
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
description
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-matcher/-disjunction/description.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | description
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
description
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-matcher/-negation/-negation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Negation
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
Negation
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-matcher/-negation/description.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | description
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
description
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-matcher/-negation/negated-description.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | negatedDescription
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
negatedDescription
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-matcher/-negation/not.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | not
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
not
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-matcher/-primitive/-primitive.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Primitive
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
Primitive
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-matcher/as-predicate.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | asPredicate
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
asPredicate
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-matcher/description.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | description
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
description
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-matcher/invoke.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | invoke
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
invoke
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-matcher/negated-description.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | negatedDescription
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
negatedDescription
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-matcher/not.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | not
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
not
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-self-describing/description.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | description
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
description
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/-string-matcher/-string-matcher.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | StringMatcher
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
StringMatcher
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/absent.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | absent
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
absent
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/anything.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | anything
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
anything
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/identifier-to-words.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | identifierToWords
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
identifierToWords
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/is-blank.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | isBlank
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
isBlank
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/is-empty-string.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | isEmptyString
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
isEmptyString
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/is-empty.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | isEmpty
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
isEmpty
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/is-null-or-blank.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | isNullOrBlank
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
isNullOrBlank
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/is-null-or-empty-string.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | isNullOrEmptyString
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
isNullOrEmptyString
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/nothing.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | nothing
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
nothing
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/hamkrest/com.natpryce.hamkrest/same-instance.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | sameInstance
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
sameInstance
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/docs/dokka/images/anchor-copy-button.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/docs/dokka/images/arrow_down.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/docs/dokka/images/burger.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
10 |
--------------------------------------------------------------------------------
/docs/dokka/images/copy-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/docs/dokka/images/copy-successful-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/docs/dokka/images/footer-go-to-link.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/docs/dokka/images/go-to-top-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/docs/dokka/images/homepage.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/dokka/images/logo-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
15 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/abstract-class-kotlin.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
27 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/abstract-class.svg:
--------------------------------------------------------------------------------
1 |
2 |
21 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/annotation-kotlin.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/annotation.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/class-kotlin.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/class.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/enum-kotlin.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/enum.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/exception-class.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/field-value.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
11 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/field-variable.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
11 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/function.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/interface-kotlin.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/interface.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/object.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/docs/dokka/images/nav-icons/typealias-kotlin.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/docs/dokka/images/theme-toggle.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/docs/dokka/scripts/clipboard.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 | */
4 |
5 | window.addEventListener('load', () => {
6 | document.querySelectorAll('span.copy-icon').forEach(element => {
7 | element.addEventListener('click', (el) => copyElementsContentToClipboard(element));
8 | })
9 |
10 | document.querySelectorAll('span.anchor-icon').forEach(element => {
11 | element.addEventListener('click', (el) => {
12 | if(element.hasAttribute('pointing-to')){
13 | const location = hrefWithoutCurrentlyUsedAnchor() + '#' + element.getAttribute('pointing-to')
14 | copyTextToClipboard(element, location)
15 | }
16 | });
17 | })
18 | })
19 |
20 | const copyElementsContentToClipboard = (element) => {
21 | const selection = window.getSelection();
22 | const range = document.createRange();
23 | range.selectNodeContents(element.parentNode.parentNode);
24 | selection.removeAllRanges();
25 | selection.addRange(range);
26 |
27 | copyAndShowPopup(element, () => selection.removeAllRanges())
28 | }
29 |
30 | const copyTextToClipboard = (element, text) => {
31 | var textarea = document.createElement("textarea");
32 | textarea.textContent = text;
33 | textarea.style.position = "fixed";
34 | document.body.appendChild(textarea);
35 | textarea.select();
36 |
37 | copyAndShowPopup(element, () => document.body.removeChild(textarea))
38 | }
39 |
40 | const copyAndShowPopup = (element, after) => {
41 | try {
42 | document.execCommand('copy');
43 | element.nextElementSibling.classList.add('active-popup');
44 | setTimeout(() => {
45 | element.nextElementSibling.classList.remove('active-popup');
46 | }, 1200);
47 | } catch (e) {
48 | console.error('Failed to write to clipboard:', e)
49 | }
50 | finally {
51 | if(after) after()
52 | }
53 | }
54 |
55 | const hrefWithoutCurrentlyUsedAnchor = () => window.location.href.split('#')[0]
56 |
57 |
--------------------------------------------------------------------------------
/docs/dokka/scripts/navigation-loader.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 | */
4 |
5 | navigationPageText = fetch(pathToRoot + "navigation.html").then(response => response.text())
6 |
7 | displayNavigationFromPage = () => {
8 | navigationPageText.then(data => {
9 | document.getElementById("sideMenu").innerHTML = data;
10 | }).then(() => {
11 | document.querySelectorAll(".overview > a").forEach(link => {
12 | link.setAttribute("href", pathToRoot + link.getAttribute("href"));
13 | })
14 | }).then(() => {
15 | document.querySelectorAll(".sideMenuPart").forEach(nav => {
16 | if (!nav.classList.contains("hidden"))
17 | nav.classList.add("hidden")
18 | })
19 | }).then(() => {
20 | revealNavigationForCurrentPage()
21 | }).then(() => {
22 | scrollNavigationToSelectedElement()
23 | })
24 | document.querySelectorAll('.footer a[href^="#"]').forEach(anchor => {
25 | anchor.addEventListener('click', function (e) {
26 | e.preventDefault();
27 | document.querySelector(this.getAttribute('href')).scrollIntoView({
28 | behavior: 'smooth'
29 | });
30 | });
31 | });
32 | }
33 |
34 | revealNavigationForCurrentPage = () => {
35 | let pageId = document.getElementById("content").attributes["pageIds"].value.toString();
36 | let parts = document.querySelectorAll(".sideMenuPart");
37 | let found = 0;
38 | do {
39 | parts.forEach(part => {
40 | if (part.attributes['pageId'].value.indexOf(pageId) !== -1 && found === 0) {
41 | found = 1;
42 | if (part.classList.contains("hidden")) {
43 | part.classList.remove("hidden");
44 | part.setAttribute('data-active', "");
45 | }
46 | revealParents(part)
47 | }
48 | });
49 | pageId = pageId.substring(0, pageId.lastIndexOf("/"))
50 | } while (pageId.indexOf("/") !== -1 && found === 0)
51 | };
52 | revealParents = (part) => {
53 | if (part.classList.contains("sideMenuPart")) {
54 | if (part.classList.contains("hidden"))
55 | part.classList.remove("hidden");
56 | revealParents(part.parentNode)
57 | }
58 | };
59 |
60 | scrollNavigationToSelectedElement = () => {
61 | let selectedElement = document.querySelector('div.sideMenuPart[data-active]')
62 | if (selectedElement == null) { // nothing selected, probably just the main page opened
63 | return
64 | }
65 |
66 | let hasIcon = selectedElement.querySelectorAll(":scope > div.overview span.nav-icon").length > 0
67 |
68 | // for instance enums also have children and are expandable, but are not package/module elements
69 | let isPackageElement = selectedElement.children.length > 1 && !hasIcon
70 | if (isPackageElement) {
71 | // if package is selected or linked, it makes sense to align it to top
72 | // so that you can see all the members it contains
73 | selectedElement.scrollIntoView(true)
74 | } else {
75 | // if a member within a package is linked, it makes sense to center it since it,
76 | // this should make it easier to look at surrounding members
77 | selectedElement.scrollIntoView({
78 | behavior: 'auto',
79 | block: 'center',
80 | inline: 'center'
81 | })
82 | }
83 | }
84 |
85 | /*
86 | This is a work-around for safari being IE of our times.
87 | It doesn't fire a DOMContentLoaded, presumabely because eventListener is added after it wants to do it
88 | */
89 | if (document.readyState == 'loading') {
90 | window.addEventListener('DOMContentLoaded', () => {
91 | displayNavigationFromPage()
92 | })
93 | } else {
94 | displayNavigationFromPage()
95 | }
96 |
--------------------------------------------------------------------------------
/docs/dokka/scripts/sourceset_dependencies.js:
--------------------------------------------------------------------------------
1 | sourceset_dependencies='{":dokkaHtml/main":[]}'
2 |
--------------------------------------------------------------------------------
/docs/dokka/scripts/symbol-parameters-wrapper_deferred.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 | */
4 |
5 | // helps with some corner cases where starts working already,
6 | // but the signature is not yet long enough to be wrapped
7 | (function() {
8 | const leftPaddingPx = 60;
9 |
10 | function createNbspIndent() {
11 | let indent = document.createElement("span");
12 | indent.append(document.createTextNode("\u00A0\u00A0\u00A0\u00A0"));
13 | indent.classList.add("nbsp-indent");
14 | return indent;
15 | }
16 |
17 | function wrapSymbolParameters(entry) {
18 | const symbol = entry.target;
19 | const symbolBlockWidth = entry.borderBoxSize && entry.borderBoxSize[0] && entry.borderBoxSize[0].inlineSize;
20 |
21 | // Even though the script is marked as `defer` and we wait for `DOMContentLoaded` event,
22 | // or if this block is a part of hidden tab, it can happen that `symbolBlockWidth` is 0,
23 | // indicating that something hasn't been loaded.
24 | // In this case, observer will be triggered onсe again when it will be ready.
25 | if (symbolBlockWidth > 0) {
26 | const node = symbol.querySelector(".parameters");
27 |
28 | if (node) {
29 | // if window resize happened and observer was triggered, reset previously wrapped
30 | // parameters as they might not need wrapping anymore, and check again
31 | node.classList.remove("wrapped");
32 | node.querySelectorAll(".parameter .nbsp-indent")
33 | .forEach(indent => indent.remove());
34 |
35 | const innerTextWidth = Array.from(symbol.children)
36 | .filter(it => !it.classList.contains("block")) // blocks are usually on their own (like annotations), so ignore it
37 | .map(it => it.getBoundingClientRect().width)
38 | .reduce((a, b) => a + b, 0);
39 |
40 | // if signature text takes up more than a single line, wrap params for readability
41 | if (innerTextWidth > (symbolBlockWidth - leftPaddingPx)) {
42 | node.classList.add("wrapped");
43 | node.querySelectorAll(".parameter").forEach(param => {
44 | // has to be a physical indent so that it can be copied. styles like
45 | // paddings and `::before { content: " " }` do not work for that
46 | param.prepend(createNbspIndent());
47 | });
48 | }
49 | }
50 | }
51 | }
52 |
53 | const symbolsObserver = new ResizeObserver(entries => entries.forEach(wrapSymbolParameters));
54 |
55 | function initHandlers() {
56 | document.querySelectorAll("div.symbol").forEach(symbol => symbolsObserver.observe(symbol));
57 | }
58 |
59 | if (document.readyState === 'loading') window.addEventListener('DOMContentLoaded', initHandlers);
60 | else initHandlers();
61 |
62 | // ToDo: Add `unobserve` if dokka will be SPA-like:
63 | // https://github.com/w3c/csswg-drafts/issues/5155
64 | })();
65 |
--------------------------------------------------------------------------------
/docs/dokka/styles/font-jb-sans-auto.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 | */
4 |
5 | /* Light weight */
6 | @font-face {
7 | font-family: 'JetBrains Sans';
8 | src: url('https://resources.jetbrains.com/storage/jetbrains-sans/JetBrainsSans-Light.woff2') format('woff2'), url('https://resources.jetbrains.com/storage/jetbrains-sans/JetBrainsSans-Light.woff') format('woff');
9 | font-weight: 300;
10 | font-style: normal;
11 | }
12 | /* Regular weight */
13 | @font-face {
14 | font-family: 'JetBrains Sans';
15 | src: url('https://resources.jetbrains.com/storage/jetbrains-sans/JetBrainsSans-Regular.woff2') format('woff2'), url('https://resources.jetbrains.com/storage/jetbrains-sans/JetBrainsSans-Regular.woff') format('woff');
16 | font-weight: 400;
17 | font-style: normal;
18 | }
19 | /* SemiBold weight */
20 | @font-face {
21 | font-family: 'JetBrains Sans';
22 | src: url('https://resources.jetbrains.com/storage/jetbrains-sans/JetBrainsSans-SemiBold.woff2') format('woff2'), url('https://resources.jetbrains.com/storage/jetbrains-sans/JetBrainsSans-SemiBold.woff') format('woff');
23 | font-weight: 600;
24 | font-style: normal;
25 | }
26 |
27 | @supports (font-variation-settings: normal) {
28 | @font-face {
29 | font-family: 'JetBrains Sans';
30 | src: url('https://resources.jetbrains.com/storage/jetbrains-sans/JetBrainsSans.woff2') format('woff2 supports variations'),
31 | url('https://resources.jetbrains.com/storage/jetbrains-sans/JetBrainsSans.woff2') format('woff2-variations'),
32 | url('https://resources.jetbrains.com/storage/jetbrains-sans/JetBrainsSans.woff') format('woff-variations');
33 | font-weight: 100 900;
34 | font-style: normal;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/docs/dokka/styles/logo-styles.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 | */
4 |
5 | :root {
6 | --dokka-logo-image-url: url('../images/logo-icon.svg');
7 | --dokka-logo-height: 50px;
8 | --dokka-logo-width: 50px;
9 | }
10 |
--------------------------------------------------------------------------------
/docs/dokka/styles/prism.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 | */
4 |
5 | /*
6 | * Custom Dokka styles
7 | */
8 | code .token {
9 | white-space: pre;
10 | }
11 |
12 | /**
13 | * Styles based on webhelp's prism.js styles
14 | * Changes:
15 | * - Since webhelp's styles are in .pcss, they use nesting which is not achievable in native CSS
16 | * so nested css blocks have been unrolled (like dark theme).
17 | * - Webhelp uses "Custom Class" prism.js plugin, so all of their prism classes are prefixed with "--prism".
18 | * Dokka doesn't seem to need this plugin at the moment, so all "--prism" prefixes have been removed.
19 | * - Removed all styles related to `pre` and `code` tags. Kotlinlang's resulting styles are so spread out and complicated
20 | * that it's difficult to gather in one place. Instead use code styles defined in the main Dokka styles,
21 | * which at the moment looks fairly similar.
22 | *
23 | * Based on prism.js default theme
24 | * Based on dabblet (http://dabblet.com)
25 | * @author Lea Verou
26 | */
27 |
28 | .token.comment,
29 | .token.prolog,
30 | .token.doctype,
31 | .token.cdata {
32 | color: #8c8c8c;
33 | }
34 |
35 | .token.punctuation {
36 | color: #999;
37 | }
38 |
39 | .token.namespace {
40 | opacity: 0.7;
41 | }
42 |
43 | .token.property,
44 | .token.tag,
45 | .token.boolean,
46 | .token.number,
47 | .token.constant,
48 | .token.symbol,
49 | .token.deleted {
50 | color: #871094;
51 | }
52 |
53 | .token.selector,
54 | .token.attr-name,
55 | .token.string,
56 | .token.char,
57 | .token.builtin,
58 | .token.inserted {
59 | color: #067d17;
60 | }
61 |
62 | .token.operator,
63 | .token.entity,
64 | .token.url,
65 | .language-css .token.string,
66 | .style .token.string {
67 | color: #9a6e3a;
68 | /* This background color was intended by the author of this theme. */
69 | background: hsla(0, 0%, 100%, 0.5);
70 | }
71 |
72 | .token.atrule,
73 | .token.attr-value,
74 | .token.keyword {
75 | font-size: inherit; /* to override .keyword */
76 | color: #0033b3;
77 | }
78 |
79 | .token.function {
80 | color: #00627a;
81 | }
82 |
83 | .token.class-name {
84 | color: #000000;
85 | }
86 |
87 | .token.regex,
88 | .token.important,
89 | .token.variable {
90 | color: #871094;
91 | }
92 |
93 | .token.important,
94 | .token.bold {
95 | font-weight: bold;
96 | }
97 | .token.italic {
98 | font-style: italic;
99 | }
100 |
101 | .token.entity {
102 | cursor: help;
103 | }
104 |
105 | .token.operator {
106 | background: none;
107 | }
108 |
109 | /*
110 | * DARK THEME
111 | */
112 | :root.theme-dark .token.comment,
113 | :root.theme-dark .token.prolog,
114 | :root.theme-dark .token.cdata {
115 | color: #808080;
116 | }
117 |
118 | :root.theme-dark .token.delimiter,
119 | :root.theme-dark .token.boolean,
120 | :root.theme-dark .token.keyword,
121 | :root.theme-dark .token.selector,
122 | :root.theme-dark .token.important,
123 | :root.theme-dark .token.atrule {
124 | color: #cc7832;
125 | }
126 |
127 | :root.theme-dark .token.operator,
128 | :root.theme-dark .token.punctuation,
129 | :root.theme-dark .token.attr-name {
130 | color: #a9b7c6;
131 | }
132 |
133 | :root.theme-dark .token.tag,
134 | :root.theme-dark .token.tag .punctuation,
135 | :root.theme-dark .token.doctype,
136 | :root.theme-dark .token.builtin {
137 | color: #e8bf6a;
138 | }
139 |
140 | :root.theme-dark .token.entity,
141 | :root.theme-dark .token.number,
142 | :root.theme-dark .token.symbol {
143 | color: #6897bb;
144 | }
145 |
146 | :root.theme-dark .token.property,
147 | :root.theme-dark .token.constant,
148 | :root.theme-dark .token.variable {
149 | color: #9876aa;
150 | }
151 |
152 | :root.theme-dark .token.string,
153 | :root.theme-dark .token.char {
154 | color: #6a8759;
155 | }
156 |
157 | :root.theme-dark .token.attr-value,
158 | :root.theme-dark .token.attr-value .punctuation {
159 | color: #a5c261;
160 | }
161 |
162 | :root.theme-dark .token.attr-value .punctuation:first-child {
163 | color: #a9b7c6;
164 | }
165 |
166 | :root.theme-dark .token.url {
167 | text-decoration: underline;
168 |
169 | color: #287bde;
170 | background: transparent;
171 | }
172 |
173 | :root.theme-dark .token.function {
174 | color: #ffc66d;
175 | }
176 |
177 | :root.theme-dark .token.regex {
178 | background: #364135;
179 | }
180 |
181 | :root.theme-dark .token.deleted {
182 | background: #484a4a;
183 | }
184 |
185 | :root.theme-dark .token.inserted {
186 | background: #294436;
187 | }
188 |
189 | :root.theme-dark .token.class-name {
190 | color: #a9b7c6;
191 | }
192 |
193 | :root.theme-dark .token.function {
194 | color: #ffc66d;
195 | }
196 |
197 | :root.theme-darkcode .language-css .token.property,
198 | :root.theme-darkcode .language-css,
199 | :root.theme-dark .token.property + .token.punctuation {
200 | color: #a9b7c6;
201 | }
202 |
203 | code.language-css .token.id {
204 | color: #ffc66d;
205 | }
206 |
207 | :root.theme-dark code.language-css .token.selector > .token.class,
208 | :root.theme-dark code.language-css .token.selector > .token.attribute,
209 | :root.theme-dark code.language-css .token.selector > .token.pseudo-class,
210 | :root.theme-dark code.language-css .token.selector > .token.pseudo-element {
211 | color: #ffc66d;
212 | }
213 |
214 | :root.theme-dark .language-plaintext .token {
215 | /* plaintext code should be colored as article text */
216 | color: inherit !important;
217 | }
218 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Hamkrest
2 |
3 | A reimplementation of [Hamcrest](https://hamcrest.org/) to take advantage of Kotlin language features.
4 |
5 | - [Built In Matchers with Examples (wip)](matchers/)
6 |
7 | - [Dokka Documentation](dokka/)
--------------------------------------------------------------------------------
/docs/matchers/equalTo.md:
--------------------------------------------------------------------------------
1 | # [`equalTo`](https://github.com/npryce/hamkrest/blob/9ce6f5882203b97130d1e85fa10818cbce1b7693/src/main/kotlin/com/natpryce/hamkrest/core_matchers.kt#L18-L27)
2 |
3 | A simple matcher that asserts equality. Works with `null` just like Kotlin's `==` operator.
4 |
5 | ### Assertion
6 |
7 | ```
8 | assertThat("one", equalTo("two"))
9 | ```
10 |
11 | ### Test Output
12 |
13 | ```
14 | expected: a value that is equal to "two"
15 | but was: "one"
16 | ```
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/docs/matchers/index.md:
--------------------------------------------------------------------------------
1 | # Matcher Index
2 |
3 | ## [`equalTo`](equalTo.html)
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/npryce/hamkrest/0cc6e6df36b72fd9788e4127c28777a89d310437/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto init
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto init
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :init
68 | @rem Get command-line arguments, handling Windows variants
69 |
70 | if not "%OS%" == "Windows_NT" goto win9xME_args
71 |
72 | :win9xME_args
73 | @rem Slurp the command line arguments.
74 | set CMD_LINE_ARGS=
75 | set _SKIP=2
76 |
77 | :win9xME_args_slurp
78 | if "x%~1" == "x" goto execute
79 |
80 | set CMD_LINE_ARGS=%*
81 |
82 | :execute
83 | @rem Setup the command line
84 |
85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
86 |
87 |
88 | @rem Execute Gradle
89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
90 |
91 | :end
92 | @rem End local scope for the variables with windows NT shell
93 | if "%ERRORLEVEL%"=="0" goto mainEnd
94 |
95 | :fail
96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
97 | rem the _cmd.exe /c_ return code!
98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
99 | exit /b 1
100 |
101 | :mainEnd
102 | if "%OS%"=="Windows_NT" endlocal
103 |
104 | :omega
105 |
--------------------------------------------------------------------------------
/publish-release:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 | `dirname $0`/b clean uploadArchives "$@"
4 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'hamkrest'
2 |
3 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/natpryce/hamkrest/SelfDescribing.kt:
--------------------------------------------------------------------------------
1 | package com.natpryce.hamkrest
2 |
3 | /**
4 | * An object that can describe itself.
5 | */
6 | interface SelfDescribing {
7 | /**
8 | * The description of this object
9 | */
10 | val description: String
11 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/natpryce/hamkrest/ValueDescription.kt:
--------------------------------------------------------------------------------
1 | package com.natpryce.hamkrest
2 |
3 | /**
4 | * A service interface for extensions to the [describe] function.
5 | */
6 | interface ValueDescription {
7 | /**
8 | * Describes the value [v] or returns `null` to indicate that this service cannot describe the value, in which
9 | * case, other registered services are tried. If no services can describe the value, the [defaultDescription]
10 | * function is called.
11 | */
12 | fun describe(v: Any?): String?
13 | }
--------------------------------------------------------------------------------
/src/main/kotlin/com/natpryce/hamkrest/assertion/assert.kt:
--------------------------------------------------------------------------------
1 | package com.natpryce.hamkrest.assertion
2 |
3 | import com.natpryce.hamkrest.MatchResult
4 | import com.natpryce.hamkrest.Matcher
5 | import com.natpryce.hamkrest.describe
6 | import kotlin.reflect.KFunction1
7 | import kotlin.reflect.KFunction2
8 |
9 | private fun noMessage() = ""
10 |
11 | /**
12 | * Asserts that [criteria] matches [actual].
13 | * On failure, the diagnostic is prefixed with the result of calling [message].
14 | * @throws AssertionError if there is a mismatch
15 | */
16 | fun assertThat(actual: T, criteria: Matcher, message: () -> String = ::noMessage) {
17 | val judgement = criteria(actual)
18 | if (judgement is MatchResult.Mismatch) {
19 | throw AssertionError(
20 | message().let { if (it.isEmpty()) it else "$it: " } +
21 | "expected: a value that ${describe(criteria)}\n" +
22 | "but ${describe(judgement)}")
23 | }
24 | }
25 |
26 | /**
27 | * Asserts that [criteria] matches [actual].
28 | * On failure, the diagnostic is prefixed with the result of calling [message].
29 | * @throws AssertionError if there is a mismatch
30 | */
31 | fun assertThat(actual: T, criteria: KFunction1, message: () -> String = ::noMessage) {
32 | assertThat(actual, Matcher(criteria), message)
33 | }
34 |
35 | /**
36 | * Asserts that [criteria]([actual], [other]) returns true.
37 | * On failure, the diagnostic is prefixed with the result of calling [message].
38 | * @throws AssertionError if there is a mismatch
39 | */
40 |
41 | fun assertThat(actual: T, criteria: KFunction2, other: U, message: () -> String = ::noMessage) {
42 | assertThat(actual, Matcher(criteria, other), message)
43 | }
44 |
45 |
46 | /**
47 | * Asserts that [criteria] matches [actual]. On failure, the diagnostic is prefixed with [message].
48 | * @throws AssertionError if there is a mismatch
49 | */
50 | fun assertThat(message: String, actual: T, criteria: Matcher) {
51 | assertThat(actual, criteria, { message })
52 | }
53 |
54 | /**
55 | * Asserts that [criteria] returns true for [actual]. On failure, the diagnostic is prefixed with [message].
56 | * @throws AssertionError if there is a mismatch
57 | */
58 | fun assertThat(message: String, actual: T, criteria: KFunction1) {
59 | assertThat(actual, criteria, { message })
60 | }
61 |
62 | /**
63 | * Asserts that [criteria]([actual], [other]) returns true. On failure, the diagnostic is prefixed with [message].
64 | * @throws AssertionError if there is a mismatch
65 | */
66 | fun assertThat(message: String, actual: T, criteria: KFunction2, other: U) {
67 | assertThat(actual, criteria, other, { message })
68 | }
69 |
70 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/natpryce/hamkrest/collection_matchers.kt:
--------------------------------------------------------------------------------
1 | package com.natpryce.hamkrest
2 |
3 | import kotlin.reflect.KFunction1
4 |
5 | /**
6 | * Matches an [Iterable] if any element is matched by [elementMatcher].
7 | */
8 | fun anyElement(elementMatcher: Matcher) = object : Matcher.Primitive>() {
9 | override fun invoke(actual: Iterable): MatchResult =
10 | match(actual.any(elementMatcher.asPredicate())) { "was ${describe(actual)}" }
11 |
12 | override val description: String get() = "in which any element ${describe(elementMatcher)}"
13 | override val negatedDescription: String get() = "in which no element ${describe(elementMatcher)}"
14 | }
15 |
16 | /**
17 | * Matches an [Iterable] if any element is matched by [elementPredicate].
18 | */
19 | fun anyElement(elementPredicate: KFunction1) = anyElement(Matcher(elementPredicate))
20 |
21 | /**
22 | * Matches an [Iterable] if all elements are matched by [elementMatcher].
23 | */
24 | fun allElements(elementMatcher: Matcher) = object : Matcher.Primitive>() {
25 | override fun invoke(actual: Iterable): MatchResult =
26 | match(actual.all(elementMatcher.asPredicate())) { "was ${describe(actual)}" }
27 |
28 | override val description: String get() = "in which all elements ${describe(elementMatcher)}"
29 | override val negatedDescription: String get() = "in which not all elements ${describe(elementMatcher)}"
30 | }
31 |
32 | /**
33 | * Matches an [Iterable] if all elements are matched by [elementPredicate].
34 | */
35 | fun allElements(elementPredicate: KFunction1) = allElements(Matcher(elementPredicate))
36 |
37 | /**
38 | * Matches an empty collection.
39 | */
40 | val isEmpty = Matcher(Collection::isEmpty)
41 |
42 | /**
43 | * Matches a collection with a size that matches [sizeMatcher].
44 | */
45 | fun hasSize(sizeMatcher: Matcher) = has(Collection::size, sizeMatcher)
46 |
47 | /**
48 | * Matches a collection that contains [element]
49 | *
50 | * See [Collection::contains]
51 | */
52 | fun hasElement(element: T): Matcher> = object : Matcher.Primitive>() {
53 | override fun invoke(actual: Collection): MatchResult =
54 | match(element in actual) { "was ${describe(actual)}" }
55 | override val description: String get() = "contains ${describe(element)}"
56 | override val negatedDescription: String get() = "does not contain ${describe(element)}"
57 | }
58 |
59 | fun isIn(i: Iterable) : Matcher = object : Matcher.Primitive() {
60 | override fun invoke(actual: T) = match(actual in i) {"was not in ${describe(i)}"}
61 | override val description: String get() = "is in ${describe(i)}"
62 | override val negatedDescription: String get() = "is not in ${describe(i)}"
63 | }
64 |
65 | fun isIn(vararg elements: T) = isIn(elements.toList())
66 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/natpryce/hamkrest/describe.kt:
--------------------------------------------------------------------------------
1 | package com.natpryce.hamkrest
2 |
3 | import java.util.ServiceLoader
4 |
5 | /**
6 | * Formats [v] to be included in a description. Strings are delimited with quotes and elements of tuples, ranges,
7 | * iterable collections and maps are (recursively) described. A null reference is described as `null`.
8 | * For anything else, the result of [Any.toString] is used.
9 | *
10 | * @param v the value to be described.
11 | */
12 | fun describe(v: Any?): String =
13 | ServiceLoader.load(ValueDescription::class.java).asSequence()
14 | .mapNotNull { it.describe(v) }
15 | .firstOrNull() ?: defaultDescription(v)
16 |
17 |
18 | /**
19 | * The default description of a value, used when a value is not described by any registered [ValueDescription] services.
20 | */
21 | private fun defaultDescription(v: Any?): String = when (v) {
22 | null -> "null"
23 | is SelfDescribing -> v.description
24 | is String -> "\"" + v.replace("\\", "\\\\").replace("\"", "\\\"") + "\""
25 | is Pair<*, *> -> Pair(describe(v.first), describe(v.second)).toString()
26 | is Triple<*, *, *> -> Triple(describe(v.first), describe(v.second), describe(v.third)).toString()
27 | is ClosedRange<*> -> "${describe(v.start)}..${describe(v.endInclusive)}"
28 | is Set<*> -> v.map(::describe).joinToString(prefix = "{", separator = ", ", postfix = "}")
29 | is Collection<*> -> v.map(::describe).joinToString(prefix = "[", separator = ", ", postfix = "]")
30 | is Map<*, *> -> v.entries.map { "${describe(it.key)}:${describe(it.value)}" }.joinToString(prefix = "{", separator = ", ", postfix = "}")
31 | else -> v.toString()
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/natpryce/hamkrest/internal.kt:
--------------------------------------------------------------------------------
1 | package com.natpryce.hamkrest
2 |
3 | import java.util.ArrayList
4 |
5 | internal fun match(comparisonResult: Boolean, describeMismatch: () -> String): MatchResult =
6 | if (comparisonResult) {
7 | MatchResult.Match
8 | } else {
9 | MatchResult.Mismatch(describeMismatch())
10 | }
11 |
12 | internal fun identifierToDescription(id: String) = identifierToWords(id).joinToString(" ")
13 |
14 | internal fun identifierToNegatedDescription(id: String): String {
15 | val words = identifierToWords(id)
16 | val first = words[0]
17 | val rest = words.drop(1).joinToString(" ")
18 |
19 | return when (first) {
20 | "is" -> "is not ${rest}"
21 | "has" -> "does not have ${rest}"
22 | else -> "not ${first} ${rest}"
23 | }
24 | }
25 |
26 | fun identifierToWords(s: String): List {
27 | val words: MutableList = ArrayList()
28 | val buf = StringBuilder()
29 |
30 | for ((prev, c) in (s[0] + s).zip(s)) {
31 | if (isWordStart(prev, c)) {
32 | if (buf.isNotEmpty()) {
33 | words.add(buf.toString())
34 | buf.setLength(0)
35 | }
36 | }
37 |
38 | if (isWordPart(c)) {
39 | buf.append(c.toLowerCase())
40 | }
41 | }
42 |
43 | words.add(buf.toString())
44 |
45 | return words
46 | }
47 |
48 | internal fun isWordPart(c: Char): Boolean = c.isLetterOrDigit()
49 |
50 | internal fun isWordStart(prev: Char, c: Char): Boolean = when {
51 | c.isLetter() != prev.isLetter() -> true
52 | prev.isLowerCase() && c.isUpperCase() -> true
53 | else -> false
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/kotlin/com/natpryce/hamkrest/number_matchers.kt:
--------------------------------------------------------------------------------
1 | package com.natpryce.hamkrest
2 |
3 | /**
4 | * Returns a matcher that reports if a value is equal to an [expected] value, withing a range of +/- [error].
5 | */
6 | fun closeTo(expected: Float, error: Float = 0.00001f): Matcher = _closeTo(expected, error)
7 |
8 | /**
9 | * Returns a matcher that reports if a value is equal to an [expected] value, withing a range of +/- [error].
10 | */
11 | fun closeTo(expected: Double, error: Double = 0.00001): Matcher = _closeTo(expected, error)
12 |
13 | private fun _closeTo(expected: T, error: T): Matcher =
14 | object : Matcher {
15 | override fun invoke(actual: T): MatchResult = match(delta(actual, expected, error) <= 0.0) {
16 | "a numeric value ${describe(actual)} differed by ${describe(delta(actual, expected, error))} more than error ${describe(error)}"
17 | }
18 | override val description: String get() = "is equal to ${describe(expected)} within ${describe(error)}"
19 | override val negatedDescription: String get() = "is not equal to ${describe(expected)} within ${describe(error)}"
20 |
21 | private fun delta(actual: T, expected: T, error: T) = Math.abs(actual.toDouble() - expected.toDouble()) - error.toDouble()
22 | }
--------------------------------------------------------------------------------
/src/test/kotlin/com/natpryce/hamkrest/assertion/assert_tests.kt:
--------------------------------------------------------------------------------
1 | package com.natpryce.hamkrest.assertion
2 |
3 | import com.natpryce.hamkrest.equalTo
4 | import com.natpryce.hamkrest.matches
5 | import org.junit.Test
6 | import kotlin.text.RegexOption.DOT_MATCHES_ALL
7 | import kotlin.text.RegexOption.MULTILINE
8 |
9 | class AssertOutput {
10 | @Test
11 | fun produces_ide_friendly_error_message() {
12 | try {
13 | assertThat("foo", equalTo("bar"))
14 | } catch (e: AssertionError) {
15 | assertThat(e.message!!, matches(Regex("expected: .*but was: .*", setOf(MULTILINE, DOT_MATCHES_ALL))))
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/natpryce/hamkrest/assertions.kt:
--------------------------------------------------------------------------------
1 | package com.natpryce.hamkrest
2 |
3 | import kotlin.test.assertEquals
4 | import kotlin.test.fail
5 |
6 | fun assertMismatchWithDescription(expectedDescription: String, m: MatchResult) {
7 | when (m) {
8 | MatchResult.Match -> fail("unexpected match")
9 | is MatchResult.Mismatch -> {
10 | assertEquals(expectedDescription, m.description)
11 | }
12 | }
13 | }
14 |
15 | fun assertMismatch(m: MatchResult) {
16 | if (m == MatchResult.Match) fail("unexpected match")
17 | }
18 |
19 | fun assertMatch(result: MatchResult) {
20 | assertEquals(MatchResult.Match, result)
21 | }
--------------------------------------------------------------------------------
/src/test/kotlin/com/natpryce/hamkrest/collection_matchers_tests.kt:
--------------------------------------------------------------------------------
1 | package com.natpryce.hamkrest
2 |
3 | import com.natpryce.hamkrest.assertion.assertThat
4 | import org.junit.Test
5 |
6 |
7 | class Contains {
8 | @Test
9 | fun contains_any() {
10 | assertThat(listOf(1, 2, 3, 4), anyElement(equalTo(3)))
11 | assertThat(listOf(1, 2, 3, 4), anyElement(greaterThanOrEqualTo(4)))
12 | assertThat(emptyList(), !anyElement(anything))
13 | }
14 |
15 | @Test
16 | fun contains_element() {
17 | assertThat(listOf(1, 2, 3, 4), hasElement(1))
18 | assertThat(listOf(1, 2, 3, 4), hasElement(2))
19 | assertThat(listOf(1, 2, 3, 4), !hasElement(0))
20 | }
21 |
22 | @Test
23 | fun empty_sequence_never_meets_any_elements() {
24 | assertThat(emptyList(), !anyElement(anything))
25 | }
26 |
27 | @Test
28 | fun any_elements_can_be_passed_a_function_reference() {
29 | assertThat(listOf("1", "2", " "), anyElement(String::isBlank))
30 | }
31 |
32 | @Test
33 | fun all_elements() {
34 | assertThat(listOf(1), allElements(equalTo(1)))
35 | assertThat(listOf(1, 2, 3, 4), allElements(greaterThan(0)))
36 | assertThat(listOf(1, 2, 3, 4), !allElements(equalTo(1)))
37 | }
38 |
39 | @Test
40 | fun empty_sequence_always_meets_all_elements() {
41 | assertThat(emptyList(), allElements(anything))
42 | }
43 |
44 | @Test
45 | fun all_elements_can_be_passed_a_function_reference() {
46 | assertThat(listOf("1", "2", " "), !allElements(String::isBlank))
47 | }
48 | }
49 |
50 | class IsIn {
51 | @Test
52 | fun is_in_collection() {
53 | assertThat(1, isIn(listOf(1, 2, 3, 4)))
54 | assertThat(2, isIn(listOf(1, 2, 3, 4)))
55 | assertThat(3, isIn(listOf(1, 2, 3, 4)))
56 | assertThat(4, isIn(listOf(1, 2, 3, 4)))
57 |
58 | assertThat(5, !isIn(listOf(1, 2, 3, 4)))
59 |
60 | assertThat(3, !isIn(emptyList()))
61 |
62 | assertThat(1, isIn(setOf(1, 2, 3, 4)))
63 | assertThat(3, isIn(setOf(1, 2, 3, 4)))
64 | }
65 |
66 | @Test
67 | fun is_in_varargs_treated_as_list() {
68 | assertThat(1, isIn(1, 2, 3, 4))
69 | assertThat(2, isIn(1, 2, 3, 4))
70 | }
71 | }
72 |
73 |
74 | class CollectionSize {
75 | @Test
76 | fun size() {
77 | val l = listOf(1,2,3)
78 |
79 | assertThat(l, hasSize(equalTo(3)))
80 | assertThat(l, hasSize(greaterThan(2)))
81 | assertThat(l, !hasSize(lessThan(3)))
82 | }
83 |
84 | @Test
85 | fun size_description() {
86 | assertThat(hasSize(greaterThan(3)).description, equalTo("has size that is greater than 3"))
87 | assertThat((!hasSize(greaterThan(3))).description, equalTo("does not have size that is greater than 3"))
88 | }
89 |
90 | @Test
91 | fun empty() {
92 | assertThat(emptyList(), isEmpty)
93 | assertThat(listOf(1,2,3), !isEmpty)
94 | }
95 |
96 | @Test
97 | fun empty_description() {
98 | assertThat(isEmpty.description, equalTo("is empty"))
99 | assertThat((!isEmpty).description, equalTo("is not empty"))
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/natpryce/hamkrest/describe_tests.kt:
--------------------------------------------------------------------------------
1 | package com.natpryce.hamkrest
2 |
3 | import com.natpryce.hamkrest.assertion.assertThat
4 | import org.junit.Test
5 |
6 | class DelimitingValuesInStrings {
7 | @Test
8 | fun quotes_around_string_values_and_escaped_within() {
9 | assertThat(describe("hello, \"bob\""), equalTo("\"hello, \\\"bob\\\"\""))
10 | }
11 |
12 | @Test
13 | fun pairs() {
14 | assertThat(describe(Pair("x", "y")), equalTo("(\"x\", \"y\")"))
15 | }
16 |
17 | @Test
18 | fun triples() {
19 | assertThat(describe(Triple("x", "y", "z")), equalTo("(\"x\", \"y\", \"z\")"))
20 | }
21 |
22 | @Test
23 | fun iterables() {
24 | assertThat(describe(listOf(1, 2)), equalTo("[1, 2]"))
25 | assertThat(describe(listOf("1", "2")), equalTo("""["1", "2"]"""))
26 | }
27 |
28 | @Test
29 | fun sets() {
30 | assertThat(describe(setOf(1, 2)), equalTo("{1, 2}"))
31 | assertThat(describe(setOf("1", "2")), equalTo("""{"1", "2"}"""))
32 | }
33 |
34 | @Test
35 | fun ranges() {
36 | assertThat(describe(1..8), equalTo("1..8"))
37 | }
38 |
39 | @Test
40 | fun maps() {
41 | assertThat(describe(mapOf("a" to 1, "b" to 2)), equalTo("""{"a":1, "b":2}"""))
42 | }
43 |
44 | @Test
45 | fun self_describing_objects() {
46 | val d = object : SelfDescribing {
47 | override val description = "d"
48 | }
49 |
50 | assertThat(describe(d), equalTo("d"))
51 | }
52 |
53 | class IterableButNotCollection(private vararg val elements: String) : Iterable {
54 | override fun iterator() = elements.iterator()
55 | override fun toString() = elements.joinToString("/")
56 | }
57 |
58 | @Test
59 | fun does_not_generate_collectionlike_description_for_iterables_that_are_not_collections() {
60 | val thing = IterableButNotCollection("foo", "bar", "baz")
61 | assertThat(describe(thing), equalTo("foo/bar/baz"))
62 | }
63 | }
64 |
65 |
66 | class NeedsCustomDescription
67 |
68 | class ExampleDescribeExtension : ValueDescription {
69 | override fun describe(v: Any?) =
70 | when (v) {
71 | is NeedsCustomDescription -> "the-custom-description"
72 | else -> null
73 | }
74 | }
75 |
76 | class DescribeExtensionPointTests {
77 | @Test
78 | fun can_extend_describe_function_with_jvm_services() {
79 | assertThat(describe(NeedsCustomDescription()), equalTo("the-custom-description"))
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/test/kotlin/com/natpryce/hamkrest/internal_tests.kt:
--------------------------------------------------------------------------------
1 | package com.natpryce.hamkrest
2 |
3 | import com.natpryce.hamkrest.assertion.assertThat
4 | import org.junit.Test
5 |
6 | class Utilities {
7 | @Test
8 | fun splits_identifier_into_words() {
9 | val cases = listOf(
10 | "identifier" to listOf("identifier"),
11 | "an_identifier" to listOf("an", "identifier"),
12 | "anIdentifier" to listOf("an", "identifier"),
13 | "farenheit451" to listOf("farenheit", "451"),
14 | "i_got_99_problems" to listOf("i", "got", "99", "problems")
15 | )
16 |
17 | for ((identifier, words) in cases) {
18 | assertThat("${describe(identifier)} to words", identifierToWords(identifier), equalTo(words))
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/test/kotlin/com/natpryce/hamkrest/number_matchers.kt:
--------------------------------------------------------------------------------
1 | package com.natpryce.hamkrest
2 |
3 | import org.junit.Test
4 | import kotlin.test.assertEquals
5 |
6 | class CloseTo {
7 | @Test
8 | fun close() {
9 | assertMatch(closeTo(3.0, 0.1)(3.01))
10 | assertMatch(closeTo(3.0)(3.0))
11 | assertMatch(closeTo(3.0f, 0.1f)(3.01f))
12 | assertMatch(closeTo(3.0f)(3.0f))
13 | }
14 |
15 | @Test
16 | fun not_close() {
17 | assertMismatchWithDescription("a numeric value 4.0 differed by 0.9 more than error 0.1", closeTo(3.0, 0.1)(4.0))
18 | }
19 |
20 | @Test
21 | fun description() {
22 | assertEquals("is equal to 3.0 within 0.1", closeTo(3.0, 0.1).description)
23 | assertEquals("is not equal to 3.0 within 0.01", closeTo(3.0, 0.01).negatedDescription)
24 | assertEquals("is equal to 3.0 within 0.1", closeTo(3.0f, 0.1f).description)
25 | assertEquals("is not equal to 3.0 within 0.01", closeTo(3.0f, 0.01f).negatedDescription)
26 | }
27 | }
--------------------------------------------------------------------------------
/src/test/kotlin/com/natpryce/hamkrest/string_matchers_tests.kt:
--------------------------------------------------------------------------------
1 | package com.natpryce.hamkrest
2 |
3 | import com.natpryce.hamkrest.assertion.assertThat
4 | import org.junit.Test
5 |
6 | class RegexMatching {
7 | val pattern = Regex("a(b*)a")
8 |
9 | @Test
10 | fun entire_string_matches_regex() {
11 | assertThat("abba", matches(pattern))
12 | assertThat("aba", matches(pattern))
13 | assertThat("abbbbbba", matches(pattern))
14 | assertThat("aha!", !matches(pattern))
15 | assertThat("yabba dabba doo", !matches(pattern))
16 | }
17 |
18 | @Test
19 | fun string_contains_regex() {
20 | assertThat("abba", contains(pattern))
21 | assertThat("aba", contains(pattern))
22 | assertThat("abbbbbba", contains(pattern))
23 | assertThat("aha!", !matches(pattern))
24 | assertThat("yabba dabba doo", contains(pattern))
25 | }
26 |
27 | }
28 |
29 | class ContainsSubstring {
30 | @Test
31 | fun contains_substring() {
32 | assertThat("qwerty", containsSubstring("qwe"))
33 | assertThat("qwerty", containsSubstring("wert"))
34 | assertThat("qwerty", containsSubstring("erty"))
35 | }
36 |
37 | @Test
38 | fun contains_substring_can_specify_case_sensitivity() {
39 | assertThat("qwerty", containsSubstring("WERT").caseInsensitive())
40 | assertThat("qwerty", !containsSubstring("WERT").caseInsensitive().caseSensitive())
41 | }
42 |
43 | @Test
44 | fun description() {
45 | assertThat(containsSubstring("foo").description, equalTo("contains substring \"foo\""))
46 | assertThat(containsSubstring("foo").caseInsensitive().description, equalTo("contains substring \"foo\" (case insensitive)"))
47 | }
48 | }
49 |
50 | class StringPrefixAndSuffix {
51 | @Test
52 | fun prefix() {
53 | assertThat("qwerty", startsWith("q"))
54 | assertThat("qwerty", startsWith("qwe"))
55 | assertThat("qwerty", !startsWith("Q"))
56 | assertThat("qwerty", startsWith(""))
57 | }
58 |
59 | @Test
60 | fun prefix_can_specify_case_sensitivity() {
61 | assertThat("qwerty", startsWith("Q").caseInsensitive())
62 | assertThat("qwerty", !startsWith("Q").caseInsensitive().caseSensitive())
63 | }
64 |
65 | @Test
66 | fun suffix() {
67 | assertThat("qwerty", endsWith("y"))
68 | assertThat("qwerty", endsWith("rty"))
69 | assertThat("qwerty", !endsWith("Y"))
70 | assertThat("qwerty", endsWith(""))
71 | }
72 |
73 | @Test
74 | fun suffix_can_specify_case_sensitivity() {
75 | assertThat("qwerty", endsWith("Y").caseInsensitive())
76 | assertThat("qwerty", !endsWith("Y").caseInsensitive().caseSensitive())
77 | }
78 | }
79 |
80 | class NullableString {
81 | @Test
82 | fun null_or_empty_works_on_null_or_empty_string() {
83 | assertThat(null, isNullOrEmptyString)
84 | assertThat("", isNullOrEmptyString)
85 | }
86 |
87 | @Test
88 | fun null_or_blank_works_on_null_or_blank_string() {
89 | assertThat(null, isNullOrBlank)
90 | assertThat("", isNullOrBlank)
91 | assertThat(" ", isNullOrBlank)
92 | }
93 | }
94 |
95 | class CaseInsensitiveEquals {
96 | @Test
97 | fun equal_to_ignoring_case() {
98 | assertThat("qwerty", equalToIgnoringCase("qwerty"))
99 | assertThat("qwerty", equalToIgnoringCase("QWERTY"))
100 | assertThat("QWERTY", equalToIgnoringCase("qwerty"))
101 | assertThat("qwerty", !equalToIgnoringCase(" qwerty"))
102 | assertThat("qwerty", !equalToIgnoringCase(null))
103 | assertThat(null, equalToIgnoringCase(null))
104 | assertThat(null, !equalToIgnoringCase("qwerty"))
105 | }
106 |
107 | @Test
108 | fun description() {
109 | assertThat(equalToIgnoringCase("foo").description, equalToIgnoringCase("""IS EQUAL (IGNORING CASE) TO "foo""""))
110 | assertThat(equalToIgnoringCase(null).description, equalToIgnoringCase("""IS EQUAL (IGNORING CASE) TO NULL"""))
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/test/resources/META-INF/services/com.natpryce.hamkrest.ValueDescription:
--------------------------------------------------------------------------------
1 | com.natpryce.hamkrest.ExampleDescribeExtension
2 |
--------------------------------------------------------------------------------
/tag-release:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 | version=$1
3 |
4 | if [ -z ${version} ]
5 | then
6 | echo "ERROR no tag given; last tag:" `git tag -l | grep -E '[0-9]+(.[0-9]+){3}' | sort -t. -k 1,1nr -k 2,2nr -k 3,3nr -k 4,4nr | head -1`
7 | exit 1
8 | else
9 | git tag -u sw@natpryce.com ${version} -m "tagging version ${version}"
10 | fi
11 |
--------------------------------------------------------------------------------