Home | History | Annotate | Download | only in test
      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", EmptyString()));
     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                               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           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_
    373