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 "extensions/common/switches.h"
54 #include "net/base/net_util.h"
55 #include "ui/gl/gl_implementation.h"
59 #include "base/win/windows_version.h"
63 using base::TimeDelta
;
64 using base::TimeTicks
;
66 const wchar_t UITestBase::kFailedNoCrashService
[] =
68 L
"NOTE: This test is expected to fail if crash_service.exe is not "
69 L
"running. Start it manually before running this test (see the build "
70 L
"output directory).";
71 #elif defined(OS_LINUX)
72 L
"NOTE: This test is expected to fail if breakpad is not built in "
73 L
"or if chromium is not running headless (try CHROME_HEADLESS=1).";
75 L
"NOTE: Crash service not ported to this platform!";
78 UITestBase::UITestBase()
79 : launch_arguments_(CommandLine::NO_PROGRAM
),
82 homepage_(content::kAboutBlankURL
),
83 wait_for_initial_loads_(true),
84 dom_automation_enabled_(false),
85 stats_collection_controller_enabled_(false),
88 include_testing_id_(true),
89 enable_file_cookies_(true) {
90 PathService::Get(chrome::DIR_APP
, &browser_directory_
);
91 PathService::Get(chrome::DIR_TEST_DATA
, &test_data_directory_
);
94 UITestBase::UITestBase(base::MessageLoop::Type msg_loop_type
)
95 : launch_arguments_(CommandLine::NO_PROGRAM
),
98 wait_for_initial_loads_(true),
99 dom_automation_enabled_(false),
100 stats_collection_controller_enabled_(false),
102 clear_profile_(true),
103 include_testing_id_(true),
104 enable_file_cookies_(true) {
105 PathService::Get(chrome::DIR_APP
, &browser_directory_
);
106 PathService::Get(chrome::DIR_TEST_DATA
, &test_data_directory_
);
109 UITestBase::~UITestBase() {}
111 void UITestBase::SetUp() {
112 // Tests that do a session restore (e.g. SessionRestoreUITest, StartupTest)
113 // call SetUp() multiple times because they restart the browser mid-test.
114 // We don't want to reset the ProxyLauncher's state in those cases.
115 if (!launcher_
.get())
116 launcher_
.reset(CreateProxyLauncher());
117 launcher_
->AssertAppNotRunning("Please close any other instances "
118 "of the app before testing.");
120 test_start_time_
= Time::NowFromSystemTime();
123 ASSERT_TRUE(launcher_
->InitializeConnection(DefaultLaunchState(),
124 wait_for_initial_loads_
));
127 void UITestBase::TearDown() {
129 launcher_
->TerminateConnection();
131 CheckErrorsAndCrashes();
134 AutomationProxy
* UITestBase::automation() const {
135 return launcher_
->automation();
138 base::TimeDelta
UITestBase::action_timeout() {
139 return automation()->action_timeout();
142 int UITestBase::action_timeout_ms() {
143 return action_timeout().InMilliseconds();
146 void UITestBase::set_action_timeout(base::TimeDelta timeout
) {
147 automation()->set_action_timeout(timeout
);
148 VLOG(1) << "Automation action timeout set to "
149 << timeout
.InMilliseconds() << " ms";
152 void UITestBase::set_action_timeout_ms(int timeout
) {
153 set_action_timeout(base::TimeDelta::FromMilliseconds(timeout
));
156 ProxyLauncher
* UITestBase::CreateProxyLauncher() {
157 return new AnonymousProxyLauncher(false);
160 ProxyLauncher::LaunchState
UITestBase::DefaultLaunchState() {
161 base::FilePath browser_executable
=
162 browser_directory_
.Append(GetExecutablePath());
163 CommandLine
command(browser_executable
);
164 command
.AppendArguments(launch_arguments_
, false);
165 base::Closure setup_profile_callback
= base::Bind(&UITestBase::SetUpProfile
,
166 base::Unretained(this));
167 ProxyLauncher::LaunchState state
=
168 { clear_profile_
, template_user_data_
, setup_profile_callback
,
169 command
, include_testing_id_
, show_window_
};
173 void UITestBase::SetLaunchSwitches() {
174 // All flags added here should also be added in ExtraChromeFlags() in
175 // chrome/test/pyautolib/pyauto.py as well to take effect for all tests
178 // Propagate commandline settings from test_launcher_utils.
179 test_launcher_utils::PrepareBrowserCommandLineForTests(&launch_arguments_
);
181 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kWaitForDebugger
))
182 launch_arguments_
.AppendSwitch(switches::kWaitForDebugger
);
184 // We need cookies on file:// for things like the page cycler.
185 if (enable_file_cookies_
)
186 launch_arguments_
.AppendSwitch(switches::kEnableFileCookies
);
187 if (dom_automation_enabled_
)
188 launch_arguments_
.AppendSwitch(switches::kDomAutomationController
);
189 if (stats_collection_controller_enabled_
)
190 launch_arguments_
.AppendSwitch(switches::kStatsCollectionController
);
191 // Allow off-store extension installs.
192 launch_arguments_
.AppendSwitchASCII(
193 extensions::switches::kEasyOffStoreExtensionInstall
, "1");
194 if (!homepage_
.empty()) {
195 // Pass |homepage_| both as an arg (so that it opens on startup) and to the
196 // homepage switch (so that the homepage is set).
198 if (!launch_arguments_
.HasSwitch(switches::kHomePage
))
199 launch_arguments_
.AppendSwitchASCII(switches::kHomePage
, homepage_
);
201 if (launch_arguments_
.GetArgs().empty() &&
202 !launch_arguments_
.HasSwitch(switches::kRestoreLastSession
)) {
203 launch_arguments_
.AppendArg(homepage_
);
206 if (!test_name_
.empty())
207 launch_arguments_
.AppendSwitchASCII(switches::kTestName
, test_name_
);
210 void UITestBase::SetUpProfile() {
213 void UITestBase::LaunchBrowser() {
214 LaunchBrowser(launch_arguments_
, clear_profile_
);
217 void UITestBase::LaunchBrowserAndServer() {
218 ASSERT_TRUE(launcher_
->LaunchBrowserAndServer(DefaultLaunchState(),
219 wait_for_initial_loads_
));
222 void UITestBase::ConnectToRunningBrowser() {
223 ASSERT_TRUE(launcher_
->ConnectToRunningBrowser(wait_for_initial_loads_
));
226 void UITestBase::CloseBrowserAndServer() {
228 launcher_
->CloseBrowserAndServer();
231 void UITestBase::LaunchBrowser(const CommandLine
& arguments
,
232 bool clear_profile
) {
233 ProxyLauncher::LaunchState state
= DefaultLaunchState();
234 state
.clear_profile
= clear_profile
;
235 ASSERT_TRUE(launcher_
->LaunchBrowser(state
));
238 void UITestBase::QuitBrowser() {
239 launcher_
->QuitBrowser();
242 scoped_refptr
<TabProxy
> UITestBase::GetActiveTab(int window_index
) {
243 EXPECT_GE(window_index
, 0);
244 int window_count
= -1;
245 // We have to use EXPECT rather than ASSERT here because ASSERT_* only works
246 // in functions that return void.
247 EXPECT_TRUE(automation()->GetBrowserWindowCount(&window_count
));
248 if (window_count
== -1)
250 EXPECT_GT(window_count
, window_index
);
251 scoped_refptr
<BrowserProxy
> window_proxy(automation()->
252 GetBrowserWindow(window_index
));
253 EXPECT_TRUE(window_proxy
.get());
254 if (!window_proxy
.get())
257 int active_tab_index
= -1;
258 EXPECT_TRUE(window_proxy
->GetActiveTabIndex(&active_tab_index
));
259 if (active_tab_index
== -1)
262 return window_proxy
->GetTab(active_tab_index
);
265 scoped_refptr
<TabProxy
> UITestBase::GetActiveTab() {
266 scoped_refptr
<BrowserProxy
> window_proxy(automation()->
267 GetBrowserWindow(0));
268 EXPECT_TRUE(window_proxy
.get());
269 if (!window_proxy
.get())
272 scoped_refptr
<TabProxy
> tab_proxy
= window_proxy
->GetActiveTab();
273 EXPECT_TRUE(tab_proxy
.get());
277 void UITestBase::NavigateToURL(const GURL
& url
) {
278 NavigateToURL(url
, 0, GetActiveTabIndex(0));
281 void UITestBase::NavigateToURL(const GURL
& url
, int window_index
) {
282 NavigateToURL(url
, window_index
, GetActiveTabIndex(window_index
));
285 void UITestBase::NavigateToURL(const GURL
& url
, int window_index
, int
287 NavigateToURLBlockUntilNavigationsComplete(url
, 1, window_index
, tab_index
);
290 void UITestBase::NavigateToURLBlockUntilNavigationsComplete(
291 const GURL
& url
, int number_of_navigations
) {
292 scoped_refptr
<TabProxy
> tab_proxy(GetActiveTab());
293 ASSERT_TRUE(tab_proxy
.get());
294 EXPECT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS
,
295 tab_proxy
->NavigateToURLBlockUntilNavigationsComplete(
296 url
, number_of_navigations
)) << url
.spec();
299 void UITestBase::NavigateToURLBlockUntilNavigationsComplete(
300 const GURL
& url
, int number_of_navigations
, int window_index
,
302 scoped_refptr
<BrowserProxy
> window
=
303 automation()->GetBrowserWindow(window_index
);
304 ASSERT_TRUE(window
.get());
305 scoped_refptr
<TabProxy
> tab_proxy(window
->GetTab(tab_index
));
306 ASSERT_TRUE(tab_proxy
.get());
307 EXPECT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS
,
308 tab_proxy
->NavigateToURLBlockUntilNavigationsComplete(
309 url
, number_of_navigations
)) << url
.spec();
312 GURL
UITestBase::GetActiveTabURL(int window_index
) {
313 scoped_refptr
<TabProxy
> tab_proxy(GetActiveTab(window_index
));
314 EXPECT_TRUE(tab_proxy
.get());
315 if (!tab_proxy
.get())
319 bool success
= tab_proxy
->GetCurrentURL(&url
);
320 EXPECT_TRUE(success
);
326 std::wstring
UITestBase::GetActiveTabTitle(int window_index
) {
328 scoped_refptr
<TabProxy
> tab_proxy(GetActiveTab(window_index
));
329 EXPECT_TRUE(tab_proxy
.get());
330 if (!tab_proxy
.get())
333 EXPECT_TRUE(tab_proxy
->GetTabTitle(&title
));
337 int UITestBase::GetActiveTabIndex(int window_index
) {
338 scoped_refptr
<BrowserProxy
> window_proxy(automation()->
339 GetBrowserWindow(window_index
));
340 EXPECT_TRUE(window_proxy
.get());
341 if (!window_proxy
.get())
344 int active_tab_index
= -1;
345 EXPECT_TRUE(window_proxy
->GetActiveTabIndex(&active_tab_index
));
346 return active_tab_index
;
349 int UITestBase::GetTabCount() {
350 return GetTabCount(0);
353 int UITestBase::GetTabCount(int window_index
) {
354 scoped_refptr
<BrowserProxy
> window(
355 automation()->GetBrowserWindow(window_index
));
356 EXPECT_TRUE(window
.get());
361 EXPECT_TRUE(window
->GetTabCount(&result
));
366 void UITestBase::WaitUntilTabCount(int tab_count
) {
367 const int kMaxIntervals
= 10;
368 const TimeDelta kDelay
= TestTimeouts::action_timeout() / kMaxIntervals
;
370 for (int i
= 0; i
< kMaxIntervals
; ++i
) {
371 if (GetTabCount() == tab_count
)
374 base::PlatformThread::Sleep(kDelay
);
377 ADD_FAILURE() << "Timeout reached in WaitUntilTabCount";
380 const base::FilePath::CharType
* UITestBase::GetExecutablePath() {
381 if (launch_arguments_
.HasSwitch(switches::kEnableChromiumBranding
))
382 return chrome::kBrowserProcessExecutablePathChromium
;
383 return chrome::kBrowserProcessExecutablePath
;
386 bool UITestBase::CloseBrowser(BrowserProxy
* browser
,
387 bool* application_closed
) const {
388 DCHECK(application_closed
);
389 if (!browser
->is_valid() || !browser
->handle())
394 ChromeProcessList processes
= GetRunningChromeProcesses(
395 browser_process_id());
397 bool succeeded
= automation()->Send(new AutomationMsg_CloseBrowser(
398 browser
->handle(), &result
, application_closed
));
403 if (*application_closed
) {
405 EXPECT_TRUE(launcher_
->WaitForBrowserProcessToQuit(
406 TestTimeouts::action_max_timeout(), &exit_code
));
407 EXPECT_EQ(0, exit_code
); // Expect a clean shutown.
408 // Ensure no child processes are left dangling.
409 TerminateAllChromeProcesses(processes
);
415 int UITestBase::GetCrashCount() const {
416 base::FilePath crash_dump_path
;
417 PathService::Get(chrome::DIR_CRASH_DUMPS
, &crash_dump_path
);
420 base::FileEnumerator
en(crash_dump_path
, false, base::FileEnumerator::FILES
);
421 while (!en
.Next().empty()) {
422 if (en
.GetInfo().GetLastModifiedTime() > test_start_time_
)
427 // Each crash creates two dump files on Windows.
428 return files_found
/ 2;
434 std::string
UITestBase::CheckErrorsAndCrashes() const {
435 // Make sure that we didn't encounter any assertion failures
436 logging::AssertionList assertions
;
437 logging::GetFatalAssertions(&assertions
);
439 // If there were errors, get all the error strings for display.
440 std::wstring failures
=
441 L
"The following error(s) occurred in the application during this test:";
442 if (assertions
.size() > expected_errors_
) {
443 logging::AssertionList::const_iterator iter
= assertions
.begin();
444 for (; iter
!= assertions
.end(); ++iter
) {
449 EXPECT_EQ(expected_errors_
, assertions
.size()) << failures
;
451 int actual_crashes
= GetCrashCount();
453 std::wstring error_msg
=
454 L
"Encountered an unexpected crash in the program during this test.";
455 if (expected_crashes_
> 0 && actual_crashes
== 0) {
457 error_msg
+= kFailedNoCrashService
;
459 EXPECT_EQ(expected_crashes_
, actual_crashes
) << error_msg
;
461 std::wstring wide_result
;
462 if (expected_errors_
!= assertions
.size()) {
463 wide_result
+= failures
;
464 wide_result
+= L
"\n\n";
466 if (expected_crashes_
!= actual_crashes
)
467 wide_result
+= error_msg
;
469 return std::string(wide_result
.begin(), wide_result
.end());
472 void UITestBase::SetBrowserDirectory(const base::FilePath
& dir
) {
473 browser_directory_
= dir
;
476 void UITestBase::AppendBrowserLaunchSwitch(const char* name
) {
477 launch_arguments_
.AppendSwitch(name
);
480 void UITestBase::AppendBrowserLaunchSwitch(const char* name
,
482 launch_arguments_
.AppendSwitchASCII(name
, value
);
485 bool UITestBase::BeginTracing(const std::string
& category_patterns
) {
486 return automation()->BeginTracing(category_patterns
);
489 std::string
UITestBase::EndTracing() {
490 std::string json_trace_output
;
491 if (!automation()->EndTracing(&json_trace_output
))
492 return std::string();
493 return json_trace_output
;
498 void UITest::SetUp() {
499 // Pass the test case name to chrome.exe on the command line to help with
500 // parsing Purify output.
501 const testing::TestInfo
* const test_info
=
502 testing::UnitTest::GetInstance()->current_test_info();
504 set_test_name(test_info
->test_case_name() + std::string(".") +
509 PlatformTest::SetUp();
512 void UITest::TearDown() {
513 UITestBase::TearDown();
514 PlatformTest::TearDown();
517 ProxyLauncher
* UITest::CreateProxyLauncher() {
518 // Make the AutomationProxy disconnect the channel on the first error,
519 // so that we avoid spending a lot of time in timeouts. The browser is likely
520 // hosed if we hit those errors.
521 return new AnonymousProxyLauncher(true);
524 bool UITest::GetBrowserProcessCount(int* count
) {
526 if (!automation()->WaitForProcessLauncherThreadToGoIdle())
528 *count
= GetRunningChromeProcesses(browser_process_id()).size();
532 static base::DictionaryValue
* LoadDictionaryValueFromPath(
533 const base::FilePath
& path
) {
537 JSONFileValueSerializer
serializer(path
);
538 scoped_ptr
<base::Value
> root_value(serializer
.Deserialize(NULL
, NULL
));
539 if (!root_value
.get() ||
540 root_value
->GetType() != base::Value::TYPE_DICTIONARY
) {
544 return static_cast<base::DictionaryValue
*>(root_value
.release());
547 base::DictionaryValue
* UITest::GetLocalState() {
548 base::FilePath local_state_path
;
549 PathService::Get(chrome::FILE_LOCAL_STATE
, &local_state_path
);
550 return LoadDictionaryValueFromPath(local_state_path
);
553 base::DictionaryValue
* UITest::GetDefaultProfilePreferences() {
555 PathService::Get(chrome::DIR_USER_DATA
, &path
);
556 path
= path
.AppendASCII(TestingProfile::kTestUserProfileDir
);
557 return LoadDictionaryValueFromPath(path
.Append(chrome::kPreferencesFilename
));
560 void UITest::WaitForFinish(const std::string
&name
,
561 const std::string
&id
,
563 const std::string
& test_complete_cookie
,
564 const std::string
& expected_cookie_value
,
565 const base::TimeDelta wait_time
) {
566 // The webpage being tested has javascript which sets a cookie
567 // which signals completion of the test. The cookie name is
568 // a concatenation of the test name and the test id. This allows
569 // us to run multiple tests within a single webpage and test
571 std::string cookie_name
= name
;
572 cookie_name
.append(".");
573 cookie_name
.append(id
);
574 cookie_name
.append(".");
575 cookie_name
.append(test_complete_cookie
);
577 scoped_refptr
<TabProxy
> tab(GetActiveTab());
578 ASSERT_TRUE(tab
.get());
579 std::string cookie_value
= WaitUntilCookieNonEmpty(tab
.get(), url
,
582 EXPECT_EQ(expected_cookie_value
, cookie_value
);
585 bool UITest::WaitUntilJavaScriptCondition(TabProxy
* tab
,
586 const std::wstring
& frame_xpath
,
587 const std::wstring
& jscript
,
588 base::TimeDelta timeout
) {
589 const TimeDelta kDelay
= TimeDelta::FromMilliseconds(250);
590 const int kMaxDelays
= timeout
/ kDelay
;
592 // Wait until the test signals it has completed.
593 for (int i
= 0; i
< kMaxDelays
; ++i
) {
594 bool done_value
= false;
595 bool success
= tab
->ExecuteAndExtractBool(frame_xpath
, jscript
,
597 EXPECT_TRUE(success
);
603 base::PlatformThread::Sleep(kDelay
);
606 ADD_FAILURE() << "Timeout reached in WaitUntilJavaScriptCondition";
610 bool UITest::WaitUntilCookieValue(TabProxy
* tab
,
612 const char* cookie_name
,
613 base::TimeDelta timeout
,
614 const char* expected_value
) {
615 const TimeDelta kDelay
= TimeDelta::FromMilliseconds(250);
616 const int kMaxDelays
= timeout
/ kDelay
;
618 std::string cookie_value
;
619 for (int i
= 0; i
< kMaxDelays
; ++i
) {
620 EXPECT_TRUE(tab
->GetCookieByName(url
, cookie_name
, &cookie_value
));
621 if (cookie_value
== expected_value
)
624 base::PlatformThread::Sleep(kDelay
);
627 ADD_FAILURE() << "Timeout reached in WaitUntilCookieValue";
631 std::string
UITest::WaitUntilCookieNonEmpty(TabProxy
* tab
,
633 const char* cookie_name
,
634 base::TimeDelta timeout
) {
635 const TimeDelta kDelay
= TimeDelta::FromMilliseconds(250);
636 const int kMaxDelays
= timeout
/ kDelay
;
638 for (int i
= 0; i
< kMaxDelays
; ++i
) {
639 std::string cookie_value
;
640 EXPECT_TRUE(tab
->GetCookieByName(url
, cookie_name
, &cookie_value
));
641 if (!cookie_value
.empty())
644 base::PlatformThread::Sleep(kDelay
);
647 ADD_FAILURE() << "Timeout reached in WaitUntilCookieNonEmpty";
648 return std::string();
651 bool UITest::WaitForFindWindowVisibilityChange(BrowserProxy
* browser
,
652 bool wait_for_open
) {
653 const int kCycles
= 10;
654 const TimeDelta kDelay
= TestTimeouts::action_timeout() / kCycles
;
655 for (int i
= 0; i
< kCycles
; i
++) {
656 bool visible
= false;
657 if (!browser
->IsFindWindowFullyVisible(&visible
))
658 return false; // Some error.
659 if (visible
== wait_for_open
)
660 return true; // Find window visibility change complete.
662 // Give it a chance to catch up.
663 base::PlatformThread::Sleep(kDelay
);
666 ADD_FAILURE() << "Timeout reached in WaitForFindWindowVisibilityChange";
670 void UITest::TerminateBrowser() {
671 launcher_
->TerminateBrowser();
673 // Make sure the UMA metrics say we didn't crash.
674 scoped_ptr
<base::DictionaryValue
> local_prefs(GetLocalState());
676 ASSERT_TRUE(local_prefs
.get());
677 ASSERT_TRUE(local_prefs
->GetBoolean(prefs::kStabilityExitedCleanly
,
679 ASSERT_TRUE(exited_cleanly
);
681 // And that session end was successful.
682 bool session_end_completed
;
683 ASSERT_TRUE(local_prefs
->GetBoolean(prefs::kStabilitySessionEndCompleted
,
684 &session_end_completed
));
685 ASSERT_TRUE(session_end_completed
);
687 // Make sure session restore says we didn't crash.
688 scoped_ptr
<base::DictionaryValue
> profile_prefs(
689 GetDefaultProfilePreferences());
690 ASSERT_TRUE(profile_prefs
.get());
691 std::string exit_type
;
692 ASSERT_TRUE(profile_prefs
->GetString(prefs::kSessionExitedCleanly
,
694 EXPECT_EQ(ProfileImpl::kPrefExitTypeNormal
, exit_type
);