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.
5 #include "chrome/test/ui/ui_test.h"
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"
58 #include "base/win/windows_version.h"
62 using base::TimeDelta;
63 using base::TimeTicks;
65 const wchar_t UITestBase::kFailedNoCrashService[] =
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).";
74 L"NOTE: Crash service not ported to this platform!";
77 UITestBase::UITestBase()
78 : launch_arguments_(CommandLine::NO_PROGRAM),
81 homepage_(content::kAboutBlankURL),
82 wait_for_initial_loads_(true),
83 dom_automation_enabled_(false),
84 stats_collection_controller_enabled_(false),
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_);
93 UITestBase::UITestBase(base::MessageLoop::Type msg_loop_type)
94 : launch_arguments_(CommandLine::NO_PROGRAM),
97 wait_for_initial_loads_(true),
98 dom_automation_enabled_(false),
99 stats_collection_controller_enabled_(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_);
108 UITestBase::~UITestBase() {}
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.");
119 test_start_time_ = Time::NowFromSystemTime();
122 ASSERT_TRUE(launcher_->InitializeConnection(DefaultLaunchState(),
123 wait_for_initial_loads_));
126 void UITestBase::TearDown() {
128 launcher_->TerminateConnection();
130 CheckErrorsAndCrashes();
133 AutomationProxy* UITestBase::automation() const {
134 return launcher_->automation();
137 base::TimeDelta UITestBase::action_timeout() {
138 return automation()->action_timeout();
141 int UITestBase::action_timeout_ms() {
142 return action_timeout().InMilliseconds();
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";
151 void UITestBase::set_action_timeout_ms(int timeout) {
152 set_action_timeout(base::TimeDelta::FromMilliseconds(timeout));
155 ProxyLauncher* UITestBase::CreateProxyLauncher() {
156 return new AnonymousProxyLauncher(false);
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_ };
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
177 // Propagate commandline settings from test_launcher_utils.
178 test_launcher_utils::PrepareBrowserCommandLineForTests(&launch_arguments_);
180 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kWaitForDebugger))
181 launch_arguments_.AppendSwitch(switches::kWaitForDebugger);
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).
197 if (!launch_arguments_.HasSwitch(switches::kHomePage))
198 launch_arguments_.AppendSwitchASCII(switches::kHomePage, homepage_);
200 if (launch_arguments_.GetArgs().empty() &&
201 !launch_arguments_.HasSwitch(switches::kRestoreLastSession)) {
202 launch_arguments_.AppendArg(homepage_);
205 if (!test_name_.empty())
206 launch_arguments_.AppendSwitchASCII(switches::kTestName, test_name_);
209 void UITestBase::SetUpProfile() {
212 void UITestBase::LaunchBrowser() {
213 LaunchBrowser(launch_arguments_, clear_profile_);
216 void UITestBase::LaunchBrowserAndServer() {
217 ASSERT_TRUE(launcher_->LaunchBrowserAndServer(DefaultLaunchState(),
218 wait_for_initial_loads_));
221 void UITestBase::ConnectToRunningBrowser() {
222 ASSERT_TRUE(launcher_->ConnectToRunningBrowser(wait_for_initial_loads_));
225 void UITestBase::CloseBrowserAndServer() {
227 launcher_->CloseBrowserAndServer();
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));
237 void UITestBase::QuitBrowser() {
238 launcher_->QuitBrowser();
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)
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())
256 int active_tab_index = -1;
257 EXPECT_TRUE(window_proxy->GetActiveTabIndex(&active_tab_index));
258 if (active_tab_index == -1)
261 return window_proxy->GetTab(active_tab_index);
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())
271 scoped_refptr<TabProxy> tab_proxy = window_proxy->GetActiveTab();
272 EXPECT_TRUE(tab_proxy.get());
276 void UITestBase::NavigateToURL(const GURL& url) {
277 NavigateToURL(url, 0, GetActiveTabIndex(0));
280 void UITestBase::NavigateToURL(const GURL& url, int window_index) {
281 NavigateToURL(url, window_index, GetActiveTabIndex(window_index));
284 void UITestBase::NavigateToURL(const GURL& url, int window_index, int
286 NavigateToURLBlockUntilNavigationsComplete(url, 1, window_index, tab_index);
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();
298 void UITestBase::NavigateToURLBlockUntilNavigationsComplete(
299 const GURL& url, int number_of_navigations, int window_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();
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())
318 bool success = tab_proxy->GetCurrentURL(&url);
319 EXPECT_TRUE(success);
325 std::wstring UITestBase::GetActiveTabTitle(int window_index) {
327 scoped_refptr<TabProxy> tab_proxy(GetActiveTab(window_index));
328 EXPECT_TRUE(tab_proxy.get());
329 if (!tab_proxy.get())
332 EXPECT_TRUE(tab_proxy->GetTabTitle(&title));
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())
343 int active_tab_index = -1;
344 EXPECT_TRUE(window_proxy->GetActiveTabIndex(&active_tab_index));
345 return active_tab_index;
348 int UITestBase::GetTabCount() {
349 return GetTabCount(0);
352 int UITestBase::GetTabCount(int window_index) {
353 scoped_refptr<BrowserProxy> window(
354 automation()->GetBrowserWindow(window_index));
355 EXPECT_TRUE(window.get());
360 EXPECT_TRUE(window->GetTabCount(&result));
365 void UITestBase::WaitUntilTabCount(int tab_count) {
366 const int kMaxIntervals = 10;
367 const TimeDelta kDelay = TestTimeouts::action_timeout() / kMaxIntervals;
369 for (int i = 0; i < kMaxIntervals; ++i) {
370 if (GetTabCount() == tab_count)
373 base::PlatformThread::Sleep(kDelay);
376 ADD_FAILURE() << "Timeout reached in WaitUntilTabCount";
379 const base::FilePath::CharType* UITestBase::GetExecutablePath() {
380 if (launch_arguments_.HasSwitch(switches::kEnableChromiumBranding))
381 return chrome::kBrowserProcessExecutablePathChromium;
382 return chrome::kBrowserProcessExecutablePath;
385 bool UITestBase::CloseBrowser(BrowserProxy* browser,
386 bool* application_closed) const {
387 DCHECK(application_closed);
388 if (!browser->is_valid() || !browser->handle())
393 ChromeProcessList processes = GetRunningChromeProcesses(
394 browser_process_id());
396 bool succeeded = automation()->Send(new AutomationMsg_CloseBrowser(
397 browser->handle(), &result, application_closed));
402 if (*application_closed) {
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);
414 int UITestBase::GetCrashCount() const {
415 base::FilePath crash_dump_path;
416 PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dump_path);
419 base::FileEnumerator en(crash_dump_path, false, base::FileEnumerator::FILES);
420 while (!en.Next().empty()) {
421 if (en.GetInfo().GetLastModifiedTime() > test_start_time_)
426 // Each crash creates two dump files on Windows.
427 return files_found / 2;
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);
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) {
448 EXPECT_EQ(expected_errors_, assertions.size()) << failures;
450 int actual_crashes = GetCrashCount();
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) {
456 error_msg += kFailedNoCrashService;
458 EXPECT_EQ(expected_crashes_, actual_crashes) << error_msg;
460 std::wstring wide_result;
461 if (expected_errors_ != assertions.size()) {
462 wide_result += failures;
463 wide_result += L"\n\n";
465 if (expected_crashes_ != actual_crashes)
466 wide_result += error_msg;
468 return std::string(wide_result.begin(), wide_result.end());
471 void UITestBase::SetBrowserDirectory(const base::FilePath& dir) {
472 browser_directory_ = dir;
475 void UITestBase::AppendBrowserLaunchSwitch(const char* name) {
476 launch_arguments_.AppendSwitch(name);
479 void UITestBase::AppendBrowserLaunchSwitch(const char* name,
481 launch_arguments_.AppendSwitchASCII(name, value);
484 bool UITestBase::BeginTracing(const std::string& category_patterns) {
485 return automation()->BeginTracing(category_patterns);
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;
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();
503 set_test_name(test_info->test_case_name() + std::string(".") +
508 PlatformTest::SetUp();
511 void UITest::TearDown() {
512 UITestBase::TearDown();
513 PlatformTest::TearDown();
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);
523 bool UITest::GetBrowserProcessCount(int* count) {
525 if (!automation()->WaitForProcessLauncherThreadToGoIdle())
527 *count = GetRunningChromeProcesses(browser_process_id()).size();
531 static DictionaryValue* LoadDictionaryValueFromPath(
532 const base::FilePath& path) {
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)
541 return static_cast<DictionaryValue*>(root_value.release());
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);
550 DictionaryValue* UITest::GetDefaultProfilePreferences() {
552 PathService::Get(chrome::DIR_USER_DATA, &path);
553 path = path.AppendASCII(TestingProfile::kTestUserProfileDir);
554 return LoadDictionaryValueFromPath(path.Append(chrome::kPreferencesFilename));
557 void UITest::WaitForFinish(const std::string &name,
558 const std::string &id,
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
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);
574 scoped_refptr<TabProxy> tab(GetActiveTab());
575 ASSERT_TRUE(tab.get());
576 std::string cookie_value = WaitUntilCookieNonEmpty(tab.get(), url,
579 EXPECT_EQ(expected_cookie_value, cookie_value);
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;
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,
594 EXPECT_TRUE(success);
600 base::PlatformThread::Sleep(kDelay);
603 ADD_FAILURE() << "Timeout reached in WaitUntilJavaScriptCondition";
607 bool UITest::WaitUntilCookieValue(TabProxy* tab,
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;
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)
621 base::PlatformThread::Sleep(kDelay);
624 ADD_FAILURE() << "Timeout reached in WaitUntilCookieValue";
628 std::string UITest::WaitUntilCookieNonEmpty(TabProxy* tab,
630 const char* cookie_name,
631 base::TimeDelta timeout) {
632 const TimeDelta kDelay = TimeDelta::FromMilliseconds(250);
633 const int kMaxDelays = timeout / kDelay;
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())
641 base::PlatformThread::Sleep(kDelay);
644 ADD_FAILURE() << "Timeout reached in WaitUntilCookieNonEmpty";
645 return std::string();
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.
659 // Give it a chance to catch up.
660 base::PlatformThread::Sleep(kDelay);
663 ADD_FAILURE() << "Timeout reached in WaitForFindWindowVisibilityChange";
667 void UITest::TerminateBrowser() {
668 launcher_->TerminateBrowser();
670 // Make sure the UMA metrics say we didn't crash.
671 scoped_ptr<DictionaryValue> local_prefs(GetLocalState());
673 ASSERT_TRUE(local_prefs.get());
674 ASSERT_TRUE(local_prefs->GetBoolean(prefs::kStabilityExitedCleanly,
676 ASSERT_TRUE(exited_cleanly);
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);
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,
690 EXPECT_EQ(ProfileImpl::kPrefExitTypeNormal, exit_type);