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 "chrome/browser/ui/fullscreen/fullscreen_controller_state_test.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_tabstrip.h"
14 #include "chrome/browser/ui/browser_window.h"
15 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
16 #include "content/public/common/url_constants.h"
17 #include "testing/gtest/include/gtest/gtest.h"
19 FullscreenControllerStateTest::FullscreenControllerStateTest()
20 : state_(STATE_NORMAL
),
22 // Human specified state machine data.
23 // For each state, for each event, define the resulting state.
24 State transition_table_data
[][NUM_EVENTS
] = {
26 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME
, // Event TOGGLE_FULLSCREEN
27 STATE_TO_TAB_FULLSCREEN
, // Event TAB_FULLSCREEN_TRUE
28 STATE_NORMAL
, // Event TAB_FULLSCREEN_FALSE
30 STATE_METRO_SNAP
, // Event METRO_SNAP_TRUE
31 STATE_NORMAL
, // Event METRO_SNAP_FALSE
33 STATE_NORMAL
, // Event WINDOW_CHANGE
35 { // STATE_BROWSER_FULLSCREEN_NO_CHROME:
36 STATE_TO_NORMAL
, // Event TOGGLE_FULLSCREEN
37 STATE_TAB_BROWSER_FULLSCREEN
, // Event TAB_FULLSCREEN_TRUE
38 STATE_BROWSER_FULLSCREEN_NO_CHROME
, // Event TAB_FULLSCREEN_FALSE
40 STATE_METRO_SNAP
, // Event METRO_SNAP_TRUE
41 STATE_BROWSER_FULLSCREEN_NO_CHROME
, // Event METRO_SNAP_FALSE
43 STATE_BROWSER_FULLSCREEN_NO_CHROME
, // Event WINDOW_CHANGE
46 { // STATE_METRO_SNAP:
47 STATE_METRO_SNAP
, // Event TOGGLE_FULLSCREEN
48 STATE_METRO_SNAP
, // Event TAB_FULLSCREEN_TRUE
49 STATE_METRO_SNAP
, // Event TAB_FULLSCREEN_FALSE
50 STATE_METRO_SNAP
, // Event METRO_SNAP_TRUE
51 STATE_NORMAL
, // Event METRO_SNAP_FALSE
52 STATE_METRO_SNAP
, // Event WINDOW_CHANGE
55 { // STATE_TAB_FULLSCREEN:
56 STATE_TO_NORMAL
, // Event TOGGLE_FULLSCREEN
57 STATE_TAB_FULLSCREEN
, // Event TAB_FULLSCREEN_TRUE
58 STATE_TO_NORMAL
, // Event TAB_FULLSCREEN_FALSE
60 STATE_METRO_SNAP
, // Event METRO_SNAP_TRUE
61 STATE_TAB_FULLSCREEN
, // Event METRO_SNAP_FALSE
63 STATE_TAB_FULLSCREEN
, // Event WINDOW_CHANGE
65 { // STATE_TAB_BROWSER_FULLSCREEN:
66 STATE_TO_NORMAL
, // Event TOGGLE_FULLSCREEN
67 STATE_TAB_BROWSER_FULLSCREEN
, // Event TAB_FULLSCREEN_TRUE
68 #if defined(OS_MACOSX)
69 // TODO(scheib) Mac exits browser mode too http://crbug.com/155642
70 STATE_TO_NORMAL
, // Event TAB_FULLSCREEN_FALSE
72 STATE_BROWSER_FULLSCREEN_NO_CHROME
, // Event TAB_FULLSCREEN_FALSE
75 STATE_METRO_SNAP
, // Event METRO_SNAP_TRUE
76 STATE_TAB_BROWSER_FULLSCREEN
, // Event METRO_SNAP_FALSE
78 STATE_TAB_BROWSER_FULLSCREEN
, // Event WINDOW_CHANGE
81 { STATE_TO_NORMAL
, // Event TOGGLE_FULLSCREEN
82 // TODO(scheib) Should be a route back to TAB. http://crbug.com/154196
83 STATE_TO_NORMAL
, // Event TAB_FULLSCREEN_TRUE
84 STATE_TO_NORMAL
, // Event TAB_FULLSCREEN_FALSE
86 STATE_METRO_SNAP
, // Event METRO_SNAP_TRUE
87 STATE_TO_NORMAL
, // Event METRO_SNAP_FALSE
89 STATE_NORMAL
, // Event WINDOW_CHANGE
91 // STATE_TO_BROWSER_FULLSCREEN_NO_CHROME:
92 { STATE_TO_BROWSER_FULLSCREEN_NO_CHROME
, // Event TOGGLE_FULLSCREEN
93 // TODO(scheib) Should be a route to TAB_BROWSER http://crbug.com/154196
94 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME
, // Event TAB_FULLSCREEN_TRUE
95 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME
, // Event TAB_FULLSCREEN_FALSE
97 STATE_METRO_SNAP
, // Event METRO_SNAP_TRUE
98 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME
, // Event METRO_SNAP_FALSE
100 STATE_BROWSER_FULLSCREEN_NO_CHROME
, // Event WINDOW_CHANGE
102 // STATE_TO_TAB_FULLSCREEN:
103 { // TODO(scheib) Should be a route to TAB_BROWSER http://crbug.com/154196
104 STATE_TO_TAB_FULLSCREEN
, // Event TOGGLE_FULLSCREEN
105 STATE_TO_TAB_FULLSCREEN
, // Event TAB_FULLSCREEN_TRUE
106 // TODO(scheib) Should be a route back to NORMAL. http://crbug.com/154196
107 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME
, // Event TAB_FULLSCREEN_FALSE
109 STATE_METRO_SNAP
, // Event METRO_SNAP_TRUE
110 STATE_TO_TAB_FULLSCREEN
, // Event METRO_SNAP_FALSE
112 STATE_TAB_FULLSCREEN
, // Event WINDOW_CHANGE
115 CHECK_EQ(sizeof(transition_table_data
), sizeof(transition_table_
));
116 memcpy(transition_table_
, transition_table_data
,
117 sizeof(transition_table_data
));
119 // Verify that transition_table_ has been completely defined.
120 for (int source
= 0; source
< NUM_STATES
; source
++) {
121 for (int event
= 0; event
< NUM_EVENTS
; event
++) {
122 CHECK_NE(STATE_INVALID
, transition_table_
[source
][event
]);
123 CHECK_LE(0, transition_table_
[source
][event
]);
124 CHECK_GT(NUM_STATES
, transition_table_
[source
][event
]);
128 // Copy transition_table_ data into state_transitions_ table.
129 for (int source
= 0; source
< NUM_STATES
; source
++) {
130 for (int event
= 0; event
< NUM_EVENTS
; event
++) {
131 State destination
= transition_table_
[source
][event
];
132 state_transitions_
[source
][destination
].event
= static_cast<Event
>(event
);
133 state_transitions_
[source
][destination
].state
= destination
;
134 state_transitions_
[source
][destination
].distance
= 1;
139 FullscreenControllerStateTest::~FullscreenControllerStateTest() {
143 const char* FullscreenControllerStateTest::GetStateString(State state
) {
146 return "STATE_NORMAL";
147 case STATE_BROWSER_FULLSCREEN_NO_CHROME
:
148 return "STATE_BROWSER_FULLSCREEN_NO_CHROME";
150 case STATE_METRO_SNAP
:
151 return "STATE_METRO_SNAP";
153 case STATE_TAB_FULLSCREEN
:
154 return "STATE_TAB_FULLSCREEN";
155 case STATE_TAB_BROWSER_FULLSCREEN
:
156 return "STATE_TAB_BROWSER_FULLSCREEN";
157 case STATE_TO_NORMAL
:
158 return "STATE_TO_NORMAL";
159 case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME
:
160 return "STATE_TO_BROWSER_FULLSCREEN_NO_CHROME";
161 case STATE_TO_TAB_FULLSCREEN
:
162 return "STATE_TO_TAB_FULLSCREEN";
164 return "STATE_INVALID";
166 NOTREACHED() << "No string for state " << state
;
167 return "State-Unknown";
172 const char* FullscreenControllerStateTest::GetEventString(Event event
) {
174 case TOGGLE_FULLSCREEN
:
175 return "TOGGLE_FULLSCREEN";
176 case TAB_FULLSCREEN_TRUE
:
177 return "TAB_FULLSCREEN_TRUE";
178 case TAB_FULLSCREEN_FALSE
:
179 return "TAB_FULLSCREEN_FALSE";
181 case METRO_SNAP_TRUE
:
182 return "METRO_SNAP_TRUE";
183 case METRO_SNAP_FALSE
:
184 return "METRO_SNAP_FALSE";
187 return "WINDOW_CHANGE";
189 return "EVENT_INVALID";
191 NOTREACHED() << "No string for event " << event
;
192 return "Event-Unknown";
196 void FullscreenControllerStateTest::TransitionToState(State final_state
) {
197 int max_steps
= NUM_STATES
;
198 while (max_steps
-- && TransitionAStepTowardState(final_state
))
200 ASSERT_GE(max_steps
, 0) << "TransitionToState was unable to achieve desired "
201 << "target state. TransitionAStepTowardState iterated too many times."
202 << GetAndClearDebugLog();
203 ASSERT_EQ(final_state
, state_
) << "TransitionToState was unable to achieve "
204 << "desired target state. TransitionAStepTowardState returned false."
205 << GetAndClearDebugLog();
208 bool FullscreenControllerStateTest::TransitionAStepTowardState(
209 State destination_state
) {
210 State source_state
= state_
;
211 if (source_state
== destination_state
)
214 StateTransitionInfo next
= NextTransitionInShortestPath(source_state
,
217 if (next
.state
== STATE_INVALID
) {
218 NOTREACHED() << "TransitionAStepTowardState unable to transition. "
219 << "NextTransitionInShortestPath("
220 << GetStateString(source_state
) << ", "
221 << GetStateString(destination_state
) << ") returned STATE_INVALID."
222 << GetAndClearDebugLog();
226 return InvokeEvent(next
.event
);
229 const char* FullscreenControllerStateTest::GetWindowStateString() {
233 bool FullscreenControllerStateTest::InvokeEvent(Event event
) {
234 State source_state
= state_
;
235 State next_state
= transition_table_
[source_state
][event
];
237 // When simulating reentrant window change calls, expect the next state
240 next_state
= transition_table_
[next_state
][WINDOW_CHANGE
];
242 debugging_log_
<< " InvokeEvent(" << std::left
243 << std::setw(MAX_EVENT_NAME_LENGTH
) << GetEventString(event
)
245 << std::setw(MAX_STATE_NAME_LENGTH
) << GetStateString(next_state
);
250 case TOGGLE_FULLSCREEN
:
251 GetFullscreenController()->ToggleFullscreenMode();
253 case TAB_FULLSCREEN_TRUE
:
254 GetFullscreenController()->ToggleFullscreenModeForTab(
255 chrome::GetActiveWebContents(GetBrowser()), true);
257 case TAB_FULLSCREEN_FALSE
:
258 GetFullscreenController()->ToggleFullscreenModeForTab(
259 chrome::GetActiveWebContents(GetBrowser()), false);
262 case METRO_SNAP_TRUE
:
263 GetFullscreenController()->SetMetroSnapMode(true);
265 case METRO_SNAP_FALSE
:
266 GetFullscreenController()->SetMetroSnapMode(false);
270 ChangeWindowFullscreenState();
273 NOTREACHED() << "InvokeEvent needs a handler for event "
274 << GetEventString(event
) << GetAndClearDebugLog();
278 if (GetWindowStateString())
279 debugging_log_
<< " Window state now " << GetWindowStateString() << "\n";
281 debugging_log_
<< "\n";
288 void FullscreenControllerStateTest::VerifyWindowState() {
291 #if defined(OS_MACOSX)
292 EXPECT_FALSE(GetBrowser()->window()->InPresentationMode())
293 << GetAndClearDebugLog();
295 EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser())
296 << GetAndClearDebugLog();
297 EXPECT_FALSE(GetFullscreenController()->IsFullscreenForTabOrPending())
298 << GetAndClearDebugLog();
299 EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
300 << GetAndClearDebugLog();
302 case STATE_BROWSER_FULLSCREEN_NO_CHROME
:
303 #if defined(OS_MACOSX)
304 EXPECT_FALSE(GetBrowser()->window()->InPresentationMode())
305 << GetAndClearDebugLog();
307 EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser())
308 << GetAndClearDebugLog();
309 EXPECT_FALSE(GetFullscreenController()->IsFullscreenForTabOrPending())
310 << GetAndClearDebugLog();
311 EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
312 << GetAndClearDebugLog();
315 case STATE_METRO_SNAP
:
316 // No expectation for InPresentationMode.
318 // TODO(scheib) IsFullscreenForBrowser and IsFullscreenForTabOrPending
319 // are returning true and false in interactive tests with real window.
320 // With only a single Metro Snap state in this test framework it isn't
321 // fair to try to have an expectation anyway.
323 // No expectation for IsFullscreenForBrowser.
324 // No expectation for IsFullscreenForTabOrPending.
325 EXPECT_TRUE(GetFullscreenController()->IsInMetroSnapMode())
326 << GetAndClearDebugLog();
329 case STATE_TAB_FULLSCREEN
:
330 #if defined(OS_MACOSX)
331 EXPECT_TRUE(GetBrowser()->window()->InPresentationMode())
332 << GetAndClearDebugLog();
334 EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser())
335 << GetAndClearDebugLog();
336 EXPECT_TRUE(GetFullscreenController()->IsFullscreenForTabOrPending())
337 << GetAndClearDebugLog();
338 EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
339 << GetAndClearDebugLog();
341 case STATE_TAB_BROWSER_FULLSCREEN
:
342 #if defined(OS_MACOSX)
343 EXPECT_TRUE(GetBrowser()->window()->InPresentationMode())
344 << GetAndClearDebugLog();
346 #if defined(OS_MACOSX)
347 // TODO(scheib) Mac is reporting incorrect IsFullscreenForBrowser().
348 // e.g. in FullscreenControllerStateTest.
349 // STATE_BROWSER_FULLSCREEN_NO_CHROME__TAB_FULLSCREEN_TRUE
350 // At the end of ToggleFullscreenModeForTab
351 // tab_caused_fullscreen_ has incorrectly been set to true even
352 // though controller was already in browser fullscreen.
353 // http://crbug.com/155650
354 EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser())
355 << GetAndClearDebugLog();
357 EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser())
358 << GetAndClearDebugLog();
360 EXPECT_TRUE(GetFullscreenController()->IsFullscreenForTabOrPending())
361 << GetAndClearDebugLog();
362 EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
363 << GetAndClearDebugLog();
365 case STATE_TO_NORMAL
:
366 #if defined(OS_MACOSX)
367 EXPECT_FALSE(GetBrowser()->window()->InPresentationMode())
368 << GetAndClearDebugLog();
370 // No expectation for IsFullscreenForBrowser.
371 // No expectation for IsFullscreenForTabOrPending.
372 EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
373 << GetAndClearDebugLog();
375 case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME
:
376 #if defined(OS_MACOSX)
377 EXPECT_FALSE(GetBrowser()->window()->InPresentationMode())
378 << GetAndClearDebugLog();
379 EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser())
380 << GetAndClearDebugLog();
382 EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser())
383 << GetAndClearDebugLog();
385 // No expectation for IsFullscreenForTabOrPending.
386 EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
387 << GetAndClearDebugLog();
389 case STATE_TO_TAB_FULLSCREEN
:
390 #if defined(OS_MACOSX)
391 // TODO(scheib) InPresentationMode returns false when invoking events:
392 // TAB_FULLSCREEN_TRUE, TOGGLE_FULLSCREEN. http://crbug.com/156645
393 // It may be that a new testing state TO_TAB_BROWSER_FULLSCREEN
394 // would help work around this http://crbug.com/154196
395 // Test with: STATE_TO_TAB_FULLSCREEN__TOGGLE_FULLSCREEN
397 // EXPECT_TRUE(GetBrowser()->window()->InPresentationMode())
398 // << GetAndClearDebugLog();
400 EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser())
401 << GetAndClearDebugLog();
402 EXPECT_TRUE(GetFullscreenController()->IsFullscreenForTabOrPending())
403 << GetAndClearDebugLog();
404 EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
405 << GetAndClearDebugLog();
408 NOTREACHED() << GetAndClearDebugLog();
412 void FullscreenControllerStateTest::TestTransitionsForEachState() {
413 for (int reentrant
= 0; reentrant
<= 1; reentrant
++) {
414 for (int source_int
= 0; source_int
< NUM_STATES
; source_int
++) {
415 for (int event1_int
= 0; event1_int
< NUM_EVENTS
; event1_int
++) {
416 State state
= static_cast<State
>(source_int
);
417 Event event1
= static_cast<Event
>(event1_int
);
419 // Early out if skipping all tests for this state, reduces log noise.
420 if (ShouldSkipTest(state
, event1
, !!reentrant
))
423 for (int event2_int
= 0; event2_int
< NUM_EVENTS
; event2_int
++) {
424 for (int event3_int
= 0; event3_int
< NUM_EVENTS
; event3_int
++) {
425 Event event2
= static_cast<Event
>(event2_int
);
426 Event event3
= static_cast<Event
>(event3_int
);
428 // Test each state and each event.
429 ASSERT_NO_FATAL_FAILURE(TestStateAndEvent(state
,
432 << GetAndClearDebugLog();
434 // Then, add an additional event to the sequence.
435 if (ShouldSkipStateAndEventPair(state_
, event2
))
437 ASSERT_TRUE(InvokeEvent(event2
)) << GetAndClearDebugLog();
439 // Then, add an additional event to the sequence.
440 if (ShouldSkipStateAndEventPair(state_
, event3
))
442 ASSERT_TRUE(InvokeEvent(event3
)) << GetAndClearDebugLog();
450 FullscreenControllerStateTest::StateTransitionInfo
451 FullscreenControllerStateTest::NextTransitionInShortestPath(
452 State source
, State destination
, int search_limit
) {
453 if (search_limit
== 0)
454 return StateTransitionInfo(); // Return a default (invalid) state.
456 if (state_transitions_
[source
][destination
].state
== STATE_INVALID
) {
457 // Don't know the next state yet, do a depth first search.
458 StateTransitionInfo result
;
460 // Consider all states reachable via each event from the source state.
461 for (int event_int
= 0; event_int
< NUM_EVENTS
; event_int
++) {
462 Event event
= static_cast<Event
>(event_int
);
463 State next_state_candidate
= transition_table_
[source
][event
];
465 if (ShouldSkipStateAndEventPair(source
, event
))
469 StateTransitionInfo candidate
= NextTransitionInShortestPath(
470 next_state_candidate
, destination
, search_limit
- 1);
472 if (candidate
.distance
+ 1 < result
.distance
) {
473 result
.event
= event
;
474 result
.state
= next_state_candidate
;
475 result
.distance
= candidate
.distance
+ 1;
479 // Cache result so that a search is not required next time.
480 state_transitions_
[source
][destination
] = result
;
483 return state_transitions_
[source
][destination
];
486 std::string
FullscreenControllerStateTest::GetAndClearDebugLog() {
487 debugging_log_
<< "(End of Debugging Log)\n";
488 std::string output_log
= "\nDebugging Log:\n" + debugging_log_
.str();
489 debugging_log_
.str("");
493 bool FullscreenControllerStateTest::ShouldSkipStateAndEventPair(State state
,
495 // TODO(scheib) Toggling Tab fullscreen while pending Tab or
496 // Browser fullscreen is broken currently http://crbug.com/154196
497 if ((state
== STATE_TO_BROWSER_FULLSCREEN_NO_CHROME
||
498 state
== STATE_TO_TAB_FULLSCREEN
) &&
499 (event
== TAB_FULLSCREEN_TRUE
|| event
== TAB_FULLSCREEN_FALSE
))
501 if (state
== STATE_TO_NORMAL
&& event
== TAB_FULLSCREEN_TRUE
)
507 bool FullscreenControllerStateTest::ShouldSkipTest(State state
,
511 // FullscreenController verifies that WindowFullscreenStateChanged is
512 // always reentrant on Windows. It will fail if we mock asynchronous calls.
514 debugging_log_
<< "\nSkipping non-reentrant test on Windows.\n";
519 debugging_log_
<< "\nSkipping reentrant test on non-Windows.\n";
524 // When testing reentrancy there are states the fullscreen controller
525 // will be unable to remain in, as they will progress due to the
526 // reentrant window change call. Skip states that will be instantly
527 // exited by the reentrant call.
528 if (reentrant
&& (transition_table_
[state
][WINDOW_CHANGE
] != state
)) {
529 debugging_log_
<< "\nSkipping reentrant test for transitory source state "
530 << GetStateString(state
) << ".\n";
534 if (ShouldSkipStateAndEventPair(state
, event
)) {
535 debugging_log_
<< "\nSkipping test due to ShouldSkipStateAndEventPair("
536 << GetStateString(state
) << ", "
537 << GetEventString(event
) << ").\n";
538 LOG(INFO
) << "Skipping test due to ShouldSkipStateAndEventPair("
539 << GetStateString(state
) << ", "
540 << GetEventString(event
) << ").";
547 void FullscreenControllerStateTest::TestStateAndEvent(State state
,
550 if (ShouldSkipTest(state
, event
, reentrant
))
553 debugging_log_
<< "\nTest transition from state "
554 << GetStateString(state
)
555 << (reentrant
? " with reentrant calls.\n" : ".\n");
556 reentrant_
= reentrant
;
558 debugging_log_
<< " First, from "
559 << GetStateString(state_
) << "\n";
560 ASSERT_NO_FATAL_FAILURE(TransitionToState(state
))
561 << GetAndClearDebugLog();
563 debugging_log_
<< " Then,\n";
564 ASSERT_TRUE(InvokeEvent(event
)) << GetAndClearDebugLog();
567 FullscreenController
* FullscreenControllerStateTest::GetFullscreenController() {
568 return GetBrowser()->fullscreen_controller();