2 * Copyright (C) 2012 Samsung Electronics Ltd. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "WebViewTest.h"
28 #include <wtf/gobject/GRefPtr.h>
29 #include <wtf/text/WTFString.h>
31 // Name of the test server application creating the webView object.
32 static const char* gTestServerAppName = "InspectorTestServer";
34 // Max seconds to wait for the test server before inspecting it.
35 static const int gMaxWaitForChild = 5;
37 // The PID for the test server running, so we can kill it if needed.
38 static GPid gChildProcessPid = 0;
40 // Whether the child has replied and it's ready.
41 static bool gChildIsReady = false;
43 static void stopTestServer()
45 // Do nothing if there's no server running.
46 if (!gChildProcessPid)
49 g_spawn_close_pid(gChildProcessPid);
50 kill(gChildProcessPid, SIGTERM);
54 static void sigAbortHandler(int sigNum)
56 // Just stop the test server if SIGABRT was received.
60 static gpointer testServerMonitorThreadFunc(gpointer)
62 // Wait for the specified timeout to happen.
63 g_usleep(gMaxWaitForChild * G_USEC_PER_SEC);
65 // Kill the child process if not ready yet.
73 static void startTestServerMonitor()
75 gChildIsReady = false;
76 g_thread_new("TestServerMonitor", testServerMonitorThreadFunc, 0);
79 static void startTestServer()
81 // Prepare argv[] for spawning the server process.
82 GOwnPtr<char> testServerPath(g_build_filename(WEBKIT_EXEC_PATH, "WebKit2APITests", gTestServerAppName, NULL));
84 // We install a handler to ensure that we kill the child process
85 // if the parent dies because of whatever the reason is.
86 signal(SIGABRT, sigAbortHandler);
88 char* testServerArgv[2];
89 testServerArgv[0] = testServerPath.get();
90 testServerArgv[1] = 0;
92 // Spawn the server, getting its stdout file descriptor to set a
93 // communication channel, so we know when it's ready.
95 g_assert(g_spawn_async_with_pipes(0, testServerArgv, 0, static_cast<GSpawnFlags>(0), 0, 0,
96 &gChildProcessPid, 0, &childStdout, 0, 0));
98 // Start monitoring the test server (in a separate thread) to
99 // ensure we don't block on the child process more than a timeout.
100 startTestServerMonitor();
103 GIOChannel* ioChannel = g_io_channel_unix_new(childStdout);
104 if (g_io_channel_read_chars(ioChannel, msg, 2, 0, 0) == G_IO_STATUS_NORMAL) {
105 // Check whether the server sent a message saying it's ready
106 // and store the result globally, so the monitor can see it.
107 gChildIsReady = msg[0] == 'O' && msg[1] == 'K';
109 g_io_channel_unref(ioChannel);
112 // The timeout was reached and the server is not ready yet, so
113 // stop it inmediately, and let the unit tests fail.
118 class InspectorServerTest: public WebViewTest {
120 MAKE_GLIB_TEST_FIXTURE(InspectorServerTest);
122 InspectorServerTest()
129 loadHtml("<script type=\"text/javascript\">\n"
131 "var xhr = new XMLHttpRequest;\n"
132 "xhr.open(\"GET\", \"/pagelist.json\");\n"
133 "xhr.onload = function(e) {\n"
134 "if (xhr.status == 200) {\n"
135 "pages = JSON.parse(xhr.responseText);\n"
136 "document.title = \"OK\";\n"
138 "document.title = \"FAIL\";\n"
142 "http://127.0.0.1:2999/");
144 waitUntilTitleChanged();
146 if (!strcmp(webkit_web_view_get_title(m_webView), "OK"))
152 ~InspectorServerTest()
157 // Test to get inspector server page list from the test server.
158 // Should contain only one entry pointing to http://127.0.0.1:2999/webinspector/inspector.html?page=1
159 static void testInspectorServerPageList(InspectorServerTest* test, gconstpointer)
161 GOwnPtr<GError> error;
163 test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
164 g_assert(test->getPageList());
166 WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages.length;", &error.outPtr());
167 g_assert(javascriptResult);
168 g_assert(!error.get());
169 g_assert_cmpint(WebViewTest::javascriptResultToNumber(javascriptResult), ==, 1);
171 javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages[0].id;", &error.outPtr());
172 g_assert(javascriptResult);
173 g_assert(!error.get());
174 int pageId = WebViewTest::javascriptResultToNumber(javascriptResult);
176 GOwnPtr<char> valueString;
177 javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages[0].url;", &error.outPtr());
178 g_assert(javascriptResult);
179 g_assert(!error.get());
180 valueString.set(WebViewTest::javascriptResultToCString(javascriptResult));
181 g_assert_cmpstr(valueString.get(), ==, "http://127.0.0.1:2999/");
183 javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages[0].inspectorUrl;", &error.outPtr());
184 g_assert(javascriptResult);
185 g_assert(!error.get());
186 valueString.set(WebViewTest::javascriptResultToCString(javascriptResult));
187 String validInspectorURL = String("/inspector.html?page=") + String::number(pageId);
188 ASSERT_CMP_CSTRING(valueString.get(), ==, validInspectorURL.utf8());
191 // Test sending a raw remote debugging message through our web socket server.
192 // For this specific message see: http://code.google.com/chrome/devtools/docs/protocol/tot/runtime.html#command-evaluate
193 static void testRemoteDebuggingMessage(InspectorServerTest* test, gconstpointer)
195 test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
197 test->loadHtml("<script type=\"text/javascript\">\n"
198 "var socket = new WebSocket('ws://127.0.0.1:2999/devtools/page/1');\n"
199 "socket.onmessage = function(message) {\n"
200 "var response = JSON.parse(message.data);\n"
201 "if (response.id === 1)\n"
202 "document.title = response.result.result.value;\n"
204 "document.title = \"FAIL\";\n"
206 "socket.onopen = function() {\n"
207 "socket.send('{\"id\": 1, \"method\": \"Runtime.evaluate\", \"params\": {\"expression\": \"2 + 2\" } }');\n"
210 "http://127.0.0.1:2999/");
211 test->waitUntilTitleChanged();
213 g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, "4");
216 static void openRemoteDebuggingSession(InspectorServerTest* test, gconstpointer)
218 // To test the whole pipeline this exploits a behavior of the inspector front-end which won't provide any title unless the
219 // debugging session was established correctly through web socket. It should be something like "Web Inspector - <Page URL>".
220 // In our case page URL should be http://127.0.0.1:2999/
221 // So this test case will fail if:
222 // - The page list didn't return a valid inspector URL
223 // - Or the front-end couldn't be loaded through the inspector HTTP server
224 // - Or the web socket connection couldn't be established between the front-end and the page through the inspector server
225 // Let's see if this test isn't raising too many false positives, in which case we should use a better predicate if available.
227 test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
229 g_assert(test->getPageList());
231 GOwnPtr<GError> error;
232 WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages[0].inspectorUrl;", &error.outPtr());
233 g_assert(javascriptResult);
234 g_assert(!error.get());
236 String resolvedURL = String("http://127.0.0.1:2999/") + String::fromUTF8(WebViewTest::javascriptResultToCString(javascriptResult));
237 test->loadURI(resolvedURL.utf8().data());
238 test->waitUntilTitleChanged();
240 g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, "Web Inspector - http://127.0.0.1:2999/");
243 static void sendIncompleteRequest(InspectorServerTest* test, gconstpointer)
245 GOwnPtr<GError> error;
247 // Connect to the inspector server.
248 GSocketClient* client = g_socket_client_new();
249 GSocketConnection* connection = g_socket_client_connect_to_host(client, "127.0.0.1", 2999, NULL, &error.outPtr());
250 g_assert(!error.get());
252 // Send incomplete request (missing blank line after headers) and check if inspector server
253 // replies. The server should not reply to an incomplete request and the test should timeout
255 GOutputStream* ostream = g_io_stream_get_output_stream(G_IO_STREAM(connection));
256 // Request missing blank line after headers.
257 const gchar* incompleteRequest = "GET /devtools/page/1 HTTP/1.1\r\nHost: Localhost\r\n";
258 g_output_stream_write(ostream, incompleteRequest, strlen(incompleteRequest), NULL, &error.outPtr());
259 g_assert(!error.get());
261 GInputStream* istream = g_io_stream_get_input_stream(G_IO_STREAM(connection));
263 memset(response, 0, sizeof(response));
264 g_input_stream_read_async(istream, response, sizeof(response) - 1, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
265 // Give a chance for the server to reply.
267 // If we got any answer it means the server replied to an incomplete request, lets fail.
268 g_assert(String(response).isEmpty());
273 // Overwrite WEBKIT_INSPECTOR_SERVER variable with default IP address but different port to avoid conflict with the test inspector server page.
274 g_setenv("WEBKIT_INSPECTOR_SERVER", "127.0.0.1:2998", TRUE);
277 InspectorServerTest::add("WebKitWebInspectorServer", "test-page-list", testInspectorServerPageList);
278 InspectorServerTest::add("WebKitWebInspectorServer", "test-remote-debugging-message", testRemoteDebuggingMessage);
279 InspectorServerTest::add("WebKitWebInspectorServer", "test-open-debugging-session", openRemoteDebuggingSession);
280 InspectorServerTest::add("WebKitWebInspectorServer", "test-incomplete-request", sendIncompleteRequest);