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/environment.h" 6 #include "base/file_util.h" 7 #include "base/files/file_enumerator.h" 8 #include "base/path_service.h" 9 #include "base/strings/string_number_conversions.h" 10 #include "base/strings/string_split.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/stringprintf.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "base/sys_info.h" 15 #include "base/test/test_file_util.h" 16 #include "base/test/test_timeouts.h" 17 #include "base/time/time.h" 18 #include "chrome/common/chrome_constants.h" 19 #include "chrome/common/chrome_paths.h" 20 #include "chrome/common/chrome_switches.h" 21 #include "chrome/common/env_vars.h" 22 #include "chrome/test/automation/automation_proxy.h" 23 #include "chrome/test/base/test_switches.h" 24 #include "chrome/test/base/testing_profile.h" 25 #include "chrome/test/base/ui_test_utils.h" 26 #include "chrome/test/perf/perf_test.h" 27 #include "chrome/test/perf/perf_ui_test_suite.h" 28 #include "chrome/test/ui/ui_perf_test.h" 29 #include "content/public/common/content_switches.h" 30 #include "net/base/net_util.h" 31 32 using base::TimeDelta; 33 using base::TimeTicks; 34 35 namespace { 36 37 class StartupTest : public UIPerfTest { 38 public: 39 StartupTest() { 40 show_window_ = true; 41 } 42 virtual void SetUp() { 43 collect_profiling_stats_ = false; 44 } 45 virtual void TearDown() {} 46 47 enum TestColdness { 48 WARM, 49 COLD 50 }; 51 52 enum TestImportance { 53 NOT_IMPORTANT, 54 IMPORTANT 55 }; 56 57 // Load a file on startup rather than about:blank. This tests a longer 58 // startup path, including resource loading. 59 void SetUpWithFileURL() { 60 const base::FilePath file_url = ui_test_utils::GetTestFilePath( 61 base::FilePath(base::FilePath::kCurrentDirectory), 62 base::FilePath(FILE_PATH_LITERAL("simple.html"))); 63 ASSERT_TRUE(base::PathExists(file_url)); 64 launch_arguments_.AppendArgPath(file_url); 65 } 66 67 // Setup the command line arguments to capture profiling data for tasks. 68 void SetUpWithProfiling() { 69 profiling_file_ = ui_test_utils::GetTestFilePath( 70 base::FilePath(base::FilePath::kCurrentDirectory), 71 base::FilePath(FILE_PATH_LITERAL("task_profile.json"))); 72 launch_arguments_.AppendSwitchPath(switches::kProfilingOutputFile, 73 profiling_file_); 74 collect_profiling_stats_ = true; 75 } 76 77 // Load a complex html file on startup represented by |which_tab|. 78 void SetUpWithComplexFileURL(unsigned int which_tab) { 79 const char* const kTestPageCyclerDomains[] = { 80 "www.google.com", "www.nytimes.com", 81 "www.yahoo.com", "espn.go.com", "www.amazon.com" 82 }; 83 unsigned int which_el = which_tab % arraysize(kTestPageCyclerDomains); 84 const char* this_domain = kTestPageCyclerDomains[which_el]; 85 86 base::FilePath page_cycler_path; 87 PathService::Get(base::DIR_SOURCE_ROOT, &page_cycler_path); 88 page_cycler_path = page_cycler_path.AppendASCII("data") 89 .AppendASCII("page_cycler").AppendASCII("moz") 90 .AppendASCII(this_domain).AppendASCII("index.html"); 91 GURL file_url = net::FilePathToFileURL(page_cycler_path).Resolve("?skip"); 92 launch_arguments_.AppendArg(file_url.spec()); 93 } 94 95 // Use the given profile in the test data extensions/profiles dir. This tests 96 // startup with extensions installed. 97 void SetUpWithExtensionsProfile(const char* profile) { 98 base::FilePath data_dir; 99 PathService::Get(chrome::DIR_TEST_DATA, &data_dir); 100 data_dir = data_dir.AppendASCII("extensions").AppendASCII("profiles"). 101 AppendASCII(profile); 102 set_template_user_data(data_dir); 103 } 104 105 // Rewrite the preferences file to point to the proper image directory. 106 virtual void SetUpProfile() OVERRIDE { 107 UIPerfTest::SetUpProfile(); 108 if (profile_type_ != PerfUITestSuite::COMPLEX_THEME) 109 return; 110 111 const base::FilePath pref_template_path(user_data_dir(). 112 AppendASCII("Default"). 113 AppendASCII("PreferencesTemplate")); 114 const base::FilePath pref_path(user_data_dir(). 115 AppendASCII(TestingProfile::kTestUserProfileDir). 116 AppendASCII("Preferences")); 117 118 // Read in preferences template. 119 std::string pref_string; 120 EXPECT_TRUE(file_util::ReadFileToString(pref_template_path, &pref_string)); 121 string16 format_string = ASCIIToUTF16(pref_string); 122 123 // Make sure temp directory has the proper format for writing to prefs file. 124 #if defined(OS_POSIX) 125 std::wstring user_data_dir_w(ASCIIToWide(user_data_dir().value())); 126 #elif defined(OS_WIN) 127 std::wstring user_data_dir_w(user_data_dir().value()); 128 // In Windows, the FilePath will write '\' for the path separators; change 129 // these to a separator that won't trigger escapes. 130 std::replace(user_data_dir_w.begin(), 131 user_data_dir_w.end(), '\\', '/'); 132 #endif 133 134 // Rewrite prefs file. 135 std::vector<string16> subst; 136 subst.push_back(WideToUTF16(user_data_dir_w)); 137 const std::string prefs_string = 138 UTF16ToASCII(ReplaceStringPlaceholders(format_string, subst, NULL)); 139 EXPECT_TRUE(file_util::WriteFile(pref_path, prefs_string.c_str(), 140 prefs_string.size())); 141 file_util::EvictFileFromSystemCache(pref_path); 142 } 143 144 // Runs a test which loads |tab_count| tabs on startup, either as command line 145 // arguments or, if |restore_session| is true, by using session restore. 146 // |nth_timed_tab|, if non-zero, will measure time to load the first n+1 tabs. 147 void RunPerfTestWithManyTabs(const char* graph, const char* trace, 148 int tab_count, int nth_timed_tab, 149 bool restore_session); 150 151 void RunStartupTest(const char* graph, const char* trace, 152 TestColdness test_cold, TestImportance test_importance, 153 PerfUITestSuite::ProfileType profile_type, 154 int num_tabs, int nth_timed_tab) { 155 bool important = (test_importance == IMPORTANT); 156 profile_type_ = profile_type; 157 158 // Sets the profile data for the run. For now, this is only used for 159 // the non-default themes test. 160 if (profile_type != PerfUITestSuite::DEFAULT_THEME) { 161 set_template_user_data( 162 PerfUITestSuite::GetPathForProfileType(profile_type)); 163 } 164 165 #if defined(NDEBUG) 166 const int kNumCyclesMax = 20; 167 #else 168 // Debug builds are too slow and we can't run that many cycles in a 169 // reasonable amount of time. 170 const int kNumCyclesMax = 10; 171 #endif 172 int numCycles = kNumCyclesMax; 173 scoped_ptr<base::Environment> env(base::Environment::Create()); 174 std::string numCyclesEnv; 175 if (env->GetVar(env_vars::kStartupTestsNumCycles, &numCyclesEnv) && 176 base::StringToInt(numCyclesEnv, &numCycles)) { 177 if (numCycles <= kNumCyclesMax) { 178 VLOG(1) << env_vars::kStartupTestsNumCycles 179 << " set in environment, so setting numCycles to " << numCycles; 180 } else { 181 VLOG(1) << env_vars::kStartupTestsNumCycles 182 << " is higher than the max, setting numCycles to " 183 << kNumCyclesMax; 184 numCycles = kNumCyclesMax; 185 } 186 } 187 188 struct TimingInfo { 189 TimeDelta end_to_end; 190 float first_start_ms; 191 float last_stop_ms; 192 float first_stop_ms; 193 float nth_tab_stop_ms; 194 }; 195 TimingInfo timings[kNumCyclesMax]; 196 197 CommandLine launch_arguments_without_trace_file(launch_arguments_); 198 for (int i = 0; i < numCycles; ++i) { 199 if (test_cold == COLD) { 200 base::FilePath dir_app; 201 ASSERT_TRUE(PathService::Get(chrome::DIR_APP, &dir_app)); 202 203 base::FilePath chrome_exe(dir_app.Append(GetExecutablePath())); 204 ASSERT_TRUE(base::EvictFileFromSystemCacheWithRetry(chrome_exe)); 205 #if defined(OS_WIN) 206 // chrome.dll is windows specific. 207 base::FilePath chrome_dll( 208 dir_app.Append(FILE_PATH_LITERAL("chrome.dll"))); 209 ASSERT_TRUE(base::EvictFileFromSystemCacheWithRetry(chrome_dll)); 210 #endif 211 } 212 UITest::SetUp(); 213 TimeTicks end_time = TimeTicks::Now(); 214 215 if (num_tabs > 0) { 216 float min_start; 217 float max_stop; 218 std::vector<float> times; 219 scoped_refptr<BrowserProxy> browser_proxy( 220 automation()->GetBrowserWindow(0)); 221 ASSERT_TRUE(browser_proxy.get()); 222 223 if (browser_proxy->GetInitialLoadTimes( 224 TestTimeouts::action_max_timeout(), 225 &min_start, 226 &max_stop, 227 ×) && 228 !times.empty()) { 229 ASSERT_LT(nth_timed_tab, num_tabs); 230 ASSERT_EQ(times.size(), static_cast<size_t>(num_tabs)); 231 timings[i].first_start_ms = min_start; 232 timings[i].last_stop_ms = max_stop; 233 timings[i].first_stop_ms = times[0]; 234 timings[i].nth_tab_stop_ms = times[nth_timed_tab]; 235 } else { 236 // Browser might not support initial load times. 237 // Only use end-to-end time for this test. 238 num_tabs = 0; 239 } 240 } 241 timings[i].end_to_end = end_time - browser_launch_time(); 242 UITest::TearDown(); 243 244 if (i == 0) { 245 // Re-use the profile data after first run so that the noise from 246 // creating databases doesn't impact all the runs. 247 clear_profile_ = false; 248 // Clear template_user_data_ so we don't try to copy it over each time 249 // through. 250 set_template_user_data(base::FilePath()); 251 } 252 } 253 254 std::string times; 255 for (int i = 0; i < numCycles; ++i) { 256 base::StringAppendF(×, 257 "%.2f,", 258 timings[i].end_to_end.InMillisecondsF()); 259 } 260 perf_test::PrintResultList( 261 graph, std::string(), trace, times, "ms", important); 262 263 if (num_tabs > 0) { 264 std::string name_base = trace; 265 std::string name; 266 267 times.clear(); 268 name = name_base + "-start"; 269 for (int i = 0; i < numCycles; ++i) 270 base::StringAppendF(×, "%.2f,", timings[i].first_start_ms); 271 perf_test::PrintResultList( 272 graph, std::string(), name.c_str(), times, "ms", important); 273 274 times.clear(); 275 name = name_base + "-first"; 276 for (int i = 0; i < numCycles; ++i) 277 base::StringAppendF(×, "%.2f,", timings[i].first_stop_ms); 278 perf_test::PrintResultList( 279 graph, std::string(), name.c_str(), times, "ms", important); 280 281 if (nth_timed_tab > 0) { 282 // Display only the time necessary to load the first n tabs. 283 times.clear(); 284 name = name_base + "-" + base::IntToString(nth_timed_tab); 285 for (int i = 0; i < numCycles; ++i) 286 base::StringAppendF(×, "%.2f,", timings[i].nth_tab_stop_ms); 287 perf_test::PrintResultList( 288 graph, std::string(), name.c_str(), times, "ms", important); 289 } 290 291 if (num_tabs > 1) { 292 // Display the time necessary to load all of the tabs. 293 times.clear(); 294 name = name_base + "-all"; 295 for (int i = 0; i < numCycles; ++i) 296 base::StringAppendF(×, "%.2f,", timings[i].last_stop_ms); 297 perf_test::PrintResultList( 298 graph, std::string(), name.c_str(), times, "ms", important); 299 } 300 } 301 } 302 303 base::FilePath profiling_file_; 304 bool collect_profiling_stats_; 305 std::string trace_file_prefix_; 306 307 // Are we using a profile with a complex theme? 308 PerfUITestSuite::ProfileType profile_type_; 309 }; 310 311 TEST_F(StartupTest, PerfWarm) { 312 RunStartupTest("warm", "t", WARM, IMPORTANT, 313 PerfUITestSuite::DEFAULT_THEME, 0, 0); 314 } 315 316 TEST_F(StartupTest, PerfReferenceWarm) { 317 UseReferenceBuild(); 318 RunStartupTest("warm", "t_ref", WARM, IMPORTANT, 319 PerfUITestSuite::DEFAULT_THEME, 0, 0); 320 } 321 322 // TODO(mpcomplete): Should we have reference timings for all these? 323 324 // dominich: Disabling as per http://crbug.com/100900. 325 #if defined(OS_WIN) 326 #define MAYBE_PerfCold DISABLED_PerfCold 327 #else 328 #define MAYBE_PerfCold PerfCold 329 #endif 330 331 TEST_F(StartupTest, MAYBE_PerfCold) { 332 RunStartupTest("cold", "t", COLD, NOT_IMPORTANT, 333 PerfUITestSuite::DEFAULT_THEME, 0, 0); 334 } 335 336 void StartupTest::RunPerfTestWithManyTabs(const char* graph, const char* trace, 337 int tab_count, int nth_timed_tab, 338 bool restore_session) { 339 // Initialize session with |tab_count| tabs. 340 for (int i = 0; i < tab_count; ++i) 341 SetUpWithComplexFileURL(i); 342 343 if (restore_session) { 344 // Start the browser with these urls so we can save the session and exit. 345 UITest::SetUp(); 346 // Set flags to ensure profile is saved and can be restored. 347 #if defined(OS_CHROMEOS) || defined(OS_MACOSX) 348 set_shutdown_type(ProxyLauncher::USER_QUIT); 349 #endif 350 clear_profile_ = false; 351 // Quit and set flags to restore session. 352 UITest::TearDown(); 353 354 // Clear all arguments for session restore, or the number of open tabs 355 // will grow with each restore. 356 CommandLine new_launch_arguments = CommandLine(CommandLine::NO_PROGRAM); 357 // Keep the branding switch if using a reference build. 358 if (launch_arguments_.HasSwitch(switches::kEnableChromiumBranding)) { 359 new_launch_arguments.AppendSwitch(switches::kEnableChromiumBranding); 360 } 361 // The session will be restored once per cycle for numCycles test cycles, 362 // and each time, UITest::SetUp will wait for |tab_count| tabs to 363 // finish loading. 364 new_launch_arguments.AppendSwitchASCII(switches::kRestoreLastSession, 365 base::IntToString(tab_count)); 366 launch_arguments_ = new_launch_arguments; 367 } 368 RunStartupTest(graph, trace, WARM, NOT_IMPORTANT, 369 PerfUITestSuite::DEFAULT_THEME, tab_count, nth_timed_tab); 370 } 371 372 // http://crbug.com/101591 373 #if defined(OS_WIN) && !defined(NDEBUG) 374 #define MAYBE_PerfFewTabs DISABLED_PerfFewTabs 375 #else 376 #define MAYBE_PerfFewTabs PerfFewTabs 377 #endif 378 379 TEST_F(StartupTest, MAYBE_PerfFewTabs) { 380 RunPerfTestWithManyTabs("few_tabs", "cmdline", 5, 2, false); 381 } 382 383 TEST_F(StartupTest, PerfFewTabsReference) { 384 UseReferenceBuild(); 385 RunPerfTestWithManyTabs("few_tabs", "cmdline-ref", 5, 2, false); 386 } 387 388 TEST_F(StartupTest, PerfRestoreFewTabs) { 389 RunPerfTestWithManyTabs("few_tabs", "restore", 5, 2, true); 390 } 391 392 TEST_F(StartupTest, PerfRestoreFewTabsReference) { 393 UseReferenceBuild(); 394 RunPerfTestWithManyTabs("few_tabs", "restore-ref", 5, 2, true); 395 } 396 397 #if defined(OS_MACOSX) 398 // http://crbug.com/46609 399 #define MAYBE_PerfSeveralTabsReference DISABLED_PerfSeveralTabsReference 400 #define MAYBE_PerfSeveralTabs DISABLED_PerfSeveralTabs 401 // http://crbug.com/52858 402 #define MAYBE_PerfRestoreSeveralTabs DISABLED_PerfRestoreSeveralTabs 403 #define MAYBE_PerfExtensionContentScript50 DISABLED_PerfExtensionContentScript50 404 #elif defined(OS_WIN) 405 // http://crbug.com/46609 406 #if !defined(NDEBUG) 407 // This test is disabled on Windows Debug. See bug http://crbug.com/132279 408 #define MAYBE_PerfRestoreSeveralTabs DISABLED_PerfRestoreSeveralTabs 409 #else 410 #define MAYBE_PerfRestoreSeveralTabs PerfRestoreSeveralTabs 411 #endif // !defined(NDEBUG) 412 // http://crbug.com/102584 413 #define MAYBE_PerfSeveralTabs DISABLED_PerfSeveralTabs 414 #define MAYBE_PerfSeveralTabsReference PerfSeveralTabsReference 415 #define MAYBE_PerfExtensionContentScript50 PerfExtensionContentScript50 416 #else 417 #define MAYBE_PerfSeveralTabsReference PerfSeveralTabsReference 418 #define MAYBE_PerfSeveralTabs PerfSeveralTabs 419 #define MAYBE_PerfRestoreSeveralTabs PerfRestoreSeveralTabs 420 #define MAYBE_PerfExtensionContentScript50 PerfExtensionContentScript50 421 #endif 422 423 // http://crbug.com/99604 424 #if defined(OS_WIN) && !defined(NDEBUG) 425 #define MAYBE_PerfComplexTheme DISABLED_PerfComplexTheme 426 #else 427 #define MAYBE_PerfComplexTheme PerfComplexTheme 428 #endif 429 430 TEST_F(StartupTest, MAYBE_PerfSeveralTabs) { 431 RunPerfTestWithManyTabs("several_tabs", "cmdline", 10, 4, false); 432 } 433 434 TEST_F(StartupTest, MAYBE_PerfSeveralTabsReference) { 435 UseReferenceBuild(); 436 RunPerfTestWithManyTabs("several_tabs", "cmdline-ref", 10, 4, false); 437 } 438 439 TEST_F(StartupTest, MAYBE_PerfRestoreSeveralTabs) { 440 RunPerfTestWithManyTabs("several_tabs", "restore", 10, 4, true); 441 } 442 443 TEST_F(StartupTest, PerfRestoreSeveralTabsReference) { 444 UseReferenceBuild(); 445 RunPerfTestWithManyTabs("several_tabs", "restore-ref", 10, 4, true); 446 } 447 448 TEST_F(StartupTest, PerfExtensionEmpty) { 449 SetUpWithFileURL(); 450 SetUpWithExtensionsProfile("empty"); 451 RunStartupTest("warm", "extension_empty", WARM, NOT_IMPORTANT, 452 PerfUITestSuite::DEFAULT_THEME, 1, 0); 453 } 454 455 TEST_F(StartupTest, PerfExtensionContentScript1) { 456 SetUpWithFileURL(); 457 SetUpWithExtensionsProfile("content_scripts1"); 458 RunStartupTest("warm", "extension_content_scripts1", WARM, NOT_IMPORTANT, 459 PerfUITestSuite::DEFAULT_THEME, 1, 0); 460 } 461 462 TEST_F(StartupTest, MAYBE_PerfExtensionContentScript50) { 463 SetUpWithFileURL(); 464 SetUpWithExtensionsProfile("content_scripts50"); 465 RunStartupTest("warm", "extension_content_scripts50", WARM, NOT_IMPORTANT, 466 PerfUITestSuite::DEFAULT_THEME, 1, 0); 467 } 468 469 TEST_F(StartupTest, MAYBE_PerfComplexTheme) { 470 RunStartupTest("warm", "t-theme", WARM, NOT_IMPORTANT, 471 PerfUITestSuite::COMPLEX_THEME, 0, 0); 472 } 473 474 TEST_F(StartupTest, ProfilingScript1) { 475 SetUpWithFileURL(); 476 SetUpWithProfiling(); 477 RunStartupTest("warm", "profiling_scripts1", WARM, NOT_IMPORTANT, 478 PerfUITestSuite::DEFAULT_THEME, 1, 0); 479 } 480 481 } // namespace 482