--- /dev/null
+// Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
+
+const DEFAULT_CIRCULAR_ARRAY_SIZE = 1024
+
+/**
+ * Array with a maximum length shifting items when full.
+ */
+export class CircularArray<T> extends Array<T> {
+ public size: number
+
+ constructor (size: number = DEFAULT_CIRCULAR_ARRAY_SIZE, ...items: T[]) {
+ super()
+ this.checkSize(size)
+ this.size = size
+ if (arguments.length > 1) {
+ this.push(...items)
+ }
+ }
+
+ public push (...items: T[]): number {
+ const length = super.push(...items)
+ if (length > this.size) {
+ super.splice(0, length - this.size)
+ }
+ return this.length
+ }
+
+ public unshift (...items: T[]): number {
+ const length = super.unshift(...items)
+ if (length > this.size) {
+ super.splice(this.size, items.length)
+ }
+ return this.length
+ }
+
+ public concat (...items: Array<T | ConcatArray<T>>): CircularArray<T> {
+ const concatenatedCircularArray = super.concat(
+ items as T[]
+ ) as CircularArray<T>
+ concatenatedCircularArray.size = this.size
+ if (concatenatedCircularArray.length > concatenatedCircularArray.size) {
+ concatenatedCircularArray.splice(
+ 0,
+ concatenatedCircularArray.length - concatenatedCircularArray.size
+ )
+ }
+ return concatenatedCircularArray
+ }
+
+ public splice (start: number, deleteCount?: number, ...items: T[]): T[] {
+ let itemsRemoved: T[]
+ if (arguments.length >= 3 && deleteCount !== undefined) {
+ itemsRemoved = super.splice(start, deleteCount)
+ // FIXME: that makes the items insert not in place
+ this.push(...items)
+ } else if (arguments.length === 2) {
+ itemsRemoved = super.splice(start, deleteCount)
+ } else {
+ itemsRemoved = super.splice(start)
+ }
+ return itemsRemoved
+ }
+
+ public resize (size: number): void {
+ this.checkSize(size)
+ if (size === 0) {
+ this.length = 0
+ } else if (size < this.size) {
+ for (let i = size; i < this.size; i++) {
+ super.pop()
+ }
+ }
+ this.size = size
+ }
+
+ public empty (): boolean {
+ return this.length === 0
+ }
+
+ public full (): boolean {
+ return this.length === this.size
+ }
+
+ private checkSize (size: number): void {
+ if (!Number.isSafeInteger(size)) {
+ throw new TypeError(
+ `Invalid circular array size: ${size} is not a safe integer`
+ )
+ }
+ if (size < 0) {
+ throw new RangeError(`Invalid circular array size: ${size} < 0`)
+ }
+ }
+}
--- /dev/null
+const { expect } = require('expect')
+const { CircularArray } = require('../lib/circular-array')
+
+describe('Circular array test suite', () => {
+ it('Verify that circular array can be instantiated', () => {
+ const circularArray = new CircularArray()
+ expect(circularArray).toBeInstanceOf(CircularArray)
+ })
+
+ it('Verify circular array default size at instance creation', () => {
+ const circularArray = new CircularArray()
+ expect(circularArray.size).toBe(1024)
+ })
+
+ it('Verify that circular array size can be set at instance creation', () => {
+ const circularArray = new CircularArray(1000)
+ expect(circularArray.size).toBe(1000)
+ })
+
+ it('Verify that circular array size and items can be set at instance creation', () => {
+ let circularArray = new CircularArray(1000, 1, 2, 3, 4, 5)
+ expect(circularArray.size).toBe(1000)
+ expect(circularArray.length).toBe(5)
+ circularArray = new CircularArray(4, 1, 2, 3, 4, 5)
+ expect(circularArray.size).toBe(4)
+ expect(circularArray.length).toBe(4)
+ })
+
+ it('Verify that circular array size is valid at instance creation', () => {
+ expect(() => new CircularArray(0.25)).toThrowError(
+ new TypeError('Invalid circular array size: 0.25 is not a safe integer')
+ )
+ expect(() => new CircularArray(-1)).toThrowError(
+ new RangeError('Invalid circular array size: -1 < 0')
+ )
+ expect(() => new CircularArray(Number.MAX_SAFE_INTEGER + 1)).toThrowError(
+ new TypeError(
+ `Invalid circular array size: ${
+ Number.MAX_SAFE_INTEGER + 1
+ } is not a safe integer`
+ )
+ )
+ })
+
+ it('Verify that circular array empty works as intended', () => {
+ const circularArray = new CircularArray()
+ expect(circularArray.empty()).toBe(true)
+ })
+
+ it('Verify that circular array full works as intended', () => {
+ const circularArray = new CircularArray(5, 1, 2, 3, 4, 5)
+ expect(circularArray.full()).toBe(true)
+ })
+
+ it('Verify that circular array push works as intended', () => {
+ let circularArray = new CircularArray(4)
+ let arrayLength = circularArray.push(1, 2, 3, 4, 5)
+ expect(arrayLength).toBe(circularArray.size)
+ expect(circularArray.length).toBe(circularArray.size)
+ expect(circularArray).toStrictEqual(new CircularArray(4, 2, 3, 4, 5))
+ arrayLength = circularArray.push(6, 7)
+ expect(arrayLength).toBe(circularArray.size)
+ expect(circularArray.length).toBe(circularArray.size)
+ expect(circularArray).toStrictEqual(new CircularArray(4, 4, 5, 6, 7))
+ circularArray = new CircularArray(100)
+ arrayLength = circularArray.push(1, 2, 3, 4, 5)
+ expect(arrayLength).toBe(5)
+ expect(circularArray.size).toBe(100)
+ expect(circularArray.length).toBe(5)
+ expect(circularArray).toStrictEqual(new CircularArray(100, 1, 2, 3, 4, 5))
+ })
+
+ it('Verify that circular array splice works as intended', () => {
+ let circularArray = new CircularArray(1000, 1, 2, 3, 4, 5)
+ let deletedItems = circularArray.splice(2)
+ expect(deletedItems).toStrictEqual(new CircularArray(3, 3, 4, 5))
+ expect(circularArray.length).toBe(2)
+ expect(circularArray).toStrictEqual(new CircularArray(1000, 1, 2))
+ circularArray = new CircularArray(1000, 1, 2, 3, 4, 5)
+ deletedItems = circularArray.splice(2, 1)
+ expect(deletedItems).toStrictEqual(new CircularArray(1, 3))
+ expect(circularArray.length).toBe(4)
+ expect(circularArray).toStrictEqual(new CircularArray(1000, 1, 2, 4, 5))
+ circularArray = new CircularArray(4, 1, 2, 3, 4)
+ deletedItems = circularArray.splice(2, 1, 5, 6)
+ expect(deletedItems).toStrictEqual(new CircularArray(1, 3))
+ expect(circularArray.length).toBe(4)
+ expect(circularArray).toStrictEqual(new CircularArray(4, 2, 4, 5, 6))
+ })
+
+ it('Verify that circular array concat works as intended', () => {
+ let circularArray = new CircularArray(5, 1, 2, 3, 4, 5)
+ circularArray = circularArray.concat(6, 7)
+ expect(circularArray.length).toBe(5)
+ expect(circularArray).toStrictEqual(new CircularArray(5, 3, 4, 5, 6, 7))
+ circularArray = new CircularArray(1)
+ circularArray = circularArray.concat(6, 7)
+ expect(circularArray.length).toBe(1)
+ expect(circularArray).toStrictEqual(new CircularArray(1, 7))
+ })
+
+ it('Verify that circular array unshift works as intended', () => {
+ let circularArray = new CircularArray(5, 1, 2, 3, 4, 5)
+ let arrayLength = circularArray.unshift(6, 7)
+ expect(arrayLength).toBe(5)
+ expect(circularArray.length).toBe(5)
+ expect(circularArray).toStrictEqual(new CircularArray(5, 6, 7, 1, 2, 3))
+ circularArray = new CircularArray(1)
+ arrayLength = circularArray.unshift(6, 7)
+ expect(arrayLength).toBe(1)
+ expect(circularArray.length).toBe(1)
+ expect(circularArray).toStrictEqual(new CircularArray(1, 6))
+ })
+
+ it('Verify that circular array resize works as intended', () => {
+ expect(() => new CircularArray().resize(0.25)).toThrowError(
+ new TypeError('Invalid circular array size: 0.25 is not a safe integer')
+ )
+ expect(() => new CircularArray().resize(-1)).toThrowError(
+ new RangeError('Invalid circular array size: -1 < 0')
+ )
+ expect(() =>
+ new CircularArray().resize(Number.MAX_SAFE_INTEGER + 1)
+ ).toThrowError(
+ new TypeError(
+ `Invalid circular array size: ${
+ Number.MAX_SAFE_INTEGER + 1
+ } is not a safe integer`
+ )
+ )
+ let circularArray = new CircularArray(5, 1, 2, 3, 4, 5)
+ circularArray.resize(0)
+ expect(circularArray.size).toBe(0)
+ expect(circularArray).toStrictEqual(new CircularArray(0))
+ circularArray = new CircularArray(5, 1, 2, 3, 4, 5)
+ circularArray.resize(3)
+ expect(circularArray.size).toBe(3)
+ expect(circularArray).toStrictEqual(new CircularArray(3, 1, 2, 3))
+ circularArray = new CircularArray(5, 1, 2, 3, 4, 5)
+ circularArray.resize(8)
+ expect(circularArray.size).toBe(8)
+ expect(circularArray).toStrictEqual(new CircularArray(8, 1, 2, 3, 4, 5))
+ })
+})
PoolEvents,
WorkerChoiceStrategies
} = require('../../../lib/index')
+const { CircularArray } = require('../../../lib/circular-array')
describe('Abstract pool test suite', () => {
const numberOfWorkers = 1
expect(workerItem.tasksUsage.run).toBe(0)
expect(workerItem.tasksUsage.running).toBe(0)
expect(workerItem.tasksUsage.runTime).toBe(0)
+ expect(workerItem.tasksUsage.runTimeHistory).toBeInstanceOf(CircularArray)
expect(workerItem.tasksUsage.avgRunTime).toBe(0)
+ expect(workerItem.tasksUsage.medRunTime).toBe(0)
expect(workerItem.tasksUsage.error).toBe(0)
}
await pool.destroy()
expect(workerItem.tasksUsage.run).toBe(0)
expect(workerItem.tasksUsage.running).toBe(numberOfWorkers * 2)
expect(workerItem.tasksUsage.runTime).toBe(0)
+ expect(workerItem.tasksUsage.runTimeHistory).toBeInstanceOf(CircularArray)
expect(workerItem.tasksUsage.avgRunTime).toBe(0)
+ expect(workerItem.tasksUsage.medRunTime).toBe(0)
expect(workerItem.tasksUsage.error).toBe(0)
}
await Promise.all(promises)
expect(workerItem.tasksUsage.run).toBe(numberOfWorkers * 2)
expect(workerItem.tasksUsage.running).toBe(0)
expect(workerItem.tasksUsage.runTime).toBeGreaterThanOrEqual(0)
+ expect(workerItem.tasksUsage.runTimeHistory).toBeInstanceOf(CircularArray)
expect(workerItem.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0)
+ expect(workerItem.tasksUsage.medRunTime).toBe(0)
expect(workerItem.tasksUsage.error).toBe(0)
}
await pool.destroy()
expect(workerItem.tasksUsage.run).toBe(numberOfWorkers * 2)
expect(workerItem.tasksUsage.running).toBe(0)
expect(workerItem.tasksUsage.runTime).toBeGreaterThanOrEqual(0)
+ expect(workerItem.tasksUsage.runTimeHistory).toBeInstanceOf(CircularArray)
expect(workerItem.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0)
+ expect(workerItem.tasksUsage.medRunTime).toBe(0)
expect(workerItem.tasksUsage.error).toBe(0)
}
pool.setWorkerChoiceStrategy(WorkerChoiceStrategies.FAIR_SHARE)
expect(workerItem.tasksUsage.run).toBe(0)
expect(workerItem.tasksUsage.running).toBe(0)
expect(workerItem.tasksUsage.runTime).toBe(0)
+ expect(workerItem.tasksUsage.runTimeHistory).toBeInstanceOf(CircularArray)
expect(workerItem.tasksUsage.avgRunTime).toBe(0)
+ expect(workerItem.tasksUsage.medRunTime).toBe(0)
expect(workerItem.tasksUsage.error).toBe(0)
}
await pool.destroy()