Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:56:56 +01:00
parent 75dc487a7a
commit 39c29d175b
6317 changed files with 388324 additions and 2 deletions

View file

@ -0,0 +1,19 @@
plugins {
id(ThunderbirdPlugins.Library.kmp)
}
android {
namespace = "net.thunderbird.core.testing"
}
kotlin {
sourceSets {
commonMain.dependencies {
implementation(libs.kotlin.test)
implementation(libs.kotlin.test.junit)
implementation(libs.kotlinx.coroutines.test)
implementation(libs.assertk)
implementation(libs.turbine)
}
}
}

View file

@ -0,0 +1,13 @@
package assertk.assertions
import assertk.Assert
import assertk.assertions.support.expected
import assertk.assertions.support.show
fun <T> Assert<List<T>>.containsNoDuplicates() = given { actual ->
val seen: MutableSet<T> = mutableSetOf()
val duplicates = actual.filter { !seen.add(it) }
if (duplicates.isNotEmpty()) {
expected("to contain no duplicates but found: ${show(duplicates)}")
}
}

View file

@ -0,0 +1,21 @@
package net.thunderbird.core.testing
import kotlin.time.Clock
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
import kotlin.time.Instant
@OptIn(ExperimentalTime::class)
class TestClock(
private var currentTime: Instant = Clock.System.now(),
) : Clock {
override fun now(): Instant = currentTime
fun changeTimeTo(time: Instant) {
currentTime = time
}
fun advanceTimeBy(duration: Duration) {
currentTime += duration
}
}

View file

@ -0,0 +1,34 @@
package net.thunderbird.core.testing.coroutines
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import org.junit.rules.TestWatcher
import org.junit.runner.Description
/**
* A JUnit rule that swaps the [kotlinx.coroutines.Dispatchers.Main] dispatcher with a [kotlinx.coroutines.test.TestDispatcher] for the duration of the test.
*
* Use this rule to ensure that coroutines running on the main dispatcher are executed in a controlled manner during tests.
*
* This uses [kotlinx.coroutines.test.UnconfinedTestDispatcher] by default, but you can provide a different [kotlinx.coroutines.test.TestDispatcher] if needed.
* Especially when testing view models use the [kotlinx.coroutines.test.StandardTestDispatcher], this allows you to
* control the execution of coroutines in a more predictable way.
*
* @param testDispatcher The [kotlinx.coroutines.test.TestDispatcher] to use as the main dispatcher during tests. Defaults to [kotlinx.coroutines.test.UnconfinedTestDispatcher].
*/
@OptIn(ExperimentalCoroutinesApi::class)
class MainDispatcherRule(
val testDispatcher: TestDispatcher = UnconfinedTestDispatcher(),
) : TestWatcher() {
override fun starting(description: Description) {
Dispatchers.setMain(testDispatcher)
}
override fun finished(description: Description) {
Dispatchers.resetMain()
}
}

View file

@ -0,0 +1,24 @@
package assertk.assertions
import assertk.assertFailure
import assertk.assertThat
import kotlin.test.Test
class ListExtensionsKtTest {
@Test
fun `containsNoDuplicates() should succeed with no duplicates`() {
val list = listOf("a", "b", "c")
assertThat(list).containsNoDuplicates()
}
@Test
fun `containsNoDuplicates() should fail with duplicates`() {
val list = listOf("a", "b", "c", "a", "a")
assertFailure {
assertThat(list).containsNoDuplicates()
}.hasMessage("""expected to contain no duplicates but found: <["a", "a"]>""")
}
}

View file

@ -0,0 +1,41 @@
package net.thunderbird.core.testing
import assertk.assertThat
import assertk.assertions.isEqualTo
import kotlin.test.Test
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.ExperimentalTime
import kotlin.time.Instant
@OptIn(ExperimentalTime::class)
internal class TestClockTest {
@Test
fun `should return the current time`() {
val testClock = TestClock(Instant.DISTANT_PAST)
val currentTime = testClock.now()
assertThat(currentTime).isEqualTo(Instant.DISTANT_PAST)
}
@Test
fun `should return the changed time`() {
val testClock = TestClock(Instant.DISTANT_PAST)
testClock.changeTimeTo(Instant.DISTANT_FUTURE)
val currentTime = testClock.now()
assertThat(currentTime).isEqualTo(Instant.DISTANT_FUTURE)
}
@Test
fun `should advance time by duration`() {
val testClock = TestClock(Instant.DISTANT_PAST)
testClock.advanceTimeBy(1L.milliseconds)
val currentTime = testClock.now()
assertThat(currentTime).isEqualTo(Instant.DISTANT_PAST + 1L.milliseconds)
}
}