| <!doctype html> | |
| <!-- Originally developed by Aryeh Gregor, funded by Google. Copyright belongs | |
| to Google. --> | |
| <title>atob()/btoa() tests</title> | |
| <meta charset=utf-8> | |
| <div id=log></div> | |
| <script src=/resources/testharness.js></script> | |
| <script src=/resources/testharnessreport.js></script> | |
| <script> | |
| /** | |
| * btoa() as defined by the HTML5 spec, which mostly just references RFC4648. | |
| */ | |
| function mybtoa(s) { | |
| // String conversion as required by WebIDL. | |
| s = String(s); | |
| // "The btoa() method must throw an INVALID_CHARACTER_ERR exception if the | |
| // method's first argument contains any character whose code point is | |
| // greater than U+00FF." | |
| for (var i = 0; i < s.length; i++) { | |
| if (s.charCodeAt(i) > 255) { | |
| return "INVALID_CHARACTER_ERR"; | |
| } | |
| } | |
| var out = ""; | |
| for (var i = 0; i < s.length; i += 3) { | |
| var groupsOfSix = [undefined, undefined, undefined, undefined]; | |
| groupsOfSix[0] = s.charCodeAt(i) >> 2; | |
| groupsOfSix[1] = (s.charCodeAt(i) & 0x03) << 4; | |
| if (s.length > i + 1) { | |
| groupsOfSix[1] |= s.charCodeAt(i + 1) >> 4; | |
| groupsOfSix[2] = (s.charCodeAt(i + 1) & 0x0f) << 2; | |
| } | |
| if (s.length > i + 2) { | |
| groupsOfSix[2] |= s.charCodeAt(i + 2) >> 6; | |
| groupsOfSix[3] = s.charCodeAt(i + 2) & 0x3f; | |
| } | |
| for (var j = 0; j < groupsOfSix.length; j++) { | |
| if (typeof groupsOfSix[j] == "undefined") { | |
| out += "="; | |
| } else { | |
| out += btoaLookup(groupsOfSix[j]); | |
| } | |
| } | |
| } | |
| return out; | |
| } | |
| /** | |
| * Lookup table for mybtoa(), which converts a six-bit number into the | |
| * corresponding ASCII character. | |
| */ | |
| function btoaLookup(idx) { | |
| if (idx < 26) { | |
| return String.fromCharCode(idx + 'A'.charCodeAt(0)); | |
| } | |
| if (idx < 52) { | |
| return String.fromCharCode(idx - 26 + 'a'.charCodeAt(0)); | |
| } | |
| if (idx < 62) { | |
| return String.fromCharCode(idx - 52 + '0'.charCodeAt(0)); | |
| } | |
| if (idx == 62) { | |
| return '+'; | |
| } | |
| if (idx == 63) { | |
| return '/'; | |
| } | |
| // Throw INVALID_CHARACTER_ERR exception here -- won't be hit in the tests. | |
| } | |
| function btoaException(input) { | |
| input = String(input); | |
| for (var i = 0; i < input.length; i++) { | |
| if (input.charCodeAt(i) > 255) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| function testBtoa(input) { | |
| // "The btoa() method must throw an INVALID_CHARACTER_ERR exception if the | |
| // method's first argument contains any character whose code point is | |
| // greater than U+00FF." | |
| var normalizedInput = String(input); | |
| for (var i = 0; i < normalizedInput.length; i++) { | |
| if (normalizedInput.charCodeAt(i) > 255) { | |
| assert_throws("InvalidCharacterError", function() { btoa(input); }, | |
| "Code unit " + i + " has value " + normalizedInput.charCodeAt(i) + ", which is greater than 255"); | |
| return; | |
| } | |
| } | |
| assert_equals(btoa(input), mybtoa(input)); | |
| assert_equals(atob(btoa(input)), String(input), "atob(btoa(input)) must be the same as String(input)"); | |
| } | |
| var tests = ["עברית", "", "ab", "abc", "abcd", "abcde", | |
| // This one is thrown in because IE9 seems to fail atob(btoa()) on it. Or | |
| // possibly to fail btoa(). I actually can't tell what's happening here, | |
| // but it doesn't hurt. | |
| "\xff\xff\xc0", | |
| // Is your DOM implementation binary-safe? | |
| // Disabled due to Cobalt's inability to store JS strings with embedded | |
| // null. | |
| // "\0a", "a\0b", | |
| // WebIDL tests. | |
| undefined, null, 7, 12, 1.5, true, false, NaN, +Infinity, -Infinity, 0, -0, | |
| {toString: function() { return "foo" }}, | |
| ]; | |
| // TODO: Change i's starting index to 0 when Cobalt supports strings with | |
| // embedded null. | |
| for (var i = 1; i < 258; i++) { | |
| tests.push(String.fromCharCode(i)); | |
| } | |
| tests.push(String.fromCharCode(10000)); | |
| tests.push(String.fromCharCode(65534)); | |
| tests.push(String.fromCharCode(65535)); | |
| // This is supposed to be U+10000. | |
| tests.push(String.fromCharCode(0xd800, 0xdc00)); | |
| tests = tests.map( | |
| function(elem) { | |
| var expected = mybtoa(elem); | |
| if (expected === "INVALID_CHARACTER_ERR") { | |
| return ["btoa(" + format_value(elem) + ") must raise INVALID_CHARACTER_ERR", elem]; | |
| } | |
| return ["btoa(" + format_value(elem) + ") == " + format_value(mybtoa(elem)), elem]; | |
| } | |
| ); | |
| var everything = ""; | |
| // TODO: Change starting index to 0 when Cobalt supports strings with embedded | |
| // null. | |
| for (var i = 1; i < 256; i++) { | |
| everything += String.fromCharCode(i); | |
| } | |
| tests.push(["btoa(first 256 code points concatenated)", everything]); | |
| generate_tests(testBtoa, tests); | |
| // TODO: Re-enable the following method to run atob tests. | |
| // promise_test(() => fetch("../../../fetch/data-urls/resources/base64.json").then(res => res.json()).then(runAtobTests), "atob() setup."); | |
| // TODO: delete these test cases after the previous line is uncommented. | |
| atobtests = [ | |
| [" abcd", [105, 183, 29]], | |
| ["abcd ", [105, 183, 29]], | |
| [" abcd===", null], | |
| ["abcd=== ", null], | |
| ["abcd ===", null], | |
| ["a", null], | |
| ["ab", [105]], | |
| ["abc", [105, 183]], | |
| ["abcde", null], | |
| ["𐀀", null], | |
| ["=", null], | |
| ["==", null], | |
| ["===", null], | |
| ["====", null], | |
| ["=====", null], | |
| ["a=", null], | |
| ["a==", null], | |
| ["a===", null], | |
| ["a====", null], | |
| ["a=====", null], | |
| ["ab=", null], | |
| ["ab==", [105]], | |
| ["ab===", null], | |
| ["ab====", null], | |
| ["ab=====", null], | |
| ["abc=", [105, 183]], | |
| ["abc==", null], | |
| ["abc===", null], | |
| ["abc====", null], | |
| ["abc=====", null], | |
| ["abcd=", null], | |
| ["abcd==", null], | |
| ["abcd===", null], | |
| ["abcd====", null], | |
| ["abcd=====", null], | |
| ["abcde=", null], | |
| ["abcde==", null], | |
| ["abcde===", null], | |
| ["abcde====", null], | |
| ["abcde=====", null], | |
| ["=a", null], | |
| ["=a=", null], | |
| ["a=b", null], | |
| ["a=b=", null], | |
| ["ab=c", null], | |
| ["ab=c=", null], | |
| ["abc=d", null], | |
| ["abc=d=", null], | |
| ["ab\tcd", [105, 183, 29]], | |
| ["ab\ncd", [105, 183, 29]], | |
| ["ab\fcd", [105, 183, 29]], | |
| ["ab\rcd", [105, 183, 29]], | |
| ["ab cd", [105, 183, 29]], | |
| ["ab\u00a0cd", null], | |
| ["ab\t\n\f\r cd", [105, 183, 29]], | |
| [" \t\n\f\r ab\t\n\f\r cd\t\n\f\r ", [105, 183, 29]], | |
| ["ab\t\n\f\r =\t\n\f\r =\t\n\f\r ", [105]], | |
| ["A", null], | |
| ["/A", [252]], | |
| ["//A", [255, 240]], | |
| ["///A", [255, 255, 192]], | |
| ["////A", null], | |
| ["/", null], | |
| ["A/", [3]], | |
| ["AA/", [0, 15]], | |
| ["AAAA/", null], | |
| ["AAA/", [0, 0, 63]], | |
| // TODO: Re-enable this test case once Cobalt supports strings with embedded | |
| // null. | |
| // ["\u0000nonsense", null], | |
| ["YQ", [97]], | |
| ["YR", [97]], | |
| ["~~", null], | |
| ["..", null], | |
| ["--", null], | |
| ["__", null], | |
| ["", ""], | |
| [" ", ""], | |
| [" ", ""] | |
| ] | |
| const idlTests = [ | |
| [undefined, null], | |
| [null, [158, 233, 101]], | |
| [7, null], | |
| [12, [215]], | |
| [1.5, null], | |
| [true, [182, 187]], | |
| [false, null], | |
| [NaN, [53, 163]], | |
| [+Infinity, [34, 119, 226, 158, 43, 114]], | |
| [-Infinity, null], | |
| [0, null], | |
| [-0, null], | |
| [{toString: function() { return "foo" }}, [126, 138]], | |
| [{toString: function() { return "abcd" }}, [105, 183, 29]] | |
| ]; | |
| function runAtobTests(tests) { | |
| const allTests = tests.concat(idlTests); | |
| for(let i = 0; i < allTests.length; i++) { | |
| const input = allTests[i][0], | |
| output = allTests[i][1]; | |
| test(() => { | |
| if(output === null) { | |
| assert_throws("InvalidCharacterError", () => window.atob(input)); | |
| } else { | |
| const result = window.atob(input); | |
| for(let ii = 0; ii < output.length; ii++) { | |
| assert_equals(result.charCodeAt(ii), output[ii]); | |
| } | |
| } | |
| }, "atob(" + format_value(input) + ")"); | |
| } | |
| } | |
| // TODO: delete this line once we restore the old way to run tests. | |
| runAtobTests(atobtests); | |
| </script> |