48b4e2be34fa9c8608f8cdff952fb916f266919e
[WebKit-https.git] / Source / WebKit2 / UIProcess / API / gtk / tests / TestInspectorServer.cpp
1 /*
2  * Copyright (C) 2012 Samsung Electronics Ltd. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
23  */
24
25 #include "config.h"
26
27 #include "WebViewTest.h"
28 #include <wtf/gobject/GRefPtr.h>
29 #include <wtf/text/WTFString.h>
30
31 // Name of the test server application creating the webView object.
32 static const char* gTestServerAppName = "InspectorTestServer";
33
34 // Max seconds to wait for the test server before inspecting it.
35 static const int gMaxWaitForChild = 5;
36
37 // The PID for the test server running, so we can kill it if needed.
38 static GPid gChildProcessPid = 0;
39
40 // Whether the child has replied and it's ready.
41 static bool gChildIsReady = false;
42
43 static void stopTestServer()
44 {
45     // Do nothing if there's no server running.
46     if (!gChildProcessPid)
47         return;
48
49     g_spawn_close_pid(gChildProcessPid);
50     kill(gChildProcessPid, SIGTERM);
51     gChildProcessPid = 0;
52 }
53
54 static void sigAbortHandler(int sigNum)
55 {
56     // Just stop the test server if SIGABRT was received.
57     stopTestServer();
58 }
59
60 static gpointer testServerMonitorThreadFunc(gpointer)
61 {
62     // Wait for the specified timeout to happen.
63     g_usleep(gMaxWaitForChild * G_USEC_PER_SEC);
64
65     // Kill the child process if not ready yet.
66     if (!gChildIsReady)
67         stopTestServer();
68
69     g_thread_exit(0);
70     return 0;
71 }
72
73 static void startTestServerMonitor()
74 {
75     gChildIsReady = false;
76
77 #if (!GLIB_CHECK_VERSION(2, 31, 0))
78     g_thread_create(testServerMonitorThreadFunc, 0, FALSE, 0);
79 #else
80     g_thread_new("TestServerMonitor", testServerMonitorThreadFunc, 0);
81 #endif
82 }
83
84 static void startTestServer()
85 {
86     // Prepare argv[] for spawning the server process.
87     GOwnPtr<char> testServerPath(g_build_filename(WEBKIT_EXEC_PATH, "WebKit2APITests", gTestServerAppName, NULL));
88
89     // We install a handler to ensure that we kill the child process
90     // if the parent dies because of whatever the reason is.
91     signal(SIGABRT, sigAbortHandler);
92
93     char* testServerArgv[2];
94     testServerArgv[0] = testServerPath.get();
95     testServerArgv[1] = 0;
96
97     // Spawn the server, getting its stdout file descriptor to set a
98     // communication channel, so we know when it's ready.
99     int childStdout = 0;
100     g_assert(g_spawn_async_with_pipes(0, testServerArgv, 0, static_cast<GSpawnFlags>(0), 0, 0,
101         &gChildProcessPid, 0, &childStdout, 0, 0));
102
103     // Start monitoring the test server (in a separate thread) to
104     // ensure we don't block on the child process more than a timeout.
105     startTestServerMonitor();
106
107     char msg[2];
108     GIOChannel* ioChannel = g_io_channel_unix_new(childStdout);
109     if (g_io_channel_read_chars(ioChannel, msg, 2, 0, 0) == G_IO_STATUS_NORMAL) {
110         // Check whether the server sent a message saying it's ready
111         // and store the result globally, so the monitor can see it.
112         gChildIsReady = msg[0] == 'O' && msg[1] == 'K';
113     }
114     g_io_channel_unref(ioChannel);
115     close(childStdout);
116
117     // The timeout was reached and the server is not ready yet, so
118     // stop it inmediately, and let the unit tests fail.
119     if (!gChildIsReady)
120         stopTestServer();
121 }
122
123 class InspectorServerTest: public WebViewTest {
124 public:
125     MAKE_GLIB_TEST_FIXTURE(InspectorServerTest);
126
127     InspectorServerTest()
128         : WebViewTest()
129     {
130     }
131
132     bool getPageList()
133     {
134         loadHtml("<script type=\"text/javascript\">\n"
135             "var pages;\n"
136             "var xhr = new XMLHttpRequest;\n"
137             "xhr.open(\"GET\", \"/pagelist.json\");\n"
138             "xhr.onload = function(e) {\n"
139                 "if (xhr.status == 200) {\n"
140                     "pages = JSON.parse(xhr.responseText);\n"
141                     "document.title = \"OK\";\n"
142                 "} else \n"
143                     "document.title = \"FAIL\";\n"
144                 "}\n"
145             "xhr.send();\n"
146             "</script>\n",
147             "http://127.0.0.1:2999/");
148
149         waitUntilTitleChanged();
150
151         if (!strcmp(webkit_web_view_get_title(m_webView), "OK"))
152             return true;
153
154         return false;
155     }
156
157     ~InspectorServerTest()
158     {
159     }
160 };
161
162 // Test to get inspector server page list from the test server.
163 // Should contain only one entry pointing to http://127.0.0.1:2999/webinspector/inspector.html?page=1
164 static void testInspectorServerPageList(InspectorServerTest* test, gconstpointer)
165 {
166     GOwnPtr<GError> error;
167
168     test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
169     g_assert(test->getPageList());
170
171     WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages.length;", &error.outPtr());
172     g_assert(javascriptResult);
173     g_assert(!error.get());
174     g_assert_cmpint(WebViewTest::javascriptResultToNumber(javascriptResult), ==, 1);
175
176     javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages[0].id;", &error.outPtr());
177     g_assert(javascriptResult);
178     g_assert(!error.get());
179     int pageId = WebViewTest::javascriptResultToNumber(javascriptResult);
180
181     GOwnPtr<char> valueString;
182     javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages[0].url;", &error.outPtr());
183     g_assert(javascriptResult);
184     g_assert(!error.get());
185     valueString.set(WebViewTest::javascriptResultToCString(javascriptResult));
186     g_assert_cmpstr(valueString.get(), ==, "http://127.0.0.1:2999/");
187
188     javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages[0].inspectorUrl;", &error.outPtr());
189     g_assert(javascriptResult);
190     g_assert(!error.get());
191     valueString.set(WebViewTest::javascriptResultToCString(javascriptResult));
192     String validInspectorURL = String("/webinspector/inspector.html?page=") + String::number(pageId);
193     ASSERT_CMP_CSTRING(valueString.get(), ==, validInspectorURL.utf8());
194 }
195
196 // Test sending a raw remote debugging message through our web socket server.
197 // For this specific message see: http://code.google.com/chrome/devtools/docs/protocol/tot/runtime.html#command-evaluate
198 static void testRemoteDebuggingMessage(InspectorServerTest* test, gconstpointer)
199 {
200     test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
201
202     test->loadHtml("<script type=\"text/javascript\">\n"
203         "var socket = new WebSocket('ws://127.0.0.1:2999/devtools/page/1');\n"
204         "socket.onmessage = function(message) {\n"
205             "var response = JSON.parse(message.data);\n"
206             "if (response.id === 1)\n"
207                 "document.title = response.result.result.value;\n"
208             "else\n"
209                 "document.title = \"FAIL\";\n"
210             "}\n"
211             "socket.onopen = function() {\n"
212             "socket.send('{\"id\": 1, \"method\": \"Runtime.evaluate\", \"params\": {\"expression\": \"2 + 2\" } }');\n"
213         "}\n"
214         "</script>",
215         "http://127.0.0.1:2999/");
216     test->waitUntilTitleChanged();
217
218     g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, "4");
219 }
220
221 static void openRemoteDebuggingSession(InspectorServerTest* test, gconstpointer)
222 {
223     // To test the whole pipeline this exploits a behavior of the inspector front-end which won't provide any title unless the
224     // debugging session was established correctly through web socket. It should be something like "Web Inspector - <Page URL>".
225     // In our case page URL should be http://127.0.0.1:2999/
226     // So this test case will fail if:
227     // - The page list didn't return a valid inspector URL
228     // - Or the front-end couldn't be loaded through the inspector HTTP server
229     // - Or the web socket connection couldn't be established between the front-end and the page through the inspector server
230     // Let's see if this test isn't raising too many false positives, in which case we should use a better predicate if available.
231
232     test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
233
234     g_assert(test->getPageList());
235
236     GOwnPtr<GError> error;
237     WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("pages[0].inspectorUrl;", &error.outPtr());
238     g_assert(javascriptResult);
239     g_assert(!error.get());
240
241     String resolvedURL = String("http://127.0.0.1:2999/") + String::fromUTF8(WebViewTest::javascriptResultToCString(javascriptResult));
242     test->loadURI(resolvedURL.utf8().data());
243     test->waitUntilTitleChanged();
244
245     g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, "Web Inspector - http://127.0.0.1:2999/");
246 }
247
248
249 void beforeAll()
250 {
251     // Overwrite WEBKIT_INSPECTOR_SERVER variable with default IP address but different port to avoid conflict with the test inspector server page.
252     g_setenv("WEBKIT_INSPECTOR_SERVER", "127.0.0.1:2998", TRUE);
253
254     startTestServer();
255     InspectorServerTest::add("WebKitWebInspectorServer", "test-page-list", testInspectorServerPageList);
256     InspectorServerTest::add("WebKitWebInspectorServer", "test-remote-debugging-message", testRemoteDebuggingMessage);
257     InspectorServerTest::add("WebKitWebInspectorServer", "test-open-debugging-session", openRemoteDebuggingSession);
258
259 }
260
261 void afterAll()
262 {
263     stopTestServer();
264 }