- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / test / ui / ui_test.cc
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 }