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 <fstream> 6 #include <string> 7 #include <vector> 8 9 #include "base/command_line.h" 10 #include "base/environment.h" 11 #include "base/file_util.h" 12 #include "base/file_version_info.h" 13 #include "base/files/file_enumerator.h" 14 #include "base/files/file_path.h" 15 #include "base/i18n/time_formatting.h" 16 #include "base/logging.h" 17 #include "base/path_service.h" 18 #include "base/rand_util.h" 19 #include "base/strings/string_number_conversions.h" 20 #include "base/strings/string_split.h" 21 #include "base/strings/string_util.h" 22 #include "base/strings/stringprintf.h" 23 #include "base/threading/platform_thread.h" 24 #include "base/time/time.h" 25 #include "chrome/app/chrome_command_ids.h" 26 #include "chrome/browser/browser_process.h" 27 #include "chrome/browser/character_encoding.h" 28 #include "chrome/browser/ui/view_ids.h" 29 #include "chrome/common/chrome_paths.h" 30 #include "chrome/common/chrome_version_info.h" 31 #include "chrome/common/env_vars.h" 32 #include "chrome/common/url_constants.h" 33 #include "chrome/test/automation/automation_proxy.h" 34 #include "chrome/test/automation/browser_proxy.h" 35 #include "chrome/test/automation/tab_proxy.h" 36 #include "chrome/test/automation/window_proxy.h" 37 #include "chrome/test/reliability/automated_ui_tests.h" 38 #include "chrome/test/ui/ui_test.h" 39 #include "ui/base/keycodes/keyboard_codes.h" 40 #include "url/gurl.h" 41 42 #if defined(TOOLKIT_VIEWS) 43 #include "ui/views/view.h" 44 #endif 45 46 namespace { 47 48 const char kReproSwitch[] = "key"; 49 50 const char kReproRepeatSwitch[] = "num-reproductions"; 51 52 const char kInputFilePathSwitch[] = "input"; 53 54 const char kOutputFilePathSwitch[] = "output"; 55 56 const char kDebugModeSwitch[] = "debug"; 57 58 const char kWaitSwitch[] = "wait-after-action"; 59 60 const char kTestLogFilePathSwitch[] = "testlog"; 61 62 const base::FilePath::CharType* const kDefaultInputFilePath = 63 FILE_PATH_LITERAL("automated_ui_tests.txt"); 64 65 const base::FilePath::CharType* const kDefaultOutputFilePath = 66 FILE_PATH_LITERAL("automated_ui_tests_error_report.txt"); 67 68 const base::FilePath::CharType* const kDefaultTestLogFilePath = 69 FILE_PATH_LITERAL("automated_ui_tests_log.txt"); 70 71 const int kDebuggingTimeoutMsec = 5000; 72 73 // How many commands to run when testing a dialog box. 74 const int kTestDialogActionsToRun = 7; 75 76 // String name of local chrome dll for looking up file information. 77 const wchar_t kChromeDll[] = L"chrome.dll"; 78 79 void SilentRuntimeReportHandler(const std::string& str) { 80 } 81 82 base::FilePath GetInputFilePath() { 83 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); 84 if (parsed_command_line.HasSwitch(kInputFilePathSwitch)) { 85 return parsed_command_line.GetSwitchValuePath(kInputFilePathSwitch); 86 } else { 87 return base::FilePath(kDefaultInputFilePath); 88 } 89 } 90 91 base::FilePath GetOutputFilePath() { 92 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); 93 if (parsed_command_line.HasSwitch(kOutputFilePathSwitch)) { 94 return parsed_command_line.GetSwitchValuePath(kOutputFilePathSwitch); 95 } else { 96 return base::FilePath(kDefaultOutputFilePath); 97 } 98 } 99 100 base::FilePath GetTestLogFilePath() { 101 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); 102 if (parsed_command_line.HasSwitch(kTestLogFilePathSwitch)) { 103 return parsed_command_line.GetSwitchValuePath(kTestLogFilePathSwitch); 104 } else { 105 return base::FilePath(kDefaultTestLogFilePath); 106 } 107 } 108 109 std::string GetChromeRevision() { 110 // Get Chrome version 111 std::string last_change; 112 #if defined(OS_WIN) 113 // Check file version info for chrome dll. 114 scoped_ptr<FileVersionInfo> file_info; 115 file_info.reset( 116 FileVersionInfo::CreateFileVersionInfo(base::FilePath(kChromeDll))); 117 last_change = WideToASCII(file_info->last_change()); 118 #elif defined(OS_POSIX) 119 chrome::VersionInfo version_info; 120 last_change = version_info.LastChange(); 121 #endif // !defined(OS_WIN) 122 return last_change; 123 } 124 125 void InitTestLog(base::Time start_time) { 126 base::FilePath path = GetTestLogFilePath(); 127 std::ofstream test_log_file; 128 if (!path.empty()) 129 test_log_file.open(path.value().c_str(), std::ios::out); 130 131 const std::string time = 132 UTF16ToASCII(base::TimeFormatFriendlyDateAndTime(start_time)); 133 134 test_log_file << "Last Change: " << GetChromeRevision() << std::endl; 135 test_log_file << "Test Start: " << time << std::endl; 136 test_log_file.close(); 137 } 138 139 void AppendToTestLog(const std::string& append_string) { 140 base::FilePath path = GetTestLogFilePath(); 141 std::ofstream test_log_file; 142 if (!path.empty()) { 143 test_log_file.open(path.value().c_str(), 144 std::ios::out | std::ios_base::app); 145 } 146 147 test_log_file << append_string << std::endl; 148 test_log_file.close(); 149 } 150 151 double CalculateTestDuration(base::Time start_time) { 152 base::Time time_now = base::Time::Now(); 153 return time_now.ToDoubleT() - start_time.ToDoubleT(); 154 } 155 156 } // namespace 157 158 // This subset of commands is used to test dialog boxes, which aren't likely 159 // to respond to most other commands. 160 const std::string kTestDialogPossibleActions[] = { 161 // See FuzzyTestDialog for details on why Enter and SpaceBar must appear first 162 // in this list. 163 "PressEnterKey", 164 "PressSpaceBar", 165 "PressTabKey", 166 "DownArrow" 167 }; 168 169 // The list of dialogs that can be shown. 170 const std::string kDialogs[] = { 171 "About", 172 "Options", 173 "TaskManager", 174 "JavaScriptConsole", 175 "ClearBrowsingData", 176 "ImportSettings", 177 "EditSearchEngines", 178 "ViewPasswords" 179 }; 180 181 AutomatedUITest::AutomatedUITest() 182 : test_start_time_(base::Time::NowFromSystemTime()), 183 total_crashes_(0), 184 debug_logging_enabled_(false), 185 post_action_delay_(0) { 186 show_window_ = true; 187 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); 188 if (parsed_command_line.HasSwitch(kDebugModeSwitch)) 189 debug_logging_enabled_ = true; 190 if (parsed_command_line.HasSwitch(kWaitSwitch)) { 191 std::string str = parsed_command_line.GetSwitchValueASCII(kWaitSwitch); 192 if (str.empty()) { 193 post_action_delay_ = 1; 194 } else { 195 base::StringToInt(str, &post_action_delay_); 196 } 197 } 198 scoped_ptr<base::Environment> env(base::Environment::Create()); 199 if (env->HasVar(env_vars::kHeadless)) 200 logging::SetLogReportHandler(SilentRuntimeReportHandler); 201 } 202 203 AutomatedUITest::~AutomatedUITest() {} 204 205 void AutomatedUITest::RunReproduction() { 206 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); 207 208 InitTestLog(test_start_time_); 209 xml_writer_.StartWriting(); 210 xml_writer_.StartElement("Report"); 211 std::string action_string = 212 parsed_command_line.GetSwitchValueASCII(kReproSwitch); 213 214 int64 num_reproductions = 1; 215 if (parsed_command_line.HasSwitch(kReproRepeatSwitch)) { 216 base::StringToInt64( 217 parsed_command_line.GetSwitchValueASCII(kReproRepeatSwitch), 218 &num_reproductions); 219 } 220 std::vector<std::string> actions; 221 base::SplitString(action_string, ',', &actions); 222 bool did_crash = false; 223 bool command_complete = false; 224 225 for (int64 i = 0; i < num_reproductions && !did_crash; ++i) { 226 bool did_teardown = false; 227 xml_writer_.StartElement("Executed"); 228 for (size_t j = 0; j < actions.size(); ++j) { 229 DoAction(actions[j]); 230 if (DidCrash(true)) { 231 did_crash = true; 232 if (j >= (actions.size() - 1)) 233 command_complete = true; 234 break; 235 } 236 if (LowerCaseEqualsASCII(actions[j], "teardown")) 237 did_teardown = true; 238 } 239 240 // Force proper teardown after each run, if it didn't already happen. But 241 // don't teardown after crashes. 242 if (!did_teardown && !did_crash) 243 DoAction("TearDown"); 244 245 xml_writer_.EndElement(); // End "Executed" element. 246 } 247 248 if (did_crash) { 249 base::FilePath crash_dump = GetMostRecentCrashDump(); 250 base::FilePath::StringType result = 251 FILE_PATH_LITERAL("*** Crash dump produced. ") 252 FILE_PATH_LITERAL("See result file for more details. Dump = "); 253 result.append(crash_dump.value()); 254 result.append(FILE_PATH_LITERAL(" ***\n")); 255 printf("%s", result.c_str()); 256 LogCrashResult(crash_dump, command_complete); 257 EXPECT_TRUE(false) << "Crash detected."; 258 } else { 259 printf("*** No crashes. See result file for more details. ***\n"); 260 LogSuccessResult(); 261 } 262 263 AppendToTestLog(base::StringPrintf("total_duration_seconds=%f", 264 CalculateTestDuration(test_start_time_))); 265 WriteReportToFile(); 266 } 267 268 269 void AutomatedUITest::RunAutomatedUITest() { 270 InitTestLog(test_start_time_); 271 272 ASSERT_TRUE(InitXMLReader()) << "Error initializing XMLReader"; 273 xml_writer_.StartWriting(); 274 xml_writer_.StartElement("Report"); 275 276 while (init_reader_.Read()) { 277 init_reader_.SkipToElement(); 278 std::string node_name = init_reader_.NodeName(); 279 if (LowerCaseEqualsASCII(node_name, "command")) { 280 bool no_errors = true; 281 xml_writer_.StartElement("Executed"); 282 std::string command_number; 283 if (init_reader_.NodeAttribute("number", &command_number)) { 284 xml_writer_.AddAttribute("command_number", command_number); 285 } 286 xml_writer_.StopIndenting(); 287 288 // Starts the browser, logging it as an action. 289 DoAction("SetUp"); 290 291 // Record the depth of the root of the command subtree, then advance to 292 // the first element in preparation for parsing. 293 int start_depth = init_reader_.Depth(); 294 ASSERT_TRUE(init_reader_.Read()) << "Malformed XML file."; 295 init_reader_.SkipToElement(); 296 297 // Check for a crash right after startup. 298 if (DidCrash(true)) { 299 LogCrashResult(GetMostRecentCrashDump(), false); 300 // Try and start up again. 301 CloseBrowserAndServer(); 302 LaunchBrowserAndServer(); 303 set_active_browser(automation()->GetBrowserWindow(0).get()); 304 if (DidCrash(true)) { 305 no_errors = false; 306 // We crashed again, so skip to the end of the this command. 307 while (init_reader_.Depth() != start_depth) { 308 ASSERT_TRUE(init_reader_.Read()) << "Malformed XML file."; 309 } 310 } else { 311 // We didn't crash, so end the old element, logging a crash for that. 312 // Then start a new element to log this command. 313 xml_writer_.StartIndenting(); 314 xml_writer_.EndElement(); 315 xml_writer_.StartElement("Executed"); 316 xml_writer_.AddAttribute("command_number", command_number); 317 xml_writer_.StopIndenting(); 318 xml_writer_.StartElement("SetUp"); 319 xml_writer_.EndElement(); 320 } 321 } 322 // Parse the command, performing the specified actions and checking 323 // for a crash after each one. 324 while (init_reader_.Depth() != start_depth) { 325 node_name = init_reader_.NodeName(); 326 327 DoAction(node_name); 328 329 // Advance to the next element 330 ASSERT_TRUE(init_reader_.Read()) << "Malformed XML file."; 331 init_reader_.SkipToElement(); 332 if (DidCrash(true)) { 333 no_errors = false; 334 // This was the last action if we've returned to the initial depth 335 // of the command subtree. 336 bool wasLastAction = init_reader_.Depth() == start_depth; 337 LogCrashResult(GetMostRecentCrashDump(), wasLastAction); 338 // Skip to the beginning of the next command. 339 while (init_reader_.Depth() != start_depth) { 340 ASSERT_TRUE(init_reader_.Read()) << "Malformed XML file."; 341 } 342 } 343 } 344 345 if (no_errors) { 346 // If there were no previous crashes, log our tear down and check for 347 // a crash, log success for the entire command if this doesn't crash. 348 DoAction("TearDown"); 349 if (DidCrash(true)) 350 LogCrashResult(GetMostRecentCrashDump(), true); 351 else 352 LogSuccessResult(); 353 } else { 354 // If there was a previous crash, just tear down without logging, so 355 // that we know what the last command was before we crashed. 356 CloseBrowserAndServer(); 357 } 358 359 xml_writer_.StartIndenting(); 360 xml_writer_.EndElement(); // End "Executed" element. 361 } 362 } 363 364 AppendToTestLog(base::StringPrintf("total_duration_seconds=%f", 365 CalculateTestDuration(test_start_time_))); 366 367 // The test is finished so write our report. 368 WriteReportToFile(); 369 } 370 371 bool AutomatedUITest::DoAction(const std::string& action) { 372 bool did_complete_action = false; 373 xml_writer_.StartElement(action); 374 if (debug_logging_enabled_) 375 AppendToOutputFile(action); 376 377 if (LowerCaseEqualsASCII(action, "about")) { 378 did_complete_action = OpenAboutDialog(); 379 } else if (LowerCaseEqualsASCII(action, "back")) { 380 did_complete_action = BackButton(); 381 } else if (LowerCaseEqualsASCII(action, "changeencoding")) { 382 did_complete_action = ChangeEncoding(); 383 } else if (LowerCaseEqualsASCII(action, "closetab")) { 384 did_complete_action = CloseActiveTab(); 385 } else if (LowerCaseEqualsASCII(action, "clearbrowsingdata")) { 386 did_complete_action = OpenClearBrowsingDataDialog(); 387 } else if (LowerCaseEqualsASCII(action, "crash")) { 388 did_complete_action = ForceCrash(); 389 } else if (LowerCaseEqualsASCII(action, "dialog")) { 390 did_complete_action = ExerciseDialog(); 391 } else if (LowerCaseEqualsASCII(action, "downloads")) { 392 did_complete_action = ShowDownloads(); 393 } else if (LowerCaseEqualsASCII(action, "duplicatetab")) { 394 did_complete_action = DuplicateTab(); 395 } else if (LowerCaseEqualsASCII(action, "editsearchengines")) { 396 did_complete_action = OpenEditSearchEnginesDialog(); 397 } else if (LowerCaseEqualsASCII(action, "findinpage")) { 398 did_complete_action = FindInPage(); 399 } else if (LowerCaseEqualsASCII(action, "forward")) { 400 did_complete_action = ForwardButton(); 401 } else if (LowerCaseEqualsASCII(action, "goofftherecord")) { 402 did_complete_action = GoOffTheRecord(); 403 } else if (LowerCaseEqualsASCII(action, "history")) { 404 did_complete_action = ShowHistory(); 405 } else if (LowerCaseEqualsASCII(action, "home")) { 406 did_complete_action = Home(); 407 } else if (LowerCaseEqualsASCII(action, "importsettings")) { 408 did_complete_action = OpenImportSettingsDialog(); 409 } else if (LowerCaseEqualsASCII(action, "javascriptconsole")) { 410 did_complete_action = JavaScriptConsole(); 411 } else if (LowerCaseEqualsASCII(action, "navigate")) { 412 std::string url = content::kAboutBlankURL; 413 if (init_reader_.NodeAttribute("url", &url)) { 414 xml_writer_.AddAttribute("url", url); 415 } 416 GURL test_url(url); 417 did_complete_action = Navigate(test_url); 418 } else if (LowerCaseEqualsASCII(action, "newtab")) { 419 did_complete_action = NewTab(); 420 } else if (LowerCaseEqualsASCII(action, "openwindow")) { 421 did_complete_action = OpenAndActivateNewBrowserWindow(NULL); 422 } else if (LowerCaseEqualsASCII(action, "options")) { 423 did_complete_action = Options(); 424 } else if (LowerCaseEqualsASCII(action, "reload")) { 425 did_complete_action = ReloadPage(); 426 } else if (LowerCaseEqualsASCII(action, "restoretab")) { 427 did_complete_action = RestoreTab(); 428 } else if (LowerCaseEqualsASCII(action, "selectnexttab")) { 429 did_complete_action = SelectNextTab(); 430 } else if (LowerCaseEqualsASCII(action, "selectprevtab")) { 431 did_complete_action = SelectPreviousTab(); 432 } else if (LowerCaseEqualsASCII(action, "setup")) { 433 AutomatedUITestBase::SetUp(); 434 did_complete_action = true; 435 } else if (LowerCaseEqualsASCII(action, "sleep")) { 436 // This is for debugging, it probably shouldn't be used real tests. 437 base::PlatformThread::Sleep( 438 base::TimeDelta::FromMilliseconds(kDebuggingTimeoutMsec)); 439 did_complete_action = true; 440 } else if (LowerCaseEqualsASCII(action, "star")) { 441 did_complete_action = StarPage(); 442 } else if (LowerCaseEqualsASCII(action, "taskmanager")) { 443 did_complete_action = OpenTaskManagerDialog(); 444 } else if (LowerCaseEqualsASCII(action, "teardown")) { 445 CloseBrowserAndServer(); 446 did_complete_action = true; 447 } else if (LowerCaseEqualsASCII(action, "testaboutchrome")) { 448 did_complete_action = TestAboutChrome(); 449 } else if (LowerCaseEqualsASCII(action, "testclearbrowsingdata")) { 450 did_complete_action = TestClearBrowsingData(); 451 } else if (LowerCaseEqualsASCII(action, "testeditsearchengines")) { 452 did_complete_action = TestEditSearchEngines(); 453 } else if (LowerCaseEqualsASCII(action, "testimportsettings")) { 454 did_complete_action = TestImportSettings(); 455 } else if (LowerCaseEqualsASCII(action, "testoptions")) { 456 did_complete_action = TestOptions(); 457 } else if (LowerCaseEqualsASCII(action, "testtaskmanager")) { 458 did_complete_action = TestTaskManager(); 459 } else if (LowerCaseEqualsASCII(action, "testviewpasswords")) { 460 did_complete_action = TestViewPasswords(); 461 } else if (LowerCaseEqualsASCII(action, "viewpasswords")) { 462 did_complete_action = OpenViewPasswordsDialog(); 463 } else if (LowerCaseEqualsASCII(action, "viewsource")) { 464 did_complete_action = ViewSource(); 465 } else if (LowerCaseEqualsASCII(action, "zoomplus")) { 466 did_complete_action = ZoomPlus(); 467 } else if (LowerCaseEqualsASCII(action, "zoomminus")) { 468 did_complete_action = ZoomMinus(); 469 } else { 470 NOTREACHED() << "Unknown command passed into DoAction: " 471 << action.c_str(); 472 } 473 474 EXPECT_TRUE(did_complete_action) << action; 475 476 if (!did_complete_action) 477 xml_writer_.AddAttribute("failed_to_complete", "yes"); 478 xml_writer_.EndElement(); 479 480 if (post_action_delay_) { 481 base::PlatformThread::Sleep( 482 base::TimeDelta::FromSeconds(post_action_delay_)); 483 } 484 485 return did_complete_action; 486 } 487 488 bool AutomatedUITest::ChangeEncoding() { 489 // Get the encoding list that is used to populate the UI (encoding menu) 490 std::string cur_locale = g_browser_process->GetApplicationLocale(); 491 const std::vector<CharacterEncoding::EncodingInfo>* encodings = 492 CharacterEncoding::GetCurrentDisplayEncodings( 493 cur_locale, "ISO-8859-1,windows-1252", std::string()); 494 DCHECK(encodings); 495 DCHECK(!encodings->empty()); 496 unsigned len = static_cast<unsigned>(encodings->size()); 497 498 // The vector will contain mostly IDC values for encoding commands plus a few 499 // menu separators (0 values). If we hit a separator we just retry. 500 int index = base::RandInt(0, len); 501 while ((*encodings)[index].encoding_id == 0) { 502 index = base::RandInt(0, len); 503 } 504 505 return RunCommandAsync((*encodings)[index].encoding_id); 506 } 507 508 bool AutomatedUITest::JavaScriptConsole() { 509 return RunCommandAsync(IDC_DEV_TOOLS); 510 } 511 512 bool AutomatedUITest::OpenAboutDialog() { 513 return RunCommandAsync(IDC_ABOUT); 514 } 515 516 bool AutomatedUITest::OpenClearBrowsingDataDialog() { 517 return RunCommandAsync(IDC_CLEAR_BROWSING_DATA); 518 } 519 520 bool AutomatedUITest::OpenEditSearchEnginesDialog() { 521 return RunCommandAsync(IDC_EDIT_SEARCH_ENGINES); 522 } 523 524 bool AutomatedUITest::OpenImportSettingsDialog() { 525 return RunCommandAsync(IDC_IMPORT_SETTINGS); 526 } 527 528 bool AutomatedUITest::OpenTaskManagerDialog() { 529 return RunCommandAsync(IDC_TASK_MANAGER); 530 } 531 532 bool AutomatedUITest::OpenViewPasswordsDialog() { 533 return RunCommandAsync(IDC_VIEW_PASSWORDS); 534 } 535 536 bool AutomatedUITest::Options() { 537 return RunCommandAsync(IDC_OPTIONS); 538 } 539 540 bool AutomatedUITest::StarPage() { 541 return RunCommandAsync(IDC_BOOKMARK_PAGE); 542 } 543 544 bool AutomatedUITest::ViewSource() { 545 return RunCommandAsync(IDC_VIEW_SOURCE); 546 } 547 548 bool AutomatedUITest::ZoomMinus() { 549 return RunCommandAsync(IDC_ZOOM_MINUS); 550 } 551 552 bool AutomatedUITest::ZoomPlus() { 553 return RunCommandAsync(IDC_ZOOM_PLUS); 554 } 555 556 bool AutomatedUITest::TestAboutChrome() { 557 DoAction("About"); 558 return FuzzyTestDialog(kTestDialogActionsToRun); 559 } 560 561 bool AutomatedUITest::TestClearBrowsingData() { 562 DoAction("ClearBrowsingData"); 563 return FuzzyTestDialog(kTestDialogActionsToRun); 564 } 565 566 bool AutomatedUITest::TestEditSearchEngines() { 567 DoAction("EditSearchEngines"); 568 return FuzzyTestDialog(kTestDialogActionsToRun); 569 } 570 571 bool AutomatedUITest::TestImportSettings() { 572 DoAction("ImportSettings"); 573 return FuzzyTestDialog(kTestDialogActionsToRun); 574 } 575 576 bool AutomatedUITest::TestTaskManager() { 577 DoAction("TaskManager"); 578 return FuzzyTestDialog(kTestDialogActionsToRun); 579 } 580 581 bool AutomatedUITest::TestOptions() { 582 DoAction("Options"); 583 return FuzzyTestDialog(kTestDialogActionsToRun); 584 } 585 586 bool AutomatedUITest::TestViewPasswords() { 587 DoAction("ViewPasswords"); 588 return FuzzyTestDialog(kTestDialogActionsToRun); 589 } 590 591 bool AutomatedUITest::ExerciseDialog() { 592 int index = base::RandInt(0, arraysize(kDialogs) - 1); 593 return DoAction(kDialogs[index]) && FuzzyTestDialog(kTestDialogActionsToRun); 594 } 595 596 bool AutomatedUITest::FuzzyTestDialog(int num_actions) { 597 bool return_value = true; 598 599 for (int i = 0; i < num_actions; i++) { 600 // We want to make sure the first action performed on the dialog is not 601 // Space or Enter because focus is likely on the Close button. Both Space 602 // and Enter would close the dialog without performing more actions. We 603 // rely on the fact that those two actions are first in the array and set 604 // the lower bound to 2 if i == 0 to skip those two actions. 605 int action_index = base::RandInt(i == 0 ? 2 : 0, 606 arraysize(kTestDialogPossibleActions) 607 - 1); 608 return_value = return_value && 609 DoAction(kTestDialogPossibleActions[action_index]); 610 if (DidCrash(false)) 611 break; 612 } 613 return DoAction("PressEscapeKey") && return_value; 614 } 615 616 bool AutomatedUITest::ForceCrash() { 617 scoped_refptr<TabProxy> tab(GetActiveTab()); 618 GURL test_url(content::kChromeUICrashURL); 619 AutomationMsg_NavigationResponseValues result = tab->NavigateToURL(test_url); 620 if (result != AUTOMATION_MSG_NAVIGATION_SUCCESS) { 621 AddErrorAttribute("navigation_failed"); 622 return false; 623 } 624 return true; 625 } 626 627 bool AutomatedUITest::InitXMLReader() { 628 base::FilePath input_path = GetInputFilePath(); 629 630 if (!file_util::ReadFileToString(input_path, &xml_init_file_)) 631 return false; 632 return init_reader_.Load(xml_init_file_); 633 } 634 635 bool AutomatedUITest::WriteReportToFile() { 636 base::FilePath path = GetOutputFilePath(); 637 std::ofstream error_file; 638 if (!path.empty()) 639 error_file.open(path.value().c_str(), std::ios::out); 640 641 // Closes all open elements and free the writer. This is required 642 // in order to retrieve the contents of the buffer. 643 xml_writer_.StopWriting(); 644 error_file << xml_writer_.GetWrittenString(); 645 error_file.close(); 646 return true; 647 } 648 649 void AutomatedUITest::AppendToOutputFile(const std::string& append_string) { 650 base::FilePath path = GetOutputFilePath(); 651 std::ofstream error_file; 652 if (!path.empty()) 653 error_file.open(path.value().c_str(), std::ios::out | std::ios_base::app); 654 655 error_file << append_string << " "; 656 error_file.close(); 657 } 658 659 void AutomatedUITest::LogCrashResult(const base::FilePath& crash_dump, 660 bool command_completed) { 661 xml_writer_.StartElement("result"); 662 xml_writer_.AddAttribute("test_log_path", 663 GetTestLogFilePath().MaybeAsASCII()); 664 xml_writer_.AddAttribute("revision", GetChromeRevision()); 665 xml_writer_.StartElement("crash"); 666 #if defined(OS_WIN) 667 xml_writer_.AddAttribute("crash_dump", WideToASCII(crash_dump.value())); 668 #else 669 xml_writer_.AddAttribute("crash_dump", crash_dump.value()); 670 #endif 671 if (command_completed) 672 xml_writer_.AddAttribute("command_completed", "yes"); 673 else 674 xml_writer_.AddAttribute("command_completed", "no"); 675 xml_writer_.EndElement(); 676 xml_writer_.EndElement(); 677 } 678 679 void AutomatedUITest::LogSuccessResult() { 680 xml_writer_.StartElement("result"); 681 xml_writer_.AddAttribute("test_log_path", 682 GetTestLogFilePath().MaybeAsASCII()); 683 xml_writer_.AddAttribute("revision", GetChromeRevision()); 684 xml_writer_.StartElement("success"); 685 xml_writer_.EndElement(); 686 xml_writer_.EndElement(); 687 } 688 689 void AutomatedUITest::AddInfoAttribute(const std::string& info) { 690 xml_writer_.AddAttribute("info", info); 691 } 692 693 void AutomatedUITest::AddWarningAttribute(const std::string& warning) { 694 xml_writer_.AddAttribute("warning", warning); 695 } 696 697 void AutomatedUITest::AddErrorAttribute(const std::string& error) { 698 xml_writer_.AddAttribute("error", error); 699 } 700 701 void AutomatedUITest::LogErrorMessage(const std::string& error) { 702 AddErrorAttribute(error); 703 } 704 705 void AutomatedUITest::LogWarningMessage(const std::string& warning) { 706 AddWarningAttribute(warning); 707 } 708 709 void AutomatedUITest::LogInfoMessage(const std::string& info) { 710 AddWarningAttribute(info); 711 } 712 713 base::FilePath AutomatedUITest::GetMostRecentCrashDump() { 714 base::FilePath crash_dump_path; 715 base::FilePath most_recent_file_name; 716 PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dump_path); 717 base::Time most_recent_file_time; 718 719 bool first_file = true; 720 721 base::FileEnumerator enumerator(crash_dump_path, 722 false, // not recursive 723 base::FileEnumerator::FILES); 724 for (base::FilePath path = enumerator.Next(); !path.value().empty(); 725 path = enumerator.Next()) { 726 base::PlatformFileInfo file_info; 727 file_util::GetFileInfo(path, &file_info); 728 if (first_file) { 729 most_recent_file_time = file_info.last_modified; 730 most_recent_file_name = path.BaseName(); 731 first_file = false; 732 } else if (file_info.last_modified >= most_recent_file_time) { 733 most_recent_file_time = file_info.last_modified; 734 most_recent_file_name = path.BaseName(); 735 } 736 } 737 if (most_recent_file_name.empty()) { 738 return base::FilePath(); 739 } else { 740 crash_dump_path = crash_dump_path.Append(most_recent_file_name); 741 return crash_dump_path; 742 } 743 } 744 745 bool AutomatedUITest::DidCrash(bool update_total_crashes) { 746 int actual_crashes = GetCrashCount(); 747 748 // If there are more crash dumps than the total dumps which we have recorded 749 // then this is a new crash. 750 if (actual_crashes > total_crashes_) { 751 if (update_total_crashes) 752 total_crashes_ = actual_crashes; 753 return true; 754 } else { 755 return false; 756 } 757 } 758 759 TEST_F(AutomatedUITest, TheOneAndOnlyTest) { 760 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); 761 if (parsed_command_line.HasSwitch(kReproSwitch)) 762 RunReproduction(); 763 else 764 RunAutomatedUITest(); 765 } 766