summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/IndexedDB/blob-composite-blob-reads.any.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/IndexedDB/blob-composite-blob-reads.any.js
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/IndexedDB/blob-composite-blob-reads.any.js')
-rw-r--r--testing/web-platform/tests/IndexedDB/blob-composite-blob-reads.any.js155
1 files changed, 155 insertions, 0 deletions
diff --git a/testing/web-platform/tests/IndexedDB/blob-composite-blob-reads.any.js b/testing/web-platform/tests/IndexedDB/blob-composite-blob-reads.any.js
new file mode 100644
index 0000000000..7d6ab9be35
--- /dev/null
+++ b/testing/web-platform/tests/IndexedDB/blob-composite-blob-reads.any.js
@@ -0,0 +1,155 @@
+// META: title=IDB-backed composite blobs maintain coherency
+// META: script=resources/support-promises.js
+// META: timeout=long
+
+// This test file is intended to help validate browser handling of complex blob
+// scenarios where one or more levels of multipart blobs are used and varying
+// IPC serialization strategies may be used depending on various complexity
+// heuristics.
+//
+// A variety of approaches of reading the blob's contents are attempted for
+// completeness:
+// - `fetch-blob-url`: fetch of a URL created via URL.createObjectURL
+// - Note that this is likely to involve multi-process behavior in a way that
+// the next 2 currently will not unless their Blobs are round-tripped
+// through a MessagePort.
+// - `file-reader`: FileReader
+// - `direct`: Blob.prototype.arrayBuffer()
+
+function composite_blob_test({ blobCount, blobSize, name }) {
+ // NOTE: In order to reduce the runtime of this test and due to the similarity
+ // of the "file-reader" mechanism to the "direct", "file-reader" is commented
+ // out, but if you are investigating failures detected by this test, you may
+ // want to uncomment it.
+ for (const mode of ["fetch-blob-url", /*"file-reader",*/ "direct"]) {
+ promise_test(async testCase => {
+ const key = "the-blobs";
+ let memBlobs = [];
+ for (let iBlob = 0; iBlob < blobCount; iBlob++) {
+ memBlobs.push(new Blob([make_arraybuffer_contents(iBlob, blobSize)]));
+ }
+
+ const db = await createDatabase(testCase, db => {
+ db.createObjectStore("blobs");
+ });
+
+ const write_tx = db.transaction("blobs", "readwrite", {durability: "relaxed"});
+ let store = write_tx.objectStore("blobs");
+ store.put(memBlobs, key);
+ // Make the blobs eligible for GC which is most realistic and most likely
+ // to cause problems.
+ memBlobs = null;
+
+ await promiseForTransaction(testCase, write_tx);
+
+ const read_tx = db.transaction("blobs", "readonly", {durability: "relaxed"});
+ store = read_tx.objectStore("blobs");
+ const read_req = store.get(key);
+
+ await promiseForTransaction(testCase, read_tx);
+
+ const diskBlobs = read_req.result;
+ const compositeBlob = new Blob(diskBlobs);
+
+ if (mode === "fetch-blob-url") {
+ const blobUrl = URL.createObjectURL(compositeBlob);
+ let urlResp = await fetch(blobUrl);
+ let urlFetchArrayBuffer = await urlResp.arrayBuffer();
+ urlResp = null;
+
+ URL.revokeObjectURL(blobUrl);
+ validate_arraybuffer_contents("fetched URL", urlFetchArrayBuffer, blobCount, blobSize);
+ urlFetchArrayBuffer = null;
+
+ } else if (mode === "file-reader") {
+ let reader = new FileReader();
+ let readerPromise = new Promise(resolve => {
+ reader.onload = () => {
+ resolve(reader.result);
+ }
+ })
+ reader.readAsArrayBuffer(compositeBlob);
+
+ let readArrayBuffer = await readerPromise;
+ readerPromise = null;
+ reader = null;
+
+ validate_arraybuffer_contents("FileReader", readArrayBuffer, blobCount, blobSize);
+ readArrayBuffer = null;
+ } else if (mode === "direct") {
+ let directArrayBuffer = await compositeBlob.arrayBuffer();
+ validate_arraybuffer_contents("arrayBuffer", directArrayBuffer, blobCount, blobSize);
+ }
+ }, `Composite Blob Handling: ${name}: ${mode}`);
+ }
+}
+
+// Create an ArrayBuffer whose even bytes are the index identifier and whose
+// odd bytes are a sequence incremented by 3 (wrapping at 256) so that
+// discontinuities at power-of-2 boundaries are more detectable.
+function make_arraybuffer_contents(index, size) {
+ const arr = new Uint8Array(size);
+ for (let i = 0, counter = 0; i < size; i += 2, counter = (counter + 3) % 256) {
+ arr[i] = index;
+ arr[i + 1] = counter;
+ }
+ return arr.buffer;
+}
+
+function validate_arraybuffer_contents(source, buffer, blobCount, blobSize) {
+ // Accumulate a list of problems we perceive so we can report what seems to
+ // have happened all at once.
+ const problems = [];
+
+ const arr = new Uint8Array(buffer);
+
+ const expectedLength = blobCount * blobSize;
+ const actualCount = arr.length / blobSize;
+ if (arr.length !== expectedLength) {
+ problems.push(`ArrayBuffer only holds ${actualCount} blobs' worth instead of ${blobCount}.`);
+ problems.push(`Actual ArrayBuffer is ${arr.length} bytes but expected ${expectedLength}`);
+ }
+
+ const counterBlobStep = (blobSize / 2 * 3) % 256;
+ let expectedBlob = 0;
+ let blobSeenSoFar = 0;
+ let expectedCounter = 0;
+ let counterDrift = 0;
+ for (let i = 0; i < arr.length; i += 2) {
+ if (arr[i] !== expectedBlob || blobSeenSoFar >= blobSize) {
+ if (blobSeenSoFar !== blobSize) {
+ problems.push(`Truncated blob ${expectedBlob} after ${blobSeenSoFar} bytes.`);
+ } else {
+ expectedBlob++;
+ }
+ if (expectedBlob !== arr[i]) {
+ problems.push(`Expected blob ${expectedBlob} but found ${arr[i]}, compensating.`);
+ expectedBlob = arr[i];
+ }
+ blobSeenSoFar = 0;
+ expectedCounter = (expectedBlob * counterBlobStep) % 256;
+ counterDrift = 0;
+ }
+
+ if (arr[i + 1] !== (expectedCounter + counterDrift) % 256) {
+ const newDrift = expectedCounter - arr[i + 1];
+ problems.push(`In blob ${expectedBlob} at ${blobSeenSoFar + 1} bytes in, counter drift now ${newDrift} was ${counterDrift}`);
+ counterDrift = newDrift;
+ }
+
+ blobSeenSoFar += 2;
+ expectedCounter = (expectedCounter + 3) % 256;
+ }
+
+ if (problems.length) {
+ assert_true(false, `${source} blob payload problem: ${problems.join("\n")}`);
+ } else {
+ assert_true(true, `${source} blob payloads validated.`);
+ }
+}
+
+composite_blob_test({
+ blobCount: 16,
+ blobSize: 256 * 1024,
+ name: "Many blobs",
+});