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.
5 #ifndef CHROME_FRAME_TEST_TEST_WITH_WEB_SERVER_H_
6 #define CHROME_FRAME_TEST_TEST_WITH_WEB_SERVER_H_
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/string_util.h"
13 #include "base/stringprintf.h"
14 #include "base/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"
22 // Specifies the invocation method for CF.
31 CFInvocation(): method_(NONE
) {}
32 explicit CFInvocation(Type method
): method_(method
) {}
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
); }
39 // Returns whether this page does invoke CF.
40 bool invokes_cf() const {
41 return method_
!= NONE
;
44 Type
type() const { return method_
; }
50 // An interface for listeners of interesting events on a MockWebServer.
51 class WebServerListener
{
53 virtual ~WebServerListener() {}
55 // Invoked when a MockWebServer receives an expected response; see
56 // MockWebServer::ExpectAndHandlePostedResult.
57 virtual void OnExpectedResponse() = 0;
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", EmptyString()));
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));
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
{
81 MockWebServer(int port
, const std::wstring
& address
, base::FilePath root_dir
)
82 : test_server::HTTPTestServer(port
, address
, root_dir
), listener_(NULL
) {}
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
));
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
);
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
);
105 // Same as above except do not include the no-cache header.
106 void ExpectAndServeRequestAllowCache(CFInvocation invocation
,
107 const std::wstring
& url
);
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
);
114 void set_listener(WebServerListener
* listener
) { listener_
= listener
; }
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
120 void ExpectAndHandlePostedResult(CFInvocation invocation
,
121 const std::wstring
& post_suffix
);
123 // Expect and serve all incoming GET requests.
124 void ExpectAndServeAnyRequests(CFInvocation invocation
) {
125 ExpectAndServeRequestAnyNumberTimes(invocation
, L
"");
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
);
141 void ClearResults() {
142 posted_result_
.clear();
143 expected_result_
.clear();
146 void set_expected_result(const std::string
& expected_result
) {
147 expected_result_
= expected_result
;
150 const std::string
& posted_result() const {
151 return posted_result_
;
155 WebServerListener
* listener_
;
156 // Holds the results of tests which post success/failure.
157 std::string posted_result_
;
158 std::string expected_result_
;
161 class MockWebServerListener
: public WebServerListener
{
163 MOCK_METHOD0(OnExpectedResponse
, void());
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
{
175 ChromeFrameTestWithWebServer();
178 enum BrowserKind
{ INVALID
, IE
, CHROME
};
180 bool LaunchBrowser(BrowserKind browser
, const wchar_t* url
);
182 // Returns true if the test completed in time, or false if it timed out.
183 bool WaitForTestToComplete(base::TimeDelta duration
);
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
);
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
);
198 // Sets up expectations for a page to post back a result.
199 void ExpectAndHandlePostedResult();
201 // Test if chrome frame correctly reports its version.
202 void VersionTest(BrowserKind browser
, const wchar_t* page
);
206 // Ensures (well, at least tries to ensure) that the browser window has focus.
207 bool BringBrowserToTop();
209 const base::FilePath
& GetCFTestFilePath() {
210 return test_file_path_
;
213 static chrome_frame_test::TimedMsgLoop
& loop() {
217 static testing::StrictMock
<MockWebServerListener
>& listener_mock() {
218 return *listener_mock_
;
221 static testing::StrictMock
<MockWebServer
>& server_mock() {
222 return *server_mock_
;
225 static void SetUpTestCase();
226 static void TearDownTestCase();
228 static const base::FilePath
& GetChromeUserDataDirectory();
230 virtual void SetUp() OVERRIDE
;
231 virtual void TearDown() OVERRIDE
;
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_
;
240 // The user data directory used for Chrome instances.
241 static base::ScopedTempDir temp_dir_
;
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_
;
249 BrowserKind browser_
;
250 base::win::ScopedHandle browser_handle_
;
253 // A helper class for doing some bookkeeping when using the
254 // SimpleWebServer class.
255 class SimpleWebServerTest
{
257 SimpleWebServerTest(const std::string
& address
, int port
)
258 : server_(address
, port
), port_(port
) {
261 ~SimpleWebServerTest() {
262 server_
.DeleteAllResponses();
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
])));
275 std::wstring
FormatHttpPath(const wchar_t* document_path
) {
276 return base::StringPrintf(L
"http://%ls:%i/%ls",
277 ASCIIToWide(server_
.host()).c_str(), port_
,
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();
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())) {
298 *request
= &connection
->request();
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
;
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 ASCIIToWide(r
.path().substr(1)).compare(page
) == 0) {
318 if (expected_method
) {
319 EXPECT_EQ(expected_method
, r
.method());
327 test_server::SimpleWebServer
* web_server() {
332 test_server::SimpleWebServer server_
;
336 ACTION_P2(SendFast
, headers
, content
) {
337 arg0
->Send(headers
, content
);
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
),
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
),
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);
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);
368 ACTION_P2(HandlePostedResponseHelper
, server
, invocation
) {
369 server
->HandlePostedResponse(arg0
, arg2
);
372 #endif // CHROME_FRAME_TEST_TEST_WITH_WEB_SERVER_H_