| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #ifndef CHROME_FRAME_TEST_TEST_WITH_WEB_SERVER_H_ | |
| 6 #define CHROME_FRAME_TEST_TEST_WITH_WEB_SERVER_H_ | |
| 7 | |
| 8 #include <windows.h> | |
| 9 #include <string> | |
| 10 | |
| 11 #include "base/files/scoped_temp_dir.h" | |
| 12 #include "base/strings/string_util.h" | |
| 13 #include "base/strings/stringprintf.h" | |
| 14 #include "base/strings/utf_string_conversions.h" | |
| 15 #include "base/win/scoped_handle.h" | |
| 16 #include "chrome_frame/chrome_tab.h" | |
| 17 #include "chrome_frame/test/chrome_frame_test_utils.h" | |
| 18 #include "chrome_frame/test/test_server.h" | |
| 19 #include "testing/gmock/include/gmock/gmock.h" | |
| 20 #include "testing/gtest/include/gtest/gtest.h" | |
| 21 | |
| 22 // Specifies the invocation method for CF. | |
| 23 class CFInvocation { | |
| 24 public: | |
| 25 enum Type { | |
| 26 NONE = 0, | |
| 27 META_TAG, | |
| 28 HTTP_HEADER | |
| 29 }; | |
| 30 | |
| 31 CFInvocation(): method_(NONE) {} | |
| 32 explicit CFInvocation(Type method): method_(method) {} | |
| 33 | |
| 34 // Convience methods for creating this class. | |
| 35 static CFInvocation None() { return CFInvocation(NONE); } | |
| 36 static CFInvocation MetaTag() { return CFInvocation(META_TAG); } | |
| 37 static CFInvocation HttpHeader() { return CFInvocation(HTTP_HEADER); } | |
| 38 | |
| 39 // Returns whether this page does invoke CF. | |
| 40 bool invokes_cf() const { | |
| 41 return method_ != NONE; | |
| 42 } | |
| 43 | |
| 44 Type type() const { return method_; } | |
| 45 | |
| 46 private: | |
| 47 Type method_; | |
| 48 }; | |
| 49 | |
| 50 // An interface for listeners of interesting events on a MockWebServer. | |
| 51 class WebServerListener { | |
| 52 public: | |
| 53 virtual ~WebServerListener() {} | |
| 54 | |
| 55 // Invoked when a MockWebServer receives an expected response; see | |
| 56 // MockWebServer::ExpectAndHandlePostedResult. | |
| 57 virtual void OnExpectedResponse() = 0; | |
| 58 }; | |
| 59 | |
| 60 // Simple Gmock friendly web server. Sample usage: | |
| 61 // MockWebServer mock(9999, "0.0.0.0"); | |
| 62 // EXPECT_CALL(mock, Get(_, StrEq("/favicon.ico"), _)).WillRepeatedly(SendFast( | |
| 63 // "HTTP/1.1 404 Not Found" | |
| 64 // "text/html; charset=UTF-8", std::string())); | |
| 65 // | |
| 66 // EXPECT_CALL(mock, Get(_, StrEq("/book"), _)).WillRepeatedly(Send( | |
| 67 // "HTTP/1.1 302 Found\r\n" | |
| 68 // "Connection: close\r\n" | |
| 69 // "Content-Type: text/html\r\n" | |
| 70 // "Location: library\r\n", | |
| 71 // "<html>Lalalala</html>", 3, 1000)); | |
| 72 // | |
| 73 // EXPECT_CALL(mock, Get(_, StrEq("/library"), _)).WillRepeatedly(Send( | |
| 74 // "HTTP/1.1 200 OK\r\n" | |
| 75 // "Connection: close\r\n" | |
| 76 // "Content-Type: text/html\r\n", | |
| 77 // "<html><meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\" />" | |
| 78 // "<body>Rendered in CF.</body></html>", 4, 1000)); | |
| 79 class MockWebServer : public test_server::HTTPTestServer { | |
| 80 public: | |
| 81 MockWebServer(int port, const std::wstring& address, base::FilePath root_dir) | |
| 82 : test_server::HTTPTestServer(port, address, root_dir), listener_(NULL) {} | |
| 83 | |
| 84 // Overriden from test_server::HTTPTestServer. | |
| 85 MOCK_METHOD3(Get, void(test_server::ConfigurableConnection* connection, | |
| 86 const std::wstring& path, | |
| 87 const test_server::Request& r)); | |
| 88 MOCK_METHOD3(Post, void(test_server::ConfigurableConnection* connection, | |
| 89 const std::wstring& path, | |
| 90 const test_server::Request& r)); | |
| 91 | |
| 92 // Expect a GET request for |url|. Respond with the file appropriate for | |
| 93 // the given |url|. Modify the file to follow the given CFInvocation method. | |
| 94 // The response includes a no-cache header. |allow_meta_tag_double_req| | |
| 95 // specifies whether to allow the request to happen twice if the invocation | |
| 96 // is using the CF meta tag. | |
| 97 void ExpectAndServeRequest(CFInvocation invocation, const std::wstring& url); | |
| 98 | |
| 99 // Expect a number of GET requests for |url|. Rest is similar to the function | |
| 100 // ExpectAndServeRequest. | |
| 101 void ExpectAndServeRequestWithCardinality(CFInvocation invocation, | |
| 102 const std::wstring& url, | |
| 103 testing::Cardinality cardinality); | |
| 104 | |
| 105 // Same as above except do not include the no-cache header. | |
| 106 void ExpectAndServeRequestAllowCache(CFInvocation invocation, | |
| 107 const std::wstring& url); | |
| 108 | |
| 109 // Expect any number of GETs for the given resource path (e.g, /favicon.ico) | |
| 110 // and respond with the file, if it exists, or a 404 if it does not. | |
| 111 void ExpectAndServeRequestAnyNumberTimes(CFInvocation invocation, | |
| 112 const std::wstring& path_prefix); | |
| 113 | |
| 114 void set_listener(WebServerListener* listener) { listener_ = listener; } | |
| 115 | |
| 116 // Expect a POST to an URL containing |post_suffix|, saving the response | |
| 117 // contents for retrieval by posted_result(). Invokes the listener's | |
| 118 // OnExpectedResponse method if the posted response matches the expected | |
| 119 // result. | |
| 120 void ExpectAndHandlePostedResult(CFInvocation invocation, | |
| 121 const std::wstring& post_suffix); | |
| 122 | |
| 123 // Expect and serve all incoming GET requests. | |
| 124 void ExpectAndServeAnyRequests(CFInvocation invocation) { | |
| 125 ExpectAndServeRequestAnyNumberTimes(invocation, L""); | |
| 126 } | |
| 127 | |
| 128 | |
| 129 // Send a response on the given connection appropriate for |resource_uri|. | |
| 130 // If the file referred to by |path| exists, send the file data, otherwise | |
| 131 // send 404. Modify the file data according to the given invocation method. | |
| 132 void SendResponseHelper(test_server::ConfigurableConnection* connection, | |
| 133 const std::wstring& resource_uri, | |
| 134 const test_server::Request& request, | |
| 135 CFInvocation invocation, | |
| 136 bool add_no_cache_header); | |
| 137 // Handles the posted /writefile response | |
| 138 void HandlePostedResponse(test_server::ConfigurableConnection* connection, | |
| 139 const test_server::Request& request); | |
| 140 | |
| 141 void ClearResults() { | |
| 142 posted_result_.clear(); | |
| 143 expected_result_.clear(); | |
| 144 } | |
| 145 | |
| 146 void set_expected_result(const std::string& expected_result) { | |
| 147 expected_result_ = expected_result; | |
| 148 } | |
| 149 | |
| 150 const std::string& posted_result() const { | |
| 151 return posted_result_; | |
| 152 } | |
| 153 | |
| 154 private: | |
| 155 WebServerListener* listener_; | |
| 156 // Holds the results of tests which post success/failure. | |
| 157 std::string posted_result_; | |
| 158 std::string expected_result_; | |
| 159 }; | |
| 160 | |
| 161 class MockWebServerListener : public WebServerListener { | |
| 162 public: | |
| 163 MOCK_METHOD0(OnExpectedResponse, void()); | |
| 164 }; | |
| 165 | |
| 166 // Class that: | |
| 167 // 1) Starts the local webserver, | |
| 168 // 2) Supports launching browsers - Internet Explorer with local url | |
| 169 // 3) Wait the webserver to finish. It is supposed the test webpage to shutdown | |
| 170 // the server by navigating to "kill" page | |
| 171 // 4) Supports read the posted results from the test webpage to the "dump" | |
| 172 // webserver directory | |
| 173 class ChromeFrameTestWithWebServer : public testing::Test { | |
| 174 public: | |
| 175 ChromeFrameTestWithWebServer(); | |
| 176 | |
| 177 protected: | |
| 178 enum BrowserKind { INVALID, IE, CHROME }; | |
| 179 | |
| 180 bool LaunchBrowser(BrowserKind browser, const wchar_t* url); | |
| 181 | |
| 182 // Returns true if the test completed in time, or false if it timed out. | |
| 183 bool WaitForTestToComplete(base::TimeDelta duration); | |
| 184 | |
| 185 // Waits for the page to notify us of the window.onload event firing. | |
| 186 // Note that the milliseconds value is only approximate. | |
| 187 bool WaitForOnLoad(int milliseconds); | |
| 188 | |
| 189 // Launches the specified browser and waits for the test to complete (see | |
| 190 // WaitForTestToComplete). Then checks that the outcome is equal to the | |
| 191 // expected result. The test is repeated once if it fails due to a timeout. | |
| 192 // This function uses EXPECT_TRUE and ASSERT_TRUE for all steps performed | |
| 193 // hence no return value. | |
| 194 void SimpleBrowserTestExpectedResult(BrowserKind browser, | |
| 195 const wchar_t* page, const char* result); | |
| 196 void SimpleBrowserTest(BrowserKind browser, const wchar_t* page); | |
| 197 | |
| 198 // Sets up expectations for a page to post back a result. | |
| 199 void ExpectAndHandlePostedResult(); | |
| 200 | |
| 201 // Test if chrome frame correctly reports its version. | |
| 202 void VersionTest(BrowserKind browser, const wchar_t* page); | |
| 203 | |
| 204 void CloseBrowser(); | |
| 205 | |
| 206 // Ensures (well, at least tries to ensure) that the browser window has focus. | |
| 207 bool BringBrowserToTop(); | |
| 208 | |
| 209 const base::FilePath& GetCFTestFilePath() { | |
| 210 return test_file_path_; | |
| 211 } | |
| 212 | |
| 213 static chrome_frame_test::TimedMsgLoop& loop() { | |
| 214 return *loop_; | |
| 215 } | |
| 216 | |
| 217 static testing::StrictMock<MockWebServerListener>& listener_mock() { | |
| 218 return *listener_mock_; | |
| 219 } | |
| 220 | |
| 221 static testing::StrictMock<MockWebServer>& server_mock() { | |
| 222 return *server_mock_; | |
| 223 } | |
| 224 | |
| 225 static void SetUpTestCase(); | |
| 226 static void TearDownTestCase(); | |
| 227 | |
| 228 static const base::FilePath& GetChromeUserDataDirectory(); | |
| 229 | |
| 230 virtual void SetUp() OVERRIDE; | |
| 231 virtual void TearDown() OVERRIDE; | |
| 232 | |
| 233 // The on-disk path to our html test files. | |
| 234 static base::FilePath test_file_path_; | |
| 235 static base::FilePath results_dir_; | |
| 236 static base::FilePath CFInstall_path_; | |
| 237 static base::FilePath CFInstance_path_; | |
| 238 static base::FilePath chrome_user_data_dir_; | |
| 239 | |
| 240 // The user data directory used for Chrome instances. | |
| 241 static base::ScopedTempDir temp_dir_; | |
| 242 | |
| 243 // The web server from which we serve the web! | |
| 244 static chrome_frame_test::TimedMsgLoop* loop_; | |
| 245 static std::string local_address_; | |
| 246 static testing::StrictMock<MockWebServerListener>* listener_mock_; | |
| 247 static testing::StrictMock<MockWebServer>* server_mock_; | |
| 248 | |
| 249 BrowserKind browser_; | |
| 250 base::win::ScopedHandle browser_handle_; | |
| 251 }; | |
| 252 | |
| 253 // A helper class for doing some bookkeeping when using the | |
| 254 // SimpleWebServer class. | |
| 255 class SimpleWebServerTest { | |
| 256 public: | |
| 257 SimpleWebServerTest(const std::string& address, int port) | |
| 258 : server_(address, port), port_(port) { | |
| 259 } | |
| 260 | |
| 261 ~SimpleWebServerTest() { | |
| 262 server_.DeleteAllResponses(); | |
| 263 } | |
| 264 | |
| 265 template <class ResponseClass> | |
| 266 void PopulateStaticFileListT(const wchar_t* pages[], int count, | |
| 267 const base::FilePath& directory) { | |
| 268 for (int i = 0; i < count; ++i) { | |
| 269 server_.AddResponse(new ResponseClass( | |
| 270 base::StringPrintf("/%ls", pages[i]).c_str(), | |
| 271 directory.Append(pages[i]))); | |
| 272 } | |
| 273 } | |
| 274 | |
| 275 std::wstring FormatHttpPath(const wchar_t* document_path) { | |
| 276 return base::StringPrintf(L"http://%ls:%i/%ls", | |
| 277 base::ASCIIToWide(server_.host()).c_str(), port_, | |
| 278 document_path); | |
| 279 } | |
| 280 | |
| 281 // Returns the last client request object. | |
| 282 // Under normal circumstances this will be the request for /quit. | |
| 283 const test_server::Request& last_request() const { | |
| 284 const test_server::ConnectionList& connections = server_.connections(); | |
| 285 DCHECK(connections.size()); | |
| 286 const test_server::Connection* c = connections.back(); | |
| 287 return c->request(); | |
| 288 } | |
| 289 | |
| 290 bool FindRequest(const std::string& path, | |
| 291 const test_server::Request** request) { | |
| 292 test_server::ConnectionList::const_iterator index; | |
| 293 for (index = server_.connections().begin(); | |
| 294 index != server_.connections().end(); index++) { | |
| 295 const test_server::Connection* connection = *index; | |
| 296 if (!lstrcmpiA(connection->request().path().c_str(), path.c_str())) { | |
| 297 if (request) | |
| 298 *request = &connection->request(); | |
| 299 return true; | |
| 300 } | |
| 301 } | |
| 302 return false; | |
| 303 } | |
| 304 | |
| 305 // Counts the number of times a page was requested. | |
| 306 // Optionally checks if the request method for each is equal to | |
| 307 // |expected_method|. If expected_method is NULL no such check is made. | |
| 308 int GetRequestCountForPage(const wchar_t* page, const char* expected_method) { | |
| 309 // Check how many requests we got for the cf page. | |
| 310 test_server::ConnectionList::const_iterator it; | |
| 311 int requests = 0; | |
| 312 const test_server::ConnectionList& connections = server_.connections(); | |
| 313 for (it = connections.begin(); it != connections.end(); ++it) { | |
| 314 const test_server::Connection* c = (*it); | |
| 315 const test_server::Request& r = c->request(); | |
| 316 if (!r.path().empty() && | |
| 317 base::ASCIIToWide(r.path().substr(1)).compare(page) == 0) { | |
| 318 if (expected_method) { | |
| 319 EXPECT_EQ(expected_method, r.method()); | |
| 320 } | |
| 321 requests++; | |
| 322 } | |
| 323 } | |
| 324 return requests; | |
| 325 } | |
| 326 | |
| 327 test_server::SimpleWebServer* web_server() { | |
| 328 return &server_; | |
| 329 } | |
| 330 | |
| 331 protected: | |
| 332 test_server::SimpleWebServer server_; | |
| 333 int port_; | |
| 334 }; | |
| 335 | |
| 336 ACTION_P2(SendFast, headers, content) { | |
| 337 arg0->Send(headers, content); | |
| 338 } | |
| 339 | |
| 340 ACTION_P4(Send, headers, content, chunk, timeout) { | |
| 341 test_server::ConfigurableConnection::SendOptions options( | |
| 342 test_server::ConfigurableConnection::SendOptions:: | |
| 343 IMMEDIATE_HEADERS_DELAYED_CONTENT, chunk, timeout); | |
| 344 arg0->SendWithOptions(std::string(headers), | |
| 345 std::string(content), | |
| 346 options); | |
| 347 } | |
| 348 | |
| 349 ACTION_P4(SendSlow, headers, content, chunk, timeout) { | |
| 350 test_server::ConfigurableConnection::SendOptions options( | |
| 351 test_server::ConfigurableConnection::SendOptions::DELAYED, chunk, timeout); | |
| 352 arg0->SendWithOptions(std::string(headers), | |
| 353 std::string(content), | |
| 354 options); | |
| 355 } | |
| 356 | |
| 357 // Sends a response with the file data for the given path, if the file exists, | |
| 358 // or a 404 if the file does not. This response includes a no-cache header. | |
| 359 ACTION_P2(SendResponse, server, invocation) { | |
| 360 server->SendResponseHelper(arg0, arg1, arg2, invocation, true); | |
| 361 } | |
| 362 | |
| 363 // Same as above except that the response does not include the no-cache header. | |
| 364 ACTION_P2(SendAllowCacheResponse, server, invocation) { | |
| 365 server->SendResponseHelper(arg0, arg1, invocation, false); | |
| 366 } | |
| 367 | |
| 368 ACTION_P2(HandlePostedResponseHelper, server, invocation) { | |
| 369 server->HandlePostedResponse(arg0, arg2); | |
| 370 } | |
| 371 | |
| 372 #endif // CHROME_FRAME_TEST_TEST_WITH_WEB_SERVER_H_ | |
| OLD | NEW |