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 "chrome/test/ui/ui_test.h" 6 7 #if defined(OS_POSIX) 8 #include <signal.h> 9 #include <sys/types.h> 10 #endif 11 12 #include <set> 13 #include <vector> 14 15 #include "base/base_switches.h" 16 #include "base/bind.h" 17 #include "base/command_line.h" 18 #include "base/environment.h" 19 #include "base/file_util.h" 20 #include "base/files/file_enumerator.h" 21 #include "base/files/file_path.h" 22 #include "base/files/scoped_temp_dir.h" 23 #include "base/json/json_file_value_serializer.h" 24 #include "base/logging.h" 25 #include "base/memory/scoped_ptr.h" 26 #include "base/path_service.h" 27 #include "base/strings/string_number_conversions.h" 28 #include "base/strings/string_split.h" 29 #include "base/strings/utf_string_conversions.h" 30 #include "base/test/test_file_util.h" 31 #include "base/test/test_timeouts.h" 32 #include "base/threading/platform_thread.h" 33 #include "base/time/time.h" 34 #include "chrome/app/chrome_command_ids.h" 35 #include "chrome/browser/profiles/profile_impl.h" 36 #include "chrome/common/automation_messages.h" 37 #include "chrome/common/chrome_constants.h" 38 #include "chrome/common/chrome_paths.h" 39 #include "chrome/common/chrome_switches.h" 40 #include "chrome/common/logging_chrome.h" 41 #include "chrome/common/net/url_fixer_upper.h" 42 #include "chrome/common/pref_names.h" 43 #include "chrome/common/url_constants.h" 44 #include "chrome/test/automation/automation_proxy.h" 45 #include "chrome/test/automation/browser_proxy.h" 46 #include "chrome/test/automation/proxy_launcher.h" 47 #include "chrome/test/automation/tab_proxy.h" 48 #include "chrome/test/automation/window_proxy.h" 49 #include "chrome/test/base/chrome_process_util.h" 50 #include "chrome/test/base/test_launcher_utils.h" 51 #include "chrome/test/base/test_switches.h" 52 #include "chrome/test/base/testing_profile.h" 53 #include "net/base/net_util.h" 54 #include "ui/gl/gl_implementation.h" 55 #include "url/gurl.h" 56 57 #if defined(OS_WIN) 58 #include "base/win/windows_version.h" 59 #endif 60 61 using base::Time; 62 using base::TimeDelta; 63 using base::TimeTicks; 64 65 const wchar_t UITestBase::kFailedNoCrashService[] = 66 #if defined(OS_WIN) 67 L"NOTE: This test is expected to fail if crash_service.exe is not " 68 L"running. Start it manually before running this test (see the build " 69 L"output directory)."; 70 #elif defined(OS_LINUX) 71 L"NOTE: This test is expected to fail if breakpad is not built in " 72 L"or if chromium is not running headless (try CHROME_HEADLESS=1)."; 73 #else 74 L"NOTE: Crash service not ported to this platform!"; 75 #endif 76 77 UITestBase::UITestBase() 78 : launch_arguments_(CommandLine::NO_PROGRAM), 79 expected_errors_(0), 80 expected_crashes_(0), 81 homepage_(content::kAboutBlankURL), 82 wait_for_initial_loads_(true), 83 dom_automation_enabled_(false), 84 stats_collection_controller_enabled_(false), 85 show_window_(false), 86 clear_profile_(true), 87 include_testing_id_(true), 88 enable_file_cookies_(true) { 89 PathService::Get(chrome::DIR_APP, &browser_directory_); 90 PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory_); 91 } 92 93 UITestBase::UITestBase(base::MessageLoop::Type msg_loop_type) 94 : launch_arguments_(CommandLine::NO_PROGRAM), 95 expected_errors_(0), 96 expected_crashes_(0), 97 wait_for_initial_loads_(true), 98 dom_automation_enabled_(false), 99 stats_collection_controller_enabled_(false), 100 show_window_(false), 101 clear_profile_(true), 102 include_testing_id_(true), 103 enable_file_cookies_(true) { 104 PathService::Get(chrome::DIR_APP, &browser_directory_); 105 PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory_); 106 } 107 108 UITestBase::~UITestBase() {} 109 110 void UITestBase::SetUp() { 111 // Tests that do a session restore (e.g. SessionRestoreUITest, StartupTest) 112 // call SetUp() multiple times because they restart the browser mid-test. 113 // We don't want to reset the ProxyLauncher's state in those cases. 114 if (!launcher_.get()) 115 launcher_.reset(CreateProxyLauncher()); 116 launcher_->AssertAppNotRunning("Please close any other instances " 117 "of the app before testing."); 118 119 test_start_time_ = Time::NowFromSystemTime(); 120 121 SetLaunchSwitches(); 122 ASSERT_TRUE(launcher_->InitializeConnection(DefaultLaunchState(), 123 wait_for_initial_loads_)); 124 } 125 126 void UITestBase::TearDown() { 127 if (launcher_.get()) 128 launcher_->TerminateConnection(); 129 130 CheckErrorsAndCrashes(); 131 } 132 133 AutomationProxy* UITestBase::automation() const { 134 return launcher_->automation(); 135 } 136 137 base::TimeDelta UITestBase::action_timeout() { 138 return automation()->action_timeout(); 139 } 140 141 int UITestBase::action_timeout_ms() { 142 return action_timeout().InMilliseconds(); 143 } 144 145 void UITestBase::set_action_timeout(base::TimeDelta timeout) { 146 automation()->set_action_timeout(timeout); 147 VLOG(1) << "Automation action timeout set to " 148 << timeout.InMilliseconds() << " ms"; 149 } 150 151 void UITestBase::set_action_timeout_ms(int timeout) { 152 set_action_timeout(base::TimeDelta::FromMilliseconds(timeout)); 153 } 154 155 ProxyLauncher* UITestBase::CreateProxyLauncher() { 156 return new AnonymousProxyLauncher(false); 157 } 158 159 ProxyLauncher::LaunchState UITestBase::DefaultLaunchState() { 160 base::FilePath browser_executable = 161 browser_directory_.Append(GetExecutablePath()); 162 CommandLine command(browser_executable); 163 command.AppendArguments(launch_arguments_, false); 164 base::Closure setup_profile_callback = base::Bind(&UITestBase::SetUpProfile, 165 base::Unretained(this)); 166 ProxyLauncher::LaunchState state = 167 { clear_profile_, template_user_data_, setup_profile_callback, 168 command, include_testing_id_, show_window_ }; 169 return state; 170 } 171 172 void UITestBase::SetLaunchSwitches() { 173 // All flags added here should also be added in ExtraChromeFlags() in 174 // chrome/test/pyautolib/pyauto.py as well to take effect for all tests 175 // on chromeos. 176 177 // Propagate commandline settings from test_launcher_utils. 178 test_launcher_utils::PrepareBrowserCommandLineForTests(&launch_arguments_); 179 180 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kWaitForDebugger)) 181 launch_arguments_.AppendSwitch(switches::kWaitForDebugger); 182 183 // We need cookies on file:// for things like the page cycler. 184 if (enable_file_cookies_) 185 launch_arguments_.AppendSwitch(switches::kEnableFileCookies); 186 if (dom_automation_enabled_) 187 launch_arguments_.AppendSwitch(switches::kDomAutomationController); 188 if (stats_collection_controller_enabled_) 189 launch_arguments_.AppendSwitch(switches::kStatsCollectionController); 190 // Allow off-store extension installs. 191 launch_arguments_.AppendSwitchASCII( 192 switches::kEasyOffStoreExtensionInstall, "1"); 193 if (!homepage_.empty()) { 194 // Pass |homepage_| both as an arg (so that it opens on startup) and to the 195 // homepage switch (so that the homepage is set). 196 197 if (!launch_arguments_.HasSwitch(switches::kHomePage)) 198 launch_arguments_.AppendSwitchASCII(switches::kHomePage, homepage_); 199 200 if (launch_arguments_.GetArgs().empty() && 201 !launch_arguments_.HasSwitch(switches::kRestoreLastSession)) { 202 launch_arguments_.AppendArg(homepage_); 203 } 204 } 205 if (!test_name_.empty()) 206 launch_arguments_.AppendSwitchASCII(switches::kTestName, test_name_); 207 } 208 209 void UITestBase::SetUpProfile() { 210 } 211 212 void UITestBase::LaunchBrowser() { 213 LaunchBrowser(launch_arguments_, clear_profile_); 214 } 215 216 void UITestBase::LaunchBrowserAndServer() { 217 ASSERT_TRUE(launcher_->LaunchBrowserAndServer(DefaultLaunchState(), 218 wait_for_initial_loads_)); 219 } 220 221 void UITestBase::ConnectToRunningBrowser() { 222 ASSERT_TRUE(launcher_->ConnectToRunningBrowser(wait_for_initial_loads_)); 223 } 224 225 void UITestBase::CloseBrowserAndServer() { 226 if (launcher_.get()) 227 launcher_->CloseBrowserAndServer(); 228 } 229 230 void UITestBase::LaunchBrowser(const CommandLine& arguments, 231 bool clear_profile) { 232 ProxyLauncher::LaunchState state = DefaultLaunchState(); 233 state.clear_profile = clear_profile; 234 ASSERT_TRUE(launcher_->LaunchBrowser(state)); 235 } 236 237 void UITestBase::QuitBrowser() { 238 launcher_->QuitBrowser(); 239 } 240 241 scoped_refptr<TabProxy> UITestBase::GetActiveTab(int window_index) { 242 EXPECT_GE(window_index, 0); 243 int window_count = -1; 244 // We have to use EXPECT rather than ASSERT here because ASSERT_* only works 245 // in functions that return void. 246 EXPECT_TRUE(automation()->GetBrowserWindowCount(&window_count)); 247 if (window_count == -1) 248 return NULL; 249 EXPECT_GT(window_count, window_index); 250 scoped_refptr<BrowserProxy> window_proxy(automation()-> 251 GetBrowserWindow(window_index)); 252 EXPECT_TRUE(window_proxy.get()); 253 if (!window_proxy.get()) 254 return NULL; 255 256 int active_tab_index = -1; 257 EXPECT_TRUE(window_proxy->GetActiveTabIndex(&active_tab_index)); 258 if (active_tab_index == -1) 259 return NULL; 260 261 return window_proxy->GetTab(active_tab_index); 262 } 263 264 scoped_refptr<TabProxy> UITestBase::GetActiveTab() { 265 scoped_refptr<BrowserProxy> window_proxy(automation()-> 266 GetBrowserWindow(0)); 267 EXPECT_TRUE(window_proxy.get()); 268 if (!window_proxy.get()) 269 return NULL; 270 271 scoped_refptr<TabProxy> tab_proxy = window_proxy->GetActiveTab(); 272 EXPECT_TRUE(tab_proxy.get()); 273 return tab_proxy; 274 } 275 276 void UITestBase::NavigateToURL(const GURL& url) { 277 NavigateToURL(url, 0, GetActiveTabIndex(0)); 278 } 279 280 void UITestBase::NavigateToURL(const GURL& url, int window_index) { 281 NavigateToURL(url, window_index, GetActiveTabIndex(window_index)); 282 } 283 284 void UITestBase::NavigateToURL(const GURL& url, int window_index, int 285 tab_index) { 286 NavigateToURLBlockUntilNavigationsComplete(url, 1, window_index, tab_index); 287 } 288 289 void UITestBase::NavigateToURLBlockUntilNavigationsComplete( 290 const GURL& url, int number_of_navigations) { 291 scoped_refptr<TabProxy> tab_proxy(GetActiveTab()); 292 ASSERT_TRUE(tab_proxy.get()); 293 EXPECT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, 294 tab_proxy->NavigateToURLBlockUntilNavigationsComplete( 295 url, number_of_navigations)) << url.spec(); 296 } 297 298 void UITestBase::NavigateToURLBlockUntilNavigationsComplete( 299 const GURL& url, int number_of_navigations, int window_index, 300 int tab_index) { 301 scoped_refptr<BrowserProxy> window = 302 automation()->GetBrowserWindow(window_index); 303 ASSERT_TRUE(window.get()); 304 scoped_refptr<TabProxy> tab_proxy(window->GetTab(tab_index)); 305 ASSERT_TRUE(tab_proxy.get()); 306 EXPECT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, 307 tab_proxy->NavigateToURLBlockUntilNavigationsComplete( 308 url, number_of_navigations)) << url.spec(); 309 } 310 311 GURL UITestBase::GetActiveTabURL(int window_index) { 312 scoped_refptr<TabProxy> tab_proxy(GetActiveTab(window_index)); 313 EXPECT_TRUE(tab_proxy.get()); 314 if (!tab_proxy.get()) 315 return GURL(); 316 317 GURL url; 318 bool success = tab_proxy->GetCurrentURL(&url); 319 EXPECT_TRUE(success); 320 if (!success) 321 return GURL(); 322 return url; 323 } 324 325 std::wstring UITestBase::GetActiveTabTitle(int window_index) { 326 std::wstring title; 327 scoped_refptr<TabProxy> tab_proxy(GetActiveTab(window_index)); 328 EXPECT_TRUE(tab_proxy.get()); 329 if (!tab_proxy.get()) 330 return title; 331 332 EXPECT_TRUE(tab_proxy->GetTabTitle(&title)); 333 return title; 334 } 335 336 int UITestBase::GetActiveTabIndex(int window_index) { 337 scoped_refptr<BrowserProxy> window_proxy(automation()-> 338 GetBrowserWindow(window_index)); 339 EXPECT_TRUE(window_proxy.get()); 340 if (!window_proxy.get()) 341 return -1; 342 343 int active_tab_index = -1; 344 EXPECT_TRUE(window_proxy->GetActiveTabIndex(&active_tab_index)); 345 return active_tab_index; 346 } 347 348 int UITestBase::GetTabCount() { 349 return GetTabCount(0); 350 } 351 352 int UITestBase::GetTabCount(int window_index) { 353 scoped_refptr<BrowserProxy> window( 354 automation()->GetBrowserWindow(window_index)); 355 EXPECT_TRUE(window.get()); 356 if (!window.get()) 357 return 0; 358 359 int result = 0; 360 EXPECT_TRUE(window->GetTabCount(&result)); 361 362 return result; 363 } 364 365 void UITestBase::WaitUntilTabCount(int tab_count) { 366 const int kMaxIntervals = 10; 367 const TimeDelta kDelay = TestTimeouts::action_timeout() / kMaxIntervals; 368 369 for (int i = 0; i < kMaxIntervals; ++i) { 370 if (GetTabCount() == tab_count) 371 return; 372 373 base::PlatformThread::Sleep(kDelay); 374 } 375 376 ADD_FAILURE() << "Timeout reached in WaitUntilTabCount"; 377 } 378 379 const base::FilePath::CharType* UITestBase::GetExecutablePath() { 380 if (launch_arguments_.HasSwitch(switches::kEnableChromiumBranding)) 381 return chrome::kBrowserProcessExecutablePathChromium; 382 return chrome::kBrowserProcessExecutablePath; 383 } 384 385 bool UITestBase::CloseBrowser(BrowserProxy* browser, 386 bool* application_closed) const { 387 DCHECK(application_closed); 388 if (!browser->is_valid() || !browser->handle()) 389 return false; 390 391 bool result = true; 392 393 ChromeProcessList processes = GetRunningChromeProcesses( 394 browser_process_id()); 395 396 bool succeeded = automation()->Send(new AutomationMsg_CloseBrowser( 397 browser->handle(), &result, application_closed)); 398 399 if (!succeeded) 400 return false; 401 402 if (*application_closed) { 403 int exit_code = -1; 404 EXPECT_TRUE(launcher_->WaitForBrowserProcessToQuit( 405 TestTimeouts::action_max_timeout(), &exit_code)); 406 EXPECT_EQ(0, exit_code); // Expect a clean shutown. 407 // Ensure no child processes are left dangling. 408 TerminateAllChromeProcesses(processes); 409 } 410 411 return result; 412 } 413 414 int UITestBase::GetCrashCount() const { 415 base::FilePath crash_dump_path; 416 PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dump_path); 417 418 int files_found = 0; 419 base::FileEnumerator en(crash_dump_path, false, base::FileEnumerator::FILES); 420 while (!en.Next().empty()) { 421 if (en.GetInfo().GetLastModifiedTime() > test_start_time_) 422 files_found++; 423 } 424 425 #if defined(OS_WIN) 426 // Each crash creates two dump files on Windows. 427 return files_found / 2; 428 #else 429 return files_found; 430 #endif 431 } 432 433 std::string UITestBase::CheckErrorsAndCrashes() const { 434 // Make sure that we didn't encounter any assertion failures 435 logging::AssertionList assertions; 436 logging::GetFatalAssertions(&assertions); 437 438 // If there were errors, get all the error strings for display. 439 std::wstring failures = 440 L"The following error(s) occurred in the application during this test:"; 441 if (assertions.size() > expected_errors_) { 442 logging::AssertionList::const_iterator iter = assertions.begin(); 443 for (; iter != assertions.end(); ++iter) { 444 failures += L"\n\n"; 445 failures += *iter; 446 } 447 } 448 EXPECT_EQ(expected_errors_, assertions.size()) << failures; 449 450 int actual_crashes = GetCrashCount(); 451 452 std::wstring error_msg = 453 L"Encountered an unexpected crash in the program during this test."; 454 if (expected_crashes_ > 0 && actual_crashes == 0) { 455 error_msg += L" "; 456 error_msg += kFailedNoCrashService; 457 } 458 EXPECT_EQ(expected_crashes_, actual_crashes) << error_msg; 459 460 std::wstring wide_result; 461 if (expected_errors_ != assertions.size()) { 462 wide_result += failures; 463 wide_result += L"\n\n"; 464 } 465 if (expected_crashes_ != actual_crashes) 466 wide_result += error_msg; 467 468 return std::string(wide_result.begin(), wide_result.end()); 469 } 470 471 void UITestBase::SetBrowserDirectory(const base::FilePath& dir) { 472 browser_directory_ = dir; 473 } 474 475 void UITestBase::AppendBrowserLaunchSwitch(const char* name) { 476 launch_arguments_.AppendSwitch(name); 477 } 478 479 void UITestBase::AppendBrowserLaunchSwitch(const char* name, 480 const char* value) { 481 launch_arguments_.AppendSwitchASCII(name, value); 482 } 483 484 bool UITestBase::BeginTracing(const std::string& category_patterns) { 485 return automation()->BeginTracing(category_patterns); 486 } 487 488 std::string UITestBase::EndTracing() { 489 std::string json_trace_output; 490 if (!automation()->EndTracing(&json_trace_output)) 491 return std::string(); 492 return json_trace_output; 493 } 494 495 // UITest methods 496 497 void UITest::SetUp() { 498 // Pass the test case name to chrome.exe on the command line to help with 499 // parsing Purify output. 500 const testing::TestInfo* const test_info = 501 testing::UnitTest::GetInstance()->current_test_info(); 502 if (test_info) { 503 set_test_name(test_info->test_case_name() + std::string(".") + 504 test_info->name()); 505 } 506 507 UITestBase::SetUp(); 508 PlatformTest::SetUp(); 509 } 510 511 void UITest::TearDown() { 512 UITestBase::TearDown(); 513 PlatformTest::TearDown(); 514 } 515 516 ProxyLauncher* UITest::CreateProxyLauncher() { 517 // Make the AutomationProxy disconnect the channel on the first error, 518 // so that we avoid spending a lot of time in timeouts. The browser is likely 519 // hosed if we hit those errors. 520 return new AnonymousProxyLauncher(true); 521 } 522 523 bool UITest::GetBrowserProcessCount(int* count) { 524 *count = 0; 525 if (!automation()->WaitForProcessLauncherThreadToGoIdle()) 526 return false; 527 *count = GetRunningChromeProcesses(browser_process_id()).size(); 528 return true; 529 } 530 531 static DictionaryValue* LoadDictionaryValueFromPath( 532 const base::FilePath& path) { 533 if (path.empty()) 534 return NULL; 535 536 JSONFileValueSerializer serializer(path); 537 scoped_ptr<Value> root_value(serializer.Deserialize(NULL, NULL)); 538 if (!root_value.get() || root_value->GetType() != Value::TYPE_DICTIONARY) 539 return NULL; 540 541 return static_cast<DictionaryValue*>(root_value.release()); 542 } 543 544 DictionaryValue* UITest::GetLocalState() { 545 base::FilePath local_state_path; 546 PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path); 547 return LoadDictionaryValueFromPath(local_state_path); 548 } 549 550 DictionaryValue* UITest::GetDefaultProfilePreferences() { 551 base::FilePath path; 552 PathService::Get(chrome::DIR_USER_DATA, &path); 553 path = path.AppendASCII(TestingProfile::kTestUserProfileDir); 554 return LoadDictionaryValueFromPath(path.Append(chrome::kPreferencesFilename)); 555 } 556 557 void UITest::WaitForFinish(const std::string &name, 558 const std::string &id, 559 const GURL &url, 560 const std::string& test_complete_cookie, 561 const std::string& expected_cookie_value, 562 const base::TimeDelta wait_time) { 563 // The webpage being tested has javascript which sets a cookie 564 // which signals completion of the test. The cookie name is 565 // a concatenation of the test name and the test id. This allows 566 // us to run multiple tests within a single webpage and test 567 // that they all c 568 std::string cookie_name = name; 569 cookie_name.append("."); 570 cookie_name.append(id); 571 cookie_name.append("."); 572 cookie_name.append(test_complete_cookie); 573 574 scoped_refptr<TabProxy> tab(GetActiveTab()); 575 ASSERT_TRUE(tab.get()); 576 std::string cookie_value = WaitUntilCookieNonEmpty(tab.get(), url, 577 cookie_name.c_str(), 578 wait_time); 579 EXPECT_EQ(expected_cookie_value, cookie_value); 580 } 581 582 bool UITest::WaitUntilJavaScriptCondition(TabProxy* tab, 583 const std::wstring& frame_xpath, 584 const std::wstring& jscript, 585 base::TimeDelta timeout) { 586 const TimeDelta kDelay = TimeDelta::FromMilliseconds(250); 587 const int kMaxDelays = timeout / kDelay; 588 589 // Wait until the test signals it has completed. 590 for (int i = 0; i < kMaxDelays; ++i) { 591 bool done_value = false; 592 bool success = tab->ExecuteAndExtractBool(frame_xpath, jscript, 593 &done_value); 594 EXPECT_TRUE(success); 595 if (!success) 596 return false; 597 if (done_value) 598 return true; 599 600 base::PlatformThread::Sleep(kDelay); 601 } 602 603 ADD_FAILURE() << "Timeout reached in WaitUntilJavaScriptCondition"; 604 return false; 605 } 606 607 bool UITest::WaitUntilCookieValue(TabProxy* tab, 608 const GURL& url, 609 const char* cookie_name, 610 base::TimeDelta timeout, 611 const char* expected_value) { 612 const TimeDelta kDelay = TimeDelta::FromMilliseconds(250); 613 const int kMaxDelays = timeout / kDelay; 614 615 std::string cookie_value; 616 for (int i = 0; i < kMaxDelays; ++i) { 617 EXPECT_TRUE(tab->GetCookieByName(url, cookie_name, &cookie_value)); 618 if (cookie_value == expected_value) 619 return true; 620 621 base::PlatformThread::Sleep(kDelay); 622 } 623 624 ADD_FAILURE() << "Timeout reached in WaitUntilCookieValue"; 625 return false; 626 } 627 628 std::string UITest::WaitUntilCookieNonEmpty(TabProxy* tab, 629 const GURL& url, 630 const char* cookie_name, 631 base::TimeDelta timeout) { 632 const TimeDelta kDelay = TimeDelta::FromMilliseconds(250); 633 const int kMaxDelays = timeout / kDelay; 634 635 for (int i = 0; i < kMaxDelays; ++i) { 636 std::string cookie_value; 637 EXPECT_TRUE(tab->GetCookieByName(url, cookie_name, &cookie_value)); 638 if (!cookie_value.empty()) 639 return cookie_value; 640 641 base::PlatformThread::Sleep(kDelay); 642 } 643 644 ADD_FAILURE() << "Timeout reached in WaitUntilCookieNonEmpty"; 645 return std::string(); 646 } 647 648 bool UITest::WaitForFindWindowVisibilityChange(BrowserProxy* browser, 649 bool wait_for_open) { 650 const int kCycles = 10; 651 const TimeDelta kDelay = TestTimeouts::action_timeout() / kCycles; 652 for (int i = 0; i < kCycles; i++) { 653 bool visible = false; 654 if (!browser->IsFindWindowFullyVisible(&visible)) 655 return false; // Some error. 656 if (visible == wait_for_open) 657 return true; // Find window visibility change complete. 658 659 // Give it a chance to catch up. 660 base::PlatformThread::Sleep(kDelay); 661 } 662 663 ADD_FAILURE() << "Timeout reached in WaitForFindWindowVisibilityChange"; 664 return false; 665 } 666 667 void UITest::TerminateBrowser() { 668 launcher_->TerminateBrowser(); 669 670 // Make sure the UMA metrics say we didn't crash. 671 scoped_ptr<DictionaryValue> local_prefs(GetLocalState()); 672 bool exited_cleanly; 673 ASSERT_TRUE(local_prefs.get()); 674 ASSERT_TRUE(local_prefs->GetBoolean(prefs::kStabilityExitedCleanly, 675 &exited_cleanly)); 676 ASSERT_TRUE(exited_cleanly); 677 678 // And that session end was successful. 679 bool session_end_completed; 680 ASSERT_TRUE(local_prefs->GetBoolean(prefs::kStabilitySessionEndCompleted, 681 &session_end_completed)); 682 ASSERT_TRUE(session_end_completed); 683 684 // Make sure session restore says we didn't crash. 685 scoped_ptr<DictionaryValue> profile_prefs(GetDefaultProfilePreferences()); 686 ASSERT_TRUE(profile_prefs.get()); 687 std::string exit_type; 688 ASSERT_TRUE(profile_prefs->GetString(prefs::kSessionExitedCleanly, 689 &exit_type)); 690 EXPECT_EQ(ProfileImpl::kPrefExitTypeNormal, exit_type); 691 } 692