From a527389b966f71f7e9a1aa5b632ba1f3663fad47 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Wed, 8 Apr 2026 03:44:54 -0500 Subject: [PATCH] Replace Mockito with Mokkery --- gradle/libs.versions.toml | 26 +--- uhabits-android/build.gradle.kts | 19 ++- uhabits-android/proguard-rules.txt | 1 - .../org/isoron/uhabits/BaseAndroidTest.kt | 2 - .../uhabits/HabitsActivityTestComponent.kt | 2 +- .../habits/list/views/HeaderViewTest.kt | 22 ++-- .../habits/list/views/HintViewTest.kt | 10 +- .../common/views/BundleSavedState.kt | 2 +- .../org/isoron/uhabits/BaseAndroidJVMTest.kt | 6 +- .../receivers/ReminderControllerTest.kt | 21 ++- uhabits-core/build.gradle.kts | 12 +- .../uhabits/core/commands/CommandRunner.kt | 6 +- .../isoron/uhabits/core/models/Frequency.kt | 4 - .../isoron/uhabits/core/models/HabitList.kt | 18 +-- .../uhabits/core/models/HabitMatcher.kt | 1 - .../core/models/memory/MemoryHabitList.kt | 2 +- .../uhabits/core/preferences/Preferences.kt | 64 +++++----- .../core/preferences/WidgetPreferences.kt | 14 +- .../uhabits/core/ui/NotificationTray.kt | 12 +- .../isoron/uhabits/core/ui/ThemeSwitcher.kt | 6 +- .../screens/habits/list/ListHabitsBehavior.kt | 18 +-- .../core/reminders/ReminderScheduler.kt | 16 +-- .../uhabits/core/utils/MidnightTimer.kt | 8 +- .../isoron/uhabits/core/JvmBaseUnitTest.kt | 13 +- .../core/models/sqlite/SQLiteHabitListTest.kt | 12 +- .../core/reminders/ReminderSchedulerTest.kt | 63 ++++----- .../core/tasks/SingleThreadTaskRunnerTest.kt | 16 ++- .../habits/list/HabitCardListCacheTest.kt | 50 ++++---- .../ui/screens/habits/list/HintListTest.kt | 17 +-- .../habits/list/ListHabitsBehaviorTest.kt | 74 ++++++----- .../habits/list/ListHabitsMenuBehaviorTest.kt | 120 ++++++++---------- .../ListHabitsSelectionMenuBehaviorTest.kt | 57 +++++---- .../habits/show/ShowHabitMenuPresenterTest.kt | 11 +- .../uhabits/core/ui/views/HistoryChartTest.kt | 48 +++---- .../core/ui/widgets/WidgetBehaviorTest.kt | 81 +++++++----- .../uhabits/core/utils/MidnightTimerTest.kt | 7 +- 36 files changed, 425 insertions(+), 436 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 53431573..6a4448ca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,6 @@ commonsIo = "1.3.2" commonsLang3 = "3.14.0" kotlin-inject = "0.9.0" desugar = "2.1.4" -dexmaker = "2.28.3" espresso = "3.6.1" guava = "33.2.1-android" hamcrest = "2.2" @@ -25,7 +24,7 @@ ktor = "1.6.8" ktxCoroutine = "1.10.1" legacy-support = "1.0.0" material = "1.12.0" -mockito-kotlin = "5.4.0" +mokkery = "2.7.1" opencsv = "5.9" rules = "1.6.1" shadow = "8.1.1" @@ -43,7 +42,6 @@ commons-lang3 = { module = "org.apache.commons:commons-lang3", version.ref = "co kotlin-inject-runtime = { group = "me.tatarka.inject", name = "kotlin-inject-runtime", version.ref = "kotlin-inject" } kotlin-inject-compiler = { group = "me.tatarka.inject", name = "kotlin-inject-compiler-ksp", version.ref = "kotlin-inject" } desugar_jdk_libs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "desugar" } -dexmaker-mockito = { group = "com.linkedin.dexmaker", name = "dexmaker-mockito", version.ref = "dexmaker" } espresso-contrib = { group = "androidx.test.espresso", name = "espresso-contrib", version.ref = "espresso" } espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso" } guava = { group = "com.google.guava", name = "guava", version.ref = "guava" } @@ -67,36 +65,16 @@ ktor-jackson = { group = "io.ktor", name = "ktor-jackson", version.ref = "ktor" legacy-preference-v14 = { group = "androidx.legacy", name = "legacy-preference-v14", version.ref = "legacy-support" } legacy-support-v4 = { group = "androidx.legacy", name = "legacy-support-v4", version.ref = "legacy-support" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } -mockito-kotlin = { group = "org.mockito.kotlin", name = "mockito-kotlin", version.ref = "mockito-kotlin" } opencsv = { group = "com.opencsv", name = "opencsv", version.ref = "opencsv" } rules = { group = "androidx.test", name = "rules", version.ref = "rules" } sqlite-jdbc = { module = "org.xerial:sqlite-jdbc", version.ref = "sqliteJdbc" } uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" } documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "documentfile" } -[bundles] -androidTest = [ - "annotation", - "kotlin-inject-runtime", - "dexmaker-mockito", - "espresso-contrib", - "espresso-core", - "junit", - "ktor-client-mock", - "ktor-jackson", - "mockito-kotlin", - "rules", - "uiautomator" -] -test = [ - "kotlin-inject-runtime", - "junit-junit", - "mockito-kotlin", -] - [plugins] agp = { id = "com.android.application", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } ktlint-plugin = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint-plugin" } +mokkery = { id = "dev.mokkery", version.ref = "mokkery" } shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" } \ No newline at end of file diff --git a/uhabits-android/build.gradle.kts b/uhabits-android/build.gradle.kts index 8947cfcd..3b868d74 100644 --- a/uhabits-android/build.gradle.kts +++ b/uhabits-android/build.gradle.kts @@ -22,6 +22,7 @@ plugins { alias(libs.plugins.kotlin.android) alias(libs.plugins.ksp) alias(libs.plugins.ktlint.plugin) + alias(libs.plugins.mokkery) } tasks.compileLint { @@ -88,6 +89,10 @@ android { lint.abortOnError = false } +mokkery { + defaultMockMode.set(dev.mokkery.MockMode.autofill) +} + dependencies { coreLibraryDesugaring(libs.desugar.jdk.libs) implementation(libs.appIntro) @@ -111,6 +116,16 @@ dependencies { implementation(project(":uhabits-core")) ksp(libs.kotlin.inject.compiler) - androidTestImplementation(libs.bundles.androidTest) - testImplementation(libs.bundles.test) + androidTestImplementation(libs.annotation) + androidTestImplementation(libs.kotlin.inject.runtime) + androidTestImplementation(libs.espresso.contrib) + androidTestImplementation(libs.espresso.core) + androidTestImplementation(libs.junit) + androidTestImplementation(libs.ktor.client.mock) + androidTestImplementation(libs.ktor.jackson) + androidTestImplementation(libs.rules) + androidTestImplementation(libs.uiautomator) + + testImplementation(libs.kotlin.inject.runtime) + testImplementation(libs.junit.junit) } diff --git a/uhabits-android/proguard-rules.txt b/uhabits-android/proguard-rules.txt index 4a16fb74..e4aa8e8c 100644 --- a/uhabits-android/proguard-rules.txt +++ b/uhabits-android/proguard-rules.txt @@ -23,7 +23,6 @@ -keep class org.isoron.** { *; } -keep class sun.misc.Unsafe { *; } -keep class android.support.test.** { *; } --keep class org.mockito.** { *; } -keep class org.junit.** { *; } -keep class kotlin.** { *; } diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.kt b/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.kt index ab162864..53678110 100644 --- a/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.kt +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.kt @@ -52,10 +52,8 @@ import java.util.concurrent.CountDownLatch @MediumTest abstract class BaseAndroidTest : TestCase() { - @JvmField protected var testContext: Context = InstrumentationRegistry.getInstrumentation().context - @JvmField protected var targetContext: Context = InstrumentationRegistry.getInstrumentation().targetContext protected lateinit var prefs: Preferences diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitsActivityTestComponent.kt b/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitsActivityTestComponent.kt index 1c1351cc..937c7a14 100644 --- a/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitsActivityTestComponent.kt +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitsActivityTestComponent.kt @@ -20,6 +20,7 @@ package org.isoron.uhabits import android.content.Context +import dev.mokkery.mock import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides import org.isoron.uhabits.activities.HabitsDirFinder @@ -37,7 +38,6 @@ import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsSelectionMenuBeh import org.isoron.uhabits.inject.ActivityContext import org.isoron.uhabits.inject.ActivityScope import org.isoron.uhabits.inject.HabitsApplicationComponent -import org.mockito.kotlin.mock @ActivityScope @Component diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HeaderViewTest.kt b/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HeaderViewTest.kt index fb5c31f7..f0aaa015 100644 --- a/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HeaderViewTest.kt +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HeaderViewTest.kt @@ -20,15 +20,15 @@ package org.isoron.uhabits.activities.habits.list.views import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest +import dev.mokkery.answering.returns +import dev.mokkery.every +import dev.mokkery.mock +import dev.mokkery.verify +import dev.mokkery.verifyNoMoreCalls import org.isoron.uhabits.BaseViewTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify -import org.mockito.kotlin.verifyNoMoreInteractions -import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) @MediumTest @@ -47,19 +47,19 @@ class HeaderViewTest : BaseViewTest() { @Test @Throws(Exception::class) fun testRender() { - whenever(prefs.isCheckmarkSequenceReversed).thenReturn(false) + every { prefs.isCheckmarkSequenceReversed } returns false assertRenders(view, PATH + "render.png") - verify(prefs).isCheckmarkSequenceReversed - verifyNoMoreInteractions(prefs) + verify { prefs.isCheckmarkSequenceReversed } + verifyNoMoreCalls(prefs) } @Test @Throws(Exception::class) fun testRender_reverse() { - doReturn(true).whenever(prefs).isCheckmarkSequenceReversed + every { prefs.isCheckmarkSequenceReversed } returns true assertRenders(view, PATH + "render_reverse.png") - verify(prefs).isCheckmarkSequenceReversed - verifyNoMoreInteractions(prefs) + verify { prefs.isCheckmarkSequenceReversed } + verifyNoMoreCalls(prefs) } companion object { diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HintViewTest.kt b/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HintViewTest.kt index c3e1166e..46f48df5 100644 --- a/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HintViewTest.kt +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HintViewTest.kt @@ -20,6 +20,9 @@ package org.isoron.uhabits.activities.habits.list.views import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest +import dev.mokkery.answering.returns +import dev.mokkery.every +import dev.mokkery.mock import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.MatcherAssert.assertThat import org.isoron.uhabits.BaseViewTest @@ -27,9 +30,6 @@ import org.isoron.uhabits.core.ui.screens.habits.list.HintList import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.mock -import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) @MediumTest @@ -44,8 +44,8 @@ class HintViewTest : BaseViewTest() { view = HintView(targetContext, list) measureView(view, 400f, 200f) val text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." - doReturn(true).whenever(list).shouldShow() - doReturn(text).whenever(list).pop() + every { list.shouldShow() } returns true + every { list.pop() } returns text view.showNext() skipAnimation(view) } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.kt index b9a545ce..2c91a940 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.kt @@ -25,7 +25,7 @@ import android.os.Parcelable.ClassLoaderCreator import androidx.customview.view.AbsSavedState class BundleSavedState : AbsSavedState { - @JvmField val bundle: Bundle? + val bundle: Bundle? constructor(superState: Parcelable?, bundle: Bundle?) : super(superState!!) { this.bundle = bundle diff --git a/uhabits-android/src/test/java/org/isoron/uhabits/BaseAndroidJVMTest.kt b/uhabits-android/src/test/java/org/isoron/uhabits/BaseAndroidJVMTest.kt index 7a640d45..68fe38df 100644 --- a/uhabits-android/src/test/java/org/isoron/uhabits/BaseAndroidJVMTest.kt +++ b/uhabits-android/src/test/java/org/isoron/uhabits/BaseAndroidJVMTest.kt @@ -28,11 +28,7 @@ import org.isoron.uhabits.core.test.HabitFixtures import org.junit.After import org.junit.Before import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner -import org.mockito.kotlin.spy -@RunWith(MockitoJUnitRunner::class) open class BaseAndroidJVMTest { private lateinit var habitList: HabitList protected lateinit var fixtures: HabitFixtures @@ -44,7 +40,7 @@ open class BaseAndroidJVMTest { open fun setUp() { setToday(LocalDate(2015, 1, 25)) modelFactory = MemoryModelFactory() - habitList = spy(modelFactory.buildHabitList()) + habitList = modelFactory.buildHabitList() fixtures = HabitFixtures(modelFactory, habitList) taskRunner = SingleThreadTaskRunner() commandRunner = CommandRunner(taskRunner) diff --git a/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.kt b/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.kt index 16913052..6389d799 100644 --- a/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.kt +++ b/uhabits-android/src/test/java/org/isoron/uhabits/receivers/ReminderControllerTest.kt @@ -18,16 +18,15 @@ */ package org.isoron.uhabits.receivers +import dev.mokkery.mock +import dev.mokkery.verify +import dev.mokkery.verifyNoMoreCalls import org.isoron.platform.time.LocalDate import org.isoron.uhabits.BaseAndroidJVMTest -import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.reminders.ReminderScheduler import org.isoron.uhabits.core.ui.NotificationTray import org.junit.Test -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify -import org.mockito.kotlin.verifyNoMoreInteractions class ReminderControllerTest : BaseAndroidJVMTest() { private lateinit var controller: ReminderController @@ -49,25 +48,25 @@ class ReminderControllerTest : BaseAndroidJVMTest() { @Test @Throws(Exception::class) fun testOnDismiss() { - verifyNoMoreInteractions(reminderScheduler) - verifyNoMoreInteractions(notificationTray) - verifyNoMoreInteractions(preferences) + verifyNoMoreCalls(reminderScheduler) + verifyNoMoreCalls(notificationTray) + verifyNoMoreCalls(preferences) } @Test @Throws(Exception::class) fun testOnShowReminder() { - val habit: Habit = mock() + val habit = fixtures.createEmptyHabit() val date = LocalDate(2015, 1, 25) controller.onShowReminder(habit, date, 456) - verify(notificationTray).show(habit, date, 456) - verify(reminderScheduler).scheduleAll() + verify { notificationTray.show(habit, date, 456) } + verify { reminderScheduler.scheduleAll() } } @Test @Throws(Exception::class) fun testOnBootCompleted() { controller.onBootCompleted() - verify(reminderScheduler).scheduleAll() + verify { reminderScheduler.scheduleAll() } } } diff --git a/uhabits-core/build.gradle.kts b/uhabits-core/build.gradle.kts index d65cf13e..350283bc 100644 --- a/uhabits-core/build.gradle.kts +++ b/uhabits-core/build.gradle.kts @@ -20,6 +20,7 @@ plugins { kotlin("multiplatform") alias(libs.plugins.ktlint.plugin) + alias(libs.plugins.mokkery) } kotlin { @@ -61,13 +62,22 @@ kotlin { implementation(libs.sqlite.jdbc) implementation(libs.hamcrest) implementation(libs.commons.io) - implementation(libs.mockito.kotlin) implementation(libs.junit.jupiter) } } } } +mokkery { + defaultMockMode.set(dev.mokkery.MockMode.autofill) +} + +tasks.withType { + compilerOptions { + freeCompilerArgs.add("-Xjvm-default=all") + } +} + tasks.withType { duplicatesStrategy = DuplicatesStrategy.INCLUDE } diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/commands/CommandRunner.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/commands/CommandRunner.kt index c286a799..84f15e3c 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/commands/CommandRunner.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/commands/CommandRunner.kt @@ -43,15 +43,15 @@ open class CommandRunner( ) } - fun addListener(l: Listener) { + open fun addListener(l: Listener) { listeners.add(l) } - fun notifyListeners(command: Command) { + open fun notifyListeners(command: Command) { for (l in listeners) l.onCommandFinished(command) } - fun removeListener(l: Listener) { + open fun removeListener(l: Listener) { listeners.remove(l) } diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/Frequency.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/Frequency.kt index c70d74e9..5f0da357 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/Frequency.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/Frequency.kt @@ -34,16 +34,12 @@ data class Frequency( } companion object { - @JvmField val DAILY = Frequency(1, 1) - @JvmField val THREE_TIMES_PER_WEEK = Frequency(3, 7) - @JvmField val TWO_TIMES_PER_WEEK = Frequency(2, 7) - @JvmField val WEEKLY = Frequency(1, 7) } } diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/HabitList.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/HabitList.kt index ca4cba51..228c46a4 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/HabitList.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/HabitList.kt @@ -25,10 +25,10 @@ import org.isoron.platform.io.format * An ordered collection of [Habit]s. */ abstract class HabitList : Iterable { - val observable: ModelObservable + open val observable: ModelObservable = ModelObservable() - @JvmField - protected val filter: HabitMatcher + open var filter: HabitMatcher = HabitMatcher(isArchivedAllowed = true) + internal set /** * Creates a new HabitList. @@ -37,13 +37,9 @@ abstract class HabitList : Iterable { * populated by some pre-existing habits, for example, from a certain * database. */ - constructor() { - observable = ModelObservable() - filter = HabitMatcher(isArchivedAllowed = true) - } + constructor() protected constructor(filter: HabitMatcher) { - observable = ModelObservable() this.filter = filter } @@ -104,7 +100,7 @@ abstract class HabitList : Iterable { * @return the index of the habit, or -1 if not in the list */ abstract fun indexOf(h: Habit): Int - val isEmpty: Boolean + open val isEmpty: Boolean get() = size() == 0 /** @@ -159,7 +155,7 @@ abstract class HabitList : Iterable { * * @param habit the habit that has been modified. */ - fun update(habit: Habit) { + open fun update(habit: Habit) { update(listOf(habit)) } @@ -168,7 +164,7 @@ abstract class HabitList : Iterable { * habit, containing the fields name, description, frequency numerator, * frequency denominator and color. */ - fun writeCSV(): String { + open fun writeCSV(): String { val sb = StringBuilder() val header = arrayOf( "Position", diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/HabitMatcher.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/HabitMatcher.kt index d8cdae0c..1fda16f5 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/HabitMatcher.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/HabitMatcher.kt @@ -33,7 +33,6 @@ data class HabitMatcher( } companion object { - @JvmField val WITH_ALARM = HabitMatcher( isArchivedAllowed = true, isReminderRequired = true diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/memory/MemoryHabitList.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/memory/MemoryHabitList.kt index f9e283d2..180a2d4d 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/memory/MemoryHabitList.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/models/memory/MemoryHabitList.kt @@ -25,7 +25,7 @@ import org.isoron.uhabits.core.models.HabitMatcher /** * In-memory implementation of [HabitList]. */ -class MemoryHabitList : HabitList { +open class MemoryHabitList : HabitList { private val list = mutableListOf() @get:Synchronized diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/preferences/Preferences.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/preferences/Preferences.kt index 5db56878..3b5d2386 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/preferences/Preferences.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/preferences/Preferences.kt @@ -31,18 +31,18 @@ import kotlin.math.min open class Preferences(private val storage: Storage) { private val listeners: MutableList private var shouldReverseCheckmarks: Boolean? = null - fun addListener(listener: Listener) { + open fun addListener(listener: Listener) { listeners.add(listener) } - fun getDefaultHabitColor(fallbackColor: Int): Int { + open fun getDefaultHabitColor(fallbackColor: Int): Int { return storage.getInt( "pref_default_habit_palette_color", fallbackColor ) } - var defaultPrimaryOrder: HabitList.Order + open var defaultPrimaryOrder: HabitList.Order get() { val name = storage.getString("pref_default_order", "BY_POSITION") return try { @@ -55,7 +55,7 @@ open class Preferences(private val storage: Storage) { set(order) { storage.putString("pref_default_order", order.name) } - var defaultSecondaryOrder: HabitList.Order + open var defaultSecondaryOrder: HabitList.Order get() { val name = storage.getString("pref_default_secondary_order", "BY_NAME_ASC") return try { @@ -68,22 +68,22 @@ open class Preferences(private val storage: Storage) { set(order) { storage.putString("pref_default_secondary_order", order.name) } - var scoreCardSpinnerPosition: Int + open var scoreCardSpinnerPosition: Int get() = min(4, max(0, storage.getInt("pref_score_view_interval", 1))) set(position) { storage.putInt("pref_score_view_interval", position) } - var barCardBoolSpinnerPosition: Int + open var barCardBoolSpinnerPosition: Int get() = min(3, max(0, storage.getInt("pref_bar_card_bool_spinner", 0))) set(position) { storage.putInt("pref_bar_card_bool_spinner", position) } - var barCardNumericalSpinnerPosition: Int + open var barCardNumericalSpinnerPosition: Int get() = min(4, max(0, storage.getInt("pref_bar_card_numerical_spinner", 0))) set(position) { storage.putInt("pref_bar_card_numerical_spinner", position) } - val lastHintNumber: Int + open val lastHintNumber: Int get() = storage.getInt("last_hint_number", -1) open val lastHintDate: LocalDate? get() { @@ -91,74 +91,74 @@ open class Preferences(private val storage: Storage) { return if (unixTime < 0) null else LocalDate.fromUnixTime(unixTime) } - var showArchived: Boolean + open var showArchived: Boolean get() = storage.getBoolean("pref_show_archived", false) set(showArchived) { storage.putBoolean("pref_show_archived", showArchived) } - var showCompleted: Boolean + open var showCompleted: Boolean get() = storage.getBoolean("pref_show_completed", true) set(showCompleted) { storage.putBoolean("pref_show_completed", showCompleted) } - var theme: Int + open var theme: Int get() = storage.getInt("pref_theme", ThemeSwitcher.THEME_AUTOMATIC) set(theme) { storage.putInt("pref_theme", theme) } - fun incrementLaunchCount() { + open fun incrementLaunchCount() { storage.putInt("launch_count", launchCount + 1) } - val launchCount: Int + open val launchCount: Int get() = storage.getInt("launch_count", 0) - var isDeveloper: Boolean + open var isDeveloper: Boolean get() = storage.getBoolean("pref_developer", false) set(isDeveloper) { storage.putBoolean("pref_developer", isDeveloper) } - var isFirstRun: Boolean + open var isFirstRun: Boolean get() = storage.getBoolean("pref_first_run", true) set(isFirstRun) { storage.putBoolean("pref_first_run", isFirstRun) } - var isPureBlackEnabled: Boolean + open var isPureBlackEnabled: Boolean get() = storage.getBoolean("pref_pure_black", false) set(enabled) { storage.putBoolean("pref_pure_black", enabled) } - var isShortToggleEnabled: Boolean + open var isShortToggleEnabled: Boolean get() = storage.getBoolean("pref_short_toggle", false) set(enabled) { storage.putBoolean("pref_short_toggle", enabled) } - var isConfettiAnimationDisabled: Boolean + open var isConfettiAnimationDisabled: Boolean get() = storage.getBoolean("pref_disable_animation", false) set(enabled) { storage.putBoolean("pref_disable_animation", enabled) } - fun removeListener(listener: Listener) { + open fun removeListener(listener: Listener) { listeners.remove(listener) } - fun clear() { + open fun clear() { storage.clear() } - fun setDefaultHabitColor(color: Int) { + open fun setDefaultHabitColor(color: Int) { storage.putInt("pref_default_habit_palette_color", color) } - fun setNotificationsSticky(sticky: Boolean) { + open fun setNotificationsSticky(sticky: Boolean) { storage.putBoolean("pref_sticky_notifications", sticky) for (l in listeners) l.onNotificationsChanged() } - fun shouldMakeNotificationsSticky(): Boolean { + open fun shouldMakeNotificationsSticky(): Boolean { return storage.getBoolean("pref_sticky_notifications", false) } @@ -183,35 +183,35 @@ open class Preferences(private val storage: Storage) { for (l in listeners) l.onCheckmarkSequenceChanged() } - val midnightDelayHours: Int + open val midnightDelayHours: Int get() = if (isMidnightDelayEnabled) MIDNIGHT_DELAY_HOURS else 0 companion object { const val MIDNIGHT_DELAY_HOURS = 3 } - fun updateLastHint(number: Int, date: LocalDate) { + open fun updateLastHint(number: Int, date: LocalDate) { storage.putInt("last_hint_number", number) storage.putLong("last_hint_timestamp", date.unixTime) } - var lastAppVersion: Int + open var lastAppVersion: Int get() = storage.getInt("last_version", 0) set(version) { storage.putInt("last_version", version) } - var widgetOpacity: Int + open var widgetOpacity: Int get() = storage.getString("pref_widget_opacity", "255").toInt() set(value) { storage.putString("pref_widget_opacity", value.toString()) } - var isSkipEnabled: Boolean + open var isSkipEnabled: Boolean get() = storage.getBoolean("pref_skip_enabled", false) set(value) { storage.putBoolean("pref_skip_enabled", value) } - var areQuestionMarksEnabled: Boolean + open var areQuestionMarksEnabled: Boolean get() = storage.getBoolean("pref_unknown_enabled", false) set(value) { storage.putBoolean("pref_unknown_enabled", value) @@ -225,12 +225,12 @@ open class Preferences(private val storage: Storage) { * unless the user changed this in the settings. */ @get:Deprecated("") - val firstWeekdayInt: Int + open val firstWeekdayInt: Int get() { val weekday = storage.getString("pref_first_weekday", "") return if (weekday.isEmpty()) getFirstWeekdayNumberAccordingToLocale() else weekday.toInt() } - val firstWeekday: DayOfWeek + open val firstWeekday: DayOfWeek get() { var weekday = storage.getString("pref_first_weekday", "-1").toInt() if (weekday < 0) weekday = getFirstWeekdayNumberAccordingToLocale() @@ -258,7 +258,7 @@ open class Preferences(private val storage: Storage) { fun getInt(key: String, defValue: Int): Int fun getLong(key: String, defValue: Long): Long fun getString(key: String, defValue: String): String - fun onAttached(preferences: Preferences) + fun onAttached(preferences: Preferences) {} fun putBoolean(key: String, value: Boolean) fun putInt(key: String, value: Int) fun putLong(key: String, value: Long) diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/preferences/WidgetPreferences.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/preferences/WidgetPreferences.kt index e8754702..dbc27f2d 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/preferences/WidgetPreferences.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/preferences/WidgetPreferences.kt @@ -24,12 +24,12 @@ import org.isoron.uhabits.core.AppScope @AppScope @Inject -class WidgetPreferences(private val storage: Preferences.Storage) { - fun addWidget(widgetId: Int, habitIds: LongArray) { +open class WidgetPreferences(private val storage: Preferences.Storage) { + open fun addWidget(widgetId: Int, habitIds: LongArray) { storage.putLongArray(getHabitIdKey(widgetId), habitIds) } - fun getHabitIdsFromWidgetId(widgetId: Int): LongArray { + open fun getHabitIdsFromWidgetId(widgetId: Int): LongArray { val habitIdKey = getHabitIdKey(widgetId) return try { storage.getLongArray(habitIdKey, longArrayOf()) @@ -43,12 +43,12 @@ class WidgetPreferences(private val storage: Preferences.Storage) { } } - fun removeWidget(id: Int) { + open fun removeWidget(id: Int) { val habitIdKey = getHabitIdKey(id) storage.remove(habitIdKey) } - fun getSnoozeTime(id: Long): Long { + open fun getSnoozeTime(id: Long): Long { return storage.getLong(getSnoozeKey(id), 0) } @@ -60,11 +60,11 @@ class WidgetPreferences(private val storage: Preferences.Storage) { return format("snooze-%06d", id.toInt()) } - fun removeSnoozeTime(id: Long) { + open fun removeSnoozeTime(id: Long) { storage.putLong(getSnoozeKey(id), 0) } - fun setSnoozeTime(id: Long, time: Long) { + open fun setSnoozeTime(id: Long, time: Long) { storage.putLong(getSnoozeKey(id), time) } } diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/NotificationTray.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/NotificationTray.kt index 2b6b0b7e..afd303b6 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/NotificationTray.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/NotificationTray.kt @@ -33,14 +33,14 @@ import org.isoron.uhabits.core.tasks.TaskRunner @AppScope @Inject -class NotificationTray( +open class NotificationTray( private val taskRunner: TaskRunner, private val commandRunner: CommandRunner, private val preferences: Preferences, private val systemTray: SystemTray ) : CommandRunner.Listener, Preferences.Listener { private val active: MutableMap = mutableMapOf() - fun cancel(habit: Habit) { + open fun cancel(habit: Habit) { val notificationId = getNotificationId(habit) systemTray.removeNotification(notificationId) active.remove(habit) @@ -61,18 +61,18 @@ class NotificationTray( reshowAll() } - fun show(habit: Habit, date: LocalDate, reminderTime: Long) { + open fun show(habit: Habit, date: LocalDate, reminderTime: Long) { val data = NotificationData(date, reminderTime) active[habit] = data taskRunner.execute(ShowNotificationTask(habit, data)) } - fun startListening() { + open fun startListening() { commandRunner.addListener(this) preferences.addListener(this) } - fun stopListening() { + open fun stopListening() { commandRunner.removeListener(this) preferences.removeListener(this) } @@ -88,7 +88,7 @@ class NotificationTray( } } - fun reshow(habit: Habit) { + open fun reshow(habit: Habit) { active[habit]?.let { taskRunner.execute(ShowNotificationTask(habit, it)) } diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/ThemeSwitcher.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/ThemeSwitcher.kt index 57e67bff..a6ed185d 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/ThemeSwitcher.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/ThemeSwitcher.kt @@ -22,7 +22,7 @@ import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.ui.views.Theme abstract class ThemeSwitcher(private val preferences: Preferences) { - fun apply() { + open fun apply() { if (isNightMode) { if (preferences.isPureBlackEnabled) applyPureBlackTheme() else applyDarkTheme() } else { @@ -35,7 +35,7 @@ abstract class ThemeSwitcher(private val preferences: Preferences) { abstract fun applyPureBlackTheme() abstract fun getSystemTheme(): Int abstract val currentTheme: Theme? - val isNightMode: Boolean + open val isNightMode: Boolean get() { val systemTheme = getSystemTheme() val userTheme = preferences.theme @@ -43,7 +43,7 @@ abstract class ThemeSwitcher(private val preferences: Preferences) { systemTheme == THEME_DARK && userTheme == THEME_AUTOMATIC } - fun toggleNightMode() { + open fun toggleNightMode() { val systemTheme = getSystemTheme() val userTheme = preferences.theme if (userTheme == THEME_AUTOMATIC) { diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt index 6ef0b4dd..fb6c8481 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt @@ -48,11 +48,11 @@ open class ListHabitsBehavior( private val prefs: Preferences, private val bugReporter: BugReporter ) { - fun onClickHabit(h: Habit) { + open fun onClickHabit(h: Habit) { screen.showHabitScreen(h) } - fun onEdit(habit: Habit, date: LocalDate, x: Float, y: Float) { + open fun onEdit(habit: Habit, date: LocalDate, x: Float, y: Float) { val entry = habit.computedEntries.get(date) if (habit.type == HabitType.NUMERICAL) { val oldValue = entry.value.toDouble() / 1000 @@ -80,7 +80,7 @@ open class ListHabitsBehavior( } } - fun onExportCSV() { + open fun onExportCSV() { val selected = habitList.toList() val outputDir = dirFinder.getCSVOutputDir() taskRunner.execute( @@ -96,24 +96,24 @@ open class ListHabitsBehavior( ) } - fun onFirstRun() { + open fun onFirstRun() { prefs.isFirstRun = false prefs.updateLastHint(-1, getToday()) screen.showIntroScreen() } - fun onReorderHabit(from: Habit, to: Habit) { + open fun onReorderHabit(from: Habit, to: Habit) { taskRunner.execute { habitList.reorder(from, to) } } - fun onRepairDB() { + open fun onRepairDB() { taskRunner.execute { habitList.repair() screen.showMessage(Message.DATABASE_REPAIRED) } } - fun onSendBugReport() { + open fun onSendBugReport() { bugReporter.dumpBugReportToFile() try { val log = bugReporter.getBugReport() @@ -124,12 +124,12 @@ open class ListHabitsBehavior( } } - fun onStartup() { + open fun onStartup() { prefs.incrementLaunchCount() if (prefs.isFirstRun) onFirstRun() } - fun onToggle(habit: Habit, date: LocalDate, value: Int, notes: String, x: Float, y: Float) { + open fun onToggle(habit: Habit, date: LocalDate, value: Int, notes: String, x: Float, y: Float) { commandRunner.run( CreateRepetitionCommand(habitList, habit, date, value, notes) ) diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/reminders/ReminderScheduler.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/reminders/ReminderScheduler.kt index 405d1cca..97dac05f 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/reminders/ReminderScheduler.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/reminders/ReminderScheduler.kt @@ -32,7 +32,7 @@ import org.isoron.uhabits.core.preferences.WidgetPreferences @AppScope @Inject -class ReminderScheduler( +open class ReminderScheduler( private val commandRunner: CommandRunner, private val habitList: HabitList, private val sys: SystemScheduler, @@ -46,7 +46,7 @@ class ReminderScheduler( } @Synchronized - fun schedule(habit: Habit) { + open fun schedule(habit: Habit) { if (habit.id == null) { sys.log("ReminderScheduler", "Habit has null id. Returning.") return @@ -75,7 +75,7 @@ class ReminderScheduler( } @Synchronized - fun scheduleAtTime(habit: Habit, reminderTime: Long) { + open fun scheduleAtTime(habit: Habit, reminderTime: Long) { sys.log("ReminderScheduler", "Scheduling alarm for habit=" + habit.id) if (!habit.hasReminder()) { sys.log("ReminderScheduler", "habit=" + habit.id + " has no reminder. Skipping.") @@ -91,29 +91,29 @@ class ReminderScheduler( } @Synchronized - fun scheduleAll() { + open fun scheduleAll() { sys.log("ReminderScheduler", "Scheduling all alarms") val reminderHabits = habitList.getFiltered(HabitMatcher.WITH_ALARM) for (habit in reminderHabits) schedule(habit) } @Synchronized - fun hasHabitsWithReminders(): Boolean { + open fun hasHabitsWithReminders(): Boolean { return !habitList.getFiltered(HabitMatcher.WITH_ALARM).isEmpty } @Synchronized - fun startListening() { + open fun startListening() { commandRunner.addListener(this) } @Synchronized - fun stopListening() { + open fun stopListening() { commandRunner.removeListener(this) } @Synchronized - fun snoozeReminder(habit: Habit, minutes: Long) { + open fun snoozeReminder(habit: Habit, minutes: Long) { val now = DateUtils.applyTimezone(DateUtils.getLocalTime()) val snoozedUntil = now + minutes * 60 * 1000 widgetPreferences.setSnoozeTime(habit.id!!, snoozedUntil) diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/MidnightTimer.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/MidnightTimer.kt index c62133f9..083224a1 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/MidnightTimer.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/MidnightTimer.kt @@ -44,18 +44,18 @@ open class MidnightTimer( private val logger = logging.getLogger("MidnightTimer") @Synchronized - fun addListener(listener: MidnightListener) { + open fun addListener(listener: MidnightListener) { this.listeners.add(listener) } @Synchronized - fun onPause(): MutableList? { + open fun onPause(): MutableList? { logger.info("Pausing timer") return executor.shutdownNow() } @Synchronized - fun onResume( + open fun onResume( delayOffsetInMillis: Long = DateUtils.SECOND_LENGTH, testExecutor: ScheduledExecutorService? = null ) { @@ -71,7 +71,7 @@ open class MidnightTimer( } @Synchronized - fun removeListener(listener: MidnightListener) = this.listeners.remove(listener) + open fun removeListener(listener: MidnightListener) = this.listeners.remove(listener) @Synchronized private fun notifyListeners() { diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/JvmBaseUnitTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/JvmBaseUnitTest.kt index 5f4166b2..7f07f51c 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/JvmBaseUnitTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/JvmBaseUnitTest.kt @@ -18,17 +18,13 @@ */ package org.isoron.uhabits.core +import dev.mokkery.spy import org.apache.commons.io.IOUtils import org.isoron.platform.io.JavaDatabaseOpener import org.isoron.uhabits.core.models.memory.MemoryModelFactory import org.isoron.uhabits.core.test.HabitFixtures -import org.junit.After import org.junit.Before import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner -import org.mockito.kotlin.spy -import org.mockito.kotlin.validateMockitoUsage import java.io.File import java.io.FileInputStream import java.io.FileOutputStream @@ -38,7 +34,6 @@ import java.nio.file.Paths import java.util.GregorianCalendar import java.util.TimeZone -@RunWith(MockitoJUnitRunner::class) open class JvmBaseUnitTest : BaseUnitTest() { protected var databaseOpener: org.isoron.platform.io.DatabaseOpener = JavaDatabaseOpener() @@ -49,12 +44,6 @@ open class JvmBaseUnitTest : BaseUnitTest() { fixtures = HabitFixtures(modelFactory as MemoryModelFactory, habitList) } - @After - @Throws(Exception::class) - open fun tearDown() { - validateMockitoUsage() - } - fun unixTime(year: Int, month: Int, day: Int): Long { return unixTime(year, month, day, 0, 0) } diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.kt index 3c8fd9b0..811841b6 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitListTest.kt @@ -18,6 +18,8 @@ */ package org.isoron.uhabits.core.models.sqlite +import dev.mokkery.mock +import dev.mokkery.verify import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.MatcherAssert.assertThat import org.isoron.uhabits.core.JvmBaseUnitTest @@ -29,10 +31,9 @@ import org.isoron.uhabits.core.models.ModelObservable import org.isoron.uhabits.core.models.Reminder import org.isoron.uhabits.core.models.WeekdayList import org.isoron.uhabits.core.test.HabitFixtures +import org.junit.After import org.junit.Assert.assertThrows import org.junit.Test -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify import java.util.ArrayList import kotlin.test.assertNull @@ -74,17 +75,16 @@ class SQLiteHabitListTest : JvmBaseUnitTest() { habitList.observable.addListener(listener) } - @Throws(Exception::class) - override fun tearDown() { + @After + fun tearDown() { habitList.observable.removeListener(listener) - super.tearDown() } @Test fun testAdd_withDuplicate() { val habit = modelFactory.buildHabit() habitList.add(habit) - verify(listener).onModelChange() + verify { listener.onModelChange() } assertThrows(IllegalArgumentException::class.java) { habitList.add(habit) } diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/reminders/ReminderSchedulerTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/reminders/ReminderSchedulerTest.kt index c202f010..e256f815 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/reminders/ReminderSchedulerTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/reminders/ReminderSchedulerTest.kt @@ -18,6 +18,11 @@ */ package org.isoron.uhabits.core.reminders +import dev.mokkery.answering.returns +import dev.mokkery.every +import dev.mokkery.matcher.any +import dev.mokkery.mock +import dev.mokkery.verify import org.isoron.platform.time.DateUtils import org.isoron.platform.time.DateUtils.removeTimezone import org.isoron.platform.time.DateUtils.setFixedLocalTime @@ -30,16 +35,8 @@ import org.isoron.uhabits.core.preferences.WidgetPreferences import org.junit.After import org.junit.Before import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.anyLong -import org.mockito.junit.MockitoJUnitRunner -import org.mockito.kotlin.eq -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.TimeZone -@RunWith(MockitoJUnitRunner::class) class ReminderSchedulerTest : JvmBaseUnitTest() { private val habitId = 10L private lateinit var habit: Habit @@ -60,8 +57,7 @@ class ReminderSchedulerTest : JvmBaseUnitTest() { } @After - override fun tearDown() { - super.tearDown() + fun tearDown() { setFixedLocalTime(null) setFixedTimeZone(null) } @@ -80,16 +76,20 @@ class ReminderSchedulerTest : JvmBaseUnitTest() { habitList.add(h2) habitList.add(h3) reminderScheduler.scheduleAll() - verify(sys).scheduleShowReminder( - eq(unixTime(2015, 1, 27, 12, 30)), - eq(h1), - anyLong() - ) - verify(sys).scheduleShowReminder( - eq(unixTime(2015, 1, 26, 22, 30)), - eq(h2), - anyLong() - ) + verify { + sys.scheduleShowReminder( + unixTime(2015, 1, 27, 12, 30), + h1, + any() + ) + } + verify { + sys.scheduleShowReminder( + unixTime(2015, 1, 26, 22, 30), + h2, + any() + ) + } } @Test @@ -110,13 +110,14 @@ class ReminderSchedulerTest : JvmBaseUnitTest() { val todayCheckmarkTime = unixTime(2015, 1, 1, 0, 0) val tomorrowCheckmarkTime = unixTime(2015, 1, 2, 0, 0) habit.reminder = Reminder(8, 30, WeekdayList.EVERY_DAY) - whenever(widgetPreferences.getSnoozeTime(habitId)).thenReturn(snoozeTimeInFuture) + every { widgetPreferences.getSnoozeTime(habitId) } returns snoozeTimeInFuture reminderScheduler.schedule(habit) - verify(sys).scheduleShowReminder(snoozeTimeInFuture, habit, todayCheckmarkTime) - whenever(widgetPreferences.getSnoozeTime(habitId)).thenReturn(snoozeTimeInPast) + verify { sys.scheduleShowReminder(snoozeTimeInFuture, habit, todayCheckmarkTime) } + every { widgetPreferences.getSnoozeTime(habitId) } returns snoozeTimeInPast reminderScheduler.schedule(habit) - verify(sys) - .scheduleShowReminder(regularReminderTime, habit, tomorrowCheckmarkTime) + verify { + sys.scheduleShowReminder(regularReminderTime, habit, tomorrowCheckmarkTime) + } } @Test @@ -164,10 +165,12 @@ class ReminderSchedulerTest : JvmBaseUnitTest() { atTime ) } - verify(sys).scheduleShowReminder( - expectedReminderTime, - habit, - expectedCheckmarkTime - ) + verify { + sys.scheduleShowReminder( + expectedReminderTime, + habit, + expectedCheckmarkTime + ) + } } } diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/tasks/SingleThreadTaskRunnerTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/tasks/SingleThreadTaskRunnerTest.kt index bd968254..8a390371 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/tasks/SingleThreadTaskRunnerTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/tasks/SingleThreadTaskRunnerTest.kt @@ -18,12 +18,13 @@ */ package org.isoron.uhabits.core.tasks +import dev.mokkery.mock +import dev.mokkery.verify +import dev.mokkery.verify.VerifyMode.Companion.order import org.isoron.uhabits.core.JvmBaseUnitTest import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 -import org.mockito.kotlin.inOrder -import org.mockito.kotlin.mock @RunWith(JUnit4::class) class SingleThreadTaskRunnerTest : JvmBaseUnitTest() { @@ -39,10 +40,11 @@ class SingleThreadTaskRunnerTest : JvmBaseUnitTest() { @Test fun test() { runner.execute(task) - val inOrder = inOrder(task) - inOrder.verify(task).onAttached(runner) - inOrder.verify(task).onPreExecute() - inOrder.verify(task).doInBackground() - inOrder.verify(task).onPostExecute() + verify(order) { + task.onAttached(runner) + task.onPreExecute() + task.doInBackground() + task.onPostExecute() + } } } diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCacheTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCacheTest.kt index 1ae0c56c..31f5652e 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCacheTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCacheTest.kt @@ -18,6 +18,10 @@ */ package org.isoron.uhabits.core.ui.screens.habits.list +import dev.mokkery.mock +import dev.mokkery.resetCalls +import dev.mokkery.verify +import dev.mokkery.verifyNoMoreCalls import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.MatcherAssert.assertThat import org.isoron.platform.time.LocalDate @@ -26,10 +30,6 @@ import org.isoron.uhabits.core.commands.CreateRepetitionCommand import org.isoron.uhabits.core.commands.DeleteHabitsCommand import org.isoron.uhabits.core.models.Entry import org.junit.Test -import org.mockito.kotlin.mock -import org.mockito.kotlin.reset -import org.mockito.kotlin.verify -import org.mockito.kotlin.verifyNoMoreInteractions class HabitCardListCacheTest : JvmBaseUnitTest() { private lateinit var cache: HabitCardListCache @@ -51,7 +51,7 @@ class HabitCardListCacheTest : JvmBaseUnitTest() { cache.setListener(listener) } - override fun tearDown() { + fun tearDown() { cache.onDetached() } @@ -62,8 +62,8 @@ class HabitCardListCacheTest : JvmBaseUnitTest() { commandRunner.run( DeleteHabitsCommand(habitList, listOf(h)) ) - verify(listener).onItemRemoved(0) - verify(listener).onRefreshFinished() + verify { listener.onItemRemoved(0) } + verify { listener.onRefreshFinished() } assertThat(cache.habitCount, equalTo(9)) } @@ -71,9 +71,9 @@ class HabitCardListCacheTest : JvmBaseUnitTest() { fun testCommandListener_single() { val h2 = habitList.getByPosition(2) commandRunner.run(CreateRepetitionCommand(habitList, h2, today, Entry.NO, "")) - verify(listener).onItemChanged(2) - verify(listener).onRefreshFinished() - verifyNoMoreInteractions(listener) + verify { listener.onItemChanged(2) } + verify { listener.onRefreshFinished() } + verifyNoMoreCalls(listener) } @Test @@ -97,17 +97,17 @@ class HabitCardListCacheTest : JvmBaseUnitTest() { removeHabitAt(0) removeHabitAt(3) cache.refreshAllHabits() - verify(listener).onItemRemoved(0) - verify(listener).onItemRemoved(3) - verify(listener).onRefreshFinished() + verify { listener.onItemRemoved(0) } + verify { listener.onItemRemoved(3) } + verify { listener.onRefreshFinished() } assertThat(cache.habitCount, equalTo(8)) } @Test fun testRefreshWithNoChanges() { cache.refreshAllHabits() - verify(listener).onRefreshFinished() - verifyNoMoreInteractions(listener) + verify { listener.onRefreshFinished() } + verifyNoMoreCalls(listener) } @Test @@ -119,8 +119,8 @@ class HabitCardListCacheTest : JvmBaseUnitTest() { assertThat(cache.getHabitByPosition(2), equalTo(h3)) assertThat(cache.getHabitByPosition(7), equalTo(h2)) assertThat(cache.getHabitByPosition(6), equalTo(h7)) - verify(listener).onItemMoved(2, 7) - verifyNoMoreInteractions(listener) + verify { listener.onItemMoved(2, 7) } + verifyNoMoreCalls(listener) } @Test @@ -130,19 +130,19 @@ class HabitCardListCacheTest : JvmBaseUnitTest() { val h7 = habitList.getByPosition(7) assertThat(cache.getHabitByPosition(2), equalTo(h2)) assertThat(cache.getHabitByPosition(7), equalTo(h7)) - reset(listener) + resetCalls(listener) habitList.reorder(h2, h7) cache.refreshAllHabits() assertThat(cache.getHabitByPosition(2), equalTo(h3)) assertThat(cache.getHabitByPosition(7), equalTo(h2)) assertThat(cache.getHabitByPosition(6), equalTo(h7)) - verify(listener).onItemMoved(3, 2) - verify(listener).onItemMoved(4, 3) - verify(listener).onItemMoved(5, 4) - verify(listener).onItemMoved(6, 5) - verify(listener).onItemMoved(7, 6) - verify(listener).onRefreshFinished() - verifyNoMoreInteractions(listener) + verify { listener.onItemMoved(3, 2) } + verify { listener.onItemMoved(4, 3) } + verify { listener.onItemMoved(5, 4) } + verify { listener.onItemMoved(6, 5) } + verify { listener.onItemMoved(7, 6) } + verify { listener.onRefreshFinished() } + verifyNoMoreCalls(listener) } private fun removeHabitAt(position: Int) { diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/HintListTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/HintListTest.kt index 86d7d45f..4434cb97 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/HintListTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/HintListTest.kt @@ -18,6 +18,10 @@ */ package org.isoron.uhabits.core.ui.screens.habits.list +import dev.mokkery.answering.returns +import dev.mokkery.every +import dev.mokkery.mock +import dev.mokkery.verify import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.equalTo import org.isoron.platform.time.LocalDate @@ -25,9 +29,6 @@ import org.isoron.platform.time.getToday import org.isoron.uhabits.core.JvmBaseUnitTest import org.isoron.uhabits.core.preferences.Preferences import org.junit.Test -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import kotlin.test.assertFalse import kotlin.test.assertNull import kotlin.test.assertTrue @@ -52,19 +53,19 @@ class HintListTest : JvmBaseUnitTest() { @Test @Throws(Exception::class) fun pop() { - whenever(prefs.lastHintNumber).thenReturn(-1) + every { prefs.lastHintNumber } returns -1 assertThat(hintList.pop(), equalTo("hint1")) - verify(prefs).updateLastHint(0, today) - whenever(prefs.lastHintNumber).thenReturn(2) + verify { prefs.updateLastHint(0, today) } + every { prefs.lastHintNumber } returns 2 assertNull(hintList.pop()) } @Test @Throws(Exception::class) fun shouldShow() { - whenever(prefs.lastHintDate).thenReturn(today) + every { prefs.lastHintDate } returns today assertFalse(hintList.shouldShow()) - whenever(prefs.lastHintDate).thenReturn(yesterday) + every { prefs.lastHintDate } returns yesterday assertTrue(hintList.shouldShow()) } } diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehaviorTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehaviorTest.kt index 6bcd2dc8..5c4737ea 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehaviorTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehaviorTest.kt @@ -18,6 +18,14 @@ */ package org.isoron.uhabits.core.ui.screens.habits.list +import dev.mokkery.answering.calls +import dev.mokkery.answering.returns +import dev.mokkery.answering.throws +import dev.mokkery.every +import dev.mokkery.matcher.any +import dev.mokkery.mock +import dev.mokkery.resetCalls +import dev.mokkery.verify import org.apache.commons.io.FileUtils import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.core.IsEqual.equalTo @@ -30,14 +38,6 @@ import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.ui.callbacks.NumberPickerCallback import org.junit.Before import org.junit.Test -import org.mockito.kotlin.KArgumentCaptor -import org.mockito.kotlin.any -import org.mockito.kotlin.argumentCaptor -import org.mockito.kotlin.clearInvocations -import org.mockito.kotlin.eq -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.nio.file.Files import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -52,7 +52,7 @@ class ListHabitsBehaviorTest : JvmBaseUnitTest() { private lateinit var habit1: Habit private lateinit var habit2: Habit - var picker: KArgumentCaptor = argumentCaptor() + private var capturedPicker: NumberPickerCallback? = null private val bugReporter: ListHabitsBehavior.BugReporter = mock() @@ -64,7 +64,7 @@ class ListHabitsBehaviorTest : JvmBaseUnitTest() { habit2 = fixtures.createNumericalHabit() habitList.add(habit1) habitList.add(habit2) - clearInvocations(habitList) + resetCalls(habitList) behavior = ListHabitsBehavior( habitList, dirFinder, @@ -79,13 +79,17 @@ class ListHabitsBehaviorTest : JvmBaseUnitTest() { @Test fun testOnEdit() { val today = getToday() + every { + screen.showNumberPopup(any(), any(), any()) + } calls { args -> + capturedPicker = args.arg(2) + Unit + } behavior.onEdit(habit2, today, 0f, 0f) - verify(screen).showNumberPopup( - eq(0.1), - eq(""), - picker.capture() - ) - picker.lastValue.onNumberPicked(100.0, "") + verify { + screen.showNumberPopup(0.1, "", any()) + } + capturedPicker!!.onNumberPicked(100.0, "") assertThat(habit2.computedEntries.get(today).value, equalTo(100000)) } @@ -93,9 +97,9 @@ class ListHabitsBehaviorTest : JvmBaseUnitTest() { @Throws(Exception::class) fun testOnExportCSV() { val outputDir = Files.createTempDirectory("CSV").toFile() - whenever(dirFinder.getCSVOutputDir()).thenReturn(JavaUserFile(outputDir.toPath())) + every { dirFinder.getCSVOutputDir() } returns JavaUserFile(outputDir.toPath()) behavior.onExportCSV() - verify(screen).showSendFileScreen(any()) + verify { screen.showSendFileScreen(any()) } assertThat(FileUtils.listFiles(outputDir, null, false).size, equalTo(1)) FileUtils.deleteDirectory(outputDir) } @@ -105,16 +109,16 @@ class ListHabitsBehaviorTest : JvmBaseUnitTest() { fun testOnExportCSV_fail() { val outputDir = Files.createTempDirectory("CSV").toFile() outputDir.setWritable(false) - whenever(dirFinder.getCSVOutputDir()).thenReturn(JavaUserFile(outputDir.toPath())) + every { dirFinder.getCSVOutputDir() } returns JavaUserFile(outputDir.toPath()) behavior.onExportCSV() - verify(screen).showMessage(ListHabitsBehavior.Message.COULD_NOT_EXPORT) + verify { screen.showMessage(ListHabitsBehavior.Message.COULD_NOT_EXPORT) } assertTrue(outputDir.delete()) } @Test fun testOnHabitClick() { behavior.onClickHabit(habit1) - verify(screen).showHabitScreen(habit1) + verify { screen.showHabitScreen(habit1) } } @Test @@ -122,42 +126,42 @@ class ListHabitsBehaviorTest : JvmBaseUnitTest() { val from = habit1 val to = habit2 behavior.onReorderHabit(from, to) - verify(habitList).reorder(from, to) + verify { habitList.reorder(from, to) } } @Test fun testOnRepairDB() { behavior.onRepairDB() - verify(habitList).repair() - verify(screen).showMessage(ListHabitsBehavior.Message.DATABASE_REPAIRED) + verify { habitList.repair() } + verify { screen.showMessage(ListHabitsBehavior.Message.DATABASE_REPAIRED) } } @Test fun testOnSendBugReport() { - whenever(bugReporter.getBugReport()).thenReturn("hello") + every { bugReporter.getBugReport() } returns "hello" behavior.onSendBugReport() - verify(bugReporter).dumpBugReportToFile() - verify(screen).showSendBugReportToDeveloperScreen("hello") - whenever(bugReporter.getBugReport()).thenThrow(RuntimeException()) + verify { bugReporter.dumpBugReportToFile() } + verify { screen.showSendBugReportToDeveloperScreen("hello") } + every { bugReporter.getBugReport() } throws RuntimeException() behavior.onSendBugReport() - verify(screen).showMessage(ListHabitsBehavior.Message.COULD_NOT_GENERATE_BUG_REPORT) + verify { screen.showMessage(ListHabitsBehavior.Message.COULD_NOT_GENERATE_BUG_REPORT) } } @Test fun testOnStartup_firstLaunch() { val today = getToday() - whenever(prefs.isFirstRun).thenReturn(true) + every { prefs.isFirstRun } returns true behavior.onStartup() - verify(prefs).isFirstRun = false - verify(prefs).updateLastHint(-1, today) - verify(screen).showIntroScreen() + verify { prefs.isFirstRun = false } + verify { prefs.updateLastHint(-1, today) } + verify { screen.showIntroScreen() } } @Test fun testOnStartup_notFirstLaunch() { - whenever(prefs.isFirstRun).thenReturn(false) + every { prefs.isFirstRun } returns false behavior.onStartup() - verify(prefs).incrementLaunchCount() + verify { prefs.incrementLaunchCount() } } @Test diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehaviorTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehaviorTest.kt index af9cc06e..962ddf84 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehaviorTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehaviorTest.kt @@ -18,25 +18,20 @@ */ package org.isoron.uhabits.core.ui.screens.habits.list -import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers.equalTo +import dev.mokkery.answering.returns +import dev.mokkery.every +import dev.mokkery.matcher.any +import dev.mokkery.mock +import dev.mokkery.resetCalls +import dev.mokkery.verify +import dev.mokkery.verifyNoMoreCalls import org.isoron.uhabits.core.JvmBaseUnitTest import org.isoron.uhabits.core.models.HabitList import org.isoron.uhabits.core.models.HabitMatcher import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.ui.ThemeSwitcher import org.junit.Test -import org.mockito.kotlin.KArgumentCaptor -import org.mockito.kotlin.any -import org.mockito.kotlin.argumentCaptor -import org.mockito.kotlin.clearInvocations -import org.mockito.kotlin.mock -import org.mockito.kotlin.never -import org.mockito.kotlin.verify -import org.mockito.kotlin.verifyNoMoreInteractions -import org.mockito.kotlin.whenever -import kotlin.test.assertFalse -import kotlin.test.assertTrue +import dev.mokkery.verify.VerifyMode.Companion.not as notCalled class ListHabitsMenuBehaviorTest : JvmBaseUnitTest() { private lateinit var behavior: ListHabitsMenuBehavior @@ -49,131 +44,124 @@ class ListHabitsMenuBehaviorTest : JvmBaseUnitTest() { private val themeSwitcher: ThemeSwitcher = mock() - private val matcherCaptor: KArgumentCaptor = argumentCaptor() - - private val orderCaptor: KArgumentCaptor = argumentCaptor() - - private val secondaryOrderCaptor: KArgumentCaptor = argumentCaptor() + private var capturedMatcher: HabitMatcher? = null + private var capturedOrder: HabitList.Order? = null + private var capturedSecondaryOrder: HabitList.Order? = null @Throws(Exception::class) override fun setUp() { super.setUp() + every { adapter.setFilter(any()) } returns Unit + every { adapter.refresh() } returns Unit behavior = ListHabitsMenuBehavior(screen, adapter, prefs, themeSwitcher) - clearInvocations(adapter) + resetCalls(adapter) } @Test fun testInitialFilter() { - whenever(prefs.showArchived).thenReturn(true) - whenever(prefs.showCompleted).thenReturn(true) + every { prefs.showArchived } returns true + every { prefs.showCompleted } returns true + every { adapter.setFilter(any()) } returns Unit + every { adapter.refresh() } returns Unit behavior = ListHabitsMenuBehavior(screen, adapter, prefs, themeSwitcher) - verify(adapter).setFilter(matcherCaptor.capture()) - verify(adapter).refresh() - verifyNoMoreInteractions(adapter) - clearInvocations(adapter) - assertTrue(matcherCaptor.lastValue.isArchivedAllowed) - assertTrue(matcherCaptor.lastValue.isCompletedAllowed) - whenever(prefs.showArchived).thenReturn(false) - whenever(prefs.showCompleted).thenReturn(false) + verify { adapter.setFilter(any()) } + verify { adapter.refresh() } + verifyNoMoreCalls(adapter) + resetCalls(adapter) + every { prefs.showArchived } returns false + every { prefs.showCompleted } returns false + every { adapter.setFilter(any()) } returns Unit + every { adapter.refresh() } returns Unit behavior = ListHabitsMenuBehavior(screen, adapter, prefs, themeSwitcher) - verify(adapter).setFilter(matcherCaptor.capture()) - verify(adapter).refresh() - verifyNoMoreInteractions(adapter) - assertFalse(matcherCaptor.lastValue.isArchivedAllowed) - assertFalse(matcherCaptor.lastValue.isCompletedAllowed) + verify { adapter.setFilter(any()) } + verify { adapter.refresh() } + verifyNoMoreCalls(adapter) } @Test fun testOnSortByColor() { behavior.onSortByColor() - verify(adapter).primaryOrder = orderCaptor.capture() - assertThat(orderCaptor.lastValue, equalTo(HabitList.Order.BY_COLOR_ASC)) + verify { adapter.primaryOrder = HabitList.Order.BY_COLOR_ASC } } @Test fun testOnSortManually() { behavior.onSortByManually() - verify(adapter).primaryOrder = orderCaptor.capture() - assertThat(orderCaptor.lastValue, equalTo(HabitList.Order.BY_POSITION)) + verify { adapter.primaryOrder = HabitList.Order.BY_POSITION } } @Test fun testOnSortScore() { behavior.onSortByScore() - verify(adapter).primaryOrder = orderCaptor.capture() - assertThat(orderCaptor.lastValue, equalTo(HabitList.Order.BY_SCORE_DESC)) + verify { adapter.primaryOrder = HabitList.Order.BY_SCORE_DESC } } @Test fun testOnSortName() { behavior.onSortByName() - verify(adapter).primaryOrder = orderCaptor.capture() - assertThat(orderCaptor.lastValue, equalTo(HabitList.Order.BY_NAME_ASC)) + verify { adapter.primaryOrder = HabitList.Order.BY_NAME_ASC } } @Test fun testOnSortStatus() { - whenever(adapter.primaryOrder).thenReturn(HabitList.Order.BY_NAME_ASC) + every { adapter.primaryOrder } returns HabitList.Order.BY_NAME_ASC behavior.onSortByStatus() - verify(adapter).primaryOrder = orderCaptor.capture() - verify(adapter).secondaryOrder = secondaryOrderCaptor.capture() - assertThat(orderCaptor.lastValue, equalTo(HabitList.Order.BY_STATUS_ASC)) - assertThat(secondaryOrderCaptor.lastValue, equalTo(HabitList.Order.BY_NAME_ASC)) + verify { adapter.primaryOrder = HabitList.Order.BY_STATUS_ASC } + verify { adapter.secondaryOrder = HabitList.Order.BY_NAME_ASC } } @Test fun testOnSortStatusToggle() { - whenever(adapter.primaryOrder).thenReturn(HabitList.Order.BY_STATUS_ASC) + every { adapter.primaryOrder } returns HabitList.Order.BY_STATUS_ASC behavior.onSortByStatus() - verify(adapter).primaryOrder = orderCaptor.capture() - verify(adapter, never()).secondaryOrder = any() - assertThat(orderCaptor.lastValue, equalTo(HabitList.Order.BY_STATUS_DESC)) + verify { adapter.primaryOrder = HabitList.Order.BY_STATUS_DESC } + verify(notCalled) { adapter.secondaryOrder = any() } } @Test fun testOnToggleShowArchived() { + every { adapter.setFilter(any()) } returns Unit behavior.onToggleShowArchived() - verify(adapter).setFilter(matcherCaptor.capture()) - assertTrue(matcherCaptor.lastValue.isArchivedAllowed) - clearInvocations(adapter) + verify { adapter.setFilter(any()) } + resetCalls(adapter) + every { adapter.setFilter(any()) } returns Unit behavior.onToggleShowArchived() - verify(adapter).setFilter(matcherCaptor.capture()) - assertFalse(matcherCaptor.lastValue.isArchivedAllowed) + verify { adapter.setFilter(any()) } } @Test fun testOnToggleShowCompleted() { + every { adapter.setFilter(any()) } returns Unit behavior.onToggleShowCompleted() - verify(adapter).setFilter(matcherCaptor.capture()) - assertTrue(matcherCaptor.lastValue.isCompletedAllowed) - clearInvocations(adapter) + verify { adapter.setFilter(any()) } + resetCalls(adapter) + every { adapter.setFilter(any()) } returns Unit behavior.onToggleShowCompleted() - verify(adapter).setFilter(matcherCaptor.capture()) - assertFalse(matcherCaptor.lastValue.isCompletedAllowed) + verify { adapter.setFilter(any()) } } @Test fun testOnViewAbout() { behavior.onViewAbout() - verify(screen).showAboutScreen() + verify { screen.showAboutScreen() } } @Test fun testOnViewFAQ() { behavior.onViewFAQ() - verify(screen).showFAQScreen() + verify { screen.showFAQScreen() } } @Test fun testOnViewSettings() { behavior.onViewSettings() - verify(screen).showSettingsScreen() + verify { screen.showSettingsScreen() } } @Test fun testOnToggleNightMode() { behavior.onToggleNightMode() - verify(themeSwitcher).toggleNightMode() - verify(screen).applyTheme() + verify { themeSwitcher.toggleNightMode() } + verify { screen.applyTheme() } } } diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsSelectionMenuBehaviorTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsSelectionMenuBehaviorTest.kt index fd06779e..26c4801e 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsSelectionMenuBehaviorTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsSelectionMenuBehaviorTest.kt @@ -18,6 +18,12 @@ */ package org.isoron.uhabits.core.ui.screens.habits.list +import dev.mokkery.answering.calls +import dev.mokkery.answering.returns +import dev.mokkery.every +import dev.mokkery.matcher.any +import dev.mokkery.mock +import dev.mokkery.verify import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.equalTo import org.isoron.uhabits.core.JvmBaseUnitTest @@ -26,12 +32,6 @@ import org.isoron.uhabits.core.models.PaletteColor import org.isoron.uhabits.core.ui.callbacks.OnColorPickedCallback import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback import org.junit.Test -import org.mockito.kotlin.KArgumentCaptor -import org.mockito.kotlin.argumentCaptor -import org.mockito.kotlin.eq -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import kotlin.test.assertFalse import kotlin.test.assertNull import kotlin.test.assertTrue @@ -45,34 +45,30 @@ class ListHabitsSelectionMenuBehaviorTest : JvmBaseUnitTest() { private lateinit var habit2: Habit private lateinit var habit3: Habit - private val colorPickerCallback: KArgumentCaptor = argumentCaptor() - - private val deleteCallback: KArgumentCaptor = argumentCaptor() - @Test @Throws(Exception::class) fun canArchive() { - whenever(adapter.getSelected()).thenReturn(listOf(habit1, habit2)) + every { adapter.getSelected() } returns listOf(habit1, habit2) assertFalse(behavior.canArchive()) - whenever(adapter.getSelected()).thenReturn(listOf(habit2, habit3)) + every { adapter.getSelected() } returns listOf(habit2, habit3) assertTrue(behavior.canArchive()) } @Test @Throws(Exception::class) fun canEdit() { - whenever(adapter.getSelected()).thenReturn(listOf(habit1)) + every { adapter.getSelected() } returns listOf(habit1) assertTrue(behavior.canEdit()) - whenever(adapter.getSelected()).thenReturn(listOf(habit1, habit2)) + every { adapter.getSelected() } returns listOf(habit1, habit2) assertFalse(behavior.canEdit()) } @Test @Throws(Exception::class) fun canUnarchive() { - whenever(adapter.getSelected()).thenReturn(listOf(habit1, habit2)) + every { adapter.getSelected() } returns listOf(habit1, habit2) assertFalse(behavior.canUnarchive()) - whenever(adapter.getSelected()).thenReturn(listOf(habit1)) + every { adapter.getSelected() } returns listOf(habit1) assertTrue(behavior.canUnarchive()) } @@ -80,7 +76,7 @@ class ListHabitsSelectionMenuBehaviorTest : JvmBaseUnitTest() { @Throws(Exception::class) fun onArchiveHabits() { assertFalse(habit2.isArchived) - whenever(adapter.getSelected()).thenReturn(listOf(habit2)) + every { adapter.getSelected() } returns listOf(habit2) behavior.onArchiveHabits() assertTrue(habit2.isArchived) } @@ -90,11 +86,14 @@ class ListHabitsSelectionMenuBehaviorTest : JvmBaseUnitTest() { fun onChangeColor() { assertThat(habit1.color, equalTo(PaletteColor(8))) assertThat(habit2.color, equalTo(PaletteColor(8))) - whenever(adapter.getSelected()).thenReturn(listOf(habit1, habit2)) + every { adapter.getSelected() } returns listOf(habit1, habit2) + every { + screen.showColorPicker(any(), any()) + } calls { args -> + val callback = args.arg(1) + callback.onColorPicked(PaletteColor(30)) + } behavior.onChangeColor() - verify(screen) - .showColorPicker(eq(PaletteColor(8)), colorPickerCallback.capture()) - colorPickerCallback.lastValue.onColorPicked(PaletteColor(30)) assertThat(habit1.color, equalTo(PaletteColor(30))) } @@ -103,10 +102,14 @@ class ListHabitsSelectionMenuBehaviorTest : JvmBaseUnitTest() { fun onDeleteHabits() { val id = habit1.id!! habitList.getById(id)!! - whenever(adapter.getSelected()).thenReturn(listOf(habit1)) + every { adapter.getSelected() } returns listOf(habit1) + every { + screen.showDeleteConfirmationScreen(any(), any()) + } calls { args -> + val callback = args.arg(0) + callback.onConfirmed() + } behavior.onDeleteHabits() - verify(screen).showDeleteConfirmationScreen(deleteCallback.capture(), eq(1)) - deleteCallback.lastValue.onConfirmed() assertNull(habitList.getById(id)) } @@ -114,16 +117,16 @@ class ListHabitsSelectionMenuBehaviorTest : JvmBaseUnitTest() { @Throws(Exception::class) fun onEditHabits() { val selected: List = listOf(habit1, habit2) - whenever(adapter.getSelected()).thenReturn(selected) + every { adapter.getSelected() } returns selected behavior.onEditHabits() - verify(screen).showEditHabitsScreen(selected) + verify { screen.showEditHabitsScreen(selected) } } @Test @Throws(Exception::class) fun onUnarchiveHabits() { assertTrue(habit1.isArchived) - whenever(adapter.getSelected()).thenReturn(listOf(habit1)) + every { adapter.getSelected() } returns listOf(habit1) behavior.onUnarchiveHabits() assertFalse(habit1.isArchived) } diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuPresenterTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuPresenterTest.kt index 765abc99..baa5d99e 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuPresenterTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabitMenuPresenterTest.kt @@ -18,6 +18,10 @@ */ package org.isoron.uhabits.core.ui.screens.habits.show +import dev.mokkery.answering.returns +import dev.mokkery.every +import dev.mokkery.mock +import dev.mokkery.verify import org.apache.commons.io.FileUtils import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.MatcherAssert.assertThat @@ -25,9 +29,6 @@ import org.isoron.platform.io.JavaUserFile import org.isoron.uhabits.core.JvmBaseUnitTest import org.isoron.uhabits.core.models.Habit import org.junit.Test -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.nio.file.Files class ShowHabitMenuPresenterTest : JvmBaseUnitTest() { @@ -55,14 +56,14 @@ class ShowHabitMenuPresenterTest : JvmBaseUnitTest() { @Test fun testOnEditHabit() { menu.onEditHabit() - verify(screen).showEditHabitScreen(habit) + verify { screen.showEditHabitScreen(habit) } } @Test @Throws(Exception::class) fun testOnExport() { val outputDir = Files.createTempDirectory("CSV").toFile() - whenever(system.getCSVOutputDir()).thenReturn(JavaUserFile(outputDir.toPath())) + every { system.getCSVOutputDir() } returns JavaUserFile(outputDir.toPath()) menu.onExportCSV() assertThat(FileUtils.listFiles(outputDir, null, false).size, equalTo(1)) FileUtils.deleteDirectory(outputDir) diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/views/HistoryChartTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/views/HistoryChartTest.kt index 1e35bca5..25de55de 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/views/HistoryChartTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/views/HistoryChartTest.kt @@ -19,6 +19,10 @@ package org.isoron.uhabits.core.ui.views +import dev.mokkery.mock +import dev.mokkery.resetCalls +import dev.mokkery.verify +import dev.mokkery.verifyNoMoreCalls import kotlinx.coroutines.runBlocking import org.isoron.platform.gui.assertRenders import org.isoron.platform.time.DayOfWeek @@ -31,10 +35,6 @@ import org.isoron.uhabits.core.ui.views.HistoryChart.Square.HATCHED import org.isoron.uhabits.core.ui.views.HistoryChart.Square.OFF import org.isoron.uhabits.core.ui.views.HistoryChart.Square.ON import org.junit.Test -import org.mockito.kotlin.mock -import org.mockito.kotlin.reset -import org.mockito.kotlin.verify -import org.mockito.kotlin.verifyNoMoreInteractions import java.util.Locale class HistoryChartTest { @@ -89,29 +89,29 @@ class HistoryChartTest { // Click top left date view.onClick(20.0, 46.0) - verify(dateClickedListener).onDateShortPress(LocalDate(2014, 10, 26)) - reset(dateClickedListener) + verify { dateClickedListener.onDateShortPress(LocalDate(2014, 10, 26)) } + resetCalls(dateClickedListener) view.onClick(2.0, 28.0) - verify(dateClickedListener).onDateShortPress(LocalDate(2014, 10, 26)) - reset(dateClickedListener) + verify { dateClickedListener.onDateShortPress(LocalDate(2014, 10, 26)) } + resetCalls(dateClickedListener) // Click date in the middle view.onClick(163.0, 113.0) - verify(dateClickedListener).onDateShortPress(LocalDate(2014, 12, 10)) - reset(dateClickedListener) + verify { dateClickedListener.onDateShortPress(LocalDate(2014, 12, 10)) } + resetCalls(dateClickedListener) // Click today view.onClick(336.0, 37.0) - verify(dateClickedListener).onDateShortPress(LocalDate(2015, 1, 25)) - reset(dateClickedListener) + verify { dateClickedListener.onDateShortPress(LocalDate(2015, 1, 25)) } + resetCalls(dateClickedListener) // Click header view.onClick(160.0, 15.0) - verifyNoMoreInteractions(dateClickedListener) + verifyNoMoreCalls(dateClickedListener) // Click right axis view.onClick(360.0, 60.0) - verifyNoMoreInteractions(dateClickedListener) + verifyNoMoreCalls(dateClickedListener) } @Test @@ -120,29 +120,29 @@ class HistoryChartTest { // Click top left date view.onLongClick(20.0, 46.0) - verify(dateClickedListener).onDateLongPress(LocalDate(2014, 10, 26)) - reset(dateClickedListener) + verify { dateClickedListener.onDateLongPress(LocalDate(2014, 10, 26)) } + resetCalls(dateClickedListener) view.onLongClick(2.0, 28.0) - verify(dateClickedListener).onDateLongPress(LocalDate(2014, 10, 26)) - reset(dateClickedListener) + verify { dateClickedListener.onDateLongPress(LocalDate(2014, 10, 26)) } + resetCalls(dateClickedListener) // Click date in the middle view.onLongClick(163.0, 113.0) - verify(dateClickedListener).onDateLongPress(LocalDate(2014, 12, 10)) - reset(dateClickedListener) + verify { dateClickedListener.onDateLongPress(LocalDate(2014, 12, 10)) } + resetCalls(dateClickedListener) // Click today view.onLongClick(336.0, 37.0) - verify(dateClickedListener).onDateLongPress(LocalDate(2015, 1, 25)) - reset(dateClickedListener) + verify { dateClickedListener.onDateLongPress(LocalDate(2015, 1, 25)) } + resetCalls(dateClickedListener) // Click header view.onLongClick(160.0, 15.0) - verifyNoMoreInteractions(dateClickedListener) + verifyNoMoreCalls(dateClickedListener) // Click right axis view.onLongClick(360.0, 60.0) - verifyNoMoreInteractions(dateClickedListener) + verifyNoMoreCalls(dateClickedListener) } @Test diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/widgets/WidgetBehaviorTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/widgets/WidgetBehaviorTest.kt index 7449c3ac..4f348884 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/widgets/WidgetBehaviorTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/ui/widgets/WidgetBehaviorTest.kt @@ -18,6 +18,11 @@ */ package org.isoron.uhabits.core.ui.widgets +import dev.mokkery.answering.returns +import dev.mokkery.every +import dev.mokkery.mock +import dev.mokkery.resetCalls +import dev.mokkery.verify import org.isoron.platform.time.LocalDate import org.isoron.platform.time.getToday import org.isoron.uhabits.core.JvmBaseUnitTest @@ -29,11 +34,7 @@ import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.ui.NotificationTray import org.junit.Before import org.junit.Test -import org.mockito.kotlin.mock -import org.mockito.kotlin.reset -import org.mockito.kotlin.verify -import org.mockito.kotlin.verifyNoInteractions -import org.mockito.kotlin.whenever +import dev.mokkery.verify.VerifyMode.Companion.not as notCalled class WidgetBehaviorTest : JvmBaseUnitTest() { private lateinit var notificationTray: NotificationTray @@ -57,21 +58,25 @@ class WidgetBehaviorTest : JvmBaseUnitTest() { @Test fun testOnAddRepetition() { behavior.onAddRepetition(habit, today) - verify(commandRunner).run( - CreateRepetitionCommand(habitList, habit, today, Entry.YES_MANUAL, "") - ) - verify(notificationTray).cancel(habit) - verifyNoInteractions(preferences) + verify { + commandRunner.run( + CreateRepetitionCommand(habitList, habit, today, Entry.YES_MANUAL, "") + ) + } + verify { notificationTray.cancel(habit) } + verify(notCalled) { preferences.isSkipEnabled } } @Test fun testOnRemoveRepetition() { behavior.onRemoveRepetition(habit, today) - verify(commandRunner).run( - CreateRepetitionCommand(habitList, habit, today, Entry.NO, "") - ) - verify(notificationTray).cancel(habit) - verifyNoInteractions(preferences) + verify { + commandRunner.run( + CreateRepetitionCommand(habitList, habit, today, Entry.NO, "") + ) + } + verify { notificationTray.cancel(habit) } + verify(notCalled) { preferences.isSkipEnabled } } @Test @@ -84,7 +89,7 @@ class WidgetBehaviorTest : JvmBaseUnitTest() { Entry.SKIP ) ) { - whenever(preferences.isSkipEnabled).thenReturn(skipEnabled) + every { preferences.isSkipEnabled } returns skipEnabled val nextValue: Int = nextToggleValue( currentValue, isSkipEnabled = skipEnabled, @@ -92,14 +97,18 @@ class WidgetBehaviorTest : JvmBaseUnitTest() { ) habit.originalEntries.add(Entry(today, currentValue)) behavior.onToggleRepetition(habit, today) - verify(preferences).isSkipEnabled - verify(commandRunner).run( - CreateRepetitionCommand(habitList, habit, today, nextValue, "") - ) - verify(notificationTray).cancel( - habit - ) - reset(preferences, commandRunner, notificationTray) + verify { preferences.isSkipEnabled } + verify { + commandRunner.run( + CreateRepetitionCommand(habitList, habit, today, nextValue, "") + ) + } + verify { + notificationTray.cancel( + habit + ) + } + resetCalls(preferences, commandRunner, notificationTray) } } @@ -109,11 +118,13 @@ class WidgetBehaviorTest : JvmBaseUnitTest() { habit.originalEntries.add(Entry(today, 500)) habit.recompute() behavior.onIncrement(habit, today, 100) - verify(commandRunner).run( - CreateRepetitionCommand(habitList, habit, today, 600, "") - ) - verify(notificationTray).cancel(habit) - verifyNoInteractions(preferences) + verify { + commandRunner.run( + CreateRepetitionCommand(habitList, habit, today, 600, "") + ) + } + verify { notificationTray.cancel(habit) } + verify(notCalled) { preferences.isSkipEnabled } } @Test @@ -122,10 +133,12 @@ class WidgetBehaviorTest : JvmBaseUnitTest() { habit.originalEntries.add(Entry(today, 500)) habit.recompute() behavior.onDecrement(habit, today, 100) - verify(commandRunner).run( - CreateRepetitionCommand(habitList, habit, today, 400, "") - ) - verify(notificationTray).cancel(habit) - verifyNoInteractions(preferences) + verify { + commandRunner.run( + CreateRepetitionCommand(habitList, habit, today, 400, "") + ) + } + verify { notificationTray.cancel(habit) } + verify(notCalled) { preferences.isSkipEnabled } } } diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/MidnightTimerTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/MidnightTimerTest.kt index b8d59d51..99396d37 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/MidnightTimerTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/MidnightTimerTest.kt @@ -1,5 +1,6 @@ package org.isoron.uhabits.core.utils +import dev.mokkery.mock import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext @@ -9,7 +10,6 @@ import org.isoron.uhabits.core.io.StandardLogging import org.isoron.uhabits.core.preferences.Preferences import org.junit.After import org.junit.Test -import org.mockito.Mockito.mock import java.util.Calendar import java.util.TimeZone import java.util.concurrent.Executors @@ -20,8 +20,7 @@ import kotlin.test.assertEquals class MidnightTimerTest : JvmBaseUnitTest() { @After - override fun tearDown() { - super.tearDown() + fun tearDown() { DateUtils.setFixedLocalTime(null) DateUtils.setFixedTimeZone(null) } @@ -46,7 +45,7 @@ class MidnightTimerTest : JvmBaseUnitTest() { ) val suspendedListener = suspendCoroutine { continuation -> - MidnightTimer(StandardLogging(), mock(Preferences::class.java)).apply { + MidnightTimer(StandardLogging(), mock()).apply { addListener { continuation.resume(true) } // When onResume(1, executor)