Home | History | Annotate | Download | only in page_cycler
      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 #include "base/command_line.h"
      6 #include "base/file_util.h"
      7 #include "base/files/file_path.h"
      8 #include "base/path_service.h"
      9 #include "base/strings/string_split.h"
     10 #include "base/strings/string_util.h"
     11 #include "base/threading/sequenced_worker_pool.h"
     12 #include "chrome/browser/chrome_notification_types.h"
     13 #include "chrome/browser/browser_process.h"
     14 #include "chrome/browser/page_cycler/page_cycler.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "chrome/browser/profiles/profile_manager.h"
     17 #include "chrome/browser/ui/browser_list.h"
     18 #include "chrome/common/chrome_paths.h"
     19 #include "chrome/common/chrome_switches.h"
     20 #include "chrome/test/base/in_process_browser_test.h"
     21 #include "chrome/test/base/testing_profile.h"
     22 #include "chrome/test/base/ui_test_utils.h"
     23 #include "content/public/browser/notification_observer.h"
     24 #include "content/public/browser/notification_registrar.h"
     25 #include "content/public/browser/notification_service.h"
     26 #include "content/public/common/content_switches.h"
     27 #include "content/public/common/url_constants.h"
     28 #include "url/gurl.h"
     29 
     30 // TODO(kbr): remove: http://crbug.com/222296
     31 #if defined(OS_MACOSX)
     32 #import "base/mac/mac_util.h"
     33 #endif
     34 
     35 // Basic PageCyclerBrowserTest structure; used in testing most of PageCycler's
     36 // functionality.
     37 class PageCyclerBrowserTest : public content::NotificationObserver,
     38                               public InProcessBrowserTest {
     39  public:
     40   PageCyclerBrowserTest() : page_cycler_(NULL) {
     41   }
     42 
     43   virtual ~PageCyclerBrowserTest() {
     44   }
     45 
     46   // Initialize file paths within a temporary directory; this should be
     47   // empty and nonexistent.
     48   virtual void InitFilePaths(base::FilePath temp_path) {
     49     temp_path_ = temp_path;
     50     urls_file_ = temp_path.AppendASCII("urls_file");
     51     errors_file_ = temp_path.AppendASCII("errors");
     52     stats_file_ = temp_path.AppendASCII("stats");
     53 
     54     ASSERT_FALSE(base::PathExists(urls_file_));
     55     ASSERT_FALSE(base::PathExists(errors_file_));
     56     ASSERT_FALSE(base::PathExists(stats_file_));
     57   }
     58 
     59   // Initialize a PageCycler using either the base fields, or using provided
     60   // ones.
     61   void InitPageCycler() {
     62     page_cycler_ = new PageCycler(browser(), urls_file());
     63     page_cycler_->set_errors_file(errors_file());
     64     page_cycler_->set_stats_file(stats_file());
     65   }
     66 
     67   void InitPageCycler(base::FilePath urls_file,
     68                       base::FilePath errors_file,
     69                       base::FilePath stats_file) {
     70     page_cycler_ = new PageCycler(browser(), urls_file);
     71     page_cycler_->set_errors_file(errors_file);
     72     page_cycler_->set_stats_file(stats_file);
     73   }
     74 
     75   void RegisterForNotifications() {
     76     registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSED,
     77                    content::NotificationService::AllSources());
     78   }
     79 
     80   // Get a collection of basic urls which are stored in the test directory.
     81   // NOTE: |test_server| must be started first!
     82   std::vector<GURL> GetURLs() {
     83     std::vector<GURL> urls;
     84     urls.push_back(test_server()->GetURL("files/page_cycler/basic_html.html"));
     85     urls.push_back(test_server()->GetURL("files/page_cycler/basic_js.html"));
     86     urls.push_back(test_server()->GetURL("files/page_cycler/basic_css.html"));
     87     return urls;
     88   }
     89 
     90   // Read the errors file, and generate a vector of error strings.
     91   std::vector<std::string> GetErrorsFromFile() {
     92     std::string error_file_contents;
     93     CHECK(file_util::ReadFileToString(errors_file_,
     94                                       &error_file_contents));
     95     if (error_file_contents[error_file_contents.size() - 1] == '\n')
     96       error_file_contents.resize(error_file_contents.size() - 1);
     97 
     98     std::vector<std::string> errors;
     99     base::SplitString(error_file_contents, '\n', &errors);
    100 
    101     return errors;
    102   }
    103 
    104   // Convert a vector of GURLs into a newline-separated string, ready to be
    105   // written to the urls file for PageCycler to use.
    106   std::string GetStringFromURLs(std::vector<GURL> urls) {
    107     std::string urls_string;
    108     for (std::vector<GURL>::const_iterator iter = urls.begin();
    109          iter != urls.end(); ++iter)
    110       urls_string.append(iter->spec() + "\n");
    111     return urls_string;
    112   }
    113 
    114   // content::NotificationObserver.
    115   virtual void Observe(int type,
    116                        const content::NotificationSource& source,
    117                        const content::NotificationDetails& details) OVERRIDE {
    118     switch (type) {
    119       case chrome::NOTIFICATION_BROWSER_CLOSED:
    120         base::MessageLoop::current()->PostTask(
    121             FROM_HERE, base::MessageLoop::QuitClosure());
    122         break;
    123       default:
    124         NOTREACHED();
    125         break;
    126     }
    127   }
    128 
    129   base::FilePath urls_file() { return urls_file_; }
    130   base::FilePath errors_file() { return errors_file_; }
    131   base::FilePath stats_file() { return stats_file_; }
    132   PageCycler* page_cycler() { return page_cycler_; }
    133 
    134  protected:
    135   base::FilePath temp_path_;
    136   base::FilePath urls_file_;
    137   base::FilePath errors_file_;
    138   base::FilePath stats_file_;
    139   PageCycler* page_cycler_;
    140   content::NotificationRegistrar registrar_;
    141 };
    142 
    143 // Structure used for testing PageCycler's ability to playback a series of
    144 // URLs given a cache directory.
    145 class PageCyclerCachedBrowserTest : public PageCyclerBrowserTest {
    146  public:
    147   // For a cached test, we use the provided user data directory from the test
    148   // directory.
    149   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    150     base::FilePath test_dir;
    151     ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir));
    152     test_dir = test_dir.AppendASCII("page_cycler");
    153 
    154     base::FilePath source_data_dir = test_dir.AppendASCII("cached_data_dir");
    155     CHECK(base::PathExists(source_data_dir));
    156 
    157     CHECK(user_data_dir_.CreateUniqueTempDir());
    158 
    159     base::FilePath dest_data_dir =
    160         user_data_dir_.path().AppendASCII("cached_data_dir");
    161     CHECK(!base::PathExists(dest_data_dir));
    162 
    163     CHECK(base::CopyDirectory(source_data_dir, user_data_dir_.path(),
    164                               true));  // recursive.
    165     CHECK(base::PathExists(dest_data_dir));
    166 
    167     command_line->AppendSwitchPath(switches::kUserDataDir,
    168                                    dest_data_dir);
    169     command_line->AppendSwitch(switches::kPlaybackMode);
    170   }
    171 
    172   // Initialize the file paths to use the UserDataDir's urls file, instead
    173   // of one to be written.
    174   virtual void InitFilePaths(base::FilePath temp_path) OVERRIDE {
    175     urls_file_ = user_data_dir_.path().AppendASCII("cached_data_dir")
    176                                       .AppendASCII("urls");
    177     errors_file_ = temp_path.AppendASCII("errors");
    178     stats_file_ = temp_path.AppendASCII("stats");
    179 
    180     ASSERT_TRUE(base::PathExists(urls_file_));
    181     ASSERT_FALSE(base::PathExists(errors_file_));
    182     ASSERT_FALSE(base::PathExists(stats_file_));
    183   }
    184 
    185  private:
    186   // The directory storing the copy of the UserDataDir.
    187   base::ScopedTempDir user_data_dir_;
    188 };
    189 
    190 // Sanity check; iterate through a series of URLs and make sure there are no
    191 // errors.
    192 IN_PROC_BROWSER_TEST_F(PageCyclerBrowserTest, BasicTest) {
    193   base::ScopedTempDir temp;
    194   ASSERT_TRUE(temp.CreateUniqueTempDir());
    195 
    196   RegisterForNotifications();
    197   InitFilePaths(temp.path());
    198 
    199   ASSERT_TRUE(test_server()->Start());
    200 
    201   std::string urls_string = GetStringFromURLs(GetURLs());;
    202 
    203   ASSERT_TRUE(file_util::WriteFile(urls_file(), urls_string.c_str(),
    204                                    urls_string.size()));
    205 
    206   InitPageCycler();
    207   page_cycler()->Run();
    208 
    209   content::RunMessageLoop();
    210   ASSERT_FALSE(base::PathExists(errors_file()));
    211   ASSERT_TRUE(base::PathExists(stats_file()));
    212 }
    213 
    214 // Test to make sure that PageCycler will recognize unvisitable URLs, and will
    215 // handle them appropriately.
    216 IN_PROC_BROWSER_TEST_F(PageCyclerBrowserTest, UnvisitableURL) {
    217   const size_t kNumErrors = 1;
    218   const char kFakeURL[] = "http://www.pleasenoonehavethisurlanytimeinthenext"
    219                           "century.com/gibberish";
    220   base::ScopedTempDir temp;
    221   ASSERT_TRUE(temp.CreateUniqueTempDir());
    222 
    223   RegisterForNotifications();
    224   InitFilePaths(temp.path());
    225 
    226   ASSERT_TRUE(test_server()->Start());
    227 
    228   std::vector<GURL> urls = GetURLs();
    229   urls.push_back(GURL(kFakeURL));
    230   std::string urls_string = GetStringFromURLs(urls);
    231 
    232   ASSERT_TRUE(file_util::WriteFile(urls_file(), urls_string.c_str(),
    233                                    urls_string.size()));
    234 
    235   InitPageCycler();
    236   page_cycler()->Run();
    237 
    238   content::RunMessageLoop();
    239   ASSERT_TRUE(base::PathExists(errors_file()));
    240   ASSERT_TRUE(base::PathExists(stats_file()));
    241 
    242   std::vector<std::string> errors = GetErrorsFromFile();
    243 
    244   ASSERT_EQ(kNumErrors, errors.size());
    245 
    246   // Check that each error message contains the fake URL (i.e., that it wasn't
    247   // from a valid URL, and that the fake URL was caught each time).
    248   ASSERT_NE(std::string::npos, errors[0].find(kFakeURL));
    249 }
    250 
    251 // Test that PageCycler will remove an invalid URL prior to running.
    252 IN_PROC_BROWSER_TEST_F(PageCyclerBrowserTest, InvalidURL) {
    253   const char kBadURL[] = "notarealurl";
    254 
    255   base::ScopedTempDir temp;
    256   ASSERT_TRUE(temp.CreateUniqueTempDir());
    257 
    258   RegisterForNotifications();
    259   InitFilePaths(temp.path());
    260 
    261   ASSERT_TRUE(test_server()->Start());
    262 
    263   std::string urls_string = GetStringFromURLs(GetURLs());
    264   urls_string.append(kBadURL).append("\n");
    265 
    266   ASSERT_TRUE(file_util::WriteFile(urls_file(), urls_string.c_str(),
    267                                    urls_string.size()));
    268 
    269   InitPageCycler();
    270   page_cycler()->Run();
    271 
    272   content::RunMessageLoop();
    273   ASSERT_TRUE(base::PathExists(errors_file()));
    274   ASSERT_TRUE(base::PathExists(stats_file()));
    275 
    276   std::vector<std::string> errors = GetErrorsFromFile();
    277   ASSERT_EQ(1u, errors.size());
    278 
    279   std::string expected_error = "Omitting invalid URL: ";
    280   expected_error.append(kBadURL).append(".");
    281 
    282   ASSERT_FALSE(errors[0].compare(expected_error));
    283 }
    284 
    285 // Test that PageCycler will remove a Chrome Error URL prior to running.
    286 IN_PROC_BROWSER_TEST_F(PageCyclerBrowserTest, ChromeErrorURL) {
    287   base::ScopedTempDir temp;
    288   ASSERT_TRUE(temp.CreateUniqueTempDir());
    289 
    290   RegisterForNotifications();
    291   InitFilePaths(temp.path());
    292 
    293   ASSERT_TRUE(test_server()->Start());
    294 
    295   std::vector<GURL> urls = GetURLs();
    296   urls.push_back(GURL(content::kUnreachableWebDataURL));
    297   std::string urls_string = GetStringFromURLs(urls);
    298 
    299   ASSERT_TRUE(file_util::WriteFile(urls_file(), urls_string.c_str(),
    300                                    urls_string.size()));
    301 
    302   InitPageCycler();
    303   page_cycler()->Run();
    304 
    305   content::RunMessageLoop();
    306   ASSERT_TRUE(base::PathExists(errors_file()));
    307   ASSERT_TRUE(base::PathExists(stats_file()));
    308 
    309   std::vector<std::string> errors = GetErrorsFromFile();
    310   ASSERT_EQ(1u, errors.size());
    311 
    312   std::string expected_error = "Chrome error pages are not allowed as urls. "
    313                                "Omitting url: ";
    314   expected_error.append(content::kUnreachableWebDataURL).append(".");
    315 
    316   ASSERT_FALSE(errors[0].compare(expected_error));
    317 }
    318 
    319 #if !defined(OS_CHROMEOS)
    320 // TODO(rdevlin.cronin): Perhaps page cycler isn't completely implemented on
    321 // ChromeOS?
    322 
    323 // Test that PageCycler will visit all the urls from a cache directory
    324 // successfully while in playback mode.
    325 // Disabled due to flaky timeouts.  Tracking bugs include
    326 // [ http://crbug.com/159026 ], [ http://crbug.com/131333 ], and
    327 // [ http://crbug.com/222296 ].
    328 IN_PROC_BROWSER_TEST_F(PageCyclerCachedBrowserTest, DISABLED_PlaybackMode) {
    329 #if defined(OS_MACOSX)
    330   // TODO(kbr): re-enable: http://crbug.com/222296
    331   if (base::mac::IsOSMountainLionOrLater())
    332     return;
    333 #endif
    334 
    335   base::ScopedTempDir temp;
    336   ASSERT_TRUE(temp.CreateUniqueTempDir());
    337 
    338   RegisterForNotifications();
    339   InitFilePaths(temp.path());
    340 
    341   InitPageCycler();
    342 
    343   page_cycler()->Run();
    344 
    345   content::RunMessageLoop();
    346   ASSERT_TRUE(base::PathExists(stats_file()));
    347   ASSERT_FALSE(base::PathExists(errors_file()));
    348 }
    349 #endif  // !defined(OS_CHROMEOS)
    350 
    351 #if !defined(OS_CHROMEOS)
    352 // TODO(rdevlin.cronin): Perhaps page cycler isn't completely implemented on
    353 // ChromeOS?
    354 
    355 // Test that PageCycler will have a cache miss if a URL is missing from the
    356 // cache directory while in playback mode.
    357 // Bug 131333: This test fails on a XP debug bot since Build 17609.
    358 #if (defined(OS_WIN) || defined(OS_MACOSX)) && !defined(NDEBUG)
    359 #define MAYBE_URLNotInCache DISABLED_URLNotInCache
    360 #else
    361 #define MAYBE_URLNotInCache URLNotInCache
    362 #endif
    363 IN_PROC_BROWSER_TEST_F(PageCyclerCachedBrowserTest, MAYBE_URLNotInCache) {
    364   const char kCacheMissURL[] = "http://www.images.google.com/";
    365 
    366   base::ScopedTempDir temp;
    367   ASSERT_TRUE(temp.CreateUniqueTempDir());
    368 
    369   RegisterForNotifications();
    370   InitFilePaths(temp.path());
    371 
    372   // Only use a single URL that is not in cache. That's sufficient for the test
    373   // scenario, and makes things faster than needlessly cycling through all the
    374   // other URLs.
    375 
    376   base::FilePath new_urls_file = temp.path().AppendASCII("urls");
    377   ASSERT_FALSE(base::PathExists(new_urls_file));
    378 
    379   ASSERT_TRUE(file_util::WriteFile(new_urls_file, kCacheMissURL,
    380                                    sizeof(kCacheMissURL)));
    381 
    382   InitPageCycler(new_urls_file, errors_file(), stats_file());
    383   page_cycler()->Run();
    384 
    385   content::RunMessageLoop();
    386   ASSERT_TRUE(base::PathExists(errors_file()));
    387   ASSERT_TRUE(base::PathExists(stats_file()));
    388 
    389   std::vector<std::string> errors = GetErrorsFromFile();
    390   ASSERT_EQ(1u, errors.size());
    391 
    392   std::string expected_error;
    393   expected_error.append("Failed to load the page at: ")
    394       .append(kCacheMissURL)
    395       .append(": The requested entry was not found in the cache.");
    396 
    397   ASSERT_FALSE(errors[0].compare(expected_error));
    398 }
    399 #endif  // !defined(OS_CHROMEOS)
    400