| 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 #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) | |
| OLD | NEW |