1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "build/build_config.h"
6 #include "chrome/browser/ui/browser.h"
7 #include "chrome/browser/ui/browser_tabstrip.h"
8 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
9 #include "chrome/browser/ui/fullscreen/fullscreen_controller_state_test.h"
10 #include "chrome/browser/ui/tabs/tab_strip_model.h"
11 #include "chrome/test/base/browser_with_test_window_test.h"
12 #include "content/public/browser/web_contents.h"
13 #include "content/public/common/url_constants.h"
14 #include "testing/gtest/include/gtest/gtest.h"
16 // The FullscreenControllerStateUnitTest unit test suite exhastively tests
17 // the FullscreenController through all permutations of events. The behavior
18 // of the BrowserWindow is mocked via FullscreenControllerTestWindow.
21 // FullscreenControllerTestWindow ----------------------------------------------
23 // A BrowserWindow used for testing FullscreenController. The behavior of this
24 // mock is verfied manually by running FullscreenControllerStateInteractiveTest.
25 class FullscreenControllerTestWindow
: public TestBrowserWindow
{
27 // Simulate the window state with an enumeration.
31 // No TO_ state for METRO_SNAP, the windows implementation is synchronous.
37 FullscreenControllerTestWindow();
38 virtual ~FullscreenControllerTestWindow() {}
40 // BrowserWindow Interface:
41 virtual void EnterFullscreen(const GURL
& url
,
42 FullscreenExitBubbleType type
) OVERRIDE
;
43 virtual void ExitFullscreen() OVERRIDE
;
44 virtual bool ShouldHideUIForFullscreen() const OVERRIDE
;
45 virtual bool IsFullscreen() const OVERRIDE
;
47 virtual void SetMetroSnapMode(bool enable
) OVERRIDE
;
48 virtual bool IsInMetroSnapMode() const OVERRIDE
;
50 #if defined(OS_MACOSX)
51 virtual void EnterFullscreenWithChrome() OVERRIDE
;
52 virtual bool IsFullscreenWithChrome() OVERRIDE
;
53 virtual bool IsFullscreenWithoutChrome() OVERRIDE
;
56 static const char* GetWindowStateString(WindowState state
);
57 WindowState
state() const { return state_
; }
58 void set_browser(Browser
* browser
) { browser_
= browser
; }
60 // Simulates the window changing state.
61 void ChangeWindowFullscreenState();
64 // Enters fullscreen with |new_mac_with_chrome_mode|.
65 void EnterFullscreen(bool new_mac_with_chrome_mode
);
67 // Returns true if ChangeWindowFullscreenState() should be called as a result
68 // of updating the current fullscreen state to the passed in state.
69 bool IsTransitionReentrant(bool new_fullscreen
,
70 bool new_mac_with_chrome_mode
);
73 bool mac_with_chrome_mode_
;
77 FullscreenControllerTestWindow::FullscreenControllerTestWindow()
79 mac_with_chrome_mode_(false),
83 void FullscreenControllerTestWindow::EnterFullscreen(
84 const GURL
& url
, FullscreenExitBubbleType type
) {
85 EnterFullscreen(false);
88 void FullscreenControllerTestWindow::ExitFullscreen() {
91 mac_with_chrome_mode_
= false;
93 if (IsTransitionReentrant(false, false))
94 ChangeWindowFullscreenState();
98 bool FullscreenControllerTestWindow::ShouldHideUIForFullscreen() const {
99 return IsFullscreen();
102 bool FullscreenControllerTestWindow::IsFullscreen() const {
103 #if defined(OS_MACOSX)
104 return state_
== FULLSCREEN
|| state_
== TO_FULLSCREEN
;
106 return state_
== FULLSCREEN
|| state_
== TO_NORMAL
;
111 void FullscreenControllerTestWindow::SetMetroSnapMode(bool enable
) {
112 if (enable
!= IsInMetroSnapMode())
113 state_
= enable
? METRO_SNAP
: NORMAL
;
115 if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
116 ChangeWindowFullscreenState();
119 bool FullscreenControllerTestWindow::IsInMetroSnapMode() const {
120 return state_
== METRO_SNAP
;
124 #if defined(OS_MACOSX)
125 void FullscreenControllerTestWindow::EnterFullscreenWithChrome() {
126 EnterFullscreen(true);
129 bool FullscreenControllerTestWindow::IsFullscreenWithChrome() {
130 return IsFullscreen() && mac_with_chrome_mode_
;
133 bool FullscreenControllerTestWindow::IsFullscreenWithoutChrome() {
134 return IsFullscreen() && !mac_with_chrome_mode_
;
139 const char* FullscreenControllerTestWindow::GetWindowStateString(
142 ENUM_TO_STRING(NORMAL
);
143 ENUM_TO_STRING(FULLSCREEN
);
144 ENUM_TO_STRING(METRO_SNAP
);
145 ENUM_TO_STRING(TO_FULLSCREEN
);
146 ENUM_TO_STRING(TO_NORMAL
);
148 NOTREACHED() << "No string for state " << state
;
149 return "WindowState-Unknown";
153 void FullscreenControllerTestWindow::ChangeWindowFullscreenState() {
154 // Most states result in "no operation" intentionally. The tests
155 // assume that all possible states and event pairs can be tested, even
156 // though window managers will not generate all of these.
157 if (state_
== TO_FULLSCREEN
)
159 else if (state_
== TO_NORMAL
)
162 // Emit a change event from every state to ensure the Fullscreen Controller
163 // handles it in all circumstances.
164 browser_
->WindowFullscreenStateChanged();
167 void FullscreenControllerTestWindow::EnterFullscreen(
168 bool new_mac_with_chrome_mode
) {
169 bool reentrant
= IsTransitionReentrant(true, new_mac_with_chrome_mode
);
171 mac_with_chrome_mode_
= new_mac_with_chrome_mode
;
173 state_
= TO_FULLSCREEN
;
176 ChangeWindowFullscreenState();
179 bool FullscreenControllerTestWindow::IsTransitionReentrant(
181 bool new_mac_with_chrome_mode
) {
182 #if defined(OS_MACOSX)
183 bool mac_with_chrome_mode_changed
= new_mac_with_chrome_mode
?
184 IsFullscreenWithoutChrome() : IsFullscreenWithChrome();
186 bool mac_with_chrome_mode_changed
= false;
188 bool fullscreen_changed
= (new_fullscreen
!= IsFullscreen());
190 if (!fullscreen_changed
&& !mac_with_chrome_mode_changed
)
193 if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
196 // BrowserWindowCocoa::EnterFullscreen() and
197 // BrowserWindowCocoa::EnterFullscreenWithChrome() are reentrant when
198 // switching between fullscreen with chrome and fullscreen without chrome.
199 return state_
== FULLSCREEN
&&
200 !fullscreen_changed
&&
201 mac_with_chrome_mode_changed
;
205 // FullscreenControllerStateUnitTest -------------------------------------------
207 // Unit test fixture testing Fullscreen Controller through its states. Most of
208 // the test logic comes from FullscreenControllerStateTest.
209 class FullscreenControllerStateUnitTest
: public BrowserWithTestWindowTest
,
210 public FullscreenControllerStateTest
{
212 FullscreenControllerStateUnitTest();
214 // FullscreenControllerStateTest:
215 virtual void SetUp() OVERRIDE
;
216 virtual BrowserWindow
* CreateBrowserWindow() OVERRIDE
;
217 virtual void ChangeWindowFullscreenState() OVERRIDE
;
218 virtual const char* GetWindowStateString() OVERRIDE
;
219 virtual void VerifyWindowState() OVERRIDE
;
222 // FullscreenControllerStateTest:
223 virtual bool ShouldSkipStateAndEventPair(State state
, Event event
) OVERRIDE
;
224 virtual Browser
* GetBrowser() OVERRIDE
;
225 FullscreenControllerTestWindow
* window_
;
228 FullscreenControllerStateUnitTest::FullscreenControllerStateUnitTest ()
232 void FullscreenControllerStateUnitTest::SetUp() {
233 BrowserWithTestWindowTest::SetUp();
234 window_
->set_browser(browser());
237 BrowserWindow
* FullscreenControllerStateUnitTest::CreateBrowserWindow() {
238 window_
= new FullscreenControllerTestWindow();
239 return window_
; // BrowserWithTestWindowTest takes ownership.
242 void FullscreenControllerStateUnitTest::ChangeWindowFullscreenState() {
243 window_
->ChangeWindowFullscreenState();
246 const char* FullscreenControllerStateUnitTest::GetWindowStateString() {
247 return FullscreenControllerTestWindow::GetWindowStateString(window_
->state());
250 void FullscreenControllerStateUnitTest::VerifyWindowState() {
253 EXPECT_EQ(FullscreenControllerTestWindow::NORMAL
,
254 window_
->state()) << GetAndClearDebugLog();
257 case STATE_BROWSER_FULLSCREEN_NO_CHROME
:
258 case STATE_BROWSER_FULLSCREEN_WITH_CHROME
:
259 case STATE_TAB_FULLSCREEN
:
260 case STATE_TAB_BROWSER_FULLSCREEN
:
261 case STATE_TAB_BROWSER_FULLSCREEN_CHROME
:
262 EXPECT_EQ(FullscreenControllerTestWindow::FULLSCREEN
,
263 window_
->state()) << GetAndClearDebugLog();
267 case STATE_METRO_SNAP
:
268 EXPECT_EQ(FullscreenControllerTestWindow::METRO_SNAP
,
269 window_
->state()) << GetAndClearDebugLog();
273 case STATE_TO_NORMAL
:
274 EXPECT_EQ(FullscreenControllerTestWindow::TO_NORMAL
,
275 window_
->state()) << GetAndClearDebugLog();
278 case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME
:
279 case STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME
:
280 case STATE_TO_TAB_FULLSCREEN
:
281 EXPECT_EQ(FullscreenControllerTestWindow::TO_FULLSCREEN
,
282 window_
->state()) << GetAndClearDebugLog();
286 NOTREACHED() << GetAndClearDebugLog();
289 FullscreenControllerStateTest::VerifyWindowState();
292 bool FullscreenControllerStateUnitTest::ShouldSkipStateAndEventPair(
293 State state
, Event event
) {
294 #if defined(OS_MACOSX)
295 // TODO(scheib) Toggle, Window Event, Toggle, Toggle on Mac as exposed by
296 // test *.STATE_TO_NORMAL__TOGGLE_FULLSCREEN runs interactively and exits to
297 // Normal. This doesn't appear to be the desired result, and would add
298 // too much complexity to mimic in our simple FullscreenControllerTestWindow.
299 // http://crbug.com/156968
300 if ((state
== STATE_TO_NORMAL
||
301 state
== STATE_TO_BROWSER_FULLSCREEN_NO_CHROME
||
302 state
== STATE_TO_TAB_FULLSCREEN
) &&
303 event
== TOGGLE_FULLSCREEN
)
307 return FullscreenControllerStateTest::ShouldSkipStateAndEventPair(state
,
311 Browser
* FullscreenControllerStateUnitTest::GetBrowser() {
312 return BrowserWithTestWindowTest::browser();
316 // Soak tests ------------------------------------------------------------------
318 // Tests all states with all permutations of multiple events to detect lingering
319 // state issues that would bleed over to other states.
320 // I.E. for each state test all combinations of events E1, E2, E3.
322 // This produces coverage for event sequences that may happen normally but
323 // would not be exposed by traversing to each state via TransitionToState().
324 // TransitionToState() always takes the same path even when multiple paths
326 TEST_F(FullscreenControllerStateUnitTest
, TransitionsForEachState
) {
327 // A tab is needed for tab fullscreen.
328 AddTab(browser(), GURL(content::kAboutBlankURL
));
329 TestTransitionsForEachState();
330 // Progress of test can be examined via LOG(INFO) << GetAndClearDebugLog();
334 // Individual tests for each pair of state and event ---------------------------
336 #define TEST_EVENT(state, event) \
337 TEST_F(FullscreenControllerStateUnitTest, state##__##event) { \
338 AddTab(browser(), GURL(content::kAboutBlankURL)); \
339 ASSERT_NO_FATAL_FAILURE(TestStateAndEvent(state, event)) \
340 << GetAndClearDebugLog(); \
342 // Progress of tests can be examined by inserting the following line:
343 // LOG(INFO) << GetAndClearDebugLog(); }
345 #include "chrome/browser/ui/fullscreen/fullscreen_controller_state_tests.h"
348 // Specific one-off tests for known issues -------------------------------------
350 // TODO(scheib) Toggling Tab fullscreen while pending Tab or
351 // Browser fullscreen is broken currently http://crbug.com/154196
352 TEST_F(FullscreenControllerStateUnitTest
,
353 DISABLED_ToggleTabWhenPendingBrowser
) {
354 // Only possible without reentrancy.
355 if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
357 AddTab(browser(), GURL(content::kAboutBlankURL
));
358 ASSERT_NO_FATAL_FAILURE(
359 TransitionToState(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME
))
360 << GetAndClearDebugLog();
362 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE
)) << GetAndClearDebugLog();
363 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE
)) << GetAndClearDebugLog();
364 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE
)) << GetAndClearDebugLog();
367 // TODO(scheib) Toggling Tab fullscreen while pending Tab or
368 // Browser fullscreen is broken currently http://crbug.com/154196
369 TEST_F(FullscreenControllerStateUnitTest
, DISABLED_ToggleTabWhenPendingTab
) {
370 // Only possible without reentrancy.
371 if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
373 AddTab(browser(), GURL(content::kAboutBlankURL
));
374 ASSERT_NO_FATAL_FAILURE(
375 TransitionToState(STATE_TO_TAB_FULLSCREEN
))
376 << GetAndClearDebugLog();
378 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE
)) << GetAndClearDebugLog();
379 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE
)) << GetAndClearDebugLog();
380 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE
)) << GetAndClearDebugLog();
383 // Debugging utility: Display the transition tables. Intentionally disabled
384 TEST_F(FullscreenControllerStateUnitTest
, DISABLED_DebugLogStateTables
) {
385 std::ostringstream output
;
386 output
<< "\n\nTransition Table:";
387 output
<< GetTransitionTableAsString();
389 output
<< "\n\nInitial transitions:";
390 output
<< GetStateTransitionsAsString();
392 // Calculate all transition pairs.
393 for (int state1_int
= 0; state1_int
< NUM_STATES
; ++state1_int
) {
394 State state1
= static_cast<State
>(state1_int
);
395 for (int state2_int
= 0; state2_int
< NUM_STATES
; ++state2_int
) {
396 State state2
= static_cast<State
>(state2_int
);
397 if (ShouldSkipStateAndEventPair(state1
, EVENT_INVALID
) ||
398 ShouldSkipStateAndEventPair(state2
, EVENT_INVALID
))
400 // Compute the transition
401 if (NextTransitionInShortestPath(state1
, state2
, NUM_STATES
).state
==
403 LOG(ERROR
) << "Should be skipping state transitions for: "
404 << GetStateString(state1
) << " " << GetStateString(state2
);
409 output
<< "\n\nAll transitions:";
410 output
<< GetStateTransitionsAsString();
411 LOG(INFO
) << output
.str();
414 // Test that the fullscreen exit bubble is closed by
415 // WindowFullscreenStateChanged() if fullscreen is exited via BrowserWindow.
416 // This currently occurs when an extension exits fullscreen via changing the
418 TEST_F(FullscreenControllerStateUnitTest
, ExitFullscreenViaBrowserWindow
) {
419 AddTab(browser(), GURL(content::kAboutBlankURL
));
420 ASSERT_TRUE(InvokeEvent(TOGGLE_FULLSCREEN
));
421 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE
));
422 ASSERT_TRUE(browser()->window()->IsFullscreen());
423 // Exit fullscreen without going through fullscreen controller.
424 browser()->window()->ExitFullscreen();
425 ChangeWindowFullscreenState();
426 EXPECT_EQ(FEB_TYPE_NONE
,
427 browser()->fullscreen_controller()->GetFullscreenExitBubbleType());
430 // Test that switching tabs takes the browser out of tab fullscreen.
431 TEST_F(FullscreenControllerStateUnitTest
, ExitTabFullscreenViaSwitchingTab
) {
432 AddTab(browser(), GURL(content::kAboutBlankURL
));
433 AddTab(browser(), GURL(content::kAboutBlankURL
));
434 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE
));
435 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE
));
436 ASSERT_TRUE(browser()->window()->IsFullscreen());
438 browser()->tab_strip_model()->SelectNextTab();
439 ChangeWindowFullscreenState();
440 EXPECT_FALSE(browser()->window()->IsFullscreen());
443 // Test that switching tabs via detaching the active tab (which is in tab
444 // fullscreen) takes the browser out of tab fullscreen. This case can
445 // occur if the user is in both tab fullscreen and immersive browser fullscreen.
446 TEST_F(FullscreenControllerStateUnitTest
, ExitTabFullscreenViaDetachingTab
) {
447 AddTab(browser(), GURL(content::kAboutBlankURL
));
448 AddTab(browser(), GURL(content::kAboutBlankURL
));
449 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE
));
450 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE
));
451 ASSERT_TRUE(browser()->window()->IsFullscreen());
453 scoped_ptr
<content::WebContents
> web_contents(
454 browser()->tab_strip_model()->DetachWebContentsAt(0));
455 ChangeWindowFullscreenState();
456 EXPECT_FALSE(browser()->window()->IsFullscreen());
459 // Test that replacing the web contents for a tab which is in tab fullscreen
460 // takes the browser out of tab fullscreen. This can occur if the user
461 // navigates to a prerendered page from a page which is tab fullscreen.
462 TEST_F(FullscreenControllerStateUnitTest
, ExitTabFullscreenViaReplacingTab
) {
463 AddTab(browser(), GURL(content::kAboutBlankURL
));
464 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE
));
465 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE
));
466 ASSERT_TRUE(browser()->window()->IsFullscreen());
468 content::WebContents
* new_web_contents
= content::WebContents::Create(
469 content::WebContents::CreateParams(profile()));
470 scoped_ptr
<content::WebContents
> old_web_contents(
471 browser()->tab_strip_model()->ReplaceWebContentsAt(
472 0, new_web_contents
));
473 ChangeWindowFullscreenState();
474 EXPECT_FALSE(browser()->window()->IsFullscreen());