Commit 2449703f authored by huanr@chromium.org's avatar huanr@chromium.org

Making CloseWindow and CloseTab automation API

synchronous and robust.

Adding automation APIs with corresponding
IPC messages to count and find normal browser
windows.


Review URL: http://codereview.chromium.org/99268

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15058 0039d316-1c4b-4281-b951-d872f2087c98
parent d4bf3bf8
...@@ -902,10 +902,14 @@ void AutomationProvider::OnMessageReceived(const IPC::Message& message) { ...@@ -902,10 +902,14 @@ void AutomationProvider::OnMessageReceived(const IPC::Message& message) {
GetRedirectsFrom) GetRedirectsFrom)
IPC_MESSAGE_HANDLER(AutomationMsg_BrowserWindowCount, IPC_MESSAGE_HANDLER(AutomationMsg_BrowserWindowCount,
GetBrowserWindowCount) GetBrowserWindowCount)
IPC_MESSAGE_HANDLER(AutomationMsg_NormalBrowserWindowCount,
GetNormalBrowserWindowCount)
IPC_MESSAGE_HANDLER(AutomationMsg_BrowserWindow, GetBrowserWindow) IPC_MESSAGE_HANDLER(AutomationMsg_BrowserWindow, GetBrowserWindow)
IPC_MESSAGE_HANDLER(AutomationMsg_LastActiveBrowserWindow, IPC_MESSAGE_HANDLER(AutomationMsg_LastActiveBrowserWindow,
GetLastActiveBrowserWindow) GetLastActiveBrowserWindow)
IPC_MESSAGE_HANDLER(AutomationMsg_ActiveWindow, GetActiveWindow) IPC_MESSAGE_HANDLER(AutomationMsg_ActiveWindow, GetActiveWindow)
IPC_MESSAGE_HANDLER(AutomationMsg_FindNormalBrowserWindow,
FindNormalBrowserWindow)
IPC_MESSAGE_HANDLER(AutomationMsg_IsWindowActive, IsWindowActive) IPC_MESSAGE_HANDLER(AutomationMsg_IsWindowActive, IsWindowActive)
IPC_MESSAGE_HANDLER(AutomationMsg_ActivateWindow, ActivateWindow); IPC_MESSAGE_HANDLER(AutomationMsg_ActivateWindow, ActivateWindow);
#if defined(OS_WIN) #if defined(OS_WIN)
...@@ -1304,6 +1308,11 @@ void AutomationProvider::GetBrowserWindowCount(int* window_count) { ...@@ -1304,6 +1308,11 @@ void AutomationProvider::GetBrowserWindowCount(int* window_count) {
*window_count = static_cast<int>(BrowserList::size()); *window_count = static_cast<int>(BrowserList::size());
} }
void AutomationProvider::GetNormalBrowserWindowCount(int* window_count) {
*window_count = static_cast<int>(
BrowserList::GetBrowserCountForType(profile_, Browser::TYPE_NORMAL));
}
void AutomationProvider::GetShowingAppModalDialog(bool* showing_dialog, void AutomationProvider::GetShowingAppModalDialog(bool* showing_dialog,
int* dialog_button) { int* dialog_button) {
AppModalDialog* dialog_delegate = AppModalDialogQueue::active_dialog(); AppModalDialog* dialog_delegate = AppModalDialogQueue::active_dialog();
...@@ -1338,14 +1347,21 @@ void AutomationProvider::GetBrowserWindow(int index, int* handle) { ...@@ -1338,14 +1347,21 @@ void AutomationProvider::GetBrowserWindow(int index, int* handle) {
*handle = 0; *handle = 0;
if (index >= 0) { if (index >= 0) {
BrowserList::const_iterator iter = BrowserList::begin(); BrowserList::const_iterator iter = BrowserList::begin();
for (; (iter != BrowserList::end()) && (index > 0); ++iter, --index);
for (; (iter != BrowserList::end()) && (index > 0); ++iter, --index);
if (iter != BrowserList::end()) { if (iter != BrowserList::end()) {
*handle = browser_tracker_->Add(*iter); *handle = browser_tracker_->Add(*iter);
} }
} }
} }
void AutomationProvider::FindNormalBrowserWindow(int* handle) {
*handle = 0;
Browser* browser = BrowserList::FindBrowserWithType(profile_,
Browser::TYPE_NORMAL);
if (browser)
*handle = browser_tracker_->Add(browser);
}
void AutomationProvider::GetLastActiveBrowserWindow(int* handle) { void AutomationProvider::GetLastActiveBrowserWindow(int* handle) {
*handle = 0; *handle = 0;
Browser* browser = BrowserList::GetLastActive(); Browser* browser = BrowserList::GetLastActive();
......
...@@ -132,9 +132,13 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, ...@@ -132,9 +132,13 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>,
int handle, int handle,
int* response_value); int* response_value);
void GetBrowserWindowCount(int* window_count); void GetBrowserWindowCount(int* window_count);
void GetNormalBrowserWindowCount(int* window_count);
void GetShowingAppModalDialog(bool* showing_dialog, int* dialog_button); void GetShowingAppModalDialog(bool* showing_dialog, int* dialog_button);
void ClickAppModalDialogButton(int button, bool* success); void ClickAppModalDialogButton(int button, bool* success);
// Be aware that the browser window returned might be of non TYPE_NORMAL
// or in incognito mode.
void GetBrowserWindow(int index, int* handle); void GetBrowserWindow(int index, int* handle);
void FindNormalBrowserWindow(int* handle);
void GetLastActiveBrowserWindow(int* handle); void GetLastActiveBrowserWindow(int* handle);
void GetActiveWindow(int* handle); void GetActiveWindow(int* handle);
#if defined(OS_WIN) #if defined(OS_WIN)
......
...@@ -25,6 +25,54 @@ void AutomatedUITestBase::SetUp() { ...@@ -25,6 +25,54 @@ void AutomatedUITestBase::SetUp() {
set_active_browser(automation()->GetBrowserWindow(0)); set_active_browser(automation()->GetBrowserWindow(0));
} }
bool AutomatedUITestBase::CloseActiveTab() {
BrowserProxy* browser = active_browser();
int tab_count;
bool is_timeout;
browser->GetTabCountWithTimeout(&tab_count,
action_max_timeout_ms(),
&is_timeout);
if (is_timeout) {
LogInfoMessage("get_tab_count_timed_out");
return false;
}
if (tab_count > 1) {
scoped_ptr<TabProxy> tab(GetActiveTab());
// Wait until tab is closed.
return tab->Close(true);
} else if (tab_count == 1) {
// Synchronously close the window if it is not the last window.
return CloseActiveWindow();
} else {
LogInfoMessage("invalid_tab_count");
return false;
}
}
bool AutomatedUITestBase::CloseActiveWindow() {
int browser_windows_count = 0;
if (!automation()->GetNormalBrowserWindowCount(&browser_windows_count))
return false;
// Avoid quitting the application by not closing the last window.
if (browser_windows_count < 2)
return false;
bool application_closed;
CloseBrowser(active_browser(), &application_closed);
if (application_closed) {
LogErrorMessage("Application closed unexpectedly.");
return false;
}
BrowserProxy* browser = automation()->FindNormalBrowserWindow();
if (browser == NULL) {
LogErrorMessage("Can't find browser window.");
return false;
}
set_active_browser(browser);
return true;
}
bool AutomatedUITestBase::DuplicateTab() { bool AutomatedUITestBase::DuplicateTab() {
return RunCommand(IDC_DUPLICATE_TAB); return RunCommand(IDC_DUPLICATE_TAB);
} }
...@@ -91,3 +139,18 @@ bool AutomatedUITestBase::RunCommand(int browser_command) { ...@@ -91,3 +139,18 @@ bool AutomatedUITestBase::RunCommand(int browser_command) {
} }
return true; return true;
} }
TabProxy* AutomatedUITestBase::GetActiveTab() {
BrowserProxy* browser = active_browser();
if (browser == NULL) {
LogErrorMessage("browser_window_not_found");
return false;
}
bool did_timeout;
TabProxy* tab =
browser->GetActiveTabWithTimeout(action_max_timeout_ms(), &did_timeout);
if (did_timeout)
return NULL;
return tab;
}
...@@ -21,6 +21,18 @@ class AutomatedUITestBase : public UITest { ...@@ -21,6 +21,18 @@ class AutomatedUITestBase : public UITest {
// Actions // Actions
// NOTE: This list is sorted alphabetically. // NOTE: This list is sorted alphabetically.
// All functions are synchronous unless specified with Async.
// Close the selected tab in the current browser window. The function will
// not try close the tab if it is the only tab of the last normal window, so
// the application is not got closed.
// Returns true if the tab is closed, false otherwise.
bool CloseActiveTab();
// Close the current browser window if it is not the only window left.
// (Closing the last window will get application closed.)
// Returns true if the window is closed, false otherwise.
bool CloseActiveWindow();
// Duplicates the current tab. // Duplicates the current tab.
// Returns true if a duplicated tab is added. // Returns true if a duplicated tab is added.
...@@ -59,6 +71,11 @@ class AutomatedUITestBase : public UITest { ...@@ -59,6 +71,11 @@ class AutomatedUITestBase : public UITest {
} }
BrowserProxy* active_browser() const { return active_browser_.get(); } BrowserProxy* active_browser() const { return active_browser_.get(); }
// Get the selected tab within the current active browser window, then
// create a corresponding TabProxy and transfer the ownership to caller.
// If success return the pointer to the newly created TabProxy and the
// caller owns the TabProxy. Return NULL otherwise.
TabProxy* GetActiveTab();
private: private:
scoped_ptr<BrowserProxy> active_browser_; scoped_ptr<BrowserProxy> active_browser_;
......
...@@ -30,7 +30,7 @@ TEST_F(AutomatedUITestBase, DuplicateTab) { ...@@ -30,7 +30,7 @@ TEST_F(AutomatedUITestBase, DuplicateTab) {
ASSERT_EQ(3, tab_count); ASSERT_EQ(3, tab_count);
} }
TEST_F(AutomatedUITestBase, OpenCloseBrowserWindow) { TEST_F(AutomatedUITestBase, OpenBrowserWindow) {
int num_browser_windows; int num_browser_windows;
int tab_count; int tab_count;
automation()->GetBrowserWindowCount(&num_browser_windows); automation()->GetBrowserWindowCount(&num_browser_windows);
...@@ -76,3 +76,83 @@ TEST_F(AutomatedUITestBase, OpenCloseBrowserWindow) { ...@@ -76,3 +76,83 @@ TEST_F(AutomatedUITestBase, OpenCloseBrowserWindow) {
automation()->GetBrowserWindowCount(&num_browser_windows); automation()->GetBrowserWindowCount(&num_browser_windows);
ASSERT_EQ(1, num_browser_windows); ASSERT_EQ(1, num_browser_windows);
} }
TEST_F(AutomatedUITestBase, CloseBrowserWindow) {
int tab_count;
NewTab();
active_browser()->GetTabCount(&tab_count);
ASSERT_EQ(2, tab_count);
ASSERT_TRUE(OpenAndActivateNewBrowserWindow(NULL));
NewTab();
NewTab();
active_browser()->GetTabCount(&tab_count);
ASSERT_EQ(3, tab_count);
ASSERT_TRUE(OpenAndActivateNewBrowserWindow(NULL));
NewTab();
NewTab();
NewTab();
active_browser()->GetTabCount(&tab_count);
ASSERT_EQ(4, tab_count);
ASSERT_TRUE(CloseActiveWindow());
active_browser()->GetTabCount(&tab_count);
if (tab_count == 2) {
ASSERT_TRUE(CloseActiveWindow());
active_browser()->GetTabCount(&tab_count);
ASSERT_EQ(3, tab_count);
} else {
ASSERT_EQ(3, tab_count);
ASSERT_TRUE(CloseActiveWindow());
active_browser()->GetTabCount(&tab_count);
ASSERT_EQ(2, tab_count);
}
ASSERT_FALSE(CloseActiveWindow());
}
TEST_F(AutomatedUITestBase, CloseTab) {
int num_browser_windows;
int tab_count;
NewTab();
automation()->GetBrowserWindowCount(&num_browser_windows);
ASSERT_EQ(1, num_browser_windows);
active_browser()->GetTabCount(&tab_count);
ASSERT_EQ(2, tab_count);
ASSERT_TRUE(OpenAndActivateNewBrowserWindow(NULL));
NewTab();
NewTab();
active_browser()->GetTabCount(&tab_count);
ASSERT_EQ(3, tab_count);
automation()->GetBrowserWindowCount(&num_browser_windows);
ASSERT_EQ(2, num_browser_windows);
ASSERT_TRUE(CloseActiveTab());
active_browser()->GetTabCount(&tab_count);
ASSERT_EQ(2, tab_count);
ASSERT_TRUE(CloseActiveTab());
active_browser()->GetTabCount(&tab_count);
ASSERT_EQ(1, tab_count);
num_browser_windows = 0;
automation()->GetBrowserWindowCount(&num_browser_windows);
ASSERT_EQ(2, num_browser_windows);
// The browser window is closed by closing this tab.
ASSERT_TRUE(CloseActiveTab());
automation()->GetBrowserWindowCount(&num_browser_windows);
ASSERT_EQ(1, num_browser_windows);
// Active_browser_ is now the first created window.
active_browser()->GetTabCount(&tab_count);
ASSERT_EQ(2, tab_count);
ASSERT_TRUE(CloseActiveTab());
active_browser()->GetTabCount(&tab_count);
ASSERT_EQ(1, tab_count);
// The last tab should not be closed.
ASSERT_FALSE(CloseActiveTab());
active_browser()->GetTabCount(&tab_count);
ASSERT_EQ(1, tab_count);
}
...@@ -418,44 +418,6 @@ bool AutomatedUITest::ChangeEncoding() { ...@@ -418,44 +418,6 @@ bool AutomatedUITest::ChangeEncoding() {
return RunCommandAsync((*encodings)[index].encoding_id); return RunCommandAsync((*encodings)[index].encoding_id);
} }
bool AutomatedUITest::CloseActiveTab() {
bool return_value = false;
BrowserProxy* browser = active_browser();
if (browser == NULL) {
AddErrorAttribute("browser_window_not_found");
return false;
}
int browser_windows_count;
int tab_count;
bool is_timeout;
browser->GetTabCountWithTimeout(&tab_count,
action_max_timeout_ms(),
&is_timeout);
automation()->GetBrowserWindowCount(&browser_windows_count);
// Avoid quitting the application by not closing the last window.
if (tab_count > 1) {
return_value = browser->RunCommandAsync(IDC_CLOSE_TAB);
// Wait for the tab to close before we continue.
if (!browser->WaitForTabCountToBecome(tab_count - 1,
action_max_timeout_ms())) {
AddWarningAttribute("tab_count_failed_to_change");
return false;
}
} else if (tab_count == 1 && browser_windows_count > 1) {
return_value = browser->RunCommandAsync(IDC_CLOSE_TAB);
// Wait for the window to close before we continue.
if (!automation()->WaitForWindowCountToBecome(browser_windows_count - 1,
action_max_timeout_ms())) {
AddWarningAttribute("window_count_failed_to_change");
return false;
}
} else {
AddInfoAttribute("would_have_exited_application");
return false;
}
return return_value;
}
bool AutomatedUITest::FindInPage() { bool AutomatedUITest::FindInPage() {
return RunCommandAsync(IDC_FIND); return RunCommandAsync(IDC_FIND);
} }
...@@ -481,18 +443,7 @@ bool AutomatedUITest::JavaScriptDebugger() { ...@@ -481,18 +443,7 @@ bool AutomatedUITest::JavaScriptDebugger() {
} }
bool AutomatedUITest::Navigate() { bool AutomatedUITest::Navigate() {
BrowserProxy* browser = active_browser(); scoped_ptr<TabProxy> tab(GetActiveTab());
if (browser == NULL) {
AddErrorAttribute("browser_window_not_found");
return false;
}
bool did_timeout;
scoped_ptr<TabProxy> tab(
browser->GetActiveTabWithTimeout(action_max_timeout_ms(), &did_timeout));
// TODO(devint): This might be masking a bug. I can't think of many
// valid cases where we would get a browser window, but not be able
// to return an active tab. Yet this has happened and has triggered crashes.
// Investigate this.
if (tab.get() == NULL) { if (tab.get() == NULL) {
AddErrorAttribute("active_tab_not_found"); AddErrorAttribute("active_tab_not_found");
return false; return false;
...@@ -502,7 +453,7 @@ bool AutomatedUITest::Navigate() { ...@@ -502,7 +453,7 @@ bool AutomatedUITest::Navigate() {
xml_writer_.AddAttribute("url", url); xml_writer_.AddAttribute("url", url);
} }
GURL test_url(url); GURL test_url(url);
did_timeout = false; bool did_timeout = false;
tab->NavigateToURLWithTimeout(test_url, tab->NavigateToURLWithTimeout(test_url,
command_execution_timeout_ms(), command_execution_timeout_ms(),
&did_timeout); &did_timeout);
...@@ -679,12 +630,7 @@ bool AutomatedUITest::FuzzyTestDialog(int num_actions) { ...@@ -679,12 +630,7 @@ bool AutomatedUITest::FuzzyTestDialog(int num_actions) {
} }
bool AutomatedUITest::ForceCrash() { bool AutomatedUITest::ForceCrash() {
BrowserProxy* browser = active_browser(); scoped_ptr<TabProxy> tab(GetActiveTab());
if (browser == NULL) {
AddErrorAttribute("browser_window_not_found");
return false;
}
scoped_ptr<TabProxy> tab(browser->GetActiveTab());
GURL test_url("about:crash"); GURL test_url("about:crash");
bool did_timeout; bool did_timeout;
tab->NavigateToURLWithTimeout(test_url, kDebuggingTimeoutMsec, &did_timeout); tab->NavigateToURLWithTimeout(test_url, kDebuggingTimeoutMsec, &did_timeout);
......
...@@ -141,11 +141,6 @@ class AutomatedUITest : public AutomatedUITestBase { ...@@ -141,11 +141,6 @@ class AutomatedUITest : public AutomatedUITestBase {
// XML element: <ChangeEncoding/> // XML element: <ChangeEncoding/>
bool ChangeEncoding(); bool ChangeEncoding();
// Uses accelerator to close the active tab if it isn't the only tab.
// Returns false if active tab is the only tab, true otherwise.
// XML element: <CloseTab/>
bool CloseActiveTab();
// Opens one of the dialogs (chosen randomly) and exercises it. // Opens one of the dialogs (chosen randomly) and exercises it.
// XML element: <Dialog/> // XML element: <Dialog/>
bool ExerciseDialog(); bool ExerciseDialog();
......
...@@ -895,4 +895,14 @@ IPC_BEGIN_MESSAGES(Automation) ...@@ -895,4 +895,14 @@ IPC_BEGIN_MESSAGES(Automation)
// The return value contains the index, which will be -1 on failure. // The return value contains the index, which will be -1 on failure.
IPC_SYNC_MESSAGE_ROUTED1_1(AutomationMsg_TabIndex, int, int) IPC_SYNC_MESSAGE_ROUTED1_1(AutomationMsg_TabIndex, int, int)
// This message requests the handle (int64 app-unique identifier) of
// a valid normal browser window, i.e. normal type and non-incognito mode.
// On error, the returned handle value is 0.
IPC_SYNC_MESSAGE_ROUTED0_1(AutomationMsg_FindNormalBrowserWindow, int)
// This message requests the number of normal browser windows, i.e. normal
// type and non-incognito mode that the app currently has open. The return
// value is the number of windows.
IPC_SYNC_MESSAGE_ROUTED0_1(AutomationMsg_NormalBrowserWindowCount, int)
IPC_END_MESSAGES(Automation) IPC_END_MESSAGES(Automation)
...@@ -299,6 +299,24 @@ bool AutomationProxy::GetBrowserWindowCount(int* num_windows) { ...@@ -299,6 +299,24 @@ bool AutomationProxy::GetBrowserWindowCount(int* num_windows) {
return succeeded; return succeeded;
} }
bool AutomationProxy::GetNormalBrowserWindowCount(int* num_windows) {
if (!num_windows) {
NOTREACHED();
return false;
}
bool succeeded = SendWithTimeout(
new AutomationMsg_NormalBrowserWindowCount(0, num_windows),
command_execution_timeout_ms(), NULL);
if (!succeeded) {
DLOG(ERROR) << "GetNormalWindowCount did not complete in a timely fashion";
return false;
}
return succeeded;
}
bool AutomationProxy::WaitForWindowCountToBecome(int count, bool AutomationProxy::WaitForWindowCountToBecome(int count,
int wait_timeout) { int wait_timeout) {
const TimeTicks start = TimeTicks::Now(); const TimeTicks start = TimeTicks::Now();
...@@ -438,7 +456,6 @@ WindowProxy* AutomationProxy::GetActiveWindow() { ...@@ -438,7 +456,6 @@ WindowProxy* AutomationProxy::GetActiveWindow() {
return new WindowProxy(this, tracker_.get(), handle); return new WindowProxy(this, tracker_.get(), handle);
} }
BrowserProxy* AutomationProxy::GetBrowserWindow(int window_index) { BrowserProxy* AutomationProxy::GetBrowserWindow(int window_index) {
int handle = 0; int handle = 0;
...@@ -456,6 +473,21 @@ BrowserProxy* AutomationProxy::GetBrowserWindow(int window_index) { ...@@ -456,6 +473,21 @@ BrowserProxy* AutomationProxy::GetBrowserWindow(int window_index) {
return new BrowserProxy(this, tracker_.get(), handle); return new BrowserProxy(this, tracker_.get(), handle);
} }
BrowserProxy* AutomationProxy::FindNormalBrowserWindow() {
int handle = 0;
if (!SendWithTimeout(new AutomationMsg_FindNormalBrowserWindow(0, &handle),
command_execution_timeout_ms(), NULL)) {
return NULL;
}
if (handle == 0) {
return NULL;
}
return new BrowserProxy(this, tracker_.get(), handle);
}
BrowserProxy* AutomationProxy::GetLastActiveBrowserWindow() { BrowserProxy* AutomationProxy::GetLastActiveBrowserWindow() {
int handle = 0; int handle = 0;
......
...@@ -94,6 +94,11 @@ class AutomationProxy : public IPC::Channel::Listener, ...@@ -94,6 +94,11 @@ class AutomationProxy : public IPC::Channel::Listener,
// Returns true on success. // Returns true on success.
bool WaitForWindowCountToBecome(int target_count, int wait_timeout); bool WaitForWindowCountToBecome(int target_count, int wait_timeout);
// Fills the number of open normal browser windows (normal type and
// non-incognito mode) into the given variable, returning true on success.
// False likely indicates an IPC error.
bool GetNormalBrowserWindowCount(int* num_windows);
// Returns whether an app modal dialog window is showing right now (i.e., a // Returns whether an app modal dialog window is showing right now (i.e., a
// javascript alert), and what buttons it contains. // javascript alert), and what buttons it contains.
bool GetShowingAppModalDialog(bool* showing_app_modal_dialog, bool GetShowingAppModalDialog(bool* showing_app_modal_dialog,
...@@ -118,6 +123,12 @@ class AutomationProxy : public IPC::Channel::Listener, ...@@ -118,6 +123,12 @@ class AutomationProxy : public IPC::Channel::Listener,
// Window numbers are 0-based. // Window numbers are 0-based.
BrowserProxy* GetBrowserWindow(int window_index); BrowserProxy* GetBrowserWindow(int window_index);
// Finds the first browser window that is not incognito mode and of type
// TYPE_NORMAL, and returns its corresponding BrowserProxy, transferring
// ownership of the pointer to the caller.
// On failure, returns NULL.
BrowserProxy* FindNormalBrowserWindow();
// Returns the BrowserProxy for the browser window which was last active, // Returns the BrowserProxy for the browser window which was last active,
// transferring ownership of the pointer to the caller. // transferring ownership of the pointer to the caller.
// TODO: If there was no last active browser window, or the last active // TODO: If there was no last active browser window, or the last active
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment