Web Inspector: add protocol test for existing error handling performed by the backend
authorbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 24 Aug 2015 23:58:31 +0000 (23:58 +0000)
committerbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 24 Aug 2015 23:58:31 +0000 (23:58 +0000)
https://bugs.webkit.org/show_bug.cgi?id=147097

Reviewed by Joseph Pecoraro.

Source/JavaScriptCore:

A new test revealed that the protocol "method" parameter was being parsed in a naive way.
Rewrite it to use String::split and improve error checking to avoid failing later.

* inspector/InspectorBackendDispatcher.cpp:
(Inspector::BackendDispatcher::dispatch):

Source/WebInspectorUI:

Add a way to send raw messages to the backend while still awaiting on responses.
This is necessary to test protocol error handling in the inspector backend.

* UserInterface/Test/InspectorProtocol.js:
(InspectorProtocol.sendCommand):
(InspectorProtocol.awaitCommand): Use awaitMessage internally.
(InspectorProtocol.awaitMessage): Added. Use a dummy requestId if none is supplied.
(InspectorProtocol._sendMessage): Added.
(InspectorProtocol.dispatchMessageFromBackend):
Reject with the error object instead of the error message, so error code/data can be checked.
(InspectorProtocol.sendMessage): Deleted, it is now a private method.

LayoutTests:

Add a bunch of test cases to cover existing error handling by the backend dispatcher.

* inspector/protocol/backend-dispatcher-argument-errors-expected.txt: Added.
* inspector/protocol/backend-dispatcher-argument-errors.html: Added.
* inspector/protocol/backend-dispatcher-malformed-message-errors-expected.txt: Added.
* inspector/protocol/backend-dispatcher-malformed-message-errors.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@188897 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/inspector/protocol/backend-dispatcher-argument-errors-expected.txt [new file with mode: 0644]
LayoutTests/inspector/protocol/backend-dispatcher-argument-errors.html [new file with mode: 0644]
LayoutTests/inspector/protocol/backend-dispatcher-malformed-message-errors-expected.txt [new file with mode: 0644]
LayoutTests/inspector/protocol/backend-dispatcher-malformed-message-errors.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Test/InspectorProtocol.js

index aedd2da..8dfba93 100644 (file)
@@ -1,3 +1,17 @@
+2015-08-24  Brian Burg  <bburg@apple.com>
+
+        Web Inspector: add protocol test for existing error handling performed by the backend
+        https://bugs.webkit.org/show_bug.cgi?id=147097
+
+        Reviewed by Joseph Pecoraro.
+
+        Add a bunch of test cases to cover existing error handling by the backend dispatcher.
+
+        * inspector/protocol/backend-dispatcher-argument-errors-expected.txt: Added.
+        * inspector/protocol/backend-dispatcher-argument-errors.html: Added.
+        * inspector/protocol/backend-dispatcher-malformed-message-errors-expected.txt: Added.
+        * inspector/protocol/backend-dispatcher-malformed-message-errors.html: Added.
+
 2015-08-24  Alexey Proskuryakov  <ap@apple.com>
 
         Mark flaky scrolling tests as such.
diff --git a/LayoutTests/inspector/protocol/backend-dispatcher-argument-errors-expected.txt b/LayoutTests/inspector/protocol/backend-dispatcher-argument-errors-expected.txt
new file mode 100644 (file)
index 0000000..44c4942
--- /dev/null
@@ -0,0 +1,57 @@
+Testing the inspector backend's error handling when sending messages with wrong parameters.
+
+
+== Running test suite: Protocol.ArgumentModeErrors
+-- Running test case: ParametersNotAnObject
+Sending message: {"id":123,"method":"Runtime.evaluate","params":"junk"}
+PASS: the backend should send a protocol error when receiving an invalid message.
+PASS: the reported error should be "InvalidParams" (-32602)
+Actual error code: -32602
+Actual error message: Some arguments of method 'Runtime.evaluate' can't be processed
+Actual error data: 'params' object must contain required parameter 'expression' with type 'String'.
+
+-- Running test case: MissingRequiredParameter
+Sending message: {"id":123,"method":"Runtime.evaluate","params":{"stuff":123}}
+PASS: the backend should send a protocol error when receiving an invalid message.
+PASS: the reported error should be "InvalidParams" (-32602)
+Actual error code: -32602
+Actual error message: Some arguments of method 'Runtime.evaluate' can't be processed
+Actual error data: Parameter 'expression' with type 'String' was not found.
+
+-- Running test case: RequiredParameterWrongType
+Sending message: {"id":123,"method":"Runtime.evaluate","params":{"expression":[]}}
+PASS: the backend should send a protocol error when receiving an invalid message.
+PASS: the reported error should be "InvalidParams" (-32602)
+Actual error code: -32602
+Actual error message: Some arguments of method 'Runtime.evaluate' can't be processed
+Actual error data: Parameter 'expression' has wrong type. It must be 'String'.
+
+-- Running test case: OptionalParameterWrongType
+Sending message: {"id":123,"method":"Runtime.evaluate","params":{"expression":"42","includeCommandLineAPI":123}}
+PASS: the backend should send a protocol error when receiving an invalid message.
+PASS: the reported error should be "InvalidParams" (-32602)
+Actual error code: -32602
+Actual error message: Some arguments of method 'Runtime.evaluate' can't be processed
+Actual error data: Parameter 'includeCommandLineAPI' has wrong type. It must be 'Boolean'.
+
+-- Running test case: TestErrorCodeForSyncServerError
+Sending message: {"id":123,"method":"Database.getDatabaseTableNames","params":{"databaseId":"thisisNotADatabase"}}
+PASS: the backend should send a protocol error when receiving an invalid message.
+PASS: the reported error should be "ServerError" (-32000)
+Actual error code: -32000
+Actual error message: Database agent is not enabled
+
+-- Running test case: TestErrorCodeForAsyncServerError
+Sending message: {"id":123,"method":"Database.executeSQL","params":{"databaseId":"thisisNotADatabase","query":"asdf"}}
+PASS: the backend should send a protocol error when receiving an invalid message.
+PASS: the reported error should be "ServerError" (-32000)
+Actual error code: -32000
+Actual error message: Database agent is not enabled
+
+-- Running test case: CommandWithBadArgumentValue
+Sending message: {"id":123,"method":"Runtime.getProperties","params":{"objectId":"thisisNotAnId"}}
+PASS: the backend should send a protocol error when receiving an invalid message.
+PASS: the reported error should be "ServerError" (-32000)
+Actual error code: -32000
+Actual error message: Inspected frame has gone
+
diff --git a/LayoutTests/inspector/protocol/backend-dispatcher-argument-errors.html b/LayoutTests/inspector/protocol/backend-dispatcher-argument-errors.html
new file mode 100644 (file)
index 0000000..150e832
--- /dev/null
@@ -0,0 +1,104 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/protocol-test.js"></script>
+<script>
+function test()
+{
+    let suite = ProtocolTest.createAsyncSuite("Protocol.ArgumentModeErrors");
+
+    let errorCodes = {
+        ParseError: -32700,
+        InvalidRequest: -32600,
+        MethodNotFound: -32601,
+        InvalidParams: -32602,
+        InternalError: -32603,
+        ServerError: -32000,
+    };
+
+    function addErrorResponseTestCase(args)
+    {
+        let {name, description, message, expectedError} = args;
+
+        suite.addTestCase({
+            name,
+            description,
+            test: (resolve, reject) => {
+                let stringifiedMessage = (typeof message !== "string") ? JSON.stringify(message) : message;
+                ProtocolTest.log("Sending message: " + stringifiedMessage);
+                InspectorProtocol.awaitMessage(message)
+                .then(function resolved(response) {
+                    ProtocolTest.log("FAIL: the backend should send a protocol error when receiving an invalid message.");
+                    reject();
+                }, function rejected(response) {
+                    ProtocolTest.log("PASS: the backend should send a protocol error when receiving an invalid message.");
+                    ProtocolTest.expectThat(response.code === errorCodes[expectedError], `the reported error should be "${expectedError}" (${errorCodes[expectedError]})`);
+                    ProtocolTest.log("Actual error code: " + response.code);
+                    ProtocolTest.log("Actual error message: " + response.message);
+                    if (response.data)
+                        ProtocolTest.log("Actual error data: " + response.data);
+
+                    resolve();
+                })
+                .catch(reject);
+            }
+        });
+    }
+
+    addErrorResponseTestCase({
+        name: "ParametersNotAnObject",
+        description: "The backend should return an error if a message has a non-object 'params'.",
+        message: {id: 123, method: "Runtime.evaluate", params: "junk"},
+        expectedError: "InvalidParams"
+    });
+
+    addErrorResponseTestCase({
+        name: "MissingRequiredParameter",
+        description: "The backend should return an error if a message is missing a required parameter.",
+        message: {id: 123, method: "Runtime.evaluate", params: {stuff: 123}},
+        expectedError: "InvalidParams"
+    });
+
+    addErrorResponseTestCase({
+        name: "RequiredParameterWrongType",
+        description: "The backend should return an error if a message has a required parameter with wrong type.",
+        message: {id: 123, method: "Runtime.evaluate", params: {expression: []}},
+        expectedError: "InvalidParams"
+    });
+
+    addErrorResponseTestCase({
+        name: "OptionalParameterWrongType",
+        description: "The backend should return an error if a message has an optional parameter with wrong type.",
+        message: {id: 123, method: "Runtime.evaluate", params: {expression: "42", includeCommandLineAPI: 123}},
+        expectedError: "InvalidParams"
+    });
+
+    addErrorResponseTestCase({
+        name: "TestErrorCodeForSyncServerError",
+        description: "The backend should return a server error with the correct error code.",
+        message: {id: 123, method: "Database.getDatabaseTableNames", params: {databaseId: "thisisNotADatabase"}},
+        expectedError: "ServerError"
+    });
+
+    addErrorResponseTestCase({
+        name: "TestErrorCodeForAsyncServerError",
+        description: "The backend should return a server error with the correct error code.",
+        message: {id: 123, method: "Database.executeSQL", params: {databaseId: "thisisNotADatabase", query: "asdf"}},
+        expectedError: "ServerError"
+    });
+
+    addErrorResponseTestCase({
+        name: "CommandWithBadArgumentValue",
+        description: "The backend should return an error if something went wrong when invoking the agent's method.",
+        message: {id: 123, method: "Runtime.getProperties", params: {objectId: "thisisNotAnId"}},
+        expectedError: "ServerError"
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+    <p>Testing the inspector backend's error handling when sending messages with wrong parameters.</p>
+</body>
+</html>
diff --git a/LayoutTests/inspector/protocol/backend-dispatcher-malformed-message-errors-expected.txt b/LayoutTests/inspector/protocol/backend-dispatcher-malformed-message-errors-expected.txt
new file mode 100644 (file)
index 0000000..6c3c5f7
--- /dev/null
@@ -0,0 +1,123 @@
+Testing the inspector backend's error handling when sending invalid messages.
+
+
+== Running test suite: Protocol.MalformedMessageErrors
+-- Running test case: UnparseableStringMessage
+Sending message: not-a-json-object
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "ParseError" (-32700)
+Actual error code: -32700
+Actual error message: Message must be in JSON format
+
+-- Running test case: JSONObjectWithSyntaxError
+Sending message: {name: 123,}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "ParseError" (-32700)
+Actual error code: -32700
+Actual error message: Message must be in JSON format
+
+-- Running test case: EmptyJSONObject
+Sending message: {}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "InvalidRequest" (-32600)
+Actual error code: -32600
+Actual error message: 'id' property was not found
+
+-- Running test case: MissingIdField
+Sending message: {"lucky":123}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "InvalidRequest" (-32600)
+Actual error code: -32600
+Actual error message: 'id' property was not found
+
+-- Running test case: IdFieldWithArrayType
+Sending message: {"id":[]}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "InvalidRequest" (-32600)
+Actual error code: -32600
+Actual error message: The type of 'id' property must be integer
+
+-- Running test case: IdFieldWithStringType
+Sending message: {"id":"123"}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "InvalidRequest" (-32600)
+Actual error code: -32600
+Actual error message: The type of 'id' property must be integer
+
+-- Running test case: MissingMethodField
+Sending message: {"id":123}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "InvalidRequest" (-32600)
+Actual error code: -32600
+Actual error message: 'method' property wasn't found
+
+-- Running test case: MethodFieldWithArrayType
+Sending message: {"id":123,"method":[]}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "InvalidRequest" (-32600)
+Actual error code: -32600
+Actual error message: The type of 'method' property must be string
+
+-- Running test case: MethodFieldWithNumberType
+Sending message: {"id":123,"method":123}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "InvalidRequest" (-32600)
+Actual error code: -32600
+Actual error message: The type of 'method' property must be string
+
+-- Running test case: MethodFieldWithBadFormatting
+Sending message: {"id":123,"method":"ThisIsDefinitelyNotADomainAndMethod"}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "InvalidRequest" (-32600)
+Actual error code: -32600
+Actual error message: The 'method' property was formatted incorrectly. It should be 'Domain.method'
+
+-- Running test case: MethodFieldWithBadFormatting2
+Sending message: {"id":123,"method":"ThisIsDefinitelyNotADomainAndMethod."}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "InvalidRequest" (-32600)
+Actual error code: -32600
+Actual error message: The 'method' property was formatted incorrectly. It should be 'Domain.method'
+
+-- Running test case: MethodFieldWithBadFormatting3
+Sending message: {"id":123,"method":".ThisIsDefinitelyNotADomainAndMethod"}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "InvalidRequest" (-32600)
+Actual error code: -32600
+Actual error message: The 'method' property was formatted incorrectly. It should be 'Domain.method'
+
+-- Running test case: MethodFieldWithBadFormatting4
+Sending message: {"id":123,"method":"."}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "InvalidRequest" (-32600)
+Actual error code: -32600
+Actual error message: The 'method' property was formatted incorrectly. It should be 'Domain.method'
+
+-- Running test case: MethodFieldWithBadFormatting5
+Sending message: {"id":123,"method":".FooBar."}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "InvalidRequest" (-32600)
+Actual error code: -32600
+Actual error message: The 'method' property was formatted incorrectly. It should be 'Domain.method'
+
+-- Running test case: MethodFieldWithBadFormatting6
+Sending message: {"id":123,"method":"Foo.Bar.Baz"}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "InvalidRequest" (-32600)
+Actual error code: -32600
+Actual error message: The 'method' property was formatted incorrectly. It should be 'Domain.method'
+
+-- Running test case: MethodFieldWithFakeDomain
+Sending message: {"id":123,"method":"DummyDomain.DummyMethod"}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "MethodNotFound" (-32601)
+Actual error code: -32601
+Actual error message: 'DummyDomain' domain was not found
+
+-- Running test case: MethodFieldWithFakeMethod
+Sending message: {"id":123,"method":"Inspector.DummyMethod"}
+PASS: the backend should send a protocol error when receiving for an invalid message.
+PASS: the reported error should be "MethodNotFound" (-32601)
+Actual error code: -32601
+Actual error message: 'Inspector.DummyMethod' was not found
+
diff --git a/LayoutTests/inspector/protocol/backend-dispatcher-malformed-message-errors.html b/LayoutTests/inspector/protocol/backend-dispatcher-malformed-message-errors.html
new file mode 100644 (file)
index 0000000..4552120
--- /dev/null
@@ -0,0 +1,172 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/protocol-test.js"></script>
+<script>
+function test()
+{
+    let suite = ProtocolTest.createAsyncSuite("Protocol.MalformedMessageErrors");
+
+    let errorCodes = {
+        ParseError: -32700,
+        InvalidRequest: -32600,
+        MethodNotFound: -32601,
+        InvalidParams: -32602,
+        InternalError: -32603,
+        ServerError: -32000,
+    };
+
+    function addErrorResponseTestCase(args)
+    {
+        let {name, description, message, expectedError} = args;
+
+        suite.addTestCase({
+            name,
+            description,
+            test: (resolve, reject) => {
+                let stringifiedMessage = (typeof message !== "string") ? JSON.stringify(message) : message;
+                ProtocolTest.log("Sending message: " + stringifiedMessage);
+                InspectorProtocol.awaitMessage(message)
+                .then(function resolved(response) {
+                    ProtocolTest.log("FAIL: the backend should send a protocol error when receiving for an invalid message.");
+                    reject();
+                }, function rejected(response) {
+                    ProtocolTest.log("PASS: the backend should send a protocol error when receiving for an invalid message.");
+                    ProtocolTest.expectThat(response.code === errorCodes[expectedError], `the reported error should be "${expectedError}" (${errorCodes[expectedError]})`);
+                    ProtocolTest.log("Actual error code: " + response.code);
+                    ProtocolTest.log("Actual error message: " + response.message);
+                    resolve();
+                })
+                .catch(reject);
+            }
+        });
+    }
+
+    addErrorResponseTestCase({
+        name: "UnparseableStringMessage",
+        description: "The backend should return an error if a message is not valid JSON.",
+        message: "not-a-json-object",
+        expectedError: "ParseError"
+    });
+
+    addErrorResponseTestCase({
+        name: "JSONObjectWithSyntaxError",
+        description: "The backend should return an error if a message is not valid JSON.",
+        message: "{name: 123,}",
+        expectedError: "ParseError"
+    });
+
+    addErrorResponseTestCase({
+        name: "EmptyJSONObject",
+        description: "The backend should return an error if a message lacks an 'id' field.",
+        message: {},
+        expectedError: "InvalidRequest"
+    });
+
+    addErrorResponseTestCase({
+        name: "MissingIdField",
+        description: "The backend should return an error if a message lacks an 'id' field.",
+        message: {lucky: 123},
+        expectedError: "InvalidRequest"
+    });
+
+    addErrorResponseTestCase({
+        name: "IdFieldWithArrayType",
+        description: "The backend should return an error if a message has a non-numeric 'id' field.",
+        message: {id: []},
+        expectedError: "InvalidRequest"
+    });
+
+    addErrorResponseTestCase({
+        name: "IdFieldWithStringType",
+        description: "The backend should return an error if a message has a non-numeric 'id' field.",
+        message: {id: "123"},
+        expectedError: "InvalidRequest"
+    });
+
+    addErrorResponseTestCase({
+        name: "MissingMethodField",
+        description: "The backend should return an error if a message lacks a 'method' field.",
+        message: {id: 123},
+        expectedError: "InvalidRequest"
+    });
+
+    addErrorResponseTestCase({
+        name: "MethodFieldWithArrayType",
+        description: "The backend should return an error if a message has a non-string 'method' field.",
+        message: {id: 123, method: []},
+        expectedError: "InvalidRequest"
+    });
+
+    addErrorResponseTestCase({
+        name: "MethodFieldWithNumberType",
+        description: "The backend should return an error if a message has a non-string 'method' field.",
+        message: {id: 123, method: 123},
+        expectedError: "InvalidRequest"
+    });
+
+    addErrorResponseTestCase({
+        name: "MethodFieldWithBadFormatting",
+        description: "The backend should return an error if a message has a 'method' field not formatted as 'Domain.Methodname'.",
+        message: {id: 123, method: "ThisIsDefinitelyNotADomainAndMethod"},
+        expectedError: "InvalidRequest"
+    });
+
+    addErrorResponseTestCase({
+        name: "MethodFieldWithBadFormatting2",
+        description: "The backend should return an error if a message has a 'method' field not formatted as 'Domain.Methodname'.",
+        message: {id: 123, method: "ThisIsDefinitelyNotADomainAndMethod."},
+        expectedError: "InvalidRequest"
+    });
+
+    addErrorResponseTestCase({
+        name: "MethodFieldWithBadFormatting3",
+        description: "The backend should return an error if a message has a 'method' field not formatted as 'Domain.Methodname'.",
+        message: {id: 123, method: ".ThisIsDefinitelyNotADomainAndMethod"},
+        expectedError: "InvalidRequest"
+    });
+
+    addErrorResponseTestCase({
+        name: "MethodFieldWithBadFormatting4",
+        description: "The backend should return an error if a message has a 'method' field not formatted as 'Domain.Methodname'.",
+        message: {id: 123, method: "."},
+        expectedError: "InvalidRequest"
+    });
+
+    addErrorResponseTestCase({
+        name: "MethodFieldWithBadFormatting5",
+        description: "The backend should return an error if a message has a 'method' field not formatted as 'Domain.Methodname'.",
+        message: {id: 123, method: ".FooBar."},
+        expectedError: "InvalidRequest"
+    });
+
+    addErrorResponseTestCase({
+        name: "MethodFieldWithBadFormatting6",
+        description: "The backend should return an error if a message has a 'method' field not formatted as 'Domain.Methodname'.",
+        message: {id: 123, method: "Foo.Bar.Baz"},
+        expectedError: "InvalidRequest"
+    });
+
+    addErrorResponseTestCase({
+        name: "MethodFieldWithFakeDomain",
+        description: "The backend should return an error if a message specifies a domain that doesn't exist.",
+        message: {id: 123, method: "DummyDomain.DummyMethod"},
+        expectedError: "MethodNotFound"
+    });
+
+    addErrorResponseTestCase({
+        name: "MethodFieldWithFakeMethod",
+        description: "The backend should return an error if a message specifies a method that doesn't exist.",
+        message: {id: 123, method: "Inspector.DummyMethod"},
+        expectedError: "MethodNotFound"
+    });
+
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+    <p>Testing the inspector backend's error handling when sending invalid messages.</p>
+</body>
+</html>
index 3000d3b..b768877 100644 (file)
@@ -1,3 +1,16 @@
+2015-08-24  Brian Burg  <bburg@apple.com>
+
+        Web Inspector: add protocol test for existing error handling performed by the backend
+        https://bugs.webkit.org/show_bug.cgi?id=147097
+
+        Reviewed by Joseph Pecoraro.
+
+        A new test revealed that the protocol "method" parameter was being parsed in a naive way.
+        Rewrite it to use String::split and improve error checking to avoid failing later.
+
+        * inspector/InspectorBackendDispatcher.cpp:
+        (Inspector::BackendDispatcher::dispatch):
+
 2015-08-24  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [ES6] Return JSInternalPromise as result of evaluateModule
index f88cfbd..d67ab99 100644 (file)
@@ -106,27 +106,28 @@ void BackendDispatcher::dispatch(const String& message)
         return;
     }
 
-    String method;
-    if (!methodValue->asString(method)) {
+    String methodString;
+    if (!methodValue->asString(methodString)) {
         reportProtocolError(&callId, InvalidRequest, ASCIILiteral("The type of 'method' property must be string"));
         return;
     }
 
-    size_t position = method.find('.');
-    if (position == WTF::notFound) {
+    Vector<String> domainAndMethod;
+    methodString.split('.', true, domainAndMethod);
+    if (domainAndMethod.size() != 2 || !domainAndMethod[0].length() || !domainAndMethod[1].length()) {
         reportProtocolError(&callId, InvalidRequest, ASCIILiteral("The 'method' property was formatted incorrectly. It should be 'Domain.method'"));
         return;
     }
 
-    String domain = method.substring(0, position);
+    String domain = domainAndMethod[0];
     SupplementalBackendDispatcher* domainDispatcher = m_dispatchers.get(domain);
     if (!domainDispatcher) {
         reportProtocolError(&callId, MethodNotFound, "'" + domain + "' domain was not found");
         return;
     }
 
-    String domainMethod = method.substring(position + 1);
-    domainDispatcher->dispatch(callId, domainMethod, messageObject.releaseNonNull());
+    String method = domainAndMethod[1];
+    domainDispatcher->dispatch(callId, method, messageObject.releaseNonNull());
 }
 
 void BackendDispatcher::sendResponse(long callId, RefPtr<InspectorObject>&& result, const ErrorString& invocationError)
index 21b010a..f1d46a1 100644 (file)
@@ -1,3 +1,22 @@
+2015-08-24  Brian Burg  <bburg@apple.com>
+
+        Web Inspector: add protocol test for existing error handling performed by the backend
+        https://bugs.webkit.org/show_bug.cgi?id=147097
+
+        Reviewed by Joseph Pecoraro.
+
+        Add a way to send raw messages to the backend while still awaiting on responses.
+        This is necessary to test protocol error handling in the inspector backend.
+
+        * UserInterface/Test/InspectorProtocol.js:
+        (InspectorProtocol.sendCommand):
+        (InspectorProtocol.awaitCommand): Use awaitMessage internally.
+        (InspectorProtocol.awaitMessage): Added. Use a dummy requestId if none is supplied.
+        (InspectorProtocol._sendMessage): Added.
+        (InspectorProtocol.dispatchMessageFromBackend):
+        Reject with the error object instead of the error message, so error code/data can be checked.
+        (InspectorProtocol.sendMessage): Deleted, it is now a private method.
+
 2015-08-24  Timothy Hatcher  <timothy@apple.com>
 
         Web Inspector: Change webkitAnimationEnd use, should be animationEnd
index 58e8aa0..489542c 100644 (file)
@@ -26,6 +26,7 @@
 
 InspectorProtocol = {};
 InspectorProtocol._dispatchTable = [];
+InspectorProtocol._placeholderRequestIds = [];
 InspectorProtocol._requestId = -1;
 InspectorProtocol.eventHandler = {};
 
@@ -38,7 +39,7 @@ InspectorProtocol.sendCommand = function(methodOrObject, params, handler)
 
     this._dispatchTable[++this._requestId] = handler;
     let messageObject = {method, params, id: this._requestId};
-    this.sendMessage(messageObject);
+    this._sendMessage(messageObject);
 
     return this._requestId;
 }
@@ -46,10 +47,26 @@ InspectorProtocol.sendCommand = function(methodOrObject, params, handler)
 InspectorProtocol.awaitCommand = function(args)
 {
     let {method, params} = args;
+    let messageObject = {method, params, id: ++this._requestId};
+
+    return this.awaitMessage(messageObject);
+}
+
+InspectorProtocol.awaitMessage = function(messageObject)
+{
+    // Send a raw message to the backend. Mostly used to test the backend's error handling.
     return new Promise((resolve, reject) => {
-        this._dispatchTable[++this._requestId] = {resolve, reject};
-        let messageObject = {method, params, id: this._requestId};
-        this.sendMessage(messageObject);
+        let requestId = messageObject.id;
+
+        // If the caller did not provide an id, then make one up so that the response
+        // can be used to settle a promise.
+        if (typeof requestId !== "number") {
+            requestId = ++this._requestId;
+            this._placeholderRequestIds.push(requestId);
+        }
+
+        this._dispatchTable[requestId] = {resolve, reject};
+        this._sendMessage(messageObject);
     });
 }
 
@@ -67,6 +84,16 @@ InspectorProtocol.awaitEvent = function(args)
     });
 }
 
+InspectorProtocol._sendMessage = function(messageObject)
+{
+    let messageString = typeof messageObject !== "string" ? JSON.stringify(messageObject) : messageObject;
+
+    if (ProtocolTest.dumpInspectorProtocolMessages)
+        console.log(`frontend: ${messageString}`);
+
+    InspectorFrontendHost.sendMessageToBackend(messageString);
+}
+
 InspectorProtocol.addEventListener = function(eventTypeOrObject, listener)
 {
     let event = eventTypeOrObject;
@@ -93,16 +120,6 @@ InspectorProtocol.addEventListener = function(eventTypeOrObject, listener)
     listeners.push(listener);
 }
 
-InspectorProtocol.sendMessage = function(messageObject)
-{
-    // This matches the debug dumping in InspectorBackend, which is bypassed
-    // by InspectorProtocol. Return messages should be dumped by InspectorBackend.
-    if (ProtocolTest.dumpInspectorProtocolMessages)
-        console.log("frontend: " + JSON.stringify(messageObject));
-
-    InspectorFrontendHost.sendMessageToBackend(JSON.stringify(messageObject));
-}
-
 InspectorProtocol.checkForError = function(responseObject)
 {
     if (responseObject.error) {
@@ -121,6 +138,12 @@ InspectorProtocol.dispatchMessageFromBackend = function(messageObject)
 
     // If the message has an id, then it is a reply to a command.
     let messageId = messageObject.id;
+
+    // If the id is 'null', then it may be an error response.
+    if (messageId === null)
+        messageId = InspectorProtocol._placeholderRequestIds.shift();
+
+    // If we could figure out a requestId, then dispatch the message.
     if (typeof messageId === "number") {
         let handler = InspectorProtocol._dispatchTable[messageId];
         if (!handler)
@@ -131,7 +154,7 @@ InspectorProtocol.dispatchMessageFromBackend = function(messageObject)
         else if (typeof handler === "object") {
             let {resolve, reject} = handler;
             if ("error" in messageObject)
-                reject(messageObject.error.message);
+                reject(messageObject.error);
             else
                 resolve(messageObject.result);
         }
@@ -149,7 +172,7 @@ InspectorProtocol.dispatchMessageFromBackend = function(messageObject)
         } else if (typeof handler === "object") {
             let {resolve, reject} = handler;
             if ("error" in messageObject)
-                reject(messageObject.error.message);
+                reject(messageObject.error);
             else
                 resolve(messageObject.result);
         }