UNPKG

ts-data-forge

Version:

[![npm version](https://img.shields.io/npm/v/ts-data-forge.svg)](https://www.npmjs.com/package/ts-data-forge) [![npm downloads](https://img.shields.io/npm/dm/ts-data-forge.svg)](https://www.npmjs.com/package/ts-data-forge) [![License](https://img.shields.

346 lines (221 loc) 8.69 kB
import { Arr } from '../array/index.mjs'; import { Optional } from '../functional/index.mjs'; import { range } from '../iterator/index.mjs'; import { asUint32 } from '../number/index.mjs'; import { createQueue, type Queue } from './queue.mjs'; describe('Queue test', () => { describe('initialization', () => { test('should be empty if initialized without values', () => { const q = createQueue(); assert.isTrue(q.isEmpty); expect(q.size).toBe(0); }); test('should not be empty if initialized with values', () => { const q = createQueue([1, 2, 3]); assert.isFalse(q.isEmpty); expect(q.size).toBe(3); }); }); describe('enqueue', () => { let mut_q: Queue<number>; // eslint-disable-next-line vitest/no-hooks beforeEach(() => { mut_q = createQueue(); }); test('should increase size and not be empty after enqueueing to an empty queue', () => { mut_q.enqueue(1); assert.isFalse(mut_q.isEmpty); expect(mut_q.size).toBe(1); }); test('should increase size when enqueueing to a non-empty queue', () => { mut_q.enqueue(1); mut_q.enqueue(2); expect(mut_q.size).toBe(2); }); }); describe('dequeue', () => { test('should return Optional.none and size should be 0 when dequeuing from an empty queue', () => { const q = createQueue<number>(); const result = q.dequeue(); assert.isTrue(Optional.isNone(result)); assert.isTrue(q.isEmpty); expect(q.size).toBe(0); }); test('should decrease size and return the dequeued element for a non-empty queue', () => { const q = createQueue([1, 2, 3]); // FIFO: elements are in order [1, 2, 3] const initialSize = q.size; const result1 = q.dequeue(); // Dequeues 1 (first element) assert.isTrue(Optional.isSome(result1)); if (Optional.isSome(result1)) { expect(result1.value).toBe(1); } expect(q.size).toBe(initialSize - 1); const result2 = q.dequeue(); // Dequeues 2 assert.isTrue(Optional.isSome(result2)); if (Optional.isSome(result2)) { expect(result2.value).toBe(2); } expect(q.size).toBe(initialSize - 2); }); test('should become empty after dequeuing all elements', () => { const q = createQueue([1, 2]); // Internal: [1, 2] q.dequeue(); // Dequeues 1 q.dequeue(); // Dequeues 2 assert.isTrue(q.isEmpty); expect(q.size).toBe(0); const result = q.dequeue(); // Dequeue from empty assert.isTrue(Optional.isNone(result)); }); }); describe('FIFO behavior', () => { test('elements should be dequeued in first-in, first-out order', () => { const q = createQueue<number>(); q.enqueue(1); // internal: [1] q.enqueue(2); // internal: [1, 2] q.enqueue(3); // internal: [1, 2, 3] expect(q.size).toBe(3); let mut_result = q.dequeue(); // Dequeues 1 (first in) assert.isTrue(Optional.isSome(mut_result) && mut_result.value === 1); expect(q.size).toBe(2); mut_result = q.dequeue(); // Dequeues 2 assert.isTrue(Optional.isSome(mut_result) && mut_result.value === 2); expect(q.size).toBe(1); mut_result = q.dequeue(); // Dequeues 3 assert.isTrue(Optional.isSome(mut_result) && mut_result.value === 3); expect(q.size).toBe(0); assert.isTrue(q.isEmpty); mut_result = q.dequeue(); assert.isTrue(Optional.isNone(mut_result)); }); test('initial values are dequeued in the same order (FIFO)', () => { const q = createQueue([1, 2, 3]); // Internal: [1, 2, 3] expect(q.size).toBe(3); const result1 = q.dequeue(); // Dequeues 1 (first element) assert.isTrue(Optional.isSome(result1) && result1.value === 1); const result2 = q.dequeue(); // Dequeues 2 assert.isTrue(Optional.isSome(result2) && result2.value === 2); const result3 = q.dequeue(); // Dequeues 3 assert.isTrue(Optional.isSome(result3) && result3.value === 3); assert.isTrue(q.isEmpty); }); test('mixed enqueue and dequeue operations maintain FIFO order', () => { const q = createQueue<string>(); q.enqueue('A'); q.enqueue('B'); const result1 = q.dequeue(); assert.isTrue(Optional.isSome(result1) && result1.value === 'A'); q.enqueue('C'); q.enqueue('D'); const result2 = q.dequeue(); assert.isTrue(Optional.isSome(result2) && result2.value === 'B'); const result3 = q.dequeue(); assert.isTrue(Optional.isSome(result3) && result3.value === 'C'); const result4 = q.dequeue(); assert.isTrue(Optional.isSome(result4) && result4.value === 'D'); assert.isTrue(q.isEmpty); }); }); describe('Circular buffer behavior', () => { test('should handle buffer wraparound correctly', () => { const q = createQueue<number>(); // Fill and partially empty the queue to create wraparound conditions for (const i of range(1, 6)) { q.enqueue(i); } // Remove first 3 elements expect(Optional.unwrap(q.dequeue())).toBe(1); expect(Optional.unwrap(q.dequeue())).toBe(2); expect(Optional.unwrap(q.dequeue())).toBe(3); // Add more elements (this should wrap around in the buffer) q.enqueue(6); q.enqueue(7); q.enqueue(8); // Verify FIFO order is maintained expect(Optional.unwrap(q.dequeue())).toBe(4); expect(Optional.unwrap(q.dequeue())).toBe(5); expect(Optional.unwrap(q.dequeue())).toBe(6); expect(Optional.unwrap(q.dequeue())).toBe(7); expect(Optional.unwrap(q.dequeue())).toBe(8); assert.isTrue(q.isEmpty); }); test('should automatically resize when buffer becomes full', () => { const q = createQueue<number>(); // Add more elements than initial capacity (8) to trigger resize for (const i of range(1, 21)) { q.enqueue(i); } expect(q.size).toBe(20); // Verify all elements can be dequeued in correct order for (const i of range(1, 21)) { const result = q.dequeue(); assert.isTrue(Optional.isSome(result) && result.value === i); } assert.isTrue(q.isEmpty); }); test('should handle multiple resize operations', () => { const q = createQueue<number>(); // Add many elements to trigger multiple resizes for (const i of range(1, 101)) { q.enqueue(i); } expect(q.size).toBe(100); // Remove half for (const i of range(1, 51)) { expect(Optional.unwrap(q.dequeue())).toBe(i); } // Add more to trigger another resize for (const i of range(101, 151)) { q.enqueue(i); } expect(q.size).toBe(100); // 50 remaining + 50 new // Verify correct order for (const i of range(51, 101)) { expect(Optional.unwrap(q.dequeue())).toBe(i); } for (const i of range(101, 151)) { expect(Optional.unwrap(q.dequeue())).toBe(i); } assert.isTrue(q.isEmpty); }); test('should handle edge case of single element operations', () => { const q = createQueue<string>(); // Test single element enqueue/dequeue cycles for (const i of range(10)) { q.enqueue(`item-${i}`); expect(q.size).toBe(1); const result = q.dequeue(); assert.isTrue(Optional.isSome(result) && result.value === `item-${i}`); assert.isTrue(q.isEmpty); } }); test('should handle large initial values array', () => { const largeArray = Arr.range(1, asUint32(51)); const q = createQueue(largeArray); expect(q.size).toBe(50); // Verify all elements are in correct order for (const i of range(1, 51)) { expect(Optional.unwrap(q.dequeue())).toBe(i); } assert.isTrue(q.isEmpty); }); test('should properly clean up references for garbage collection', () => { const q = createQueue<{ id: number }>(); const objects = Arr.seq(10).map((i) => ({ id: i })); // Add all objects for (const obj of objects) { q.enqueue(obj); } // Remove first 5 objects for (const i of range(0, 5)) { const result = q.dequeue(); assert.isTrue(Optional.isSome(result) && result.value.id === i); } // Remaining objects should still be accessible for (const i of range(5, 10)) { const result = q.dequeue(); assert.isTrue(Optional.isSome(result) && result.value.id === i); } assert.isTrue(q.isEmpty); }); }); });