mirror of https://github.com/tasks/tasks
Convert Throttle to Kotlin
parent
41f7d1da6b
commit
6e6fb3eada
@ -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 {} }
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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<Long>
|
||||||
|
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 {} }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue