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 // This file provides reliablity test which runs under UI test framework. The | |
6 // test is intended to run within QEMU environment. | |
7 // | |
8 // Usage 1: reliability_test | |
9 // Upon invocation, it visits a hard coded list of sample URLs. This is mainly | |
10 // used by buildbot, to verify reliability_test itself runs ok. | |
11 // | |
12 // Usage 2: reliability_test --site=url --startpage=start --endpage=end [...] | |
13 // Upon invocation, it visits a list of URLs constructed as | |
14 // "http://url/page?id=k". (start <= k <= end). | |
15 // | |
16 // Usage 3: reliability_test --list=file --startline=start --endline=end [...] | |
17 // Upon invocation, it visits each of the URLs on line numbers between start | |
18 // and end, inclusive, stored in the input file. The line number starts from 1. | |
19 // | |
20 // If both "--site" and "--list" are provided, the "--site" set of arguments | |
21 // are ignored. | |
22 // | |
23 // Optional Switches: | |
24 // --iterations=num: goes through the list of URLs constructed in usage 2 or 3 | |
25 // num times. | |
26 // --continuousload: continuously visits the list of URLs without restarting | |
27 // browser for each page load. | |
28 // --memoryusage: prints out memory usage when visiting each page. | |
29 // --endurl=url: visits the specified url in the end. | |
30 // --logfile=filepath: saves the visit log to the specified path. | |
31 // --noclearprofile: do not clear profile dir before firing up each time. | |
32 // --savedebuglog: save Chrome, V8, and test debug log for each page loaded. | |
33 // --searchdumpsbypid: Look for crash dumps by browser process id. | |
34 // "crash_dir/pid/" | |
35 | |
36 #include <fstream> | |
37 #include <vector> | |
38 | |
39 #include "base/command_line.h" | |
40 #include "base/environment.h" | |
41 #include "base/file_util.h" | |
42 #include "base/file_version_info.h" | |
43 #include "base/files/file_enumerator.h" | |
44 #include "base/files/file_path.h" | |
45 #include "base/i18n/time_formatting.h" | |
46 #include "base/memory/scoped_ptr.h" | |
47 #include "base/path_service.h" | |
48 #include "base/prefs/json_pref_store.h" | |
49 #include "base/prefs/pref_registry_simple.h" | |
50 #include "base/prefs/pref_service.h" | |
51 #include "base/process/kill.h" | |
52 #include "base/strings/string_number_conversions.h" | |
53 #include "base/strings/string_util.h" | |
54 #include "base/test/test_file_util.h" | |
55 #include "base/threading/platform_thread.h" | |
56 #include "base/time/time.h" | |
57 #include "chrome/browser/prefs/pref_service_mock_builder.h" | |
58 #include "chrome/common/automation_messages.h" | |
59 #include "chrome/common/chrome_constants.h" | |
60 #include "chrome/common/chrome_paths.h" | |
61 #include "chrome/common/chrome_switches.h" | |
62 #include "chrome/common/chrome_version_info.h" | |
63 #include "chrome/common/logging_chrome.h" | |
64 #include "chrome/common/net/url_fixer_upper.h" | |
65 #include "chrome/common/pref_names.h" | |
66 #include "chrome/common/render_messages.h" | |
67 #include "chrome/common/url_constants.h" | |
68 #include "chrome/test/automation/automation_proxy.h" | |
69 #include "chrome/test/automation/browser_proxy.h" | |
70 #include "chrome/test/automation/tab_proxy.h" | |
71 #include "chrome/test/automation/window_proxy.h" | |
72 #include "chrome/test/ui/ui_test.h" | |
73 #include "net/base/net_util.h" | |
74 #include "v8/include/v8-testing.h" | |
75 | |
76 namespace { | |
77 | |
78 // See comments at the beginning of the file for the definition of switches. | |
79 const char kSiteSwitch[] = "site"; | |
80 const char kStartPageSwitch[] = "startpage"; | |
81 const char kEndPageSwitch[] = "endpage"; | |
82 const char kListSwitch[] = "list"; | |
83 const char kStartIndexSwitch[] = "startline"; | |
84 const char kEndIndexSwitch[] = "endline"; | |
85 const char kIterationSwitch[] = "iterations"; | |
86 const char kContinuousLoadSwitch[] = "continuousload"; | |
87 const char kMemoryUsageSwitch[] = "memoryusage"; | |
88 const char kEndURLSwitch[] = "endurl"; | |
89 const char kLogFileSwitch[] = "logfile"; | |
90 const char kNoPageDownSwitch[] = "nopagedown"; | |
91 const char kNoClearProfileSwitch[] = "noclearprofile"; | |
92 const char kSaveDebugLogSwitch[] = "savedebuglog"; | |
93 const char kStressOptSwitch[] = "stress-opt"; | |
94 const char kStressDeoptSwitch[] = "stress-deopt"; | |
95 | |
96 const char kDefaultServerUrl[] = "http://urllist.com"; | |
97 std::string g_server_url; | |
98 const char kTestPage1[] = "page1.html"; | |
99 const char kTestPage2[] = "page2.html"; | |
100 | |
101 // These are copied from v8 definitions as we cannot include them. | |
102 const char kV8LogFileSwitch[] = "logfile"; | |
103 const char kV8LogFileDefaultName[] = "v8.log"; | |
104 | |
105 // String name of local chrome dll for looking up file information. | |
106 const wchar_t kChromeDll[] = L"chrome.dll"; | |
107 | |
108 bool g_append_page_id = false; | |
109 int32 g_start_page; | |
110 int32 g_end_page; | |
111 base::FilePath g_url_file_path; | |
112 int32 g_start_index = 1; | |
113 int32 g_end_index = kint32max; | |
114 int32 g_iterations = 1; | |
115 bool g_memory_usage = false; | |
116 bool g_continuous_load = false; | |
117 bool g_browser_existing = false; | |
118 bool g_clear_profile = true; | |
119 std::string g_end_url; | |
120 base::FilePath g_log_file_path; | |
121 bool g_save_debug_log = false; | |
122 base::FilePath g_chrome_log_path; | |
123 base::FilePath g_v8_log_path; | |
124 base::FilePath g_test_log_path; | |
125 bool g_stand_alone = false; | |
126 bool g_stress_opt = false; | |
127 bool g_stress_deopt = false; | |
128 | |
129 void ReportHandler(const std::string& str) { | |
130 // Ignore report events. | |
131 } | |
132 | |
133 void SetPageRange(const CommandLine& parsed_command_line) { | |
134 // If calling into this function, we are running as a standalone program. | |
135 g_stand_alone = true; | |
136 | |
137 // Since we use --enable-dcheck for reliability tests, suppress the error | |
138 // dialog in the test process. | |
139 logging::SetLogReportHandler(ReportHandler); | |
140 | |
141 if (parsed_command_line.HasSwitch(kStartPageSwitch)) { | |
142 ASSERT_TRUE(parsed_command_line.HasSwitch(kEndPageSwitch)); | |
143 ASSERT_TRUE( | |
144 base::StringToInt(parsed_command_line.GetSwitchValueASCII( | |
145 kStartPageSwitch), | |
146 &g_start_page)); | |
147 ASSERT_TRUE( | |
148 base::StringToInt(parsed_command_line.GetSwitchValueASCII( | |
149 kEndPageSwitch), | |
150 &g_end_page)); | |
151 ASSERT_TRUE(g_start_page > 0 && g_end_page > 0); | |
152 ASSERT_TRUE(g_start_page < g_end_page); | |
153 g_append_page_id = true; | |
154 } else { | |
155 ASSERT_FALSE(parsed_command_line.HasSwitch(kEndPageSwitch)); | |
156 } | |
157 | |
158 if (parsed_command_line.HasSwitch(kSiteSwitch)) { | |
159 g_server_url = parsed_command_line.GetSwitchValueASCII(kSiteSwitch); | |
160 } | |
161 | |
162 if (parsed_command_line.HasSwitch(kStartIndexSwitch)) { | |
163 ASSERT_TRUE( | |
164 base::StringToInt(parsed_command_line.GetSwitchValueASCII( | |
165 kStartIndexSwitch), | |
166 &g_start_index)); | |
167 ASSERT_GT(g_start_index, 0); | |
168 } | |
169 | |
170 if (parsed_command_line.HasSwitch(kEndIndexSwitch)) { | |
171 ASSERT_TRUE( | |
172 base::StringToInt(parsed_command_line.GetSwitchValueASCII( | |
173 kEndIndexSwitch), | |
174 &g_end_index)); | |
175 ASSERT_GT(g_end_index, 0); | |
176 } | |
177 | |
178 ASSERT_TRUE(g_end_index >= g_start_index); | |
179 | |
180 if (parsed_command_line.HasSwitch(kListSwitch)) | |
181 g_url_file_path = parsed_command_line.GetSwitchValuePath(kListSwitch); | |
182 | |
183 if (parsed_command_line.HasSwitch(kIterationSwitch)) { | |
184 ASSERT_TRUE( | |
185 base::StringToInt(parsed_command_line.GetSwitchValueASCII( | |
186 kIterationSwitch), | |
187 &g_iterations)); | |
188 ASSERT_GT(g_iterations, 0); | |
189 } | |
190 | |
191 if (parsed_command_line.HasSwitch(kMemoryUsageSwitch)) | |
192 g_memory_usage = true; | |
193 | |
194 if (parsed_command_line.HasSwitch(kContinuousLoadSwitch)) | |
195 g_continuous_load = true; | |
196 | |
197 if (parsed_command_line.HasSwitch(kEndURLSwitch)) | |
198 g_end_url = parsed_command_line.GetSwitchValueASCII(kEndURLSwitch); | |
199 | |
200 if (parsed_command_line.HasSwitch(kLogFileSwitch)) | |
201 g_log_file_path = parsed_command_line.GetSwitchValuePath(kLogFileSwitch); | |
202 | |
203 if (parsed_command_line.HasSwitch(kNoClearProfileSwitch)) | |
204 g_clear_profile = false; | |
205 | |
206 if (parsed_command_line.HasSwitch(kSaveDebugLogSwitch)) { | |
207 g_save_debug_log = true; | |
208 g_chrome_log_path = logging::GetLogFileName(); | |
209 // We won't get v8 log unless --no-sandbox is specified. | |
210 if (parsed_command_line.HasSwitch(switches::kNoSandbox)) { | |
211 PathService::Get(base::DIR_CURRENT, &g_v8_log_path); | |
212 g_v8_log_path = g_v8_log_path.AppendASCII(kV8LogFileDefaultName); | |
213 // The command line switch may override the default v8 log path. | |
214 if (parsed_command_line.HasSwitch(switches::kJavaScriptFlags)) { | |
215 CommandLine v8_command_line( | |
216 parsed_command_line.GetSwitchValuePath(switches::kJavaScriptFlags)); | |
217 if (v8_command_line.HasSwitch(kV8LogFileSwitch)) { | |
218 g_v8_log_path = base::MakeAbsoluteFilePath( | |
219 v8_command_line.GetSwitchValuePath(kV8LogFileSwitch)); | |
220 } | |
221 } | |
222 } | |
223 } | |
224 | |
225 if (parsed_command_line.HasSwitch(kStressOptSwitch)) { | |
226 g_stress_opt = true; | |
227 } | |
228 if (parsed_command_line.HasSwitch(kStressDeoptSwitch)) { | |
229 g_stress_deopt = true; | |
230 } | |
231 } | |
232 | |
233 class PageLoadTest : public UITest { | |
234 public: | |
235 enum NavigationResult { | |
236 NAVIGATION_ERROR = 0, | |
237 NAVIGATION_SUCCESS, | |
238 NAVIGATION_AUTH_NEEDED, | |
239 NAVIGATION_TIME_OUT, | |
240 }; | |
241 | |
242 typedef struct { | |
243 // These are results from the test automation that drives Chrome | |
244 NavigationResult result; | |
245 int crash_dump_count; | |
246 // These are stability metrics recorded by Chrome itself | |
247 bool browser_clean_exit; | |
248 int browser_launch_count; | |
249 int page_load_count; | |
250 int browser_crash_count; | |
251 int renderer_crash_count; | |
252 int plugin_crash_count; | |
253 } NavigationMetrics; | |
254 | |
255 PageLoadTest() { | |
256 show_window_ = true; | |
257 SetPageRange(*CommandLine::ForCurrentProcess()); | |
258 } | |
259 | |
260 void EnsureBrowserAndServer() { | |
261 if (!g_browser_existing) { | |
262 LaunchBrowserAndServer(); | |
263 g_browser_existing = true; | |
264 } | |
265 } | |
266 | |
267 // Load a URL in a browser tab and perform a couple of page down events. | |
268 // url_string: The URL to navigate to. Accept URL as std::string here | |
269 // because the url may also act as a test id and needs to | |
270 // be logged in its original format even if invalid. | |
271 // log_file: Log file for test results and possible crash dump | |
272 // files. This file does not need to be opened in which | |
273 // case nothing is logged. | |
274 // metrics_output: Return metrics for the page load. | |
275 // keep_browser: Set to true if the browser should be kept open after | |
276 // loading the page. | |
277 // log_only_errors: Set to true if only errors should be logged otherwise | |
278 // successful navigations will also be logged. | |
279 bool NavigateToURLLogResult(const std::string& url_string, | |
280 std::ofstream& log_file, | |
281 NavigationMetrics* metrics_output, | |
282 bool keep_browser, | |
283 bool log_only_error) { | |
284 GURL url(url_string); | |
285 NavigationMetrics metrics = {NAVIGATION_ERROR}; | |
286 std::ofstream test_log; | |
287 | |
288 // Create a test log. | |
289 g_test_log_path = base::FilePath(FILE_PATH_LITERAL("test_log.log")); | |
290 test_log.open(g_test_log_path.value().c_str()); | |
291 | |
292 // Get the version of Chrome we're running. | |
293 std::string last_change; | |
294 #if defined(OS_WIN) | |
295 // Check file version info for chrome dll. | |
296 scoped_ptr<FileVersionInfo> file_info; | |
297 file_info.reset( | |
298 FileVersionInfo::CreateFileVersionInfo(base::FilePath(kChromeDll))); | |
299 last_change = WideToASCII(file_info->last_change()); | |
300 #elif defined(OS_POSIX) | |
301 // TODO(fmeawad): On Mac, the version retrieved here belongs to the test | |
302 // module and not the chrome binary, need to be changed to chrome binary | |
303 // instead. | |
304 chrome::VersionInfo version_info; | |
305 last_change = version_info.LastChange(); | |
306 #endif // !defined(OS_WIN) | |
307 test_log << "Last Change: "; | |
308 test_log << last_change << std::endl; | |
309 | |
310 // Log timestamp for test start. | |
311 base::Time time_now = base::Time::Now(); | |
312 double time_start = time_now.ToDoubleT(); | |
313 test_log << "Test Start: "; | |
314 test_log << base::TimeFormatFriendlyDateAndTime(time_now) << std::endl; | |
315 | |
316 // Make sure the browser is running. | |
317 EnsureBrowserAndServer(); | |
318 | |
319 // Log Browser Launched time. | |
320 time_now = base::Time::Now(); | |
321 test_log << "browser_launched_seconds="; | |
322 test_log << (time_now.ToDoubleT() - time_start) << std::endl; | |
323 | |
324 int result = AUTOMATION_MSG_NAVIGATION_ERROR; | |
325 // This is essentially what NavigateToURL does except we don't fire | |
326 // assertion when page loading fails. We log the result instead. | |
327 { | |
328 // TabProxy should be released before Browser is closed. | |
329 scoped_refptr<TabProxy> tab_proxy(GetActiveTab()); | |
330 if (tab_proxy.get()) | |
331 result = tab_proxy->NavigateToURL(url); | |
332 } | |
333 | |
334 // Log navigate complete time. | |
335 time_now = base::Time::Now(); | |
336 test_log << "navigate_complete_seconds="; | |
337 test_log << (time_now.ToDoubleT() - time_start) << std::endl; | |
338 | |
339 if (!keep_browser) { | |
340 CloseBrowserAndServer(); | |
341 g_browser_existing = false; | |
342 } | |
343 | |
344 // Log end of test time. | |
345 time_now = base::Time::Now(); | |
346 test_log << "total_duration_seconds="; | |
347 test_log << (time_now.ToDoubleT() - time_start) << std::endl; | |
348 | |
349 // Get navigation result and metrics, and optionally write to the log file | |
350 // provided. The log format is: | |
351 // <url> <navigation_result> <browser_crash_count> <renderer_crash_count> | |
352 // <plugin_crash_count> <crash_dump_count> [chrome_log=<path> | |
353 // v8_log=<path>] crash_dump=<path> | |
354 switch (result) { | |
355 case AUTOMATION_MSG_NAVIGATION_ERROR: | |
356 metrics.result = NAVIGATION_ERROR; | |
357 break; | |
358 case AUTOMATION_MSG_NAVIGATION_SUCCESS: | |
359 metrics.result = NAVIGATION_SUCCESS; | |
360 break; | |
361 case AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED: | |
362 metrics.result = NAVIGATION_AUTH_NEEDED; | |
363 break; | |
364 default: | |
365 metrics.result = NAVIGATION_ERROR; | |
366 break; | |
367 } | |
368 | |
369 // Get crash dumps - don't delete them if logging. | |
370 std::vector<base::FilePath> new_crash_dumps; | |
371 CollectNewCrashDumps(new_crash_dumps, &metrics, !log_file.is_open()); | |
372 | |
373 bool do_log = log_file.is_open() && | |
374 (!log_only_error || | |
375 metrics.result != NAVIGATION_SUCCESS || | |
376 !new_crash_dumps.empty()); | |
377 if (do_log) { | |
378 log_file << url_string; | |
379 switch (metrics.result) { | |
380 case NAVIGATION_ERROR: | |
381 log_file << " error"; | |
382 break; | |
383 case NAVIGATION_SUCCESS: | |
384 log_file << " success"; | |
385 break; | |
386 case NAVIGATION_AUTH_NEEDED: | |
387 log_file << " auth_needed"; | |
388 break; | |
389 case NAVIGATION_TIME_OUT: | |
390 log_file << " timeout"; | |
391 break; | |
392 default: | |
393 break; | |
394 } | |
395 } | |
396 | |
397 #if !defined(OS_MACOSX) // Not used by mac chromebot. | |
398 // Get stability metrics recorded by Chrome itself. | |
399 GetStabilityMetrics(&metrics); | |
400 #endif | |
401 | |
402 if (do_log) { | |
403 log_file << " " << metrics.browser_crash_count \ | |
404 // The renderer crash count is flaky due to 1183283. | |
405 // Ignore the count since we also catch crash by | |
406 // crash_dump_count. | |
407 << " " << 0 \ | |
408 << " " << metrics.plugin_crash_count \ | |
409 << " " << metrics.crash_dump_count; | |
410 } | |
411 | |
412 // Close test log. | |
413 test_log.close(); | |
414 | |
415 if (do_log && g_save_debug_log && !g_continuous_load) | |
416 SaveDebugLogs(log_file); | |
417 | |
418 // Log revision information for Chrome build under test. | |
419 if (do_log) | |
420 log_file << " " << "revision=" << last_change; | |
421 | |
422 if (do_log) { | |
423 for (size_t i = 0; i < new_crash_dumps.size(); i++) | |
424 log_file << " crash_dump=" << new_crash_dumps[i].value().c_str(); | |
425 } | |
426 | |
427 if (do_log) | |
428 log_file << std::endl; | |
429 | |
430 if (metrics_output) | |
431 *metrics_output = metrics; | |
432 | |
433 return do_log; | |
434 } | |
435 | |
436 void NavigateThroughPageID(std::ofstream& log_file) { | |
437 if (g_append_page_id) { | |
438 // For usage 2 | |
439 for (int i = g_start_page; i <= g_end_page; ++i) { | |
440 const char* server = g_server_url.empty() ? kDefaultServerUrl : | |
441 g_server_url.c_str(); | |
442 std::string test_page_url( | |
443 base::StringPrintf("%s/page?id=%d", server, i)); | |
444 NavigateToURLLogResult( | |
445 test_page_url, log_file, NULL, g_continuous_load, false); | |
446 } | |
447 } else { | |
448 // Don't run if running as a standalone program which is for distributed | |
449 // testing, to avoid mistakenly hitting web sites with many instances. | |
450 if (g_stand_alone) | |
451 return; | |
452 // For usage 1 | |
453 NavigationMetrics metrics; | |
454 | |
455 base::FilePath sample_data_dir = GetSampleDataDir(); | |
456 base::FilePath test_page_1 = sample_data_dir.AppendASCII(kTestPage1); | |
457 base::FilePath test_page_2 = sample_data_dir.AppendASCII(kTestPage2); | |
458 | |
459 GURL test_url_1 = net::FilePathToFileURL(test_page_1); | |
460 GURL test_url_2 = net::FilePathToFileURL(test_page_2); | |
461 | |
462 // Convert back to string so that all calls to navigate are the same. | |
463 const std::string test_url_1_string = test_url_1.spec(); | |
464 const std::string test_url_2_string = test_url_2.spec(); | |
465 | |
466 NavigateToURLLogResult( | |
467 test_url_1_string, log_file, &metrics, g_continuous_load, false); | |
468 // Verify everything is fine | |
469 EXPECT_EQ(NAVIGATION_SUCCESS, metrics.result); | |
470 EXPECT_EQ(0, metrics.crash_dump_count); | |
471 EXPECT_TRUE(metrics.browser_clean_exit); | |
472 EXPECT_EQ(1, metrics.browser_launch_count); | |
473 // Both starting page and test_url_1 are loaded. | |
474 EXPECT_EQ(2, metrics.page_load_count); | |
475 EXPECT_EQ(0, metrics.browser_crash_count); | |
476 EXPECT_EQ(0, metrics.renderer_crash_count); | |
477 EXPECT_EQ(0, metrics.plugin_crash_count); | |
478 | |
479 // Go to "about:crash" | |
480 NavigateToURLLogResult(content::kChromeUICrashURL, | |
481 log_file, | |
482 &metrics, | |
483 g_continuous_load, | |
484 false); | |
485 // Found a crash dump | |
486 EXPECT_EQ(1, metrics.crash_dump_count) << kFailedNoCrashService; | |
487 // Browser did not crash, and exited cleanly. | |
488 EXPECT_TRUE(metrics.browser_clean_exit); | |
489 EXPECT_EQ(1, metrics.browser_launch_count); | |
490 // Only the renderer should have crashed. | |
491 EXPECT_EQ(0, metrics.browser_crash_count); | |
492 EXPECT_EQ(1, metrics.renderer_crash_count); | |
493 EXPECT_EQ(0, metrics.plugin_crash_count); | |
494 | |
495 NavigateToURLLogResult( | |
496 test_url_2_string, log_file, &metrics, g_continuous_load, false); | |
497 // The data on previous crash should be cleared and we should get | |
498 // metrics for a successful page load. | |
499 EXPECT_EQ(NAVIGATION_SUCCESS, metrics.result); | |
500 EXPECT_EQ(0, metrics.crash_dump_count); | |
501 EXPECT_TRUE(metrics.browser_clean_exit); | |
502 EXPECT_EQ(1, metrics.browser_launch_count); | |
503 EXPECT_EQ(0, metrics.browser_crash_count); | |
504 EXPECT_EQ(0, metrics.renderer_crash_count); | |
505 EXPECT_EQ(0, metrics.plugin_crash_count); | |
506 | |
507 // Verify metrics service does what we need when browser process crashes. | |
508 LaunchBrowserAndServer(); | |
509 { | |
510 // TabProxy should be released before Browser is closed. | |
511 scoped_refptr<TabProxy> tab_proxy(GetActiveTab()); | |
512 EXPECT_TRUE(tab_proxy.get()); | |
513 if (tab_proxy.get()) { | |
514 EXPECT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, | |
515 tab_proxy->NavigateToURL(GURL(test_url_1))); | |
516 } | |
517 } | |
518 // Kill browser process. | |
519 base::ProcessHandle browser_process = process(); | |
520 base::KillProcess(browser_process, 0, false); | |
521 | |
522 GetStabilityMetrics(&metrics); | |
523 // This is not a clean shutdown. | |
524 EXPECT_FALSE(metrics.browser_clean_exit); | |
525 EXPECT_EQ(1, metrics.browser_crash_count); | |
526 EXPECT_EQ(0, metrics.renderer_crash_count); | |
527 EXPECT_EQ(0, metrics.plugin_crash_count); | |
528 // Relaunch browser so UITest does not fire assertion during TearDown. | |
529 LaunchBrowserAndServer(); | |
530 } | |
531 } | |
532 | |
533 // For usage 3 | |
534 void NavigateThroughURLList(std::ofstream& log_file) { | |
535 std::ifstream file(g_url_file_path.value().c_str()); | |
536 ASSERT_TRUE(file.is_open()); | |
537 | |
538 for (int line_index = 1; | |
539 line_index <= g_end_index && !file.eof(); | |
540 ++line_index) { | |
541 std::string url_str; | |
542 std::getline(file, url_str); | |
543 | |
544 if (file.fail()) | |
545 break; | |
546 | |
547 if (g_start_index <= line_index) { | |
548 if (g_stress_opt || g_stress_deopt) { | |
549 // Make sure the browser is running to communicate the stress | |
550 // setting. | |
551 EnsureBrowserAndServer(); | |
552 v8::Testing::StressType stress_type = | |
553 g_stress_opt | |
554 ? v8::Testing::kStressTypeOpt | |
555 : v8::Testing::kStressTypeDeopt; | |
556 scoped_refptr<TabProxy> tab_proxy(GetActiveTab()); | |
557 if (tab_proxy.get()) { | |
558 tab_proxy->JavaScriptStressTestControl( | |
559 kJavaScriptStressTestSetStressRunType, stress_type); | |
560 } | |
561 | |
562 bool success = true; | |
563 // Load each page a number of times and keep the same browser open | |
564 // for these loads. This loop will end if an error is encountered | |
565 // during one of the loads and the error logged. | |
566 for (int i = 0; | |
567 i < v8::Testing::GetStressRuns() && success; | |
568 i++) { | |
569 bool last_load = (i == (v8::Testing::GetStressRuns() - 1)); | |
570 bool keep_browser = !last_load || g_continuous_load; | |
571 bool log_only_error = !last_load; | |
572 NavigationMetrics metrics; | |
573 | |
574 scoped_refptr<TabProxy> tab_proxy(GetActiveTab()); | |
575 if (tab_proxy.get()) { | |
576 tab_proxy->JavaScriptStressTestControl( | |
577 kJavaScriptStressTestPrepareStressRun, i); | |
578 } | |
579 bool did_log_error; | |
580 did_log_error = NavigateToURLLogResult(url_str, | |
581 log_file, | |
582 &metrics, | |
583 keep_browser, | |
584 log_only_error); | |
585 success = metrics.result == NAVIGATION_SUCCESS && !did_log_error; | |
586 } | |
587 } else { | |
588 NavigateToURLLogResult( | |
589 url_str, log_file, NULL, g_continuous_load, false); | |
590 } | |
591 } | |
592 } | |
593 | |
594 file.close(); | |
595 } | |
596 | |
597 protected: | |
598 // Call the base class's SetUp method and initialize our own class members. | |
599 virtual void SetUp() { | |
600 // Set UI Test members before setting up browser. | |
601 clear_profile_ = g_clear_profile; | |
602 | |
603 UITest::SetUp(); | |
604 g_browser_existing = true; | |
605 | |
606 // If 'BREAKPAD_DUMP_LOCATION' environment variable is set, use it instead. | |
607 scoped_ptr<base::Environment> env(base::Environment::Create()); | |
608 std::string alternate_minidump_location; | |
609 if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_minidump_location)) { | |
610 crash_dumps_dir_path_ = base::FilePath::FromUTF8Unsafe( | |
611 alternate_minidump_location); | |
612 } else { | |
613 PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dumps_dir_path_); | |
614 } | |
615 | |
616 base::FileEnumerator enumerator(crash_dumps_dir_path_, | |
617 false, // not recursive | |
618 base::FileEnumerator::FILES); | |
619 for (base::FilePath path = enumerator.Next(); !path.value().empty(); | |
620 path = enumerator.Next()) { | |
621 if (path.MatchesExtension(FILE_PATH_LITERAL(".dmp"))) | |
622 crash_dumps_[path.BaseName()] = true; | |
623 } | |
624 } | |
625 | |
626 base::FilePath ConstructSavedDebugLogPath( | |
627 const base::FilePath& debug_log_path, | |
628 int index) { | |
629 std::string suffix("_"); | |
630 suffix.append(base::IntToString(index)); | |
631 return debug_log_path.InsertBeforeExtensionASCII(suffix); | |
632 } | |
633 | |
634 void SaveDebugLog(const base::FilePath& log_path, const std::wstring& log_id, | |
635 std::ofstream& log_file, int index) { | |
636 if (!log_path.empty()) { | |
637 base::FilePath saved_log_file_path = | |
638 ConstructSavedDebugLogPath(log_path, index); | |
639 if (base::Move(log_path, saved_log_file_path)) { | |
640 log_file << " " << log_id << "=" << saved_log_file_path.value(); | |
641 } | |
642 } | |
643 } | |
644 | |
645 // Rename the chrome and v8 debug log files if existing, and save the file | |
646 // paths in the log_file provided. | |
647 void SaveDebugLogs(std::ofstream& log_file) { | |
648 static int url_count = 1; | |
649 SaveDebugLog(g_chrome_log_path, L"chrome_log", log_file, url_count); | |
650 SaveDebugLog(g_v8_log_path, L"v8_log", log_file, url_count); | |
651 SaveDebugLog(g_test_log_path, L"test_log", log_file, url_count); | |
652 url_count++; | |
653 } | |
654 | |
655 // Delete a crash dump file. | |
656 void DeleteCrashDump(base::FilePath crash_dump_file_name) { | |
657 base::FilePath crash_dump_file_path(crash_dumps_dir_path_); | |
658 crash_dump_file_path = crash_dump_file_path.Append(crash_dump_file_name); | |
659 base::FilePath crash_text_file_path = | |
660 crash_dump_file_path.ReplaceExtension(FILE_PATH_LITERAL("txt")); | |
661 | |
662 ASSERT_TRUE(file_util::DieFileDie(crash_dump_file_path, false)); | |
663 ASSERT_TRUE(file_util::DieFileDie(crash_text_file_path, false)); | |
664 } | |
665 | |
666 bool HasNewCrashDumps() { | |
667 base::FileEnumerator enumerator(crash_dumps_dir_path_, | |
668 false, // not recursive | |
669 base::FileEnumerator::FILES); | |
670 for (base::FilePath path = enumerator.Next(); !path.value().empty(); | |
671 path = enumerator.Next()) { | |
672 if (path.MatchesExtension(FILE_PATH_LITERAL(".dmp")) && | |
673 !crash_dumps_[path.BaseName()]) { | |
674 return true; | |
675 } | |
676 } | |
677 | |
678 return false; | |
679 } | |
680 | |
681 // Check whether there are new .dmp files. Return the list and optionally | |
682 // delete them afterwards. | |
683 void CollectNewCrashDumps(std::vector<base::FilePath>& new_crash_dumps, | |
684 NavigationMetrics* metrics, | |
685 bool delete_dumps) { | |
686 int num_dumps = 0; | |
687 base::FileEnumerator enumerator(crash_dumps_dir_path_, | |
688 false, // not recursive | |
689 base::FileEnumerator::FILES); | |
690 for (base::FilePath path = enumerator.Next(); !path.value().empty(); | |
691 path = enumerator.Next()) { | |
692 if (path.MatchesExtension(FILE_PATH_LITERAL(".dmp")) && | |
693 !crash_dumps_[path.BaseName()]) { | |
694 crash_dumps_[path.BaseName()] = true; | |
695 base::FilePath crash_dump_file_path(crash_dumps_dir_path_); | |
696 crash_dump_file_path = crash_dump_file_path.Append(path.BaseName()); | |
697 new_crash_dumps.push_back(crash_dump_file_path); | |
698 if (delete_dumps) | |
699 DeleteCrashDump(path.BaseName()); | |
700 num_dumps++; | |
701 } | |
702 } | |
703 if (metrics) | |
704 metrics->crash_dump_count = num_dumps; | |
705 } | |
706 | |
707 // Get a PrefService whose contents correspond to the Local State file | |
708 // that was saved by the app as it closed. The caller takes ownership of the | |
709 // returned PrefService object. | |
710 PrefService* GetLocalState(PrefRegistry* registry) { | |
711 base::FilePath path = user_data_dir().Append(chrome::kLocalStateFilename); | |
712 PrefServiceMockBuilder builder; | |
713 builder.WithUserFilePrefs( | |
714 path, base::MessageLoop::current()->message_loop_proxy().get()); | |
715 return builder.Create(registry); | |
716 } | |
717 | |
718 void GetStabilityMetrics(NavigationMetrics* metrics) { | |
719 if (!metrics) | |
720 return; | |
721 scoped_refptr<PrefRegistrySimple> registry = new PrefRegistrySimple(); | |
722 registry->RegisterBooleanPref(prefs::kStabilityExitedCleanly, false); | |
723 registry->RegisterIntegerPref(prefs::kStabilityLaunchCount, -1); | |
724 registry->RegisterIntegerPref(prefs::kStabilityPageLoadCount, -1); | |
725 registry->RegisterIntegerPref(prefs::kStabilityCrashCount, 0); | |
726 registry->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0); | |
727 | |
728 scoped_ptr<PrefService> local_state(GetLocalState(registry.get())); | |
729 if (!local_state.get()) | |
730 return; | |
731 | |
732 metrics->browser_clean_exit = | |
733 local_state->GetBoolean(prefs::kStabilityExitedCleanly); | |
734 metrics->browser_launch_count = | |
735 local_state->GetInteger(prefs::kStabilityLaunchCount); | |
736 metrics->page_load_count = | |
737 local_state->GetInteger(prefs::kStabilityPageLoadCount); | |
738 metrics->browser_crash_count = | |
739 local_state->GetInteger(prefs::kStabilityCrashCount); | |
740 metrics->renderer_crash_count = | |
741 local_state->GetInteger(prefs::kStabilityRendererCrashCount); | |
742 // TODO(huanr) | |
743 metrics->plugin_crash_count = 0; | |
744 | |
745 if (!metrics->browser_clean_exit) | |
746 metrics->browser_crash_count++; | |
747 } | |
748 | |
749 base::FilePath GetSampleDataDir() { | |
750 base::FilePath test_dir; | |
751 PathService::Get(chrome::DIR_TEST_DATA, &test_dir); | |
752 test_dir = test_dir.AppendASCII("reliability"); | |
753 test_dir = test_dir.AppendASCII("sample_pages"); | |
754 return test_dir; | |
755 } | |
756 | |
757 // The pathname of Chrome's crash dumps directory. | |
758 base::FilePath crash_dumps_dir_path_; | |
759 | |
760 // The set of all the crash dumps we have seen. Each crash generates a | |
761 // .dmp and a .txt file in the crash dumps directory. We only store the | |
762 // .dmp files in this set. | |
763 // | |
764 // The set is implemented as a std::map. The key is the file name, and | |
765 // the value is false (the file is not in the set) or true (the file is | |
766 // in the set). The initial value for any key in std::map is 0 (false), | |
767 // which in this case means a new file is not in the set initially, | |
768 // exactly the semantics we want. | |
769 std::map<base::FilePath, bool> crash_dumps_; | |
770 }; | |
771 | |
772 TEST_F(PageLoadTest, Reliability) { | |
773 std::ofstream log_file; | |
774 | |
775 if (!g_log_file_path.empty()) { | |
776 log_file.open(g_log_file_path.value().c_str()); | |
777 } | |
778 | |
779 for (int k = 0; k < g_iterations; ++k) { | |
780 if (g_url_file_path.empty()) { | |
781 NavigateThroughPageID(log_file); | |
782 } else { | |
783 NavigateThroughURLList(log_file); | |
784 } | |
785 } | |
786 | |
787 if (!g_end_url.empty()) { | |
788 NavigateToURLLogResult( | |
789 g_end_url, log_file, NULL, g_continuous_load, false); | |
790 } | |
791 | |
792 log_file.close(); | |
793 } | |
794 | |
795 } // namespace | |
796 | |
OLD | NEW |