ui_test.cc revision 4311e82a78ceafbe0585f51d4c8a86df9f21aa0d
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
61using base::Time;
62using base::TimeDelta;
63using base::TimeTicks;
64
65const 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
77UITestBase::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
93UITestBase::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
108UITestBase::~UITestBase() {}
109
110void 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
126void UITestBase::TearDown() {
127  if (launcher_.get())
128    launcher_->TerminateConnection();
129
130  CheckErrorsAndCrashes();
131}
132
133AutomationProxy* UITestBase::automation() const {
134  return launcher_->automation();
135}
136
137base::TimeDelta UITestBase::action_timeout() {
138  return automation()->action_timeout();
139}
140
141int UITestBase::action_timeout_ms() {
142  return action_timeout().InMilliseconds();
143}
144
145void 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
151void UITestBase::set_action_timeout_ms(int timeout) {
152  set_action_timeout(base::TimeDelta::FromMilliseconds(timeout));
153}
154
155ProxyLauncher* UITestBase::CreateProxyLauncher() {
156  return new AnonymousProxyLauncher(false);
157}
158
159ProxyLauncher::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
172void 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
209void UITestBase::SetUpProfile() {
210}
211
212void UITestBase::LaunchBrowser() {
213  LaunchBrowser(launch_arguments_, clear_profile_);
214}
215
216void UITestBase::LaunchBrowserAndServer() {
217  ASSERT_TRUE(launcher_->LaunchBrowserAndServer(DefaultLaunchState(),
218                                                wait_for_initial_loads_));
219}
220
221void UITestBase::ConnectToRunningBrowser() {
222  ASSERT_TRUE(launcher_->ConnectToRunningBrowser(wait_for_initial_loads_));
223}
224
225void UITestBase::CloseBrowserAndServer() {
226  if (launcher_.get())
227    launcher_->CloseBrowserAndServer();
228}
229
230void 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
237void UITestBase::QuitBrowser() {
238  launcher_->QuitBrowser();
239}
240
241scoped_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
264scoped_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
276void UITestBase::NavigateToURL(const GURL& url) {
277  NavigateToURL(url, 0, GetActiveTabIndex(0));
278}
279
280void UITestBase::NavigateToURL(const GURL& url, int window_index) {
281  NavigateToURL(url, window_index, GetActiveTabIndex(window_index));
282}
283
284void UITestBase::NavigateToURL(const GURL& url, int window_index, int
285    tab_index) {
286  NavigateToURLBlockUntilNavigationsComplete(url, 1, window_index, tab_index);
287}
288
289void 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
298void 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
311GURL 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
325std::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
336int 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
348int UITestBase::GetTabCount() {
349  return GetTabCount(0);
350}
351
352int 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
365void 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
379const base::FilePath::CharType* UITestBase::GetExecutablePath() {
380  if (launch_arguments_.HasSwitch(switches::kEnableChromiumBranding))
381    return chrome::kBrowserProcessExecutablePathChromium;
382  return chrome::kBrowserProcessExecutablePath;
383}
384
385bool 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
414int 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
433std::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
471void UITestBase::SetBrowserDirectory(const base::FilePath& dir) {
472  browser_directory_ = dir;
473}
474
475void UITestBase::AppendBrowserLaunchSwitch(const char* name) {
476  launch_arguments_.AppendSwitch(name);
477}
478
479void UITestBase::AppendBrowserLaunchSwitch(const char* name,
480                                           const char* value) {
481  launch_arguments_.AppendSwitchASCII(name, value);
482}
483
484bool UITestBase::BeginTracing(const std::string& category_patterns) {
485  return automation()->BeginTracing(category_patterns);
486}
487
488std::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
497void 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
511void UITest::TearDown() {
512  UITestBase::TearDown();
513  PlatformTest::TearDown();
514}
515
516ProxyLauncher* 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
523bool 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
531static 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
544DictionaryValue* 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
550DictionaryValue* 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
557void 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
582bool 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
607bool 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
628std::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
648bool 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
667void 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