From b2951a3475e1484fdf9d9399dd38e952bebd3c9f Mon Sep 17 00:00:00 2001 From: sgallese Date: Sun, 19 Sep 2021 06:48:01 -0700 Subject: [PATCH 1/4] Place MidnightTimer under test --- .../uhabits/core/utils/MidnightTimer.kt | 6 +-- .../org/isoron/uhabits/core/BaseUnitTest.kt | 8 ++-- .../core/reminders/ReminderSchedulerTest.kt | 2 +- .../uhabits/core/utils/MidnightTimerTest.kt | 45 +++++++++++++++++++ 4 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/MidnightTimerTest.kt 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 e0661ba77..3dbba72c5 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 @@ -39,11 +39,11 @@ open class MidnightTimer @Inject constructor() { @Synchronized fun onPause(): MutableList? = executor.shutdownNow() - @Synchronized fun onResume() { - executor = Executors.newSingleThreadScheduledExecutor() + @Synchronized fun onResume(delayOffsetInMillis: Long = DateUtils.MINUTE_LENGTH, testExecutor: ScheduledExecutorService? = null) { + executor = testExecutor ?: Executors.newSingleThreadScheduledExecutor() executor.scheduleAtFixedRate( { notifyListeners() }, - DateUtils.millisecondsUntilTomorrowWithOffset() + 1000, + DateUtils.millisecondsUntilTomorrowWithOffset() + delayOffsetInMillis, DateUtils.DAY_LENGTH, TimeUnit.MILLISECONDS ) diff --git a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/BaseUnitTest.kt b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/BaseUnitTest.kt index f97991b6d..4079600ab 100644 --- a/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/BaseUnitTest.kt +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/BaseUnitTest.kt @@ -95,15 +95,13 @@ open class BaseUnitTest { } fun unixTime(year: Int, month: Int, day: Int): Long { - val cal = getStartOfTodayCalendar() - cal.set(year, month, day, 0, 0, 0) - return cal.timeInMillis + return unixTime(year, month, day, 0, 0) } - open fun unixTime(year: Int, month: Int, day: Int, hour: Int, minute: Int): Long { + open fun unixTime(year: Int, month: Int, day: Int, hour: Int, minute: Int, milliseconds: Long = 0): Long { val cal = getStartOfTodayCalendar() cal.set(year, month, day, hour, minute) - return cal.timeInMillis + return cal.timeInMillis + milliseconds } fun timestamp(year: Int, month: Int, day: Int): Timestamp { 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 d354e81f0..cf81fbe9a 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 @@ -138,7 +138,7 @@ class ReminderSchedulerTest : BaseUnitTest() { reminderScheduler.schedule(habit) } - override fun unixTime(year: Int, month: Int, day: Int, hour: Int, minute: Int): Long { + override fun unixTime(year: Int, month: Int, day: Int, hour: Int, minute: Int, milliseconds: Long): Long { val cal: Calendar = getStartOfTodayCalendar() cal[year, month, day, hour] = minute return cal.timeInMillis 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 new file mode 100644 index 000000000..80a90edb9 --- /dev/null +++ b/uhabits-core/src/jvmTest/java/org/isoron/uhabits/core/utils/MidnightTimerTest.kt @@ -0,0 +1,45 @@ +package org.isoron.uhabits.core.utils + +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import org.isoron.uhabits.core.BaseUnitTest +import org.junit.Test +import java.util.Calendar +import java.util.TimeZone +import java.util.concurrent.Executors +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine +import kotlin.test.assertEquals + +class MidnightTimerTest : BaseUnitTest() { + + @Test + fun testMidnightTimer_notifyListener_atMidnight() = runBlocking { + // Given + val executor = Executors.newSingleThreadScheduledExecutor() + val dispatcher = executor.asCoroutineDispatcher() + + withContext(dispatcher) { + DateUtils.setFixedTimeZone(TimeZone.getTimeZone("GMT")) + DateUtils.setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 1, 23, 59, DateUtils.MINUTE_LENGTH - 1)) + + val suspendedListener = suspendCoroutine { continuation -> + val listener = object : MidnightTimer.MidnightListener { + override fun atMidnight() { + continuation.resume(true) + } + } + + MidnightTimer().apply { + addListener(listener) + // When + onResume(1, executor) + } + } + + // Then + assertEquals(true, suspendedListener) + } + } +} From 3e6a9181d6d836a85984bf166a57e676438ef3f0 Mon Sep 17 00:00:00 2001 From: sgallese Date: Tue, 21 Sep 2021 19:16:00 -0700 Subject: [PATCH 2/4] Code review- keep timer offset to one second --- .../java/org/isoron/uhabits/core/utils/DateUtils.kt | 7 ++++++- .../java/org/isoron/uhabits/core/utils/MidnightTimer.kt | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt index fd7929b87..8af545504 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/utils/DateUtils.kt @@ -36,10 +36,15 @@ abstract class DateUtils { private var startDayHourOffset: Int = 0 private var startDayMinuteOffset: Int = 0 + /** + * Number of milliseconds in one second. + */ + const val SECOND_LENGTH: Long = 1000 + /** * Number of milliseconds in one minute. */ - const val MINUTE_LENGTH: Long = 60 * 1000 + const val MINUTE_LENGTH: Long = 60 * SECOND_LENGTH /** * Number of milliseconds in one hour. 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 3dbba72c5..5ae0b6ad7 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 @@ -39,7 +39,7 @@ open class MidnightTimer @Inject constructor() { @Synchronized fun onPause(): MutableList? = executor.shutdownNow() - @Synchronized fun onResume(delayOffsetInMillis: Long = DateUtils.MINUTE_LENGTH, testExecutor: ScheduledExecutorService? = null) { + @Synchronized fun onResume(delayOffsetInMillis: Long = DateUtils.SECOND_LENGTH, testExecutor: ScheduledExecutorService? = null) { executor = testExecutor ?: Executors.newSingleThreadScheduledExecutor() executor.scheduleAtFixedRate( { notifyListeners() }, From a29943e783b0f727b19414d69e559451694bbc52 Mon Sep 17 00:00:00 2001 From: sgallese Date: Tue, 21 Sep 2021 19:20:54 -0700 Subject: [PATCH 3/4] Simplify midnight timer interface for testing --- .../java/org/isoron/uhabits/core/utils/MidnightTimer.kt | 2 +- .../org/isoron/uhabits/core/utils/MidnightTimerTest.kt | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) 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 5ae0b6ad7..49c088ca7 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 @@ -57,7 +57,7 @@ open class MidnightTimer @Inject constructor() { } } - interface MidnightListener { + fun interface MidnightListener { fun atMidnight() } } 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 80a90edb9..6e80ca0b4 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 @@ -25,14 +25,8 @@ class MidnightTimerTest : BaseUnitTest() { DateUtils.setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 1, 23, 59, DateUtils.MINUTE_LENGTH - 1)) val suspendedListener = suspendCoroutine { continuation -> - val listener = object : MidnightTimer.MidnightListener { - override fun atMidnight() { - continuation.resume(true) - } - } - MidnightTimer().apply { - addListener(listener) + addListener { continuation.resume(true) } // When onResume(1, executor) } From 508200abeb70266bd8286dbc543de64b73a1c2fc Mon Sep 17 00:00:00 2001 From: sgallese Date: Tue, 21 Sep 2021 19:25:21 -0700 Subject: [PATCH 4/4] Format classes with ktlint --- .../isoron/uhabits/core/utils/MidnightTimer.kt | 18 +++++++++++++----- .../uhabits/core/utils/MidnightTimerTest.kt | 11 ++++++++++- 2 files changed, 23 insertions(+), 6 deletions(-) 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 49c088ca7..903099293 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 @@ -33,13 +33,19 @@ open class MidnightTimer @Inject constructor() { private val listeners: MutableList = LinkedList() private lateinit var executor: ScheduledExecutorService - @Synchronized fun addListener(listener: MidnightListener) { + @Synchronized + fun addListener(listener: MidnightListener) { this.listeners.add(listener) } - @Synchronized fun onPause(): MutableList? = executor.shutdownNow() + @Synchronized + fun onPause(): MutableList? = executor.shutdownNow() - @Synchronized fun onResume(delayOffsetInMillis: Long = DateUtils.SECOND_LENGTH, testExecutor: ScheduledExecutorService? = null) { + @Synchronized + fun onResume( + delayOffsetInMillis: Long = DateUtils.SECOND_LENGTH, + testExecutor: ScheduledExecutorService? = null + ) { executor = testExecutor ?: Executors.newSingleThreadScheduledExecutor() executor.scheduleAtFixedRate( { notifyListeners() }, @@ -49,9 +55,11 @@ open class MidnightTimer @Inject constructor() { ) } - @Synchronized fun removeListener(listener: MidnightListener) = this.listeners.remove(listener) + @Synchronized + fun removeListener(listener: MidnightListener) = this.listeners.remove(listener) - @Synchronized private fun notifyListeners() { + @Synchronized + private fun notifyListeners() { for (l in listeners) { l.atMidnight() } 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 6e80ca0b4..b92507da0 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 @@ -22,7 +22,16 @@ class MidnightTimerTest : BaseUnitTest() { withContext(dispatcher) { DateUtils.setFixedTimeZone(TimeZone.getTimeZone("GMT")) - DateUtils.setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 1, 23, 59, DateUtils.MINUTE_LENGTH - 1)) + DateUtils.setFixedLocalTime( + unixTime( + 2017, + Calendar.JANUARY, + 1, + 23, + 59, + DateUtils.MINUTE_LENGTH - 1 + ) + ) val suspendedListener = suspendCoroutine { continuation -> MidnightTimer().apply {