Browse Source

test: add loop call order tests

Peter 10 months ago
parent
commit
7fa04308c5
1 changed files with 155 additions and 1 deletions
  1. 155 1
      src/core/loop.test.ts

+ 155 - 1
src/core/loop.test.ts

@@ -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
+};