Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / fullscreen / fullscreen_controller_state_unittest.cc
blob0f2d94d93fe884175c88a17f4d0f5b185b267de6
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 {
26 public:
27 // Simulate the window state with an enumeration.
28 enum WindowState {
29 NORMAL,
30 FULLSCREEN,
31 // No TO_ state for METRO_SNAP, the windows implementation is synchronous.
32 METRO_SNAP,
33 TO_NORMAL,
34 TO_FULLSCREEN,
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;
46 #if defined(OS_WIN)
47 virtual void SetMetroSnapMode(bool enable) OVERRIDE;
48 virtual bool IsInMetroSnapMode() const OVERRIDE;
49 #endif
50 #if defined(OS_MACOSX)
51 virtual void EnterFullscreenWithChrome() OVERRIDE;
52 virtual bool IsFullscreenWithChrome() OVERRIDE;
53 virtual bool IsFullscreenWithoutChrome() OVERRIDE;
54 #endif
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();
63 private:
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);
72 WindowState state_;
73 bool mac_with_chrome_mode_;
74 Browser* browser_;
77 FullscreenControllerTestWindow::FullscreenControllerTestWindow()
78 : state_(NORMAL),
79 mac_with_chrome_mode_(false),
80 browser_(NULL) {
83 void FullscreenControllerTestWindow::EnterFullscreen(
84 const GURL& url, FullscreenExitBubbleType type) {
85 EnterFullscreen(false);
88 void FullscreenControllerTestWindow::ExitFullscreen() {
89 if (IsFullscreen()) {
90 state_ = TO_NORMAL;
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;
105 #else
106 return state_ == FULLSCREEN || state_ == TO_NORMAL;
107 #endif
110 #if defined(OS_WIN)
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;
122 #endif
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_;
136 #endif
138 // static
139 const char* FullscreenControllerTestWindow::GetWindowStateString(
140 WindowState state) {
141 switch (state) {
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);
147 default:
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)
158 state_ = FULLSCREEN;
159 else if (state_ == TO_NORMAL)
160 state_ = 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;
172 if (!IsFullscreen())
173 state_ = TO_FULLSCREEN;
175 if (reentrant)
176 ChangeWindowFullscreenState();
179 bool FullscreenControllerTestWindow::IsTransitionReentrant(
180 bool new_fullscreen,
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();
185 #else
186 bool mac_with_chrome_mode_changed = false;
187 #endif
188 bool fullscreen_changed = (new_fullscreen != IsFullscreen());
190 if (!fullscreen_changed && !mac_with_chrome_mode_changed)
191 return false;
193 if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
194 return true;
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 {
211 public:
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;
221 protected:
222 // FullscreenControllerStateTest:
223 virtual bool ShouldSkipStateAndEventPair(State state, Event event) OVERRIDE;
224 virtual Browser* GetBrowser() OVERRIDE;
225 FullscreenControllerTestWindow* window_;
228 FullscreenControllerStateUnitTest::FullscreenControllerStateUnitTest ()
229 : window_(NULL) {
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() {
251 switch (state()) {
252 case STATE_NORMAL:
253 EXPECT_EQ(FullscreenControllerTestWindow::NORMAL,
254 window_->state()) << GetAndClearDebugLog();
255 break;
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();
264 break;
266 #if defined(OS_WIN)
267 case STATE_METRO_SNAP:
268 EXPECT_EQ(FullscreenControllerTestWindow::METRO_SNAP,
269 window_->state()) << GetAndClearDebugLog();
270 break;
271 #endif
273 case STATE_TO_NORMAL:
274 EXPECT_EQ(FullscreenControllerTestWindow::TO_NORMAL,
275 window_->state()) << GetAndClearDebugLog();
276 break;
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();
283 break;
285 default:
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)
304 return true;
305 #endif
307 return FullscreenControllerStateTest::ShouldSkipStateAndEventPair(state,
308 event);
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
325 // exist.
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())
356 return;
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())
372 return;
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))
399 continue;
400 // Compute the transition
401 if (NextTransitionInShortestPath(state1, state2, NUM_STATES).state ==
402 STATE_INVALID) {
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
417 // browser bounds.
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());