Merge pull request #384 from robstoll/charSequence-and-throwable-for-new-infix-api

add throwable and charSequenceAssertions to the new infix API
This commit is contained in:
Robert Stoll
2020-03-03 22:08:28 +01:00
committed by GitHub
42 changed files with 1955 additions and 58 deletions

View File

@@ -229,6 +229,8 @@ fun <T : CharSequence> Expect<T>.isNotBlank() = addAssertion(ExpectImpl.charSequ
/**
* Expects that the subject of the assertion (a [CharSequence]) matches the given [expected] [Regex].
*
* In contrast to [containsRegex] it does not look for a partial match but for an entire match.
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*
@@ -240,6 +242,8 @@ fun <T : CharSequence> Expect<T>.matches(expected: Regex) =
/**
* Expects that the subject of the assertion (a [CharSequence]) mismatches the given [expected] [Regex].
*
* In contrast to `containsNot.regex` it does not look for a partial match but for an entire match.
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*

View File

@@ -3,7 +3,8 @@ package ch.tutteli.atrium.api.fluent.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.ExpectImpl
import ch.tutteli.atrium.domain.builders.creating.basic.contains.addAssertion
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
import ch.tutteli.atrium.domain.builders.utils.toVarArg
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains.*
import ch.tutteli.atrium.domain.creating.charsequence.contains.searchbehaviours.IgnoringCaseSearchBehaviour
import ch.tutteli.atrium.domain.creating.charsequence.contains.searchbehaviours.NoOpSearchBehaviour
import ch.tutteli.kbox.glue
@@ -26,7 +27,7 @@ import kotlin.jvm.JvmName
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case [expected] is not a [CharSequence], [Number] or [Char].
*/
fun <T : CharSequence> CharSequenceContains.CheckerOption<T, NoOpSearchBehaviour>.value(expected: Any): Expect<T> =
fun <T : CharSequence> CheckerOption<T, NoOpSearchBehaviour>.value(expected: Any): Expect<T> =
values(expected)
/**
@@ -55,7 +56,7 @@ fun <T : CharSequence> CharSequenceContains.CheckerOption<T, NoOpSearchBehaviour
* @throws IllegalArgumentException in case [expected] or one of the [otherExpected] is not a
* [CharSequence], [Number] or [Char].
*/
fun <T : CharSequence> CharSequenceContains.CheckerOption<T, NoOpSearchBehaviour>.values(
fun <T : CharSequence> CheckerOption<T, NoOpSearchBehaviour>.values(
expected: Any,
vararg otherExpected: Any
): Expect<T> = addAssertion(ExpectImpl.charSequence.contains.values(this, expected glue otherExpected))
@@ -79,7 +80,7 @@ fun <T : CharSequence> CharSequenceContains.CheckerOption<T, NoOpSearchBehaviour
* @throws IllegalArgumentException in case [expected] is not a [CharSequence], [Number] or [Char].
*/
@JvmName("valueIgnoringCase")
fun <T : CharSequence> CharSequenceContains.CheckerOption<T, IgnoringCaseSearchBehaviour>.value(
fun <T : CharSequence> CheckerOption<T, IgnoringCaseSearchBehaviour>.value(
expected: Any
): Expect<T> = values(expected)
@@ -110,7 +111,7 @@ fun <T : CharSequence> CharSequenceContains.CheckerOption<T, IgnoringCaseSearchB
* [CharSequence], [Number] or [Char].
*/
@JvmName("valuesIgnoringCase")
fun <T : CharSequence> CharSequenceContains.CheckerOption<T, IgnoringCaseSearchBehaviour>.values(
fun <T : CharSequence> CheckerOption<T, IgnoringCaseSearchBehaviour>.values(
expected: Any,
vararg otherExpected: Any
): Expect<T> = addAssertion(ExpectImpl.charSequence.contains.valuesIgnoringCase(this, expected glue otherExpected))
@@ -120,7 +121,7 @@ fun <T : CharSequence> CharSequenceContains.CheckerOption<T, IgnoringCaseSearchB
* Finishes the specification of the sophisticated `contains` assertion where the [expected] value shall be searched
* (ignoring case), using a non disjoint search where it needs to be contained at least once.
*
* Delegates to `atLeast(1).values(expected)`.
* Delegates to `atLeast(1).value(expected)`.
*
* Notice that a runtime check applies which assures that only [CharSequence], [Number] and [Char] are passed (this
* function expects `Any` for your convenience, so that you can mix [String] and [Int] for instance).
@@ -133,7 +134,7 @@ fun <T : CharSequence> CharSequenceContains.CheckerOption<T, IgnoringCaseSearchB
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case [expected] is not a [CharSequence], [Number] or [Char].
*/
fun <T : CharSequence> CharSequenceContains.Builder<T, IgnoringCaseSearchBehaviour>.value(expected: Any): Expect<T> =
fun <T : CharSequence> Builder<T, IgnoringCaseSearchBehaviour>.value(expected: Any): Expect<T> =
atLeast(1).value(expected)
/**
@@ -159,7 +160,7 @@ fun <T : CharSequence> CharSequenceContains.Builder<T, IgnoringCaseSearchBehavio
* @throws IllegalArgumentException in case [expected] or one of the [otherExpected] is not a
* [CharSequence], [Number] or [Char].
*/
fun <T : CharSequence> CharSequenceContains.Builder<T, IgnoringCaseSearchBehaviour>.values(
fun <T : CharSequence> Builder<T, IgnoringCaseSearchBehaviour>.values(
expected: Any,
vararg otherExpected: Any
): Expect<T> = atLeast(1).values(expected, *otherExpected)
@@ -185,13 +186,13 @@ fun <T : CharSequence> CharSequenceContains.Builder<T, IgnoringCaseSearchBehavio
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
fun <T : CharSequence> CharSequenceContains.CheckerOption<T, NoOpSearchBehaviour>.regex(
fun <T : CharSequence> CheckerOption<T, NoOpSearchBehaviour>.regex(
pattern: String,
vararg otherPatterns: String
): Expect<T> = addAssertion(ExpectImpl.charSequence.contains.regex(this, pattern glue otherPatterns))
/**
* Finishes the specification of the sophisticated `contains` assertion where the given regular expression [pattern]
* Finishes the specification of the sophisticated `contains` assertion where the given [Regex] [pattern]
* as well as the [otherPatterns] are expected to have a match, using a non disjoint search.
*
* By non disjoint is meant that `'aa'` in `'aaaa'` is found three times and not only two times.
@@ -213,7 +214,8 @@ fun <T : CharSequence> CharSequenceContains.CheckerOption<T, NoOpSearchBehaviour
*
* @since 0.9.0
*/
fun <T : CharSequence> CharSequenceContains.CheckerOption<T, NoOpSearchBehaviour>.regex(
//TODO rename to `matchFor` with 1.0.0
fun <T : CharSequence> CheckerOption<T, NoOpSearchBehaviour>.regex(
pattern: Regex,
vararg otherPatterns: Regex
): Expect<T> = addAssertion(ExpectImpl.charSequence.contains.regex(this, pattern glue otherPatterns))
@@ -240,7 +242,7 @@ fun <T : CharSequence> CharSequenceContains.CheckerOption<T, NoOpSearchBehaviour
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
@JvmName("regexIgnoringCase")
fun <T : CharSequence> CharSequenceContains.CheckerOption<T, IgnoringCaseSearchBehaviour>.regex(
fun <T : CharSequence> CheckerOption<T, IgnoringCaseSearchBehaviour>.regex(
pattern: String,
vararg otherPatterns: String
): Expect<T> = addAssertion(ExpectImpl.charSequence.contains.regexIgnoringCase(this, pattern glue otherPatterns))
@@ -269,7 +271,7 @@ fun <T : CharSequence> CharSequenceContains.CheckerOption<T, IgnoringCaseSearchB
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
fun <T : CharSequence> CharSequenceContains.Builder<T, IgnoringCaseSearchBehaviour>.regex(
fun <T : CharSequence> Builder<T, IgnoringCaseSearchBehaviour>.regex(
pattern: String,
vararg otherPatterns: String
): Expect<T> = atLeast(1).regex(pattern, *otherPatterns)
@@ -292,13 +294,14 @@ fun <T : CharSequence> CharSequenceContains.Builder<T, IgnoringCaseSearchBehavio
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case [expectedIterable] is not a [CharSequence], [Number] or [Char] or the given
* [expectedIterable] does not have elements (is empty).
*
* @since 0.9.0
*/
fun <T : CharSequence> CharSequenceContains.CheckerOption<T, NoOpSearchBehaviour>.elementsOf(
fun <T : CharSequence> CheckerOption<T, NoOpSearchBehaviour>.elementsOf(
expectedIterable: Iterable<Any>
): Expect<T> {
require(expectedIterable.iterator().hasNext()) { "Iterable without elements are not allowed." }
return values(expectedIterable.first(), *expectedIterable.drop(1).toTypedArray())
val (first, rest) = toVarArg(expectedIterable)
return values(first, *rest)
}
/**
@@ -319,12 +322,13 @@ fun <T : CharSequence> CharSequenceContains.CheckerOption<T, NoOpSearchBehaviour
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case [expectedIterable] is not a [CharSequence], [Number] or [Char] or the given
* [expectedIterable] does not have elements (is empty).
*
* @since 0.9.0
*/
@JvmName("elementsOfIgnoringCase")
fun <T : CharSequence> CharSequenceContains.CheckerOption<T, IgnoringCaseSearchBehaviour>.elementsOf(
fun <T : CharSequence> CheckerOption<T, IgnoringCaseSearchBehaviour>.elementsOf(
expectedIterable: Iterable<Any>
): Expect<T> {
require(expectedIterable.iterator().hasNext()) { "Iterable without elements are not allowed." }
return values(expectedIterable.first(), *expectedIterable.drop(1).toTypedArray())
val (first, rest) = toVarArg(expectedIterable)
return values(first, *rest)
}

View File

@@ -3,6 +3,7 @@ package ch.tutteli.atrium.api.fluent.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.ExpectImpl
import ch.tutteli.atrium.domain.builders.creating.basic.contains.addAssertion
import ch.tutteli.atrium.domain.builders.utils.toVarArg
import ch.tutteli.atrium.domain.creating.iterable.contains.IterableContains
import ch.tutteli.atrium.domain.creating.iterable.contains.searchbehaviours.InAnyOrderSearchBehaviour
import ch.tutteli.kbox.glue
@@ -107,6 +108,6 @@ fun <E : Any, T : Iterable<E?>> IterableContains.CheckerOption<E?, T, InAnyOrder
inline fun <reified E, T : Iterable<E>> IterableContains.CheckerOption<E, T, InAnyOrderSearchBehaviour>.elementsOf(
expectedIterable: Iterable<E>
): Expect<T> {
require(expectedIterable.iterator().hasNext()) { "Iterable without elements are not allowed." }
return values(expectedIterable.first(), *expectedIterable.drop(1).toTypedArray())
val (first, rest) = toVarArg(expectedIterable)
return values(first, *rest)
}

View File

@@ -3,6 +3,7 @@ package ch.tutteli.atrium.api.fluent.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.ExpectImpl
import ch.tutteli.atrium.domain.builders.creating.basic.contains.addAssertion
import ch.tutteli.atrium.domain.builders.utils.toVarArg
import ch.tutteli.atrium.domain.creating.iterable.contains.IterableContains
import ch.tutteli.atrium.domain.creating.iterable.contains.searchbehaviours.InAnyOrderOnlySearchBehaviour
import ch.tutteli.kbox.glue
@@ -128,6 +129,6 @@ fun <E : Any, T : Iterable<E?>> IterableContains.Builder<E?, T, InAnyOrderOnlySe
inline fun <reified E, T : Iterable<E>> IterableContains.Builder<E, T, InAnyOrderOnlySearchBehaviour>.elementsOf(
expectedIterable: Iterable<E>
): Expect<T> {
require(expectedIterable.iterator().hasNext()) { "Iterable without elements are not allowed" }
return values(expectedIterable.first(), *expectedIterable.drop(1).toTypedArray())
val (first, rest) = toVarArg(expectedIterable)
return values(first, *rest)
}

View File

@@ -3,6 +3,7 @@ package ch.tutteli.atrium.api.fluent.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.ExpectImpl
import ch.tutteli.atrium.domain.builders.creating.basic.contains.addAssertion
import ch.tutteli.atrium.domain.builders.utils.toVarArg
import ch.tutteli.atrium.domain.creating.iterable.contains.IterableContains
import ch.tutteli.atrium.domain.creating.iterable.contains.searchbehaviours.InOrderOnlySearchBehaviour
import ch.tutteli.kbox.glue
@@ -118,6 +119,6 @@ fun <E : Any, T : Iterable<E?>> IterableContains.Builder<E?, T, InOrderOnlySearc
inline fun <reified E, T : Iterable<E>> IterableContains.Builder<E, T, InOrderOnlySearchBehaviour>.elementsOf(
expectedIterable: Iterable<E>
): Expect<T> {
require(expectedIterable.iterator().hasNext()) { "Iterable without elements are not allowed." }
return values(expectedIterable.first(), *expectedIterable.drop(1).toTypedArray())
val (first, rest) = toVarArg(expectedIterable)
return values(first, *rest)
}

View File

@@ -3,8 +3,9 @@ package ch.tutteli.atrium.api.fluent.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.specs.fun0
import ch.tutteli.atrium.specs.fun1
import ch.tutteli.atrium.specs.notImplemented
object CharSequenceAssertionsSpec : ch.tutteli.atrium.specs.integration.CharSequenceAssertionsSpec(
class CharSequenceAssertionsSpec : ch.tutteli.atrium.specs.integration.CharSequenceAssertionsSpec(
fun0(Expect<CharSequence>::isEmpty),
fun0(Expect<CharSequence>::isNotEmpty),
fun0(Expect<CharSequence>::isNotBlank),
@@ -18,4 +19,26 @@ object CharSequenceAssertionsSpec : ch.tutteli.atrium.specs.integration.CharSequ
fun1<CharSequence, Char>(Expect<CharSequence>::endsNotWith),
fun1<CharSequence, Regex>(Expect<CharSequence>::matches),
fun1<CharSequence, Regex>(Expect<CharSequence>::mismatches)
)
) {
@Suppress("unused", "UNUSED_VALUE")
private fun ambiguityTest() {
val a1: Expect<String> = notImplemented()
a1.isEmpty()
a1.isNotEmpty()
a1.isNotBlank()
a1.startsWith("expected")
a1.startsNotWith("expected")
a1.endsWith("expected")
a1.endsNotWith("expected")
a1.startsWith('a')
a1.startsNotWith('a')
a1.endsWith('a')
a1.endsNotWith('a')
a1.matches(Regex("a"))
a1.mismatches(Regex("a"))
}
}

View File

@@ -155,7 +155,7 @@ class CharSequenceContainsAtLeastAssertionsSpec : Spek({
aX: Array<out Any>
) = expect.contains.ignoringCase.atLeast(atLeast).butAtMost(butAtMost).elementsOf(listOf(a, *aX))
private fun getContainsNotPair() = containsNot to Companion::getErrorMsgContainsNot
private fun getContainsNotPair() = containsNot to ::getErrorMsgContainsNot
private fun getErrorMsgContainsNot(times: Int) = "use $containsNot instead of $atLeast($times)"

View File

@@ -1,28 +1,21 @@
package ch.tutteli.atrium.api.fluent.en_GB
import ch.tutteli.atrium.creating.Expect
import kotlin.reflect.KFunction3
import ch.tutteli.atrium.specs.fun2
import ch.tutteli.atrium.specs.notImplemented
class CharSequenceContainsContainsNotAssertionsSpec :
ch.tutteli.atrium.specs.integration.CharSequenceContainsContainsNotAssertionsSpec(
getContainsPair(),
getContainsNotPair(),
fun2<CharSequence, String, Array<out String>>(Expect<CharSequence>::contains),
fun2<CharSequence, String, Array<out String>>(Expect<CharSequence>::containsNot),
"◆ ", "", "▶ "
) {
companion object : CharSequenceContainsSpecBase() {
private val containsFun: KFunction3<Expect<CharSequence>, Any, Array<out Any>, Expect<CharSequence>> =
Expect<CharSequence>::contains
fun getContainsPair() = containsFun.name to Companion::containsShortcut
@Suppress("unused", "UNUSED_VALUE")
private fun ambiguityTest() {
val a1: Expect<String> = notImplemented()
private fun containsShortcut(expect: Expect<CharSequence>, a: Any, aX: Array<out Any>) = expect.contains(a, *aX)
private val containsNotFun: KFunction3<Expect<CharSequence>, Any, Array<out Any>, Expect<CharSequence>> =
Expect<CharSequence>::containsNot
private fun getContainsNotPair() = containsNotFun.name to Companion::containsNotShortcut
private fun containsNotShortcut(expect: Expect<CharSequence>, a: Any, aX: Array<out Any>) =
expect.containsNot(a, *aX)
a1.contains(1, "a", 'c')
a1.containsNot(1, "a", 'c')
}
}

View File

@@ -6,6 +6,7 @@ import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceConta
import ch.tutteli.atrium.domain.creating.charsequence.contains.searchbehaviours.NoOpSearchBehaviour
import ch.tutteli.atrium.specs.fun2
import ch.tutteli.atrium.specs.name
import ch.tutteli.atrium.specs.notImplemented
import kotlin.reflect.KFunction3
import kotlin.reflect.KProperty
@@ -28,4 +29,31 @@ abstract class CharSequenceContainsSpecBase {
> = CharSequenceContains.CheckerOption<*, NoOpSearchBehaviour>::regex
protected val regex = regexKFun.name
protected val ignoringCase = CharSequenceContains.Builder<*, NoOpSearchBehaviour>::ignoringCase.name
@Suppress("unused", "UNUSED_VALUE")
private fun ambiguityTest() {
val a1: Expect<CharSequence> = notImplemented()
a1.contains.atLeast(1).value(1)
a1.contains.atMost(2).values("a", 1)
a1.contains.notOrAtMost(2).regex("h|b")
a1.contains.exactly(2).regex("h|b", "b")
a1.contains.atLeast(2).regex(Regex("bla"))
a1.contains.atLeast(2).regex(Regex("bla"), Regex("b"))
a1.contains.atLeast(2).elementsOf(listOf("a", 2))
a1.contains.ignoringCase.atLeast(1).value("a")
a1.contains.ignoringCase.atLeast(1).values("a", 'b')
a1.contains.ignoringCase.atLeast(1).regex("a")
a1.contains.ignoringCase.atLeast(1).regex("a", "bl")
a1.contains.ignoringCase.atLeast(1).elementsOf(listOf(1, 2))
// skip atLeast
a1.contains.ignoringCase.value("a")
a1.contains.ignoringCase.values("a", 'b')
a1.contains.ignoringCase.regex("a")
a1.contains.ignoringCase.regex("a", "bl")
//TODO add to infix as well as fluent
// a1.contains.ignoringCase.elementsOf(listOf(1, 2))("a", "bl")
}
}

View File

@@ -0,0 +1,324 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.NotCheckerOption
import ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.impl.NotCheckerOptionImpl
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.ExpectImpl
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
import ch.tutteli.atrium.domain.creating.charsequence.contains.searchbehaviours.NoOpSearchBehaviour
import ch.tutteli.atrium.domain.creating.charsequence.contains.searchbehaviours.NotSearchBehaviour
/**
* Creates a [CharSequenceContains.Builder] based on this [Expect] which allows to define
* a sophisticated `contains` assertion.
*
* @param o The filler object [o]; use [O] in case you are in an [Expect] context due to a Kotlin bug
* (see type alias for more information)
*
* @return The newly created builder.
*/
infix fun <T : CharSequence> Expect<T>.contains(
@Suppress("UNUSED_PARAMETER") o: o
): CharSequenceContains.Builder<T, NoOpSearchBehaviour> = ExpectImpl.charSequence.containsBuilder(this)
/**
* Creates a [CharSequenceContains.Builder] based on this [Expect] which allows to define
* more sophisticated `contains not` assertion.
*
* @return The newly created builder.
*/
infix fun <T : CharSequence> Expect<T>.contains(
@Suppress("UNUSED_PARAMETER") not: not
): NotCheckerOption<T, NotSearchBehaviour> = NotCheckerOptionImpl(ExpectImpl.charSequence.containsNotBuilder(this))
/**
* Expects that the subject of the assertion (a [CharSequence]) contains the [expected]'s [toString] representation.
*
* It is a shortcut for `contains o atLeast 1 value expected`.
*
* Notice that a runtime check applies which assures that only [CharSequence], [Number] and [Char] are passed (this
* function expects `Any` for your convenience, so that you can mix [String] and [Int] for instance).
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case [expected] is not a
* [CharSequence], [Number] or [Char].
*/
infix fun <T : CharSequence> Expect<T>.contains(expected: Any): Expect<T> =
this contains O atLeast 1 value expected
/**
* Expects that the subject of the assertion (a [CharSequence]) contains the [toString] representation of the
* given [values] using a non disjoint search.
*
* It is a shortcut for `contains o atLeast 1 the Values(expected, *otherExpected)`.
*
* Notice that a runtime check applies which assures that only [CharSequence], [Number] and [Char] are passed (this
* function expects `Any` for your convenience, so that you can mix [String] and [Int] for instance).
*
* By non disjoint is meant that `'aa'` in `'aaaa'` is found three times and not only two times.
* Also notice, that it does not search for unique matches. Meaning, if the input of the search is `'a'` and
* [Values.expected] is defined as `'a'` and one [Values.otherExpected] is defined as `'a'` as well, then both match,
* even though they match the same sequence in the input of the search. Use the property `contains` to create
* a more sophisticated `contains` assertion where you can use options such as [atLeast], [atMost] and [exactly]
* to control the number of occurrences you expect.
*
* Meaning you might want to use:
* `contains o exactly 2 value 'a'`
* instead of:
* `contains Values('a', 'a')`
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case one of the [values] is not a
* [CharSequence], [Number] or [Char].
*/
infix fun <T : CharSequence> Expect<T>.contains(values: Values<Any>): Expect<T> =
this contains O atLeast 1 the values
/**
* Expects that the subject of the assertion (a [CharSequence]) does not contain [expected]'s [toString] representation.
*
* It is a shortcut for `contains not value expected`.
*
* Notice that a runtime check applies which assures that only [CharSequence], [Number] and [Char] are passed (this
* function expects `Any` for your convenience, so that you can mix [String] and [Int] for instance).
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Expect<T>.containsNot(expected: Any) =
this contains not value expected
/**
* Expects that the subject of the assertion (a [CharSequence]) does not contain the [toString] representation
* of the given [values].
*
* It is a shortcut for `contains not the Values(expected, *otherExpected)`.
*
* Notice that a runtime check applies which assures that only [CharSequence], [Number] and [Char] are passed (this
* function expects `Any` for your convenience, so that you can mix [String] and [Int] for instance).
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Expect<T>.containsNot(values: Values<Any>) =
this contains not the values
/**
* Expects that the subject of the assertion (a [CharSequence]) contains a sequence which matches the given
* regular expression [pattern].
*
* It is a shortcut for `contains o atLeast 1 regex pattern`.
*
* @param pattern The pattern which is expected to have a match against the input of the search.
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Expect<T>.containsRegex(pattern: String): Expect<T> =
this contains O atLeast 1 regex pattern
/**
* Expects that the subject of the assertion (a [CharSequence]) contains a sequence which matches the given
* regular expression [pattern].
*
* It is a shortcut for `contains o atLeast 1 regex pattern`.
*
* @param pattern The pattern which is expected to have a match against the input of the search.
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Expect<T>.contains(pattern: Regex): Expect<T> =
this contains O atLeast 1 matchFor pattern
/**
* Expects that the subject of the assertion (a [CharSequence]) contains a sequence which matches the given
* regular expression [patterns], using a non disjoint search.
*
* It is a shortcut for `contains o atLeast 1 the RegexPatterns(pattern, *otherPatterns)`.
*
* By non disjoint is meant that `'aa'` in `'aaaa'` is found three times and not only two times.
* Also notice, that it does not search for unique matches. Meaning, if the input of the search is `'ab'` and
* [RegexPatterns.expected] is defined as `'a(b)?'` and one of the [RegexPatterns.otherExpected] is defined
* as `'a(b)?'` as well, then both match, even though they match the same sequence in the input of the search.
* Use an option such as [atLeast], [atMost] and [exactly] to control the number of occurrences you expect.
*
* Meaning you might want to use:
* `contains o exactly 2 regex "a(b)?"`
* instead of:
* `contains o atLeast 1 the RegexPatterns("a(b)?", "a(b)?")`
*
* @param patterns The patterns which are expected to have a match against the input of the search.
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Expect<T>.containsRegex(patterns: RegexPatterns): Expect<T> =
this contains O atLeast 1 the patterns
/**
* Expects that the subject of the assertion (a [CharSequence]) contains a sequence which matches the given
* regular expression [patterns], using a non disjoint search.
*
* It is a shortcut for `contains o atLeast 1 regex All(pattern, *otherPatterns)`.
*
* By non disjoint is meant that `'aa'` in `'aaaa'` is found three times and not only two times.
* Also notice, that it does not search for unique matches. Meaning, if the input of the search is `'ab'` and
* [RegexPatterns.expected] is defined as `'a(b)?'` and one of the [RegexPatterns.otherExpected] is defined
* as `'a(b)?'` as well, then both match, even though they match the same sequence in the input of the search.
* Use an option such as [atLeast], [atMost] and [exactly] to control the number of occurrences you expect.
*
* Meaning you might want to use:
* `contains o exactly 2 regex "a(b)?"`
* instead of:
* `contains o atLeast 1 the RegexPatterns("a(b)?", "a(b)?")`
*
* @param patterns The patterns which are expected to have a match against the input of the search.
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Expect<T>.contains(patterns: All<Regex>): Expect<T> =
this contains O atLeast 1 matchFor patterns
/**
* Expects that the subject of the assertion (a [CharSequence]) starts with [expected].
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Expect<T>.startsWith(expected: CharSequence) =
addAssertion(ExpectImpl.charSequence.startsWith(this, expected))
/**
* Expects that the subject of the assertion (a [CharSequence]) starts with [expected].
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*
* @since 0.10.0
*/
infix fun <T : CharSequence> Expect<T>.startsWith(expected: Char) =
o startsWith expected.toString()
/**
* Expects that the subject of the assertion (a [CharSequence]) does not start with [expected].
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Expect<T>.startsNotWith(expected: CharSequence) =
addAssertion(ExpectImpl.charSequence.startsNotWith(this, expected))
/**
* Expects that the subject of the assertion (a [CharSequence]) does not start with [expected].
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*
* @since 0.10.0
*/
infix fun <T : CharSequence> Expect<T>.startsNotWith(expected: Char) =
o startsNotWith expected.toString()
/**
* Expects that the subject of the assertion (a [CharSequence]) ends with [expected].
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Expect<T>.endsWith(expected: CharSequence) =
addAssertion(ExpectImpl.charSequence.endsWith(this, expected))
/**
* Expects that the subject of the assertion (a [CharSequence]) ends with [expected].
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*
* @since 0.10.0
*/
infix fun <T : CharSequence> Expect<T>.endsWith(expected: Char) =
o endsWith expected.toString()
/**
* Expects that the subject of the assertion (a [CharSequence]) does not end with [expected].
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Expect<T>.endsNotWith(expected: CharSequence) =
addAssertion(ExpectImpl.charSequence.endsNotWith(this, expected))
/**
* Expects that the subject of the assertion (a [CharSequence]) does not end with [expected].
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*
* @since 0.10.0
*/
infix fun <T : CharSequence> Expect<T>.endsNotWith(expected: Char) =
o endsNotWith expected.toString()
/**
* Expects that the subject of the assertion (a [CharSequence]) [CharSequence].[kotlin.text.isEmpty].
*
* @param Empty Has to be `Empty`.
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Expect<T>.toBe(@Suppress("UNUSED_PARAMETER") Empty: Empty) =
addAssertion(ExpectImpl.charSequence.isEmpty(this))
/**
* Expects that the subject of the assertion (a [CharSequence]) [CharSequence].[kotlin.text.isNotEmpty].
*
* @param Empty Has to be `Empty`.
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Expect<T>.notToBe(@Suppress("UNUSED_PARAMETER") Empty: Empty) =
addAssertion(ExpectImpl.charSequence.isNotEmpty(this))
/**
* Expects that the subject of the assertion (a [CharSequence]) [CharSequence].[kotlin.text.isNotBlank].
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Expect<T>.notToBe(@Suppress("UNUSED_PARAMETER") Blank: Blank) =
addAssertion(ExpectImpl.charSequence.isNotBlank(this))
/**
* Expects that the subject of the assertion (a [CharSequence]) matches the given [expected] [Regex].
*
* In contrast to [containsRegex] it does not look for a partial match but for an entire match.
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*
* @since 0.10.0
*/
infix fun <T : CharSequence> Expect<T>.matches(expected: Regex) =
addAssertion(ExpectImpl.charSequence.matches(this, expected))
/**
* Expects that the subject of the assertion (a [CharSequence]) mismatches the given [expected] [Regex].
*
* In contrast to `containsNot.regex` it does not look for a partial match but for an entire match.
*
* @return This assertion container to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*
* @since 0.10.0
*/
infix fun <T : CharSequence> Expect<T>.mismatches(expected: Regex) =
addAssertion(ExpectImpl.charSequence.mismatches(this, expected))

View File

@@ -0,0 +1,90 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.*
import ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.impl.*
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains.SearchBehaviour
/**
* Restricts a `contains` assertion by specifying that the number of occurrences of the value which we are looking
* for occurs `at least` number of [times] within the search input.
*
* @param times The number which the check will compare against the actual number of times an expected value is
* found in the input of the search.
*
* @return The newly created builder.
* @throws IllegalArgumentException In case [times] is smaller than zero.
* @throws IllegalArgumentException In case [times] equals to zero; use [containsNot] instead.
*/
infix fun <T : CharSequence, S : SearchBehaviour> CharSequenceContains.Builder<T, S>.atLeast(
times: Int
): AtLeastCheckerOption<T, S> = AtLeastCheckerOptionImpl(times, this)
/**
* Restricts a `contains at least` assertion by specifying that the number of occurrences of the value which we
* are looking for occurs `at most` number of [times] within the search input.
*
* The resulting restriction will be a `contains at least but at most` assertion.
*
* @param times The number which the check will compare against the actual number of times an expected value is
* found in the input of the search.
*
* @return The newly created builder.
* @throws IllegalArgumentException In case [times] is smaller than zero.
* @throws IllegalArgumentException In case [times] equals to zero; use [containsNot] instead.
* @throws IllegalArgumentException In case [times] of this `at most` restriction equals to the number of the
* `at least` restriction; use the [exactly] restriction instead.
*/
infix fun <T : CharSequence, S : SearchBehaviour> AtLeastCheckerOption<T, S>.butAtMost(
times: Int
): ButAtMostCheckerOption<T, S> = ButAtMostCheckerOptionImpl(times, this, containsBuilder)
/**
* Restricts a `contains` assertion by specifying that the number of occurrences of the value which we
* are looking for occurs `exactly` number of [times] within the search input.
*
* @param times The number which the check will compare against the actual number of times an expected value is
* found in the input of the search.
*
* @return The newly created builder.
* @throws IllegalArgumentException In case [times] is smaller than zero.
* @throws IllegalArgumentException In case [times] equals to zero; use [containsNot] instead.
*/
infix fun <T : CharSequence, S : SearchBehaviour> CharSequenceContains.Builder<T, S>.exactly(
times: Int
): ExactlyCheckerOption<T, S> = ExactlyCheckerOptionImpl(times, this)
/**
* Restricts a `contains` assertion by specifying that the number of occurrences of the value which we
* are looking for occurs `at least` once but `at most` number of [times] within the search input.
*
* If you want to use a higher lower bound than one, then use `atLeast(2).butAtMost(3)` instead of `atMost(3)`.
* And in case you want to state that it is either not contained at all or at most a certain number of times,
* then use `notOrAstMost(2)` instead.
*
* @param times The number which the check will compare against the actual number of times an expected value is
* found in the input of the search.
*
* @return The newly created builder.
* @throws IllegalArgumentException In case [times] is smaller than zero.
* @throws IllegalArgumentException In case [times] equals to zero; use [containsNot] instead.
* @throws IllegalArgumentException In case [times] equals to one; use [exactly] instead.
*/
infix fun <T : CharSequence, S : SearchBehaviour> CharSequenceContains.Builder<T, S>.atMost(
times: Int
): AtMostCheckerOption<T, S> = AtMostCheckerOptionImpl(times, this)
/**
* Restricts a `contains` assertion by specifying that the number of occurrences of the value which we
* are looking for occurs `not at all or at most` number of [times] within the search input.
*
* @param times The number which the check will compare against the actual number of times an expected value is
* found in the input of the search.
*
* @return The newly created builder.
* @throws IllegalArgumentException In case [times] is smaller than zero.
* @throws IllegalArgumentException In case [times] equals to zero; use [containsNot] instead.
*/
infix fun <T : CharSequence, S : SearchBehaviour> CharSequenceContains.Builder<T, S>.notOrAtMost(
times: Int
): NotOrAtMostCheckerOption<T, S> = NotOrAtMostCheckerOptionImpl(times, this)

View File

@@ -0,0 +1,376 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.ExpectImpl
import ch.tutteli.atrium.domain.builders.creating.basic.contains.addAssertion
import ch.tutteli.atrium.domain.builders.utils.toVarArg
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains.Builder
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains.CheckerOption
import ch.tutteli.atrium.domain.creating.charsequence.contains.searchbehaviours.IgnoringCaseSearchBehaviour
import ch.tutteli.atrium.domain.creating.charsequence.contains.searchbehaviours.NoOpSearchBehaviour
import kotlin.jvm.JvmName
/**
* Finishes the specification of the sophisticated `contains` assertion where the [expected] object shall be searched,
* using a non disjoint search.
*
* Delegates to `the Values(expected)`.
*
* Notice that a runtime check applies which assures that only [CharSequence], [Number] and [Char] are passed (this
* function expects `Any` for your convenience, so that you can mix [String] and [Int] for instance).
*
* By non disjoint is meant that 'aa' in 'aaaa' is found three times and not only two times.
*
* @param expected The value which is expected to be contained within the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case [expected] is not a [CharSequence], [Number] or [Char].
*/
infix fun <T : CharSequence> CheckerOption<T, NoOpSearchBehaviour>.value(expected: Any): Expect<T> =
this the Values(expected)
/**
* Finishes the specification of the sophisticated `contains` assertion where the given [values]
* shall be searched, using a non disjoint search.
*
* Notice that a runtime check applies which assures that only [CharSequence], [Number] and [Char] are passed (this
* function expects `Any` for your convenience, so that you can mix [String] and [Int] for instance).
*
* By non disjoint is meant that `'aa'` in `'aaaa'` is found three times and not only two times.
* Also notice, that it does not search for unique matches. Meaning, if the input of the search is `'a'` and
* [Values.expected] is defined as `'a'` and one [Values.otherExpected] is defined as `'a'` as well, then both match,
* even though they match the same sequence in the input of the search. Use an option such as
* [atLeast], [atMost] and [exactly] to control the number of occurrences you expect.
*
* Meaning you might want to use:
* `to contain exactly 2 the value 'a'`
* instead of:
* `to contain atLeast 1 the Values('a', 'a')`
*
* @param values The values which are expected to be contained within the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case one of the [values] is not a [CharSequence], [Number] or [Char].
*/
infix fun <T : CharSequence> CheckerOption<T, NoOpSearchBehaviour>.the(values: Values<Any>): Expect<T> =
addAssertion(ExpectImpl.charSequence.contains.values(this, values.toList()))
/**
* Finishes the specification of the sophisticated `contains` assertion where the [expected] value shall be searched
* (ignoring case), using a non disjoint search.
*
* Delegates to `the Values(expected)`.
*
* Notice that a runtime check applies which assures that only [CharSequence], [Number] and [Char] are passed (this
* function expects `Any` for your convenience, so that you can mix [String] and [Int] for instance).
*
* By non disjoint is meant that 'aa' in 'aaaa' is found three times and not only two times.
*
* @param expected The value which is expected to be contained within the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case [expected] is not a [CharSequence], [Number] or [Char].
*/
@JvmName("valueIgnoringCase")
infix fun <T : CharSequence> CheckerOption<T, IgnoringCaseSearchBehaviour>.value(expected: Any): Expect<T> =
this the Values(expected)
/**
* Finishes the specification of the sophisticated `contains` assertion where the [values]
* shall be searched (ignoring case), using a non disjoint search.
*
* Notice that a runtime check applies which assures that only [CharSequence], [Number] and [Char] are passed (this
* function expects `Any` for your convenience, so that you can mix [String] and [Int] for instance).
*
* By non disjoint is meant that `'aa'` in `'aaaa'` is found three times and not only two times.
* Also notice, that it does not search for unique matches. Meaning, if the input of the search is `'a'` and
* [Values.expected] is defined as `'a'` and one [Values.otherExpected] is defined as `'a'` as well, then both match,
* even though they match the same sequence in the input of the search. Use an option such as
* [atLeast], [atMost] and [exactly] to control the number of occurrences you expect.
*
* Meaning you might want to use:
* `to contain ignoring case exactly 2 the value 'a'`
* instead of:
* `to contain ignoring case atLeast 1 the Values('a', 'a')`
*
* @param values The values which are expected to be contained within the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case one of the [values] is not a [CharSequence], [Number] or [Char].
*/
@JvmName("valuesIgnoringCase")
infix fun <T : CharSequence> CheckerOption<T, IgnoringCaseSearchBehaviour>.the(values: Values<Any>): Expect<T> =
addAssertion(ExpectImpl.charSequence.contains.valuesIgnoringCase(this, values.toList()))
/**
* Finishes the specification of the sophisticated `contains` assertion where the [expected] value shall be searched
* (ignoring case), using a non disjoint search where it needs to be contained at least once.
*
* Delegates to `atLeast 1 value expected`.
*
* Notice that a runtime check applies which assures that only [CharSequence], [Number] and [Char] are passed (this
* function expects `Any` for your convenience, so that you can mix [String] and [Int] for instance).
*
* By non disjoint is meant that 'aa' in 'aaaa' is found three times and not only two times.
*
* @param expected The value which is expected to be contained within the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case [expected] is not a [CharSequence], [Number] or [Char].
*/
infix fun <T : CharSequence> Builder<T, IgnoringCaseSearchBehaviour>.value(expected: Any): Expect<T> =
this atLeast 1 value expected
/**
* Finishes the specification of the sophisticated `contains` assertion where the [values]
* shall be searched (ignoring case), using a non disjoint search
* where each need to be contained at least once.
*
* Delegates to `atLeast 1 the value`
*
* By non disjoint is meant that `'aa'` in `'aaaa'` is found three times and not only two times.
* Also notice, that it does not search for unique matches. Meaning, if the input of the search is `'a'` and
* [Values.expected] is defined as `'a'` and one [Values.otherExpected] is defined as `'a'` as well, then both match,
* even though they match the same sequence in the input of the search.
* Use an option such as [atLeast], [atMost] and [exactly] to control the number of occurrences you expect.
*
* Meaning you might want to use:
* `to contain ignoring case exactly 2 the value 'a'`
* instead of:
* `to contain ignoring case atLeast 1 the Values('a', 'a')`
*
* @param values The values which are expected to be contained within the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case one of the [values] is not a [CharSequence], [Number] or [Char].
*/
infix fun <T : CharSequence> Builder<T, IgnoringCaseSearchBehaviour>.the(values: Values<Any>): Expect<T> =
this atLeast 1 the values
/**
* Finishes the specification of the sophisticated `contains` assertion where the given regular expression [pattern]
* is expected to have a match, using a non disjoint search.
*
* Delegates to `the RegexPatterns(pattern)`.
*
* @param pattern The pattern which is expected to have a match against the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> CheckerOption<T, NoOpSearchBehaviour>.regex(pattern: String): Expect<T> =
this the RegexPatterns(pattern)
/**
* Finishes the specification of the sophisticated `contains` assertion where the given [Regex] [pattern]
* is expected to have a match.
*
* Delegates to `All(pattern)`
*
* @param pattern The pattern which is expected to have a match against the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*
* @since 0.10.0
*/
infix fun <T : CharSequence> CheckerOption<T, NoOpSearchBehaviour>.matchFor(
pattern: Regex
): Expect<T> = this matchFor All(pattern)
/**
* Finishes the specification of the sophisticated `contains` assertion where the given regular expression [patterns]
* are expected to have a match, using a non disjoint search.
*
* By non disjoint is meant that `'aa'` in `'aaaa'` is found three times and not only two times.
* Also notice, that it does not search for unique matches. Meaning, if the input of the search is `'ab'` and
* [patterns].[pattern][RegexPatterns.expected] is defined as `'a(b)?'` and one of the
* [patterns].[otherPatterns][RegexPatterns.otherExpected] is defined as `'a(b)?'` as well, then both match, even though
* they match the same sequence in the input of the search.
* Use an option such as [atLeast], [atMost] and [exactly] to control the number of occurrences you expect.
*
* Meaning you might want to use:
* `to contain exactly 2 the regex 'a(b)?'`
* instead of:
* `to contain atLeast 1 the RegexPatterns('a(b)?', 'a(b)?')`
*
* @param patterns The patterns which are expected to have a match against the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> CheckerOption<T, NoOpSearchBehaviour>.the(patterns: RegexPatterns): Expect<T> =
addAssertion(ExpectImpl.charSequence.contains.regex(this, patterns.toList()))
/**
* Finishes the specification of the sophisticated `contains` assertion where the given [Regex] [patterns]
* are expected to have a match, using a non disjoint search.
*
* By non disjoint is meant that `'aa'` in `'aaaa'` is found three times and not only two times.
* Also notice, that it does not search for unique matches. Meaning, if the input of the search is `'ab'` and the first
* pattern in [patterns] is defined as `'a(b)?'` and one of the other patterns is defined as `'a(b)?'` as well,
* then both match, even though they match the same sequence in the input of the search.
* Use an option such as [atLeast], [atMost] and [exactly] to
* control the number of occurrences you expect.
*
* Meaning you might want to use:
* `contains o exactly 2 matchFor Regex("a(b)?")`
* instead of:
* `contains o atLeast 1 matchFor All(Regex("a(b)?"), Regex("a(b)?"))
*
* @param patterns The patterns which are expected to have a match against the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*
* @since 0.10.0
*/
infix fun <T : CharSequence> CheckerOption<T, NoOpSearchBehaviour>.matchFor(patterns: All<Regex>): Expect<T> =
addAssertion(ExpectImpl.charSequence.contains.regex(this, patterns.toList()))
/**
* Finishes the specification of the sophisticated `contains` assertion where the given regular expression [pattern]
* is expected to have a match (ignoring case), using a non disjoint search.
*
* Delegates to `the RegexPatterns(pattern)`.
*
* @param pattern The patterns which is expected to have a match against the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
@JvmName("regexIgnoringCase")
infix fun <T : CharSequence> CheckerOption<T, IgnoringCaseSearchBehaviour>.regex(pattern: String): Expect<T> =
this the RegexPatterns(pattern)
/**
* Finishes the specification of the sophisticated `contains` assertion where the given regular expression [patterns]
* are expected to have a match (ignoring case), using a non disjoint search.
*
* By non disjoint is meant that `'aa'` in `'aaaa'` is found three times and not only two times.
* Also notice, that it does not search for unique matches. Meaning, if the input of the search is `'ab'` and
* [patterns].[pattern][RegexPatterns.expected] is defined as `'a(b)?'` and one of the
* [patterns].[otherPatterns][RegexPatterns.otherExpected] is defined as `'a(b)?'` as well, then both match, even though
* they match the same sequence in the input of the search.
* Use an option such as [atLeast], [atMost] and [exactly] to control the number of occurrences you expect.
*
* Meaning you might want to use:
* `to contain ignoring case exactly 2 the regex 'a(b)?'`
* instead of:
* `to contain ignoring case atLeast 1 the RegexPatterns('a(b)?', 'a(b)?')`
*
* @param patterns The patterns which are expected to have a match against the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
@JvmName("regexIgnoringCase")
infix fun <T : CharSequence> CheckerOption<T, IgnoringCaseSearchBehaviour>.the(patterns: RegexPatterns): Expect<T> =
addAssertion(ExpectImpl.charSequence.contains.regexIgnoringCase(this, patterns.toList()))
/**
* Finishes the specification of the sophisticated `contains` assertion where the given regular expression [pattern]
* is expected to have at least one match (ignoring case), using a non disjoint search.
*
* Delegates to `atLeast 1 regex pattern`.
*
* @param pattern The patterns which is expected to have a match against the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Builder<T, IgnoringCaseSearchBehaviour>.regex(pattern: String): Expect<T> =
this atLeast 1 regex pattern
/**
* Finishes the specification of the sophisticated `contains` assertion where the given regular expression [patterns]
* are expected to have at least one match (ignoring case), using a non disjoint search.
*
* Delegates to `atLeast 1 the patterns`.
*
* By non disjoint is meant that `'aa'` in `'aaaa'` is found three times and not only two times.
* Also notice, that it does not search for unique matches. Meaning, if the input of the search is `'ab'` and
* [patterns].[pattern][RegexPatterns.expected] is defined as `'a(b)?'` and one of the
* [patterns].[otherPatterns][RegexPatterns.otherExpected] is defined as `'a(b)?'` as well, then both match, even though
* they match the same sequence in the input of the search.
* Use an option such as [atLeast], [atMost] and [exactly] to control the number of occurrences you expect.
*
* Meaning you might want to use:
* `to contain ignoring case exactly 2 the regex 'a(b)?'`
* instead of:
* `to contain ignoring case atLeast 1 the RegexPatterns('a(b)?', 'a(b)?')`
*
* @param patterns The patterns which are expected to have a match against the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : CharSequence> Builder<T, IgnoringCaseSearchBehaviour>.the(patterns: RegexPatterns): Expect<T> =
this atLeast 1 the patterns
/**
* Finishes the specification of the sophisticated `contains` assertion where all elements of the [expectedIterable]
* shall be searched, using a non disjoint search.
*
* Delegates to `the Values(expectedIterable.first(), *expectedIterable.drop(1).toTypedArray())`
* (see [the] for more information).
*
* Notice that a runtime check applies which assures that only [CharSequence], [Number] and [Char] are passed (this
* function expects `Any` for your convenience, so that you can mix [String] and [Int] for instance).
*
* By non disjoint is meant that 'aa' in 'aaaa' is found three times and not only two times.
*
* @param expectedIterable The [Iterable] whose elements are expected to be contained within the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case [expectedIterable] is not a [CharSequence], [Number] or [Char] or the given
* [expectedIterable] does not have elements (is empty).
*
* @since 0.10.0
*/
infix fun <T : CharSequence> CheckerOption<T, NoOpSearchBehaviour>.elementsOf(
expectedIterable: Iterable<Any>
): Expect<T> {
val (first, rest) = toVarArg(expectedIterable)
return this the Values(first, *rest)
}
/**
* Finishes the specification of the sophisticated `contains` assertion where all elements of the [expectedIterable]
* shall be searched (ignoring case), using a non disjoint search.
*
* Delegates to `the Values(expectedIterable.first(), *expectedIterable.drop(1).toTypedArray())`
* (see [the] for more information).
*
* Notice that a runtime check applies which assures that only [CharSequence], [Number] and [Char] are passed (this
* function expects `Any` for your convenience, so that you can mix [String] and [Int] for instance).
*
* By non disjoint is meant that 'aa' in 'aaaa' is found three times and not only two times.
*
* @param expectedIterable The [Iterable] whose elements are expected to be contained within the input of the search.
*
* @return The [Expect] for which the assertion was built to support a fluent API.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case [expectedIterable] is not a [CharSequence], [Number] or [Char] or the given
* [expectedIterable] does not have elements (is empty).
*
* @since 0.10.0
*/
@JvmName("elementsOfIgnoringCase")
infix fun <T : CharSequence> CheckerOption<T, IgnoringCaseSearchBehaviour>.elementsOf(
expectedIterable: Iterable<Any>
): Expect<T> {
val (first, rest) = toVarArg(expectedIterable)
return this the Values(first, *rest)
}

View File

@@ -0,0 +1,33 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.NotCheckerOption
import ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.impl.NotCheckerOptionImpl
import ch.tutteli.atrium.domain.builders.ExpectImpl
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
import ch.tutteli.atrium.domain.creating.charsequence.contains.searchbehaviours.IgnoringCaseSearchBehaviour
import ch.tutteli.atrium.domain.creating.charsequence.contains.searchbehaviours.NoOpSearchBehaviour
import ch.tutteli.atrium.domain.creating.charsequence.contains.searchbehaviours.NotSearchBehaviour
/**
* Defines that the search behaviour `ignore case` shall be applied to this sophisticated `contains` assertion.
*
* @param case Has to be `case`.
*
* @return The newly created builder.
*/
infix fun <T : CharSequence> CharSequenceContains.Builder<T, NoOpSearchBehaviour>.ignoring(
@Suppress("UNUSED_PARAMETER") case: case
): CharSequenceContains.Builder<T, IgnoringCaseSearchBehaviour> =
ExpectImpl.charSequence.contains.searchBehaviours.ignoringCase(this)
/**
* Defines that the search behaviour `ignore case` shall be applied to this sophisticated `contains not` assertion.
*
* @param case Has to be `case`.
*
* @return The newly created builder.
*/
infix fun <T : CharSequence> NotCheckerOption<T, NotSearchBehaviour>.ignoring(
@Suppress("UNUSED_PARAMETER") case: case
): NotCheckerOption<T, IgnoringCaseSearchBehaviour> =
NotCheckerOptionImpl(containsBuilder ignoring case)

View File

@@ -0,0 +1,14 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders
import ch.tutteli.atrium.domain.builders.creating.charsequence.contains.builders.WithTimesCheckerOption
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
/**
* Represents the extension point for another option after a `contains at least`-check within a sophisticated
* `contains` assertion building process for [CharSequence].
*
* @param T The input type of the search.
* @param S The search behaviour which should be applied for the input of the search.
*/
interface AtLeastCheckerOption<out T : CharSequence, out S : CharSequenceContains.SearchBehaviour>
: WithTimesCheckerOption<T, S>

View File

@@ -0,0 +1,13 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
/**
* Represents the extension point for another option after a `contains at least once but at most`-check within
* a sophisticated `contains` assertion building process for [CharSequence].
*
* @param T The input type of the search.
* @param S The search behaviour which should be applied for the input of the search.
*/
interface AtMostCheckerOption<out T : CharSequence, out S : CharSequenceContains.SearchBehaviour>
: CharSequenceContains.CheckerOption<T, S>

View File

@@ -0,0 +1,13 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
/**
* Represents the extension point for another option after a `contains at least but at most`-check within
* a sophisticated `contains` assertion building process for [CharSequence].
*
* @param T The input type of the search.
* @param S The search behaviour which should be applied for the input of the search.
*/
interface ButAtMostCheckerOption<out T : CharSequence, out S : CharSequenceContains.SearchBehaviour>
: CharSequenceContains.CheckerOption<T, S>

View File

@@ -0,0 +1,13 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
/**
* Represents the extension point for another option after a `contains exactly`-check within
* a sophisticated `contains` assertion building process for [CharSequence].
*
* @param T The input type of the search.
* @param S The search behaviour which should be applied for the input of the search.
*/
interface ExactlyCheckerOption<out T : CharSequence, out S : CharSequenceContains.SearchBehaviour>
: CharSequenceContains.CheckerOption<T, S>

View File

@@ -0,0 +1,13 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
/**
* Represents the extension point for another option after a `contains not at all`-check within
* a sophisticated `contains` assertion building process for [CharSequence].
*
* @param T The input type of the search.
* @param S The search behaviour which should be applied for the input of the search.
*/
interface NotCheckerOption<out T : CharSequence, out S : CharSequenceContains.SearchBehaviour>
: CharSequenceContains.CheckerOption<T, S>

View File

@@ -0,0 +1,13 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
/**
* Represents the extension point for another option after a `contains not or at most`-check within
* a sophisticated `contains` assertion building process for [CharSequence].
*
* @param T The input type of the search.
* @param S The search behaviour which should be applied for the input of the search.
*/
interface NotOrAtMostCheckerOption<out T : CharSequence, out S : CharSequenceContains.SearchBehaviour>
: CharSequenceContains.CheckerOption<T, S>

View File

@@ -0,0 +1,29 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.impl
import ch.tutteli.atrium.api.infix.en_GB.atLeast
import ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.AtLeastCheckerOption
import ch.tutteli.atrium.domain.builders.creating.charsequence.contains.builders.AtLeastCheckerOptionBase
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
/**
* Represents the builder of a `contains at least`-check within the fluent API of a sophisticated
* `contains` assertion for [CharSequence].
*
* @param T The input type of the search.
* @param S The search behaviour which should be applied for the input of the search.
*
* @constructor Represents the builder of a `contains at least` check within the fluent API of a sophisticated
* `contains` assertion for [CharSequence].
* @param times The number which the check will compare against the actual number of times an expected object is
* found in the input of the search.
* @param containsBuilder The previously used [CharSequenceContains.Builder].
*/
internal class AtLeastCheckerOptionImpl<out T : CharSequence, out S : CharSequenceContains.SearchBehaviour>(
times: Int,
containsBuilder: CharSequenceContains.Builder<T, S>
) : AtLeastCheckerOptionBase<T, S>(
times,
containsBuilder,
nameContainsNotValuesFun(),
{ "`${containsBuilder::atLeast.name} $it`" }
), AtLeastCheckerOption<T, S>

View File

@@ -0,0 +1,33 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.impl
import ch.tutteli.atrium.api.infix.en_GB.atLeast
import ch.tutteli.atrium.api.infix.en_GB.atMost
import ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.AtMostCheckerOption
import ch.tutteli.atrium.api.infix.en_GB.exactly
import ch.tutteli.atrium.domain.builders.creating.charsequence.contains.builders.AtMostCheckerOptionBase
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
/**
* Represents the builder of a `contains at least once but at most` check within the fluent API of a
* sophisticated `contains` assertion for [CharSequence].
*
* @param T The input type of the search.
* @param S The search behaviour which should be applied for the input of the search.
*
* @constructor Represents the builder of a `contains at least once but at most` check within the fluent API of a
* sophisticated `contains` assertion for [CharSequence].
* @param times The number which the check will compare against the actual number of times an expected object is
* found in the input of the search.
* @param containsBuilder The previously used [CharSequenceContains.Builder].
*/
internal class AtMostCheckerOptionImpl<out T : CharSequence, out S : CharSequenceContains.SearchBehaviour>(
times: Int,
containsBuilder: CharSequenceContains.Builder<T, S>
) : AtMostCheckerOptionBase<T, S>(
times,
containsBuilder,
nameContainsNotValuesFun(),
{ "`${containsBuilder::atMost.name} $it`" },
{ "`${containsBuilder::atLeast.name} $it`" },
{ "`${containsBuilder::exactly.name} $it`" }
), AtMostCheckerOption<T, S>

View File

@@ -0,0 +1,39 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.impl
import ch.tutteli.atrium.api.infix.en_GB.atLeast
import ch.tutteli.atrium.api.infix.en_GB.atMost
import ch.tutteli.atrium.api.infix.en_GB.butAtMost
import ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.AtLeastCheckerOption
import ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.ButAtMostCheckerOption
import ch.tutteli.atrium.api.infix.en_GB.exactly
import ch.tutteli.atrium.domain.builders.creating.charsequence.contains.builders.ButAtMostCheckerOptionBase
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
/**
* Represents the builder of the second step of a `contains at least but at most` check within the
* fluent API of a sophisticated `contains` assertion for [CharSequence].
*
* @param T The input type of the search.
* @param S The search behaviour which should be applied to the input of the search.
*
* @constructor Represents the builder of the second step of a `contains at least but at most` check within the
* fluent API of a sophisticated `contains` assertion for [CharSequence].
* @param times The number which the check will compare against the actual number of times an expected object is
* found in the input of the search.
* @param containsBuilder The previously used [CharSequenceContains.Builder].
*/
internal class ButAtMostCheckerOptionImpl<out T : CharSequence, out S : CharSequenceContains.SearchBehaviour>(
times: Int,
atLeastBuilder: AtLeastCheckerOption<T, S>,
containsBuilder: CharSequenceContains.Builder<T, S>
) : ButAtMostCheckerOptionBase<T, S>(
times,
atLeastBuilder,
containsBuilder,
nameContainsNotValuesFun(),
{ l, u -> "`${containsBuilder::atLeast.name} $l ${atLeastBuilder::butAtMost.name} $u`" },
{ "`${containsBuilder::atMost.name} $it`" },
{ "`${containsBuilder::atLeast.name} $it`" },
{ "`${atLeastBuilder::butAtMost.name} $it`" },
{ "`${containsBuilder::exactly.name} $it`" }
), ButAtMostCheckerOption<T, S>

View File

@@ -0,0 +1,29 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.impl
import ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.ExactlyCheckerOption
import ch.tutteli.atrium.api.infix.en_GB.exactly
import ch.tutteli.atrium.domain.builders.creating.charsequence.contains.builders.ExactlyCheckerOptionBase
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
/**
* Represents the builder of a `contains exactly` check within the fluent API of a sophisticated
* `contains` assertion for [CharSequence].
*
* @param T The input type of the search.
* @param S The search behaviour which should be applied for the input of the search.
*
* @constructor Represents the builder of a `contains exactly` check within the fluent API of a sophisticated
* `contains` assertion for [CharSequence].
* @param times The number which the check will compare against the actual number of times an expected object is
* found in the input of the search.
* @param containsBuilder The previously used [CharSequenceContains.Builder].
*/
internal class ExactlyCheckerOptionImpl<out T : CharSequence, out S : CharSequenceContains.SearchBehaviour>(
times: Int,
containsBuilder: CharSequenceContains.Builder<T, S>
) : ExactlyCheckerOptionBase<T, S>(
times,
containsBuilder,
nameContainsNotValuesFun(),
{ "`${containsBuilder::exactly.name} $it`" }
), ExactlyCheckerOption<T, S>

View File

@@ -0,0 +1,21 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.impl
import ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.NotCheckerOption
import ch.tutteli.atrium.domain.builders.creating.charsequence.contains.builders.NotCheckerOptionBase
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
/**
* Represents the builder of a `contains not at all` check within the fluent API of a sophisticated
* `contains` assertion for [CharSequence].
*
* @param T The input type of the search.
* @param S The search behaviour which should be applied for the input of the search.
*
* @constructor Represents the builder of a `contains not at all` check within the fluent API of a sophisticated
* `contains` assertion for [CharSequence].
* @param containsBuilder The previously used [CharSequenceContains.Builder].
*/
internal class NotCheckerOptionImpl<out T : CharSequence, out S : CharSequenceContains.SearchBehaviour>(
containsBuilder: CharSequenceContains.Builder<T, S>
) : NotCheckerOptionBase<T, S>(containsBuilder),
NotCheckerOption<T, S>

View File

@@ -0,0 +1,29 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.impl
import ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.NotOrAtMostCheckerOption
import ch.tutteli.atrium.api.infix.en_GB.notOrAtMost
import ch.tutteli.atrium.domain.builders.creating.charsequence.contains.builders.NotOrAtMostCheckerOptionBase
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
/**
* Represents the builder of a `contains not or at most` check within the fluent API of a
* sophisticated `contains` assertion for [CharSequence].
*
* @param T The input type of the search.
* @param S The search behaviour which should be applied for the input of the search.
*
* @constructor Represents the builder of a `contains not or at most` check within the fluent API of a
* sophisticated `contains` assertion for [CharSequence].
* @param times The number which the check will compare against the actual number of times an expected object is
* found in the input of the search.
* @param containsBuilder The previously used [CharSequenceContains.Builder].
*/
internal class NotOrAtMostCheckerOptionImpl<out T : CharSequence, out S : CharSequenceContains.SearchBehaviour>(
times: Int,
containsBuilder: CharSequenceContains.Builder<T, S>
) : NotOrAtMostCheckerOptionBase<T, S>(
times,
containsBuilder,
nameContainsNotValuesFun(),
{ "`${containsBuilder::notOrAtMost.name} $it`" }
), NotOrAtMostCheckerOption<T, S>

View File

@@ -0,0 +1,11 @@
package ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.impl
import ch.tutteli.atrium.api.infix.en_GB.Values
import ch.tutteli.atrium.api.infix.en_GB.containsNot
import ch.tutteli.atrium.creating.Expect
import kotlin.reflect.KFunction2
internal fun nameContainsNotValuesFun(): String {
val f: KFunction2<Expect<CharSequence>, Values<Any>, Expect<CharSequence>> = Expect<CharSequence>::containsNot
return "`${f.name} ${Values::class.simpleName}`"
}

View File

@@ -20,7 +20,7 @@ import kotlin.reflect.*
*
* @since 0.10.0
*/
infix fun <T, R> Expect<T>.feature(property: KProperty1<T, R>): FeatureExpect<T, R> =
infix fun <T, R> Expect<T>.feature(property: KProperty1<in T, R>): FeatureExpect<T, R> =
ExpectImpl.feature.property(this, property).getExpectOfFeature()
/**
@@ -47,6 +47,8 @@ infix fun <T, R> Expect<T>.feature(f: KFunction1<T, R>): FeatureExpect<T, R> =
* Use `of(K..., ...)` to create a [Feature] where the first argument is the extractor in form of a
* [KProperty1] or a `KFunctionX` and potentially the required arguments for a `KFunctionX` where `X` > 1.
*
* Note, [Feature] will be made invariant once Kotlin 1.4 is out and Atrium depends on it (most likely with 1.0.0)
*
* @param of Use `of(K..., ...)` to create a [Feature] where the first argument is the extractor in form of a
* [KProperty1] or a `KFunctionX` and potentially the required arguments for a `KFunctionX` where `X` > 1.
*
@@ -54,7 +56,8 @@ infix fun <T, R> Expect<T>.feature(f: KFunction1<T, R>): FeatureExpect<T, R> =
*
* @since 0.10.0
*/
infix fun <T, R> Expect<T>.feature(of: Feature<T, R>): FeatureExpect<T, R> =
//TODO remove `in` with Kotlin 1.4 (most likely with Atrium 1.0.0)
infix fun <T, R> Expect<T>.feature(of: Feature<in T, R>): FeatureExpect<T, R> =
ExpectImpl.feature.manualFeature(this, of.description, of.extractor).getExpectOfFeature()
/**
@@ -67,6 +70,8 @@ infix fun <T, R> Expect<T>.feature(of: Feature<T, R>): FeatureExpect<T, R> =
* form of a [KProperty1] or a `KFunctionX`, the last an `assertionCreator`-lambda and the remaining arguments
* in-between the required arguments in case of a `KFunctionX` where `X` > 1.
*
* Note, [FeatureWithCreator] will be made invariant once Kotlin 1.4 is out and Atrium depends on it (most likely with 1.0.0)
*
* @param of Use `of(K..., ...) { ... }` to create a [FeatureWithCreator] where the first argument is the extractor in
* form of a [KProperty1] or a `KFunctionX`, the last an `assertionCreator`-lambda and the remaining arguments
* in-between the required arguments in case of a `KFunctionX` where `X` > 1.
@@ -76,7 +81,8 @@ infix fun <T, R> Expect<T>.feature(of: Feature<T, R>): FeatureExpect<T, R> =
*
* @since 0.10.0
*/
infix fun <T, R> Expect<T>.feature(of: FeatureWithCreator<T, R>): Expect<T> =
//TODO remove `in` with Kotlin 1.4 (most likely with Atrium 1.0.0)
infix fun <T, R> Expect<T>.feature(of: FeatureWithCreator<in T, R>): Expect<T> =
ExpectImpl.feature.manualFeature(this, of.description, of.extractor).addToInitial(of.assertionCreator)
@@ -182,7 +188,7 @@ fun <T, A1, A2, A3, A4, A5, R> of(f: KFunction6<T, A1, A2, A3, A4, A5, R>, a1: A
/**
* Helper function to create a [FeatureWithCreator] based on a [KProperty1] + [assertionCreator].
*/
fun <T, R> of(property: KProperty1<T, R>, assertionCreator: Expect<R>.() -> Unit): FeatureWithCreator<T, R> =
fun <T, R> of(property: KProperty1<in T, R>, assertionCreator: Expect<R>.() -> Unit): FeatureWithCreator<T, R> =
FeatureWithCreator(property.name, { property.invoke(it) }, assertionCreator)
/**

View File

@@ -2,6 +2,8 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
/**
* Marker interface for keywords.
*
@@ -10,6 +12,7 @@ package ch.tutteli.atrium.api.infix.en_GB
*/
interface Keyword
//TODO not used yet, should be used though
internal const val ERR_KEYWORD_GIVEN_COLLECTION_ASSUMED =
"This call will most probably fail at runtime because the given subject is not a collection as you might have assumed. If you really want to compare the subject against the keyword, then cast the keyword to Any"
@@ -49,6 +52,14 @@ object entries : Keyword
*/
object group : Keyword
/**
* Represents the pseudo keyword `not` as in [contains] `not`.
* It can be used for a parameter less function so that it has one parameter and thus can be used as infix function.
*
* @since 0.10.0
*/
object not : Keyword
/**
* Represents a filler, a pseudo keyword where there isn't really a good keyword.
* A reader should skip this filler without reading it. For instance, `contains o atLeast 1...` should be read as
@@ -60,6 +71,16 @@ object group : Keyword
*/
object o : Keyword
/**
* Workaround for https://youtrack.jetbrains.com/issue/KT-36624, extension property takes precedence over object.
*
* In case you want to refer to the pseudo-keyword `o` inside an [Expect] context,
* then you can use this type alias instead to circumvent the bug.
*
* @since 0.10.0
*/
typealias O = o
/**
* Represents the pseudo keyword `only` as in [and] `only`.
* It can be used for a parameter less function so that it has one parameter and thus can be used as infix function.

View File

@@ -1,6 +1,10 @@
@file:Suppress("DEPRECATION" /** TODO remove suppress with 1.0.0 */)
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.utils.Group
import ch.tutteli.atrium.domain.builders.utils.GroupWithNullableEntries
import ch.tutteli.atrium.domain.builders.utils.GroupWithoutNullableEntries
import ch.tutteli.atrium.domain.builders.utils.VarArgHelper
/**
@@ -25,3 +29,24 @@ class Pairs<out K, out V>(
override val expected: Pair<K, V>,
override vararg val otherExpected: Pair<K, V>
) : VarArgHelper<Pair<K, V>>
/**
* Parameter object to express `String, vararg String` in the infix-api.
*/
class RegexPatterns(pattern: String, vararg otherPatterns: String) : VarArgHelper<String> {
override val expected = pattern
override val otherExpected = otherPatterns
}
/**
* Represents a [Group] of multiple values.
*
* Note, [Values] will be made invariant once Kotlin 1.4 is out and Atrium depends on it (most likely with 1.0.0)
*/
//TODO remove `out` with Kotlin 1.4 (most likely with Atrium 1.0.0)
class Values<out T>(
override val expected: T,
override vararg val otherExpected: T
) : GroupWithoutNullableEntries<T>, GroupWithNullableEntries<T>, VarArgHelper<T> {
override fun toList() = listOf(expected, *otherExpected)
}

View File

@@ -0,0 +1,50 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
/**
* Expects that the property [Throwable.message] of the subject of the assertion is not null,
* creates an [Expect] for it and returns it.
*
* @return The newly created [Expect] for the property [Throwable.message] of the subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
val <T : Throwable> Expect<T>.message: Expect<String>
get() = o feature Throwable::message notToBeNull O
/**
* Expects that the property [Throwable.message] of the subject of the assertion is not null and
* holds all assertions the given [assertionCreator] creates for it and
* returns an [Expect] for the current subject of the assertion.
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : Throwable> Expect<T>.message(assertionCreator: Expect<String>.() -> Unit): Expect<T> =
o feature of(Throwable::message) { o notToBeNull assertionCreator }
/**
* Expects that the property [Throwable.message] of the subject of the assertion is not null and contains
* [expected]'s [toString] representation using a non disjoint search.
**
* Notice that a runtime check applies which assures that only [CharSequence], [Number] and [Char] are passed
* (this function expects `Any` for your convenience, so that you can mix [String] and [Int] for instance).
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : Throwable> Expect<T>.messageContains(expected: Any): Expect<T> =
this messageContains Values(expected)
/**
* Expects that the property [Throwable.message] of the subject of the assertion is not null and contains
* [values]'s [toString] representation using a non disjoint search.
**
* Notice that a runtime check applies which assures that only [CharSequence], [Number] and [Char] are passed
* (this function expects `Any` for your convenience, so that you can mix [String] and [Int] for instance).
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*/
infix fun <T : Throwable> Expect<T>.messageContains(values: Values<Any>): Expect<T> =
message { contains(values) }

View File

@@ -0,0 +1,50 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.specs.fun1
import ch.tutteli.atrium.specs.notImplemented
import ch.tutteli.atrium.specs.testutils.WithAsciiReporter
class CharSequenceAssertionsSpec : ch.tutteli.atrium.specs.integration.CharSequenceAssertionsSpec(
"toBe ${Empty::class.simpleName}" to ::toBeEmpty,
"notToBe ${Empty::class.simpleName}" to ::notToBeEmpty,
"notToBe ${Blank::class.simpleName}" to ::notToBeBlank,
fun1<CharSequence, CharSequence>(Expect<CharSequence>::startsWith),
fun1<CharSequence, Char>(Expect<CharSequence>::startsWith),
fun1<CharSequence, CharSequence>(Expect<CharSequence>::startsNotWith),
fun1<CharSequence, Char>(Expect<CharSequence>::startsNotWith),
fun1<CharSequence, CharSequence>(Expect<CharSequence>::endsWith),
fun1<CharSequence, Char>(Expect<CharSequence>::endsWith),
fun1<CharSequence, CharSequence>(Expect<CharSequence>::endsNotWith),
fun1<CharSequence, Char>(Expect<CharSequence>::endsNotWith),
fun1<CharSequence, Regex>(Expect<CharSequence>::matches),
fun1<CharSequence, Regex>(Expect<CharSequence>::mismatches)
) {
companion object : WithAsciiReporter()
@Suppress("unused", "UNUSED_VALUE")
private fun ambiguityTest() {
val a1: Expect<String> = notImplemented()
a1 toBe Empty
a1 notToBe Empty
a1 notToBe Blank
a1 startsWith "expected"
a1 startsNotWith "expected"
a1 endsWith "expected"
a1 endsNotWith "expected"
a1 startsWith 'a'
a1 startsNotWith 'a'
a1 endsWith 'a'
a1 endsNotWith 'a'
a1 matches Regex("a")
a1 mismatches Regex("a")
}
}
private fun toBeEmpty(expect: Expect<CharSequence>) = expect toBe Empty
private fun notToBeEmpty(expect: Expect<CharSequence>) = expect notToBe Empty
private fun notToBeBlank(expect: Expect<CharSequence>) = expect notToBe Blank

View File

@@ -0,0 +1,174 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.api.verbs.internal.expect
import ch.tutteli.atrium.creating.Expect
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
class CharSequenceContainsAtLeastAssertionsSpec : Spek({
include(object : ch.tutteli.atrium.specs.integration.CharSequenceContainsAtLeastAssertionsSpec(
getAtLeastValuesTriple(),
getAtLeastIgnoringCaseValuesTriple(),
getAtLeastButAtMostValuesTriple(),
getAtLeastBustAtMostIgnoringCaseValuesTriple(),
getContainsNotPair(),
getExactlyPair(),
CharSequenceContainsAtLeastAssertionsSpec.Companion::getErrorMsgAtLeastButAtMost,
"* ", "- "
) {})
include(object : ch.tutteli.atrium.specs.integration.CharSequenceContainsAtLeastAssertionsSpec(
getAtLeastElementsOfTriple(),
getAtLeastIgnoringCaseElementsOfTriple(),
getAtLeastButAtMostElementsOfTriple(),
getAtLeastButAtMostIgnoringCaseElementsOfTriple(),
getContainsNotPair(),
getExactlyPair(),
CharSequenceContainsAtLeastAssertionsSpec.Companion::getErrorMsgAtLeastButAtMost,
"* ", "- "
) {})
include(object : Spek({
describe("elementsOf") {
it("passing an empty iterable throws an IllegalArgumentException") {
expect {
expect("test") contains o atLeast 1 elementsOf emptyList()
}.toThrow<IllegalArgumentException> { o messageContains "Iterable without elements are not allowed" }
}
}
describe("elementsOf ignoring case") {
it("passing an empty iterable throws an IllegalArgumentException") {
expect {
expect("test") contains o ignoring case atLeast 1 elementsOf emptyList()
}.toThrow<IllegalArgumentException> { o messageContains "Iterable without elements are not allowed" }
}
}
}) {})
}) {
companion object : CharSequenceContainsSpecBase() {
private val atLeastDescr = { what: String, times: String -> "$contains $what $atLeast $times" }
internal fun getAtLeastValuesTriple() =
atLeastDescr to ("$contains o $atLeast" to Companion::containsAtLeast)
private fun containsAtLeast(
expect: Expect<CharSequence>,
atLeast: Int,
a: Any,
aX: Array<out Any>
): Expect<CharSequence> =
if (aX.isEmpty()) expect contains o atLeast atLeast value a
else expect contains o atLeast atLeast the Values(a, *aX)
internal fun getAtLeastElementsOfTriple() =
atLeastDescr to ("$contains o $atLeast" to Companion::containsAtLeastElementsOf)
private fun containsAtLeastElementsOf(
expect: Expect<CharSequence>,
atLeast: Int,
a: Any,
aX: Array<out Any>
): Expect<CharSequence> =
expect contains o atLeast atLeast elementsOf listOf(a, *aX)
private val atLeastIgnoringCaseDescr =
{ what: String, times: String -> "$contains o $ignoringCase $what $atLeast $times" }
private fun getAtLeastIgnoringCaseValuesTriple() =
atLeastIgnoringCaseDescr to ("$contains o $ignoringCase $atLeast" to Companion::containsAtLeastIgnoringCaseValues)
private fun containsAtLeastIgnoringCaseValues(
expect: Expect<CharSequence>,
atLeast: Int,
a: Any,
aX: Array<out Any>
): Expect<CharSequence> =
if (aX.isEmpty()) {
if (atLeast == 1) expect contains o ignoring case value a
else expect contains o ignoring case atLeast atLeast value a
} else {
if (atLeast == 1) expect contains o ignoring case the Values(a, *aX)
else expect contains o ignoring case atLeast atLeast the Values(a, *aX)
}
private fun getAtLeastIgnoringCaseElementsOfTriple() =
atLeastIgnoringCaseDescr to ("$contains o $ignoringCase $atLeast" to Companion::containsAtLeastIgnoringCaseElementsOf)
private fun containsAtLeastIgnoringCaseElementsOf(
expect: Expect<CharSequence>,
atLeast: Int,
a: Any,
aX: Array<out Any>
): Expect<CharSequence> =
expect contains o ignoring case atLeast atLeast elementsOf listOf(a, *aX)
private val atLeastButAtMostDescr = { what: String, timesAtLeast: String, timesAtMost: String ->
"$contains o $what $atLeast $timesAtLeast $butAtMost $timesAtMost"
}
private fun getAtLeastButAtMostElementsOfTriple() =
atLeastButAtMostDescr to ("$contains o $atLeast o $butAtMost" to Companion::containsAtLeastButAtMostElementsOf)
private fun containsAtLeastButAtMostElementsOf(
expect: Expect<CharSequence>,
atLeast: Int,
butAtMost: Int,
a: Any,
aX: Array<out Any>
) = expect contains o atLeast atLeast butAtMost butAtMost elementsOf listOf(a, *aX)
private fun getAtLeastButAtMostValuesTriple() =
atLeastButAtMostDescr to ("$contains o $atLeast o $butAtMost" to Companion::containsAtLeastButAtMostValues)
private fun containsAtLeastButAtMostValues(
expect: Expect<CharSequence>,
atLeast: Int,
butAtMost: Int,
a: Any,
aX: Array<out Any>
) =
if (aX.isEmpty()) expect contains o atLeast atLeast butAtMost butAtMost value a
else expect contains o atLeast atLeast butAtMost butAtMost the Values(a, *aX)
private val atLeastButAtMostIgnoringCaseDescr = { what: String, timesAtLeast: String, timesAtMost: String ->
"$contains $ignoringCase $what $atLeast $timesAtLeast $butAtMost $timesAtMost"
}
private fun getAtLeastBustAtMostIgnoringCaseValuesTriple() =
atLeastButAtMostIgnoringCaseDescr to ("$contains o $ignoringCase $atLeast $butAtMost" to Companion::containsAtLeastButAtMostIgnoringCaseValues)
private fun containsAtLeastButAtMostIgnoringCaseValues(
expect: Expect<CharSequence>,
atLeast: Int,
butAtMost: Int,
a: Any,
aX: Array<out Any>
) =
if (aX.isEmpty()) expect contains o ignoring case atLeast atLeast butAtMost butAtMost value a
else expect contains o ignoring case atLeast atLeast butAtMost butAtMost the Values(a, *aX)
private fun getAtLeastButAtMostIgnoringCaseElementsOfTriple() =
atLeastButAtMostIgnoringCaseDescr to ("$contains o $ignoringCase $atLeast $butAtMost" to Companion::containsAtLeastButAtMostIgnoringCaseElementsOf)
private fun containsAtLeastButAtMostIgnoringCaseElementsOf(
expect: Expect<CharSequence>,
atLeast: Int,
butAtMost: Int,
a: Any,
aX: Array<out Any>
) = expect contains o ignoring case atLeast atLeast butAtMost butAtMost elementsOf listOf(a, *aX)
private fun getContainsNotPair() = containsNotValues to Companion::getErrorMsgContainsNot
private fun getErrorMsgContainsNot(times: Int) = "use `$containsNotValues` instead of `$atLeast $times`"
private fun getExactlyPair() = exactly to Companion::getErrorMsgExactly
private fun getErrorMsgExactly(times: Int) =
"use `$exactly $times` instead of `$atLeast $times $butAtMost $times`"
internal fun getErrorMsgAtLeastButAtMost(timesAtLeast: Int, timesButAtMost: Int) =
"specifying `$butAtMost $timesButAtMost` does not make sense if `$atLeast $timesAtLeast` was used before"
}
}

View File

@@ -0,0 +1,42 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
class CharSequenceContainsAtMostAssertionsSpec :
ch.tutteli.atrium.specs.integration.CharSequenceContainsAtMostAssertionsSpec(
getAtMostTriple(),
getAtMostIgnoringCaseTriple(),
getContainsNotPair(),
getExactlyPair(),
"* ", "- "
) {
companion object : CharSequenceContainsSpecBase() {
private fun getAtMostTriple() =
{ what: String, times: String -> "$contains $what $atMost $times" } to
("$contains o $atMost" to Companion::containsAtMost)
private fun containsAtMost(expect: Expect<CharSequence>, atMost: Int, a: Any, aX: Array<out Any>) =
if (aX.isEmpty()) expect contains o atMost atMost value a
else expect contains o atMost atMost the Values(a, *aX)
private fun getAtMostIgnoringCaseTriple() =
{ what: String, times: String -> "$contains $ignoringCase $what $atMost $times" } to
("$contains o $ignoringCase $atMost" to Companion::containsAtMostIgnoringCase)
private fun containsAtMostIgnoringCase(expect: Expect<CharSequence>, atMost: Int, a: Any, aX: Array<out Any>) =
if (aX.isEmpty()) expect contains o ignoring case atMost atMost value a
else expect contains o ignoring case atMost atMost the Values(a, *aX)
private fun getContainsNotPair() = containsNotValues to Companion::getErrorMsgContainsNot
private fun getErrorMsgContainsNot(times: Int) = "use `$containsNotValues` instead of `$atMost $times`"
private fun getExactlyPair() = exactly to Companion::getErrorMsgExactly
private fun getErrorMsgExactly(times: Int) =
"use `$exactly $times` instead of `$atMost $times`; `$atMost $times` defines implicitly `$atLeast $times` as well"
}
}

View File

@@ -0,0 +1,31 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.specs.fun2
import ch.tutteli.atrium.specs.notImplemented
import ch.tutteli.atrium.specs.testutils.WithAsciiReporter
class CharSequenceContainsContainsNotAssertionsSpec :
ch.tutteli.atrium.specs.integration.CharSequenceContainsContainsNotAssertionsSpec(
fun2<CharSequence, String, Array<out String>>(::contains),
fun2<CharSequence, String, Array<out String>>(::containsNot),
"* ", "- ", ">> "
) {
companion object : WithAsciiReporter()
@Suppress("unused", "UNUSED_VALUE")
private fun ambiguityTest() {
val a1: Expect<String> = notImplemented()
a1 contains Values(1, "a", 'c')
a1 containsNot Values(1, "a", 'c')
}
}
private fun contains(expect: Expect<CharSequence>, a: Any, aX: Array<out Any>) =
if (aX.isEmpty()) expect contains a
else expect contains Values(a, *aX)
private fun containsNot(expect: Expect<CharSequence>, a: Any, aX: Array<out Any>) =
if (aX.isEmpty()) expect containsNot a
else expect containsNot Values(a, *aX)

View File

@@ -0,0 +1,40 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
class CharSequenceContainsExactlyAssertionsSpec :
ch.tutteli.atrium.specs.integration.CharSequenceContainsExactlyAssertionsSpec(
getExactlyTriple(),
getExactlyIgnoringCaseTriple(),
getContainsNotPair(),
"* ", "- "
) {
companion object : CharSequenceContainsSpecBase() {
private fun getExactlyTriple() =
{ what: String, times: String -> "$contains $what $exactly $times" } to
("$contains o $exactly" to Companion::containsExactly)
private fun containsExactly(expect: Expect<CharSequence>, exactly: Int, a: Any, aX: Array<out Any>) =
if (aX.isEmpty()) expect contains o exactly exactly value a
else expect contains o exactly exactly the Values(a, *aX)
private fun getExactlyIgnoringCaseTriple() =
{ what: String, times: String -> "$contains $ignoringCase $what $exactly $times" } to
("$contains o $ignoringCase $exactly" to Companion::containsExactlyIgnoringCase)
private fun containsExactlyIgnoringCase(
expect: Expect<CharSequence>,
exactly: Int,
a: Any,
aX: Array<out Any>
) =
if (aX.isEmpty()) expect contains o ignoring case exactly exactly value a
else expect contains o ignoring case exactly exactly the Values(a, *aX)
private fun getContainsNotPair() = containsNotValues to Companion::getErrorMsgContainsNot
private fun getErrorMsgContainsNot(times: Int) = "use `$containsNotValues` instead of `$exactly $times`"
}
}

View File

@@ -0,0 +1,29 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
class CharSequenceContainsNotAssertionsSpec : ch.tutteli.atrium.specs.integration.CharSequenceContainsNotAssertionsSpec(
getContainsNotTriple(),
getContainsNotIgnoringCaseTriple(),
"* ", "- "
) {
companion object : CharSequenceContainsSpecBase() {
private fun getContainsNotTriple() =
{ what: String -> "$containsNotValues $what" } to
(containsNotValues to Companion::containsNotFun)
private fun containsNotFun(expect: Expect<CharSequence>, a: Any, aX: Array<out Any>) =
if (aX.isEmpty()) expect contains not value a
else expect contains not the Values(a, *aX)
private fun getContainsNotIgnoringCaseTriple() =
{ what: String -> "$containsNotValues $ignoringCase $what" } to
("$containsNotValues o $ignoringCase" to Companion::containsNotIgnoringCase)
private fun containsNotIgnoringCase(expect: Expect<CharSequence>, a: Any, aX: Array<out Any>) =
if (aX.isEmpty()) expect contains not ignoring case value a
else expect contains not ignoring case the Values(a, *aX)
}
}

View File

@@ -0,0 +1,42 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
class CharSequenceContainsNotOrAtMostAssertionsSpec :
ch.tutteli.atrium.specs.integration.CharSequenceContainsNotOrAtMostAssertionsSpec(
getNotOrAtMostTriple(),
getNotOrAtMostIgnoringCaseTriple(),
getContainsNotPair(),
"* ", "- "
) {
companion object : CharSequenceContainsSpecBase() {
private fun getNotOrAtMostTriple() =
{ what: String, times: String -> "$contains $what $notOrAtMost $times" } to
("$contains o $notOrAtMost" to Companion::containsNotOrAtMost)
private fun containsNotOrAtMost(expect: Expect<CharSequence>, atMost: Int, a: Any, aX: Array<out Any>) =
if (aX.isEmpty()) expect contains o notOrAtMost atMost value a
else expect contains o notOrAtMost atMost the Values(a, *aX)
private fun getNotOrAtMostIgnoringCaseTriple() =
{ what: String, times: String -> "$contains $ignoringCase $what $notOrAtMost $times" } to
("$contains o $ignoringCase $notOrAtMost" to Companion::containsNotOrAtMostIgnoringCase)
private fun containsNotOrAtMostIgnoringCase(
expect: Expect<CharSequence>,
atMost: Int,
a: Any,
aX: Array<out Any>
) =
if (aX.isEmpty()) expect contains o ignoring case notOrAtMost atMost value a
else expect contains o ignoring case notOrAtMost atMost the Values(a, *aX)
private fun getContainsNotPair() = containsNotValues to Companion::getErrorMsgContainsNot
private fun getErrorMsgContainsNot(times: Int) = "use `$containsNotValues` instead of `$notOrAtMost $times`"
}
}

View File

@@ -0,0 +1,139 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
import org.spekframework.spek2.Spek
class CharSequenceContainsRegexAssertionsSpec : Spek({
include(StringSpec)
include(RegexSpec)
}) {
object StringSpec : ch.tutteli.atrium.specs.integration.CharSequenceContainsRegexAssertionsSpec(
getNameContainsRegex(),
getAtLeastTripleString(),
getAtLeastIgnoringCaseTripleString(),
getShortcutTripleString(),
getAtMostTripleString(),
getAtMostIgnoringCaseTripleString(),
"* ", "- ",
"[StringSpec] "
)
object RegexSpec : ch.tutteli.atrium.specs.integration.CharSequenceContainsRegexAssertionsSpec(
getNameContainsRegex(),
getAtLeastTripleRegex(),
getAtLeastIgnoringCaseTripleString(),
getShortcutTripleRegex(),
getAtMostTripleRegex(),
getAtMostIgnoringCaseTripleString(),
"* ", "- ",
"[RegexSpec] "
)
companion object : CharSequenceContainsSpecBase() {
private fun getNameContainsRegex() = "$contains with search mode $regex"
private fun getAtLeastTripleString() =
{ what: String, times: String -> "$contains $what $atLeast $times" } to
("$contains o $atLeast $regex" to ::containsAtLeastString)
private fun getAtLeastTripleRegex() =
{ what: String, times: String -> "$contains $what $atLeast $times" } to
("$contains o $atLeast $regex" to ::containsAtLeastRegex)
private fun containsAtLeastString(
expect: Expect<CharSequence>,
atLeast: Int,
a: String,
aX: Array<out String>
) =
if (aX.isEmpty()) expect contains o atLeast atLeast regex a
else expect contains o atLeast atLeast the RegexPatterns(a, *aX)
private fun containsAtLeastRegex(expect: Expect<CharSequence>, atLeast: Int, a: String, aX: Array<out String>) =
if (aX.isEmpty()) expect contains o atLeast atLeast matchFor Regex(a)
else expect contains o atLeast atLeast matchFor All(Regex(a), *aX.map { it.toRegex() }.toTypedArray())
private fun getAtLeastIgnoringCaseTripleString() =
{ what: String, times: String -> "$contains $ignoringCase $what $atLeast $times" } to
("$contains o $atLeast $ignoringCase $regex" to ::containsAtLeastIgnoringCase)
private fun containsAtLeastIgnoringCase(
expect: Expect<CharSequence>,
atLeast: Int,
a: String,
aX: Array<out String>
) =
if (aX.isEmpty()) {
if (atLeast == 1) expect contains o ignoring case regex a
else expect contains o ignoring case atLeast atLeast regex a
} else {
if (atLeast == 1) expect contains o ignoring case the RegexPatterns(a, *aX)
else expect contains o ignoring case atLeast atLeast the RegexPatterns(a, *aX)
}
private fun getShortcutTripleString() =
{ what: String, times: String -> "$contains $what $atLeast $times" } to
(containsRegex to ::containsShortcutString)
private fun getShortcutTripleRegex() =
{ what: String, times: String -> "$contains $what $atLeast $times" } to
(containsRegex to ::containsShortcutRegex)
private fun containsShortcutString(
expect: Expect<CharSequence>,
a: String,
aX: Array<out String>
) =
if (aX.isEmpty()) expect containsRegex a
else expect containsRegex RegexPatterns(a, *aX)
private fun containsShortcutRegex(
expect: Expect<CharSequence>,
a: String,
aX: Array<out String>
) =
if (aX.isEmpty()) expect contains Regex(a)
else expect contains All(Regex(a), *aX.map { it.toRegex() }.toTypedArray())
private fun getAtMostTripleString() =
{ what: String, times: String -> "$contains $what $atMost $times" } to
("$contains o $atMost $regex" to ::containsAtMostString)
private fun getAtMostTripleRegex() =
{ what: String, times: String -> "$contains $what $atMost $times" } to
("$contains o $atMost $regex" to ::containsAtMostRegex)
private fun containsAtMostString(
expect: Expect<CharSequence>,
atMost: Int,
a: String,
aX: Array<out String>
) =
if (aX.isEmpty()) expect contains o atMost atMost regex a
else expect contains o atMost atMost the RegexPatterns(a, *aX)
private fun getAtMostIgnoringCaseTripleString() =
{ what: String, times: String -> "$contains $ignoringCase $what $atMost $times" } to
("$contains o $ignoringCase $atMost $regex" to ::containsAtMostIgnoringCase)
private fun containsAtMostRegex(
expect: Expect<CharSequence>,
atMost: Int,
a: String,
aX: Array<out String>
) =
if (aX.isEmpty()) expect contains o atMost atMost matchFor Regex(a)
else expect contains o atMost atMost matchFor All(Regex(a), *aX.map { it.toRegex() }.toTypedArray())
private fun containsAtMostIgnoringCase(
expect: Expect<CharSequence>,
atMost: Int,
a: String,
aX: Array<out String>
) =
if (aX.isEmpty()) expect contains o ignoring case atMost atMost regex a
else expect contains o ignoring case atMost atMost the RegexPatterns(a, *aX)
}
}

View File

@@ -0,0 +1,60 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.api.infix.en_GB.creating.charsequence.contains.builders.AtLeastCheckerOption
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.creating.charsequence.contains.CharSequenceContains
import ch.tutteli.atrium.domain.creating.charsequence.contains.searchbehaviours.NoOpSearchBehaviour
import ch.tutteli.atrium.specs.fun1
import ch.tutteli.atrium.specs.name
import ch.tutteli.atrium.specs.notImplemented
import ch.tutteli.atrium.specs.testutils.WithAsciiReporter
import kotlin.reflect.KFunction2
abstract class CharSequenceContainsSpecBase : WithAsciiReporter() {
private val containsProp: KFunction2<Expect<String>, o, CharSequenceContains.Builder<String, NoOpSearchBehaviour>> =
Expect<String>::contains
protected val contains = containsProp.name
private val containsNotFun: KFunction2<Expect<String>, Any, Expect<String>> = Expect<String>::containsNot
protected val containsNotValues = "${containsNotFun.name} ${Values::class.simpleName}"
protected val containsRegex = fun1<String, String>(Expect<String>::containsRegex).name
protected val atLeast = CharSequenceContains.Builder<*, *>::atLeast.name
protected val butAtMost = AtLeastCheckerOption<*, *>::butAtMost.name
protected val exactly = CharSequenceContains.Builder<*, *>::exactly.name
protected val atMost = CharSequenceContains.Builder<*, *>::atMost.name
protected val notOrAtMost = CharSequenceContains.Builder<*, *>::notOrAtMost.name
private val regexKFun: KFunction2<
CharSequenceContains.CheckerOption<*, NoOpSearchBehaviour>,
String,
Expect<*>
> = CharSequenceContains.CheckerOption<*, NoOpSearchBehaviour>::regex
protected val regex = regexKFun.name
protected val ignoringCase =
"${CharSequenceContains.Builder<*, NoOpSearchBehaviour>::ignoring.name} ${case::class.simpleName}"
@Suppress("unused", "UNUSED_VALUE")
private fun ambiguityTest() {
val a1: Expect<CharSequence> = notImplemented()
a1 contains o atLeast 1 value 1
a1 contains o atMost 2 the Values("a", 1)
a1 contains o notOrAtMost 2 regex "h|b"
a1 contains o exactly 2 the RegexPatterns("h|b", "b")
a1 contains o atLeast 2 matchFor Regex("bla")
a1 contains o atLeast 2 matchFor All(Regex("bla"), Regex("b"))
a1 contains o atLeast 2 elementsOf listOf(1, 2)
a1 contains o ignoring case atLeast 1 value "a"
a1 contains o ignoring case atLeast 1 the Values("a", 'b')
a1 contains o ignoring case atLeast 1 regex "a"
a1 contains o ignoring case atLeast 1 the RegexPatterns("a", "bl")
a1 contains o ignoring case atLeast 1 elementsOf listOf(1, 2)
// skip atLeast
a1 contains o ignoring case value "a"
a1 contains o ignoring case the Values("a", 'b')
a1 contains o ignoring case regex "a"
a1 contains o ignoring case the RegexPatterns("a", "bl")
//TODO add to infix as well as fluent
//a1 contains o ignoring case elementsOf listOf(1, 2)
}
}

View File

@@ -0,0 +1,29 @@
package ch.tutteli.atrium.api.infix.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.specs.fun1
import ch.tutteli.atrium.specs.fun2
import ch.tutteli.atrium.specs.notImplemented
import ch.tutteli.atrium.specs.property
class ThrowableAssertionsSpec : ch.tutteli.atrium.specs.integration.ThrowableAssertionsSpec(
property<Throwable, String>(Expect<Throwable>::message),
fun1<Throwable, Expect<String>.() -> Unit>(Expect<Throwable>::message),
fun2(::messageContains)
) {
@Suppress("unused", "UNUSED_VALUE")
private fun ambiguityTest() {
var a1: Expect<Throwable> = notImplemented()
a1.message
a1 = a1 message {}
a1 = a1 messageContains "a"
a1 = a1 messageContains 'a'
a1 = a1 messageContains Values("a", 1, 'b')
}
}
private fun messageContains(expect: Expect<Throwable>, expected: Any, vararg otherExpected: Any): Expect<Throwable> =
if (otherExpected.isEmpty()) expect messageContains expected
else expect messageContains Values(expected, *otherExpected)

View File

@@ -26,3 +26,14 @@ interface VarArgHelper<out T> {
*/
fun toList(): List<T> = expected glue otherExpected
}
/**
* Transforms the given [iterable] to `Pair<T, Array<out T>>` with the intend that it can be easily used for a function
* requiring `T, vararg T`
*
* @throws IllegalArgumentException in case the iterable is empty.
*/
inline fun <reified T> toVarArg(iterable: Iterable<T>): Pair<T, Array<out T>> {
require(iterable.iterator().hasNext()) { "Iterable without elements are not allowed for this function." }
return iterable.first() to iterable.drop(1).toTypedArray()
}

View File

@@ -66,55 +66,55 @@ abstract class CharSequenceContainsRegexAssertionsSpec(
describeFun(containsRegex) {
context("throws an ${IllegalArgumentException::class.simpleName}") {
it("if an erroneous pattern is passed to `$containsAtLeast` as first argument") {
it("if an erroneous pattern is passed to `${containsAtLeast.name}` as first argument") {
expect {
expect("a" as CharSequence).containsAtLeastFun(1, "notA(validPattern")
}.toThrow<IllegalArgumentException>()
}
it("if an erroneous pattern is passed to `$containsAtLeast` as second argument") {
it("if an erroneous pattern is passed to `${containsAtLeast.name}` as second argument") {
expect {
expect("a" as CharSequence).containsAtLeastFun(1, "h(a|e)llo", "notA(validPattern")
}.toThrow<IllegalArgumentException>()
}
it("if an erroneous pattern is passed to `$containsAtLeastIgnoringCase` as first argument") {
it("if an erroneous pattern is passed to `${containsAtLeastIgnoringCase.name}` as first argument") {
expect {
expect("a" as CharSequence).containsAtLeastIgnoringCaseFun(1, "notA(validPattern")
}.toThrow<IllegalArgumentException>()
}
it("if an erroneous pattern is passed to `$containsAtLeastIgnoringCase` as second argument") {
it("if an erroneous pattern is passed to `${containsAtLeastIgnoringCase.name}` as second argument") {
expect {
expect("a" as CharSequence).containsAtLeastIgnoringCaseFun(1, "h(a|e)llo", "notA(validPattern")
}.toThrow<IllegalArgumentException>()
}
it("if an erroneous pattern is passed to `$containsShortcut` as first argument") {
it("if an erroneous pattern is passed to `${containsShortcut.name}` as first argument") {
expect {
expect("a" as CharSequence).containsShortcutFun("notA(validPattern")
}.toThrow<IllegalArgumentException>()
}
it("if an erroneous pattern is passed to `$containsShortcut` as second argument") {
it("if an erroneous pattern is passed to `${containsShortcut.name}` as second argument") {
expect {
expect("a" as CharSequence).containsShortcutFun("h(a|e)llo", "notA(validPattern")
}.toThrow<IllegalArgumentException>()
}
it("if an erroneous pattern is passed to `$containsAtMost` as first argument") {
it("if an erroneous pattern is passed to `${containsAtMost.name}` as first argument") {
expect {
expect("a" as CharSequence).containsAtMostFun(2, "notA(validPattern")
}.toThrow<IllegalArgumentException>()
}
it("if an erroneous pattern is passed to `$containsAtMost` as second argument") {
it("if an erroneous pattern is passed to `${containsAtMost.name}` as second argument") {
expect {
expect("a" as CharSequence).containsAtMostFun(2, "h(a|e)llo", "notA(validPattern")
}.toThrow<IllegalArgumentException>()
}
it("if an erroneous pattern is passed to `$containsAtMostIgnoringCase` as first argument") {
it("if an erroneous pattern is passed to `${containsAtMostIgnoringCase.name}` as first argument") {
expect {
expect("a" as CharSequence).containsAtMostIgnoringCaseFun(2, "notA(validPattern")
}.toThrow<IllegalArgumentException>()
}
it("if an erroneous pattern is passed to `$containsAtMostIgnoringCase` as second argument") {
it("if an erroneous pattern is passed to `${containsAtMostIgnoringCase.name}` as second argument") {
expect {
expect("a" as CharSequence).containsAtMostIgnoringCaseFun(2, "h(a|e)llo", "notA(validPattern")
}.toThrow<IllegalArgumentException>()