Skip to content

Commit

Permalink
Merge pull request #559 from robstoll/#180-isNoneOf
Browse files Browse the repository at this point in the history
#180 isNoneOf and isNotIn
  • Loading branch information
robstoll authored Aug 26, 2020
2 parents bfc5ad1 + 94f3250 commit 3d055f4
Show file tree
Hide file tree
Showing 16 changed files with 312 additions and 39 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ node_modules
*.hprof
.history
.bloop
.metals
.metals
.vscode
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package ch.tutteli.atrium.api.fluent.en_GB

import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.utils.iterableLikeToIterable
import ch.tutteli.atrium.domain.creating.typeutils.IterableLike
import ch.tutteli.atrium.logic.*
import ch.tutteli.atrium.reporting.Reporter
import ch.tutteli.kbox.glue

/**
* Expects that the subject of the assertion is (equal to) [expected].
Expand Down Expand Up @@ -165,3 +168,34 @@ inline val <T> Expect<T>.and: Expect<T> get() = this
*/
infix fun <T> Expect<T>.and(assertionCreator: Expect<T>.() -> Unit): Expect<T> =
addAssertionsCreatedBy(assertionCreator)

/**
* Expects that the subject of the assertion is not (equal to) [expected] and [otherValues].
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*
* @since 0.13.0
*/
fun <T> Expect<T>.isNoneOf(expected: T, vararg otherValues: T): Expect<T> =
_logicAppend { isNotIn(expected glue otherValues) }

/**
* Expects that the subject of the assertion is not (equal to) any value of [expected].
*
* Notice that a runtime check applies which assures that only [Iterable], [Sequence] or one of the [Array] types
* are passed. This function expects [IterableLike] (which is a typealias for [Any]) to avoid cluttering the API.
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case the iterable is empty.
*
* @since 0.13.0
*/
inline fun <reified T> Expect<T>.isNotIn(expected: IterableLike): Expect<T> {
val iterable = iterableLikeToIterable<T>(expected)
require(iterable.iterator().hasNext()) { "IterableLike without elements are not allowed for this function." }
return _logicAppend { isNotIn(iterable.toList()) }
}


Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ch.tutteli.atrium.api.fluent.en_GB
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.specs.feature0
import ch.tutteli.atrium.specs.fun1
import ch.tutteli.atrium.specs.fun2
import ch.tutteli.atrium.specs.withFeatureSuffix
import ch.tutteli.atrium.specs.withNullableSuffix
import kotlin.reflect.KFunction2
Expand All @@ -11,8 +12,8 @@ import kotlin.reflect.KProperty1
class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec(
fun1<Int, Int>(Expect<Int>::toBe),
fun1<DataClass, DataClass>(Expect<DataClass>::toBe),
fun1<Int?, Int?>(Expect<Int?>::toBe),
fun1<DataClass?, DataClass?>(Expect<DataClass?>::toBe),
fun1<Int?, Int?>(Expect<Int?>::toBe).withNullableSuffix(),
fun1<DataClass?, DataClass?>(Expect<DataClass?>::toBe).withNullableSuffix(),
fun1(Expect<Int>::notToBe),
fun1(Expect<DataClass>::notToBe),
fun1(Expect<Int?>::notToBe).withNullableSuffix(),
Expand All @@ -25,6 +26,14 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec(
fun1(Expect<DataClass>::isNotSameAs),
fun1(Expect<Int?>::isNotSameAs).withNullableSuffix(),
fun1(Expect<DataClass?>::isNotSameAs).withNullableSuffix(),
fun2(Expect<Int>::isNoneOf),
fun2(Expect<DataClass>::isNoneOf),
fun2(Expect<Int?>::isNoneOf).withNullableSuffix(),
fun2(Expect<DataClass?>::isNoneOf).withNullableSuffix(),
fun1(Expect<Int>::isNotIn),
fun1(Expect<DataClass>::isNotIn),
fun1(Expect<Int?>::isNotIn).withNullableSuffix(),
fun1(Expect<DataClass?>::isNotIn).withNullableSuffix(),

"${Expect<Int?>::toBe.name}(null)" to Companion::toBeNull,
fun1(Expect<Int?>::toBeNullIfNullGivenElse),
Expand All @@ -39,7 +48,8 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec(
"notToBeNull" to Companion::notToBeNull,

getAndImmediatePair(),
getAndLazyPair()
getAndLazyPair(),
""
) {

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package ch.tutteli.atrium.api.infix.en_GB

import ch.tutteli.atrium.api.infix.en_GB.creating.Values
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.utils.iterableLikeToIterable
import ch.tutteli.atrium.domain.creating.typeutils.IterableLike
import ch.tutteli.atrium.logic.*
import ch.tutteli.atrium.reporting.Reporter

Expand Down Expand Up @@ -224,3 +227,35 @@ inline val <T> Expect<T>.it: Expect<T> get() : Expect<T> = this
* @since 0.12.0
*/
inline val <T> Expect<T>.its: Expect<T> get() : Expect<T> = this

/**
* Expects that the subject of the assertion is not (equal to) in [values].
*
* @param values The values which are not expected to be contained within the subject of the assertion
* -- use the function `values(t, ...)` to create a [Values].
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
*
* @since 0.13.0
*/
infix fun <T> Expect<T>.isNoneOf(values: Values<T>): Expect<T> =
_logicAppend { isNotIn(values.toList()) }

/**
* Expects that the subject of the assertion is not (equal to) any value of [expected].
*
* Notice that a runtime check applies which assures that only [Iterable], [Sequence] or one of the [Array] types
* are passed. This function expects [IterableLike] (which is a typealias for [Any]) to avoid cluttering the API.
*
* @return An [Expect] for the current subject of the assertion.
* @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct.
* @throws IllegalArgumentException in case the iterable is empty.
*
* @since 0.13.0
*/
inline infix fun <reified T> Expect<T>.isNotIn(expected: IterableLike): Expect<T> {
val iterable = iterableLikeToIterable<T>(expected)
require(iterable.iterator().hasNext()) { "IterableLike without elements are not allowed for this function." }
return _logicAppend { isNotIn(iterable.toList()) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ 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.testutils.WithAsciiReporter
import ch.tutteli.atrium.specs.withFeatureSuffix
Expand All @@ -11,8 +12,8 @@ import kotlin.reflect.KFunction2
class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec(
fun1<Int, Int>(Expect<Int>::toBe),
fun1<DataClass, DataClass>(Expect<DataClass>::toBe),
fun1<Int?, Int?>(Expect<Int?>::toBe),
fun1<DataClass?, DataClass?>(Expect<DataClass?>::toBe),
fun1<Int?, Int?>(Expect<Int?>::toBe).withNullableSuffix(),
fun1<DataClass?, DataClass?>(Expect<DataClass?>::toBe).withNullableSuffix(),
fun1(Expect<Int>::notToBe),
fun1(Expect<DataClass>::notToBe),
fun1(Expect<Int?>::notToBe).withNullableSuffix(),
Expand All @@ -25,6 +26,14 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec(
fun1(Expect<DataClass>::isNotSameAs),
fun1(Expect<Int?>::isNotSameAs).withNullableSuffix(),
fun1(Expect<DataClass?>::isNotSameAs).withNullableSuffix(),
fun2(Companion::isNoneOfInt),
fun2(Companion::isNoneOfDataClass),
fun2(Companion::isNoneOfIntNullable).withNullableSuffix(),
fun2(Companion::isNoneOfDataClassNullable).withNullableSuffix(),
fun1(Expect<Int>::isNotIn),
fun1(Expect<DataClass>::isNotIn),
fun1(Expect<Int?>::isNotIn).withNullableSuffix(),
fun1(Expect<DataClass?>::isNotIn).withNullableSuffix(),

"${Expect<Int?>::toBe.name}(null)" to Companion::toBeNull,
fun1(Expect<Int?>::toBeNullIfNullGivenElse),
Expand All @@ -39,10 +48,11 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec(
"notToBeNull" to Companion::notToBeNull,

getAndImmediatePair(),
getAndLazyPair()
getAndLazyPair(),
"- "
) {

companion object : WithAsciiReporter(){
companion object : WithAsciiReporter() {
private fun toBeNull(expect: Expect<Int?>) = expect toBe null

@Suppress("RemoveExplicitTypeArguments")
Expand Down Expand Up @@ -81,6 +91,18 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec(

private fun notToBeNull(expect: Expect<Int?>, assertionCreator: Expect<Int>.() -> Unit) =
expect notToBeNull assertionCreator

private fun isNoneOfInt(expect: Expect<Int>, expected: Int, otherValues: Array<out Int>): Expect<Int> =
expect isNoneOf values(expected, *otherValues)

private fun isNoneOfIntNullable(expect: Expect<Int?>, expected: Int?, otherValues: Array<out Int?>): Expect<Int?> =
expect isNoneOf values(expected, *otherValues)

private fun isNoneOfDataClass(expect: Expect<DataClass>, expected: DataClass, otherValues: Array<out DataClass>): Expect<DataClass> =
expect isNoneOf values(expected, *otherValues)

private fun isNoneOfDataClassNullable(expect: Expect<DataClass?>, expected: DataClass?, otherValues: Array<out DataClass?>): Expect<DataClass?> =
expect isNoneOf values(expected, *otherValues)
}

@Suppress("unused")
Expand All @@ -98,6 +120,8 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec(
a1 isNotSameAs 1.2
a1.isA<Int>()
a1.isA<Int> {}
a1 isNoneOf values(1, 2)
a1 isNotIn listOf(1, 1.2)

a1b toBe 1
a1b toBe 1.2
Expand All @@ -109,6 +133,8 @@ class AnyAssertionsSpec : ch.tutteli.atrium.specs.integration.AnyAssertionsSpec(
a1b isNotSameAs 1.2
a1b.isA<Int>()
a1b.isA<Int> {}
a1b isNoneOf values(1, 2)
a1b isNotIn listOf(1, 1.2)

a1b notToBeNull o toBe 1
a1b notToBeNull {}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
description = 'A fluent assertion function API in en_GB with a focus on code completion for Scala.'

dependencies {
api atriumKotlinDep('domain-builders')
api atriumKotlinDep('logic')

testImplementation atriumKotlinDep('specs')
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package ch.tutteli.atrium.api.fluent.en_GB.scala
import ch.tutteli.atrium.assertions.Assertion
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.builders.creating.AnyAssertionsBuilder
import ch.tutteli.atrium.logic.impl.DefaultAnyAssertions
import ch.tutteli.atrium.creating.AssertionContainer

class AnyAssertions[T](expect: Expect[T]) {
@inline private def anyAssertions: AnyAssertionsBuilder = ExpectImpl.getAny
Expand Down Expand Up @@ -35,6 +37,17 @@ class AnyAssertions[T](expect: Expect[T]) {
val and: Expect[T] = expect
def and(assertionCreator: Expect[T] => Unit): Expect[T] = expect.addAssertionsCreatedBy(assertionCreator)

def isNoneOf(expected: T, otherValues: T*): Expect[T] = {
isNotIn(expected +: otherValues)
}

def isNotIn(iterable: Iterable[T]): Expect[T] = {
import scala.jdk.CollectionConverters._
expect.addAssertion(new DefaultAnyAssertions().isNotIn(expect.asInstanceOf[AssertionContainer[T]], iterable.asJava))
}



@inline private def addAssertion(f: AnyAssertionsBuilder => Assertion): Expect[T] =
expect.addAssertion(f(anyAssertions))
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,35 @@ package ch.tutteli.atrium.api.fluent.en_GB.scala

import TestUtils._
import AnyAssertionsSpec._
import ch.tutteli.atrium.creating.Expect

class AnyAssertionsSpec
extends ch.tutteli.atrium.specs.integration.AnyAssertionsSpec(
toBe,
toBe,
toBe,
toBe,
notToBe,
toBe.withNullableSuffix(),
toBe.withNullableSuffix(),
notToBe,
notToBe,
notToBe,
isSameAs,
isSameAs,
notToBe.withNullableSuffix(),
notToBe.withNullableSuffix(),
isSameAs,
isSameAs,
isSameAs.withNullableSuffix(),
isSameAs.withNullableSuffix(),
isNotSameAs,
isNotSameAs,
isNotSameAs,
isNotSameAs,
isNotSameAs.withNullableSuffix(),
isNotSameAs.withNullableSuffix(),
isNoneOf,
isNoneOf,
isNoneOf.withNullableSuffix(),
isNoneOf.withNullableSuffix(),
isNotIn,
isNotIn,
isNotIn.withNullableSuffix(),
isNotIn.withNullableSuffix(),

fun0("toBe", _.toBe(null)),
fun1("toBeNullIfNullGivenElse", _.toBeNullIfNullGivenElse(_)),
new kotlin.Pair("isA (feature)", _.isA()),
Expand All @@ -33,13 +43,22 @@ class AnyAssertionsSpec
fun1("notToBeNull", _.notToBeNull(_)),
fun0("and (feature)", _.and),
fun1("and", _.and(_)),
"",
"[Atrium] "
)

//noinspection TypeAnnotation
object AnyAssertionsSpec {
import scala.jdk.CollectionConverters._

def toBe[T]: Fun1[T, T] = fun1("toBe", _.toBe(_))
def notToBe[T]: Fun1[T, T] = fun1("notToBe", _.notToBe(_))
def isSameAs[T]: Fun1[T, T] = fun1("isSameAs", _.isSameAs(_))
def isNotSameAs[T]: Fun1[T, T] = fun1("isNotSameAs", _.isNotSameAs(_))
def isNoneOf[T]: Fun2[T, T, Array[T]] = fun2("isNoneOf", (e: Expect[T], t: T, arr: Array[T]) => e.isNoneOf(t, arr.toIndexedSeq: _*))
def isNotIn[T]: Fun1[T, java.lang.Iterable[T]] = fun1("isNotIn", (e: Expect[T], i: java.lang.Iterable[T]) => e.isNotIn(i.asScala))

implicit class RxKotlinPair[T](pair: kotlin.Pair[String, T]) {
def withNullableSuffix() = new kotlin.Pair(pair.getFirst+" (nullable)", pair.getSecond)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ fun <T : Any> AssertionContainer<T?>.notToBeNull(subType: KClass<T>): ChangedSub
//TODO restrict TSub with T once type parameter for upper bounds are supported:
// https://youtrack.jetbrains.com/issue/KT-33262 is implemented
fun <T, TSub : Any> AssertionContainer<T>.isA(subType: KClass<TSub>): ChangedSubjectPostStep<T, TSub> = _anyImpl.isA(this, subType)

fun <T> AssertionContainer<T>.isNotIn(expected: Iterable<T>): Assertion = _anyImpl.isNotIn(this, expected)
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ interface AnyAssertions {
//TODO restrict TSub with T once type parameter for upper bounds are supported:
// https://youtrack.jetbrains.com/issue/KT-33262 is implemented
fun <T, TSub : Any> isA(container: AssertionContainer<T>, subType: KClass<TSub>): ChangedSubjectPostStep<T, TSub>

fun <T> isNotIn(container: AssertionContainer<T>, expected: Iterable<T>): Assertion
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ch.tutteli.atrium.logic.impl

import ch.tutteli.atrium.assertions.Assertion
import ch.tutteli.atrium.assertions.builders.assertionBuilder
import ch.tutteli.atrium.core.trueProvider
import ch.tutteli.atrium.creating.AssertionContainer
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.domain.creating.changers.ChangedSubjectPostStep
Expand Down Expand Up @@ -52,4 +54,18 @@ class DefaultAnyAssertions : AnyAssertions {
container.changeSubject.reportBuilder()
.downCastTo(subType)
.build()

override fun <T> isNotIn(container: AssertionContainer<T>, expected: Iterable<T>): Assertion {
val assertions = expected.map { value ->
assertionBuilder.representationOnly
.withTest(container) { it != value }
.withRepresentation(value)
.build()
}
return assertionBuilder.list
.withDescriptionAndEmptyRepresentation(IS_NONE_OF)
.withAssertions(assertions)
.build()
}

}
Loading

0 comments on commit 3d055f4

Please sign in to comment.