Index: LayoutTests/http/tests/websocket/resources/close-common.js |
diff --git a/LayoutTests/http/tests/websocket/resources/close-common.js b/LayoutTests/http/tests/websocket/resources/close-common.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..00fa3bff586a1bf7268b10e7aacefbf1acd1a8cb |
--- /dev/null |
+++ b/LayoutTests/http/tests/websocket/resources/close-common.js |
@@ -0,0 +1,258 @@ |
+// Variables used in js-test.js assertions. |
+var exceptionName; |
+var exceptionMessage; |
+var exceptionProto; |
+var closeEvent; |
+ |
+// Constants. |
+const invalidAccessErr = "InvalidAccessError"; |
+const syntaxErr = "SyntaxError"; |
+const normalClosure = 1000; |
+const abnormalClosure = 1006; |
+const url = "ws://127.0.0.1:8880/close"; |
+const ws_handlers = ["onopen", "onerror", "onclose", "onmessage"]; |
+ |
+// An explicit timeout is used so that we can capture the test output. |
+var timeout; |
+ |
+const badCodesTestCodes = [ |
+ 999, 1001, 2999, 5000, 65536 + 1000, 0x100000000 + 1000, 2999.9, NaN, "0", "100", 1/0, -1/0, 0/0, |
+]; |
+ |
+const badReasonTestReasons = [ |
+ "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234", // 124 Byte |
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012\u00a9", // length is 123, but 124 Byte in UTF-8 |
+]; |
+ |
+function createFailHandler(name, ws, reject) |
+{ |
+ return function() { |
+ removeAllHandlers(ws); |
+ reject(name + " was called."); |
+ }; |
+} |
+ |
+function setDefaultHandlers(ws, reject) |
+{ |
+ ws_handlers.forEach(function(handler) { |
+ ws[handler] = createFailHandler(handler, ws, reject); |
+ }); |
+} |
+ |
+// Ensure that the WebSocket can be garbage collected. |
+function removeAllHandlers(ws) |
+{ |
+ ws_handlers.forEach(function(handler) { |
+ ws[handler] = undefined; |
+ }); |
+} |
+ |
+// Verify that close() throws an exception when an invalid close code is passed. |
+function badCodesTest() |
+{ |
+ return new Promise(function(resolve, reject) { |
+ debug("badCodesTest: started"); |
+ var ws = new WebSocket(url); |
+ setDefaultHandlers(ws, reject); |
+ for (var test_code of badCodesTestCodes) { |
+ debug("badCodesTest: " + test_code); |
+ try { |
+ ws.close(test_code); |
+ reject("Exception not thrown for code " + test_code); |
+ return; |
+ } catch (e) { |
+ exceptionName = e.name; |
+ exceptionMessage = e.message; |
+ exceptionProto = Object.getPrototypeOf(e); |
+ shouldBeTrue("exceptionProto === DOMException.prototype"); |
+ shouldBeEqualToString("exceptionName", invalidAccessErr); |
+ var expectedCode = test_code; |
+ if (!expectedCode) |
+ expectedCode = 0; |
+ else if (expectedCode > 65535) |
+ expectedCode = 65535; |
+ else if (expectedCode < 0) |
+ expectedCode = 0; |
+ expectedCode = Math.floor(expectedCode); |
+ shouldBeEqualToString("exceptionMessage", "Failed to execute 'close' on 'WebSocket': The code must be either 1000, or between 3000 and 4999. " + expectedCode + " is neither."); |
+ } |
+ } |
+ removeAllHandlers(ws); |
+ resolve(); |
+ }); |
+} |
+ |
+// Verify that passing a valid code does not throw an exception. |
+function goodCodeTest() |
+{ |
+ return new Promise(function(resolve, reject) { |
+ debug("goodCodeTest: started"); |
+ var ws = new WebSocket(url); |
+ setDefaultHandlers(ws, reject); |
+ ws.onclose = function(e) { |
+ closeEvent = e; |
+ shouldBeEqualToNumber("closeEvent.code", abnormalClosure); |
+ resolve(); |
+ }; |
+ ws.onerror = function() { |
+ testPassed("onerror was called."); |
+ }; |
+ ws.close(1000.0); |
+ }); |
+} |
+ |
+// Verify that unpaired surrogates in the reason string are converted to U+FFFD |
+// before sending to the remote server. |
+function invalidUnicodeReasonTest() |
+{ |
+ return new Promise(function(resolve, reject) { |
+ debug("invalidUnicodeReasonTest: started"); |
+ var ws = new WebSocket(url); |
+ setDefaultHandlers(ws, reject); |
+ ws.onopen = function() { |
+ // 0xD834 is an unpaired surrogate. |
+ var invalidString = String.fromCharCode(0xD834); |
+ ws.close(1000, invalidString); |
+ }; |
+ ws.onclose = function(e) { |
+ closeEvent = e; |
+ shouldBeTrue("closeEvent.wasClean"); |
+ shouldBeEqualToString("closeEvent.reason", "\uFFFD"); |
+ resolve(); |
+ }; |
+ }); |
+} |
+ |
+// Verify that invalid reason strings passed to close() result in an exception |
+// being thrown. |
+function badReasonTest() |
+{ |
+ return new Promise(function(resolve, reject) { |
+ debug("badReasonTest: started"); |
+ var ws = new WebSocket(url); |
+ setDefaultHandlers(ws, reject); |
+ for (var test_reason of badReasonTestReasons) { |
+ debug("badReasonTest: " + test_reason); |
+ try { |
+ ws.close(normalClosure, test_reason); |
+ reject("Exception not thrown for bad reason " + test_reason); |
+ return; |
+ } catch (e) { |
+ exceptionName = e.name; |
+ exceptionProto = Object.getPrototypeOf(e); |
+ shouldBeTrue("exceptionProto === DOMException.prototype"); |
+ shouldBeEqualToString("exceptionName", syntaxErr); |
+ } |
+ } |
+ removeAllHandlers(ws); |
+ resolve(); |
+ }); |
+} |
+ |
+// Verify that a valid reason code passed to close() does not result in an |
+// exception. |
+function goodReasonTest() |
+{ |
+ return new Promise(function(resolve, reject) { |
+ debug("goodReasonTest: started"); |
+ var ws = new WebSocket(url); |
+ setDefaultHandlers(ws, reject); |
+ ws.onclose = function(e) { |
+ closeEvent = e; |
+ shouldBeEqualToNumber("closeEvent.code", abnormalClosure); |
+ resolve(); |
+ }; |
+ ws.onerror = function() { |
+ testPassed("onerror was called."); |
+ }; |
+ // 123 byte reason should not throw. |
+ ws.close(normalClosure, "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123"); |
+ }); |
+} |
+ |
+// Verify that valid close codes and reasons are correctly send to the |
+// WebSocket server. |
+function codeAndReasonTest() |
+{ |
+ const codes = [ |
+ 1000, |
+ 3000, |
+ 4000, |
+ 4999 |
+ ]; |
+ const reasons = [ |
+ "OK, Bye!", |
+ "3000", |
+ "code is 4000", |
+ "\u00a9 Google" |
+ ]; |
+ return new Promise(function(resolve, reject) { |
+ debug("codeAndReasonTest: started"); |
+ // Tests are run in series to produce deterministic output. |
+ var promise = Promise.resolve(); |
+ for (var id = 0; id < codes.length; ++id) { |
+ promise = promise.then(codeAndReasonSingleCase(codes[id], reasons[id])); |
+ } |
+ promise.then(resolve); |
+ }); |
+} |
+ |
+// (Return a function which returns a Promise to) handle a single code/reason |
+// pair for the codeAndReasonTest. |
+function codeAndReasonSingleCase(test_code, test_reason) |
+{ |
+ return function() { |
+ return new Promise(function(resolve, reject) { |
+ debug("codeAndReasonTest: " + test_code + ", '" + test_reason + "'"); |
+ var ws = new WebSocket(url); |
+ setDefaultHandlers(ws, reject); |
+ ws.onopen = function() { |
+ ws.close(test_code, test_reason); |
+ }; |
+ ws.onclose = function(e) { |
+ closeEvent = e; |
+ shouldBeTrue("closeEvent.wasClean"); |
+ shouldBeEqualToNumber("closeEvent.code", test_code); |
+ shouldBeEqualToString("closeEvent.reason", test_reason); |
+ resolve(); |
+ }; |
+ }); |
+ }; |
+} |
+ |
+function cleanup() |
+{ |
+ clearTimeout(timeout); |
+ finishJSTest(); |
+} |
+ |
+function onTimeout() |
+{ |
+ handleRejection("Timeout"); |
+} |
+ |
+function handleRejection(reason) |
+{ |
+ if (reason instanceof Error) { |
+ // Get a stack trace if an exception fired. |
+ testFailed(reason.stack); |
+ } else { |
+ testFailed(reason); |
+ } |
+ cleanup(); |
+} |
+ |
+function testClose() |
+{ |
+ // Set an explicit timeout in order to keep text output on failure. |
+ timeout = setTimeout(onTimeout, 5000); |
+ // Tests are run in series to produce deterministic output. |
+ badCodesTest() |
+ .then(goodCodeTest) |
+ .then(invalidUnicodeReasonTest) |
+ .then(badReasonTest) |
+ .then(goodReasonTest) |
+ .then(codeAndReasonTest) |
+ .then(cleanup) |
+ .catch(handleRejection); |
+} |