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
41#if OS(WINDOWS)
42#include <direct.h> // For _getcwd.
43#define getcwd _getcwd // MSDN says getcwd is deprecated.
44#define PATH_MAX _MAX_PATH
45#endif
46
47using namespace WebKit;
48using namespace std;
49
50namespace WTR {
51
52static WKURLRef createWKURL(const char* pathOrURL)
53{
54    if (strstr(pathOrURL, "http://") || strstr(pathOrURL, "https://") || strstr(pathOrURL, "file://"))
55        return WKURLCreateWithUTF8CString(pathOrURL);
56
57    // Creating from filesytem path.
58    size_t length = strlen(pathOrURL);
59    if (!length)
60        return 0;
61
62    // FIXME: Remove the "localhost/" suffix once <http://webkit.org/b/55683> is fixed.
63    const char* filePrefix = "file://localhost/";
64    static const size_t prefixLength = strlen(filePrefix);
65#if OS(WINDOWS)
66    const char separator = '\\';
67    bool isAbsolutePath = length >= 3 && pathOrURL[1] == ':' && pathOrURL[2] == separator;
68#else
69    const char separator = '/';
70    bool isAbsolutePath = pathOrURL[0] == separator;
71#endif
72
73    OwnArrayPtr<char> buffer;
74    if (isAbsolutePath) {
75        buffer = adoptArrayPtr(new char[prefixLength + length + 1]);
76        strcpy(buffer.get(), filePrefix);
77        strcpy(buffer.get() + prefixLength, pathOrURL);
78    } else {
79        buffer = adoptArrayPtr(new char[prefixLength + PATH_MAX + length + 2]); // 1 for the separator
80        strcpy(buffer.get(), filePrefix);
81        if (!getcwd(buffer.get() + prefixLength, PATH_MAX))
82            return 0;
83        size_t numCharacters = strlen(buffer.get());
84        buffer[numCharacters] = separator;
85        strcpy(buffer.get() + numCharacters + 1, pathOrURL);
86    }
87
88    return WKURLCreateWithUTF8CString(buffer.get());
89}
90
91TestInvocation::TestInvocation(const std::string& pathOrURL)
92    : m_url(AdoptWK, createWKURL(pathOrURL.c_str()))
93    , m_pathOrURL(pathOrURL)
94    , m_dumpPixels(false)
95    , m_gotInitialResponse(false)
96    , m_gotFinalMessage(false)
97    , m_gotRepaint(false)
98    , m_error(false)
99{
100}
101
102TestInvocation::~TestInvocation()
103{
104}
105
106void TestInvocation::setIsPixelTest(const std::string& expectedPixelHash)
107{
108    m_dumpPixels = true;
109    m_expectedPixelHash = expectedPixelHash;
110}
111
112static const unsigned w3cSVGWidth = 480;
113static const unsigned w3cSVGHeight = 360;
114static const unsigned normalWidth = 800;
115static const unsigned normalHeight = 600;
116
117static void sizeWebViewForCurrentTest(const char* pathOrURL)
118{
119    bool isSVGW3CTest = strstr(pathOrURL, "svg/W3C-SVG-1.1") || strstr(pathOrURL, "svg\\W3C-SVG-1.1");
120
121    if (isSVGW3CTest)
122        TestController::shared().mainWebView()->resizeTo(w3cSVGWidth, w3cSVGHeight);
123    else
124        TestController::shared().mainWebView()->resizeTo(normalWidth, normalHeight);
125}
126
127static bool shouldOpenWebInspector(const char* pathOrURL)
128{
129    return strstr(pathOrURL, "inspector/") || strstr(pathOrURL, "inspector\\");
130}
131
132void TestInvocation::invoke()
133{
134    sizeWebViewForCurrentTest(m_pathOrURL.c_str());
135
136    WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("BeginTest"));
137    WKRetainPtr<WKBooleanRef> dumpPixels = adoptWK(WKBooleanCreate(m_dumpPixels));
138    WKContextPostMessageToInjectedBundle(TestController::shared().context(), messageName.get(), dumpPixels.get());
139
140    TestController::shared().runUntil(m_gotInitialResponse, TestController::ShortTimeout);
141    if (!m_gotInitialResponse) {
142        dump("Timed out waiting for initial response from web process\n");
143        return;
144    }
145    if (m_error) {
146        dump("FAIL\n");
147        return;
148    }
149
150    if (shouldOpenWebInspector(m_pathOrURL.c_str()))
151        WKInspectorShow(WKPageGetInspector(TestController::shared().mainWebView()->page()));
152
153    WKPageLoadURL(TestController::shared().mainWebView()->page(), m_url.get());
154
155    TestController::shared().runUntil(m_gotFinalMessage, TestController::LongTimeout);
156    if (!m_gotFinalMessage)
157        dump("Timed out waiting for final message from web process\n");
158    else if (m_error)
159        dump("FAIL\n");
160
161    WKInspectorClose(WKPageGetInspector(TestController::shared().mainWebView()->page()));
162}
163
164void TestInvocation::dump(const char* stringToDump)
165{
166    printf("Content-Type: text/plain\n");
167    printf("%s", stringToDump);
168
169    fputs("#EOF\n", stdout);
170    fputs("#EOF\n", stderr);
171    fflush(stdout);
172    fflush(stderr);
173}
174
175void TestInvocation::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
176{
177    if (WKStringIsEqualToUTF8CString(messageName, "Error")) {
178        // Set all states to true to stop spinning the runloop.
179        m_gotInitialResponse = true;
180        m_gotFinalMessage = true;
181        m_error = true;
182        TestController::shared().notifyDone();
183        return;
184    }
185
186    if (WKStringIsEqualToUTF8CString(messageName, "Ack")) {
187        ASSERT(WKGetTypeID(messageBody) == WKStringGetTypeID());
188        WKStringRef messageBodyString = static_cast<WKStringRef>(messageBody);
189        if (WKStringIsEqualToUTF8CString(messageBodyString, "BeginTest")) {
190            m_gotInitialResponse = true;
191            TestController::shared().notifyDone();
192            return;
193        }
194
195        ASSERT_NOT_REACHED();
196    }
197
198    if (WKStringIsEqualToUTF8CString(messageName, "Done")) {
199        ASSERT(WKGetTypeID(messageBody) == WKDictionaryGetTypeID());
200        WKDictionaryRef messageBodyDictionary = static_cast<WKDictionaryRef>(messageBody);
201
202        WKRetainPtr<WKStringRef> textOutputKey(AdoptWK, WKStringCreateWithUTF8CString("TextOutput"));
203        WKStringRef textOutput = static_cast<WKStringRef>(WKDictionaryGetItemForKey(messageBodyDictionary, textOutputKey.get()));
204
205        WKRetainPtr<WKStringRef> pixelResultKey = adoptWK(WKStringCreateWithUTF8CString("PixelResult"));
206        WKImageRef pixelResult = static_cast<WKImageRef>(WKDictionaryGetItemForKey(messageBodyDictionary, pixelResultKey.get()));
207        ASSERT(!pixelResult || m_dumpPixels);
208
209        // Dump text.
210        dump(toSTD(textOutput).c_str());
211
212        // Dump pixels (if necessary).
213        if (m_dumpPixels && pixelResult)
214            dumpPixelsAndCompareWithExpected(pixelResult);
215
216        fputs("#EOF\n", stdout);
217        fflush(stdout);
218        fflush(stderr);
219
220        m_gotFinalMessage = true;
221        TestController::shared().notifyDone();
222        return;
223    }
224
225    ASSERT_NOT_REACHED();
226}
227
228WKRetainPtr<WKTypeRef> TestInvocation::didReceiveSynchronousMessageFromInjectedBundle(WKStringRef /*messageName*/, WKTypeRef /*messageBody*/)
229{
230    ASSERT_NOT_REACHED();
231    return 0;
232}
233
234} // namespace WTR
235