Skip to content

Commit

Permalink
Added Test Cases For FreeQueue Class
Browse files Browse the repository at this point in the history
  • Loading branch information
professorabhay committed Jul 23, 2024
1 parent 0161cce commit 91f6571
Show file tree
Hide file tree
Showing 5 changed files with 309 additions and 0 deletions.
1 change: 1 addition & 0 deletions .eleventy.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ module.exports = function(eleventyConfig) {
'src/README.md',
'src/sitemap.xml',
'src/lib/**/*.js',
'src/lib/**/*.html',
].map(path => eleventyConfig.addPassthroughCopy(path));

// eleventyConfig.addPassthroughCopy('src/favicon');
Expand Down
115 changes: 115 additions & 0 deletions src/lib/free-queue/coi-serviceworker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*! coi-serviceworker v0.1.6 - Guido Zuidhof, licensed under MIT */
let coepCredentialless = false;
if (typeof window === 'undefined') {
self.addEventListener("install", () => self.skipWaiting());
self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim()));

self.addEventListener("message", (ev) => {
if (!ev.data) {
return;
} else if (ev.data.type === "deregister") {
self.registration
.unregister()
.then(() => {
return self.clients.matchAll();
})
.then(clients => {
clients.forEach((client) => client.navigate(client.url));
});
} else if (ev.data.type === "coepCredentialless") {
coepCredentialless = ev.data.value;
}
});

self.addEventListener("fetch", function (event) {
const r = event.request;
if (r.cache === "only-if-cached" && r.mode !== "same-origin") {
return;
}

const request = (coepCredentialless && r.mode === "no-cors")
? new Request(r, {
credentials: "omit",
})
: r;
event.respondWith(
fetch(request)
.then((response) => {
if (response.status === 0) {
return response;
}

const newHeaders = new Headers(response.headers);
newHeaders.set("Cross-Origin-Embedder-Policy",
coepCredentialless ? "credentialless" : "require-corp"
);
newHeaders.set("Cross-Origin-Opener-Policy", "same-origin");

return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders,
});
})
.catch((e) => console.error(e))
);
});

} else {
(() => {
// You can customize the behavior of this script through a global `coi` variable.
const coi = {
shouldRegister: () => true,
shouldDeregister: () => false,
coepCredentialless: () => false,
doReload: () => window.location.reload(),
quiet: false,
...window.coi
};

const n = navigator;

if (n.serviceWorker && n.serviceWorker.controller) {
n.serviceWorker.controller.postMessage({
type: "coepCredentialless",
value: coi.coepCredentialless(),
});

if (coi.shouldDeregister()) {
n.serviceWorker.controller.postMessage({ type: "deregister" });
}
}

// If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are
// already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here.
if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return;

if (!window.isSecureContext) {
!coi.quiet && console.log("COOP/COEP Service Worker not registered, a secure context is required.");
return;
}

// In some environments (e.g. Chrome incognito mode) this won't be available
if (n.serviceWorker) {
n.serviceWorker.register(window.document.currentScript.src).then(
(registration) => {
!coi.quiet && console.log("COOP/COEP Service Worker registered", registration.scope);

registration.addEventListener("updatefound", () => {
!coi.quiet && console.log("Reloading page to make use of updated COOP/COEP Service Worker.");
coi.doReload();
});

// If the registration is active, but it's not controlling the page
if (registration.active && !n.serviceWorker.controller) {
!coi.quiet && console.log("Reloading page to make use of COOP/COEP Service Worker.");
coi.doReload();
}
},
(err) => {
!coi.quiet && console.error("COOP/COEP Service Worker failed to register:", err);
}
);
}
})();
}
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');
}

// 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');
}

// If the FIFO is completely empty, do nothing.
if (this._framesAvailable === 0) {
return;
Expand Down
38 changes: 38 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,38 @@
<!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>
<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
147 changes: 147 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,147 @@
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 = {
_malloc: (size) => new ArrayBuffer(size), // Simulate memory allocation
_free: () => {}, // Simulate memory deallocation
HEAPF32: new Float32Array(1024), // Simulate HEAPF32
};

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

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);
});
});

describe('Pushing and Pulling Data', () => {
it('should correctly push and 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 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 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 push and 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)];
expect(() => freeQueue.push(invalidTestData)).to.throw(Error, 'Channel count mismatch');
});

it('should throw an error if pulling with mismatched channel count', () => {
const invalidOutputData = [new Float32Array(bufferLength)];
expect(() => freeQueue.pull(invalidOutputData)).to.throw(Error, 'Channel count mismatch');
});
});

describe('Performance', () => {
it('should efficiently handle large data transfers', function() {
this.timeout(5000);
const largeBuffer = 1024 * 1024; // 1 MB buffer
const testData = [new Float32Array(largeBuffer).fill(1), new Float32Array(largeBuffer).fill(2)];

const start = performance.now();
freeQueue.push(testData);
freeQueue.pull(testData);
const end = performance.now();

expect(end - start).to.be.below(1000); // Ensure operations complete within 1 second
});

it('should perform consistently over many operations', function() {
this.timeout(10000);
const iterations = 10000;
const testData = [new Float32Array(RENDER_QUANTUM_FRAMES).fill(1), new Float32Array(RENDER_QUANTUM_FRAMES).fill(2)];

const start = performance.now();
for (let i = 0; i < iterations; i++) {
freeQueue.push(testData);
freeQueue.pull(testData);
}
const end = performance.now();

expect(end - start).to.be.below(5000); // Ensure it completes within 5 seconds
});
});
});

0 comments on commit 91f6571

Please sign in to comment.