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