| // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CHROME_FRAME_TEST_TEST_WITH_WEB_SERVER_H_ |
| #define CHROME_FRAME_TEST_TEST_WITH_WEB_SERVER_H_ |
| |
| #include <windows.h> |
| #include <string> |
| |
| #include "base/string_util.h" |
| #include "base/stringprintf.h" |
| #include "base/utf_string_conversions.h" |
| #include "chrome_frame/test/chrome_frame_test_utils.h" |
| #include "chrome_frame/test/http_server.h" |
| #include "chrome_frame/test/test_server.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| |
| // Include without path to make GYP build see it. |
| #include "chrome_tab.h" // NOLINT |
| |
| // Specifies the invocation method for CF. |
| class CFInvocation { |
| public: |
| enum Type { |
| NONE = 0, |
| META_TAG, |
| HTTP_HEADER |
| }; |
| |
| CFInvocation(): method_(NONE) {} |
| explicit CFInvocation(Type method): method_(method) {} |
| |
| // Convience methods for creating this class. |
| static CFInvocation None() { return CFInvocation(NONE); } |
| static CFInvocation MetaTag() { return CFInvocation(META_TAG); } |
| static CFInvocation HttpHeader() { return CFInvocation(HTTP_HEADER); } |
| |
| // Returns whether this page does invoke CF. |
| bool invokes_cf() const { |
| return method_ != NONE; |
| } |
| |
| Type type() const { return method_; } |
| |
| private: |
| Type method_; |
| }; |
| |
| // Simple Gmock friendly web server. Sample usage: |
| // MockWebServer mock(9999, "0.0.0.0"); |
| // EXPECT_CALL(mock, Get(_, StrEq("/favicon.ico"), _)).WillRepeatedly(SendFast( |
| // "HTTP/1.1 404 Not Found" |
| // "text/html; charset=UTF-8", EmptyString())); |
| // |
| // EXPECT_CALL(mock, Get(_, StrEq("/book"), _)).WillRepeatedly(Send( |
| // "HTTP/1.1 302 Found\r\n" |
| // "Connection: close\r\n" |
| // "Content-Type: text/html\r\n" |
| // "Location: library\r\n", |
| // "<html>Lalalala</html>", 3, 1000)); |
| // |
| // EXPECT_CALL(mock, Get(_, StrEq("/library"), _)).WillRepeatedly(Send( |
| // "HTTP/1.1 200 OK\r\n" |
| // "Connection: close\r\n" |
| // "Content-Type: text/html\r\n", |
| // "<html><meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\" />" |
| // "<body>Rendered in CF.</body></html>", 4, 1000)); |
| class MockWebServer : public test_server::HTTPTestServer { |
| public: |
| MockWebServer(int port, const std::wstring& address, FilePath root_dir) |
| : test_server::HTTPTestServer(port, address, root_dir) {} |
| |
| // Overriden from test_server::HTTPTestServer. |
| MOCK_METHOD3(Get, void(test_server::ConfigurableConnection* connection, |
| const std::wstring& path, |
| const test_server::Request&r)); |
| MOCK_METHOD3(Post, void(test_server::ConfigurableConnection* connection, |
| const std::wstring& path, |
| const test_server::Request&r)); |
| |
| // Expect a GET request for |url|. Respond with the file appropriate for |
| // the given |url|. Modify the file to follow the given CFInvocation method. |
| // The response includes a no-cache header. |allow_meta_tag_double_req| |
| // specifies whether to allow the request to happen twice if the invocation |
| // is using the CF meta tag. |
| void ExpectAndServeRequest(CFInvocation invocation, const std::wstring& url); |
| |
| // Same as above except do not include the no-cache header. |
| void ExpectAndServeRequestAllowCache(CFInvocation invocation, |
| const std::wstring& url); |
| |
| // Expect any number of GETs for the given resource path (e.g, /favicon.ico) |
| // and respond with the file, if it exists, or a 404 if it does not. |
| void ExpectAndServeRequestAnyNumberTimes(CFInvocation invocation, |
| const std::wstring& path_prefix); |
| |
| void ExpectAndHandlePostedResult(CFInvocation invocation, |
| const std::wstring& post_suffix); |
| |
| // Expect and serve all incoming GET requests. |
| void ExpectAndServeAnyRequests(CFInvocation invocation) { |
| ExpectAndServeRequestAnyNumberTimes(invocation, L""); |
| } |
| |
| |
| // Send a response on the given connection appropriate for |resource_uri|. |
| // If the file referred to by |path| exists, send the file data, otherwise |
| // send 404. Modify the file data according to the given invocation method. |
| void SendResponseHelper(test_server::ConfigurableConnection* connection, |
| const std::wstring& resource_uri, |
| const test_server::Request& request, |
| CFInvocation invocation, |
| bool add_no_cache_header); |
| // Handles the posted /writefile response |
| void HandlePostedResponse(test_server::ConfigurableConnection* connection, |
| const test_server::Request& request); |
| |
| void set_expected_result(const std::string& expected_result) { |
| expected_result_ = expected_result; |
| } |
| |
| const std::string& posted_result() const { |
| return posted_result_; |
| } |
| |
| private: |
| // Holds the results of tests which post success/failure. |
| std::string posted_result_; |
| std::string expected_result_; |
| }; |
| |
| // Class that: |
| // 1) Starts the local webserver, |
| // 2) Supports launching browsers - Internet Explorer and Firefox with local url |
| // 3) Wait the webserver to finish. It is supposed the test webpage to shutdown |
| // the server by navigating to "kill" page |
| // 4) Supports read the posted results from the test webpage to the "dump" |
| // webserver directory |
| class ChromeFrameTestWithWebServer: public testing::Test { |
| public: |
| ChromeFrameTestWithWebServer(); |
| |
| protected: |
| enum BrowserKind { INVALID, IE, FIREFOX, OPERA, SAFARI, CHROME }; |
| |
| bool LaunchBrowser(BrowserKind browser, const wchar_t* url); |
| bool WaitForTestToComplete(int milliseconds); |
| // Waits for the page to notify us of the window.onload event firing. |
| // Note that the milliseconds value is only approximate. |
| bool WaitForOnLoad(int milliseconds); |
| |
| // Launches the specified browser and waits for the test to complete |
| // (see WaitForTestToComplete). Then checks that the outcome is equal |
| // to the expected result. |
| // This function uses EXPECT_TRUE and ASSERT_TRUE for all steps performed |
| // hence no return value. |
| void SimpleBrowserTestExpectedResult(BrowserKind browser, |
| const wchar_t* page, const char* result); |
| void SimpleBrowserTest(BrowserKind browser, const wchar_t* page); |
| |
| // Same as SimpleBrowserTest but if the browser isn't installed (LaunchBrowser |
| // fails), the function will print out a warning but not treat the test |
| // as failed. |
| // Currently this is how we run Opera tests. |
| void OptionalBrowserTest(BrowserKind browser, const wchar_t* page); |
| |
| // Test if chrome frame correctly reports its version. |
| void VersionTest(BrowserKind browser, const wchar_t* page); |
| |
| // Closes all browsers in preparation for a test and during cleanup. |
| void CloseAllBrowsers(); |
| |
| void CloseBrowser(); |
| |
| // Ensures (well, at least tries to ensure) that the browser window has focus. |
| bool BringBrowserToTop(); |
| |
| const FilePath& GetCFTestFilePath() { |
| return test_file_path_; |
| } |
| |
| virtual void SetUp(); |
| virtual void TearDown(); |
| |
| // Important: kind means "sheep" in Icelandic. ?:-o |
| const char* ToString(BrowserKind kind) { |
| switch (kind) { |
| case IE: |
| return "IE"; |
| case FIREFOX: |
| return "Firefox"; |
| case OPERA: |
| return "Opera"; |
| case CHROME: |
| return "Chrome"; |
| case SAFARI: |
| return "Safari"; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| return ""; |
| } |
| |
| BrowserKind browser_; |
| FilePath results_dir_; |
| ScopedHandle browser_handle_; |
| // The on-disk path to our html test files. |
| FilePath test_file_path_; |
| |
| FilePath CFInstall_path_; |
| FilePath CFInstance_path_; |
| |
| chrome_frame_test::TimedMsgLoop loop_; |
| testing::StrictMock<MockWebServer> server_mock_; |
| }; |
| |
| // A helper class for doing some bookkeeping when using the |
| // SimpleWebServer class. |
| class SimpleWebServerTest { |
| public: |
| explicit SimpleWebServerTest(int port) : server_(port), port_(port) { |
| } |
| |
| ~SimpleWebServerTest() { |
| server_.DeleteAllResponses(); |
| } |
| |
| template <class ResponseClass> |
| void PopulateStaticFileListT(const wchar_t* pages[], int count, |
| const FilePath& directory) { |
| for (int i = 0; i < count; ++i) { |
| server_.AddResponse(new ResponseClass( |
| base::StringPrintf("/%ls", pages[i]).c_str(), |
| directory.Append(pages[i]))); |
| } |
| } |
| |
| std::wstring FormatHttpPath(const wchar_t* document_path) { |
| return base::StringPrintf(L"http://localhost:%i/%ls", port_, |
| document_path); |
| } |
| |
| // Returns the last client request object. |
| // Under normal circumstances this will be the request for /quit. |
| const test_server::Request& last_request() const { |
| const test_server::ConnectionList& connections = server_.connections(); |
| DCHECK(connections.size()); |
| const test_server::Connection* c = connections.back(); |
| return c->request(); |
| } |
| |
| bool FindRequest(const std::string& path, |
| const test_server::Request** request) { |
| test_server::ConnectionList::const_iterator index; |
| for (index = server_.connections().begin(); |
| index != server_.connections().end(); index++) { |
| const test_server::Connection* connection = *index; |
| if (!lstrcmpiA(connection->request().path().c_str(), path.c_str())) { |
| if (request) |
| *request = &connection->request(); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| // Counts the number of times a page was requested. |
| // Optionally checks if the request method for each is equal to |
| // |expected_method|. If expected_method is NULL no such check is made. |
| int GetRequestCountForPage(const wchar_t* page, const char* expected_method) { |
| // Check how many requests we got for the cf page. |
| test_server::ConnectionList::const_iterator it; |
| int requests = 0; |
| const test_server::ConnectionList& connections = server_.connections(); |
| for (it = connections.begin(); it != connections.end(); ++it) { |
| const test_server::Connection* c = (*it); |
| const test_server::Request& r = c->request(); |
| if (!r.path().empty() && |
| ASCIIToWide(r.path().substr(1)).compare(page) == 0) { |
| if (expected_method) { |
| EXPECT_EQ(expected_method, r.method()); |
| } |
| requests++; |
| } |
| } |
| return requests; |
| } |
| |
| test_server::SimpleWebServer* web_server() { |
| return &server_; |
| } |
| |
| protected: |
| test_server::SimpleWebServer server_; |
| int port_; |
| }; |
| |
| ACTION_P2(SendFast, headers, content) { |
| arg0->Send(headers, content); |
| } |
| |
| ACTION_P4(Send, headers, content, chunk, timeout) { |
| test_server::ConfigurableConnection::SendOptions options( |
| test_server::ConfigurableConnection::SendOptions:: |
| IMMEDIATE_HEADERS_DELAYED_CONTENT, chunk, timeout); |
| arg0->SendWithOptions(std::string(headers), |
| std::string(content), |
| options); |
| } |
| |
| ACTION_P4(SendSlow, headers, content, chunk, timeout) { |
| test_server::ConfigurableConnection::SendOptions options( |
| test_server::ConfigurableConnection::SendOptions::DELAYED, chunk, timeout); |
| arg0->SendWithOptions(std::string(headers), |
| std::string(content), |
| options); |
| } |
| |
| // Sends a response with the file data for the given path, if the file exists, |
| // or a 404 if the file does not. This response includes a no-cache header. |
| ACTION_P2(SendResponse, server, invocation) { |
| server->SendResponseHelper(arg0, arg1, arg2, invocation, true); |
| } |
| |
| // Same as above except that the response does not include the no-cache header. |
| ACTION_P2(SendAllowCacheResponse, server, invocation) { |
| server->SendResponseHelper(arg0, arg1, invocation, false); |
| } |
| |
| ACTION_P2(HandlePostedResponseHelper, server, invocation) { |
| server->HandlePostedResponse(arg0, arg2); |
| } |
| |
| #endif // CHROME_FRAME_TEST_TEST_WITH_WEB_SERVER_H_ |