|
@@ -1,11 +1,12 @@
|
|
|
import { afterEach, beforeEach, it } from 'vitest'
|
|
|
+import type { TresContext } from '../composables/useTresContextProvider'
|
|
|
import { createRenderLoop } from './loop'
|
|
|
|
|
|
let renderLoop
|
|
|
|
|
|
describe('createRenderLoop', () => {
|
|
|
beforeEach(() => {
|
|
|
- renderLoop = createRenderLoop()
|
|
|
+ renderLoop = createRenderLoop({} as TresContext)
|
|
|
})
|
|
|
afterEach(() => {
|
|
|
renderLoop.stop()
|
|
@@ -117,4 +118,157 @@ describe('createRenderLoop', () => {
|
|
|
|
|
|
expect(executionOrder).toEqual(['before', 'fbo', 'render', 'after'])
|
|
|
})
|
|
|
+
|
|
|
+ describe('`stop`, `start`, `pause`, `resume` call order', () => {
|
|
|
+ it('does not trigger a callback on `start()` unless `stop()`ped', () => {
|
|
|
+ const callbackBefore = vi.fn()
|
|
|
+ const callbackRender = vi.fn()
|
|
|
+ const callbackAfter = vi.fn()
|
|
|
+ renderLoop.register(callbackBefore, 'before')
|
|
|
+ renderLoop.register(callbackRender, 'render')
|
|
|
+ renderLoop.register(callbackAfter, 'after')
|
|
|
+ renderLoop.start()
|
|
|
+ expect(callbackBefore).toBeCalledTimes(1)
|
|
|
+ expect(callbackRender).toBeCalledTimes(1)
|
|
|
+ expect(callbackAfter).toBeCalledTimes(1)
|
|
|
+
|
|
|
+ renderLoop.start()
|
|
|
+ renderLoop.start()
|
|
|
+ renderLoop.start()
|
|
|
+ renderLoop.start()
|
|
|
+ expect(callbackBefore).toBeCalledTimes(1)
|
|
|
+ expect(callbackRender).toBeCalledTimes(1)
|
|
|
+ expect(callbackAfter).toBeCalledTimes(1)
|
|
|
+
|
|
|
+ renderLoop.stop()
|
|
|
+ renderLoop.start()
|
|
|
+ expect(callbackBefore).toBeCalledTimes(2)
|
|
|
+ expect(callbackRender).toBeCalledTimes(2)
|
|
|
+ expect(callbackAfter).toBeCalledTimes(2)
|
|
|
+ })
|
|
|
+
|
|
|
+ it('can `start()` even if `resume()`d while `stop()`ped', () => {
|
|
|
+ const callbackBefore = vi.fn()
|
|
|
+ const callbackRender = vi.fn()
|
|
|
+ const callbackAfter = vi.fn()
|
|
|
+ renderLoop.register(callbackBefore, 'before')
|
|
|
+ renderLoop.register(callbackRender, 'render')
|
|
|
+ renderLoop.register(callbackAfter, 'after')
|
|
|
+ renderLoop.stop()
|
|
|
+ renderLoop.resume()
|
|
|
+ expect(callbackBefore).toBeCalledTimes(0)
|
|
|
+ expect(callbackRender).toBeCalledTimes(0)
|
|
|
+ expect(callbackAfter).toBeCalledTimes(0)
|
|
|
+
|
|
|
+ renderLoop.start()
|
|
|
+ expect(callbackBefore).toBeCalledTimes(1)
|
|
|
+ expect(callbackRender).toBeCalledTimes(1)
|
|
|
+ expect(callbackAfter).toBeCalledTimes(1)
|
|
|
+ })
|
|
|
+
|
|
|
+ it('`isActive.value` is `true` only if both `start()`ed and `resume()`d, regardless of call order', () => {
|
|
|
+ const callbackBefore = vi.fn()
|
|
|
+ const callbackRender = vi.fn()
|
|
|
+ const callbackAfter = vi.fn()
|
|
|
+ renderLoop.register(callbackBefore, 'before')
|
|
|
+ renderLoop.register(callbackRender, 'render')
|
|
|
+ renderLoop.register(callbackAfter, 'after')
|
|
|
+
|
|
|
+ const { start, stop, resume, pause } = renderLoop
|
|
|
+
|
|
|
+ // NOTE: stop, pause | stop, resume | start, resume
|
|
|
+ // NOTE: stop, pause
|
|
|
+ stop()
|
|
|
+ pause()
|
|
|
+ expect(renderLoop.isActive.value).toBe(false)
|
|
|
+ // NOTE: stop, resume
|
|
|
+ resume()
|
|
|
+ expect(renderLoop.isActive.value).toBe(false)
|
|
|
+ // NOTE: start, resume
|
|
|
+ start()
|
|
|
+ expect(renderLoop.isActive.value).toBe(true)
|
|
|
+
|
|
|
+ // NOTE: stop, pause | start, pause | start, resume
|
|
|
+ // NOTE: stop, pause
|
|
|
+ stop()
|
|
|
+ pause()
|
|
|
+ expect(renderLoop.isActive.value).toBe(false)
|
|
|
+ // NOTE: start, pause
|
|
|
+ start()
|
|
|
+ expect(renderLoop.isActive.value).toBe(false)
|
|
|
+ // NOTE: start, resume
|
|
|
+ resume()
|
|
|
+ expect(renderLoop.isActive.value).toBe(true)
|
|
|
+
|
|
|
+ // NOTE: start, resume | start, pause | start, resume
|
|
|
+ // NOTE: start, resume
|
|
|
+ resume()
|
|
|
+ start()
|
|
|
+ expect(renderLoop.isActive.value).toBe(true)
|
|
|
+ // NOTE: start, pause
|
|
|
+ pause()
|
|
|
+ expect(renderLoop.isActive.value).toBe(false)
|
|
|
+ // NOTE: start, resume
|
|
|
+ resume()
|
|
|
+ expect(renderLoop.isActive.value).toBe(true)
|
|
|
+
|
|
|
+ // NOTE: start, resume | stop, resume | start, resume
|
|
|
+ // NOTE: start, resume
|
|
|
+ resume()
|
|
|
+ start()
|
|
|
+ expect(renderLoop.isActive.value).toBe(true)
|
|
|
+ // NOTE: stop, resume
|
|
|
+ stop()
|
|
|
+ expect(renderLoop.isActive.value).toBe(false)
|
|
|
+ // NOTE: start, resume
|
|
|
+ start()
|
|
|
+ expect(renderLoop.isActive.value).toBe(true)
|
|
|
+
|
|
|
+ // NOTE: make some random calls
|
|
|
+ const ons = [start, resume]
|
|
|
+ const offs = [stop, pause]
|
|
|
+ const onsAndOffs = [start, stop, resume, pause]
|
|
|
+ const TEST_COUNT = 100
|
|
|
+
|
|
|
+ for (let i = 0; i < TEST_COUNT; i++) {
|
|
|
+ const ARRAY_COUNT = 25 + Math.floor(Math.random() * 10)
|
|
|
+ const _offs = Array.from({ length: ARRAY_COUNT }).fill(0).map(() => choose(offs))
|
|
|
+ _offs.forEach(fn => fn())
|
|
|
+ expect(renderLoop.isActive.value).toBe(false)
|
|
|
+ shuffle(ons)
|
|
|
+ ons.forEach(fn => fn())
|
|
|
+ expect(renderLoop.isActive.value).toBe(true)
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let i = 0; i < TEST_COUNT; i++) {
|
|
|
+ const ARRAY_COUNT = 25 + Math.floor(Math.random() * 10)
|
|
|
+ const _onsAndOffs = Array.from({ length: ARRAY_COUNT }).fill(0).map(() => choose(onsAndOffs))
|
|
|
+ _onsAndOffs.forEach(fn => fn())
|
|
|
+ shuffle(offs)
|
|
|
+ offs[0]()
|
|
|
+ expect(renderLoop.isActive.value).toBe(false)
|
|
|
+ shuffle(ons)
|
|
|
+ ons.forEach(fn => fn())
|
|
|
+ expect(renderLoop.isActive.value).toBe(true)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
})
|
|
|
+
|
|
|
+function choose(array: any[]) {
|
|
|
+ const i = Math.floor(Math.random() * array.length)
|
|
|
+ return array[i]
|
|
|
+}
|
|
|
+
|
|
|
+function shuffle(array: any[]) {
|
|
|
+ let currentIndex = array.length
|
|
|
+ while (currentIndex !== 0) {
|
|
|
+ const randomIndex = Math.floor(Math.random() * currentIndex)
|
|
|
+ currentIndex--;
|
|
|
+ [array[currentIndex], array[randomIndex]] = [
|
|
|
+ array[randomIndex],
|
|
|
+ array[currentIndex],
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ return array
|
|
|
+};
|