diff options
14 files changed, 1458 insertions, 1027 deletions
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.mm b/chrome/browser/ui/cocoa/browser_window_controller.mm index 8d62a40..90bb372 100644 --- a/chrome/browser/ui/cocoa/browser_window_controller.mm +++ b/chrome/browser/ui/cocoa/browser_window_controller.mm @@ -2016,8 +2016,10 @@ willAnimateFromState:(bookmarks::VisualState)oldState [self setPresentationModeInternal:YES forceDropdown:YES]; [self releaseBarVisibilityForOwner:self withAnimation:YES delay:YES]; // Since -windowDidEnterFullScreen: won't be called in the - // fullscreen --> presentation mode case, manually show the exit bubble. + // fullscreen --> presentation mode case, manually show the exit bubble + // and notify the change happened with WindowFullscreenStateChanged(). [self showFullscreenExitBubbleIfNecessary]; + browser_->WindowFullscreenStateChanged(); } else { // If not in fullscreen mode, trigger the Lion fullscreen mode machinery. // Presentation mode will automatically be enabled in diff --git a/chrome/browser/ui/fullscreen/fullscreen_controller.cc b/chrome/browser/ui/fullscreen/fullscreen_controller.cc index cf66456..289f6e2 100644 --- a/chrome/browser/ui/fullscreen/fullscreen_controller.cc +++ b/chrome/browser/ui/fullscreen/fullscreen_controller.cc @@ -110,6 +110,10 @@ void FullscreenController::ToggleFullscreenModeForTab(WebContents* web_contents, GetFullscreenSetting(url) == CONTENT_SETTING_ALLOW; } UpdateFullscreenExitBubbleContent(); + + // This is only a change between Browser and Tab fullscreen. We generate + // a fullscreen notification now because there is no window change. + PostFullscreenChangeNotification(true); } } else { if (in_browser_or_tab_fullscreen_mode) { @@ -126,6 +130,10 @@ void FullscreenController::ToggleFullscreenModeForTab(WebContents* web_contents, // case, all we have to do is notifying the tab that it has exited "tab // fullscreen" mode. NotifyTabOfExitIfNecessary(); + + // This is only a change between Browser and Tab fullscreen. We generate + // a fullscreen notification now because there is no window change. + PostFullscreenChangeNotification(true); } } } @@ -258,9 +266,7 @@ void FullscreenController::WindowFullscreenStateChanged() { #else exiting_fullscreen = !window_->IsFullscreen(); #endif - MessageLoop::current()->PostTask(FROM_HERE, - base::Bind(&FullscreenController::NotifyFullscreenChange, - ptr_factory_.GetWeakPtr(), !exiting_fullscreen)); + PostFullscreenChangeNotification(!exiting_fullscreen); if (exiting_fullscreen) NotifyTabOfExitIfNecessary(); if (exiting_fullscreen) @@ -441,6 +447,13 @@ void FullscreenController::UpdateNotificationRegistrations() { } } +void FullscreenController::PostFullscreenChangeNotification( + bool is_fullscreen) { + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&FullscreenController::NotifyFullscreenChange, + ptr_factory_.GetWeakPtr(), is_fullscreen)); +} + void FullscreenController::NotifyFullscreenChange(bool is_fullscreen) { content::NotificationService::current()->Notify( chrome::NOTIFICATION_FULLSCREEN_CHANGED, diff --git a/chrome/browser/ui/fullscreen/fullscreen_controller.h b/chrome/browser/ui/fullscreen/fullscreen_controller.h index 918568c..7638076 100644 --- a/chrome/browser/ui/fullscreen/fullscreen_controller.h +++ b/chrome/browser/ui/fullscreen/fullscreen_controller.h @@ -130,6 +130,9 @@ class FullscreenController : public content::NotificationObserver { void UpdateNotificationRegistrations(); + // Posts a task to call NotifyFullscreenChange. + void PostFullscreenChangeNotification(bool is_fullscreen); + // Sends a NOTIFICATION_FULLSCREEN_CHANGED notification. void NotifyFullscreenChange(bool is_fullscreen); // Notifies the tab that it has been forced out of fullscreen and mouse lock // mode if necessary. diff --git a/chrome/browser/ui/fullscreen/fullscreen_controller_browsertest.cc b/chrome/browser/ui/fullscreen/fullscreen_controller_browsertest.cc index a154431..c9da148 100644 --- a/chrome/browser/ui/fullscreen/fullscreen_controller_browsertest.cc +++ b/chrome/browser/ui/fullscreen/fullscreen_controller_browsertest.cc @@ -19,17 +19,17 @@ #include "base/mac/mac_util.h" #endif +using chrome::kAboutBlankURL; using content::WebContents; +using content::PAGE_TRANSITION_TYPED; class FullscreenControllerBrowserTest: public FullscreenControllerTest { }; IN_PROC_BROWSER_TEST_F(FullscreenControllerTest, PendingMouseLockExitsOnTabSwitch) { - AddTabAtIndexAndWait(0, GURL(chrome::kAboutBlankURL), - content::PAGE_TRANSITION_TYPED); - AddTabAtIndexAndWait(0, GURL(chrome::kAboutBlankURL), - content::PAGE_TRANSITION_TYPED); + AddTabAtIndex(0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); + AddTabAtIndex(0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); WebContents* tab1 = chrome::GetActiveWebContents(browser()); // Request mouse lock. Bubble is displayed. @@ -62,10 +62,8 @@ IN_PROC_BROWSER_TEST_F(FullscreenControllerTest, IN_PROC_BROWSER_TEST_F(FullscreenControllerTest, PendingMouseLockExitsOnTabClose) { // Add more tabs. - AddTabAtIndexAndWait(0, GURL(chrome::kAboutBlankURL), - content::PAGE_TRANSITION_TYPED); - AddTabAtIndexAndWait(0, GURL(chrome::kAboutBlankURL), - content::PAGE_TRANSITION_TYPED); + AddTabAtIndex(0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); + AddTabAtIndex(0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); // Request mouse lock. Bubble is displayed. RequestToLockMouse(true, false); diff --git a/chrome/browser/ui/fullscreen/fullscreen_controller_interactive_browsertest.cc b/chrome/browser/ui/fullscreen/fullscreen_controller_interactive_browsertest.cc index 0dc9507..5882c69 100644 --- a/chrome/browser/ui/fullscreen/fullscreen_controller_interactive_browsertest.cc +++ b/chrome/browser/ui/fullscreen/fullscreen_controller_interactive_browsertest.cc @@ -20,7 +20,9 @@ #include "base/mac/mac_util.h" #endif +using chrome::kAboutBlankURL; using content::WebContents; +using content::PAGE_TRANSITION_TYPED; namespace { @@ -94,7 +96,7 @@ void FullscreenControllerInteractiveTest::ToggleBrowserFullscreen( void FullscreenControllerInteractiveTest::TestFullscreenMouseLockContentSettings() { GURL url = test_server()->GetURL("simple.html"); - AddTabAtIndexAndWait(0, url, content::PAGE_TRANSITION_TYPED); + AddTabAtIndex(0, url, PAGE_TRANSITION_TYPED); // Validate that going fullscreen for a URL defaults to asking permision. ASSERT_FALSE(IsFullscreenPermissionRequested()); @@ -154,23 +156,19 @@ FullscreenControllerInteractiveTest::TestFullscreenMouseLockContentSettings() { void FullscreenControllerInteractiveTest::ToggleTabFullscreen_Internal( bool enter_fullscreen, bool retry_until_success) { WebContents* tab = chrome::GetActiveWebContents(browser()); - if (IsFullscreenForBrowser()) { - // Changing tab fullscreen state will not actually change the window - // when browser fullscreen is in effect. + do { + FullscreenNotificationObserver fullscreen_observer; browser()->ToggleFullscreenModeForTab(tab, enter_fullscreen); - } else { // Not in browser fullscreen, expect window to actually change. - ASSERT_NE(browser()->window()->IsFullscreen(), enter_fullscreen); - do { - FullscreenNotificationObserver fullscreen_observer; - browser()->ToggleFullscreenModeForTab(tab, enter_fullscreen); - fullscreen_observer.Wait(); - // Repeat ToggleFullscreenModeForTab until the correct state is entered. - // This addresses flakiness on test bots running many fullscreen - // tests in parallel. - } while (retry_until_success && - browser()->window()->IsFullscreen() != enter_fullscreen); + fullscreen_observer.Wait(); + // Repeat ToggleFullscreenModeForTab until the correct state is entered. + // This addresses flakiness on test bots running many fullscreen + // tests in parallel. + } while (retry_until_success && + !IsFullscreenForBrowser() && + browser()->window()->IsFullscreen() != enter_fullscreen); + ASSERT_EQ(IsFullscreenForTabOrPending(), enter_fullscreen); + if (!IsFullscreenForBrowser()) ASSERT_EQ(browser()->window()->IsFullscreen(), enter_fullscreen); - } } // Tests /////////////////////////////////////////////////////////////////////// @@ -181,15 +179,13 @@ IN_PROC_BROWSER_TEST_F(FullscreenControllerInteractiveTest, DISABLED_TestNewTabExitsFullscreen) { ASSERT_TRUE(test_server()->Start()); - AddTabAtIndexAndWait( - 0, GURL(chrome::kAboutBlankURL), content::PAGE_TRANSITION_TYPED); + AddTabAtIndex(0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); ASSERT_NO_FATAL_FAILURE(ToggleTabFullscreen(true)); { FullscreenNotificationObserver fullscreen_observer; - AddTabAtIndexAndWait( - 1, GURL(chrome::kAboutBlankURL), content::PAGE_TRANSITION_TYPED); + AddTabAtIndex(1, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); fullscreen_observer.Wait(); ASSERT_FALSE(browser()->window()->IsFullscreen()); } @@ -201,8 +197,7 @@ IN_PROC_BROWSER_TEST_F(FullscreenControllerInteractiveTest, DISABLED_TestTabExitsItselfFromFullscreen) { ASSERT_TRUE(test_server()->Start()); - AddTabAtIndexAndWait( - 0, GURL(chrome::kAboutBlankURL), content::PAGE_TRANSITION_TYPED); + AddTabAtIndex(0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); ASSERT_NO_FATAL_FAILURE(ToggleTabFullscreen(true)); ASSERT_NO_FATAL_FAILURE(ToggleTabFullscreen(false)); @@ -215,10 +210,8 @@ IN_PROC_BROWSER_TEST_F(FullscreenControllerInteractiveTest, DISABLED_TestFullscreenBubbleMouseLockState) { ASSERT_TRUE(test_server()->Start()); - AddTabAtIndexAndWait(0, GURL(chrome::kAboutBlankURL), - content::PAGE_TRANSITION_TYPED); - AddTabAtIndexAndWait(1, GURL(chrome::kAboutBlankURL), - content::PAGE_TRANSITION_TYPED); + AddTabAtIndex(0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); + AddTabAtIndex(1, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); ASSERT_NO_FATAL_FAILURE(ToggleTabFullscreen(true)); @@ -257,8 +250,7 @@ IN_PROC_BROWSER_TEST_F(FullscreenControllerInteractiveTest, ASSERT_NO_FATAL_FAILURE(ToggleBrowserFullscreen(true)); // Enter tab fullscreen. - AddTabAtIndexAndWait(0, GURL(chrome::kAboutBlankURL), - content::PAGE_TRANSITION_TYPED); + AddTabAtIndex(0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); ASSERT_NO_FATAL_FAILURE(ToggleTabFullscreen(true)); // Exit browser fullscreen. @@ -274,8 +266,7 @@ IN_PROC_BROWSER_TEST_F(FullscreenControllerInteractiveTest, ASSERT_NO_FATAL_FAILURE(ToggleBrowserFullscreen(true)); // Enter and then exit tab fullscreen. - AddTabAtIndexAndWait(0, GURL(chrome::kAboutBlankURL), - content::PAGE_TRANSITION_TYPED); + AddTabAtIndex(0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); ASSERT_NO_FATAL_FAILURE(ToggleTabFullscreen(true)); ASSERT_NO_FATAL_FAILURE(ToggleTabFullscreen(false)); @@ -369,8 +360,7 @@ IN_PROC_BROWSER_TEST_F( FullscreenControllerTest, DISABLED_TabEntersPresentationModeFromWindowed) { ASSERT_TRUE(test_server()->Start()); - AddTabAtIndexAndWait( - 0, GURL(chrome::kAboutBlankURL), content::PAGE_TRANSITION_TYPED); + AddTabAtIndex(0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); WebContents* tab = chrome::GetActiveWebContents(browser()); @@ -877,7 +867,7 @@ IN_PROC_BROWSER_TEST_F(FullscreenControllerInteractiveTest, // http://crbug.com/133831 GURL url = test_server()->GetURL("simple.html"); - AddTabAtIndexAndWait(0, url, content::PAGE_TRANSITION_TYPED); + AddTabAtIndex(0, url, PAGE_TRANSITION_TYPED); // Validate that going fullscreen for a URL defaults to asking permision. ASSERT_FALSE(IsFullscreenPermissionRequested()); diff --git a/chrome/browser/ui/fullscreen/fullscreen_controller_state_interactive_browsertest.cc b/chrome/browser/ui/fullscreen/fullscreen_controller_state_interactive_browsertest.cc new file mode 100644 index 0000000..1cc5739 --- /dev/null +++ b/chrome/browser/ui/fullscreen/fullscreen_controller_state_interactive_browsertest.cc @@ -0,0 +1,226 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "build/build_config.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_tabstrip.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/fullscreen/fullscreen_controller.h" +#include "chrome/browser/ui/fullscreen/fullscreen_controller_state_test.h" +#include "chrome/browser/ui/fullscreen/fullscreen_controller_test.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/url_constants.h" +#include "content/public/test/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +using chrome::kAboutBlankURL; +using content::PAGE_TRANSITION_TYPED; + +// Interactive test fixture testing Fullscreen Controller through its states. -- +// See documentation at the top of fullscreen_controller_state_unittest.cc. +class FullscreenControllerStateInteractiveTest + : public InProcessBrowserTest, + public FullscreenControllerStateTest { + public: + // FullscreenControllerStateTest: + virtual void ChangeWindowFullscreenState() OVERRIDE; + virtual bool InvokeEvent(Event event) OVERRIDE; + + protected: + // FullscreenControllerStateTest: + virtual bool ShouldSkipTest(State state, + Event event, + bool reentrant) OVERRIDE; + virtual Browser* GetBrowser() OVERRIDE; + + scoped_ptr<FullscreenNotificationObserver> fullscreen_observer_; +}; + +void FullscreenControllerStateInteractiveTest::ChangeWindowFullscreenState() { + if (fullscreen_observer_) { + // If tests are stuck here, use the following log command to see progress. + // LOG(INFO) << GetAndClearDebugLog(); + fullscreen_observer_->Wait(); + fullscreen_observer_.reset(NULL); + } +} + +bool FullscreenControllerStateInteractiveTest::InvokeEvent(Event event) { + switch (event) { + case TOGGLE_FULLSCREEN: + case TAB_FULLSCREEN_TRUE: + case TAB_FULLSCREEN_FALSE: +#if defined(OS_WIN) + case METRO_SNAP_TRUE: + case METRO_SNAP_FALSE: +#endif + fullscreen_observer_.reset(new FullscreenNotificationObserver()); + break; + case WINDOW_CHANGE: + // ChangeWindowFullscreenState() will be called and then wait on + // fullscreen_observer_ if needed. + break; + default: + NOTREACHED() << "InvokeEvent needs a handler for event " + << GetEventString(event) << GetAndClearDebugLog(); + return false; + } + + return FullscreenControllerStateTest::InvokeEvent(event); +} + +bool FullscreenControllerStateInteractiveTest::ShouldSkipTest(State state, + Event event, + bool reentrant) { + // Interactive tests run reentrant or not based on the platform + // implementation, so limit our test runs to match. +#if defined(OS_WIN) + if (!reentrant) { + debugging_log_ << "\nSkipping non-reentrant test on Windows.\n"; + return true; + } +#else + if (reentrant) { + debugging_log_ << "\nSkipping reentrant test on non-Windows.\n"; + return true; + } +#endif + + return FullscreenControllerStateTest::ShouldSkipTest(state, + event, + reentrant); +} + +Browser* FullscreenControllerStateInteractiveTest::GetBrowser() { + return InProcessBrowserTest::browser(); +} + +// Tests ----------------------------------------------------------------------- + +#define TEST_EVENT_INNER(state, event, reentrant, reentrant_id) \ + IN_PROC_BROWSER_TEST_F(FullscreenControllerStateInteractiveTest, \ + DISABLED_##state##__##event##reentrant_id) { \ + AddTabAtIndex(0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); \ + ASSERT_NO_FATAL_FAILURE(TestStateAndEvent(state, event, reentrant)) \ + << GetAndClearDebugLog(); \ + } + // Progress of tests can be examined by inserting the following line: + // LOG(INFO) << GetAndClearDebugLog(); } + +#define TEST_EVENT(state, event) \ + TEST_EVENT_INNER(state, event, false, ); \ + TEST_EVENT_INNER(state, event, true, _Reentrant); + +// Individual tests for each pair of state and event: + +TEST_EVENT(STATE_NORMAL, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_NORMAL, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_NORMAL, TAB_FULLSCREEN_FALSE); +#if defined(OS_WIN) +TEST_EVENT(STATE_NORMAL, METRO_SNAP_TRUE); +TEST_EVENT(STATE_NORMAL, METRO_SNAP_FALSE); +#endif +TEST_EVENT(STATE_NORMAL, WINDOW_CHANGE); + +TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, TAB_FULLSCREEN_FALSE); +#if defined(OS_WIN) +TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, METRO_SNAP_TRUE); +TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, METRO_SNAP_FALSE); +#endif +TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, WINDOW_CHANGE); + +#if defined(OS_WIN) +TEST_EVENT(STATE_METRO_SNAP, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_METRO_SNAP, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_METRO_SNAP, TAB_FULLSCREEN_FALSE); +TEST_EVENT(STATE_METRO_SNAP, METRO_SNAP_TRUE); +TEST_EVENT(STATE_METRO_SNAP, METRO_SNAP_FALSE); +TEST_EVENT(STATE_METRO_SNAP, WINDOW_CHANGE); +#endif + +TEST_EVENT(STATE_TAB_FULLSCREEN, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_TAB_FULLSCREEN, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_TAB_FULLSCREEN, TAB_FULLSCREEN_FALSE); +#if defined(OS_WIN) +TEST_EVENT(STATE_TAB_FULLSCREEN, METRO_SNAP_TRUE); +TEST_EVENT(STATE_TAB_FULLSCREEN, METRO_SNAP_FALSE); +#endif +TEST_EVENT(STATE_TAB_FULLSCREEN, WINDOW_CHANGE); + +TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, TAB_FULLSCREEN_FALSE); +#if defined(OS_WIN) +TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, METRO_SNAP_TRUE); +TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, METRO_SNAP_FALSE); +#endif +TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, WINDOW_CHANGE); + +TEST_EVENT(STATE_TO_NORMAL, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_TO_NORMAL, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_TO_NORMAL, TAB_FULLSCREEN_FALSE); +#if defined(OS_WIN) +TEST_EVENT(STATE_TO_NORMAL, METRO_SNAP_TRUE); +TEST_EVENT(STATE_TO_NORMAL, METRO_SNAP_FALSE); +#endif +TEST_EVENT(STATE_TO_NORMAL, WINDOW_CHANGE); + +TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, TAB_FULLSCREEN_FALSE); +#if defined(OS_WIN) +TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, METRO_SNAP_TRUE); +TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, METRO_SNAP_FALSE); +#endif +TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, WINDOW_CHANGE); + +TEST_EVENT(STATE_TO_TAB_FULLSCREEN, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_TO_TAB_FULLSCREEN, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_TO_TAB_FULLSCREEN, TAB_FULLSCREEN_FALSE); +#if defined(OS_WIN) +TEST_EVENT(STATE_TO_TAB_FULLSCREEN, METRO_SNAP_TRUE); +TEST_EVENT(STATE_TO_TAB_FULLSCREEN, METRO_SNAP_FALSE); +#endif +TEST_EVENT(STATE_TO_TAB_FULLSCREEN, WINDOW_CHANGE); + +// Specific one-off tests for known issues: + +// Used manually to determine what happens on a platform. +IN_PROC_BROWSER_TEST_F(FullscreenControllerStateInteractiveTest, + DISABLED_ManualTest) { + // A tab is needed for tab fullscreen. + AddTabAtIndex(0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); + ASSERT_TRUE(InvokeEvent(TOGGLE_FULLSCREEN)) << GetAndClearDebugLog(); + ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE)) << GetAndClearDebugLog(); + ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE)) << GetAndClearDebugLog(); + ASSERT_TRUE(InvokeEvent(TOGGLE_FULLSCREEN)) << GetAndClearDebugLog(); + ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE)) << GetAndClearDebugLog(); + + // Wait, allowing human operator to observe the result. + scoped_refptr<content::MessageLoopRunner> message_loop; + message_loop = new content::MessageLoopRunner(); + message_loop->Run(); +} + +// Soak tests: + +// Tests all states with all permutations of multiple events to detect lingering +// state issues that would bleed over to other states. +// I.E. for each state test all combinations of events E1, E2, E3. +// +// This produces coverage for event sequences that may happen normally but +// would not be exposed by traversing to each state via TransitionToState(). +// TransitionToState() always takes the same path even when multiple paths +// exist. +IN_PROC_BROWSER_TEST_F(FullscreenControllerStateInteractiveTest, + DISABLED_TransitionsForEachState) { + // A tab is needed for tab fullscreen. + AddTabAtIndex(0, GURL(kAboutBlankURL), PAGE_TRANSITION_TYPED); + TestTransitionsForEachState(); + // Progress of test can be examined via LOG(INFO) << GetAndClearDebugLog(); +} + diff --git a/chrome/browser/ui/fullscreen/fullscreen_controller_state_test.cc b/chrome/browser/ui/fullscreen/fullscreen_controller_state_test.cc new file mode 100644 index 0000000..09283b4 --- /dev/null +++ b/chrome/browser/ui/fullscreen/fullscreen_controller_state_test.cc @@ -0,0 +1,570 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/fullscreen/fullscreen_controller_state_test.h" + +#include <memory.h> + +#include <iomanip> +#include <iostream> + +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_tabstrip.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/fullscreen/fullscreen_controller.h" +#include "content/public/common/url_constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +FullscreenControllerStateTest::FullscreenControllerStateTest() + : state_(STATE_NORMAL), + reentrant_(false) { + // Human specified state machine data. + // For each state, for each event, define the resulting state. + State transition_table_data[][NUM_EVENTS] = { + { // STATE_NORMAL: + STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TOGGLE_FULLSCREEN + STATE_TO_TAB_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE + STATE_NORMAL, // Event TAB_FULLSCREEN_FALSE +#if defined(OS_WIN) + STATE_METRO_SNAP, // Event METRO_SNAP_TRUE + STATE_NORMAL, // Event METRO_SNAP_FALSE +#endif + STATE_NORMAL, // Event WINDOW_CHANGE + }, + { // STATE_BROWSER_FULLSCREEN_NO_CHROME: + STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN + STATE_TAB_BROWSER_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE + STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_FALSE +#if defined(OS_WIN) + STATE_METRO_SNAP, // Event METRO_SNAP_TRUE + STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event METRO_SNAP_FALSE +#endif + STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event WINDOW_CHANGE + }, +#if defined(OS_WIN) + { // STATE_METRO_SNAP: + STATE_METRO_SNAP, // Event TOGGLE_FULLSCREEN + STATE_METRO_SNAP, // Event TAB_FULLSCREEN_TRUE + STATE_METRO_SNAP, // Event TAB_FULLSCREEN_FALSE + STATE_METRO_SNAP, // Event METRO_SNAP_TRUE + STATE_NORMAL, // Event METRO_SNAP_FALSE + STATE_METRO_SNAP, // Event WINDOW_CHANGE + }, +#endif + { // STATE_TAB_FULLSCREEN: + STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN + STATE_TAB_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE + STATE_TO_NORMAL, // Event TAB_FULLSCREEN_FALSE +#if defined(OS_WIN) + STATE_METRO_SNAP, // Event METRO_SNAP_TRUE + STATE_TAB_FULLSCREEN, // Event METRO_SNAP_FALSE +#endif + STATE_TAB_FULLSCREEN, // Event WINDOW_CHANGE + }, + { // STATE_TAB_BROWSER_FULLSCREEN: + STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN + STATE_TAB_BROWSER_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE +#if defined(OS_MACOSX) + // TODO(scheib) Mac exits browser mode too http://crbug.com/155642 + STATE_TO_NORMAL, // Event TAB_FULLSCREEN_FALSE +#else + STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_FALSE +#endif +#if defined(OS_WIN) + STATE_METRO_SNAP, // Event METRO_SNAP_TRUE + STATE_TAB_BROWSER_FULLSCREEN, // Event METRO_SNAP_FALSE +#endif + STATE_TAB_BROWSER_FULLSCREEN, // Event WINDOW_CHANGE + }, + // STATE_TO_NORMAL: + { STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN + // TODO(scheib) Should be a route back to TAB. http://crbug.com/154196 + STATE_TO_NORMAL, // Event TAB_FULLSCREEN_TRUE + STATE_TO_NORMAL, // Event TAB_FULLSCREEN_FALSE +#if defined(OS_WIN) + STATE_METRO_SNAP, // Event METRO_SNAP_TRUE + STATE_TO_NORMAL, // Event METRO_SNAP_FALSE +#endif + STATE_NORMAL, // Event WINDOW_CHANGE + }, + // STATE_TO_BROWSER_FULLSCREEN_NO_CHROME: + { STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TOGGLE_FULLSCREEN + // TODO(scheib) Should be a route to TAB_BROWSER http://crbug.com/154196 + STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_TRUE + STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_FALSE +#if defined(OS_WIN) + STATE_METRO_SNAP, // Event METRO_SNAP_TRUE + STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event METRO_SNAP_FALSE +#endif + STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event WINDOW_CHANGE + }, + // STATE_TO_TAB_FULLSCREEN: + { // TODO(scheib) Should be a route to TAB_BROWSER http://crbug.com/154196 + STATE_TO_TAB_FULLSCREEN, // Event TOGGLE_FULLSCREEN + STATE_TO_TAB_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE + // TODO(scheib) Should be a route back to NORMAL. http://crbug.com/154196 + STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_FALSE +#if defined(OS_WIN) + STATE_METRO_SNAP, // Event METRO_SNAP_TRUE + STATE_TO_TAB_FULLSCREEN, // Event METRO_SNAP_FALSE +#endif + STATE_TAB_FULLSCREEN, // Event WINDOW_CHANGE + }, + }; + CHECK_EQ(sizeof(transition_table_data), sizeof(transition_table_)); + memcpy(transition_table_, transition_table_data, + sizeof(transition_table_data)); + + // Verify that transition_table_ has been completely defined. + for (int source = 0; source < NUM_STATES; source++) { + for (int event = 0; event < NUM_EVENTS; event++) { + CHECK_NE(STATE_INVALID, transition_table_[source][event]); + CHECK_LE(0, transition_table_[source][event]); + CHECK_GT(NUM_STATES, transition_table_[source][event]); + } + } + + // Copy transition_table_ data into state_transitions_ table. + for (int source = 0; source < NUM_STATES; source++) { + for (int event = 0; event < NUM_EVENTS; event++) { + State destination = transition_table_[source][event]; + state_transitions_[source][destination].event = static_cast<Event>(event); + state_transitions_[source][destination].state = destination; + state_transitions_[source][destination].distance = 1; + } + } +} + +FullscreenControllerStateTest::~FullscreenControllerStateTest() { +} + +// static +const char* FullscreenControllerStateTest::GetStateString(State state) { + switch (state) { + case STATE_NORMAL: + return "STATE_NORMAL"; + case STATE_BROWSER_FULLSCREEN_NO_CHROME: + return "STATE_BROWSER_FULLSCREEN_NO_CHROME"; +#if defined(OS_WIN) + case STATE_METRO_SNAP: + return "STATE_METRO_SNAP"; +#endif + case STATE_TAB_FULLSCREEN: + return "STATE_TAB_FULLSCREEN"; + case STATE_TAB_BROWSER_FULLSCREEN: + return "STATE_TAB_BROWSER_FULLSCREEN"; + case STATE_TO_NORMAL: + return "STATE_TO_NORMAL"; + case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME: + return "STATE_TO_BROWSER_FULLSCREEN_NO_CHROME"; + case STATE_TO_TAB_FULLSCREEN: + return "STATE_TO_TAB_FULLSCREEN"; + case STATE_INVALID: + return "STATE_INVALID"; + default: + NOTREACHED() << "No string for state " << state; + return "State-Unknown"; + } +} + +// static +const char* FullscreenControllerStateTest::GetEventString(Event event) { + switch (event) { + case TOGGLE_FULLSCREEN: + return "TOGGLE_FULLSCREEN"; + case TAB_FULLSCREEN_TRUE: + return "TAB_FULLSCREEN_TRUE"; + case TAB_FULLSCREEN_FALSE: + return "TAB_FULLSCREEN_FALSE"; +#if defined(OS_WIN) + case METRO_SNAP_TRUE: + return "METRO_SNAP_TRUE"; + case METRO_SNAP_FALSE: + return "METRO_SNAP_FALSE"; +#endif + case WINDOW_CHANGE: + return "WINDOW_CHANGE"; + case EVENT_INVALID: + return "EVENT_INVALID"; + default: + NOTREACHED() << "No string for event " << event; + return "Event-Unknown"; + } +} + +void FullscreenControllerStateTest::TransitionToState(State final_state) { + int max_steps = NUM_STATES; + while (max_steps-- && TransitionAStepTowardState(final_state)) + continue; + ASSERT_GE(max_steps, 0) << "TransitionToState was unable to achieve desired " + << "target state. TransitionAStepTowardState iterated too many times." + << GetAndClearDebugLog(); + ASSERT_EQ(final_state, state_) << "TransitionToState was unable to achieve " + << "desired target state. TransitionAStepTowardState returned false." + << GetAndClearDebugLog(); +} + +bool FullscreenControllerStateTest::TransitionAStepTowardState( + State destination_state) { + State source_state = state_; + if (source_state == destination_state) + return false; + + StateTransitionInfo next = NextTransitionInShortestPath(source_state, + destination_state, + NUM_STATES); + if (next.state == STATE_INVALID) { + NOTREACHED() << "TransitionAStepTowardState unable to transition. " + << "NextTransitionInShortestPath(" + << GetStateString(source_state) << ", " + << GetStateString(destination_state) << ") returned STATE_INVALID." + << GetAndClearDebugLog(); + return false; + } + + return InvokeEvent(next.event); +} + +const char* FullscreenControllerStateTest::GetWindowStateString() { + return NULL; +} + +bool FullscreenControllerStateTest::InvokeEvent(Event event) { + State source_state = state_; + State next_state = transition_table_[source_state][event]; + + // When simulating reentrant window change calls, expect the next state + // automatically. + if (reentrant_) + next_state = transition_table_[next_state][WINDOW_CHANGE]; + + debugging_log_ << " InvokeEvent(" << std::left + << std::setw(MAX_EVENT_NAME_LENGTH) << GetEventString(event) + << ") to " + << std::setw(MAX_STATE_NAME_LENGTH) << GetStateString(next_state); + + state_ = next_state; + + switch (event) { + case TOGGLE_FULLSCREEN: + GetFullscreenController()->ToggleFullscreenMode(); + break; + case TAB_FULLSCREEN_TRUE: + GetFullscreenController()->ToggleFullscreenModeForTab( + chrome::GetActiveWebContents(GetBrowser()), true); + break; + case TAB_FULLSCREEN_FALSE: + GetFullscreenController()->ToggleFullscreenModeForTab( + chrome::GetActiveWebContents(GetBrowser()), false); + break; +#if defined(OS_WIN) + case METRO_SNAP_TRUE: + GetFullscreenController()->SetMetroSnapMode(true); + break; + case METRO_SNAP_FALSE: + GetFullscreenController()->SetMetroSnapMode(false); + break; +#endif + case WINDOW_CHANGE: + ChangeWindowFullscreenState(); + break; + default: + NOTREACHED() << "InvokeEvent needs a handler for event " + << GetEventString(event) << GetAndClearDebugLog(); + return false; + } + + if (GetWindowStateString()) + debugging_log_ << " Window state now " << GetWindowStateString() << "\n"; + else + debugging_log_ << "\n"; + + VerifyWindowState(); + + return true; +} + +void FullscreenControllerStateTest::VerifyWindowState() { + switch (state_) { + case STATE_NORMAL: +#if defined(OS_MACOSX) + EXPECT_FALSE(GetBrowser()->window()->InPresentationMode()) + << GetAndClearDebugLog(); +#endif + EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser()) + << GetAndClearDebugLog(); + EXPECT_FALSE(GetFullscreenController()->IsFullscreenForTabOrPending()) + << GetAndClearDebugLog(); + EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode()) + << GetAndClearDebugLog(); + break; + case STATE_BROWSER_FULLSCREEN_NO_CHROME: +#if defined(OS_MACOSX) + EXPECT_FALSE(GetBrowser()->window()->InPresentationMode()) + << GetAndClearDebugLog(); +#endif + EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser()) + << GetAndClearDebugLog(); + EXPECT_FALSE(GetFullscreenController()->IsFullscreenForTabOrPending()) + << GetAndClearDebugLog(); + EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode()) + << GetAndClearDebugLog(); + break; +#if defined(OS_WIN) + case STATE_METRO_SNAP: + // No expectation for InPresentationMode. + + // TODO(scheib) IsFullscreenForBrowser and IsFullscreenForTabOrPending + // are returning true and false in interactive tests with real window. + // With only a single Metro Snap state in this test framework it isn't + // fair to try to have an expectation anyway. + // + // No expectation for IsFullscreenForBrowser. + // No expectation for IsFullscreenForTabOrPending. + EXPECT_TRUE(GetFullscreenController()->IsInMetroSnapMode()) + << GetAndClearDebugLog(); + break; +#endif + case STATE_TAB_FULLSCREEN: +#if defined(OS_MACOSX) + EXPECT_TRUE(GetBrowser()->window()->InPresentationMode()) + << GetAndClearDebugLog(); +#endif + EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser()) + << GetAndClearDebugLog(); + EXPECT_TRUE(GetFullscreenController()->IsFullscreenForTabOrPending()) + << GetAndClearDebugLog(); + EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode()) + << GetAndClearDebugLog(); + break; + case STATE_TAB_BROWSER_FULLSCREEN: +#if defined(OS_MACOSX) + EXPECT_TRUE(GetBrowser()->window()->InPresentationMode()) + << GetAndClearDebugLog(); +#endif +#if defined(OS_MACOSX) + // TODO(scheib) Mac is reporting incorrect IsFullscreenForBrowser(). + // e.g. in FullscreenControllerStateTest. + // STATE_BROWSER_FULLSCREEN_NO_CHROME__TAB_FULLSCREEN_TRUE + // At the end of ToggleFullscreenModeForTab + // tab_caused_fullscreen_ has incorrectly been set to true even + // though controller was already in browser fullscreen. + // http://crbug.com/155650 + EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser()) + << GetAndClearDebugLog(); +#else + EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser()) + << GetAndClearDebugLog(); +#endif + EXPECT_TRUE(GetFullscreenController()->IsFullscreenForTabOrPending()) + << GetAndClearDebugLog(); + EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode()) + << GetAndClearDebugLog(); + break; + case STATE_TO_NORMAL: +#if defined(OS_MACOSX) + EXPECT_FALSE(GetBrowser()->window()->InPresentationMode()) + << GetAndClearDebugLog(); +#endif + // No expectation for IsFullscreenForBrowser. + // No expectation for IsFullscreenForTabOrPending. + EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode()) + << GetAndClearDebugLog(); + break; + case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME: +#if defined(OS_MACOSX) + EXPECT_FALSE(GetBrowser()->window()->InPresentationMode()) + << GetAndClearDebugLog(); + EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser()) + << GetAndClearDebugLog(); +#else + EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser()) + << GetAndClearDebugLog(); +#endif + // No expectation for IsFullscreenForTabOrPending. + EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode()) + << GetAndClearDebugLog(); + break; + case STATE_TO_TAB_FULLSCREEN: +#if defined(OS_MACOSX) + // TODO(scheib) InPresentationMode returns false when invoking events: + // TAB_FULLSCREEN_TRUE, TOGGLE_FULLSCREEN. http://crbug.com/156645 + // It may be that a new testing state TO_TAB_BROWSER_FULLSCREEN + // would help work around this http://crbug.com/154196 + // Test with: STATE_TO_TAB_FULLSCREEN__TOGGLE_FULLSCREEN + // + // EXPECT_TRUE(GetBrowser()->window()->InPresentationMode()) + // << GetAndClearDebugLog(); +#endif + EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser()) + << GetAndClearDebugLog(); + EXPECT_TRUE(GetFullscreenController()->IsFullscreenForTabOrPending()) + << GetAndClearDebugLog(); + EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode()) + << GetAndClearDebugLog(); + break; + default: + NOTREACHED() << GetAndClearDebugLog(); + } +} + +void FullscreenControllerStateTest::TestTransitionsForEachState() { + for (int reentrant = 0; reentrant <= 1; reentrant++) { + for (int source_int = 0; source_int < NUM_STATES; source_int++) { + for (int event1_int = 0; event1_int < NUM_EVENTS; event1_int++) { + State state = static_cast<State>(source_int); + Event event1 = static_cast<Event>(event1_int); + + // Early out if skipping all tests for this state, reduces log noise. + if (ShouldSkipTest(state, event1, !!reentrant)) + continue; + + for (int event2_int = 0; event2_int < NUM_EVENTS; event2_int++) { + for (int event3_int = 0; event3_int < NUM_EVENTS; event3_int++) { + Event event2 = static_cast<Event>(event2_int); + Event event3 = static_cast<Event>(event3_int); + + // Test each state and each event. + ASSERT_NO_FATAL_FAILURE(TestStateAndEvent(state, + event1, + !!reentrant)) + << GetAndClearDebugLog(); + + // Then, add an additional event to the sequence. + if (ShouldSkipStateAndEventPair(state_, event2)) + continue; + ASSERT_TRUE(InvokeEvent(event2)) << GetAndClearDebugLog(); + + // Then, add an additional event to the sequence. + if (ShouldSkipStateAndEventPair(state_, event3)) + continue; + ASSERT_TRUE(InvokeEvent(event3)) << GetAndClearDebugLog(); + } + } + } + } + } +} + +FullscreenControllerStateTest::StateTransitionInfo + FullscreenControllerStateTest::NextTransitionInShortestPath( + State source, State destination, int search_limit) { + if (search_limit == 0) + return StateTransitionInfo(); // Return a default (invalid) state. + + if (state_transitions_[source][destination].state == STATE_INVALID) { + // Don't know the next state yet, do a depth first search. + StateTransitionInfo result; + + // Consider all states reachable via each event from the source state. + for (int event_int = 0; event_int < NUM_EVENTS; event_int++) { + Event event = static_cast<Event>(event_int); + State next_state_candidate = transition_table_[source][event]; + + if (ShouldSkipStateAndEventPair(source, event)) + continue; + + // Recurse. + StateTransitionInfo candidate = NextTransitionInShortestPath( + next_state_candidate, destination, search_limit - 1); + + if (candidate.distance + 1 < result.distance) { + result.event = event; + result.state = next_state_candidate; + result.distance = candidate.distance + 1; + } + } + + // Cache result so that a search is not required next time. + state_transitions_[source][destination] = result; + } + + return state_transitions_[source][destination]; +} + +std::string FullscreenControllerStateTest::GetAndClearDebugLog() { + debugging_log_ << "(End of Debugging Log)\n"; + std::string output_log = "\nDebugging Log:\n" + debugging_log_.str(); + debugging_log_.str(""); + return output_log; +} + +bool FullscreenControllerStateTest::ShouldSkipStateAndEventPair(State state, + Event event) { + // TODO(scheib) Toggling Tab fullscreen while pending Tab or + // Browser fullscreen is broken currently http://crbug.com/154196 + if ((state == STATE_TO_BROWSER_FULLSCREEN_NO_CHROME || + state == STATE_TO_TAB_FULLSCREEN) && + (event == TAB_FULLSCREEN_TRUE || event == TAB_FULLSCREEN_FALSE)) + return true; + if (state == STATE_TO_NORMAL && event == TAB_FULLSCREEN_TRUE) + return true; + + return false; +} + +bool FullscreenControllerStateTest::ShouldSkipTest(State state, + Event event, + bool reentrant) { +#if defined(OS_WIN) + // FullscreenController verifies that WindowFullscreenStateChanged is + // always reentrant on Windows. It will fail if we mock asynchronous calls. + if (!reentrant) { + debugging_log_ << "\nSkipping non-reentrant test on Windows.\n"; + return true; + } +#else + if (reentrant) { + debugging_log_ << "\nSkipping reentrant test on non-Windows.\n"; + return true; + } +#endif + + // When testing reentrancy there are states the fullscreen controller + // will be unable to remain in, as they will progress due to the + // reentrant window change call. Skip states that will be instantly + // exited by the reentrant call. + if (reentrant && (transition_table_[state][WINDOW_CHANGE] != state)) { + debugging_log_ << "\nSkipping reentrant test for transitory source state " + << GetStateString(state) << ".\n"; + return true; + } + + if (ShouldSkipStateAndEventPair(state, event)) { + debugging_log_ << "\nSkipping test due to ShouldSkipStateAndEventPair(" + << GetStateString(state) << ", " + << GetEventString(event) << ").\n"; + LOG(INFO) << "Skipping test due to ShouldSkipStateAndEventPair(" + << GetStateString(state) << ", " + << GetEventString(event) << ")."; + return true; + } + + return false; +} + +void FullscreenControllerStateTest::TestStateAndEvent(State state, + Event event, + bool reentrant) { + if (ShouldSkipTest(state, event, reentrant)) + return; + + debugging_log_ << "\nTest transition from state " + << GetStateString(state) + << (reentrant ? " with reentrant calls.\n" : ".\n"); + reentrant_ = reentrant; + + debugging_log_ << " First, from " + << GetStateString(state_) << "\n"; + ASSERT_NO_FATAL_FAILURE(TransitionToState(state)) + << GetAndClearDebugLog(); + + debugging_log_ << " Then,\n"; + ASSERT_TRUE(InvokeEvent(event)) << GetAndClearDebugLog(); +} + +FullscreenController* FullscreenControllerStateTest::GetFullscreenController() { + return GetBrowser()->fullscreen_controller(); +} + diff --git a/chrome/browser/ui/fullscreen/fullscreen_controller_state_test.h b/chrome/browser/ui/fullscreen/fullscreen_controller_state_test.h new file mode 100644 index 0000000..14dc0de --- /dev/null +++ b/chrome/browser/ui/fullscreen/fullscreen_controller_state_test.h @@ -0,0 +1,143 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_FULLSCREEN_FULLSCREEN_CONTROLLER_STATE_TEST_H_ +#define CHROME_BROWSER_UI_FULLSCREEN_FULLSCREEN_CONTROLLER_STATE_TEST_H_ + +#include <sstream> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "build/build_config.h" + +class Browser; +class FullscreenController; + +// Test fixture testing Fullscreen Controller through its states. -------------- +class FullscreenControllerStateTest { + public: + enum State { + // The window is not in fullscreen. + STATE_NORMAL, + // User-initiated fullscreen. On Mac, this is Lion-mode for 10.7+. On 10.6, + // this is synonymous with STATE_BROWSER_FULLSCREEN_WITH_CHROME. + STATE_BROWSER_FULLSCREEN_NO_CHROME, +#if defined(OS_WIN) + // Windows 8 Metro Snap mode, which puts the window at 20% screen-width. + // No TO_ state for Metro, as the windows implementation is only reentrant. + STATE_METRO_SNAP, +#endif + // HTML5 tab-initiated fullscreen. + STATE_TAB_FULLSCREEN, + // Both tab and browser fullscreen. + STATE_TAB_BROWSER_FULLSCREEN, + // TO_ states are asynchronous states waiting for window state change + // before transitioning to their named state. + STATE_TO_NORMAL, + STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, + STATE_TO_TAB_FULLSCREEN, + NUM_STATES, + STATE_INVALID, + }; + + enum Event { + // FullscreenController::ToggleFullscreenMode() + TOGGLE_FULLSCREEN, + // FullscreenController::ToggleFullscreenModeForTab(, true) + TAB_FULLSCREEN_TRUE, + // FullscreenController::ToggleFullscreenModeForTab(, false) + TAB_FULLSCREEN_FALSE, +#if defined(OS_WIN) + // FullscreenController::SetMetroSnapMode(true) + METRO_SNAP_TRUE, + // FullscreenController::SetMetroSnapMode(flase) + METRO_SNAP_FALSE, +#endif + // FullscreenController::ChangeWindowFullscreenState() + WINDOW_CHANGE, + NUM_EVENTS, + EVENT_INVALID, + }; + + static const int MAX_STATE_NAME_LENGTH = 37; + static const int MAX_EVENT_NAME_LENGTH = 20; + + FullscreenControllerStateTest(); + virtual ~FullscreenControllerStateTest(); + + static const char* GetStateString(State state); + static const char* GetEventString(Event event); + + // Causes Fullscreen Controller to transition to an arbitrary state. + void TransitionToState(State state); + // Makes one state change to approach |destination_state| via shortest path. + // Returns true if a state change is made. + // Repeated calls are needed to reach the destination. + bool TransitionAStepTowardState(State destination_state); + + virtual void ChangeWindowFullscreenState() {} + virtual const char* GetWindowStateString(); + + // Causes the |event| to occur and return true on success. + virtual bool InvokeEvent(Event event); + + // Checks that window state matches the expected controller state. + virtual void VerifyWindowState(); + + // Tests all states with all permutations of multiple events to detect + // lingering state issues that would bleed over to other states. + // I.E. for each state test all combinations of events E1, E2, E3. + // + // This produces coverage for event sequences that may happen normally but + // would not be exposed by traversing to each state via TransitionToState(). + // TransitionToState() always takes the same path even when multiple paths + // exist. + void TestTransitionsForEachState(); + + protected: + // Generated information about the transitions between states. + struct StateTransitionInfo { + StateTransitionInfo() + : event(EVENT_INVALID), + state(STATE_INVALID), + distance(NUM_STATES) {} + Event event; // The |Event| that will cause the state transition. + State state; // The adjacent |State| transitioned to; not the final state. + int distance; // Steps to final state. NUM_STATES represents unknown. + }; + + // Returns next transition info for shortest path from source to destination. + StateTransitionInfo NextTransitionInShortestPath(State source, + State destination, + int search_limit); + + std::string GetAndClearDebugLog(); + + // Avoids currently broken cases in the fullscreen controller. + virtual bool ShouldSkipStateAndEventPair(State state, Event event); + // Skips reentrant situations and calls ShouldSkipStateAndEventPair. + virtual bool ShouldSkipTest(State state, Event event, bool reentrant); + + // Runs one test of transitioning to a state and invoking an event. + virtual void TestStateAndEvent(State state, Event event, bool reentrant); + + virtual Browser* GetBrowser() = 0; + FullscreenController* GetFullscreenController(); + + State state_; + bool reentrant_; + + // Human defined |State| that results given each [state][event] pair. + State transition_table_[NUM_STATES][NUM_EVENTS]; + + // Generated information about the transitions between states [from][to]. + StateTransitionInfo state_transitions_[NUM_STATES][NUM_STATES]; + + // Log of operations reported on errors via GetAndClearDebugLog(). + std::ostringstream debugging_log_; + + DISALLOW_COPY_AND_ASSIGN(FullscreenControllerStateTest); +}; + +#endif // CHROME_BROWSER_UI_FULLSCREEN_FULLSCREEN_CONTROLLER_STATE_TEST_H_ diff --git a/chrome/browser/ui/fullscreen/fullscreen_controller_state_unittest.cc b/chrome/browser/ui/fullscreen/fullscreen_controller_state_unittest.cc new file mode 100644 index 0000000..160f3b3 --- /dev/null +++ b/chrome/browser/ui/fullscreen/fullscreen_controller_state_unittest.cc @@ -0,0 +1,457 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "build/build_config.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_tabstrip.h" +#include "chrome/browser/ui/fullscreen/fullscreen_controller.h" +#include "chrome/browser/ui/fullscreen/fullscreen_controller_state_test.h" +#include "chrome/test/base/browser_with_test_window_test.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/url_constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +// The FullscreenControllerStateUnitTest unit test suite exhastively tests +// the FullscreenController through all permutations of events. The behavior +// of the BrowserWindow is mocked via FullscreenControllerTestWindow. +// +// FullscreenControllerStateInteractiveTest is an interactive test suite +// used to verify that the FullscreenControllerTestWindow models the behavior +// of actual windows accurately. The interactive tests are too flaky to run +// on infrastructure, and so those tests are disabled. Run them with: +// interactive_ui_tests +// --gtest_filter="FullscreenControllerStateInteractiveTest.*" +// --gtest_also_run_disabled_tests + +// A BrowserWindow used for testing FullscreenController. ---------------------- +class FullscreenControllerTestWindow : public TestBrowserWindow { + public: + // Simulate the window state with an enumeration. + enum WindowState { + NORMAL, + FULLSCREEN, + // No TO_ state for METRO_SNAP, the windows implementation is synchronous. + METRO_SNAP, + TO_NORMAL, + TO_FULLSCREEN, + }; + + FullscreenControllerTestWindow(); + virtual ~FullscreenControllerTestWindow() {} + + // BrowserWindow Interface: + virtual void EnterFullscreen(const GURL& url, + FullscreenExitBubbleType type) OVERRIDE; + virtual void EnterFullscreen(); + virtual void ExitFullscreen() OVERRIDE; + virtual bool IsFullscreen() const OVERRIDE; +#if defined(OS_WIN) + virtual void SetMetroSnapMode(bool enable) OVERRIDE; + virtual bool IsInMetroSnapMode() const OVERRIDE; +#endif +#if defined(OS_MACOSX) + virtual void EnterPresentationMode( + const GURL& url, + FullscreenExitBubbleType bubble_type) OVERRIDE; + virtual void ExitPresentationMode() OVERRIDE; + virtual bool InPresentationMode() OVERRIDE; +#endif + + static const char* GetWindowStateString(WindowState state); + WindowState state() const { return state_; } + void set_browser(Browser* browser) { browser_ = browser; } + void set_reentrant(bool value) { reentrant_ = value; } + bool reentrant() const { return reentrant_; } + + // Simulates the window changing state. + void ChangeWindowFullscreenState(); + // Calls ChangeWindowFullscreenState() if |reentrant_| is true. + void ChangeWindowFullscreenStateIfReentrant(); + + private: + WindowState state_; + bool mac_presentation_mode_; + Browser* browser_; + + // Causes reentrant calls to be made by calling + // browser_->WindowFullscreenStateChanged() from the BrowserWindow + // interface methods. + bool reentrant_; +}; + +FullscreenControllerTestWindow::FullscreenControllerTestWindow() + : state_(NORMAL), + mac_presentation_mode_(false), + browser_(NULL), + reentrant_(false) { +} + +void FullscreenControllerTestWindow::EnterFullscreen( + const GURL& url, FullscreenExitBubbleType type) { + EnterFullscreen(); +} + +void FullscreenControllerTestWindow::EnterFullscreen() { + if (!IsFullscreen()) { + state_ = TO_FULLSCREEN; + ChangeWindowFullscreenStateIfReentrant(); + } +} + +void FullscreenControllerTestWindow::ExitFullscreen() { + if (IsFullscreen()) { + state_ = TO_NORMAL; + mac_presentation_mode_ = false; + ChangeWindowFullscreenStateIfReentrant(); + } +} + +bool FullscreenControllerTestWindow::IsFullscreen() const { +#if defined(OS_MACOSX) + return state_ == FULLSCREEN || state_ == TO_FULLSCREEN; +#else + return state_ == FULLSCREEN || state_ == TO_NORMAL; +#endif +} + +#if defined(OS_WIN) +void FullscreenControllerTestWindow::SetMetroSnapMode(bool enable) { + if (enable != IsInMetroSnapMode()) { + if (enable) + state_ = METRO_SNAP; + else + state_ = NORMAL; + } + ChangeWindowFullscreenStateIfReentrant(); +} + +bool FullscreenControllerTestWindow::IsInMetroSnapMode() const { + return state_ == METRO_SNAP; +} +#endif + +#if defined(OS_MACOSX) +void FullscreenControllerTestWindow::EnterPresentationMode( + const GURL& url, + FullscreenExitBubbleType bubble_type) { + mac_presentation_mode_ = true; + EnterFullscreen(); +} + +void FullscreenControllerTestWindow::ExitPresentationMode() { + if (InPresentationMode()) { + mac_presentation_mode_ = false; + ExitFullscreen(); + } +} + +bool FullscreenControllerTestWindow::InPresentationMode() { + return mac_presentation_mode_; +} +#endif + +// static +const char* FullscreenControllerTestWindow::GetWindowStateString( + WindowState state) { + switch (state) { + case NORMAL: + return "NORMAL"; + case FULLSCREEN: + return "FULLSCREEN"; + case METRO_SNAP: + return "METRO_SNAP"; + case TO_FULLSCREEN: + return "TO_FULLSCREEN"; + case TO_NORMAL: + return "TO_NORMAL"; + default: + NOTREACHED() << "No string for state " << state; + return "WindowState-Unknown"; + } +} + +void FullscreenControllerTestWindow::ChangeWindowFullscreenState() { + // Several states result in "no operation" intentionally. The tests + // assume that all possible states and event pairs can be tested, even + // though window managers will not generate all of these. + switch (state_) { + case NORMAL: + break; + case FULLSCREEN: + break; + case METRO_SNAP: + break; + case TO_FULLSCREEN: + state_ = FULLSCREEN; + break; + case TO_NORMAL: + state_ = NORMAL; + break; + default: + NOTREACHED(); + } + // Emit a change event from every state to ensure the Fullscreen Controller + // handles it in all circumstances. + browser_->WindowFullscreenStateChanged(); +} + +void FullscreenControllerTestWindow::ChangeWindowFullscreenStateIfReentrant() { + if (reentrant_) + ChangeWindowFullscreenState(); +} + +// Unit test fixture testing Fullscreen Controller through its states. --------- +class FullscreenControllerStateUnitTest : public BrowserWithTestWindowTest, + public FullscreenControllerStateTest { + public: + FullscreenControllerStateUnitTest(); + + // FullscreenControllerStateTest: + virtual void SetUp() OVERRIDE; + virtual void ChangeWindowFullscreenState() OVERRIDE; + virtual const char* GetWindowStateString() OVERRIDE; + virtual void VerifyWindowState() OVERRIDE; + + protected: + // FullscreenControllerStateTest: + virtual bool ShouldSkipStateAndEventPair(State state, Event event) OVERRIDE; + virtual void TestStateAndEvent(State state, + Event event, + bool reentrant) OVERRIDE; + virtual Browser* GetBrowser() OVERRIDE; + FullscreenControllerTestWindow* window_; +}; + +FullscreenControllerStateUnitTest::FullscreenControllerStateUnitTest () + : window_(NULL) { +} + +void FullscreenControllerStateUnitTest::SetUp() { + window_ = new FullscreenControllerTestWindow(); + set_window(window_); // BrowserWithTestWindowTest takes ownership. + BrowserWithTestWindowTest::SetUp(); + window_->set_browser(browser()); +} + +void FullscreenControllerStateUnitTest::ChangeWindowFullscreenState() { + window_->ChangeWindowFullscreenState(); +} + +const char* FullscreenControllerStateUnitTest::GetWindowStateString() { + return FullscreenControllerTestWindow::GetWindowStateString(window_->state()); +} + +void FullscreenControllerStateUnitTest::VerifyWindowState() { + switch (state_) { + case STATE_NORMAL: + EXPECT_EQ(FullscreenControllerTestWindow::NORMAL, + window_->state()) << GetAndClearDebugLog(); + break; + case STATE_BROWSER_FULLSCREEN_NO_CHROME: + EXPECT_EQ(FullscreenControllerTestWindow::FULLSCREEN, + window_->state()) << GetAndClearDebugLog(); + break; +#if defined(OS_WIN) + case STATE_METRO_SNAP: + EXPECT_EQ(FullscreenControllerTestWindow::METRO_SNAP, + window_->state()) << GetAndClearDebugLog(); + break; +#endif + case STATE_TAB_FULLSCREEN: + EXPECT_EQ(FullscreenControllerTestWindow::FULLSCREEN, + window_->state()) << GetAndClearDebugLog(); + break; + case STATE_TAB_BROWSER_FULLSCREEN: + EXPECT_EQ(FullscreenControllerTestWindow::FULLSCREEN, + window_->state()) << GetAndClearDebugLog(); + break; + case STATE_TO_NORMAL: + EXPECT_EQ(FullscreenControllerTestWindow::TO_NORMAL, + window_->state()) << GetAndClearDebugLog(); + break; + case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME: + EXPECT_EQ(FullscreenControllerTestWindow::TO_FULLSCREEN, + window_->state()) << GetAndClearDebugLog(); + break; + case STATE_TO_TAB_FULLSCREEN: + EXPECT_EQ(FullscreenControllerTestWindow::TO_FULLSCREEN, + window_->state()) << GetAndClearDebugLog(); + break; + default: + NOTREACHED() << GetAndClearDebugLog(); + } + + FullscreenControllerStateTest::VerifyWindowState(); +} + +bool FullscreenControllerStateUnitTest::ShouldSkipStateAndEventPair( + State state, Event event) { +#if defined(OS_MACOSX) + // TODO(scheib) Toggle, Window Event, Toggle, Toggle on Mac as exposed by + // test *.STATE_TO_NORMAL__TOGGLE_FULLSCREEN runs interactively and exits to + // Normal. This doesn't appear to be the desired result, and would add + // too much complexity to mimic in our simple FullscreenControllerTestWindow. + // http://crbug.com/156968 + if ((state == STATE_TO_NORMAL || + state == STATE_TO_BROWSER_FULLSCREEN_NO_CHROME || + state == STATE_TO_TAB_FULLSCREEN) && + event == TOGGLE_FULLSCREEN) + return true; +#endif + + return FullscreenControllerStateTest::ShouldSkipStateAndEventPair(state, + event); +} + +void FullscreenControllerStateUnitTest::TestStateAndEvent(State state, + Event event, + bool reentrant) { + window_->set_reentrant(reentrant); + FullscreenControllerStateTest::TestStateAndEvent(state, event, reentrant); +} + +Browser* FullscreenControllerStateUnitTest::GetBrowser() { + return BrowserWithTestWindowTest::browser(); +} + +// Tests ----------------------------------------------------------------------- + +#define TEST_EVENT_INNER(state, event, reentrant, reentrant_id) \ + TEST_F(FullscreenControllerStateUnitTest, \ + state##__##event##reentrant_id) { \ + AddTab(browser(), GURL(chrome::kAboutBlankURL)); \ + ASSERT_NO_FATAL_FAILURE(TestStateAndEvent(state, event, reentrant)) \ + << GetAndClearDebugLog(); \ + } + // Progress of tests can be examined by inserting the following line: + // LOG(INFO) << GetAndClearDebugLog(); } + +#define TEST_EVENT(state, event) \ + TEST_EVENT_INNER(state, event, false, ); \ + TEST_EVENT_INNER(state, event, true, _Reentrant); + +// Individual tests for each pair of state and event: + +TEST_EVENT(STATE_NORMAL, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_NORMAL, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_NORMAL, TAB_FULLSCREEN_FALSE); +#if defined(OS_WIN) +TEST_EVENT(STATE_NORMAL, METRO_SNAP_TRUE); +TEST_EVENT(STATE_NORMAL, METRO_SNAP_FALSE); +#endif +TEST_EVENT(STATE_NORMAL, WINDOW_CHANGE); + +TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, TAB_FULLSCREEN_FALSE); +#if defined(OS_WIN) +TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, METRO_SNAP_TRUE); +TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, METRO_SNAP_FALSE); +#endif +TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, WINDOW_CHANGE); + +#if defined(OS_WIN) +TEST_EVENT(STATE_METRO_SNAP, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_METRO_SNAP, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_METRO_SNAP, TAB_FULLSCREEN_FALSE); +TEST_EVENT(STATE_METRO_SNAP, METRO_SNAP_TRUE); +TEST_EVENT(STATE_METRO_SNAP, METRO_SNAP_FALSE); +TEST_EVENT(STATE_METRO_SNAP, WINDOW_CHANGE); +#endif + +TEST_EVENT(STATE_TAB_FULLSCREEN, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_TAB_FULLSCREEN, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_TAB_FULLSCREEN, TAB_FULLSCREEN_FALSE); +#if defined(OS_WIN) +TEST_EVENT(STATE_TAB_FULLSCREEN, METRO_SNAP_TRUE); +TEST_EVENT(STATE_TAB_FULLSCREEN, METRO_SNAP_FALSE); +#endif +TEST_EVENT(STATE_TAB_FULLSCREEN, WINDOW_CHANGE); + +TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, TAB_FULLSCREEN_FALSE); +#if defined(OS_WIN) +TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, METRO_SNAP_TRUE); +TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, METRO_SNAP_FALSE); +#endif +TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, WINDOW_CHANGE); + +TEST_EVENT(STATE_TO_NORMAL, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_TO_NORMAL, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_TO_NORMAL, TAB_FULLSCREEN_FALSE); +#if defined(OS_WIN) +TEST_EVENT(STATE_TO_NORMAL, METRO_SNAP_TRUE); +TEST_EVENT(STATE_TO_NORMAL, METRO_SNAP_FALSE); +#endif +TEST_EVENT(STATE_TO_NORMAL, WINDOW_CHANGE); + +TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, TAB_FULLSCREEN_FALSE); +#if defined(OS_WIN) +TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, METRO_SNAP_TRUE); +TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, METRO_SNAP_FALSE); +#endif +TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, WINDOW_CHANGE); + +TEST_EVENT(STATE_TO_TAB_FULLSCREEN, TOGGLE_FULLSCREEN); +TEST_EVENT(STATE_TO_TAB_FULLSCREEN, TAB_FULLSCREEN_TRUE); +TEST_EVENT(STATE_TO_TAB_FULLSCREEN, TAB_FULLSCREEN_FALSE); +#if defined(OS_WIN) +TEST_EVENT(STATE_TO_TAB_FULLSCREEN, METRO_SNAP_TRUE); +TEST_EVENT(STATE_TO_TAB_FULLSCREEN, METRO_SNAP_FALSE); +#endif +TEST_EVENT(STATE_TO_TAB_FULLSCREEN, WINDOW_CHANGE); + +// Specific one-off tests for known issues: + +// TODO(scheib) Toggling Tab fullscreen while pending Tab or +// Browser fullscreen is broken currently http://crbug.com/154196 +TEST_F(FullscreenControllerStateUnitTest, + DISABLED_ToggleTabWhenPendingBrowser) { +#if !defined(OS_WIN) // Only possible without reentrancy + AddTab(browser(), GURL(chrome::kAboutBlankURL)); + ASSERT_NO_FATAL_FAILURE( + TransitionToState(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME)) + << GetAndClearDebugLog(); + + ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE)) << GetAndClearDebugLog(); + ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE)) << GetAndClearDebugLog(); + ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE)) << GetAndClearDebugLog(); +#endif +} + +// TODO(scheib) Toggling Tab fullscreen while pending Tab or +// Browser fullscreen is broken currently http://crbug.com/154196 +TEST_F(FullscreenControllerStateUnitTest, DISABLED_ToggleTabWhenPendingTab) { +#if !defined(OS_WIN) // Only possible without reentrancy + AddTab(browser(), GURL(chrome::kAboutBlankURL)); + ASSERT_NO_FATAL_FAILURE( + TransitionToState(STATE_TO_TAB_FULLSCREEN)) + << GetAndClearDebugLog(); + + ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE)) << GetAndClearDebugLog(); + ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE)) << GetAndClearDebugLog(); + ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE)) << GetAndClearDebugLog(); +#endif +} + +// Soak tests: + +// Tests all states with all permutations of multiple events to detect lingering +// state issues that would bleed over to other states. +// I.E. for each state test all combinations of events E1, E2, E3. +// +// This produces coverage for event sequences that may happen normally but +// would not be exposed by traversing to each state via TransitionToState(). +// TransitionToState() always takes the same path even when multiple paths +// exist. +TEST_F(FullscreenControllerStateUnitTest, TransitionsForEachState) { + // A tab is needed for tab fullscreen. + AddTab(browser(), GURL(chrome::kAboutBlankURL)); + TestTransitionsForEachState(); + // Progress of test can be examined via LOG(INFO) << GetAndClearDebugLog(); +} + diff --git a/chrome/browser/ui/fullscreen/fullscreen_controller_test.cc b/chrome/browser/ui/fullscreen/fullscreen_controller_test.cc index da86e2a..7be9a0b 100644 --- a/chrome/browser/ui/fullscreen/fullscreen_controller_test.cc +++ b/chrome/browser/ui/fullscreen/fullscreen_controller_test.cc @@ -90,16 +90,6 @@ void FullscreenControllerTest::DenyCurrentFullscreenOrMouseLockRequest() { browser()->fullscreen_controller()->OnDenyFullscreenPermission(type); } -void FullscreenControllerTest::AddTabAtIndexAndWait(int index, const GURL& url, - content::PageTransition transition) { - content::TestNavigationObserver observer( - content::NotificationService::AllSources(), NULL, 1); - - AddTabAtIndex(index, url, transition); - - observer.Wait(); -} - void FullscreenControllerTest::GoBack() { content::TestNavigationObserver observer( content::NotificationService::AllSources(), NULL, 1); diff --git a/chrome/browser/ui/fullscreen/fullscreen_controller_test.h b/chrome/browser/ui/fullscreen/fullscreen_controller_test.h index eee66d9..cdc4730 100644 --- a/chrome/browser/ui/fullscreen/fullscreen_controller_test.h +++ b/chrome/browser/ui/fullscreen/fullscreen_controller_test.h @@ -51,8 +51,6 @@ class FullscreenControllerTest : public InProcessBrowserTest { bool IsFullscreenBubbleDisplayingButtons(); void AcceptCurrentFullscreenOrMouseLockRequest(); void DenyCurrentFullscreenOrMouseLockRequest(); - void AddTabAtIndexAndWait(int index, const GURL& url, - content::PageTransition transition); void GoBack(); void Reload(); static const char kFullscreenMouseLockHTML[]; diff --git a/chrome/browser/ui/fullscreen/fullscreen_controller_unittest.cc b/chrome/browser/ui/fullscreen/fullscreen_controller_unittest.cc deleted file mode 100644 index b296122..0000000 --- a/chrome/browser/ui/fullscreen/fullscreen_controller_unittest.cc +++ /dev/null @@ -1,969 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <memory.h> - -#include <iomanip> -#include <iostream> -#include <sstream> - -#include "build/build_config.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_tabstrip.h" -#include "chrome/browser/ui/fullscreen/fullscreen_controller.h" -#include "chrome/test/base/browser_with_test_window_test.h" -#include "content/public/browser/web_contents.h" -#include "content/public/common/url_constants.h" -#include "testing/gtest/include/gtest/gtest.h" - -// A BrowserWindow used for testing FullscreenController. ---------------------- -class FullscreenControllerTestWindow : public TestBrowserWindow { - public: - // Simulate the window state with an enumeration. - enum WindowState { - NORMAL, - FULLSCREEN, - // No TO_ state for METRO_SNAP, the windows implementation is synchronous. - METRO_SNAP, - TO_NORMAL, - TO_FULLSCREEN, - }; - - FullscreenControllerTestWindow(); - virtual ~FullscreenControllerTestWindow() {} - - // BrowserWindow Interface: - virtual void EnterFullscreen(const GURL& url, - FullscreenExitBubbleType type) OVERRIDE; - virtual void EnterFullscreen(); - virtual void ExitFullscreen() OVERRIDE; - virtual bool IsFullscreen() const OVERRIDE; -#if defined(OS_WIN) - virtual void SetMetroSnapMode(bool enable) OVERRIDE; - virtual bool IsInMetroSnapMode() const OVERRIDE; -#endif -#if defined(OS_MACOSX) - virtual void EnterPresentationMode( - const GURL& url, - FullscreenExitBubbleType bubble_type) OVERRIDE; - virtual void ExitPresentationMode() OVERRIDE; - virtual bool InPresentationMode() OVERRIDE; -#endif - - static const char* GetWindowStateString(WindowState state); - WindowState state() const { return state_; } - void set_browser(Browser* browser) { browser_ = browser; } - void set_reentrant(bool value) { reentrant_ = value; } - bool reentrant() const { return reentrant_; } - - // Simulates the window changing state. - void ChangeWindowFullscreenState(); - // Calls ChangeWindowFullscreenState() if |reentrant_| is true. - void ChangeWindowFullscreenStateIfReentrant(); - - private: - WindowState state_; - bool mac_presentation_mode_; - Browser* browser_; - - // Causes reentrant calls to be made by calling - // browser_->WindowFullscreenStateChanged() from the BrowserWindow - // interface methods. - bool reentrant_; -}; - -FullscreenControllerTestWindow::FullscreenControllerTestWindow() - : state_(NORMAL), - mac_presentation_mode_(false), - browser_(NULL), - reentrant_(false) { -} - -void FullscreenControllerTestWindow::EnterFullscreen( - const GURL& url, FullscreenExitBubbleType type) { - EnterFullscreen(); -} - -void FullscreenControllerTestWindow::EnterFullscreen() { - if (!IsFullscreen()) { - state_ = TO_FULLSCREEN; - ChangeWindowFullscreenStateIfReentrant(); - } -} - -void FullscreenControllerTestWindow::ExitFullscreen() { - if (IsFullscreen()) { - state_ = TO_NORMAL; - mac_presentation_mode_ = false; - ChangeWindowFullscreenStateIfReentrant(); - } -} - -bool FullscreenControllerTestWindow::IsFullscreen() const { - return state_ == FULLSCREEN || state_ == TO_NORMAL; -} - -#if defined(OS_WIN) -void FullscreenControllerTestWindow::SetMetroSnapMode(bool enable) { - if (enable != IsInMetroSnapMode()) { - if (enable) - state_ = METRO_SNAP; - else - state_ = NORMAL; - } - ChangeWindowFullscreenStateIfReentrant(); -} - -bool FullscreenControllerTestWindow::IsInMetroSnapMode() const { - return state_ == METRO_SNAP; -} -#endif - -#if defined(OS_MACOSX) -void FullscreenControllerTestWindow::EnterPresentationMode( - const GURL& url, - FullscreenExitBubbleType bubble_type) { - mac_presentation_mode_ = true; - EnterFullscreen(); -} - -void FullscreenControllerTestWindow::ExitPresentationMode() { - if (InPresentationMode()) { - mac_presentation_mode_ = false; - ExitFullscreen(); - } -} - -bool FullscreenControllerTestWindow::InPresentationMode() { - return mac_presentation_mode_; -} -#endif - -// static -const char* FullscreenControllerTestWindow::GetWindowStateString( - WindowState state) { - switch (state) { - case NORMAL: - return "NORMAL"; - case FULLSCREEN: - return "FULLSCREEN"; - case METRO_SNAP: - return "METRO_SNAP"; - case TO_FULLSCREEN: - return "TO_FULLSCREEN"; - case TO_NORMAL: - return "TO_NORMAL"; - default: - NOTREACHED() << "No string for state " << state; - return "WindowState-Unknown"; - } -} - -void FullscreenControllerTestWindow::ChangeWindowFullscreenState() { - // Several states result in "no operation" intentionally. The tests - // assume that all possible states and event pairs can be tested, even - // though window managers will not generate all of these. - switch (state_) { - case NORMAL: - break; - case FULLSCREEN: - break; - case METRO_SNAP: - break; - case TO_FULLSCREEN: - state_ = FULLSCREEN; - break; - case TO_NORMAL: - state_ = NORMAL; - break; - default: - NOTREACHED(); - } - // Emit a change event from every state to ensure the Fullscreen Controller - // handles it in all circumstances. - browser_->WindowFullscreenStateChanged(); -} - -void FullscreenControllerTestWindow::ChangeWindowFullscreenStateIfReentrant() { - if (reentrant_) - ChangeWindowFullscreenState(); -} - -// Unit test fixture for testing Fullscreen Controller. ------------------------ -class FullscreenControllerUnitTest : public BrowserWithTestWindowTest { - public: - enum State { - // The window is not in fullscreen. - STATE_NORMAL, - // User-initiated fullscreen. On Mac, this is Lion-mode for 10.7+. On 10.6, - // this is synonymous with STATE_BROWSER_FULLSCREEN_WITH_CHROME. - STATE_BROWSER_FULLSCREEN_NO_CHROME, -#if defined(OS_WIN) - // Windows 8 Metro Snap mode, which puts the window at 20% screen-width. - // No TO_ state for Metro, as the windows implementation is only reentrant. - STATE_METRO_SNAP, -#endif - // HTML5 tab-initiated fullscreen. - STATE_TAB_FULLSCREEN, - // Both tab and browser fullscreen. - STATE_TAB_BROWSER_FULLSCREEN, - // TO_ states are asynchronous states waiting for window state change - // before transitioning to their named state. - STATE_TO_NORMAL, - STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, - STATE_TO_TAB_FULLSCREEN, - NUM_STATES, - STATE_INVALID, - }; - - enum Event { - // FullscreenController::ToggleFullscreenMode() - TOGGLE_FULLSCREEN, - // FullscreenController::ToggleFullscreenModeForTab(, true) - TAB_FULLSCREEN_TRUE, - // FullscreenController::ToggleFullscreenModeForTab(, false) - TAB_FULLSCREEN_FALSE, -#if defined(OS_WIN) - // FullscreenController::SetMetroSnapMode(true) - METRO_SNAP_TRUE, - // FullscreenController::SetMetroSnapMode(flase) - METRO_SNAP_FALSE, -#endif - // FullscreenController::ChangeWindowFullscreenState() - WINDOW_CHANGE, - NUM_EVENTS, - EVENT_INVALID, - }; - - static const int MAX_STATE_NAME_LENGTH = 37; - static const int MAX_EVENT_NAME_LENGTH = 20; - - virtual void SetUp() OVERRIDE; - - static const char* GetStateString(State state); - static const char* GetEventString(Event event); - - // Causes Fullscreen Controller to transition to an arbitrary state. - void TransitionToState(State state); - // Makes one state change to approach |destination_state| via shortest path. - // Returns true if a state change is made. - // Repeated calls are needed to reach the destination. - bool TransitionAStepTowardState(State destination_state); - - // Causes the |event| to occur and return true on success. - bool InvokeEvent(Event event); - - // Checks that window state matches the expected controller state. - void VerifyWindowState(); - - protected: - // Generated information about the transitions between states. - struct StateTransitionInfo { - StateTransitionInfo() - : event(EVENT_INVALID), - state(STATE_INVALID), - distance(NUM_STATES) {} - Event event; // The |Event| that will cause the state transition. - State state; // The adjacent |State| transitioned to; not the final state. - int distance; // Steps to final state. NUM_STATES represents unknown. - }; - - // Returns next transition info for shortest path from source to destination. - StateTransitionInfo NextTransitionInShortestPath(State source, - State destination, - int search_limit); - - std::string GetAndClearDebugLog(); - - // Avoids currently broken cases in the fullscreen controller. - bool ShouldSkipStateAndEventPair(State state, Event event); - // Skips reentrant situations and calls ShouldSkipStateAndEventPair. - bool ShouldSkipTest(State state, Event event, bool reentrant); - - // Runs one test of transitioning to a state and invoking an event. - void TestStateAndEvent(State state, Event event, bool reentrant); - - FullscreenControllerTestWindow* window_; - FullscreenController* fullscreen_controller_; - State state_; - - // Human defined |State| that results given each [state][event] pair. - State transition_table_[NUM_STATES][NUM_EVENTS]; - - // Generated information about the transitions between states [from][to]. - StateTransitionInfo state_transitions_[NUM_STATES][NUM_STATES]; - - // Log of operations reported on errors via GetAndClearDebugLog(). - std::ostringstream debugging_log_; -}; - -void FullscreenControllerUnitTest::SetUp() { - window_ = new FullscreenControllerTestWindow(); - set_window(window_); // BrowserWithTestWindowTest takes ownership. - BrowserWithTestWindowTest::SetUp(); - window_->set_browser(browser()); - fullscreen_controller_ = browser()->fullscreen_controller(); - state_ = STATE_NORMAL; - - // Human specified state machine data. - // For each state, for each event, define the resulting state. - State transition_table_data[][NUM_EVENTS] = { - { // STATE_NORMAL: - STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TOGGLE_FULLSCREEN - STATE_TO_TAB_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE - STATE_NORMAL, // Event TAB_FULLSCREEN_FALSE -#if defined(OS_WIN) - STATE_METRO_SNAP, // Event METRO_SNAP_TRUE - STATE_NORMAL, // Event METRO_SNAP_FALSE -#endif - STATE_NORMAL, // Event WINDOW_CHANGE - }, - { // STATE_BROWSER_FULLSCREEN_NO_CHROME: - STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN - STATE_TAB_BROWSER_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE - STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_FALSE -#if defined(OS_WIN) - STATE_METRO_SNAP, // Event METRO_SNAP_TRUE - STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event METRO_SNAP_FALSE -#endif - STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event WINDOW_CHANGE - }, -#if defined(OS_WIN) - { // STATE_METRO_SNAP: - STATE_METRO_SNAP, // Event TOGGLE_FULLSCREEN - STATE_METRO_SNAP, // Event TAB_FULLSCREEN_TRUE - STATE_METRO_SNAP, // Event TAB_FULLSCREEN_FALSE - STATE_METRO_SNAP, // Event METRO_SNAP_TRUE - STATE_NORMAL, // Event METRO_SNAP_FALSE - STATE_METRO_SNAP, // Event WINDOW_CHANGE - }, -#endif - { // STATE_TAB_FULLSCREEN: - STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN - STATE_TAB_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE - STATE_TO_NORMAL, // Event TAB_FULLSCREEN_FALSE -#if defined(OS_WIN) - STATE_METRO_SNAP, // Event METRO_SNAP_TRUE - STATE_TAB_FULLSCREEN, // Event METRO_SNAP_FALSE -#endif - STATE_TAB_FULLSCREEN, // Event WINDOW_CHANGE - }, - { // STATE_TAB_BROWSER_FULLSCREEN: - STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN - STATE_TAB_BROWSER_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE -#if defined(OS_MACOSX) - // TODO(scheib) Mac exits browser mode too http://crbug.com/155642 - STATE_TO_NORMAL, // Event TAB_FULLSCREEN_FALSE -#else - STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_FALSE -#endif -#if defined(OS_WIN) - STATE_METRO_SNAP, // Event METRO_SNAP_TRUE - STATE_TAB_BROWSER_FULLSCREEN, // Event METRO_SNAP_FALSE -#endif - STATE_TAB_BROWSER_FULLSCREEN, // Event WINDOW_CHANGE - }, - // STATE_TO_NORMAL: - { STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN - // TODO(scheib) Should be a route back to TAB. http://crbug.com/154196 - STATE_TO_NORMAL, // Event TAB_FULLSCREEN_TRUE - STATE_TO_NORMAL, // Event TAB_FULLSCREEN_FALSE -#if defined(OS_WIN) - STATE_METRO_SNAP, // Event METRO_SNAP_TRUE - STATE_TO_NORMAL, // Event METRO_SNAP_FALSE -#endif - STATE_NORMAL, // Event WINDOW_CHANGE - }, - // STATE_TO_BROWSER_FULLSCREEN_NO_CHROME: - { STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TOGGLE_FULLSCREEN - // TODO(scheib) Should be a route to TAB_BROWSER http://crbug.com/154196 - STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_TRUE - STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_FALSE -#if defined(OS_WIN) - STATE_METRO_SNAP, // Event METRO_SNAP_TRUE - STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event METRO_SNAP_FALSE -#endif - STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event WINDOW_CHANGE - }, - // STATE_TO_TAB_FULLSCREEN: - { // TODO(scheib) Should be a route to TAB_BROWSER http://crbug.com/154196 - STATE_TO_TAB_FULLSCREEN, // Event TOGGLE_FULLSCREEN - STATE_TO_TAB_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE - // TODO(scheib) Should be a route back to NORMAL. http://crbug.com/154196 - STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_FALSE -#if defined(OS_WIN) - STATE_METRO_SNAP, // Event METRO_SNAP_TRUE - STATE_TO_TAB_FULLSCREEN, // Event METRO_SNAP_FALSE -#endif - STATE_TAB_FULLSCREEN, // Event WINDOW_CHANGE - }, - }; - ASSERT_EQ(sizeof(transition_table_data), sizeof(transition_table_)); - memcpy(transition_table_, transition_table_data, - sizeof(transition_table_data)); - - // Verify that transition_table_ has been completely defined. - for (int source = 0; source < NUM_STATES; source++) { - for (int event = 0; event < NUM_EVENTS; event++) { - ASSERT_NE(STATE_INVALID, transition_table_[source][event]); - ASSERT_LE(0, transition_table_[source][event]); - ASSERT_GT(NUM_STATES, transition_table_[source][event]); - } - } - - // Copy transition_table_ data into state_transitions_ table. - for (int source = 0; source < NUM_STATES; source++) { - for (int event = 0; event < NUM_EVENTS; event++) { - State destination = transition_table_[source][event]; - state_transitions_[source][destination].event = static_cast<Event>(event); - state_transitions_[source][destination].state = destination; - state_transitions_[source][destination].distance = 1; - } - } -} - -// static -const char* FullscreenControllerUnitTest::GetStateString(State state) { - switch (state) { - case STATE_NORMAL: - return "STATE_NORMAL"; - case STATE_BROWSER_FULLSCREEN_NO_CHROME: - return "STATE_BROWSER_FULLSCREEN_NO_CHROME"; -#if defined(OS_WIN) - case STATE_METRO_SNAP: - return "STATE_METRO_SNAP"; -#endif - case STATE_TAB_FULLSCREEN: - return "STATE_TAB_FULLSCREEN"; - case STATE_TAB_BROWSER_FULLSCREEN: - return "STATE_TAB_BROWSER_FULLSCREEN"; - case STATE_TO_NORMAL: - return "STATE_TO_NORMAL"; - case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME: - return "STATE_TO_BROWSER_FULLSCREEN_NO_CHROME"; - case STATE_TO_TAB_FULLSCREEN: - return "STATE_TO_TAB_FULLSCREEN"; - case STATE_INVALID: - return "STATE_INVALID"; - default: - NOTREACHED() << "No string for state " << state; - return "State-Unknown"; - } -} - -// static -const char* FullscreenControllerUnitTest::GetEventString(Event event) { - switch (event) { - case TOGGLE_FULLSCREEN: - return "TOGGLE_FULLSCREEN"; - case TAB_FULLSCREEN_TRUE: - return "TAB_FULLSCREEN_TRUE"; - case TAB_FULLSCREEN_FALSE: - return "TAB_FULLSCREEN_FALSE"; -#if defined(OS_WIN) - case METRO_SNAP_TRUE: - return "METRO_SNAP_TRUE"; - case METRO_SNAP_FALSE: - return "METRO_SNAP_FALSE"; -#endif - case WINDOW_CHANGE: - return "WINDOW_CHANGE"; - case EVENT_INVALID: - return "EVENT_INVALID"; - default: - NOTREACHED() << "No string for event " << event; - return "Event-Unknown"; - } -} - -void FullscreenControllerUnitTest::TransitionToState(State final_state) { - int max_steps = NUM_STATES; - while (max_steps-- && TransitionAStepTowardState(final_state)) - continue; - ASSERT_GE(max_steps, 0) << "TransitionToState was unable to achieve desired " - << "target state. TransitionAStepTowardState iterated too many times." - << GetAndClearDebugLog(); - ASSERT_EQ(final_state, state_) << "TransitionToState was unable to achieve " - << "desired target state. TransitionAStepTowardState returned false." - << GetAndClearDebugLog(); -} - -bool FullscreenControllerUnitTest::TransitionAStepTowardState( - State destination_state) { - State source_state = state_; - if (source_state == destination_state) - return false; - - StateTransitionInfo next = NextTransitionInShortestPath(source_state, - destination_state, - NUM_STATES); - if (next.state == STATE_INVALID) { - NOTREACHED() << "TransitionAStepTowardState unable to transition. " - << "NextTransitionInShortestPath(" - << GetStateString(source_state) << ", " - << GetStateString(destination_state) << ") returned STATE_INVALID." - << GetAndClearDebugLog(); - return false; - } - - return InvokeEvent(next.event); -} - -bool FullscreenControllerUnitTest::InvokeEvent(Event event) { - State source_state = state_; - State next_state = transition_table_[source_state][event]; - - // When simulating reentrant window change calls, expect the next state - // automatically. - if (window_->reentrant()) - next_state = transition_table_[next_state][WINDOW_CHANGE]; - - debugging_log_ << " InvokeEvent(" << std::left - << std::setw(MAX_EVENT_NAME_LENGTH) << GetEventString(event) - << ") to " - << std::setw(MAX_STATE_NAME_LENGTH) << GetStateString(next_state) - << " "; - - state_ = next_state; - - switch (event) { - case TOGGLE_FULLSCREEN: - fullscreen_controller_->ToggleFullscreenMode(); - break; - case TAB_FULLSCREEN_TRUE: - fullscreen_controller_->ToggleFullscreenModeForTab( - chrome::GetActiveWebContents(browser()), true); - break; - case TAB_FULLSCREEN_FALSE: - fullscreen_controller_->ToggleFullscreenModeForTab( - chrome::GetActiveWebContents(browser()), false); - break; -#if defined(OS_WIN) - case METRO_SNAP_TRUE: - fullscreen_controller_->SetMetroSnapMode(true); - break; - case METRO_SNAP_FALSE: - fullscreen_controller_->SetMetroSnapMode(false); - break; -#endif - case WINDOW_CHANGE: - window_->ChangeWindowFullscreenState(); - break; - default: - NOTREACHED() << "InvokeEvent needs a handler for event " - << GetEventString(event) << GetAndClearDebugLog(); - return false; - } - - debugging_log_ << "Window state now " - << FullscreenControllerTestWindow::GetWindowStateString(window_->state()) - << std::endl; - - VerifyWindowState(); - - return true; -} - -void FullscreenControllerUnitTest::VerifyWindowState() { - switch (state_) { - case STATE_NORMAL: - EXPECT_EQ(FullscreenControllerTestWindow::NORMAL, - window_->state()) << GetAndClearDebugLog(); -#if defined(OS_MACOSX) - EXPECT_FALSE(window_->InPresentationMode()) - << GetAndClearDebugLog(); -#endif - EXPECT_FALSE(fullscreen_controller_->IsFullscreenForBrowser()) - << GetAndClearDebugLog(); - EXPECT_FALSE(fullscreen_controller_->IsFullscreenForTabOrPending()) - << GetAndClearDebugLog(); - EXPECT_FALSE(fullscreen_controller_->IsInMetroSnapMode()) - << GetAndClearDebugLog(); - break; - case STATE_BROWSER_FULLSCREEN_NO_CHROME: - EXPECT_EQ(FullscreenControllerTestWindow::FULLSCREEN, - window_->state()) << GetAndClearDebugLog(); -#if defined(OS_MACOSX) - EXPECT_FALSE(window_->InPresentationMode()) - << GetAndClearDebugLog(); -#endif - EXPECT_TRUE(fullscreen_controller_->IsFullscreenForBrowser()) - << GetAndClearDebugLog(); - EXPECT_FALSE(fullscreen_controller_->IsFullscreenForTabOrPending()) - << GetAndClearDebugLog(); - EXPECT_FALSE(fullscreen_controller_->IsInMetroSnapMode()) - << GetAndClearDebugLog(); - break; -#if defined(OS_WIN) - case STATE_METRO_SNAP: - EXPECT_EQ(FullscreenControllerTestWindow::METRO_SNAP, - window_->state()) << GetAndClearDebugLog(); - // No expectation for InPresentationMode. - EXPECT_FALSE(fullscreen_controller_->IsFullscreenForBrowser()) - << GetAndClearDebugLog(); - EXPECT_FALSE(fullscreen_controller_->IsFullscreenForTabOrPending()) - << GetAndClearDebugLog(); - EXPECT_TRUE(fullscreen_controller_->IsInMetroSnapMode()) - << GetAndClearDebugLog(); - break; -#endif - case STATE_TAB_FULLSCREEN: - EXPECT_EQ(FullscreenControllerTestWindow::FULLSCREEN, - window_->state()) << GetAndClearDebugLog(); -#if defined(OS_MACOSX) - EXPECT_TRUE(window_->InPresentationMode()) - << GetAndClearDebugLog(); -#endif - EXPECT_FALSE(fullscreen_controller_->IsFullscreenForBrowser()) - << GetAndClearDebugLog(); - EXPECT_TRUE(fullscreen_controller_->IsFullscreenForTabOrPending()) - << GetAndClearDebugLog(); - EXPECT_FALSE(fullscreen_controller_->IsInMetroSnapMode()) - << GetAndClearDebugLog(); - break; - case STATE_TAB_BROWSER_FULLSCREEN: - EXPECT_EQ(FullscreenControllerTestWindow::FULLSCREEN, - window_->state()) << GetAndClearDebugLog(); -#if defined(OS_MACOSX) - EXPECT_TRUE(window_->InPresentationMode()) - << GetAndClearDebugLog(); -#endif -#if defined(OS_MACOSX) - // TODO(scheib) Mac is reporting incorrect IsFullscreenForBrowser(). - // e.g. in FullscreenControllerUnitTest. - // STATE_BROWSER_FULLSCREEN_NO_CHROME_TAB_FULLSCREEN_TRUE - // At the end of ToggleFullscreenModeForTab - // tab_caused_fullscreen_ has incorrectly been set to true even - // though controller was already in browser fullscreen. - // http://crbug.com/155650 - EXPECT_FALSE(fullscreen_controller_->IsFullscreenForBrowser()) - << GetAndClearDebugLog(); -#else - EXPECT_TRUE(fullscreen_controller_->IsFullscreenForBrowser()) - << GetAndClearDebugLog(); -#endif - EXPECT_TRUE(fullscreen_controller_->IsFullscreenForTabOrPending()) - << GetAndClearDebugLog(); - EXPECT_FALSE(fullscreen_controller_->IsInMetroSnapMode()) - << GetAndClearDebugLog(); - break; - case STATE_TO_NORMAL: - EXPECT_EQ(FullscreenControllerTestWindow::TO_NORMAL, - window_->state()) << GetAndClearDebugLog(); -#if defined(OS_MACOSX) - EXPECT_FALSE(window_->InPresentationMode()) - << GetAndClearDebugLog(); -#endif - // No expectation for IsFullscreenForBrowser. - // No expectation for IsFullscreenForTabOrPending. - EXPECT_FALSE(fullscreen_controller_->IsInMetroSnapMode()) - << GetAndClearDebugLog(); - break; - case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME: - EXPECT_EQ(FullscreenControllerTestWindow::TO_FULLSCREEN, - window_->state()) << GetAndClearDebugLog(); -#if defined(OS_MACOSX) - EXPECT_FALSE(window_->InPresentationMode()) - << GetAndClearDebugLog(); -#endif - EXPECT_FALSE(fullscreen_controller_->IsFullscreenForBrowser()) - << GetAndClearDebugLog(); - // No expectation for IsFullscreenForTabOrPending. - EXPECT_FALSE(fullscreen_controller_->IsInMetroSnapMode()) - << GetAndClearDebugLog(); - break; - case STATE_TO_TAB_FULLSCREEN: - EXPECT_EQ(FullscreenControllerTestWindow::TO_FULLSCREEN, - window_->state()) << GetAndClearDebugLog(); -#if defined(OS_MACOSX) - EXPECT_TRUE(window_->InPresentationMode()) - << GetAndClearDebugLog(); -#endif - EXPECT_FALSE(fullscreen_controller_->IsFullscreenForBrowser()) - << GetAndClearDebugLog(); - EXPECT_TRUE(fullscreen_controller_->IsFullscreenForTabOrPending()) - << GetAndClearDebugLog(); - EXPECT_FALSE(fullscreen_controller_->IsInMetroSnapMode()) - << GetAndClearDebugLog(); - break; - default: - NOTREACHED() << GetAndClearDebugLog(); - } -} - -FullscreenControllerUnitTest::StateTransitionInfo - FullscreenControllerUnitTest::NextTransitionInShortestPath( - State source, State destination, int search_limit) { - if (search_limit == 0) - return StateTransitionInfo(); // Return a default (invalid) state. - - if (state_transitions_[source][destination].state == STATE_INVALID) { - // Don't know the next state yet, do a depth first search. - StateTransitionInfo result; - - // Consider all states reachable via each event from the source state. - for (int event_int = 0; event_int < NUM_EVENTS; event_int++) { - Event event = static_cast<Event>(event_int); - State next_state_candidate = transition_table_[source][event]; - - if (ShouldSkipStateAndEventPair(source, event)) - continue; - - // Recurse. - StateTransitionInfo candidate = NextTransitionInShortestPath( - next_state_candidate, destination, search_limit - 1); - - if (candidate.distance + 1 < result.distance) { - result.event = event; - result.state = next_state_candidate; - result.distance = candidate.distance + 1; - } - } - - // Cache result so that a search is not required next time. - state_transitions_[source][destination] = result; - } - - return state_transitions_[source][destination]; -} - -std::string FullscreenControllerUnitTest::GetAndClearDebugLog() { - debugging_log_ << "(end of log)\n"; - std::string output_log = "\nDebugging Log:\n" + debugging_log_.str(); - debugging_log_.str(""); - return output_log; -} - -bool FullscreenControllerUnitTest::ShouldSkipStateAndEventPair(State state, - Event event) { - // TODO(scheib) Toggling Tab fullscreen while pending Tab or - // Browser fullscreen is broken currently http://crbug.com/154196 - if ((state == STATE_TO_BROWSER_FULLSCREEN_NO_CHROME || - state == STATE_TO_TAB_FULLSCREEN) && - (event == TAB_FULLSCREEN_TRUE || event == TAB_FULLSCREEN_FALSE)) - return true; - if (state == STATE_TO_NORMAL && event == TAB_FULLSCREEN_TRUE) - return true; - - return false; -} - -bool FullscreenControllerUnitTest::ShouldSkipTest(State state, - Event event, - bool reentrant) { -#if defined(OS_WIN) - // FullscreenController verifies that WindowFullscreenStateChanged is - // always reentrant on Windows. It will fail if we mock asynchronous calls. - if (!reentrant) { - debugging_log_ << "\nSkipping non-reentrant test on Windows.\n"; - return true; - } -#endif - - // When testing reentrancy there are states the fullscreen controller - // will be unable to remain in, as they will progress due to the - // reentrant window change call. Skip states that will be instantly - // exited by the reentrant call. - if (reentrant && (transition_table_[state][WINDOW_CHANGE] != state)) { - debugging_log_ << "\nSkipping reentrant test for transitory source state " - << GetStateString(state) << ".\n"; - return true; - } - - return ShouldSkipStateAndEventPair(state, event); -} - -void FullscreenControllerUnitTest::TestStateAndEvent(State state, - Event event, - bool reentrant) { - if (ShouldSkipTest(state, event, reentrant)) - return; - - debugging_log_ << "\nTest transition from state " - << GetStateString(state) - << (reentrant ? " with reentrant calls.\n" : ".\n"); - window_->set_reentrant(reentrant); - - debugging_log_ << " First, from " - << GetStateString(state_) << "\n"; - ASSERT_NO_FATAL_FAILURE(TransitionToState(state)) - << GetAndClearDebugLog(); - - debugging_log_ << " Then,\n"; - ASSERT_TRUE(InvokeEvent(event)) << GetAndClearDebugLog(); -} - - -// Tests ----------------------------------------------------------------------- - -#define TEST_EVENT_INNER(state, event, reentrant, reentrant_id) \ - TEST_F(FullscreenControllerUnitTest, state##_##event##reentrant_id) { \ - AddTab(browser(), GURL(chrome::kAboutBlankURL)); \ - ASSERT_NO_FATAL_FAILURE(TestStateAndEvent(state, event, reentrant)) \ - << GetAndClearDebugLog(); \ - } - // Progress of tests can be examined by inserting the following line: - // LOG(INFO) << GetAndClearDebugLog(); } - -#define TEST_EVENT(state, event) \ - TEST_EVENT_INNER(state, event, false, ); \ - TEST_EVENT_INNER(state, event, true, _Reentrant); - -// Individual tests for each pair of state and event: - -TEST_EVENT(STATE_NORMAL, TOGGLE_FULLSCREEN); -TEST_EVENT(STATE_NORMAL, TAB_FULLSCREEN_TRUE); -TEST_EVENT(STATE_NORMAL, TAB_FULLSCREEN_FALSE); -#if defined(OS_WIN) -TEST_EVENT(STATE_NORMAL, METRO_SNAP_TRUE); -TEST_EVENT(STATE_NORMAL, METRO_SNAP_FALSE); -#endif -TEST_EVENT(STATE_NORMAL, WINDOW_CHANGE); - -TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, TOGGLE_FULLSCREEN); -TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, TAB_FULLSCREEN_TRUE); -TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, TAB_FULLSCREEN_FALSE); -#if defined(OS_WIN) -TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, METRO_SNAP_TRUE); -TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, METRO_SNAP_FALSE); -#endif -TEST_EVENT(STATE_BROWSER_FULLSCREEN_NO_CHROME, WINDOW_CHANGE); - -#if defined(OS_WIN) -TEST_EVENT(STATE_METRO_SNAP, TOGGLE_FULLSCREEN); -TEST_EVENT(STATE_METRO_SNAP, TAB_FULLSCREEN_TRUE); -TEST_EVENT(STATE_METRO_SNAP, TAB_FULLSCREEN_FALSE); -TEST_EVENT(STATE_METRO_SNAP, METRO_SNAP_TRUE); -TEST_EVENT(STATE_METRO_SNAP, METRO_SNAP_FALSE); -TEST_EVENT(STATE_METRO_SNAP, WINDOW_CHANGE); -#endif - -TEST_EVENT(STATE_TAB_FULLSCREEN, TOGGLE_FULLSCREEN); -TEST_EVENT(STATE_TAB_FULLSCREEN, TAB_FULLSCREEN_TRUE); -TEST_EVENT(STATE_TAB_FULLSCREEN, TAB_FULLSCREEN_FALSE); -#if defined(OS_WIN) -TEST_EVENT(STATE_TAB_FULLSCREEN, METRO_SNAP_TRUE); -TEST_EVENT(STATE_TAB_FULLSCREEN, METRO_SNAP_FALSE); -#endif -TEST_EVENT(STATE_TAB_FULLSCREEN, WINDOW_CHANGE); - -TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, TOGGLE_FULLSCREEN); -TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, TAB_FULLSCREEN_TRUE); -TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, TAB_FULLSCREEN_FALSE); -#if defined(OS_WIN) -TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, METRO_SNAP_TRUE); -TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, METRO_SNAP_FALSE); -#endif -TEST_EVENT(STATE_TAB_BROWSER_FULLSCREEN, WINDOW_CHANGE); - -TEST_EVENT(STATE_TO_NORMAL, TOGGLE_FULLSCREEN); -TEST_EVENT(STATE_TO_NORMAL, TAB_FULLSCREEN_TRUE); -TEST_EVENT(STATE_TO_NORMAL, TAB_FULLSCREEN_FALSE); -#if defined(OS_WIN) -TEST_EVENT(STATE_TO_NORMAL, METRO_SNAP_TRUE); -TEST_EVENT(STATE_TO_NORMAL, METRO_SNAP_FALSE); -#endif -TEST_EVENT(STATE_TO_NORMAL, WINDOW_CHANGE); - -TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, TOGGLE_FULLSCREEN); -TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, TAB_FULLSCREEN_TRUE); -TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, TAB_FULLSCREEN_FALSE); -#if defined(OS_WIN) -TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, METRO_SNAP_TRUE); -TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, METRO_SNAP_FALSE); -#endif -TEST_EVENT(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, WINDOW_CHANGE); - -TEST_EVENT(STATE_TO_TAB_FULLSCREEN, TOGGLE_FULLSCREEN); -TEST_EVENT(STATE_TO_TAB_FULLSCREEN, TAB_FULLSCREEN_TRUE); -TEST_EVENT(STATE_TO_TAB_FULLSCREEN, TAB_FULLSCREEN_FALSE); -#if defined(OS_WIN) -TEST_EVENT(STATE_TO_TAB_FULLSCREEN, METRO_SNAP_TRUE); -TEST_EVENT(STATE_TO_TAB_FULLSCREEN, METRO_SNAP_FALSE); -#endif -TEST_EVENT(STATE_TO_TAB_FULLSCREEN, WINDOW_CHANGE); - -// Specific one-off tests for known issues: - -// TODO(scheib) Toggling Tab fullscreen while pending Tab or -// Browser fullscreen is broken currently http://crbug.com/154196 -TEST_F(FullscreenControllerUnitTest, DISABLED_ToggleTabWhenPendingBrowser) { -#if !defined(OS_WIN) // Only possible without reentrancy - AddTab(browser(), GURL(chrome::kAboutBlankURL)); - ASSERT_NO_FATAL_FAILURE( - TransitionToState(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME)) - << GetAndClearDebugLog(); - - ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE)) << GetAndClearDebugLog(); - ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE)) << GetAndClearDebugLog(); - ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE)) << GetAndClearDebugLog(); -#endif -} - -// TODO(scheib) Toggling Tab fullscreen while pending Tab or -// Browser fullscreen is broken currently http://crbug.com/154196 -TEST_F(FullscreenControllerUnitTest, DISABLED_ToggleTabWhenPendingTab) { -#if !defined(OS_WIN) // Only possible without reentrancy - AddTab(browser(), GURL(chrome::kAboutBlankURL)); - ASSERT_NO_FATAL_FAILURE( - TransitionToState(STATE_TO_TAB_FULLSCREEN)) - << GetAndClearDebugLog(); - - ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE)) << GetAndClearDebugLog(); - ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE)) << GetAndClearDebugLog(); - ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE)) << GetAndClearDebugLog(); -#endif -} - -// Soak tests: - -// Tests all states with all permutations of multiple events to detect lingering -// state issues that would bleed over to other states. -// I.E. for each state test all combinations of events E1, E2, E3. -// -// This produces coverage for event sequences that may happen normally but -// would not be exposed by traversing to each state via TransitionToState(). -// TransitionToState() always takes the same path even when multiple paths -// exist. -TEST_F(FullscreenControllerUnitTest, TransitionsForEachState) { - // A tab is needed for tab fullscreen. - AddTab(browser(), GURL(chrome::kAboutBlankURL)); - for (int reentrant = 0; reentrant <= 1; reentrant++) { - for (int source_int = 0; source_int < NUM_STATES; source_int++) { - for (int event1_int = 0; event1_int < NUM_EVENTS; event1_int++) { - State state = static_cast<State>(source_int); - Event event1 = static_cast<Event>(event1_int); - - // Early out if skipping all tests for this state, reduces log noise. - if (ShouldSkipTest(state, event1, !!reentrant)) - continue; - - for (int event2_int = 0; event2_int < NUM_EVENTS; event2_int++) { - for (int event3_int = 0; event3_int < NUM_EVENTS; event3_int++) { - Event event2 = static_cast<Event>(event2_int); - Event event3 = static_cast<Event>(event3_int); - - // Test each state and each event. - ASSERT_NO_FATAL_FAILURE(TestStateAndEvent(state, - event1, - !!reentrant)) - << GetAndClearDebugLog(); - - // Then, add an additional event to the sequence. - if (ShouldSkipStateAndEventPair(state_, event2)) - continue; - ASSERT_TRUE(InvokeEvent(event2)) << GetAndClearDebugLog(); - - // Then, add an additional event to the sequence. - if (ShouldSkipStateAndEventPair(state_, event3)) - continue; - ASSERT_TRUE(InvokeEvent(event3)) << GetAndClearDebugLog(); - } - } - } - } - } - // Progress of test can be examined via LOG(INFO) << GetAndClearDebugLog(); -} - diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 8d62ac5..b633f33 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -185,6 +185,8 @@ 'browser/ui/cocoa/run_loop_testing.mm', 'browser/ui/cocoa/test/ui_test_utils_mac.mm', 'browser/ui/find_bar/find_bar_host_unittest_util.h', + 'browser/ui/fullscreen/fullscreen_controller_state_test.cc', + 'browser/ui/fullscreen/fullscreen_controller_state_test.h', 'browser/ui/fullscreen/fullscreen_controller_test.cc', 'browser/ui/fullscreen/fullscreen_controller_test.h', 'browser/ui/gtk/find_bar_host_unittest_util_gtk.cc', @@ -511,6 +513,7 @@ 'browser/task_manager/task_manager_browsertest_util.cc', 'browser/ui/browser_focus_uitest.cc', 'browser/ui/fullscreen/fullscreen_controller_interactive_browsertest.cc', + 'browser/ui/fullscreen/fullscreen_controller_state_interactive_browsertest.cc', 'browser/ui/gtk/bookmarks/bookmark_bar_gtk_interactive_uitest.cc', 'browser/ui/omnibox/action_box_browsertest.cc', 'browser/ui/omnibox/omnibox_view_browsertest.cc', @@ -1982,7 +1985,7 @@ 'browser/ui/content_settings/content_setting_bubble_model_unittest.cc', 'browser/ui/content_settings/content_setting_image_model_unittest.cc', 'browser/ui/find_bar/find_backend_unittest.cc', - 'browser/ui/fullscreen/fullscreen_controller_unittest.cc', + 'browser/ui/fullscreen/fullscreen_controller_state_unittest.cc', 'browser/ui/global_error/global_error_service_unittest.cc', 'browser/ui/gtk/accelerators_gtk_unittest.cc', 'browser/ui/gtk/bookmarks/bookmark_bar_gtk_unittest.cc', @@ -2680,6 +2683,7 @@ 'browser/ui/bookmarks/bookmark_context_menu_controller_unittest.cc', 'browser/ui/bookmarks/bookmark_prompt_controller_unittest.cc', 'browser/ui/browser_command_controller_unittest.cc', + 'browser/ui/fullscreen/fullscreen_controller_state_unittest.cc', 'browser/ui/fullscreen/fullscreen_controller_unittest.cc', 'browser/ui/search/search_delegate_unittest.cc', 'browser/ui/tab_contents/tab_contents_iterator_unittest.cc', diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc index 6b842dd..e135331 100644 --- a/chrome/test/base/in_process_browser_test.cc +++ b/chrome/test/base/in_process_browser_test.cc @@ -39,6 +39,7 @@ #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_browser_thread.h" #include "content/public/test/test_launcher.h" +#include "content/public/test/test_navigation_observer.h" #include "net/base/mock_host_resolver.h" #include "net/test/test_server.h" #include "ui/compositor/compositor_switches.h" @@ -212,10 +213,15 @@ void InProcessBrowserTest::AddTabAtIndexToBrowser( int index, const GURL& url, content::PageTransition transition) { + content::TestNavigationObserver observer( + content::NotificationService::AllSources(), NULL, 1); + chrome::NavigateParams params(browser, url, transition); params.tabstrip_index = index; params.disposition = NEW_FOREGROUND_TAB; chrome::Navigate(¶ms); + + observer.Wait(); } void InProcessBrowserTest::AddTabAtIndex( |