abea8e4f97a60489ce2d015b17b91a372d6b2d99
[WebKit-https.git] / Tools / WebKitTestRunner / TestInvocation.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 "config.h"
27 #include "TestInvocation.h"
28
29 #include "PlatformWebView.h"
30 #include "StringFunctions.h"
31 #include "TestController.h"
32 #include <climits>
33 #include <cstdio>
34 #include <WebKit2/WKDictionary.h>
35 #include <WebKit2/WKContextPrivate.h>
36 #include <WebKit2/WKInspector.h>
37 #include <WebKit2/WKRetainPtr.h>
38 #include <wtf/OwnArrayPtr.h>
39 #include <wtf/PassOwnArrayPtr.h>
40 #include <wtf/text/CString.h>
41
42 #if OS(WINDOWS)
43 #include <direct.h> // For _getcwd.
44 #define getcwd _getcwd // MSDN says getcwd is deprecated.
45 #define PATH_MAX _MAX_PATH
46 #else
47 #include <unistd.h> // For getcwd.
48 #endif
49
50 using namespace WebKit;
51 using namespace std;
52
53 namespace WTR {
54
55 static WKURLRef createWKURL(const char* pathOrURL)
56 {
57     if (strstr(pathOrURL, "http://") || strstr(pathOrURL, "https://") || strstr(pathOrURL, "file://"))
58         return WKURLCreateWithUTF8CString(pathOrURL);
59
60     // Creating from filesytem path.
61     size_t length = strlen(pathOrURL);
62     if (!length)
63         return 0;
64
65 #if OS(WINDOWS)
66     const char separator = '\\';
67     bool isAbsolutePath = length >= 3 && pathOrURL[1] == ':' && pathOrURL[2] == separator;
68     // FIXME: Remove the "localhost/" suffix once <http://webkit.org/b/55683> is fixed.
69     const char* filePrefix = "file://localhost/";
70 #else
71     const char separator = '/';
72     bool isAbsolutePath = pathOrURL[0] == separator;
73     const char* filePrefix = "file://";
74 #endif
75     static const size_t prefixLength = strlen(filePrefix);
76
77     OwnArrayPtr<char> buffer;
78     if (isAbsolutePath) {
79         buffer = adoptArrayPtr(new char[prefixLength + length + 1]);
80         strcpy(buffer.get(), filePrefix);
81         strcpy(buffer.get() + prefixLength, pathOrURL);
82     } else {
83         buffer = adoptArrayPtr(new char[prefixLength + PATH_MAX + length + 2]); // 1 for the separator
84         strcpy(buffer.get(), filePrefix);
85         if (!getcwd(buffer.get() + prefixLength, PATH_MAX))
86             return 0;
87         size_t numCharacters = strlen(buffer.get());
88         buffer[numCharacters] = separator;
89         strcpy(buffer.get() + numCharacters + 1, pathOrURL);
90     }
91
92     return WKURLCreateWithUTF8CString(buffer.get());
93 }
94
95 TestInvocation::TestInvocation(const std::string& pathOrURL)
96     : m_url(AdoptWK, createWKURL(pathOrURL.c_str()))
97     , m_pathOrURL(pathOrURL)
98     , m_dumpPixels(false)
99     , m_skipPixelTestOption(false)
100     , m_gotInitialResponse(false)
101     , m_gotFinalMessage(false)
102     , m_gotRepaint(false)
103     , m_error(false)
104 {
105 }
106
107 TestInvocation::~TestInvocation()
108 {
109 }
110
111 void TestInvocation::setIsPixelTest(const std::string& expectedPixelHash)
112 {
113     if (m_skipPixelTestOption && !expectedPixelHash.length())
114         return;
115     m_dumpPixels = true;
116     m_expectedPixelHash = expectedPixelHash;
117 }
118
119 static const unsigned w3cSVGWidth = 480;
120 static const unsigned w3cSVGHeight = 360;
121 static const unsigned normalWidth = 800;
122 static const unsigned normalHeight = 600;
123
124 static void sizeWebViewForCurrentTest(const char* pathOrURL)
125 {
126     bool isSVGW3CTest = strstr(pathOrURL, "svg/W3C-SVG-1.1") || strstr(pathOrURL, "svg\\W3C-SVG-1.1");
127
128     if (isSVGW3CTest)
129         TestController::shared().mainWebView()->resizeTo(w3cSVGWidth, w3cSVGHeight);
130     else
131         TestController::shared().mainWebView()->resizeTo(normalWidth, normalHeight);
132 }
133
134 static bool shouldOpenWebInspector(const char* pathOrURL)
135 {
136     return strstr(pathOrURL, "inspector/") || strstr(pathOrURL, "inspector\\");
137 }
138
139 void TestInvocation::invoke()
140 {
141     sizeWebViewForCurrentTest(m_pathOrURL.c_str());
142
143     WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("BeginTest"));
144     WKRetainPtr<WKMutableDictionaryRef> beginTestMessageBody = adoptWK(WKMutableDictionaryCreate());
145
146     WKRetainPtr<WKStringRef> dumpPixelsKey = adoptWK(WKStringCreateWithUTF8CString("DumpPixels"));
147     WKRetainPtr<WKBooleanRef> dumpPixelsValue = adoptWK(WKBooleanCreate(m_dumpPixels));
148     WKDictionaryAddItem(beginTestMessageBody.get(), dumpPixelsKey.get(), dumpPixelsValue.get());
149
150     WKRetainPtr<WKStringRef> useWaitToDumpWatchdogTimerKey = adoptWK(WKStringCreateWithUTF8CString("UseWaitToDumpWatchdogTimer"));
151     WKRetainPtr<WKBooleanRef> useWaitToDumpWatchdogTimerValue = adoptWK(WKBooleanCreate(TestController::shared().useWaitToDumpWatchdogTimer()));
152     WKDictionaryAddItem(beginTestMessageBody.get(), useWaitToDumpWatchdogTimerKey.get(), useWaitToDumpWatchdogTimerValue.get());
153
154     WKContextPostMessageToInjectedBundle(TestController::shared().context(), messageName.get(), beginTestMessageBody.get());
155
156     TestController::shared().runUntil(m_gotInitialResponse, TestController::ShortTimeout);
157     if (!m_gotInitialResponse) {
158         dump("Timed out waiting for initial response from web process\n");
159         return;
160     }
161     if (m_error) {
162         dump("FAIL\n");
163         return;
164     }
165
166     if (shouldOpenWebInspector(m_pathOrURL.c_str()))
167         WKInspectorShow(WKPageGetInspector(TestController::shared().mainWebView()->page()));
168
169     WKPageLoadURL(TestController::shared().mainWebView()->page(), m_url.get());
170
171     TestController::shared().runUntil(m_gotFinalMessage, TestController::shared().useWaitToDumpWatchdogTimer() ? TestController::LongTimeout : TestController::NoTimeout);
172     if (!m_gotFinalMessage)
173         dump("Timed out waiting for final message from web process\n");
174     else if (m_error)
175         dump("FAIL\n");
176
177     WKInspectorClose(WKPageGetInspector(TestController::shared().mainWebView()->page()));
178 }
179
180 void TestInvocation::dump(const char* stringToDump, bool singleEOF)
181 {
182     printf("Content-Type: text/plain\n");
183     printf("%s", stringToDump);
184
185     fputs("#EOF\n", stdout);
186     fputs("#EOF\n", stderr);
187     if (!singleEOF)
188         fputs("#EOF\n", stdout);
189     fflush(stdout);
190     fflush(stderr);
191 }
192
193 bool TestInvocation::compareActualHashToExpectedAndDumpResults(const char actualHash[33])
194 {
195     // Compute the hash of the bitmap context pixels
196     fprintf(stdout, "\nActualHash: %s\n", actualHash);
197
198     if (!m_expectedPixelHash.length())
199         return false;
200
201     ASSERT(m_expectedPixelHash.length() == 32);
202     fprintf(stdout, "\nExpectedHash: %s\n", m_expectedPixelHash.c_str());
203
204     // FIXME: Do case insensitive compare.
205     return m_expectedPixelHash == actualHash;
206 }
207
208 void TestInvocation::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
209 {
210     if (WKStringIsEqualToUTF8CString(messageName, "Error")) {
211         // Set all states to true to stop spinning the runloop.
212         m_gotInitialResponse = true;
213         m_gotFinalMessage = true;
214         m_error = true;
215         TestController::shared().notifyDone();
216         return;
217     }
218
219     if (WKStringIsEqualToUTF8CString(messageName, "Ack")) {
220         ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
221         WKStringRef messageBodyString = static_cast<WKStringRef>(messageBody);
222         if (WKStringIsEqualToUTF8CString(messageBodyString, "BeginTest")) {
223             m_gotInitialResponse = true;
224             TestController::shared().notifyDone();
225             return;
226         }
227
228         ASSERT_NOT_REACHED();
229     }
230
231     if (WKStringIsEqualToUTF8CString(messageName, "Done")) {
232         ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
233         WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
234
235         WKRetainPtr<WKStringRef> textOutputKey(AdoptWK, WKStringCreateWithUTF8CString("TextOutput"));
236         WKStringRef textOutput = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, textOutputKey.get()));
237
238         WKRetainPtr<WKStringRef> pixelResultKey = adoptWK(WKStringCreateWithUTF8CString("PixelResult"));
239         WKImageRef pixelResult = static_cast<WKImageRef>(WKDictionaryGetItemForKey(messageBodyDictionary, pixelResultKey.get()));
240         ASSERT(!pixelResult || m_dumpPixels);
241         
242         WKRetainPtr<WKStringRef> repaintRectsKey = adoptWK(WKStringCreateWithUTF8CString("RepaintRects"));
243         WKArrayRef repaintRects = static_cast<WKArrayRef>(WKDictionaryGetItemForKey(messageBodyDictionary, repaintRectsKey.get()));        
244
245         // Dump text.
246         dump(toWTFString(textOutput).utf8().data(), true);
247
248         // Dump pixels (if necessary).
249         if (m_dumpPixels && pixelResult)
250             dumpPixelsAndCompareWithExpected(pixelResult, repaintRects);
251
252         fputs("#EOF\n", stdout);
253         fflush(stdout);
254         fflush(stderr);
255         
256         m_gotFinalMessage = true;
257         TestController::shared().notifyDone();
258         return;
259     }
260     
261     if (WKStringIsEqualToUTF8CString(messageName, "BeforeUnloadReturnValue")) {
262         ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
263         WKBooleanRef beforeUnloadReturnValue = static_cast<WKBooleanRef>(messageBody);
264         TestController::shared().setBeforeUnloadReturnValue(WKBooleanGetValue(beforeUnloadReturnValue));
265         return;
266     }
267     
268     if (WKStringIsEqualToUTF8CString(messageName, "AddChromeInputField")) {
269         TestController::shared().mainWebView()->addChromeInputField();
270         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallAddChromeInputFieldCallback"));
271         WKContextPostMessageToInjectedBundle(TestController::shared().context(), messageName.get(), 0);
272         return;
273     }
274
275     if (WKStringIsEqualToUTF8CString(messageName, "RemoveChromeInputField")) {
276         TestController::shared().mainWebView()->removeChromeInputField();
277         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallRemoveChromeInputFieldCallback"));
278         WKContextPostMessageToInjectedBundle(TestController::shared().context(), messageName.get(), 0);
279         return;
280     }
281     
282     if (WKStringIsEqualToUTF8CString(messageName, "FocusWebView")) {
283         TestController::shared().mainWebView()->makeWebViewFirstResponder();
284         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallFocusWebViewCallback"));
285         WKContextPostMessageToInjectedBundle(TestController::shared().context(), messageName.get(), 0);
286         return;
287     }
288
289     if (WKStringIsEqualToUTF8CString(messageName, "SetBackingScaleFactor")) {
290         ASSERT(WKGetTypeID(messageBody) == WKDoubleGetTypeID());
291         double backingScaleFactor = WKDoubleGetValue(static_cast<WKDoubleRef>(messageBody));
292         WKPageSetCustomBackingScaleFactor(TestController::shared().mainWebView()->page(), backingScaleFactor);
293
294         WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CallSetBackingScaleFactorCallback"));
295         WKContextPostMessageToInjectedBundle(TestController::shared().context(), messageName.get(), 0);
296         return;
297     }
298
299     ASSERT_NOT_REACHED();
300 }
301
302 WKRetainPtr<WKTypeRef> TestInvocation::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
303 {
304     if (WKStringIsEqualToUTF8CString(messageName, "SetWindowIsKey")) {
305         ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
306         WKBooleanRef isKeyValue = static_cast<WKBooleanRef>(messageBody);
307         TestController::shared().mainWebView()->setWindowIsKey(WKBooleanGetValue(isKeyValue));
308         return 0;
309     }
310
311     ASSERT_NOT_REACHED();
312     return 0;
313 }
314
315 } // namespace WTR