Skip to content

Commit

Permalink
Merge pull request #388 from GoogleChromeLabs/FreeQueue-Test
Browse files Browse the repository at this point in the history
FreeQueue: automated test coverage
  • Loading branch information
mjwilson-google authored Aug 14, 2024
2 parents c85c633 + 5082408 commit 5d579b8
Show file tree
Hide file tree
Showing 3 changed files with 248 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/lib/free-queue/free-queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ class FreeQueue {
// The channel count of arraySequence and the length of each channel must
// match with this buffer obejct.

if (arraySequence.length !== this._channelCount) {
throw new Error(`Channel count mismatch: expected ${this._channelCount}, but got ${arraySequence.length}.`);
}

// Transfer data from the |arraySequence| storage to the internal buffer.
const sourceLength = arraySequence[0].length;
for (let i = 0; i < sourceLength; ++i) {
Expand All @@ -215,6 +219,10 @@ class FreeQueue {
// The channel count of arraySequence and the length of each channel must
// match with this buffer obejct.

if (arraySequence.length !== this._channelCount) {
throw new Error(`Channel count mismatch: expected ${this._channelCount}, but got ${arraySequence.length}.`);
}

// If the FIFO is completely empty, do nothing.
if (this._framesAvailable === 0) {
return;
Expand Down
49 changes: 49 additions & 0 deletions src/lib/free-queue/test/free-queue.test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FreeQueue Tests</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mocha/10.6.0/mocha.css"
integrity="sha512-xPbP9qFwQklQFQX6R6+36vNq6mZatPrpfEKUB/zWASwZvfDBy8Y2gEpkRuDlZ0WCiZZGMPyluZif5v2KKxylqg=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
<style>
.head {
margin-top: 5vh;
text-align: center;
}

button {
margin-left: 45vw;
margin-top: 30vh;
padding: 10px 20px;
font-size: 16px;
}
</style>
</head>

<body>
<h1 class="head">FreeQueue Test</h1>
<div id="mocha"></div>
<button id="run-tests">Run Tests</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/10.6.0/mocha.min.js"
integrity="sha512-Wt8lK9Rbdq3uAOR9lm7Fy+XTPP0xie3DzFQ0JTzQLGPvdZ7tDFMqHOJq6hbB4v8uvj7xaBEsINRs7K7HTfi9wA=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/5.1.1/chai.js"
integrity="sha512-HLs4bXIGAstDHJcCm2HfGhBkTUW9oErgCOCiDEnoOhwCEJLcLZUXX7TKcarluZx1D385vuUziYjD1uNGXy9zzg=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
mocha.setup('bdd');
</script>
<!-- We need this coi-serviceworker.js file to make SharedArrayBuffer works properly without any erros -->
<script src="../coi-serviceworker.js"></script>
<script type="module" src="./free-queue.test.js"></script>
<script>
document.getElementById('run-tests').addEventListener('click', function () {
mocha.run();
});
</script>
</body>

</html>
191 changes: 191 additions & 0 deletions src/lib/free-queue/test/free-queue.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { expect } from 'https://cdnjs.cloudflare.com/ajax/libs/chai/5.1.1/chai.js';
import { FreeQueue, MAX_CHANNEL_COUNT, RENDER_QUANTUM_FRAMES } from '../free-queue.js';

// Mock WASM module
const mockWasmModule = {
// Simulate memory allocation
_malloc: (size) => new ArrayBuffer(size),
// Simulate memory deallocation
_free: () => { },
// Simulate HEAPF32
HEAPF32: new Float32Array(1024),
};

describe('FreeQueue Class', () => {
const bufferLength = 1024;
const channelCount = 2;
const maxChannelCount = 4;
let freeQueue = null;

beforeEach(() => {
freeQueue = new FreeQueue(mockWasmModule, bufferLength, channelCount, maxChannelCount);
});

afterEach(() => {
freeQueue.free();
});

describe('Initialization', () => {
it('should initialize with correct properties', () => {
expect(freeQueue.length).to.equal(bufferLength);
expect(freeQueue.numberOfChannels).to.equal(channelCount);
expect(freeQueue.maxChannelCount).to.equal(maxChannelCount);
});

it('should allocate the correct amount of memory', () => {
const dataByteSize = channelCount * bufferLength * Float32Array.BYTES_PER_ELEMENT;
expect(freeQueue.getPointer()).to.be.instanceof(ArrayBuffer);
expect(freeQueue.getPointer().byteLength).to.equal(dataByteSize);
});
});

describe('Channel Adaptation', () => {
it('should adapt to a new channel count within limits', () => {
freeQueue.adaptChannel(3);
expect(freeQueue.numberOfChannels).to.equal(3);
});
it('should not adapt to a channel count exceeding maxChannelCount', () => {
const maxChannelCount = 8;
const initialChannelCount = freeQueue.numberOfChannels;

try {
freeQueue.adaptChannel(maxChannelCount + 1);
} catch (error) {
expect(error).to.be.instanceOf(Error);
expect(error.message).to.include('exceeds the maximum channel count');
}

expect(freeQueue.numberOfChannels).to.equal(initialChannelCount);
});
});

describe('Push Data', () => {
it('should correctly push data', () => {
const testData = [new Float32Array(bufferLength).fill(1), new Float32Array(bufferLength).fill(2)];
freeQueue.push(testData);

const outputData = [new Float32Array(bufferLength), new Float32Array(bufferLength)];
freeQueue.pull(outputData);

expect(outputData[0]).to.deep.equal(testData[0]);
expect(outputData[1]).to.deep.equal(testData[1]);
});

it('should handle buffer overflow correctly', () => {
const testData = [new Float32Array(bufferLength * 2).fill(1), new Float32Array(bufferLength * 2).fill(2)];
freeQueue.push(testData);

expect(freeQueue.framesAvailable).to.equal(bufferLength);
});

it('should handle multiple push cycles', () => {
const testData = [new Float32Array(bufferLength).fill(1), new Float32Array(bufferLength).fill(2)];

for (let i = 0; i < 5; i++) {
freeQueue.push(testData);

const outputData = [new Float32Array(bufferLength), new Float32Array(bufferLength)];
freeQueue.pull(outputData);

expect(outputData[0]).to.deep.equal(testData[0]);
expect(outputData[1]).to.deep.equal(testData[1]);
expect(freeQueue.framesAvailable).to.equal(0);
}
});
});

describe('Pull Data', () => {
it('should correctly pull data', () => {
const testData = [new Float32Array(bufferLength).fill(1), new Float32Array(bufferLength).fill(2)];
freeQueue.push(testData);

const outputData = [new Float32Array(bufferLength), new Float32Array(bufferLength)];
freeQueue.pull(outputData);

expect(outputData[0]).to.deep.equal(testData[0]);
expect(outputData[1]).to.deep.equal(testData[1]);
});

it('should not pull data when buffer is empty', () => {
const outputData = [new Float32Array(bufferLength), new Float32Array(bufferLength)];
freeQueue.pull(outputData);

expect(outputData[0]).to.deep.equal(new Float32Array(bufferLength));
expect(outputData[1]).to.deep.equal(new Float32Array(bufferLength));
});

it('should manage partial data pulls', () => {
const testData = [new Float32Array(bufferLength).fill(1), new Float32Array(bufferLength).fill(2)];
freeQueue.push(testData);

const partialOutput = [new Float32Array(bufferLength / 2), new Float32Array(bufferLength / 2)];
freeQueue.pull(partialOutput);

expect(partialOutput[0]).to.deep.equal(new Float32Array(bufferLength / 2).fill(1));
expect(partialOutput[1]).to.deep.equal(new Float32Array(bufferLength / 2).fill(2));
expect(freeQueue.framesAvailable).to.equal(bufferLength / 2);
});

it('should handle multiple pull cycles', () => {
const testData = [new Float32Array(bufferLength).fill(1), new Float32Array(bufferLength).fill(2)];

for (let i = 0; i < 5; i++) {
freeQueue.push(testData);

const outputData = [new Float32Array(bufferLength), new Float32Array(bufferLength)];
freeQueue.pull(outputData);

expect(outputData[0]).to.deep.equal(testData[0]);
expect(outputData[1]).to.deep.equal(testData[1]);
expect(freeQueue.framesAvailable).to.equal(0);
}
});
});

describe('Error Handling', () => {
it('should return null for invalid channel index in getChannelData', () => {
const invalidIndex = channelCount + 1;
expect(freeQueue.getChannelData(invalidIndex)).to.be.null;
});

it('should throw an error if pushing with mismatched channel count', () => {
const invalidTestData = [new Float32Array(bufferLength).fill(1)];

const expectedChannelCount = freeQueue._channelCount;
const actualChannelCount = invalidTestData.length;

expect(() => freeQueue.push(invalidTestData))
.to.throw(Error, `Channel count mismatch: expected ${expectedChannelCount}, but got ${actualChannelCount}.`);
});

it('should throw an error if pulling with mismatched channel count', () => {
const invalidOutputData = [new Float32Array(bufferLength)];

const expectedChannelCount = freeQueue._channelCount;
const actualChannelCount = invalidOutputData.length;

expect(() => freeQueue.pull(invalidOutputData))
.to.throw(Error, `Channel count mismatch: expected ${expectedChannelCount}, but got ${actualChannelCount}.`);
});
});

describe('Performance Tests', function () {
it('should handle large data efficiently', function () {
const input = [new Float32Array(bufferLength), new Float32Array(bufferLength)];
const output = [new Float32Array(bufferLength), new Float32Array(bufferLength)];
for (let i = 0; i < 10000; i++) {
freeQueue.push(input, bufferLength);
freeQueue.pull(output, bufferLength);
}
});

it('should handle small data efficiently', function () {
const input = [new Float32Array(1), new Float32Array(1)];
const output = [new Float32Array(1), new Float32Array(1)];
for (let i = 0; i < 1000000; i++) {
freeQueue.push(input, 1);
freeQueue.pull(output, 1);
}
});
});
});

0 comments on commit 5d579b8

Please sign in to comment.