diff options
Diffstat (limited to 'dom/file/tests/common_fileReader.js')
-rw-r--r-- | dom/file/tests/common_fileReader.js | 848 |
1 files changed, 848 insertions, 0 deletions
diff --git a/dom/file/tests/common_fileReader.js b/dom/file/tests/common_fileReader.js new file mode 100644 index 0000000000..9ee6a32b93 --- /dev/null +++ b/dom/file/tests/common_fileReader.js @@ -0,0 +1,848 @@ +function test_setup() { + return new Promise(resolve => { + const minFileSize = 20000; + + // Create strings containing data we'll test with. We'll want long + // strings to ensure they span multiple buffers while loading + let testTextData = "asd b\tlah\u1234w\u00a0r"; + while (testTextData.length < minFileSize) { + testTextData = testTextData + testTextData; + } + + let testASCIIData = "abcdef 123456\n"; + while (testASCIIData.length < minFileSize) { + testASCIIData = testASCIIData + testASCIIData; + } + + let testBinaryData = ""; + for (let i = 0; i < 256; i++) { + testBinaryData += String.fromCharCode(i); + } + while (testBinaryData.length < minFileSize) { + testBinaryData = testBinaryData + testBinaryData; + } + + let dataurldata0 = testBinaryData.substr( + 0, + testBinaryData.length - (testBinaryData.length % 3) + ); + let dataurldata1 = testBinaryData.substr( + 0, + testBinaryData.length - 2 - (testBinaryData.length % 3) + ); + let dataurldata2 = testBinaryData.substr( + 0, + testBinaryData.length - 1 - (testBinaryData.length % 3) + ); + + //Set up files for testing + let openerURL = SimpleTest.getTestFileURL("fileapi_chromeScript.js"); + let opener = SpecialPowers.loadChromeScript(openerURL); + + opener.addMessageListener("files.opened", message => { + let [ + asciiFile, + binaryFile, + nonExistingFile, + utf8TextFile, + utf16TextFile, + emptyFile, + dataUrlFile0, + dataUrlFile1, + dataUrlFile2, + ] = message; + + resolve({ + blobs: { + asciiFile, + binaryFile, + nonExistingFile, + utf8TextFile, + utf16TextFile, + emptyFile, + dataUrlFile0, + dataUrlFile1, + dataUrlFile2, + }, + data: { + text: testTextData, + ascii: testASCIIData, + binary: testBinaryData, + url0: dataurldata0, + url1: dataurldata1, + url2: dataurldata2, + }, + }); + }); + + opener.sendAsyncMessage("files.open", [ + testASCIIData, + testBinaryData, + null, + convertToUTF8(testTextData), + convertToUTF16(testTextData), + "", + dataurldata0, + dataurldata1, + dataurldata2, + ]); + }); +} + +function runBasicTests(data) { + return test_basic() + .then(() => { + return test_readAsText(data.blobs.asciiFile, data.data.ascii); + }) + .then(() => { + return test_readAsBinaryString(data.blobs.binaryFile, data.data.binary); + }) + .then(() => { + return test_readAsArrayBuffer(data.blobs.binaryFile, data.data.binary); + }); +} + +function runEncodingTests(data) { + return test_readAsTextWithEncoding( + data.blobs.asciiFile, + data.data.ascii, + data.data.ascii.length, + "" + ) + .then(() => { + return test_readAsTextWithEncoding( + data.blobs.asciiFile, + data.data.ascii, + data.data.ascii.length, + "iso8859-1" + ); + }) + .then(() => { + return test_readAsTextWithEncoding( + data.blobs.utf8TextFile, + data.data.text, + convertToUTF8(data.data.text).length, + "utf8" + ); + }) + .then(() => { + return test_readAsTextWithEncoding( + data.blobs.utf16TextFile, + data.data.text, + convertToUTF16(data.data.text).length, + "utf-16" + ); + }) + .then(() => { + return test_readAsTextWithEncoding(data.blobs.emptyFile, "", 0, ""); + }) + .then(() => { + return test_readAsTextWithEncoding(data.blobs.emptyFile, "", 0, "utf8"); + }) + .then(() => { + return test_readAsTextWithEncoding(data.blobs.emptyFile, "", 0, "utf-16"); + }); +} + +function runEmptyTests(data) { + return test_onlyResult() + .then(() => { + return test_readAsText(data.blobs.emptyFile, ""); + }) + .then(() => { + return test_readAsBinaryString(data.blobs.emptyFile, ""); + }) + .then(() => { + return test_readAsArrayBuffer(data.blobs.emptyFile, ""); + }) + .then(() => { + return test_readAsDataURL(data.blobs.emptyFile, convertToDataURL(""), 0); + }); +} + +function runTwiceTests(data) { + return test_readAsTextTwice(data.blobs.asciiFile, data.data.ascii) + .then(() => { + return test_readAsBinaryStringTwice( + data.blobs.binaryFile, + data.data.binary + ); + }) + .then(() => { + return test_readAsDataURLTwice( + data.blobs.binaryFile, + convertToDataURL(data.data.binary), + data.data.binary.length + ); + }) + .then(() => { + return test_readAsArrayBufferTwice( + data.blobs.binaryFile, + data.data.binary + ); + }) + .then(() => { + return test_readAsArrayBufferTwice2( + data.blobs.binaryFile, + data.data.binary + ); + }); +} + +function runOtherTests(data) { + return test_readAsDataURL_customLength( + data.blobs.dataUrlFile0, + convertToDataURL(data.data.url0), + data.data.url0.length, + 0 + ) + .then(() => { + return test_readAsDataURL_customLength( + data.blobs.dataUrlFile1, + convertToDataURL(data.data.url1), + data.data.url1.length, + 1 + ); + }) + .then(() => { + return test_readAsDataURL_customLength( + data.blobs.dataUrlFile2, + convertToDataURL(data.data.url2), + data.data.url2.length, + 2 + ); + }) + .then(() => { + return test_abort(data.blobs.asciiFile); + }) + .then(() => { + return test_abort_readAsX(data.blobs.asciiFile, data.data.ascii); + }) + .then(() => { + return test_nonExisting(data.blobs.nonExistingFile); + }); +} + +function convertToUTF16(s) { + let res = ""; + for (let i = 0; i < s.length; ++i) { + c = s.charCodeAt(i); + res += String.fromCharCode(c & 255, c >>> 8); + } + return res; +} + +function convertToUTF8(s) { + return unescape(encodeURIComponent(s)); +} + +function convertToDataURL(s) { + return "data:application/octet-stream;base64," + btoa(s); +} + +function loadEventHandler_string( + event, + resolve, + reader, + data, + dataLength, + testName +) { + is(event.target, reader, "Correct target."); + is( + event.target.readyState, + FileReader.DONE, + "readyState in test " + testName + ); + is(event.target.error, null, "no error in test " + testName); + is(event.target.result, data, "result in test " + testName); + is(event.lengthComputable, true, "lengthComputable in test " + testName); + is(event.loaded, dataLength, "loaded in test " + testName); + is(event.total, dataLength, "total in test " + testName); + resolve(); +} + +function loadEventHandler_arrayBuffer(event, resolve, reader, data, testName) { + is( + event.target.readyState, + FileReader.DONE, + "readyState in test " + testName + ); + is(event.target.error, null, "no error in test " + testName); + is(event.lengthComputable, true, "lengthComputable in test " + testName); + is(event.loaded, data.length, "loaded in test " + testName); + is(event.total, data.length, "total in test " + testName); + is( + event.target.result.byteLength, + data.length, + "array buffer size in test " + testName + ); + + let u8v = new Uint8Array(event.target.result); + is( + String.fromCharCode.apply(String, u8v), + data, + "array buffer contents in test " + testName + ); + u8v = null; + + if ("SpecialPowers" in self) { + SpecialPowers.gc(); + + is( + event.target.result.byteLength, + data.length, + "array buffer size after gc in test " + testName + ); + u8v = new Uint8Array(event.target.result); + is( + String.fromCharCode.apply(String, u8v), + data, + "array buffer contents after gc in test " + testName + ); + } + + resolve(); +} + +function test_basic() { + return new Promise(resolve => { + is(FileReader.EMPTY, 0, "correct EMPTY value"); + is(FileReader.LOADING, 1, "correct LOADING value"); + is(FileReader.DONE, 2, "correct DONE value"); + resolve(); + }); +} + +function test_readAsText(blob, text) { + return new Promise(resolve => { + let onloadHasRun = false; + let onloadStartHasRun = false; + + let r = new FileReader(); + is(r.readyState, FileReader.EMPTY, "correct initial text readyState"); + + r.onload = event => { + loadEventHandler_string( + event, + resolve, + r, + text, + text.length, + "readAsText" + ); + }; + + r.addEventListener("load", () => { + onloadHasRun = true; + }); + r.addEventListener("loadstart", () => { + onloadStartHasRun = true; + }); + + r.readAsText(blob); + + is(r.readyState, FileReader.LOADING, "correct loading text readyState"); + is(onloadHasRun, false, "text loading must be async"); + is(onloadStartHasRun, false, "text loadstart should fire async"); + }); +} + +function test_readAsBinaryString(blob, text) { + return new Promise(resolve => { + let onloadHasRun = false; + let onloadStartHasRun = false; + + let r = new FileReader(); + is(r.readyState, FileReader.EMPTY, "correct initial binary readyState"); + + r.addEventListener("load", function () { + onloadHasRun = true; + }); + r.addEventListener("loadstart", function () { + onloadStartHasRun = true; + }); + + r.readAsBinaryString(blob); + + r.onload = event => { + loadEventHandler_string( + event, + resolve, + r, + text, + text.length, + "readAsBinaryString" + ); + }; + + is(r.readyState, FileReader.LOADING, "correct loading binary readyState"); + is(onloadHasRun, false, "binary loading must be async"); + is(onloadStartHasRun, false, "binary loadstart should fire async"); + }); +} + +function test_readAsArrayBuffer(blob, text) { + return new Promise(resolve => { + let onloadHasRun = false; + let onloadStartHasRun = false; + + r = new FileReader(); + is( + r.readyState, + FileReader.EMPTY, + "correct initial arrayBuffer readyState" + ); + + r.addEventListener("load", function () { + onloadHasRun = true; + }); + r.addEventListener("loadstart", function () { + onloadStartHasRun = true; + }); + + r.readAsArrayBuffer(blob); + + r.onload = event => { + loadEventHandler_arrayBuffer( + event, + resolve, + r, + text, + "readAsArrayBuffer" + ); + }; + + is( + r.readyState, + FileReader.LOADING, + "correct loading arrayBuffer readyState" + ); + is(onloadHasRun, false, "arrayBuffer loading must be async"); + is(onloadStartHasRun, false, "arrayBuffer loadstart should fire sync"); + }); +} + +// Test a variety of encodings, and make sure they work properly +function test_readAsTextWithEncoding(blob, text, length, charset) { + return new Promise(resolve => { + let r = new FileReader(); + r.onload = event => { + loadEventHandler_string( + event, + resolve, + r, + text, + length, + "readAsText-" + charset + ); + }; + r.readAsText(blob, charset); + }); +} + +// Test get result without reading +function test_onlyResult() { + return new Promise(resolve => { + let r = new FileReader(); + is( + r.readyState, + FileReader.EMPTY, + "readyState in test reader get result without reading" + ); + is(r.error, null, "no error in test reader get result without reading"); + is(r.result, null, "result in test reader get result without reading"); + resolve(); + }); +} + +function test_readAsDataURL(blob, text, length) { + return new Promise(resolve => { + let r = new FileReader(); + r.onload = event => { + loadEventHandler_string(event, resolve, r, text, length, "readAsDataURL"); + }; + r.readAsDataURL(blob); + }); +} + +// Test reusing a FileReader to read multiple times +function test_readAsTextTwice(blob, text) { + return new Promise(resolve => { + let r = new FileReader(); + r.onload = event => { + loadEventHandler_string( + event, + () => {}, + r, + text, + text.length, + "readAsText-reused-once" + ); + }; + + let anotherListener = event => { + let r1 = event.target; + r1.removeEventListener("load", anotherListener); + r1.onload = evt => { + loadEventHandler_string( + evt, + resolve, + r1, + text, + text.length, + "readAsText-reused-twice" + ); + }; + r1.readAsText(blob); + }; + + r.addEventListener("load", anotherListener); + r.readAsText(blob); + }); +} + +// Test reusing a FileReader to read multiple times +function test_readAsBinaryStringTwice(blob, text) { + return new Promise(resolve => { + let r = new FileReader(); + r.onload = event => { + loadEventHandler_string( + event, + () => {}, + r, + text, + text.length, + "readAsBinaryString-reused-once" + ); + }; + + let anotherListener = event => { + let r1 = event.target; + r1.removeEventListener("load", anotherListener); + r1.onload = evt => { + loadEventHandler_string( + evt, + resolve, + r1, + text, + text.length, + "readAsBinaryString-reused-twice" + ); + }; + r1.readAsBinaryString(blob); + }; + + r.addEventListener("load", anotherListener); + r.readAsBinaryString(blob); + }); +} + +function test_readAsDataURLTwice(blob, text, length) { + return new Promise(resolve => { + let r = new FileReader(); + r.onload = event => { + loadEventHandler_string( + event, + () => {}, + r, + text, + length, + "readAsDataURL-reused-once" + ); + }; + + let anotherListener = event => { + let r1 = event.target; + r1.removeEventListener("load", anotherListener); + r1.onload = evt => { + loadEventHandler_string( + evt, + resolve, + r1, + text, + length, + "readAsDataURL-reused-twice" + ); + }; + r1.readAsDataURL(blob); + }; + + r.addEventListener("load", anotherListener); + r.readAsDataURL(blob); + }); +} + +function test_readAsArrayBufferTwice(blob, text) { + return new Promise(resolve => { + let r = new FileReader(); + r.onload = event => { + loadEventHandler_arrayBuffer( + event, + () => {}, + r, + text, + "readAsArrayBuffer-reused-once" + ); + }; + + let anotherListener = event => { + let r1 = event.target; + r1.removeEventListener("load", anotherListener); + r1.onload = evt => { + loadEventHandler_arrayBuffer( + evt, + resolve, + r1, + text, + "readAsArrayBuffer-reused-twice" + ); + }; + r1.readAsArrayBuffer(blob); + }; + + r.addEventListener("load", anotherListener); + r.readAsArrayBuffer(blob); + }); +} + +// Test first reading as ArrayBuffer then read as something else (BinaryString) +// and doesn't crash +function test_readAsArrayBufferTwice2(blob, text) { + return new Promise(resolve => { + let r = new FileReader(); + r.onload = event => { + loadEventHandler_arrayBuffer( + event, + () => {}, + r, + text, + "readAsArrayBuffer-reused-once2" + ); + }; + + let anotherListener = event => { + let r1 = event.target; + r1.removeEventListener("load", anotherListener); + r1.onload = evt => { + loadEventHandler_string( + evt, + resolve, + r1, + text, + text.length, + "readAsArrayBuffer-reused-twice2" + ); + }; + r1.readAsBinaryString(blob); + }; + + r.addEventListener("load", anotherListener); + r.readAsArrayBuffer(blob); + }); +} + +function test_readAsDataURL_customLength(blob, text, length, numb) { + return new Promise(resolve => { + is(length % 3, numb, "Want to test data with length %3 == " + numb); + let r = new FileReader(); + r.onload = event => { + loadEventHandler_string( + event, + resolve, + r, + text, + length, + "dataurl reading, %3 = " + numb + ); + }; + r.readAsDataURL(blob); + }); +} + +// Test abort() +function test_abort(blob) { + return new Promise(resolve => { + let abortHasRun = false; + let loadEndHasRun = false; + + let r = new FileReader(); + + r.onabort = function (event) { + is(abortHasRun, false, "abort should only fire once"); + is(loadEndHasRun, false, "loadend shouldn't have fired yet"); + abortHasRun = true; + is( + event.target.readyState, + FileReader.DONE, + "should be DONE while firing onabort" + ); + is( + event.target.error.name, + "AbortError", + "error set to AbortError for aborted reads" + ); + is( + event.target.result, + null, + "file data should be null on aborted reads" + ); + }; + + r.onloadend = function (event) { + is(abortHasRun, true, "abort should fire before loadend"); + is(loadEndHasRun, false, "loadend should only fire once"); + loadEndHasRun = true; + is( + event.target.readyState, + FileReader.DONE, + "should be DONE while firing onabort" + ); + is( + event.target.error.name, + "AbortError", + "error set to AbortError for aborted reads" + ); + is( + event.target.result, + null, + "file data should be null on aborted reads" + ); + }; + + r.onload = function () { + ok(false, "load should not fire for aborted reads"); + }; + r.onerror = function () { + ok(false, "error should not fire for aborted reads"); + }; + r.onprogress = function () { + ok(false, "progress should not fire for aborted reads"); + }; + + let abortThrew = false; + try { + r.abort(); + } catch (e) { + abortThrew = true; + } + + is(abortThrew, false, "abort() doesn't throw"); + is(abortHasRun, false, "abort() is a no-op unless loading"); + + r.readAsText(blob); + r.abort(); + + is(abortHasRun, true, "abort should fire sync"); + is(loadEndHasRun, true, "loadend should fire sync"); + + resolve(); + }); +} + +// Test calling readAsX to cause abort() +function test_abort_readAsX(blob, text) { + return new Promise(resolve => { + let reuseAbortHasRun = false; + + let r = new FileReader(); + r.onabort = function (event) { + is(reuseAbortHasRun, false, "abort should only fire once"); + reuseAbortHasRun = true; + is( + event.target.readyState, + FileReader.DONE, + "should be DONE while firing onabort" + ); + is( + event.target.error.name, + "AbortError", + "error set to AbortError for aborted reads" + ); + is( + event.target.result, + null, + "file data should be null on aborted reads" + ); + }; + r.onload = function () { + ok(false, "load should fire for nested reads"); + }; + + let abortThrew = false; + try { + r.abort(); + } catch (e) { + abortThrew = true; + } + + is(abortThrew, false, "abort() should not throw"); + is(reuseAbortHasRun, false, "abort() is a no-op unless loading"); + r.readAsText(blob); + + let readThrew = false; + try { + r.readAsText(blob); + } catch (e) { + readThrew = true; + } + + is(readThrew, true, "readAsText() must throw if loading"); + is(reuseAbortHasRun, false, "abort should not fire"); + + r.onload = event => { + loadEventHandler_string( + event, + resolve, + r, + text, + text.length, + "reuse-as-abort reading" + ); + }; + }); +} + +// Test reading from nonexistent files +function test_nonExisting(blob) { + return new Promise(resolve => { + let r = new FileReader(); + + r.onerror = function (event) { + is( + event.target.readyState, + FileReader.DONE, + "should be DONE while firing onerror" + ); + is( + event.target.error.name, + "NotFoundError", + "error set to NotFoundError for nonexistent files" + ); + is( + event.target.result, + null, + "file data should be null on aborted reads" + ); + resolve(); + }; + r.onload = function (event) { + is(false, "nonexistent file shouldn't load! (FIXME: bug 1122788)"); + }; + + let didThrow = false; + try { + r.readAsDataURL(blob); + } catch (ex) { + didThrow = true; + } + + // Once this test passes, we should test that onerror gets called and + // that the FileReader object is in the right state during that call. + is( + didThrow, + false, + "shouldn't throw when opening nonexistent file, should fire error instead" + ); + }); +} |