import { assert } from 'chai'; import { CircularBuffer } from '../../../lib/containers/CircularBuffer'; import { slice } from '../../../lib/core/array'; describe('containers.CircularBuffer', () => { describe('constructor', () => { function ensureNullBuffer(cb: CircularBuffer) { assert.strictEqual(cb.capacity, 0); assert.strictEqual(cb.size, 0); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, 0); assert.strictEqual(cb.isEmpty(), cb.isFull()); } function ensureFilledBuffer(cb: CircularBuffer, data: any[]) { assert.strictEqual(cb.capacity, data.length); assert.strictEqual(cb.size, data.length); assert.deepEqual((cb)._data, data); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, data.length - 1); assert.ok(!cb.isEmpty()); assert.ok(cb.isFull()); } function ensureEmptyBufferWithCapacity(cb: CircularBuffer, capacity: number) { assert.strictEqual(cb.capacity, capacity); assert.strictEqual(cb.size, 0); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, cb.capacity - 1); assert.ok(cb.isEmpty()); assert.ok(!cb.isFull()); } function ensureNewBufferFromOtherBuffer(other: CircularBuffer) { const cb = new CircularBuffer(other); assert.deepEqual((cb)._data, (other)._data); assert.strictEqual(cb.capacity, other.capacity); assert.strictEqual(cb.size, other.size); assert.strictEqual((cb)._first, (other)._first); assert.strictEqual((cb)._last, (other)._last); assert.strictEqual(cb.isEmpty(), other.isEmpty()); assert.strictEqual(cb.isFull(), other.isFull()); } function ensureBufferFromIterable(iterable: Iterable) { const cb = new CircularBuffer(iterable); const result = [...iterable]; assert.strictEqual(cb.capacity, result.length); assert.strictEqual(cb.size, result.length); assert.deepEqual((cb)._data, result); assert.strictEqual((cb)._first, 0); if (result.length !== 0) { assert.strictEqual((cb)._last, result.length - 1); assert.ok(!cb.isEmpty()); assert.ok(cb.isFull()); } else { assert.strictEqual((cb)._last, 0); assert.ok(cb.isEmpty()); assert.ok(cb.isFull()); } } function ensureBufferFromArrayLike(arrayLike: ArrayLike) { const cb = new CircularBuffer(arrayLike); const result = slice(arrayLike); assert.strictEqual(cb.capacity, result.length); assert.strictEqual(cb.size, result.length); assert.deepEqual((cb)._data, result); assert.strictEqual((cb)._first, 0); if (result.length !== 0) { assert.strictEqual((cb)._last, result.length - 1); assert.ok(!cb.isEmpty()); assert.ok(cb.isFull()); } else { assert.strictEqual((cb)._last, 0); assert.ok(cb.isEmpty()); assert.ok(cb.isFull()); } } function ensureRangeError(arg: any) { try { new CircularBuffer(arg); } catch (e) { assert.ok(e instanceof RangeError); return; } assert.fail(); } it('should construct null buffer', () => { ensureNullBuffer(new CircularBuffer()); ensureNullBuffer(new CircularBuffer(0)); }); it('should construct empty buffer with capacity', () => { ensureEmptyBufferWithCapacity(new CircularBuffer(1), 1); ensureEmptyBufferWithCapacity(new CircularBuffer(5), 5); }); it('should construct filled buffer with data', () => { ensureFilledBuffer(new CircularBuffer([1, 2]), [1, 2]); ensureFilledBuffer(new CircularBuffer(['5']), ['5']); ensureFilledBuffer(new CircularBuffer([[1, 2, 3]]), [[1, 2, 3]]); ensureFilledBuffer(new CircularBuffer([1, 2, 3]), [1, 2, 3]); ensureFilledBuffer(new CircularBuffer([undefined, undefined]), [undefined, undefined]); ensureFilledBuffer(new CircularBuffer([[]]), [[]]); ensureFilledBuffer(new CircularBuffer(['']), ['']); ensureFilledBuffer(new CircularBuffer([null]), [null]); }); it('should throw RangeError from negative number', () => { ensureRangeError(-10); }); it('should throw RangeError from float number', () => { ensureRangeError(3.5); }); it('should construct buffer from iterable', () => { ensureBufferFromIterable([]); ensureBufferFromIterable([1, 2, 3]); ensureBufferFromIterable('123'); ensureBufferFromIterable({ *[Symbol.iterator]() { yield 1; yield 'b'; yield 3.5; } }); }); it('should construct buffer from other buffer', () => { ensureNewBufferFromOtherBuffer(new CircularBuffer()); ensureNewBufferFromOtherBuffer(new CircularBuffer([1, 2, 3])); }); it('should construct buffer from array-like', () => { ensureBufferFromArrayLike({ length: 0 }); ensureBufferFromArrayLike({ 0: 3, 1: 7, 2: 9, 3: 5, 4: 1, 5: 8, length: 6 }); }); }); describe('push', () => { it('should push nothing', () => { const cb = new CircularBuffer(5); assert.ok(cb.isEmpty()); cb.push(); assert.ok(cb.isEmpty()); }); it('should push one item', () => { const capacity = 5; const cb = new CircularBuffer(capacity); assert.ok(cb.isEmpty()); cb.push(1); assert.ok(!cb.isEmpty()); assert.strictEqual(capacity - cb.size, capacity - 1); assert.strictEqual(cb.first(), 1); assert.strictEqual(cb.last(), 1); }); it('should push array as one item', () => { const capacity = 5; const cb = new CircularBuffer(capacity); assert.ok(cb.isEmpty()); cb.push([1, 2, 3]); assert.ok(!cb.isEmpty()); assert.strictEqual(capacity - cb.size, capacity - 1); }); it('should push multiple items', () => { const capacity = 5; const cb = new CircularBuffer(capacity); assert.ok(cb.isEmpty()); cb.push(1, 2, 3); assert.ok(!cb.isEmpty()); assert.strictEqual(capacity - cb.size, capacity - 3); assert.strictEqual(cb.first(), 1); assert.strictEqual(cb.last(), 3); }); it('should push to the same state as filled buffer', () => { const cb1 = new CircularBuffer([1, 2, 3, 4, 5]); const cb2 = new CircularBuffer(5); assert.strictEqual(cb1.capacity, cb2.capacity); cb2.push(1, 2, 3); cb2.push(4); cb2.push(5); assert.strictEqual(cb1.first(), cb2.first()); assert.strictEqual(cb1.last(), cb2.last()); assert.strictEqual(cb1.isEmpty(), cb2.isEmpty()); assert.strictEqual(cb1.isFull(), cb2.isFull()); assert.deepEqual((cb1)._data, (cb2)._data); }); it('should replace items at the begin in case of overflow', () => { const cb = new CircularBuffer(5); assert.ok(cb.isEmpty()); cb.push(1, 2, 3, 4, 5); assert.ok(cb.isFull()); cb.push(6); assert.ok(cb.isFull()); assert.strictEqual(cb.first(), 2); assert.strictEqual(cb.last(), 6); cb.push(7); assert.ok(cb.isFull()); assert.strictEqual(cb.first(), 3); assert.strictEqual(cb.last(), 7); cb.push(8, 9, 10, 11, 12); assert.ok(cb.isFull()); assert.strictEqual(cb.first(), 8); assert.strictEqual(cb.last(), 12); cb.push(13, 14, 15); assert.ok(cb.isFull()); assert.strictEqual(cb.first(), 11); assert.strictEqual(cb.last(), 15); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, cb.size - 1); assert.deepEqual((cb)._data, [11, 12, 13, 14, 15]); }); }); describe('pop', () => { it('should pop nothing from null buffer', () => { const cb = new CircularBuffer(); const ret = cb.pop(); assert.isUndefined(ret); assert.strictEqual(cb.size, 0); }); it('should pop nothing from empty buffer with capacity', () => { const cb = new CircularBuffer(5); const ret = cb.pop(); assert.isUndefined(ret); assert.strictEqual(cb.size, 0); }); it('should pop one item from filled buffer', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); const ret = cb.pop(); assert.strictEqual(ret, data[data.length - 1]); assert.strictEqual(cb.size, data.length - 1); }); it('should pop buffer to empty', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); data.forEach(() => { cb.pop(); }); assert.ok(cb.isEmpty()); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, data.length - 1); }); it('should pop and push', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); cb.push(6, 7); for (let i = 0; i < data.length; i++) cb.pop(); assert.ok(cb.isEmpty()); cb.push(1, 2, 3); assert.strictEqual(cb.first(), 1); assert.strictEqual(cb.last(), 3); assert.strictEqual(cb.size, 3); assert.deepEqual((cb)._data, [6, 7, 1, 2, 3]); }); }); describe('unshift', () => { it('should unshift nothing', () => { const cb = new CircularBuffer(5); assert.ok(cb.isEmpty()); cb.unshift(); assert.ok(cb.isEmpty()); }); it('should unshift one item', () => { const capacity = 5; const cb = new CircularBuffer(capacity); assert.ok(cb.isEmpty()); cb.unshift(1); assert.ok(!cb.isEmpty()); assert.strictEqual(capacity - cb.size, capacity - 1); assert.strictEqual(cb.first(), 1); assert.strictEqual(cb.last(), 1); }); it('should unshift array as one item', () => { const capacity = 5; const cb = new CircularBuffer(capacity); assert.ok(cb.isEmpty()); cb.unshift([1, 2, 3]); assert.ok(!cb.isEmpty()); assert.strictEqual(capacity - cb.size, capacity - 1); }); it('should unshift multiple items', () => { const capacity = 5; const cb = new CircularBuffer(capacity); assert.ok(cb.isEmpty()); cb.unshift(1, 2, 3); assert.ok(!cb.isEmpty()); assert.strictEqual(capacity - cb.size, capacity - 3); assert.strictEqual(cb.first(), 3); assert.strictEqual(cb.last(), 1); }); it('should unshift to the same state as filled buffer', () => { const cb1 = new CircularBuffer([5, 4, 3, 2, 1]); const cb2 = new CircularBuffer(5); assert.strictEqual(cb1.capacity, cb2.capacity); cb2.unshift(1, 2, 3); cb2.unshift(4); cb2.unshift(5); assert.strictEqual(cb1.first(), cb2.first()); assert.strictEqual(cb1.last(), cb2.last()); assert.strictEqual(cb1.isEmpty(), cb2.isEmpty()); assert.strictEqual(cb1.isFull(), cb2.isFull()); assert.deepEqual((cb1)._data, (cb2)._data); }); it('should replace items at the end in case of overflow', () => { const cb = new CircularBuffer(5); assert.ok(cb.isEmpty()); cb.unshift(1, 2, 3, 4, 5); assert.ok(cb.isFull()); cb.unshift(6); assert.ok(cb.isFull()); assert.strictEqual(cb.first(), 6); assert.strictEqual(cb.last(), 2); cb.unshift(7); assert.ok(cb.isFull()); assert.strictEqual(cb.first(), 7); assert.strictEqual(cb.last(), 3); cb.unshift(8, 9, 10, 11, 12); assert.ok(cb.isFull()); assert.strictEqual(cb.first(), 12); assert.strictEqual(cb.last(), 8); cb.unshift(13, 14, 15); assert.ok(cb.isFull()); assert.strictEqual(cb.first(), 15); assert.strictEqual(cb.last(), 11); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, cb.size - 1); assert.deepEqual((cb)._data, [15, 14, 13, 12, 11]); }); it('should push and unshit', () => { const cb = new CircularBuffer(5); cb.push(1, 2); assert.strictEqual(cb.first(), 1); assert.strictEqual(cb.last(), 2); cb.unshift(0, -1); assert.strictEqual(cb.first(), -1); assert.strictEqual(cb.last(), 2); cb.push(3, 4); assert.strictEqual(cb.first(), 0); assert.strictEqual(cb.last(), 4); cb.unshift(-3, -4); assert.strictEqual(cb.first(), -4); assert.strictEqual(cb.last(), 2); }); }); describe('shift', () => { it('should shift nothing from null buffer', () => { const cb = new CircularBuffer(); const ret = cb.shift(); assert.isUndefined(ret); assert.strictEqual(cb.size, 0); }); it('should shift nothing from empty buffer with capacity', () => { const cb = new CircularBuffer(5); const ret = cb.shift(); assert.isUndefined(ret); assert.strictEqual(cb.size, 0); }); it('should shift one item from filled buffer', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); const ret = cb.shift(); assert.strictEqual(ret, data[0]); assert.strictEqual(cb.size, data.length - 1); }); it('should shift buffer to empty', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); data.forEach(() => { cb.shift(); }); assert.ok(cb.isEmpty()); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, data.length - 1); }); it('should shift and unshift', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); cb.unshift(0, -1); for (let i = 0; i < data.length; i++) cb.shift(); assert.ok(cb.isEmpty()); cb.unshift(3, 2, 1); assert.strictEqual(cb.first(), 1); assert.strictEqual(cb.last(), 3); assert.strictEqual(cb.size, 3); assert.deepEqual((cb)._data, [1, 2, 3, -1, 0]); }); }); describe('forEach', () => { it('should do nothing with null buffer', () => { const cb = new CircularBuffer(); let ok = true; cb.forEach(() => { ok = false; }); assert.ok(ok); }); it('should do nothing with empty buffer with capacity', () => { const cb = new CircularBuffer(5); let ok = true; cb.forEach(() => { ok = false; }); assert.ok(ok); }); it('should traverse all items', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); cb.forEach((value, index) => { assert.strictEqual(value, data[index]); }); }); it('should traverse all items with correct indices', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.length); cb.push(3, 4, 5); cb.unshift(2, 1); cb.forEach((value, index) => { assert.strictEqual(value, data[index]); }); }); }); describe('Symbol.iterator', () => { it('should produce empty array with empty buffer', () => { const cb = new CircularBuffer(); assert.deepEqual([...cb], []); }); it('should produce empty array with empty buffer with capacity', () => { const cb = new CircularBuffer(5); assert.deepEqual([...cb], []); }); it('should produce items', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data); assert.deepEqual([...cb], data); }); it('should produce items in correct order', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.length); cb.push(3, 4, 5); cb.unshift(2, 1); assert.deepEqual([...cb], data); }); }); describe('get', () => { it('should get nothing from null buffer', () => { const cb = new CircularBuffer(); assert.isUndefined(cb.get(0)); }); it('should get nothing from empty buffer with capacity', () => { const cb = new CircularBuffer(5); assert.isUndefined(cb.get(0)); }); it('should get nothing from incorrect indices', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); assert.isUndefined(cb.get(-1)); assert.isUndefined(cb.get(data.length)); }); it('should get value from filled buffer', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); assert.strictEqual(cb.get(0), cb.first()); assert.strictEqual(cb.get(0), data[0]); assert.strictEqual(cb.get(cb.size - 1), cb.last()); assert.strictEqual(cb.get(cb.size - 1), data[data.length - 1]); assert.strictEqual(cb.get(2), data[2]); }); it('should get value with correct indicies', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.length); cb.push(3, 4, 5); cb.unshift(2, 1); assert.strictEqual(cb.get(0), data[0]); assert.strictEqual(cb.get(cb.size - 1), data[data.length - 1]); assert.strictEqual(cb.get(2), data[2]); }); }); describe('set', () => { it('should set nothing to null buffer', () => { const cb = new CircularBuffer(); cb.set(0, 1); assert.isUndefined(cb.get(0)); }); it('should set nothing to empty buffer with capacity', () => { const cb = new CircularBuffer(5); cb.set(0, 1); assert.isUndefined(cb.get(0)); }); it('should set nothing with indicies mapping', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); cb.set(-1, -1); assert.isUndefined(cb.get(-1)); cb.set(data.length, data.length); assert.isUndefined(cb.get(data.length)); }); it('should set values to filled buffer', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); cb.set(0, -1); assert.strictEqual(cb.get(0), -1); cb.set(cb.size - 1, -5); assert.strictEqual(cb.get(cb.size - 1), -5); cb.set(2, -3); assert.strictEqual(cb.get(2), -3); }); it('should set values with indicies mapping', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.length); cb.push(3, 4, 5); cb.unshift(2, 1); cb.set(0, -1); assert.strictEqual(cb.get(0), -1); cb.set(cb.size - 1, -5); assert.strictEqual(cb.get(cb.size - 1), -5); cb.set(2, -3); assert.strictEqual(cb.get(2), -3); }); }); describe('overflow', () => { it('should work with custom overflow', () => { class CustomCircularBuffer extends CircularBuffer { overflow() { } } const cb = new CustomCircularBuffer(3); assert.strictEqual(cb.push(1, 2, 3, 4), 3); assert.deepEqual(cb.slice(), [1, 2, 3]); }); }); describe('underflow', () => { it('should work with custom underflow', () => { class CustomCircularBuffer extends CircularBuffer { underflow() { } } const cb = new CustomCircularBuffer(3); assert.strictEqual(cb.unshift(1, 2, 3, 4), 3); assert.deepEqual(cb.slice(), [3, 2, 1]); }); }); describe('first', () => { it('should return undefined on empty buffer', () => { assert.isUndefined(new CircularBuffer().first()); }); it('should return first item on filled buffer', () => { assert.strictEqual(new CircularBuffer([1, 2, 3]).first(), 1); }); }); describe('last', () => { it('should return undefined on empty buffer', () => { assert.isUndefined(new CircularBuffer().last()); }); it('should return last item on filled buffer', () => { assert.strictEqual(new CircularBuffer([1, 2, 3]).last(), 3); }); }); describe('rotateLeft', () => { it('should not rotate with zero offset', () => { const cb = new CircularBuffer([1, 2, 3, 4, 5]); cb.rotateLeft(0); assert.deepEqual((cb)._data, [1, 2, 3, 4, 5]); }); it('should rotate by one position', () => { const cb = new CircularBuffer([1, 2, 3, 4, 5]); cb.rotateLeft(1); assert.deepEqual((cb)._data, [2, 3, 4, 5, 1]); }); it('should rotate by one position by default', () => { const cb = new CircularBuffer([1, 2, 3, 4, 5]); cb.rotateLeft(); assert.deepEqual((cb)._data, [2, 3, 4, 5, 1]); }); it('should rotate by two positions', () => { const cb = new CircularBuffer([1, 2, 3, 4, 5]); cb.rotateLeft(2); assert.deepEqual((cb)._data, [3, 4, 5, 1, 2]); }); it('should not rotate by position of size', () => { const cb = new CircularBuffer([1, 2, 3, 4, 5]); cb.rotateLeft(5); assert.deepEqual((cb)._data, [1, 2, 3, 4, 5]); }); it('should crop positions higher than size', () => { const cb = new CircularBuffer([1, 2, 3, 4, 5]); cb.rotateLeft(7); assert.deepEqual((cb)._data, [3, 4, 5, 1, 2]); }); it('should rotate opposite direction with negative positions', () => { const cb = new CircularBuffer([1, 2, 3, 4, 5]); cb.rotateLeft(-2); assert.deepEqual((cb)._data, [4, 5, 1, 2, 3]); }); }); describe('rotateRight', () => { it('should not rotate with zero offset', () => { const cb = new CircularBuffer([1, 2, 3, 4, 5]); cb.rotateRight(0); assert.deepEqual((cb)._data, [1, 2, 3, 4, 5]); }); it('should rotate by one position', () => { const cb = new CircularBuffer([1, 2, 3, 4, 5]); cb.rotateRight(1); assert.deepEqual((cb)._data, [5, 1, 2, 3, 4]); }); it('should rotate by one position by default', () => { const cb = new CircularBuffer([1, 2, 3, 4, 5]); cb.rotateRight(); assert.deepEqual((cb)._data, [5, 1, 2, 3, 4]); }); it('should rotate by two positions', () => { const cb = new CircularBuffer([1, 2, 3, 4, 5]); cb.rotateRight(2); assert.deepEqual((cb)._data, [4, 5, 1, 2, 3]); }); it('should not rotate by position of size', () => { const cb = new CircularBuffer([1, 2, 3, 4, 5]); cb.rotateRight(5); assert.deepEqual((cb)._data, [1, 2, 3, 4, 5]); }); it('should crop positions higher than size', () => { const cb = new CircularBuffer([1, 2, 3, 4, 5]); cb.rotateRight(7); assert.deepEqual((cb)._data, [4, 5, 1, 2, 3]); }); it('should rotate opposite direction with negative positions', () => { const cb = new CircularBuffer([1, 2, 3, 4, 5]); cb.rotateRight(-2); assert.deepEqual((cb)._data, [3, 4, 5, 1, 2]); }); it('should rotate denormalized buffer', () => { const cb = new CircularBuffer(5); cb.push(3, 4, 5); cb.unshift(2, 1); cb.rotateRight(2); assert.deepEqual((cb)._data, [1, 2, 3, 4, 5]); }); }); describe('normalize', () => { function isNormalized(cb: CircularBuffer) { return (cb)._first === 0; } it('should do nothing when buffer normalized', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); assert.ok(isNormalized(cb)); cb.normalize(); assert.ok(isNormalized(cb)); assert.deepEqual((cb)._data, data); }); it('should normalize pushed and unshifted buffer', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.length); cb.push(3, 4, 5); cb.unshift(2, 1); assert.ok(!isNormalized(cb)); cb.normalize(); assert.ok(isNormalized(cb)); assert.deepEqual((cb)._data, data); }); it('should normalize pushed and shifted buffer', () => { const cb = new CircularBuffer(5); cb.push(1, 2, 3, 4, 5); cb.shift(); cb.shift(); assert.ok(!isNormalized(cb)); cb.normalize(); assert.ok(isNormalized(cb)); assert.deepEqual((cb)._data.slice(0, 3), [3, 4, 5]); }); it('should normalize unshifted buffer', () => { const cb = new CircularBuffer(5); cb.unshift(1, 2); assert.ok(!isNormalized(cb)); cb.normalize(); assert.ok(isNormalized(cb)); assert.deepEqual((cb)._data.slice(0, 2), [2, 1]); }); it('should normalize overflowed buffer', () => { const cb = new CircularBuffer(5); cb.push(1, 2, 3, 4, 5, 6, 7); assert.ok(!isNormalized(cb)); cb.normalize(); assert.ok(isNormalized(cb)); assert.deepEqual((cb)._data, [3, 4, 5, 6, 7]); }); it('should normalize underflowed buffer', () => { const cb = new CircularBuffer(5); cb.unshift(1, 2, 3, 4, 5, 6, 7); assert.ok(!isNormalized(cb)); cb.normalize(); assert.ok(isNormalized(cb)); assert.deepEqual((cb)._data, [7, 6, 5, 4, 3]); }); it('should normalize partially filled buffer', () => { const cb = new CircularBuffer(5); cb.push(1, 2); cb.unshift(0); assert.ok(!isNormalized(cb)); cb.normalize(); assert.ok(isNormalized(cb)); assert.deepEqual((cb)._data.slice(0, 3), [0, 1, 2]); }); }); describe('resize', () => { it('should resize null buffer', () => { const newSize = 5; const cb = new CircularBuffer(); assert.strictEqual(cb.capacity, 0); assert.strictEqual(cb.size, 0); cb.resize(newSize); assert.strictEqual(cb.capacity, newSize); assert.strictEqual(cb.size, 0); }); it('should resize null buffer', () => { const newSize = 5; const cb = new CircularBuffer(); assert.strictEqual(cb.capacity, 0); assert.strictEqual(cb.size, 0); cb.resize(newSize); assert.strictEqual(cb.capacity, newSize); assert.strictEqual(cb.size, 0); }); it('should resize empty buffer to lower size', () => { const oldSize = 5; const newSize = 3; const cb = new CircularBuffer(oldSize); assert.strictEqual(cb.capacity, oldSize); assert.strictEqual(cb.size, 0); cb.resize(newSize); assert.strictEqual(cb.capacity, newSize); assert.strictEqual(cb.size, 0); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, newSize - 1); }); it('should resize empty buffer to higher size', () => { const oldSize = 5; const newSize = 7; const cb = new CircularBuffer(oldSize); assert.strictEqual(cb.capacity, oldSize); assert.strictEqual(cb.size, 0); cb.resize(newSize); assert.strictEqual(cb.capacity, newSize); assert.strictEqual(cb.size, 0); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, newSize - 1); }); it('should resize filled buffer to lower size', () => { const data = [1, 2, 3, 4, 5]; const newSize = 3; const cb = new CircularBuffer(data.slice()); assert.strictEqual(cb.capacity, data.length); assert.strictEqual(cb.size, data.length); cb.resize(newSize); assert.strictEqual(cb.capacity, newSize); assert.strictEqual(cb.size, newSize); assert.deepEqual((cb)._data, data.slice(0, newSize)); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, newSize - 1); }); it('should resize filled buffer to higher size', () => { const data = [1, 2, 3, 4, 5]; const newSize = 7; const cb = new CircularBuffer(data.slice()); assert.strictEqual(cb.capacity, data.length); assert.strictEqual(cb.size, data.length); cb.resize(newSize); assert.strictEqual(cb.capacity, newSize); assert.strictEqual(cb.size, data.length); assert.deepEqual((cb)._data.slice(0, data.length), data); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, data.length - 1); }); it('should resize partially filled buffer to higher size', () => { const oldSize = 6; const data = [1, 2, 3, 4]; const newSize = 8; const cb = new CircularBuffer(oldSize); cb.push.apply(cb, data); assert.strictEqual(cb.capacity, oldSize); assert.strictEqual(cb.size, data.length); assert.deepEqual((cb)._data.slice(0, data.length), data); cb.resize(newSize); assert.strictEqual(cb.capacity, newSize); assert.strictEqual(cb.size, data.length); assert.deepEqual((cb)._data.slice(0, data.length), data); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, data.length - 1); }); it('should resize partially filled buffer to lower size', () => { const oldSize = 6; const data = [1, 2, 3, 4]; const newSize = 3; const cb = new CircularBuffer(oldSize); cb.push.apply(cb, data); assert.strictEqual(cb.capacity, oldSize); assert.strictEqual(cb.size, data.length); assert.deepEqual((cb)._data.slice(0, data.length), data); cb.resize(newSize); assert.strictEqual(cb.capacity, newSize); assert.strictEqual(cb.size, newSize); assert.deepEqual((cb)._data, data.slice(0, newSize)); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, newSize - 1); }); it('should resize partially filled buffer to data size', () => { const oldSize = 6; const data = [1, 2, 3, 4]; const newSize = data.length; const cb = new CircularBuffer(oldSize); cb.push.apply(cb, data); assert.strictEqual(cb.capacity, oldSize); assert.strictEqual(cb.size, data.length); assert.deepEqual((cb)._data.slice(0, data.length), data); cb.resize(newSize); assert.strictEqual(cb.capacity, newSize); assert.strictEqual(cb.size, newSize); assert.deepEqual((cb)._data, data); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, newSize - 1); }); it('should resize shifted buffer to lower size', () => { const oldSize = 5; const newSize = 3; const cb = new CircularBuffer(oldSize); cb.push(1, 2, 3, 4, 5, 6, 7); cb.resize(newSize); assert.strictEqual(cb.capacity, newSize); assert.strictEqual(cb.size, newSize); assert.deepEqual((cb)._data, [3, 4, 5]); }); it('should resize shifted buffer to higher size', () => { const oldSize = 5; const newSize = 7; const cb = new CircularBuffer(oldSize); cb.push(1, 2, 3, 4, 5, 6, 7); cb.resize(newSize); assert.strictEqual(cb.capacity, newSize); assert.strictEqual(cb.size, oldSize); assert.deepEqual((cb)._data.slice(0, oldSize), [3, 4, 5, 6, 7]); }); it('should do nothing when resizing with buffer size', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data); cb.resize(data.length); assert.strictEqual(cb.capacity, data.length); assert.strictEqual(cb.size, data.length); assert.deepEqual((cb)._data, data); }); it('should clear buffer when resizing with zero size', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data); cb.resize(0); assert.strictEqual(cb.capacity, 0); assert.strictEqual(cb.size, 0); assert.strictEqual((cb)._first, 0); assert.strictEqual((cb)._last, 0); assert.deepEqual((cb)._data, []); }); }); describe('slice', () => { it('should return same data by default', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); assert.deepEqual(cb.slice(), data); }); it('should work with postive start and end indicies', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); assert.deepEqual(cb.slice(0), [1, 2, 3, 4, 5]); assert.deepEqual(cb.slice(0, 3), [1, 2, 3]); assert.deepEqual(cb.slice(1, 3), [2, 3]); }); it('should work with negative start and end indicies', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); assert.deepEqual(cb.slice(-2), [4, 5]); assert.deepEqual(cb.slice(0, -2), [1, 2, 3]); }); it('should work with indicies outside of the buffer size', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.slice()); assert.deepEqual(cb.slice(0, 10), [1, 2, 3, 4, 5]); assert.deepEqual(cb.slice(0, -7), []); assert.deepEqual(cb.slice(-10), [1, 2, 3, 4, 5]); }); it('should work with partially filled buffer', () => { const cb = new CircularBuffer(5); cb.push(1, 2, 3); assert.deepEqual(cb.slice(1, 3), [2, 3]); assert.deepEqual(cb.slice(1), [2, 3]); assert.deepEqual(cb.slice(1, 5), [2, 3]); assert.deepEqual(cb.slice(3, 5), []); }); it('should work with circular cases', () => { const cb = new CircularBuffer([-1, 0, 1, 3, 5]); cb.push(7, 9); assert.deepEqual(cb.slice(), [1, 3, 5, 7, 9]); assert.deepEqual(cb.slice(0), [1, 3, 5, 7, 9]); assert.deepEqual(cb.slice(1, 3), [3, 5]); assert.deepEqual(cb.slice(-3), [5, 7, 9]); assert.deepEqual(cb.slice(-4, -1), [3, 5, 7]); assert.deepEqual(cb.slice(-1, 1), []); assert.deepEqual(cb.slice(-4, -4), []); assert.deepEqual(cb.slice(-4, -5), []); }); it('should work with circular cases', () => { const data = [1, 2, 3, 4, 5]; const cb = new CircularBuffer(data.length); cb.push(3, 4, 5); cb.unshift(2, 1); assert.deepEqual(cb.slice(2), [3, 4, 5]); }); }); });