1 package expo.modules.av.progress 2 3 import io.mockk.Runs 4 import io.mockk.every 5 import io.mockk.just 6 import io.mockk.mockk 7 import io.mockk.spyk 8 import io.mockk.verify 9 import org.junit.jupiter.api.BeforeEach 10 import org.junit.jupiter.api.Test 11 12 internal class ProgressLooperTest { 13 14 interface TestTimeMachine : TimeMachine { advanceBynull15 fun advanceBy(interval: Long) 16 fun triggerListeners(by: Long = Long.MAX_VALUE) 17 } 18 19 object TimeMachineInstance : TestTimeMachine { 20 21 override var time = 0L 22 23 var callbacks: Map<TimeMachineTick, Long> = HashMap() 24 25 override fun advanceBy(interval: Long) { 26 time += interval 27 } 28 29 override fun triggerListeners(by: Long) { 30 val toInvoke = callbacks.filter { it.value < by } 31 callbacks = callbacks.filter { it.value >= by } 32 toInvoke.forEach { 33 it.key() 34 } 35 } 36 37 override fun scheduleAt(intervalMillis: Long, callback: TimeMachineTick) { 38 if (intervalMillis > 0) { 39 callbacks = callbacks.plus(callback to time + intervalMillis) 40 } 41 } 42 43 fun reset() { 44 callbacks = HashMap() 45 time = 0 46 } 47 } 48 49 lateinit var looper: ProgressLooper 50 lateinit var callback: TimeMachineTick 51 lateinit var timeMachine: TestTimeMachine 52 53 @BeforeEach setUpnull54 fun setUp() { 55 TimeMachineInstance.reset() 56 timeMachine = spyk(TimeMachineInstance) 57 looper = ProgressLooper(timeMachine) 58 callback = mockk() 59 every { callback() } just Runs 60 } 61 62 @Test callback not invoked prematurelynull63 fun `callback not invoked prematurely`() { 64 looper.loop(1000, callback) 65 verify(exactly = 0) { callback() } 66 } 67 68 @Test callback invoked once after time passednull69 fun `callback invoked once after time passed`() { 70 looper.loop(1000L, callback) 71 timeMachine.advanceBy(1000L) 72 timeMachine.triggerListeners() 73 verify(exactly = 1) { callback() } 74 } 75 76 @Test callback invoked twice after two timeoutsnull77 fun `callback invoked twice after two timeouts`() { 78 looper.loop(1000, callback) 79 timeMachine.advanceBy(1100) 80 timeMachine.triggerListeners() 81 timeMachine.advanceBy(1100) 82 timeMachine.triggerListeners() 83 verify(exactly = 2) { callback() } 84 } 85 86 @Test callback invoked once after twice too big timeoutnull87 fun `callback invoked once after twice too big timeout`() { 88 looper.loop(1000, callback) 89 timeMachine.advanceBy(2200) 90 timeMachine.triggerListeners(2200) 91 verify(exactly = 1) { callback() } 92 } 93 94 @Test callback not invoked after looping stoppednull95 fun `callback not invoked after looping stopped`() { 96 looper.loop(1000L, callback) 97 timeMachine.advanceBy(1001) 98 timeMachine.triggerListeners() 99 verify(exactly = 1) { callback() } 100 looper.stopLooping() 101 timeMachine.advanceBy(1001) 102 timeMachine.triggerListeners() 103 verify(exactly = 1) { callback() } 104 } 105 106 @Test callback not invoked earlier if interval shortenednull107 fun `callback not invoked earlier if interval shortened`() { 108 looper.loop(1000L, callback) 109 110 looper.loop(100, callback) 111 timeMachine.advanceBy(110) 112 timeMachine.triggerListeners(110) 113 verify(exactly = 0) { callback() } 114 115 timeMachine.advanceBy(900) 116 timeMachine.triggerListeners(1010) 117 verify(exactly = 1) { callback() } 118 119 timeMachine.advanceBy(100) 120 timeMachine.triggerListeners(1110) 121 verify(exactly = 2) { callback() } 122 } 123 124 @Test callback invoked earlier even if interval lengthenednull125 fun `callback invoked earlier even if interval lengthened`() { 126 looper.loop(1000, callback) 127 128 looper.loop(2000, callback) 129 timeMachine.advanceBy(1100) 130 timeMachine.triggerListeners(1100) 131 132 verify(exactly = 1) { callback() } 133 } 134 135 @Test callback not invoked later even if interval lengthenednull136 fun `callback not invoked later even if interval lengthened`() { 137 looper.loop(1000, callback) 138 139 looper.loop(2000, callback) 140 timeMachine.advanceBy(1110) 141 timeMachine.triggerListeners(1100) 142 143 verify(exactly = 1) { callback() } 144 145 timeMachine.advanceBy(1110) 146 timeMachine.triggerListeners(2200) 147 148 verify(exactly = 1) { callback() } 149 } 150 151 @Test next tick scheduled with adjustment to passed time when invoked too latenull152 fun `next tick scheduled with adjustment to passed time when invoked too late`() { 153 looper.loop(1000L, callback) 154 155 timeMachine.advanceBy(1100) 156 timeMachine.triggerListeners(1100) 157 158 verify(exactly = 1) { timeMachine.scheduleAt(900, any()) } 159 } 160 161 @Test next tick scheduled with adjustment to passed time when invoked too earlynull162 fun `next tick scheduled with adjustment to passed time when invoked too early`() { 163 looper.loop(1000L, callback) 164 165 timeMachine.advanceBy(900) 166 timeMachine.triggerListeners() 167 168 verify(exactly = 1) { timeMachine.scheduleAt(1100, any()) } 169 } 170 171 @Test old listener not notified after new is registerednull172 fun `old listener not notified after new is registered`() { 173 looper.loop(1000, callback) 174 175 timeMachine.advanceBy(1100) 176 timeMachine.triggerListeners() 177 verify(exactly = 1) { callback() } 178 179 looper.setListener { } 180 timeMachine.advanceBy(1100) 181 timeMachine.triggerListeners() 182 183 verify(exactly = 1) { callback() } 184 } 185 186 @Test new listener is notified after registrationnull187 fun `new listener is notified after registration`() { 188 looper.loop(1000) { } 189 190 timeMachine.advanceBy(1100) 191 timeMachine.triggerListeners() 192 looper.setListener(callback) 193 timeMachine.advanceBy(1100) 194 timeMachine.triggerListeners() 195 196 verify(exactly = 1) { callback() } 197 } 198 199 @Test time machine not called if no looping startednull200 fun `time machine not called if no looping started`() { 201 timeMachine.advanceBy(1100) 202 timeMachine.triggerListeners() 203 204 verify(exactly = 0) { timeMachine.scheduleAt(any(), any()) } 205 } 206 207 @Test time machine not called after looping stoppednull208 fun `time machine not called after looping stopped`() { 209 looper.loop(1000) {} 210 verify(exactly = 1) { timeMachine.scheduleAt(any(), any()) } 211 212 timeMachine.advanceBy(1100) 213 timeMachine.triggerListeners() 214 verify(exactly = 2) { timeMachine.scheduleAt(any(), any()) } 215 216 looper.stopLooping() 217 timeMachine.advanceBy(100000) 218 timeMachine.triggerListeners() 219 verify(exactly = 2) { timeMachine.scheduleAt(any(), any()) } 220 } 221 } 222