- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / fullscreen / fullscreen_controller_state_test.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 "chrome/browser/ui/fullscreen/fullscreen_controller_state_test.h"
6
7 #include <memory.h>
8
9 #include <iomanip>
10 #include <iostream>
11
12 #include "chrome/browser/fullscreen.h"
13 #include "chrome/browser/ui/browser.h"
14 #include "chrome/browser/ui/browser_window.h"
15 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
16 #include "chrome/browser/ui/fullscreen/fullscreen_controller_test.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "content/public/common/url_constants.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 #if defined(OS_MACOSX)
22 #include "base/mac/mac_util.h"
23 #endif
24
25 namespace {
26
27 bool SupportsMacSystemFullscreen() {
28 #if defined(OS_MACOSX)
29   return chrome::mac::SupportsSystemFullscreen();
30 #else
31   return false;
32 #endif
33 }
34
35 }  // namespace
36
37 FullscreenControllerStateTest::FullscreenControllerStateTest()
38     : state_(STATE_NORMAL),
39       last_notification_received_state_(STATE_NORMAL) {
40   // Human specified state machine data.
41   // For each state, for each event, define the resulting state.
42   State transition_table_data[][NUM_EVENTS] = {
43     { // STATE_NORMAL:
44       STATE_TO_BROWSER_FULLSCREEN_NO_CHROME,  // Event TOGGLE_FULLSCREEN
45       STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event TOGGLE_FULLSCREEN_CHROME
46       STATE_TO_TAB_FULLSCREEN,                // Event TAB_FULLSCREEN_TRUE
47       STATE_NORMAL,                           // Event TAB_FULLSCREEN_FALSE
48       STATE_METRO_SNAP,                       // Event METRO_SNAP_TRUE
49       STATE_NORMAL,                           // Event METRO_SNAP_FALSE
50       STATE_NORMAL,                           // Event BUBBLE_EXIT_LINK
51       STATE_NORMAL,                           // Event BUBBLE_ALLOW
52       STATE_NORMAL,                           // Event BUBBLE_DENY
53       STATE_NORMAL,                           // Event WINDOW_CHANGE
54     },
55     { // STATE_BROWSER_FULLSCREEN_NO_CHROME:
56       STATE_TO_NORMAL,                        // Event TOGGLE_FULLSCREEN
57       STATE_BROWSER_FULLSCREEN_WITH_CHROME,   // Event TOGGLE_FULLSCREEN_CHROME
58       STATE_TAB_BROWSER_FULLSCREEN,           // Event TAB_FULLSCREEN_TRUE
59       STATE_BROWSER_FULLSCREEN_NO_CHROME,     // Event TAB_FULLSCREEN_FALSE
60       STATE_METRO_SNAP,                       // Event METRO_SNAP_TRUE
61       STATE_BROWSER_FULLSCREEN_NO_CHROME,     // Event METRO_SNAP_FALSE
62       STATE_TO_NORMAL,                        // Event BUBBLE_EXIT_LINK
63       STATE_BROWSER_FULLSCREEN_NO_CHROME,     // Event BUBBLE_ALLOW
64       STATE_BROWSER_FULLSCREEN_NO_CHROME,     // Event BUBBLE_DENY
65       STATE_BROWSER_FULLSCREEN_NO_CHROME,     // Event WINDOW_CHANGE
66     },
67     { // STATE_BROWSER_FULLSCREEN_WITH_CHROME:
68       STATE_BROWSER_FULLSCREEN_NO_CHROME,     // Event TOGGLE_FULLSCREEN
69       STATE_TO_NORMAL,                        // Event TOGGLE_FULLSCREEN_CHROME
70       STATE_TAB_BROWSER_FULLSCREEN_CHROME,    // Event TAB_FULLSCREEN_TRUE
71       STATE_BROWSER_FULLSCREEN_WITH_CHROME,   // Event TAB_FULLSCREEN_FALSE
72       STATE_BROWSER_FULLSCREEN_WITH_CHROME,   // Event METRO_SNAP_TRUE
73       STATE_BROWSER_FULLSCREEN_WITH_CHROME,   // Event METRO_SNAP_FALSE
74       STATE_TO_NORMAL,                        // Event BUBBLE_EXIT_LINK
75       STATE_BROWSER_FULLSCREEN_WITH_CHROME,   // Event BUBBLE_ALLOW
76       STATE_BROWSER_FULLSCREEN_WITH_CHROME,   // Event BUBBLE_DENY
77       STATE_BROWSER_FULLSCREEN_WITH_CHROME,   // Event WINDOW_CHANGE
78     },
79     { // STATE_METRO_SNAP:
80       STATE_METRO_SNAP,                       // Event TOGGLE_FULLSCREEN
81       STATE_METRO_SNAP,                       // Event TOGGLE_FULLSCREEN_CHROME
82       STATE_METRO_SNAP,                       // Event TAB_FULLSCREEN_TRUE
83       STATE_METRO_SNAP,                       // Event TAB_FULLSCREEN_FALSE
84       STATE_METRO_SNAP,                       // Event METRO_SNAP_TRUE
85       STATE_NORMAL,                           // Event METRO_SNAP_FALSE
86       STATE_METRO_SNAP,                       // Event BUBBLE_EXIT_LINK
87       STATE_METRO_SNAP,                       // Event BUBBLE_ALLOW
88       STATE_METRO_SNAP,                       // Event BUBBLE_DENY
89       STATE_METRO_SNAP,                       // Event WINDOW_CHANGE
90     },
91     { // STATE_TAB_FULLSCREEN:
92       STATE_TO_NORMAL,                        // Event TOGGLE_FULLSCREEN
93       STATE_TO_NORMAL,                        // Event TOGGLE_FULLSCREEN_CHROME
94       STATE_TAB_FULLSCREEN,                   // Event TAB_FULLSCREEN_TRUE
95       STATE_TO_NORMAL,                        // Event TAB_FULLSCREEN_FALSE
96       STATE_METRO_SNAP,                       // Event METRO_SNAP_TRUE
97       STATE_TAB_FULLSCREEN,                   // Event METRO_SNAP_FALSE
98       STATE_TO_NORMAL,                        // Event BUBBLE_EXIT_LINK
99       STATE_TAB_FULLSCREEN,                   // Event BUBBLE_ALLOW
100       STATE_TO_NORMAL,                        // Event BUBBLE_DENY
101       STATE_TAB_FULLSCREEN,                   // Event WINDOW_CHANGE
102     },
103     { // STATE_TAB_BROWSER_FULLSCREEN:
104       STATE_TO_NORMAL,                        // Event TOGGLE_FULLSCREEN
105       STATE_TO_NORMAL,                        // Event TOGGLE_FULLSCREEN_CHROME
106       STATE_TAB_BROWSER_FULLSCREEN,           // Event TAB_FULLSCREEN_TRUE
107       STATE_BROWSER_FULLSCREEN_NO_CHROME,     // Event TAB_FULLSCREEN_FALSE
108       STATE_METRO_SNAP,                       // Event METRO_SNAP_TRUE
109       STATE_TAB_BROWSER_FULLSCREEN,           // Event METRO_SNAP_FALSE
110       STATE_BROWSER_FULLSCREEN_NO_CHROME,     // Event BUBBLE_EXIT_LINK
111       STATE_TAB_BROWSER_FULLSCREEN,           // Event BUBBLE_ALLOW
112       STATE_BROWSER_FULLSCREEN_NO_CHROME,     // Event BUBBLE_DENY
113       STATE_TAB_BROWSER_FULLSCREEN,           // Event WINDOW_CHANGE
114     },
115     { // STATE_TAB_BROWSER_FULLSCREEN_CHROME:
116       STATE_TO_NORMAL,                        // Event TOGGLE_FULLSCREEN
117       STATE_TO_NORMAL,                        // Event TOGGLE_FULLSCREEN_CHROME
118       STATE_TAB_BROWSER_FULLSCREEN_CHROME,    // Event TAB_FULLSCREEN_TRUE
119       STATE_BROWSER_FULLSCREEN_WITH_CHROME,   // Event TAB_FULLSCREEN_FALSE
120       STATE_METRO_SNAP,                       // Event METRO_SNAP_TRUE
121       STATE_TAB_BROWSER_FULLSCREEN_CHROME,    // Event METRO_SNAP_FALSE
122       STATE_BROWSER_FULLSCREEN_WITH_CHROME,   // Event BUBBLE_EXIT_LINK
123       STATE_TAB_BROWSER_FULLSCREEN_CHROME,    // Event BUBBLE_ALLOW
124       STATE_BROWSER_FULLSCREEN_WITH_CHROME,   // Event BUBBLE_DENY
125       STATE_TAB_BROWSER_FULLSCREEN_CHROME,    // Event WINDOW_CHANGE
126     },
127     { // STATE_TO_NORMAL:
128       STATE_TO_NORMAL,                        // Event TOGGLE_FULLSCREEN
129       STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event TOGGLE_FULLSCREEN_CHROME
130       // TODO(scheib) Should be a route back to TAB. http://crbug.com/154196
131       STATE_TO_NORMAL,                        // Event TAB_FULLSCREEN_TRUE
132       STATE_TO_NORMAL,                        // Event TAB_FULLSCREEN_FALSE
133       STATE_METRO_SNAP,                       // Event METRO_SNAP_TRUE
134       STATE_TO_NORMAL,                        // Event METRO_SNAP_FALSE
135       STATE_TO_NORMAL,                        // Event BUBBLE_EXIT_LINK
136       STATE_TO_NORMAL,                        // Event BUBBLE_ALLOW
137       STATE_TO_NORMAL,                        // Event BUBBLE_DENY
138       STATE_NORMAL,                           // Event WINDOW_CHANGE
139     },
140     { // STATE_TO_BROWSER_FULLSCREEN_NO_CHROME:
141       STATE_TO_BROWSER_FULLSCREEN_NO_CHROME,  // Event TOGGLE_FULLSCREEN
142       STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event TOGGLE_FULLSCREEN_CHROME
143       // TODO(scheib) Should be a route to TAB_BROWSER http://crbug.com/154196
144       STATE_TO_BROWSER_FULLSCREEN_NO_CHROME,  // Event TAB_FULLSCREEN_TRUE
145       STATE_TO_BROWSER_FULLSCREEN_NO_CHROME,  // Event TAB_FULLSCREEN_FALSE
146       STATE_METRO_SNAP,                       // Event METRO_SNAP_TRUE
147       STATE_TO_BROWSER_FULLSCREEN_NO_CHROME,  // Event METRO_SNAP_FALSE
148 #if defined(OS_MACOSX)
149       // Mac window reports fullscreen immediately and an exit triggers exit.
150       STATE_TO_NORMAL,                        // Event BUBBLE_EXIT_LINK
151 #else
152       STATE_TO_BROWSER_FULLSCREEN_NO_CHROME,  // Event BUBBLE_EXIT_LINK
153 #endif
154       STATE_TO_BROWSER_FULLSCREEN_NO_CHROME,  // Event BUBBLE_ALLOW
155       STATE_TO_BROWSER_FULLSCREEN_NO_CHROME,  // Event BUBBLE_DENY
156       STATE_BROWSER_FULLSCREEN_NO_CHROME,     // Event WINDOW_CHANGE
157     },
158     { // STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME:
159       STATE_TO_BROWSER_FULLSCREEN_NO_CHROME,  // Event TOGGLE_FULLSCREEN
160       STATE_TO_NORMAL,                        // Event TOGGLE_FULLSCREEN_CHROME
161       // TODO(scheib) Should be a route to TAB_BROWSER http://crbug.com/154196
162       STATE_TAB_BROWSER_FULLSCREEN,           // Event TAB_FULLSCREEN_TRUE
163       STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event TAB_FULLSCREEN_FALSE
164       STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event METRO_SNAP_TRUE
165       STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event METRO_SNAP_FALSE
166       STATE_TO_NORMAL,                        // Event BUBBLE_EXIT_LINK
167       STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event BUBBLE_ALLOW
168       STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event BUBBLE_DENY
169       STATE_BROWSER_FULLSCREEN_WITH_CHROME,   // Event WINDOW_CHANGE
170     },
171     { // STATE_TO_TAB_FULLSCREEN:
172       // TODO(scheib) Should be a route to TAB_BROWSER http://crbug.com/154196
173       STATE_TO_TAB_FULLSCREEN,                // Event TOGGLE_FULLSCREEN
174       STATE_TO_NORMAL,                        // Event TOGGLE_FULLSCREEN_CHROME
175       STATE_TO_TAB_FULLSCREEN,                // Event TAB_FULLSCREEN_TRUE
176 #if defined(OS_MACOSX)
177       // Mac runs as expected due to a forced NotifyTabOfExitIfNecessary();
178       STATE_TO_NORMAL,                        // Event TAB_FULLSCREEN_FALSE
179 #else
180       // TODO(scheib) Should be a route back to NORMAL. http://crbug.com/154196
181       STATE_TO_BROWSER_FULLSCREEN_NO_CHROME,  // Event TAB_FULLSCREEN_FALSE
182 #endif
183       STATE_METRO_SNAP,                       // Event METRO_SNAP_TRUE
184       STATE_TO_TAB_FULLSCREEN,                // Event METRO_SNAP_FALSE
185 #if defined(OS_MACOSX)
186       // Mac window reports fullscreen immediately and an exit triggers exit.
187       STATE_TO_NORMAL,                        // Event BUBBLE_EXIT_LINK
188 #else
189       STATE_TO_TAB_FULLSCREEN,                // Event BUBBLE_EXIT_LINK
190 #endif
191       STATE_TO_TAB_FULLSCREEN,                // Event BUBBLE_ALLOW
192 #if defined(OS_MACOSX)
193       // Mac window reports fullscreen immediately and an exit triggers exit.
194       STATE_TO_NORMAL,                        // Event BUBBLE_DENY
195 #else
196       STATE_TO_TAB_FULLSCREEN,                // Event BUBBLE_DENY
197 #endif
198       STATE_TAB_FULLSCREEN,                   // Event WINDOW_CHANGE
199     },
200   };
201   COMPILE_ASSERT(sizeof(transition_table_data) == sizeof(transition_table_),
202                  transition_table_incorrect_size);
203   memcpy(transition_table_, transition_table_data,
204          sizeof(transition_table_data));
205
206   // Verify that transition_table_ has been completely defined.
207   for (int source = 0; source < NUM_STATES; ++source) {
208     for (int event = 0; event < NUM_EVENTS; ++event) {
209       EXPECT_NE(transition_table_[source][event], STATE_INVALID);
210       EXPECT_GE(transition_table_[source][event], 0);
211       EXPECT_LT(transition_table_[source][event], NUM_STATES);
212     }
213   }
214
215   // Copy transition_table_ data into state_transitions_ table.
216   for (int source = 0; source < NUM_STATES; ++source) {
217     for (int event = 0; event < NUM_EVENTS; ++event) {
218       if (ShouldSkipStateAndEventPair(static_cast<State>(source),
219                                       static_cast<Event>(event)))
220         continue;
221       State destination = transition_table_[source][event];
222       state_transitions_[source][destination].event = static_cast<Event>(event);
223       state_transitions_[source][destination].state = destination;
224       state_transitions_[source][destination].distance = 1;
225     }
226   }
227 }
228
229 FullscreenControllerStateTest::~FullscreenControllerStateTest() {
230 }
231
232 // static
233 const char* FullscreenControllerStateTest::GetStateString(State state) {
234   switch (state) {
235     ENUM_TO_STRING(STATE_NORMAL);
236     ENUM_TO_STRING(STATE_BROWSER_FULLSCREEN_NO_CHROME);
237     ENUM_TO_STRING(STATE_BROWSER_FULLSCREEN_WITH_CHROME);
238     ENUM_TO_STRING(STATE_METRO_SNAP);
239     ENUM_TO_STRING(STATE_TAB_FULLSCREEN);
240     ENUM_TO_STRING(STATE_TAB_BROWSER_FULLSCREEN);
241     ENUM_TO_STRING(STATE_TAB_BROWSER_FULLSCREEN_CHROME);
242     ENUM_TO_STRING(STATE_TO_NORMAL);
243     ENUM_TO_STRING(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME);
244     ENUM_TO_STRING(STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME);
245     ENUM_TO_STRING(STATE_TO_TAB_FULLSCREEN);
246     ENUM_TO_STRING(STATE_INVALID);
247     default:
248       NOTREACHED() << "No string for state " << state;
249       return "State-Unknown";
250   }
251 }
252
253 // static
254 const char* FullscreenControllerStateTest::GetEventString(Event event) {
255   switch (event) {
256     ENUM_TO_STRING(TOGGLE_FULLSCREEN);
257     ENUM_TO_STRING(TOGGLE_FULLSCREEN_CHROME);
258     ENUM_TO_STRING(TAB_FULLSCREEN_TRUE);
259     ENUM_TO_STRING(TAB_FULLSCREEN_FALSE);
260     ENUM_TO_STRING(METRO_SNAP_TRUE);
261     ENUM_TO_STRING(METRO_SNAP_FALSE);
262     ENUM_TO_STRING(BUBBLE_EXIT_LINK);
263     ENUM_TO_STRING(BUBBLE_ALLOW);
264     ENUM_TO_STRING(BUBBLE_DENY);
265     ENUM_TO_STRING(WINDOW_CHANGE);
266     ENUM_TO_STRING(EVENT_INVALID);
267     default:
268       NOTREACHED() << "No string for event " << event;
269       return "Event-Unknown";
270   }
271 }
272
273 // static
274 bool FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant() {
275 #if defined(TOOLKIT_VIEWS)
276   return true;
277 #else
278   return false;
279 #endif
280 }
281
282 // static
283 bool FullscreenControllerStateTest::IsPersistentState(State state) {
284   switch (state) {
285     case STATE_NORMAL:
286     case STATE_BROWSER_FULLSCREEN_NO_CHROME:
287     case STATE_BROWSER_FULLSCREEN_WITH_CHROME:
288     case STATE_METRO_SNAP:
289     case STATE_TAB_FULLSCREEN:
290     case STATE_TAB_BROWSER_FULLSCREEN:
291     case STATE_TAB_BROWSER_FULLSCREEN_CHROME:
292       return true;
293
294     case STATE_TO_NORMAL:
295     case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME:
296     case STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME:
297     case STATE_TO_TAB_FULLSCREEN:
298       return false;
299
300     default:
301       NOTREACHED();
302       return false;
303   }
304 }
305
306 void FullscreenControllerStateTest::TransitionToState(State final_state) {
307   int max_steps = NUM_STATES;
308   while (max_steps-- && TransitionAStepTowardState(final_state))
309     continue;
310   ASSERT_GE(max_steps, 0) << "TransitionToState was unable to achieve desired "
311       << "target state. TransitionAStepTowardState iterated too many times."
312       << GetAndClearDebugLog();
313   ASSERT_EQ(final_state, state_) << "TransitionToState was unable to achieve "
314       << "desired target state. TransitionAStepTowardState returned false."
315       << GetAndClearDebugLog();
316 }
317
318 bool FullscreenControllerStateTest::TransitionAStepTowardState(
319     State destination_state) {
320   State source_state = state_;
321   if (source_state == destination_state)
322     return false;
323
324   StateTransitionInfo next = NextTransitionInShortestPath(source_state,
325                                                           destination_state,
326                                                           NUM_STATES);
327   if (next.state == STATE_INVALID) {
328     NOTREACHED() << "TransitionAStepTowardState unable to transition. "
329         << "NextTransitionInShortestPath("
330         << GetStateString(source_state) << ", "
331         << GetStateString(destination_state) << ") returned STATE_INVALID."
332         << GetAndClearDebugLog();
333     return false;
334   }
335
336   return InvokeEvent(next.event);
337 }
338
339 const char* FullscreenControllerStateTest::GetWindowStateString() {
340   return NULL;
341 }
342
343 bool FullscreenControllerStateTest::InvokeEvent(Event event) {
344   if (!fullscreen_notification_observer_.get()) {
345     // Start observing NOTIFICATION_FULLSCREEN_CHANGED. Construct the
346     // notification observer here instead of in
347     // FullscreenControllerStateTest::FullscreenControllerStateTest() so that we
348     // listen to notifications on the proper thread.
349     fullscreen_notification_observer_.reset(
350         new FullscreenNotificationObserver());
351   }
352
353   State source_state = state_;
354   State next_state = transition_table_[source_state][event];
355
356   EXPECT_FALSE(ShouldSkipStateAndEventPair(source_state, event))
357       << GetAndClearDebugLog();
358
359   // When simulating reentrant window change calls, expect the next state
360   // automatically.
361   if (IsWindowFullscreenStateChangedReentrant())
362     next_state = transition_table_[next_state][WINDOW_CHANGE];
363
364   debugging_log_ << "  InvokeEvent(" << std::left
365       << std::setw(kMaxStateNameLength) << GetEventString(event)
366       << ") to "
367       << std::setw(kMaxStateNameLength) << GetStateString(next_state);
368
369   state_ = next_state;
370
371   switch (event) {
372     case TOGGLE_FULLSCREEN:
373       GetFullscreenController()->ToggleFullscreenMode();
374       break;
375
376     case TOGGLE_FULLSCREEN_CHROME:
377 #if defined(OS_MACOSX)
378       if (chrome::mac::SupportsSystemFullscreen()) {
379         GetFullscreenController()->ToggleFullscreenWithChrome();
380         break;
381       }
382 #endif
383       NOTREACHED() << GetAndClearDebugLog();
384       break;
385
386     case TAB_FULLSCREEN_TRUE:
387       GetFullscreenController()->ToggleFullscreenModeForTab(
388            GetBrowser()->tab_strip_model()->GetActiveWebContents(), true);
389       break;
390
391     case TAB_FULLSCREEN_FALSE:
392       GetFullscreenController()->ToggleFullscreenModeForTab(
393            GetBrowser()->tab_strip_model()->GetActiveWebContents(), false);
394       break;
395
396     case METRO_SNAP_TRUE:
397 #if defined(OS_WIN)
398       GetFullscreenController()->SetMetroSnapMode(true);
399 #else
400       NOTREACHED() << GetAndClearDebugLog();
401 #endif
402       break;
403
404     case METRO_SNAP_FALSE:
405 #if defined(OS_WIN)
406       GetFullscreenController()->SetMetroSnapMode(false);
407 #else
408       NOTREACHED() << GetAndClearDebugLog();
409 #endif
410       break;
411
412     case BUBBLE_EXIT_LINK:
413       GetFullscreenController()->ExitTabOrBrowserFullscreenToPreviousState();
414       break;
415
416     case BUBBLE_ALLOW:
417       GetFullscreenController()->OnAcceptFullscreenPermission();
418       break;
419
420     case BUBBLE_DENY:
421       GetFullscreenController()->OnDenyFullscreenPermission();
422       break;
423
424     case WINDOW_CHANGE:
425       ChangeWindowFullscreenState();
426       break;
427
428     default:
429       NOTREACHED() << "InvokeEvent needs a handler for event "
430           << GetEventString(event) << GetAndClearDebugLog();
431       return false;
432   }
433
434   if (GetWindowStateString())
435     debugging_log_ << " Window state now " << GetWindowStateString() << "\n";
436   else
437     debugging_log_ << "\n";
438
439   MaybeWaitForNotification();
440   VerifyWindowState();
441
442   return true;
443 }
444
445 void FullscreenControllerStateTest::VerifyWindowState() {
446   switch (state_) {
447     case STATE_NORMAL:
448       VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_FALSE,
449                                     FULLSCREEN_WITHOUT_CHROME_FALSE,
450                                     FULLSCREEN_FOR_BROWSER_FALSE,
451                                     FULLSCREEN_FOR_TAB_FALSE,
452                                     IN_METRO_SNAP_FALSE);
453       break;
454     case STATE_BROWSER_FULLSCREEN_NO_CHROME:
455       VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_FALSE,
456                                     FULLSCREEN_WITHOUT_CHROME_TRUE,
457                                     FULLSCREEN_FOR_BROWSER_TRUE,
458                                     FULLSCREEN_FOR_TAB_FALSE,
459                                     IN_METRO_SNAP_FALSE);
460       break;
461     case STATE_BROWSER_FULLSCREEN_WITH_CHROME:
462       VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_TRUE,
463                                     FULLSCREEN_WITHOUT_CHROME_FALSE,
464                                     FULLSCREEN_FOR_BROWSER_TRUE,
465                                     FULLSCREEN_FOR_TAB_FALSE,
466                                     IN_METRO_SNAP_FALSE);
467       break;
468     case STATE_METRO_SNAP:
469       VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_NO_EXPECTATION,
470                                     FULLSCREEN_WITHOUT_CHROME_NO_EXPECTATION,
471                                     FULLSCREEN_FOR_BROWSER_NO_EXPECTATION,
472                                     FULLSCREEN_FOR_TAB_NO_EXPECTATION,
473                                     IN_METRO_SNAP_TRUE);
474       break;
475     case STATE_TAB_FULLSCREEN:
476       VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_FALSE,
477                                     FULLSCREEN_WITHOUT_CHROME_TRUE,
478                                     FULLSCREEN_FOR_BROWSER_FALSE,
479                                     FULLSCREEN_FOR_TAB_TRUE,
480                                     IN_METRO_SNAP_FALSE);
481       break;
482     case STATE_TAB_BROWSER_FULLSCREEN:
483       VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_FALSE,
484                                     FULLSCREEN_WITHOUT_CHROME_TRUE,
485                                     FULLSCREEN_FOR_BROWSER_TRUE,
486                                     FULLSCREEN_FOR_TAB_TRUE,
487                                     IN_METRO_SNAP_FALSE);
488       break;
489     case STATE_TAB_BROWSER_FULLSCREEN_CHROME:
490       VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_FALSE,
491                                     FULLSCREEN_WITHOUT_CHROME_TRUE,
492                                     FULLSCREEN_FOR_BROWSER_TRUE,
493                                     FULLSCREEN_FOR_TAB_TRUE,
494                                     IN_METRO_SNAP_FALSE);
495       break;
496     case STATE_TO_NORMAL:
497       VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_FALSE,
498                                     FULLSCREEN_WITHOUT_CHROME_FALSE,
499                                     FULLSCREEN_FOR_BROWSER_NO_EXPECTATION,
500                                     FULLSCREEN_FOR_TAB_NO_EXPECTATION,
501                                     IN_METRO_SNAP_FALSE);
502       break;
503
504     case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME:
505       VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_FALSE,
506                                     FULLSCREEN_WITHOUT_CHROME_TRUE,
507 #if defined(OS_MACOSX)
508                                     FULLSCREEN_FOR_BROWSER_TRUE,
509 #else
510                                     FULLSCREEN_FOR_BROWSER_FALSE,
511 #endif
512                                     FULLSCREEN_FOR_TAB_NO_EXPECTATION,
513                                     IN_METRO_SNAP_FALSE);
514       break;
515
516     case STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME:
517       VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_TRUE,
518                                     FULLSCREEN_WITHOUT_CHROME_FALSE,
519 #if defined(OS_MACOSX)
520                                     FULLSCREEN_FOR_BROWSER_TRUE,
521 #else
522                                     FULLSCREEN_FOR_BROWSER_FALSE,
523 #endif
524                                     FULLSCREEN_FOR_TAB_NO_EXPECTATION,
525                                     IN_METRO_SNAP_FALSE);
526       break;
527
528     case STATE_TO_TAB_FULLSCREEN:
529 #if defined(OS_MACOSX)
530       // TODO(scheib) InPresentationMode returns false when invoking events:
531       // TAB_FULLSCREEN_TRUE, TOGGLE_FULLSCREEN. http://crbug.com/156645
532       // It may be that a new testing state TO_TAB_BROWSER_FULLSCREEN
533       // would help work around this http://crbug.com/154196
534       // Test with: STATE_TO_TAB_FULLSCREEN__TOGGLE_FULLSCREEN
535       //
536       // EXPECT_TRUE(GetBrowser()->window()->InPresentationMode())
537       //     << GetAndClearDebugLog();
538 #endif
539       VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_NO_EXPECTATION,
540                                     FULLSCREEN_WITHOUT_CHROME_NO_EXPECTATION,
541                                     FULLSCREEN_FOR_BROWSER_FALSE,
542                                     FULLSCREEN_FOR_TAB_TRUE,
543                                     IN_METRO_SNAP_FALSE);
544       break;
545
546     default:
547       NOTREACHED() << GetAndClearDebugLog();
548   }
549 }
550
551 void FullscreenControllerStateTest::MaybeWaitForNotification() {
552   // We should get a fullscreen notification each time we get to a new
553   // persistent state. If we don't get a notification, the test will
554   // fail by timing out.
555   if (state_ != last_notification_received_state_ &&
556       IsPersistentState(state_)) {
557     fullscreen_notification_observer_->Wait();
558     last_notification_received_state_ = state_;
559     fullscreen_notification_observer_.reset(
560         new FullscreenNotificationObserver());
561   }
562 }
563
564 void FullscreenControllerStateTest::TestTransitionsForEachState() {
565   for (int source_int = 0; source_int < NUM_STATES; ++source_int) {
566     for (int event1_int = 0; event1_int < NUM_EVENTS; ++event1_int) {
567       State state = static_cast<State>(source_int);
568       Event event1 = static_cast<Event>(event1_int);
569
570       // Early out if skipping all tests for this state, reduces log noise.
571       if (ShouldSkipTest(state, event1))
572         continue;
573
574       for (int event2_int = 0; event2_int < NUM_EVENTS; ++event2_int) {
575         for (int event3_int = 0; event3_int < NUM_EVENTS; ++event3_int) {
576           Event event2 = static_cast<Event>(event2_int);
577           Event event3 = static_cast<Event>(event3_int);
578
579           // Test each state and each event.
580           ASSERT_NO_FATAL_FAILURE(TestStateAndEvent(state, event1))
581               << GetAndClearDebugLog();
582
583           // Then, add an additional event to the sequence.
584           if (ShouldSkipStateAndEventPair(state_, event2))
585             continue;
586           ASSERT_TRUE(InvokeEvent(event2)) << GetAndClearDebugLog();
587
588           // Then, add an additional event to the sequence.
589           if (ShouldSkipStateAndEventPair(state_, event3))
590             continue;
591           ASSERT_TRUE(InvokeEvent(event3)) << GetAndClearDebugLog();
592         }
593       }
594     }
595   }
596 }
597
598 FullscreenControllerStateTest::StateTransitionInfo
599     FullscreenControllerStateTest::NextTransitionInShortestPath(
600     State source,
601     State destination,
602     int search_limit) {
603   if (search_limit <= 0)
604     return StateTransitionInfo();  // Return a default (invalid) state.
605
606   if (state_transitions_[source][destination].state == STATE_INVALID) {
607     // Don't know the next state yet, do a depth first search.
608     StateTransitionInfo result;
609
610     // Consider all states reachable via each event from the source state.
611     for (int event_int = 0; event_int < NUM_EVENTS; ++event_int) {
612       Event event = static_cast<Event>(event_int);
613       State next_state_candidate = transition_table_[source][event];
614
615       if (ShouldSkipStateAndEventPair(source, event))
616         continue;
617
618       // Recurse.
619       StateTransitionInfo candidate = NextTransitionInShortestPath(
620           next_state_candidate, destination, search_limit - 1);
621
622       if (candidate.distance + 1 < result.distance) {
623         result.event = event;
624         result.state = next_state_candidate;
625         result.distance = candidate.distance + 1;
626       }
627     }
628
629     // Cache result so that a search is not required next time.
630     state_transitions_[source][destination] = result;
631   }
632
633   return state_transitions_[source][destination];
634 }
635
636 std::string FullscreenControllerStateTest::GetAndClearDebugLog() {
637   debugging_log_ << "(End of Debugging Log)\n";
638   std::string output_log = "\nDebugging Log:\n" + debugging_log_.str();
639   debugging_log_.str(std::string());
640   return output_log;
641 }
642
643 bool FullscreenControllerStateTest::ShouldSkipStateAndEventPair(State state,
644                                                                 Event event) {
645   // TODO(scheib) Toggling Tab fullscreen while pending Tab or
646   // Browser fullscreen is broken currently http://crbug.com/154196
647   if ((state == STATE_TO_BROWSER_FULLSCREEN_NO_CHROME ||
648        state == STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME ||
649        state == STATE_TO_TAB_FULLSCREEN) &&
650       (event == TAB_FULLSCREEN_TRUE || event == TAB_FULLSCREEN_FALSE))
651     return true;
652   if (state == STATE_TO_NORMAL && event == TAB_FULLSCREEN_TRUE)
653     return true;
654
655   // Skip metro snap state and events when not on windows.
656 #if !defined(OS_WIN)
657   if (state == STATE_METRO_SNAP ||
658       event == METRO_SNAP_TRUE ||
659       event == METRO_SNAP_FALSE)
660     return true;
661 #endif
662
663   // Skip Mac Lion Fullscreen state and events when not on OSX 10.7+.
664   if (!SupportsMacSystemFullscreen()) {
665     if (state == STATE_BROWSER_FULLSCREEN_WITH_CHROME ||
666         state == STATE_TAB_BROWSER_FULLSCREEN_CHROME ||
667         state == STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME ||
668         event == TOGGLE_FULLSCREEN_CHROME) {
669       return true;
670     }
671   }
672
673   return false;
674 }
675
676 bool FullscreenControllerStateTest::ShouldSkipTest(State state, Event event) {
677   // Quietly skip metro snap tests when not on windows.
678 #if !defined(OS_WIN)
679   if (state == STATE_METRO_SNAP ||
680       event == METRO_SNAP_TRUE ||
681       event == METRO_SNAP_FALSE) {
682     debugging_log_ << "\nSkipping metro snap test on non-Windows.\n";
683     return true;
684   }
685 #endif
686
687   // Quietly skip Mac Lion Fullscreen tests when not on OSX 10.7+.
688   if (!SupportsMacSystemFullscreen()) {
689     if (state == STATE_BROWSER_FULLSCREEN_WITH_CHROME ||
690         event == TOGGLE_FULLSCREEN_CHROME) {
691       debugging_log_ << "\nSkipping Lion Fullscreen test on non-OSX 10.7+.\n";
692       return true;
693     }
694   }
695
696   // When testing reentrancy there are states the fullscreen controller
697   // will be unable to remain in, as they will progress due to the
698   // reentrant window change call. Skip states that will be instantly
699   // exited by the reentrant call.
700   if (IsWindowFullscreenStateChangedReentrant() &&
701       (transition_table_[state][WINDOW_CHANGE] != state)) {
702     debugging_log_ << "\nSkipping reentrant test for transitory source state "
703         << GetStateString(state) << ".\n";
704     return true;
705   }
706
707   if (ShouldSkipStateAndEventPair(state, event)) {
708     debugging_log_ << "\nSkipping test due to ShouldSkipStateAndEventPair("
709         << GetStateString(state) << ", "
710         << GetEventString(event) << ").\n";
711     LOG(INFO) << "Skipping test due to ShouldSkipStateAndEventPair("
712         << GetStateString(state) << ", "
713         << GetEventString(event) << ").";
714     return true;
715   }
716
717   return false;
718 }
719
720 void FullscreenControllerStateTest::TestStateAndEvent(State state,
721                                                       Event event) {
722   if (ShouldSkipTest(state, event))
723     return;
724
725   debugging_log_ << "\nTest transition from state "
726       << GetStateString(state)
727       << (IsWindowFullscreenStateChangedReentrant() ?
728           " with reentrant calls.\n" : ".\n");
729
730   // Spaced out text to line up with columns printed in InvokeEvent().
731   debugging_log_ << "First,                                               from "
732       << GetStateString(state_) << "\n";
733   ASSERT_NO_FATAL_FAILURE(TransitionToState(state))
734       << GetAndClearDebugLog();
735
736   debugging_log_ << " Then,\n";
737   ASSERT_TRUE(InvokeEvent(event)) << GetAndClearDebugLog();
738 }
739
740 void FullscreenControllerStateTest::VerifyWindowStateExpectations(
741     FullscreenWithChromeExpectation fullscreen_with_chrome,
742     FullscreenWithoutChromeExpectation fullscreen_without_chrome,
743     FullscreenForBrowserExpectation fullscreen_for_browser,
744     FullscreenForTabExpectation fullscreen_for_tab,
745     InMetroSnapExpectation in_metro_snap) {
746 #if defined(OS_MACOSX)
747   if (fullscreen_with_chrome != FULLSCREEN_WITH_CHROME_NO_EXPECTATION) {
748     EXPECT_EQ(GetBrowser()->window()->IsFullscreenWithChrome(),
749               !!fullscreen_with_chrome) << GetAndClearDebugLog();
750   }
751   if (fullscreen_without_chrome != FULLSCREEN_WITHOUT_CHROME_NO_EXPECTATION) {
752     EXPECT_EQ(GetBrowser()->window()->IsFullscreenWithoutChrome(),
753               !!fullscreen_without_chrome) << GetAndClearDebugLog();
754   }
755 #endif
756   if (fullscreen_for_browser != FULLSCREEN_FOR_BROWSER_NO_EXPECTATION) {
757     EXPECT_EQ(GetFullscreenController()->IsFullscreenForBrowser(),
758               !!fullscreen_for_browser) << GetAndClearDebugLog();
759   }
760   if (fullscreen_for_tab != FULLSCREEN_FOR_TAB_NO_EXPECTATION) {
761     EXPECT_EQ(GetFullscreenController()->IsFullscreenForTabOrPending(),
762               !!fullscreen_for_tab) << GetAndClearDebugLog();
763   }
764   if (in_metro_snap != IN_METRO_SNAP_NO_EXPECTATION) {
765     EXPECT_EQ(GetFullscreenController()->IsInMetroSnapMode(),
766               !!in_metro_snap) << GetAndClearDebugLog();
767   }
768 }
769
770 FullscreenController* FullscreenControllerStateTest::GetFullscreenController() {
771     return GetBrowser()->fullscreen_controller();
772 }
773
774 std::string FullscreenControllerStateTest::GetTransitionTableAsString() const {
775   std::ostringstream output;
776   output << "transition_table_[NUM_STATES = " << NUM_STATES
777       << "][NUM_EVENTS = " << NUM_EVENTS
778       << "] =\n";
779   for (int state_int = 0; state_int < NUM_STATES; ++state_int) {
780     State state = static_cast<State>(state_int);
781     output << "    { // " << GetStateString(state) << ":\n";
782     for (int event_int = 0; event_int < NUM_EVENTS; ++event_int) {
783       Event event = static_cast<Event>(event_int);
784       output << "      "
785           << std::left << std::setw(kMaxStateNameLength+1)
786           << std::string(GetStateString(transition_table_[state][event])) + ","
787           << "// Event "
788           << GetEventString(event) << "\n";
789     }
790     output << "    },\n";
791   }
792   output << "  };\n";
793   return output.str();
794 }
795
796 std::string FullscreenControllerStateTest::GetStateTransitionsAsString() const {
797   std::ostringstream output;
798   output << "state_transitions_[NUM_STATES = " << NUM_STATES
799       << "][NUM_STATES = " << NUM_STATES << "] =\n";
800   for (int state1_int = 0; state1_int < NUM_STATES; ++state1_int) {
801     State state1 = static_cast<State>(state1_int);
802     output << "{ // " << GetStateString(state1) << ":\n";
803     for (int state2_int = 0; state2_int < NUM_STATES; ++state2_int) {
804       State state2 = static_cast<State>(state2_int);
805       const StateTransitionInfo& info = state_transitions_[state1][state2];
806       output << "  { "
807         << std::left << std::setw(kMaxStateNameLength+1)
808         << std::string(GetEventString(info.event)) + ","
809         << std::left << std::setw(kMaxStateNameLength+1)
810         << std::string(GetStateString(info.state)) + ","
811         << std::right << std::setw(2)
812         << info.distance
813         << " }, // "
814         << GetStateString(state2) << "\n";
815     }
816     output << "},\n";
817   }
818   output << "};";
819   return output.str();
820 }