Skip to content

Commit

Permalink
Add support for array subjects (with tests)
Browse files Browse the repository at this point in the history
  • Loading branch information
bitspittle committed Jun 18, 2024
1 parent 6310bd7 commit d6b47fe
Show file tree
Hide file tree
Showing 4 changed files with 395 additions and 15 deletions.
34 changes: 20 additions & 14 deletions src/commonMain/kotlin/com/varabyte/truthish/Truth.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,7 @@ import com.varabyte.truthish.failure.DetailsFor
import com.varabyte.truthish.failure.Report
import com.varabyte.truthish.failure.Summaries
import com.varabyte.truthish.failure.withMessage
import com.varabyte.truthish.subjects.BooleanSubject
import com.varabyte.truthish.subjects.ByteSubject
import com.varabyte.truthish.subjects.ComparableSubject
import com.varabyte.truthish.subjects.DoubleSubject
import com.varabyte.truthish.subjects.FloatSubject
import com.varabyte.truthish.subjects.IntSubject
import com.varabyte.truthish.subjects.IterableSubject
import com.varabyte.truthish.subjects.LongSubject
import com.varabyte.truthish.subjects.MapSubject
import com.varabyte.truthish.subjects.NotNullSubject
import com.varabyte.truthish.subjects.NullableSubject
import com.varabyte.truthish.subjects.ShortSubject
import com.varabyte.truthish.subjects.StringSubject
import com.varabyte.truthish.subjects.*

fun assertThat(actual: Any?) = NullableSubject(actual)
fun assertThat(actual: Any) = NotNullSubject(actual)
Expand All @@ -32,6 +20,15 @@ fun assertThat(actual: String) = StringSubject(actual)
fun <T, I: Iterable<T>> assertThat(actual: I) = IterableSubject(actual)
fun <K, V, T: Map<K, V>> assertThat(actual: T) = MapSubject(actual)
fun <T, S: Sequence<T>> assertThat(actual: S) = IterableSubject(actual.asIterable())
fun <T> assertThat(actual: Array<T>) = ArraySubject(actual)
fun assertThat(actual: BooleanArray) = BooleanArraySubject(actual)
fun assertThat(actual: ByteArray) = ByteArraySubject(actual)
fun assertThat(actual: CharArray) = CharArraySubject(actual)
fun assertThat(actual: ShortArray) = ShortArraySubject(actual)
fun assertThat(actual: IntArray) = IntArraySubject(actual)
fun assertThat(actual: LongArray) = LongArraySubject(actual)
fun assertThat(actual: FloatArray) = FloatArraySubject(actual)
fun assertThat(actual: DoubleArray) = DoubleArraySubject(actual)
// Adding a new [assertThat] here? Also add it to SummarizedSubjectBuilder

fun assertWithMessage(message: String) = SummarizedSubjectBuilder(message)
Expand All @@ -50,6 +47,15 @@ class SummarizedSubjectBuilder(private val message: String) {
fun <T, I: Iterable<T>> that(actual: I) = IterableSubject(actual).withMessage(message)
fun <K, V, T: Map<K, V>> that(actual: T) = MapSubject(actual).withMessage(message)
fun <T, S: Sequence<T>> that(actual: S) = IterableSubject(actual.asIterable()).withMessage(message)
fun <T> that(actual: Array<T>) = ArraySubject(actual).withMessage(message)
fun that(actual: BooleanArray) = BooleanArraySubject(actual).withMessage(message)
fun that(actual: ByteArray) = ByteArraySubject(actual).withMessage(message)
fun that(actual: CharArray) = CharArraySubject(actual).withMessage(message)
fun that(actual: ShortArray) = ShortArraySubject(actual).withMessage(message)
fun that(actual: IntArray) = IntArraySubject(actual).withMessage(message)
fun that(actual: LongArray) = LongArraySubject(actual).withMessage(message)
fun that(actual: FloatArray) = FloatArraySubject(actual).withMessage(message)
fun that(actual: DoubleArray) = DoubleArraySubject(actual).withMessage(message)
}

/**
Expand Down Expand Up @@ -77,4 +83,4 @@ inline fun <reified T : Throwable> assertThrows(message: String? = null, block:
}

throw AssertionError(report)
}
}
181 changes: 181 additions & 0 deletions src/commonMain/kotlin/com/varabyte/truthish/subjects/ArraySubjects.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package com.varabyte.truthish.subjects

import com.varabyte.truthish.failure.DetailsFor
import com.varabyte.truthish.failure.Report
import com.varabyte.truthish.failure.Summaries

open class ArraySubject<T>(private val actualArray: Array<T>) : IterableSubject<T>(actualArray.toList()) {
fun isEqualTo(expected: Array<T>) {
if (!actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_EQUAL, DetailsFor.expectedActual(expected.contentToString(), actualArray.contentToString())))
}
}

fun isNotEqualTo(expected: Array<T>) {
if (actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_NOT_EQUAL, DetailsFor.expected(actualArray.contentToString())))
}
}
}

open class BooleanArraySubject(private val actualArray: BooleanArray) : ArraySubject<Boolean>(actualArray.toTypedArray()) {
private fun BooleanArray.toTypedArray(): Array<Boolean> = this.map { it }.toTypedArray()

fun isEqualTo(expected: BooleanArray) {
if (!actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_EQUAL, DetailsFor.expectedActual(expected.contentToString(), actualArray.contentToString())))
}
}

fun isNotEqualTo(expected: BooleanArray) {
if (actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_NOT_EQUAL, DetailsFor.expected(actualArray.contentToString())))
}
}

fun containsAnyIn(other: BooleanArray) = containsAnyIn(other.toTypedArray())
fun containsAllIn(other: BooleanArray) = containsAllIn(other.toTypedArray())
fun containsNoneIn(other: BooleanArray) = containsNoneIn(other.toTypedArray())
fun containsExactly(other: BooleanArray) = containsExactly(other.toTypedArray())
}

open class ByteArraySubject(private val actualArray: ByteArray) : ArraySubject<Byte>(actualArray.toTypedArray()) {
private fun ByteArray.toTypedArray(): Array<Byte> = this.map { it }.toTypedArray()

fun isEqualTo(expected: ByteArray) {
if (!actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_EQUAL, DetailsFor.expectedActual(expected.contentToString(), actualArray.contentToString())))
}
}

fun isNotEqualTo(expected: ByteArray) {
if (actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_NOT_EQUAL, DetailsFor.expected(actualArray.contentToString())))
}
}

fun containsAnyIn(other: ByteArray) = containsAnyIn(other.toTypedArray())
fun containsAllIn(other: ByteArray) = containsAllIn(other.toTypedArray())
fun containsNoneIn(other: ByteArray) = containsNoneIn(other.toTypedArray())
fun containsExactly(other: ByteArray) = containsExactly(other.toTypedArray())
}
open class CharArraySubject(private val actualArray: CharArray) : ArraySubject<Char>(actualArray.toTypedArray()) {
private fun CharArray.toTypedArray(): Array<Char> = this.map { it }.toTypedArray()

fun isEqualTo(expected: CharArray) {
if (!actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_EQUAL, DetailsFor.expectedActual(expected.contentToString(), actualArray.contentToString())))
}
}

fun isNotEqualTo(expected: CharArray) {
if (actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_NOT_EQUAL, DetailsFor.expected(actualArray.contentToString())))
}
}

fun containsAnyIn(other: CharArray) = containsAnyIn(other.toTypedArray())
fun containsAllIn(other: CharArray) = containsAllIn(other.toTypedArray())
fun containsNoneIn(other: CharArray) = containsNoneIn(other.toTypedArray())
fun containsExactly(other: CharArray) = containsExactly(other.toTypedArray())
}
open class ShortArraySubject(private val actualArray: ShortArray) : ArraySubject<Short>(actualArray.toTypedArray()) {
private fun ShortArray.toTypedArray(): Array<Short> = this.map { it }.toTypedArray()

fun isEqualTo(expected: ShortArray) {
if (!actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_EQUAL, DetailsFor.expectedActual(expected.contentToString(), actualArray.contentToString())))
}
}

fun isNotEqualTo(expected: ShortArray) {
if (actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_NOT_EQUAL, DetailsFor.expected(actualArray.contentToString())))
}
}

fun containsAnyIn(other: ShortArray) = containsAnyIn(other.toTypedArray())
fun containsAllIn(other: ShortArray) = containsAllIn(other.toTypedArray())
fun containsNoneIn(other: ShortArray) = containsNoneIn(other.toTypedArray())
fun containsExactly(other: ShortArray) = containsExactly(other.toTypedArray())
}
open class IntArraySubject(private val actualArray: IntArray) : ArraySubject<Int>(actualArray.toTypedArray()) {
private fun IntArray.toTypedArray(): Array<Int> = this.map { it }.toTypedArray()

fun isEqualTo(expected: IntArray) {
if (!actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_EQUAL, DetailsFor.expectedActual(expected.contentToString(), actualArray.contentToString())))
}
}

fun isNotEqualTo(expected: IntArray) {
if (actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_NOT_EQUAL, DetailsFor.expected(actualArray.contentToString())))
}
}

fun containsAnyIn(other: IntArray) = containsAnyIn(other.toTypedArray())
fun containsAllIn(other: IntArray) = containsAllIn(other.toTypedArray())
fun containsNoneIn(other: IntArray) = containsNoneIn(other.toTypedArray())
fun containsExactly(other: IntArray) = containsExactly(other.toTypedArray())
}
open class LongArraySubject(private val actualArray: LongArray) : ArraySubject<Long>(actualArray.toTypedArray()) {
private fun LongArray.toTypedArray(): Array<Long> = this.map { it }.toTypedArray()

fun isEqualTo(expected: LongArray) {
if (!actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_EQUAL, DetailsFor.expectedActual(expected.contentToString(), actualArray.contentToString())))
}
}

fun isNotEqualTo(expected: LongArray) {
if (actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_NOT_EQUAL, DetailsFor.expected(actualArray.contentToString())))
}
}

fun containsAnyIn(other: LongArray) = containsAnyIn(other.toTypedArray())
fun containsAllIn(other: LongArray) = containsAllIn(other.toTypedArray())
fun containsNoneIn(other: LongArray) = containsNoneIn(other.toTypedArray())
fun containsExactly(other: LongArray) = containsExactly(other.toTypedArray())
}
open class FloatArraySubject(private val actualArray: FloatArray) : ArraySubject<Float>(actualArray.toTypedArray()) {
private fun FloatArray.toTypedArray(): Array<Float> = this.map { it }.toTypedArray()

fun isEqualTo(expected: FloatArray) {
if (!actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_EQUAL, DetailsFor.expectedActual(expected.contentToString(), actualArray.contentToString())))
}
}

fun isNotEqualTo(expected: FloatArray) {
if (actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_NOT_EQUAL, DetailsFor.expected(actualArray.contentToString())))
}
}

fun containsAnyIn(other: FloatArray) = containsAnyIn(other.toTypedArray())
fun containsAllIn(other: FloatArray) = containsAllIn(other.toTypedArray())
fun containsNoneIn(other: FloatArray) = containsNoneIn(other.toTypedArray())
fun containsExactly(other: FloatArray) = containsExactly(other.toTypedArray())
}
open class DoubleArraySubject(private val actualArray: DoubleArray) : ArraySubject<Double>(actualArray.toTypedArray()) {
private fun DoubleArray.toTypedArray(): Array<Double> = this.map { it }.toTypedArray()

fun isEqualTo(expected: DoubleArray) {
if (!actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_EQUAL, DetailsFor.expectedActual(expected.contentToString(), actualArray.contentToString())))
}
}

fun isNotEqualTo(expected: DoubleArray) {
if (actualArray.contentEquals(expected)) {
report(Report(Summaries.EXPECTED_NOT_EQUAL, DetailsFor.expected(actualArray.contentToString())))
}
}

fun containsAnyIn(other: DoubleArray) = containsAnyIn(other.toTypedArray())
fun containsAllIn(other: DoubleArray) = containsAllIn(other.toTypedArray())
fun containsNoneIn(other: DoubleArray) = containsNoneIn(other.toTypedArray())
fun containsExactly(other: DoubleArray) = containsExactly(other.toTypedArray())
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ open class IterableSubject<T>(actual: Iterable<T>) : NotNullSubject<Iterable<T>>
fun containsExactly(vararg elements: T) = containsExactly(elements.asIterable())
}

fun <T> IterableSubject<T>.containsAnyIn(other: Array<T>) = containsAnyIn(*other)
fun <T> IterableSubject<T>.containsAllIn(other: Array<T>) = containsAllIn(*other)
fun <T> IterableSubject<T>.containsNoneIn(other: Array<T>) = containsNoneIn(*other)
fun <T> IterableSubject<T>.containsExactly(other: Array<T>) = containsExactly(*other)


/**
* We don't want to test inorder if a check already failed, so provide this [OrderedAsserter]\
* instead, which is guaranteed to pass.
Expand Down Expand Up @@ -221,4 +227,4 @@ class OrderedAsserter<T>(
*/
class MapSubject<K, V>(actual: Map<K, V>) :
IterableSubject<Pair<K, V>>(actual.entries.map { it.toPair() }) {
}
}
Loading

0 comments on commit d6b47fe

Please sign in to comment.