deque.spec.ts 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import { describe, expect, it } from 'vitest'
  2. import { Deque } from '@deepseek-ai/dsh-deque'
  3. function backingStorage<T>(deque: Deque<T>): readonly (T | undefined)[] {
  4. // Storage retention is the behavior under test and has no public query API.
  5. return (deque as unknown as { readonly buffer: readonly (T | undefined)[] }).buffer
  6. }
  7. describe('Deque', () => {
  8. it('removes tail-appended entries in FIFO order', () => {
  9. const deque = new Deque<number>()
  10. expect(deque.size).toBe(0)
  11. expect(deque.popFront()).toBeUndefined()
  12. deque.pushBack(1)
  13. deque.pushBack(2)
  14. expect(deque.size).toBe(2)
  15. expect(deque.popFront()).toBe(1)
  16. expect(deque.popFront()).toBe(2)
  17. expect(deque.size).toBe(0)
  18. })
  19. it('prepends entries before the existing head', () => {
  20. const deque = new Deque<number>()
  21. deque.pushBack(3)
  22. deque.pushFront(2)
  23. deque.pushFront(1)
  24. expect([deque.popFront(), deque.popFront(), deque.popFront()]).toEqual([1, 2, 3])
  25. })
  26. it('appends through the array boundary without growing', () => {
  27. const deque = new Deque<number>()
  28. for (let value = 0; value < 8; value += 1) deque.pushBack(value)
  29. for (let value = 0; value < 6; value += 1) expect(deque.popFront()).toBe(value)
  30. for (let value = 8; value <= 16; value += 1) deque.pushBack(value)
  31. for (const value of [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]) {
  32. expect(deque.popFront()).toBe(value)
  33. }
  34. })
  35. it('preserves order across wrapping, growth, and sparse compaction', () => {
  36. const deque = new Deque<number>()
  37. for (let value = 0; value < 32; value += 1) deque.pushBack(value)
  38. for (let value = 0; value < 24; value += 1) expect(deque.popFront()).toBe(value)
  39. expect(backingStorage(deque)).toHaveLength(16)
  40. for (let value = 32; value < 128; value += 1) deque.pushBack(value)
  41. for (let value = 24; value < 128; value += 1) expect(deque.popFront()).toBe(value)
  42. expect(deque.size).toBe(0)
  43. expect(backingStorage(deque)).toHaveLength(16)
  44. })
  45. it('releases a removed reference before sparse compaction', () => {
  46. const deque = new Deque<object>()
  47. const removed = {}
  48. deque.pushBack(removed)
  49. deque.pushBack({})
  50. expect(deque.popFront()).toBe(removed)
  51. expect(backingStorage(deque)).not.toContain(removed)
  52. expect(backingStorage(deque)).toHaveLength(16)
  53. })
  54. it('drops retained storage and remains reusable after clear', () => {
  55. const deque = new Deque<object>()
  56. const retained = {}
  57. deque.pushBack(retained)
  58. for (let index = 1; index < 64; index += 1) deque.pushBack({ index })
  59. const grownStorage = backingStorage(deque)
  60. deque.clear()
  61. expect(deque.size).toBe(0)
  62. expect(deque.popFront()).toBeUndefined()
  63. expect(backingStorage(deque)).not.toBe(grownStorage)
  64. expect(backingStorage(deque)).not.toContain(retained)
  65. expect(backingStorage(deque)).toHaveLength(16)
  66. const value = {}
  67. deque.pushBack(value)
  68. expect(deque.popFront()).toBe(value)
  69. })
  70. it('uses size to distinguish an undefined entry from an empty deque', () => {
  71. const deque = new Deque<undefined>()
  72. deque.pushBack(undefined)
  73. expect(deque.size).toBe(1)
  74. deque.popFront()
  75. expect(deque.size).toBe(0)
  76. })
  77. })