OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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/basictypes.h" | |
6 #include "base/command_line.h" | |
7 #include "base/file_path.h" | |
8 #include "base/file_util.h" | |
9 #include "base/path_service.h" | |
10 #include "base/process_util.h" | |
11 #include "base/string_number_conversions.h" | |
12 #include "base/sys_string_conversions.h" | |
13 #include "base/test/test_timeouts.h" | |
14 #include "base/utf_string_conversions.h" | |
15 #include "chrome/browser/net/url_fixer_upper.h" | |
16 #include "chrome/common/chrome_constants.h" | |
17 #include "chrome/common/chrome_paths.h" | |
18 #include "chrome/common/chrome_switches.h" | |
19 #include "chrome/test/automation/tab_proxy.h" | |
20 #include "chrome/test/automation/window_proxy.h" | |
21 #include "chrome/test/base/chrome_process_util.h" | |
22 #include "chrome/test/base/test_switches.h" | |
23 #include "chrome/test/ui/ui_perf_test.h" | |
24 #include "googleurl/src/gurl.h" | |
25 #include "net/base/net_util.h" | |
26 | |
27 #ifndef NDEBUG | |
28 static const int kTestIterations = 2; | |
29 static const int kDatabaseTestIterations = 2; | |
30 #else | |
31 static const int kTestIterations = 10; | |
32 // For some unknown reason, the DB perf tests are much much slower on the | |
33 // Vista perf bot, so we have to cut down the number of iterations to 5 | |
34 // to make sure each test finishes in less than 10 minutes. | |
35 static const int kDatabaseTestIterations = 5; | |
36 #endif | |
37 static const int kIDBTestIterations = 5; | |
38 | |
39 // URL at which data files may be found for HTTP tests. The document root of | |
40 // this URL's server should point to data/page_cycler/. | |
41 static const char kBaseUrl[] = "http://localhost:8000/"; | |
42 | |
43 namespace { | |
44 | |
45 void PopulateBufferCache(const FilePath& test_dir) { | |
46 // This will recursively walk the directory given and read all the | |
47 // files it finds. This is done so the system file cache is likely | |
48 // to have as much loaded as possible. Without this, the tests of | |
49 // this build gets one set of timings and then the reference build | |
50 // test, gets slightly faster ones (even if the reference build is | |
51 // the same binary). The hope is by forcing all the possible data | |
52 // into the cache we equalize the tests for comparing timing data. | |
53 | |
54 // We don't want to walk into .svn dirs, so we have to do the tree walk | |
55 // ourselves. | |
56 | |
57 std::vector<FilePath> dirs; | |
58 dirs.push_back(test_dir); | |
59 const FilePath svn_dir(FILE_PATH_LITERAL(".svn")); | |
60 | |
61 for (size_t idx = 0; idx < dirs.size(); ++idx) { | |
62 file_util::FileEnumerator dir_enumerator(dirs[idx], false, | |
63 file_util::FileEnumerator::DIRECTORIES); | |
64 FilePath path; | |
65 for (path = dir_enumerator.Next(); | |
66 !path.empty(); | |
67 path = dir_enumerator.Next()) { | |
68 if (path.BaseName() != svn_dir) | |
69 dirs.push_back(path); | |
70 } | |
71 } | |
72 | |
73 unsigned int loaded = 0; | |
74 | |
75 // We seem to have some files in the data dirs that are just there for | |
76 // reference, make a quick attempt to skip them by matching suffixes. | |
77 std::vector<FilePath::StringType> ignore_suffixes; | |
78 ignore_suffixes.push_back(FILE_PATH_LITERAL(".orig.html")); | |
79 ignore_suffixes.push_back(FILE_PATH_LITERAL(".html-original")); | |
80 | |
81 std::vector<FilePath>::const_iterator iter; | |
82 for (iter = dirs.begin(); iter != dirs.end(); ++iter) { | |
83 file_util::FileEnumerator file_enumerator(*iter, false, | |
84 file_util::FileEnumerator::FILES); | |
85 FilePath path; | |
86 for (path = file_enumerator.Next(); | |
87 !path.empty(); | |
88 path = file_enumerator.Next()) { | |
89 const FilePath base_name = path.BaseName(); | |
90 const size_t base_name_size = base_name.value().size(); | |
91 | |
92 bool should_skip = false; | |
93 std::vector<FilePath::StringType>::const_iterator ignore_iter; | |
94 for (ignore_iter = ignore_suffixes.begin(); | |
95 ignore_iter != ignore_suffixes.end(); | |
96 ++ignore_iter) { | |
97 const FilePath::StringType &suffix = *ignore_iter; | |
98 | |
99 if ((base_name_size > suffix.size()) && | |
100 (base_name.value().compare(base_name_size - suffix.size(), | |
101 suffix.size(), suffix) == 0)) { | |
102 should_skip = true; | |
103 break; | |
104 } | |
105 } | |
106 if (should_skip) | |
107 continue; | |
108 | |
109 // Read the file fully to get it into the cache. | |
110 // We don't care what the contents are. | |
111 if (file_util::ReadFileToString(path, NULL)) | |
112 ++loaded; | |
113 } | |
114 } | |
115 VLOG(1) << "Buffer cache should be primed with " << loaded << " files."; | |
116 } | |
117 | |
118 class PageCyclerTest : public UIPerfTest { | |
119 protected: | |
120 bool print_times_only_; | |
121 int num_test_iterations_; | |
122 public: | |
123 PageCyclerTest() | |
124 : print_times_only_(false) { | |
125 show_window_ = true; | |
126 dom_automation_enabled_ = true; | |
127 | |
128 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); | |
129 num_test_iterations_ = kTestIterations; | |
130 | |
131 if (parsed_command_line.HasSwitch(switches::kPageCyclerIterations)) { | |
132 std::string str = parsed_command_line.GetSwitchValueASCII( | |
133 switches::kPageCyclerIterations); | |
134 base::StringToInt(str, &num_test_iterations_); | |
135 } | |
136 | |
137 // Expose garbage collection for the page cycler tests. | |
138 launch_arguments_.AppendSwitchASCII(switches::kJavaScriptFlags, | |
139 "--expose_gc"); | |
140 } | |
141 | |
142 virtual FilePath GetDataPath(const char* name) { | |
143 // Make sure the test data is checked out | |
144 FilePath test_path; | |
145 PathService::Get(base::DIR_SOURCE_ROOT, &test_path); | |
146 test_path = test_path.Append(FILE_PATH_LITERAL("data")); | |
147 test_path = test_path.Append(FILE_PATH_LITERAL("page_cycler")); | |
148 test_path = test_path.AppendASCII(name); | |
149 return test_path; | |
150 } | |
151 | |
152 virtual bool HasErrors(const std::string /*timings*/) { | |
153 return false; | |
154 } | |
155 | |
156 virtual int GetTestIterations() { | |
157 return num_test_iterations_; | |
158 } | |
159 | |
160 // For HTTP tests, the name must be safe for use in a URL without escaping. | |
161 void RunPageCycler(const char* name, std::wstring* pages, | |
162 std::string* timings, bool use_http) { | |
163 FilePath test_path = GetDataPath(name); | |
164 ASSERT_TRUE(file_util::DirectoryExists(test_path)) | |
165 << "Missing test directory " << test_path.value(); | |
166 | |
167 PopulateBufferCache(test_path); | |
168 | |
169 GURL test_url; | |
170 if (use_http) { | |
171 test_url = GURL(std::string(kBaseUrl) + name + "/start.html"); | |
172 } else { | |
173 test_path = test_path.Append(FILE_PATH_LITERAL("start.html")); | |
174 test_url = net::FilePathToFileURL(test_path); | |
175 } | |
176 | |
177 // run N iterations | |
178 GURL::Replacements replacements; | |
179 const std::string query_string = | |
180 "iterations=" + base::IntToString(GetTestIterations()) + "&auto=1"; | |
181 replacements.SetQuery( | |
182 query_string.c_str(), | |
183 url_parse::Component(0, query_string.length())); | |
184 test_url = test_url.ReplaceComponents(replacements); | |
185 | |
186 scoped_refptr<TabProxy> tab(GetActiveTab()); | |
187 ASSERT_TRUE(tab.get()); | |
188 ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab->NavigateToURL(test_url)); | |
189 | |
190 // Wait for the test to finish. | |
191 ASSERT_TRUE(WaitUntilCookieValue( | |
192 tab.get(), test_url, "__pc_done", | |
193 TestTimeouts::huge_test_timeout_ms(), "1")); | |
194 | |
195 std::string cookie; | |
196 ASSERT_TRUE(tab->GetCookieByName(test_url, "__pc_pages", &cookie)); | |
197 pages->assign(UTF8ToWide(cookie)); | |
198 ASSERT_FALSE(pages->empty()); | |
199 | |
200 // Get the timing cookie value from the DOM automation. | |
201 std::wstring wcookie; | |
202 ASSERT_TRUE(tab->ExecuteAndExtractString(L"", | |
203 L"window.domAutomationController.send(" | |
204 L"JSON.stringify(__get_timings()));", | |
205 &wcookie)); | |
206 cookie = base::SysWideToNativeMB(wcookie); | |
207 | |
208 // JSON.stringify() encapsulates the returned string in quotes, strip them. | |
209 std::string::size_type start_idx = cookie.find("\""); | |
210 std::string::size_type end_idx = cookie.find_last_of("\""); | |
211 if (start_idx != std::string::npos && | |
212 end_idx != std::string::npos && | |
213 start_idx < end_idx) { | |
214 cookie = cookie.substr(start_idx+1, end_idx-start_idx-1); | |
215 } | |
216 | |
217 timings->assign(cookie); | |
218 ASSERT_FALSE(timings->empty()); | |
219 } | |
220 | |
221 // When use_http is true, the test name passed here will be used directly in | |
222 // the path to the test data, so it must be safe for use in a URL without | |
223 // escaping. (No pound (#), question mark (?), semicolon (;), non-ASCII, or | |
224 // other funny stuff.) | |
225 void RunTestWithSuffix(const char* graph, const char* name, bool use_http, | |
226 const char* suffix) { | |
227 std::wstring pages; | |
228 std::string timings; | |
229 size_t start_size = base::GetSystemCommitCharge(); | |
230 RunPageCycler(name, &pages, &timings, use_http); | |
231 if (timings.empty() || HasErrors(timings)) | |
232 return; | |
233 size_t stop_size = base::GetSystemCommitCharge(); | |
234 | |
235 if (!print_times_only_) { | |
236 PrintMemoryUsageInfo(suffix); | |
237 PrintIOPerfInfo(suffix); | |
238 PrintSystemCommitCharge(suffix, stop_size - start_size, | |
239 false /* not important */); | |
240 } | |
241 | |
242 std::string trace_name = "t" + std::string(suffix); | |
243 | |
244 printf("Pages: [%s]\n", base::SysWideToNativeMB(pages).c_str()); | |
245 | |
246 PrintResultList(graph, "", trace_name, timings, "ms", | |
247 true /* important */); | |
248 } | |
249 | |
250 void RunTest(const char* graph, const char* name, bool use_http) { | |
251 RunTestWithSuffix(graph, name, use_http, ""); | |
252 } | |
253 }; | |
254 | |
255 class PageCyclerReferenceTest : public PageCyclerTest { | |
256 public: | |
257 void SetUp() { | |
258 UseReferenceBuild(); | |
259 PageCyclerTest::SetUp(); | |
260 } | |
261 | |
262 void RunTest(const char* graph, const char* name, bool use_http) { | |
263 // Run the test. | |
264 PageCyclerTest::RunTestWithSuffix(graph, name, use_http, "_ref"); | |
265 } | |
266 }; | |
267 | |
268 class PageCyclerExtensionTest : public PageCyclerTest { | |
269 public: | |
270 // Note: we delay the SetUp until RunTest is called so that we can set | |
271 // the user_data_dir based on the test name. | |
272 virtual void SetUp() {} | |
273 void RunTest(const char* graph, const char* extension_profile, | |
274 const char* output_suffix, const char* name, bool use_http) { | |
275 // Set up the extension profile directory. | |
276 ASSERT_TRUE(extension_profile != NULL); | |
277 FilePath data_dir; | |
278 PathService::Get(chrome::DIR_TEST_DATA, &data_dir); | |
279 data_dir = data_dir.AppendASCII("extensions").AppendASCII("profiles"). | |
280 AppendASCII(extension_profile); | |
281 ASSERT_TRUE(file_util::DirectoryExists(data_dir)); | |
282 set_template_user_data(data_dir); | |
283 | |
284 // Now run the test. | |
285 PageCyclerTest::SetUp(); | |
286 PageCyclerTest::RunTestWithSuffix(graph, name, use_http, output_suffix); | |
287 } | |
288 }; | |
289 | |
290 class PageCyclerExtensionWebRequestTest : public PageCyclerExtensionTest { | |
291 public: | |
292 PageCyclerExtensionWebRequestTest() | |
293 : PageCyclerExtensionTest() | |
294 { | |
295 // Enable experimental extension APIs for webrequest tests. | |
296 launch_arguments_.AppendSwitch(switches::kEnableExperimentalExtensionApis); | |
297 } | |
298 }; | |
299 | |
300 static FilePath GetDatabaseDataPath(const char* name) { | |
301 FilePath test_path; | |
302 PathService::Get(base::DIR_SOURCE_ROOT, &test_path); | |
303 test_path = test_path.Append(FILE_PATH_LITERAL("tools")); | |
304 test_path = test_path.Append(FILE_PATH_LITERAL("page_cycler")); | |
305 test_path = test_path.Append(FILE_PATH_LITERAL("database")); | |
306 test_path = test_path.AppendASCII(name); | |
307 return test_path; | |
308 } | |
309 | |
310 static FilePath GetIndexedDatabaseDataPath(const char* name) { | |
311 FilePath test_path; | |
312 PathService::Get(base::DIR_SOURCE_ROOT, &test_path); | |
313 test_path = test_path.Append(FILE_PATH_LITERAL("tools")); | |
314 test_path = test_path.Append(FILE_PATH_LITERAL("page_cycler")); | |
315 test_path = test_path.Append(FILE_PATH_LITERAL("indexed_db")); | |
316 test_path = test_path.AppendASCII(name); | |
317 return test_path; | |
318 } | |
319 | |
320 static bool HasDatabaseErrors(const std::string timings) { | |
321 size_t pos = 0; | |
322 size_t new_pos = 0; | |
323 std::string time_str; | |
324 int time = 0; | |
325 do { | |
326 new_pos = timings.find(',', pos); | |
327 if (new_pos == std::string::npos) | |
328 new_pos = timings.length(); | |
329 if (!base::StringToInt(timings.begin() + pos, | |
330 timings.begin() + new_pos, | |
331 &time)) { | |
332 LOG(ERROR) << "Invalid time reported: " << time_str; | |
333 return true; | |
334 } | |
335 if (time < 0) { | |
336 switch (time) { | |
337 case -1: | |
338 LOG(ERROR) << "Error while opening the database."; | |
339 break; | |
340 case -2: | |
341 LOG(ERROR) << "Error while setting up the database."; | |
342 break; | |
343 case -3: | |
344 LOG(ERROR) << "Error while running the transactions."; | |
345 break; | |
346 default: | |
347 LOG(ERROR) << "Unknown error: " << time; | |
348 } | |
349 return true; | |
350 } | |
351 | |
352 pos = new_pos + 1; | |
353 } while (pos < timings.length()); | |
354 | |
355 return false; | |
356 } | |
357 | |
358 class PageCyclerDatabaseTest : public PageCyclerTest { | |
359 public: | |
360 PageCyclerDatabaseTest() { | |
361 print_times_only_ = true; | |
362 } | |
363 | |
364 virtual FilePath GetDataPath(const char* name) { | |
365 return GetDatabaseDataPath(name); | |
366 } | |
367 | |
368 virtual bool HasErrors(const std::string timings) { | |
369 return HasDatabaseErrors(timings); | |
370 } | |
371 | |
372 virtual int GetTestIterations() { | |
373 return kDatabaseTestIterations; | |
374 } | |
375 }; | |
376 | |
377 class PageCyclerDatabaseReferenceTest : public PageCyclerReferenceTest { | |
378 public: | |
379 PageCyclerDatabaseReferenceTest() { | |
380 print_times_only_ = true; | |
381 } | |
382 | |
383 virtual FilePath GetDataPath(const char* name) { | |
384 return GetDatabaseDataPath(name); | |
385 } | |
386 | |
387 virtual bool HasErrors(const std::string timings) { | |
388 return HasDatabaseErrors(timings); | |
389 } | |
390 | |
391 virtual int GetTestIterations() { | |
392 return kDatabaseTestIterations; | |
393 } | |
394 }; | |
395 | |
396 class PageCyclerIndexedDatabaseTest : public PageCyclerTest { | |
397 public: | |
398 PageCyclerIndexedDatabaseTest() { | |
399 print_times_only_ = true; | |
400 } | |
401 | |
402 virtual FilePath GetDataPath(const char* name) { | |
403 return GetIndexedDatabaseDataPath(name); | |
404 } | |
405 | |
406 virtual bool HasErrors(const std::string timings) { | |
407 return HasDatabaseErrors(timings); | |
408 } | |
409 | |
410 virtual int GetTestIterations() { | |
411 return kIDBTestIterations; | |
412 } | |
413 }; | |
414 | |
415 class PageCyclerIndexedDatabaseReferenceTest : public PageCyclerReferenceTest { | |
416 public: | |
417 PageCyclerIndexedDatabaseReferenceTest() { | |
418 print_times_only_ = true; | |
419 } | |
420 | |
421 virtual FilePath GetDataPath(const char* name) { | |
422 return GetIndexedDatabaseDataPath(name); | |
423 } | |
424 | |
425 virtual bool HasErrors(const std::string timings) { | |
426 return HasDatabaseErrors(timings); | |
427 } | |
428 | |
429 virtual int GetTestIterations() { | |
430 return kIDBTestIterations; | |
431 } | |
432 }; | |
433 | |
434 // This macro simplifies setting up regular and reference build tests. | |
435 #define PAGE_CYCLER_TESTS(test, name, use_http) \ | |
436 TEST_F(PageCyclerTest, name) { \ | |
437 RunTest("times", test, use_http); \ | |
438 } \ | |
439 TEST_F(PageCyclerReferenceTest, name) { \ | |
440 RunTest("times", test, use_http); \ | |
441 } | |
442 | |
443 // This macro simplifies setting up regular and reference build tests | |
444 // for HTML5 database tests. | |
445 // FLAKY http://crbug.com/67918 | |
446 #define PAGE_CYCLER_DATABASE_TESTS(test, name) \ | |
447 TEST_F(PageCyclerDatabaseTest, FLAKY_Database##name##File) { \ | |
448 RunTest(test, test, false); \ | |
449 } \ | |
450 TEST_F(PageCyclerDatabaseReferenceTest, FLAKY_Database##name##File) { \ | |
451 RunTest(test, test, false); \ | |
452 } | |
453 | |
454 // This macro simplifies setting up regular and reference build tests | |
455 // for HTML5 Indexed DB tests. | |
456 // FLAKY http://crbug.com/67918 | |
457 #define PAGE_CYCLER_IDB_TESTS(test, name) \ | |
458 TEST_F(PageCyclerIndexedDatabaseTest, FLAKY_IndexedDB##name##File) { \ | |
459 RunTest(test, test, false); \ | |
460 } \ | |
461 TEST_F(PageCyclerIndexedDatabaseReferenceTest, IndexedDB##name##File) { \ | |
462 RunTest(test, test, false); \ | |
463 } | |
464 | |
465 // These are shorthand for File vs. Http tests. | |
466 #define PAGE_CYCLER_FILE_TESTS(test, name) \ | |
467 PAGE_CYCLER_TESTS(test, name, false) | |
468 #define PAGE_CYCLER_HTTP_TESTS(test, name) \ | |
469 PAGE_CYCLER_TESTS(test, name, true) | |
470 | |
471 // This macro lets us define tests with 1 and 10 extensions with 1 content | |
472 // script each. The name for the 10-extension case is changed so as not | |
473 // to run by default on the buildbots. | |
474 #define PAGE_CYCLER_EXTENSIONS_FILE_TESTS(test, name) \ | |
475 TEST_F(PageCyclerExtensionTest, name) { \ | |
476 RunTest("times", "content_scripts1", "_extcs1", test, false); \ | |
477 } \ | |
478 TEST_F(PageCyclerExtensionTest, name##10) { \ | |
479 RunTest("times", "content_scripts10", "_extcs10", test, false); \ | |
480 } | |
481 | |
482 // This macro lets us define tests with an extension that listens to the | |
483 // webrequest.onBeforeRequest. It measures the effect that a blocking event | |
484 // for every request has on page cycle time. | |
485 #define PAGE_CYCLER_EXTENSIONS_WEBREQUEST_FILE_TESTS(test, name) \ | |
486 TEST_F(PageCyclerExtensionWebRequestTest, name) { \ | |
487 RunTest("times", "extension_webrequest", "_extwr", test, false); \ | |
488 } | |
489 | |
490 // file-URL tests | |
491 PAGE_CYCLER_FILE_TESTS("moz", MozFile); | |
492 PAGE_CYCLER_EXTENSIONS_FILE_TESTS("moz", MozFile); | |
493 PAGE_CYCLER_EXTENSIONS_WEBREQUEST_FILE_TESTS("moz", MozFile) | |
494 PAGE_CYCLER_FILE_TESTS("intl1", Intl1File); | |
495 PAGE_CYCLER_FILE_TESTS("intl2", Intl2File); | |
496 PAGE_CYCLER_EXTENSIONS_WEBREQUEST_FILE_TESTS("intl2", Intl2File); | |
497 PAGE_CYCLER_FILE_TESTS("dom", DomFile); | |
498 PAGE_CYCLER_FILE_TESTS("dhtml", DhtmlFile); | |
499 PAGE_CYCLER_FILE_TESTS("morejs", MorejsFile); | |
500 PAGE_CYCLER_EXTENSIONS_FILE_TESTS("morejs", MorejsFile); | |
501 // added more tests here: | |
502 PAGE_CYCLER_FILE_TESTS("alexa_us", Alexa_usFile); | |
503 PAGE_CYCLER_FILE_TESTS("moz2", Moz2File); | |
504 PAGE_CYCLER_FILE_TESTS("morejsnp", MorejsnpFile); | |
505 PAGE_CYCLER_FILE_TESTS("bloat", BloatFile); | |
506 | |
507 // http (localhost) tests | |
508 PAGE_CYCLER_HTTP_TESTS("moz", MozHttp); | |
509 PAGE_CYCLER_HTTP_TESTS("intl1", Intl1Http); | |
510 PAGE_CYCLER_HTTP_TESTS("intl2", Intl2Http); | |
511 PAGE_CYCLER_HTTP_TESTS("dom", DomHttp); | |
512 PAGE_CYCLER_HTTP_TESTS("bloat", BloatHttp); | |
513 | |
514 // HTML5 database tests | |
515 // These tests are _really_ slow on XP/Vista. | |
516 #if !defined(OS_WIN) | |
517 PAGE_CYCLER_DATABASE_TESTS("select-transactions", | |
518 SelectTransactions); | |
519 PAGE_CYCLER_DATABASE_TESTS("select-readtransactions", | |
520 SelectReadTransactions); | |
521 PAGE_CYCLER_DATABASE_TESTS("select-readtransactions-read-results", | |
522 SelectReadTransactionsReadResults); | |
523 PAGE_CYCLER_DATABASE_TESTS("insert-transactions", | |
524 InsertTransactions); | |
525 PAGE_CYCLER_DATABASE_TESTS("update-transactions", | |
526 UpdateTransactions); | |
527 PAGE_CYCLER_DATABASE_TESTS("delete-transactions", | |
528 DeleteTransactions); | |
529 PAGE_CYCLER_DATABASE_TESTS("pseudo-random-transactions", | |
530 PseudoRandomTransactions); | |
531 #endif | |
532 | |
533 // Indexed DB tests. | |
534 PAGE_CYCLER_IDB_TESTS("basic_insert", BasicInsert); | |
535 | |
536 } // namespace | |
OLD | NEW |