- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / fullscreen / fullscreen_controller_state_unittest.cc
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.
4
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"
15
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.
19
20
21 // FullscreenControllerTestWindow ----------------------------------------------
22
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,
35   };
36
37   FullscreenControllerTestWindow();
38   virtual ~FullscreenControllerTestWindow() {}
39
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
55
56   static const char* GetWindowStateString(WindowState state);
57   WindowState state() const { return state_; }
58   void set_browser(Browser* browser) { browser_ = browser; }
59
60   // Simulates the window changing state.
61   void ChangeWindowFullscreenState();
62
63  private:
64   // Enters fullscreen with |new_mac_with_chrome_mode|.
65   void EnterFullscreen(bool new_mac_with_chrome_mode);
66
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);
71
72   WindowState state_;
73   bool mac_with_chrome_mode_;
74   Browser* browser_;
75 };
76
77 FullscreenControllerTestWindow::FullscreenControllerTestWindow()
78     : state_(NORMAL),
79       mac_with_chrome_mode_(false),
80       browser_(NULL) {
81 }
82
83 void FullscreenControllerTestWindow::EnterFullscreen(
84     const GURL& url, FullscreenExitBubbleType type) {
85   EnterFullscreen(false);
86 }
87
88 void FullscreenControllerTestWindow::ExitFullscreen() {
89   if (IsFullscreen()) {
90     state_ = TO_NORMAL;
91     mac_with_chrome_mode_ = false;
92
93     if (IsTransitionReentrant(false, false))
94       ChangeWindowFullscreenState();
95   }
96 }
97
98 bool FullscreenControllerTestWindow::ShouldHideUIForFullscreen() const {
99   return IsFullscreen();
100 }
101
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
108 }
109
110 #if defined(OS_WIN)
111 void FullscreenControllerTestWindow::SetMetroSnapMode(bool enable) {
112   if (enable != IsInMetroSnapMode())
113     state_ = enable ? METRO_SNAP : NORMAL;
114
115   if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
116     ChangeWindowFullscreenState();
117 }
118
119 bool FullscreenControllerTestWindow::IsInMetroSnapMode() const {
120   return state_ == METRO_SNAP;
121 }
122 #endif
123
124 #if defined(OS_MACOSX)
125 void FullscreenControllerTestWindow::EnterFullscreenWithChrome() {
126   EnterFullscreen(true);
127 }
128
129 bool FullscreenControllerTestWindow::IsFullscreenWithChrome() {
130   return IsFullscreen() && mac_with_chrome_mode_;
131 }
132
133 bool FullscreenControllerTestWindow::IsFullscreenWithoutChrome() {
134   return IsFullscreen() && !mac_with_chrome_mode_;
135 }
136 #endif
137
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";
150   }
151 }
152
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;
161
162   // Emit a change event from every state to ensure the Fullscreen Controller
163   // handles it in all circumstances.
164   browser_->WindowFullscreenStateChanged();
165 }
166
167 void FullscreenControllerTestWindow::EnterFullscreen(
168     bool new_mac_with_chrome_mode) {
169   bool reentrant = IsTransitionReentrant(true, new_mac_with_chrome_mode);
170
171   mac_with_chrome_mode_ = new_mac_with_chrome_mode;
172   if (!IsFullscreen())
173     state_ = TO_FULLSCREEN;
174
175   if (reentrant)
176     ChangeWindowFullscreenState();
177 }
178
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());
189
190   if (!fullscreen_changed && !mac_with_chrome_mode_changed)
191     return false;
192
193   if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
194     return true;
195
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;
202 }
203
204
205 // FullscreenControllerStateUnitTest -------------------------------------------
206
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();
213
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;
220
221  protected:
222   // FullscreenControllerStateTest:
223   virtual bool ShouldSkipStateAndEventPair(State state, Event event) OVERRIDE;
224   virtual Browser* GetBrowser() OVERRIDE;
225   FullscreenControllerTestWindow* window_;
226 };
227
228 FullscreenControllerStateUnitTest::FullscreenControllerStateUnitTest ()
229     : window_(NULL) {
230 }
231
232 void FullscreenControllerStateUnitTest::SetUp() {
233   BrowserWithTestWindowTest::SetUp();
234   window_->set_browser(browser());
235 }
236
237 BrowserWindow* FullscreenControllerStateUnitTest::CreateBrowserWindow() {
238   window_ = new FullscreenControllerTestWindow();
239   return window_;  // BrowserWithTestWindowTest takes ownership.
240 }
241
242 void FullscreenControllerStateUnitTest::ChangeWindowFullscreenState() {
243   window_->ChangeWindowFullscreenState();
244 }
245
246 const char* FullscreenControllerStateUnitTest::GetWindowStateString() {
247   return FullscreenControllerTestWindow::GetWindowStateString(window_->state());
248 }
249
250 void FullscreenControllerStateUnitTest::VerifyWindowState() {
251   switch (state()) {
252     case STATE_NORMAL:
253       EXPECT_EQ(FullscreenControllerTestWindow::NORMAL,
254                 window_->state()) << GetAndClearDebugLog();
255       break;
256
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;
265
266 #if defined(OS_WIN)
267     case STATE_METRO_SNAP:
268       EXPECT_EQ(FullscreenControllerTestWindow::METRO_SNAP,
269                 window_->state()) << GetAndClearDebugLog();
270       break;
271 #endif
272
273     case STATE_TO_NORMAL:
274       EXPECT_EQ(FullscreenControllerTestWindow::TO_NORMAL,
275                 window_->state()) << GetAndClearDebugLog();
276       break;
277
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;
284
285     default:
286       NOTREACHED() << GetAndClearDebugLog();
287   }
288
289   FullscreenControllerStateTest::VerifyWindowState();
290 }
291
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
306
307   return FullscreenControllerStateTest::ShouldSkipStateAndEventPair(state,
308                                                                     event);
309 }
310
311 Browser* FullscreenControllerStateUnitTest::GetBrowser() {
312   return BrowserWithTestWindowTest::browser();
313 }
314
315
316 // Soak tests ------------------------------------------------------------------
317
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.
321 //
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();
331 }
332
333
334 // Individual tests for each pair of state and event ---------------------------
335
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(); \
341     }
342     // Progress of tests can be examined by inserting the following line:
343     // LOG(INFO) << GetAndClearDebugLog(); }
344
345 #include "chrome/browser/ui/fullscreen/fullscreen_controller_state_tests.h"
346
347
348 // Specific one-off tests for known issues -------------------------------------
349
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();
361
362   ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE)) << GetAndClearDebugLog();
363   ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE)) << GetAndClearDebugLog();
364   ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE)) << GetAndClearDebugLog();
365 }
366
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();
377
378   ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE)) << GetAndClearDebugLog();
379   ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE)) << GetAndClearDebugLog();
380   ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE)) << GetAndClearDebugLog();
381 }
382
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();
388
389   output << "\n\nInitial transitions:";
390   output << GetStateTransitionsAsString();
391
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);
405       }
406     }
407   }
408
409   output << "\n\nAll transitions:";
410   output << GetStateTransitionsAsString();
411   LOG(INFO) << output.str();
412 }
413
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());
428 }
429
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());
437
438   browser()->tab_strip_model()->SelectNextTab();
439   ChangeWindowFullscreenState();
440   EXPECT_FALSE(browser()->window()->IsFullscreen());
441 }
442
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());
452
453   scoped_ptr<content::WebContents> web_contents(
454       browser()->tab_strip_model()->DetachWebContentsAt(0));
455   ChangeWindowFullscreenState();
456   EXPECT_FALSE(browser()->window()->IsFullscreen());
457 }
458
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());
467
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());
475 }