699982e55ff4f08eb3e3d6a3aaf79cf5109d1c5d
[WebKit.git] / Tools / WebKitTestRunner / TestController.cpp
1 /*
2  * Copyright (C) 2010 Apple Inc. 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 APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "TestController.h"
27
28 #include "PlatformWebView.h"
29 #include "StringFunctions.h"
30 #include "TestInvocation.h"
31 #include <cstdio>
32 #include <WebKit2/WKPageGroup.h>
33 #include <WebKit2/WKContextPrivate.h>
34 #include <WebKit2/WKPreferencesPrivate.h>
35 #include <wtf/PassOwnPtr.h>
36
37 namespace WTR {
38
39 static const double defaultLongTimeout = 30;
40 static const double defaultShortTimeout = 5;
41
42 static WKURLRef blankURL()
43 {
44     static WKURLRef staticBlankURL = WKURLCreateWithUTF8CString("about:blank");
45     return staticBlankURL;
46 }
47
48 static TestController* controller;
49
50 TestController& TestController::shared()
51 {
52     ASSERT(controller);
53     return *controller;
54 }
55
56 TestController::TestController(int argc, const char* argv[])
57     : m_dumpPixels(false)
58     , m_verbose(false)
59     , m_printSeparators(false)
60     , m_usingServerMode(false)
61     , m_state(Initial)
62     , m_doneResetting(false)
63     , m_longTimeout(defaultLongTimeout)
64     , m_shortTimeout(defaultShortTimeout)
65 {
66     initialize(argc, argv);
67     controller = this;
68     run();
69     controller = 0;
70 }
71
72 TestController::~TestController()
73 {
74 }
75
76 static WKRect getWindowFrameMainPage(WKPageRef page, const void* clientInfo)
77 {
78     PlatformWebView* view = static_cast<TestController*>(const_cast<void*>(clientInfo))->mainWebView();
79     return view->windowFrame();
80 }
81
82 static void setWindowFrameMainPage(WKPageRef page, WKRect frame, const void* clientInfo)
83 {
84     PlatformWebView* view = static_cast<TestController*>(const_cast<void*>(clientInfo))->mainWebView();
85     view->setWindowFrame(frame);
86 }
87
88 static WKRect getWindowFrameOtherPage(WKPageRef page, const void* clientInfo)
89 {
90     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
91     return view->windowFrame();
92 }
93
94 static void setWindowFrameOtherPage(WKPageRef page, WKRect frame, const void* clientInfo)
95 {
96     PlatformWebView* view = static_cast<PlatformWebView*>(const_cast<void*>(clientInfo));
97     view->setWindowFrame(frame);
98 }
99
100 static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, const void *clientInfo)
101 {
102     printf("%s\n", toSTD(message).c_str());
103     return true;
104 }
105
106 static void closeOtherPage(WKPageRef page, const void* clientInfo)
107 {
108     WKPageClose(page);
109     const PlatformWebView* view = static_cast<const PlatformWebView*>(clientInfo);
110     delete view;
111 }
112
113 static WKPageRef createOtherPage(WKPageRef oldPage, WKDictionaryRef, WKEventModifiers, WKEventMouseButton, const void*)
114 {
115     PlatformWebView* view = new PlatformWebView(WKPageGetContext(oldPage), WKPageGetPageGroup(oldPage));
116     WKPageRef newPage = view->page();
117
118     view->resizeTo(800, 600);
119
120     WKPageUIClient otherPageUIClient = {
121         0,
122         view,
123         createOtherPage,
124         0, // showPage
125         closeOtherPage,
126         0, // runJavaScriptAlert        
127         0, // runJavaScriptConfirm
128         0, // runJavaScriptPrompt
129         0, // setStatusText
130         0, // mouseDidMoveOverElement
131         0, // missingPluginButtonClicked
132         0, // didNotHandleKeyEvent
133         0, // toolbarsAreVisible
134         0, // setToolbarsAreVisible
135         0, // menuBarIsVisible
136         0, // setMenuBarIsVisible
137         0, // statusBarIsVisible
138         0, // setStatusBarIsVisible
139         0, // isResizable
140         0, // setIsResizable
141         getWindowFrameOtherPage,
142         setWindowFrameOtherPage,
143         runBeforeUnloadConfirmPanel,
144         0, // didDraw
145         0, // pageDidScroll
146         0, // exceededDatabaseQuota
147         0, // runOpenPanel
148         0, // decidePolicyForGeolocationPermissionRequest
149     };
150     WKPageSetPageUIClient(newPage, &otherPageUIClient);
151
152     WKRetain(newPage);
153     return newPage;
154 }
155
156 void TestController::initialize(int argc, const char* argv[])
157 {
158     platformInitialize();
159
160     bool printSupportedFeatures = false;
161
162     for (int i = 1; i < argc; ++i) {
163         std::string argument(argv[i]);
164
165         if (argument == "--timeout" && i + 1 < argc) {
166             m_longTimeout = atoi(argv[++i]);
167             // Scale up the short timeout to match.
168             m_shortTimeout = defaultShortTimeout * m_longTimeout / defaultLongTimeout;
169             continue;
170         }
171         if (argument == "--pixel-tests") {
172             m_dumpPixels = true;
173             continue;
174         }
175         if (argument == "--verbose") {
176             m_verbose = true;
177             continue;
178         }
179         if (argument == "--print-supported-features") {
180             printSupportedFeatures = true;
181             break;
182         }
183
184         // Skip any other arguments that begin with '--'.
185         if (argument.length() >= 2 && argument[0] == '-' && argument[1] == '-')
186             continue;
187
188         m_paths.push_back(argument);
189     }
190
191     if (printSupportedFeatures) {
192         // FIXME: On Windows, DumpRenderTree uses this to expose whether it supports 3d
193         // transforms and accelerated compositing. When we support those features, we
194         // should match DRT's behavior.
195         exit(0);
196     }
197
198     m_usingServerMode = (m_paths.size() == 1 && m_paths[0] == "-");
199     if (m_usingServerMode)
200         m_printSeparators = true;
201     else
202         m_printSeparators = m_paths.size() > 1;
203
204     initializeInjectedBundlePath();
205     initializeTestPluginDirectory();
206
207     WKRetainPtr<WKStringRef> pageGroupIdentifier(AdoptWK, WKStringCreateWithUTF8CString("WebKitTestRunnerPageGroup"));
208     m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get()));
209
210     m_context.adopt(WKContextCreateWithInjectedBundlePath(injectedBundlePath()));
211     platformInitializeContext();
212
213     WKContextInjectedBundleClient injectedBundleClient = {
214         0,
215         this,
216         didReceiveMessageFromInjectedBundle,
217         didReceiveSynchronousMessageFromInjectedBundle
218     };
219     WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient);
220
221     _WKContextSetAdditionalPluginsDirectory(m_context.get(), testPluginDirectory());
222
223     m_mainWebView = adoptPtr(new PlatformWebView(m_context.get(), m_pageGroup.get()));
224
225     WKPageUIClient pageUIClient = {
226         0,
227         this,
228         createOtherPage,
229         0, // showPage
230         0, // close
231         0, // runJavaScriptAlert        
232         0, // runJavaScriptConfirm
233         0, // runJavaScriptPrompt
234         0, // setStatusText
235         0, // mouseDidMoveOverElement
236         0, // missingPluginButtonClicked
237         0, // didNotHandleKeyEvent
238         0, // toolbarsAreVisible
239         0, // setToolbarsAreVisible
240         0, // menuBarIsVisible
241         0, // setMenuBarIsVisible
242         0, // statusBarIsVisible
243         0, // setStatusBarIsVisible
244         0, // isResizable
245         0, // setIsResizable
246         getWindowFrameMainPage,
247         setWindowFrameMainPage,
248         runBeforeUnloadConfirmPanel,
249         0, // didDraw
250         0, // pageDidScroll
251         0, // exceededDatabaseQuota
252         0, // runOpenPanel
253         0, // decidePolicyForGeolocationPermissionRequest
254     };
255     WKPageSetPageUIClient(m_mainWebView->page(), &pageUIClient);
256
257     WKPageLoaderClient pageLoaderClient = {
258         0,
259         this,
260         0, // didStartProvisionalLoadForFrame
261         0, // didReceiveServerRedirectForProvisionalLoadForFrame
262         0, // didFailProvisionalLoadWithErrorForFrame
263         0, // didCommitLoadForFrame
264         0, // didFinishDocumentLoadForFrame
265         didFinishLoadForFrame,
266         0, // didFailLoadWithErrorForFrame
267         0, // didSameDocumentNavigationForFrame
268         0, // didReceiveTitleForFrame
269         0, // didFirstLayoutForFrame
270         0, // didFirstVisuallyNonEmptyLayoutForFrame
271         0, // didRemoveFrameFromHierarchy
272         0, // didDisplayInsecureContentForFrame
273         0, // didRunInsecureContentForFrame
274         0, // canAuthenticateAgainstProtectionSpaceInFrame
275         0, // didReceiveAuthenticationChallengeInFrame
276         0, // didStartProgress
277         0, // didChangeProgress
278         0, // didFinishProgress
279         0, // didBecomeUnresponsive
280         0, // didBecomeResponsive
281         processDidCrash, // processDidCrash
282         0 // didChangeBackForwardList
283     };
284     WKPageSetPageLoaderClient(m_mainWebView->page(), &pageLoaderClient);
285 }
286
287 bool TestController::resetStateToConsistentValues()
288 {
289     m_state = Resetting;
290
291     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("Reset"));
292     WKContextPostMessageToInjectedBundle(TestController::shared().context(), messageName.get(), 0);
293
294     // FIXME: This function should also ensure that there is only one page open.
295
296     // Reset preferences
297     WKPreferencesRef preferences = WKPageGroupGetPreferences(m_pageGroup.get());
298     WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
299     WKPreferencesSetFontSmoothingLevel(preferences, kWKFontSmoothingLevelNoSubpixelAntiAliasing);
300     WKPreferencesSetXSSAuditorEnabled(preferences, false);
301     WKPreferencesSetDeveloperExtrasEnabled(preferences, true);
302     WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true);
303
304     static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times");
305     static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery");
306     static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus");
307     static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier");
308     static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica");
309     static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times");
310
311     WKPreferencesSetStandardFontFamily(preferences, standardFontFamily);
312     WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily);
313     WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily);
314     WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily);
315     WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily);
316     WKPreferencesSetSerifFontFamily(preferences, serifFontFamily);
317
318     m_mainWebView->focus();
319
320     // Reset main page back to about:blank
321     m_doneResetting = false;
322
323     WKPageLoadURL(m_mainWebView->page(), blankURL());
324     runUntil(m_doneResetting, ShortTimeout);
325     return m_doneResetting;
326 }
327
328 bool TestController::runTest(const char* test)
329 {
330     if (!resetStateToConsistentValues())
331         return false;
332
333     m_state = RunningTest;
334     m_currentInvocation.set(new TestInvocation(test));
335     m_currentInvocation->invoke();
336     m_currentInvocation.clear();
337
338     return true;
339 }
340
341 void TestController::runTestingServerLoop()
342 {
343     char filenameBuffer[2048];
344     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
345         char *newLineCharacter = strchr(filenameBuffer, '\n');
346         if (newLineCharacter)
347             *newLineCharacter = '\0';
348
349         if (strlen(filenameBuffer) == 0)
350             continue;
351
352         if (!runTest(filenameBuffer))
353             break;
354     }
355 }
356
357 void TestController::run()
358 {
359     if (m_usingServerMode)
360         runTestingServerLoop();
361     else {
362         for (size_t i = 0; i < m_paths.size(); ++i) {
363             if (!runTest(m_paths[i].c_str()))
364                 break;
365         }
366     }
367
368 }
369
370 void TestController::runUntil(bool& done, TimeoutDuration timeoutDuration)
371 {
372     platformRunUntil(done, timeoutDuration == ShortTimeout ? m_shortTimeout : m_longTimeout);
373 }
374
375 // WKContextInjectedBundleClient
376
377 void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
378 {
379     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
380 }
381
382 void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
383 {
384     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
385 }
386
387 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
388 {
389     m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
390 }
391
392 WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
393 {
394     return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
395 }
396
397 // WKPageLoaderClient
398
399 void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef, const void* clientInfo)
400 {
401     static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(page, frame);
402 }
403
404 void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
405 {
406     static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash(page);
407 }
408
409 void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame)
410 {
411     if (m_state != Resetting)
412         return;
413
414     if (!WKFrameIsMainFrame(frame))
415         return;
416
417     WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(frame));
418     if (!WKURLIsEqual(wkURL.get(), blankURL()))
419         return;
420
421     m_doneResetting = true;
422     shared().notifyDone();
423 }
424
425 void TestController::processDidCrash(WKPageRef page)
426 {
427     fputs("#CRASHED - WebProcess\n", stderr);
428     fflush(stderr);
429 }
430
431 } // namespace WTR