05eda8ec6154c71794cf548b19df462da31e92cb
[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         0, // headerHeight
150         0, // footerHeight
151         0, // drawHeader
152         0, // drawFooter
153     };
154     WKPageSetPageUIClient(newPage, &otherPageUIClient);
155
156     WKRetain(newPage);
157     return newPage;
158 }
159
160 void TestController::initialize(int argc, const char* argv[])
161 {
162     platformInitialize();
163
164     bool printSupportedFeatures = false;
165
166     for (int i = 1; i < argc; ++i) {
167         std::string argument(argv[i]);
168
169         if (argument == "--timeout" && i + 1 < argc) {
170             m_longTimeout = atoi(argv[++i]);
171             // Scale up the short timeout to match.
172             m_shortTimeout = defaultShortTimeout * m_longTimeout / defaultLongTimeout;
173             continue;
174         }
175         if (argument == "--pixel-tests") {
176             m_dumpPixels = true;
177             continue;
178         }
179         if (argument == "--verbose") {
180             m_verbose = true;
181             continue;
182         }
183         if (argument == "--print-supported-features") {
184             printSupportedFeatures = true;
185             break;
186         }
187
188         // Skip any other arguments that begin with '--'.
189         if (argument.length() >= 2 && argument[0] == '-' && argument[1] == '-')
190             continue;
191
192         m_paths.push_back(argument);
193     }
194
195     if (printSupportedFeatures) {
196         // FIXME: On Windows, DumpRenderTree uses this to expose whether it supports 3d
197         // transforms and accelerated compositing. When we support those features, we
198         // should match DRT's behavior.
199         exit(0);
200     }
201
202     m_usingServerMode = (m_paths.size() == 1 && m_paths[0] == "-");
203     if (m_usingServerMode)
204         m_printSeparators = true;
205     else
206         m_printSeparators = m_paths.size() > 1;
207
208     initializeInjectedBundlePath();
209     initializeTestPluginDirectory();
210
211     WKRetainPtr<WKStringRef> pageGroupIdentifier(AdoptWK, WKStringCreateWithUTF8CString("WebKitTestRunnerPageGroup"));
212     m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get()));
213
214     m_context.adopt(WKContextCreateWithInjectedBundlePath(injectedBundlePath()));
215     platformInitializeContext();
216
217     WKContextInjectedBundleClient injectedBundleClient = {
218         0,
219         this,
220         didReceiveMessageFromInjectedBundle,
221         didReceiveSynchronousMessageFromInjectedBundle
222     };
223     WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient);
224
225     _WKContextSetAdditionalPluginsDirectory(m_context.get(), testPluginDirectory());
226
227     m_mainWebView = adoptPtr(new PlatformWebView(m_context.get(), m_pageGroup.get()));
228
229     WKPageUIClient pageUIClient = {
230         0,
231         this,
232         createOtherPage,
233         0, // showPage
234         0, // close
235         0, // runJavaScriptAlert        
236         0, // runJavaScriptConfirm
237         0, // runJavaScriptPrompt
238         0, // setStatusText
239         0, // mouseDidMoveOverElement
240         0, // missingPluginButtonClicked
241         0, // didNotHandleKeyEvent
242         0, // toolbarsAreVisible
243         0, // setToolbarsAreVisible
244         0, // menuBarIsVisible
245         0, // setMenuBarIsVisible
246         0, // statusBarIsVisible
247         0, // setStatusBarIsVisible
248         0, // isResizable
249         0, // setIsResizable
250         getWindowFrameMainPage,
251         setWindowFrameMainPage,
252         runBeforeUnloadConfirmPanel,
253         0, // didDraw
254         0, // pageDidScroll
255         0, // exceededDatabaseQuota
256         0, // runOpenPanel
257         0, // decidePolicyForGeolocationPermissionRequest
258         0, // headerHeight
259         0, // footerHeight
260         0, // drawHeader
261         0, // drawFooter
262     };
263     WKPageSetPageUIClient(m_mainWebView->page(), &pageUIClient);
264
265     WKPageLoaderClient pageLoaderClient = {
266         0,
267         this,
268         0, // didStartProvisionalLoadForFrame
269         0, // didReceiveServerRedirectForProvisionalLoadForFrame
270         0, // didFailProvisionalLoadWithErrorForFrame
271         0, // didCommitLoadForFrame
272         0, // didFinishDocumentLoadForFrame
273         didFinishLoadForFrame,
274         0, // didFailLoadWithErrorForFrame
275         0, // didSameDocumentNavigationForFrame
276         0, // didReceiveTitleForFrame
277         0, // didFirstLayoutForFrame
278         0, // didFirstVisuallyNonEmptyLayoutForFrame
279         0, // didRemoveFrameFromHierarchy
280         0, // didDisplayInsecureContentForFrame
281         0, // didRunInsecureContentForFrame
282         0, // canAuthenticateAgainstProtectionSpaceInFrame
283         0, // didReceiveAuthenticationChallengeInFrame
284         0, // didStartProgress
285         0, // didChangeProgress
286         0, // didFinishProgress
287         0, // didBecomeUnresponsive
288         0, // didBecomeResponsive
289         processDidCrash, // processDidCrash
290         0 // didChangeBackForwardList
291     };
292     WKPageSetPageLoaderClient(m_mainWebView->page(), &pageLoaderClient);
293 }
294
295 bool TestController::resetStateToConsistentValues()
296 {
297     m_state = Resetting;
298
299     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("Reset"));
300     WKContextPostMessageToInjectedBundle(TestController::shared().context(), messageName.get(), 0);
301
302     // FIXME: This function should also ensure that there is only one page open.
303
304     // Reset preferences
305     WKPreferencesRef preferences = WKPageGroupGetPreferences(m_pageGroup.get());
306     WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
307     WKPreferencesSetFontSmoothingLevel(preferences, kWKFontSmoothingLevelNoSubpixelAntiAliasing);
308     WKPreferencesSetXSSAuditorEnabled(preferences, false);
309     WKPreferencesSetDeveloperExtrasEnabled(preferences, true);
310     WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true);
311
312     static WKStringRef standardFontFamily = WKStringCreateWithUTF8CString("Times");
313     static WKStringRef cursiveFontFamily = WKStringCreateWithUTF8CString("Apple Chancery");
314     static WKStringRef fantasyFontFamily = WKStringCreateWithUTF8CString("Papyrus");
315     static WKStringRef fixedFontFamily = WKStringCreateWithUTF8CString("Courier");
316     static WKStringRef sansSerifFontFamily = WKStringCreateWithUTF8CString("Helvetica");
317     static WKStringRef serifFontFamily = WKStringCreateWithUTF8CString("Times");
318
319     WKPreferencesSetStandardFontFamily(preferences, standardFontFamily);
320     WKPreferencesSetCursiveFontFamily(preferences, cursiveFontFamily);
321     WKPreferencesSetFantasyFontFamily(preferences, fantasyFontFamily);
322     WKPreferencesSetFixedFontFamily(preferences, fixedFontFamily);
323     WKPreferencesSetSansSerifFontFamily(preferences, sansSerifFontFamily);
324     WKPreferencesSetSerifFontFamily(preferences, serifFontFamily);
325
326     m_mainWebView->focus();
327
328     // Reset main page back to about:blank
329     m_doneResetting = false;
330
331     WKPageLoadURL(m_mainWebView->page(), blankURL());
332     runUntil(m_doneResetting, ShortTimeout);
333     return m_doneResetting;
334 }
335
336 bool TestController::runTest(const char* test)
337 {
338     if (!resetStateToConsistentValues())
339         return false;
340
341     m_state = RunningTest;
342     m_currentInvocation.set(new TestInvocation(test));
343     m_currentInvocation->invoke();
344     m_currentInvocation.clear();
345
346     return true;
347 }
348
349 void TestController::runTestingServerLoop()
350 {
351     char filenameBuffer[2048];
352     while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
353         char *newLineCharacter = strchr(filenameBuffer, '\n');
354         if (newLineCharacter)
355             *newLineCharacter = '\0';
356
357         if (strlen(filenameBuffer) == 0)
358             continue;
359
360         if (!runTest(filenameBuffer))
361             break;
362     }
363 }
364
365 void TestController::run()
366 {
367     if (m_usingServerMode)
368         runTestingServerLoop();
369     else {
370         for (size_t i = 0; i < m_paths.size(); ++i) {
371             if (!runTest(m_paths[i].c_str()))
372                 break;
373         }
374     }
375
376 }
377
378 void TestController::runUntil(bool& done, TimeoutDuration timeoutDuration)
379 {
380     platformRunUntil(done, timeoutDuration == ShortTimeout ? m_shortTimeout : m_longTimeout);
381 }
382
383 // WKContextInjectedBundleClient
384
385 void TestController::didReceiveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void* clientInfo)
386 {
387     static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveMessageFromInjectedBundle(messageName, messageBody);
388 }
389
390 void TestController::didReceiveSynchronousMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, WKTypeRef* returnData, const void* clientInfo)
391 {
392     *returnData = static_cast<TestController*>(const_cast<void*>(clientInfo))->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody).leakRef();
393 }
394
395 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
396 {
397     if (!m_currentInvocation)
398         return;
399     m_currentInvocation->didReceiveMessageFromInjectedBundle(messageName, messageBody);
400 }
401
402 WKRetainPtr<WKTypeRef> TestController::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
403 {
404     return m_currentInvocation->didReceiveSynchronousMessageFromInjectedBundle(messageName, messageBody);
405 }
406
407 // WKPageLoaderClient
408
409 void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef, const void* clientInfo)
410 {
411     static_cast<TestController*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(page, frame);
412 }
413
414 void TestController::processDidCrash(WKPageRef page, const void* clientInfo)
415 {
416     static_cast<TestController*>(const_cast<void*>(clientInfo))->processDidCrash(page);
417 }
418
419 void TestController::didFinishLoadForFrame(WKPageRef page, WKFrameRef frame)
420 {
421     if (m_state != Resetting)
422         return;
423
424     if (!WKFrameIsMainFrame(frame))
425         return;
426
427     WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKFrameCopyURL(frame));
428     if (!WKURLIsEqual(wkURL.get(), blankURL()))
429         return;
430
431     m_doneResetting = true;
432     shared().notifyDone();
433 }
434
435 void TestController::processDidCrash(WKPageRef page)
436 {
437     fputs("#CRASHED - WebProcess\n", stderr);
438     fflush(stderr);
439 }
440
441 } // namespace WTR