7d6ab9be3591ad04b3b2e02e97a2bbdb0dc6c3cc
[gecko.git] / blob-composite-blob-reads.any.js
blob7d6ab9be3591ad04b3b2e02e97a2bbdb0dc6c3cc
1 // META: title=IDB-backed composite blobs maintain coherency
2 // META: script=resources/support-promises.js
3 // META: timeout=long
5 // This test file is intended to help validate browser handling of complex blob
6 // scenarios where one or more levels of multipart blobs are used and varying
7 // IPC serialization strategies may be used depending on various complexity
8 // heuristics.
9 //
10 // A variety of approaches of reading the blob's contents are attempted for
11 // completeness:
12 // - `fetch-blob-url`: fetch of a URL created via URL.createObjectURL
13 //   - Note that this is likely to involve multi-process behavior in a way that
14 //     the next 2 currently will not unless their Blobs are round-tripped
15 //     through a MessagePort.
16 // - `file-reader`: FileReader
17 // - `direct`: Blob.prototype.arrayBuffer()
19 function composite_blob_test({ blobCount, blobSize, name }) {
20   // NOTE: In order to reduce the runtime of this test and due to the similarity
21   // of the "file-reader" mechanism to the "direct", "file-reader" is commented
22   // out, but if you are investigating failures detected by this test, you may
23   // want to uncomment it.
24   for (const mode of ["fetch-blob-url", /*"file-reader",*/ "direct"]) {
25     promise_test(async testCase => {
26       const key = "the-blobs";
27       let memBlobs = [];
28       for (let iBlob = 0; iBlob < blobCount; iBlob++) {
29         memBlobs.push(new Blob([make_arraybuffer_contents(iBlob, blobSize)]));
30       }
32       const db = await createDatabase(testCase, db => {
33         db.createObjectStore("blobs");
34       });
36       const write_tx = db.transaction("blobs", "readwrite", {durability: "relaxed"});
37       let store = write_tx.objectStore("blobs");
38       store.put(memBlobs, key);
39       // Make the blobs eligible for GC which is most realistic and most likely
40       // to cause problems.
41       memBlobs = null;
43       await promiseForTransaction(testCase, write_tx);
45       const read_tx = db.transaction("blobs", "readonly", {durability: "relaxed"});
46       store = read_tx.objectStore("blobs");
47       const read_req = store.get(key);
49       await promiseForTransaction(testCase, read_tx);
51       const diskBlobs = read_req.result;
52       const compositeBlob = new Blob(diskBlobs);
54       if (mode === "fetch-blob-url") {
55         const blobUrl = URL.createObjectURL(compositeBlob);
56         let urlResp = await fetch(blobUrl);
57         let urlFetchArrayBuffer = await urlResp.arrayBuffer();
58         urlResp = null;
60         URL.revokeObjectURL(blobUrl);
61         validate_arraybuffer_contents("fetched URL", urlFetchArrayBuffer, blobCount, blobSize);
62         urlFetchArrayBuffer = null;
64       } else if (mode === "file-reader") {
65         let reader = new FileReader();
66         let readerPromise = new Promise(resolve => {
67           reader.onload = () => {
68             resolve(reader.result);
69           }
70         })
71         reader.readAsArrayBuffer(compositeBlob);
73         let readArrayBuffer = await readerPromise;
74         readerPromise = null;
75         reader = null;
77         validate_arraybuffer_contents("FileReader", readArrayBuffer, blobCount, blobSize);
78         readArrayBuffer = null;
79       } else if (mode === "direct") {
80         let directArrayBuffer = await compositeBlob.arrayBuffer();
81         validate_arraybuffer_contents("arrayBuffer", directArrayBuffer, blobCount, blobSize);
82       }
83     }, `Composite Blob Handling: ${name}: ${mode}`);
84   }
87 // Create an ArrayBuffer whose even bytes are the index identifier and whose
88 // odd bytes are a sequence incremented by 3 (wrapping at 256) so that
89 // discontinuities at power-of-2 boundaries are more detectable.
90 function make_arraybuffer_contents(index, size) {
91   const arr = new Uint8Array(size);
92   for (let i = 0, counter = 0; i < size; i += 2, counter = (counter + 3) % 256) {
93     arr[i] = index;
94     arr[i + 1] = counter;
95   }
96   return arr.buffer;
99 function validate_arraybuffer_contents(source, buffer, blobCount, blobSize) {
100   // Accumulate a list of problems we perceive so we can report what seems to
101   // have happened all at once.
102   const problems = [];
104   const arr = new Uint8Array(buffer);
106   const expectedLength = blobCount * blobSize;
107   const actualCount = arr.length / blobSize;
108   if (arr.length !== expectedLength) {
109     problems.push(`ArrayBuffer only holds ${actualCount} blobs' worth instead of ${blobCount}.`);
110     problems.push(`Actual ArrayBuffer is ${arr.length} bytes but expected ${expectedLength}`);
111   }
113   const counterBlobStep = (blobSize / 2 * 3) % 256;
114   let expectedBlob = 0;
115   let blobSeenSoFar = 0;
116   let expectedCounter = 0;
117   let counterDrift = 0;
118   for (let i = 0; i < arr.length; i += 2) {
119     if (arr[i] !== expectedBlob || blobSeenSoFar >= blobSize) {
120       if (blobSeenSoFar !== blobSize) {
121         problems.push(`Truncated blob ${expectedBlob} after ${blobSeenSoFar} bytes.`);
122       } else {
123         expectedBlob++;
124       }
125       if (expectedBlob !== arr[i]) {
126         problems.push(`Expected blob ${expectedBlob} but found ${arr[i]}, compensating.`);
127         expectedBlob = arr[i];
128       }
129       blobSeenSoFar = 0;
130       expectedCounter = (expectedBlob * counterBlobStep) % 256;
131       counterDrift = 0;
132     }
134     if (arr[i + 1] !== (expectedCounter + counterDrift) % 256) {
135       const newDrift = expectedCounter - arr[i + 1];
136       problems.push(`In blob ${expectedBlob} at ${blobSeenSoFar + 1} bytes in, counter drift now ${newDrift} was ${counterDrift}`);
137       counterDrift = newDrift;
138     }
140     blobSeenSoFar += 2;
141     expectedCounter = (expectedCounter + 3) % 256;
142   }
144   if (problems.length) {
145     assert_true(false, `${source} blob payload problem: ${problems.join("\n")}`);
146   } else {
147     assert_true(true, `${source} blob payloads validated.`);
148   }
151 composite_blob_test({
152   blobCount: 16,
153   blobSize: 256 * 1024,
154   name: "Many blobs",