diff --git a/app/src/androidTest/java/org/tasks/notifications/ThrottleTest.kt b/app/src/androidTest/java/org/tasks/notifications/ThrottleTest.kt deleted file mode 100644 index 098815a0e..000000000 --- a/app/src/androidTest/java/org/tasks/notifications/ThrottleTest.kt +++ /dev/null @@ -1,72 +0,0 @@ -package org.tasks.notifications - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito -import org.tasks.Freeze -import org.tasks.time.DateTimeUtils - -@RunWith(AndroidJUnit4::class) -class ThrottleTest { - private lateinit var sleeper: Throttle.Sleeper - private lateinit var throttle: Throttle - - @Before - fun setUp() { - sleeper = Mockito.mock(Throttle.Sleeper::class.java) - throttle = Throttle(3, sleeper) - } - - @After - fun tearDown() { - Mockito.verifyNoMoreInteractions(sleeper) - } - - @Test - fun dontThrottle() { - val now = DateTimeUtils.currentTimeMillis() - runAt(now) - runAt(now) - runAt(now) - runAt(now + 1000) - } - - @Test - fun throttleForOneMillisecond() { - val now = DateTimeUtils.currentTimeMillis() - runAt(now) - runAt(now) - runAt(now) - runAt(now + 999) - Mockito.verify(sleeper).sleep(1) - } - - @Test - fun throttleForOneSecond() { - val now = DateTimeUtils.currentTimeMillis() - runAt(now) - runAt(now) - runAt(now) - runAt(now) - Mockito.verify(sleeper).sleep(1000) - } - - @Test - fun throttleMultiple() { - val now = DateTimeUtils.currentTimeMillis() - runAt(now) - runAt(now + 200) - runAt(now + 600) - runAt(now + 700) - Mockito.verify(sleeper).sleep(300) - runAt(now + 750) - Mockito.verify(sleeper).sleep(450) - } - - private fun runAt(millis: Long) { - Freeze.freezeAt(millis) { throttle.run {} } - } -} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/notifications/Throttle.java b/app/src/main/java/org/tasks/notifications/Throttle.java deleted file mode 100644 index 29acd0f27..000000000 --- a/app/src/main/java/org/tasks/notifications/Throttle.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.tasks.notifications; - -import static com.todoroo.andlib.utility.AndroidUtilities.assertNotMainThread; -import static org.tasks.time.DateTimeUtils.currentTimeMillis; - -class Throttle { - private final long[] throttle; - private final Sleeper sleeper; - private int oldest = 0; - - Throttle(int ratePerSecond) { - this(ratePerSecond, Throttle::sleep); - } - - Throttle(int ratePerSecond, Sleeper sleeper) { - this.sleeper = sleeper; - throttle = new long[ratePerSecond]; - } - - private static void sleep(long millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException ignored) { - } - } - - synchronized void run(Runnable runnable) { - assertNotMainThread(); - long sleep = throttle[oldest] - (currentTimeMillis() - 1000); - if (sleep > 0) { - sleeper.sleep(sleep); - } - runnable.run(); - throttle[oldest] = currentTimeMillis(); - oldest = (oldest + 1) % throttle.length; - } - - public interface Sleeper { - void sleep(long millis); - } -} diff --git a/app/src/main/java/org/tasks/notifications/Throttle.kt b/app/src/main/java/org/tasks/notifications/Throttle.kt new file mode 100644 index 000000000..34837e0a2 --- /dev/null +++ b/app/src/main/java/org/tasks/notifications/Throttle.kt @@ -0,0 +1,22 @@ +package org.tasks.notifications + +import kotlinx.coroutines.delay +import org.tasks.time.DateTimeUtils + +internal class Throttle constructor( + ratePerSecond: Int, + private val sleeper: suspend (Long) -> Unit = { delay(it) }) { + private val throttle: LongArray = LongArray(ratePerSecond) + private var oldest = 0 + + @Synchronized + suspend fun run(runnable: suspend () -> Unit) { + val sleep = throttle[oldest] - (DateTimeUtils.currentTimeMillis() - 1000) + if (sleep > 0) { + sleeper.invoke(sleep) + } + runnable.invoke() + throttle[oldest] = DateTimeUtils.currentTimeMillis() + oldest = (oldest + 1) % throttle.size + } +} \ No newline at end of file diff --git a/app/src/test/java/org/tasks/notifications/ThrottleTest.kt b/app/src/test/java/org/tasks/notifications/ThrottleTest.kt new file mode 100644 index 000000000..ce8f72527 --- /dev/null +++ b/app/src/test/java/org/tasks/notifications/ThrottleTest.kt @@ -0,0 +1,68 @@ +package org.tasks.notifications + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runBlockingTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.tasks.SuspendFreeze +import org.tasks.time.DateTimeUtils + +@ExperimentalCoroutinesApi +class ThrottleTest { + private lateinit var sleep: ArrayList + private lateinit var throttle: Throttle + + @Before + fun setUp() { + sleep = ArrayList() + throttle = Throttle(3) { sleep.add(it) } + } + + @Test + fun dontThrottle() = runBlockingTest { + val now = DateTimeUtils.currentTimeMillis() + runAt(now) + runAt(now) + runAt(now) + runAt(now + 1000) + assertTrue(sleep.isEmpty()) + } + + @Test + fun throttleForOneMillisecond() = runBlockingTest { + val now = DateTimeUtils.currentTimeMillis() + runAt(now) + runAt(now) + runAt(now) + runAt(now + 999) + assertEquals(arrayListOf(1L), sleep) + } + + @Test + fun throttleForOneSecond() = runBlockingTest { + val now = DateTimeUtils.currentTimeMillis() + runAt(now) + runAt(now) + runAt(now) + runAt(now) + assertEquals(arrayListOf(1000L), sleep) + } + + @Test + fun throttleMultiple() = runBlockingTest { + val now = DateTimeUtils.currentTimeMillis() + runAt(now) + runAt(now + 200) + runAt(now + 600) + runAt(now + 700) + assertEquals(arrayListOf(300L), sleep) + runAt(now + 750) + assertEquals(arrayListOf(300L, 450L), sleep) + } + + private suspend fun runAt(millis: Long) { + SuspendFreeze.freezeAt(millis) { throttle.run {} } + } +} \ No newline at end of file