fullscreen_controller_state_test.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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
25namespace {
26
27bool SupportsMacSystemFullscreen() {
28#if defined(OS_MACOSX)
29  return chrome::mac::SupportsSystemFullscreen();
30#else
31  return false;
32#endif
33}
34
35}  // namespace
36
37FullscreenControllerStateTest::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  CHECK_EQ(sizeof(transition_table_data), sizeof(transition_table_));
202  memcpy(transition_table_, transition_table_data,
203         sizeof(transition_table_data));
204
205  // Verify that transition_table_ has been completely defined.
206  for (int source = 0; source < NUM_STATES; source++) {
207    for (int event = 0; event < NUM_EVENTS; event++) {
208      CHECK_NE(STATE_INVALID, transition_table_[source][event]);
209      CHECK_LE(0, transition_table_[source][event]);
210      CHECK_GT(NUM_STATES, transition_table_[source][event]);
211    }
212  }
213
214  // Copy transition_table_ data into state_transitions_ table.
215  for (int source = 0; source < NUM_STATES; source++) {
216    for (int event = 0; event < NUM_EVENTS; event++) {
217      if (ShouldSkipStateAndEventPair(static_cast<State>(source),
218                                      static_cast<Event>(event)))
219        continue;
220      State destination = transition_table_[source][event];
221      state_transitions_[source][destination].event = static_cast<Event>(event);
222      state_transitions_[source][destination].state = destination;
223      state_transitions_[source][destination].distance = 1;
224    }
225  }
226}
227
228FullscreenControllerStateTest::~FullscreenControllerStateTest() {
229}
230
231// static
232const char* FullscreenControllerStateTest::GetStateString(State state) {
233  switch (state) {
234    case STATE_NORMAL:
235      return "STATE_NORMAL";
236    case STATE_BROWSER_FULLSCREEN_NO_CHROME:
237      return "STATE_BROWSER_FULLSCREEN_NO_CHROME";
238    case STATE_BROWSER_FULLSCREEN_WITH_CHROME:
239      return "STATE_BROWSER_FULLSCREEN_WITH_CHROME";
240    case STATE_METRO_SNAP:
241      return "STATE_METRO_SNAP";
242    case STATE_TAB_FULLSCREEN:
243      return "STATE_TAB_FULLSCREEN";
244    case STATE_TAB_BROWSER_FULLSCREEN:
245      return "STATE_TAB_BROWSER_FULLSCREEN";
246    case STATE_TAB_BROWSER_FULLSCREEN_CHROME:
247      return "STATE_TAB_BROWSER_FULLSCREEN_CHROME";
248    case STATE_TO_NORMAL:
249      return "STATE_TO_NORMAL";
250    case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME:
251      return "STATE_TO_BROWSER_FULLSCREEN_NO_CHROME";
252    case STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME:
253      return "STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME";
254    case STATE_TO_TAB_FULLSCREEN:
255      return "STATE_TO_TAB_FULLSCREEN";
256    case STATE_INVALID:
257      return "STATE_INVALID";
258    default:
259      NOTREACHED() << "No string for state " << state;
260      return "State-Unknown";
261  }
262}
263
264// static
265const char* FullscreenControllerStateTest::GetEventString(Event event) {
266  switch (event) {
267    case TOGGLE_FULLSCREEN:
268      return "TOGGLE_FULLSCREEN";
269    case TOGGLE_FULLSCREEN_CHROME:
270      return "TOGGLE_FULLSCREEN_CHROME";
271    case TAB_FULLSCREEN_TRUE:
272      return "TAB_FULLSCREEN_TRUE";
273    case TAB_FULLSCREEN_FALSE:
274      return "TAB_FULLSCREEN_FALSE";
275    case METRO_SNAP_TRUE:
276      return "METRO_SNAP_TRUE";
277    case METRO_SNAP_FALSE:
278      return "METRO_SNAP_FALSE";
279    case BUBBLE_EXIT_LINK:
280      return "BUBBLE_EXIT_LINK";
281    case BUBBLE_ALLOW:
282      return "BUBBLE_ALLOW";
283    case BUBBLE_DENY:
284      return "BUBBLE_DENY";
285    case WINDOW_CHANGE:
286      return "WINDOW_CHANGE";
287    case EVENT_INVALID:
288      return "EVENT_INVALID";
289    default:
290      NOTREACHED() << "No string for event " << event;
291      return "Event-Unknown";
292  }
293}
294
295// static
296bool FullscreenControllerStateTest::IsReentrant() {
297#if defined(TOOLKIT_VIEWS)
298  return true;
299#else
300  return false;
301#endif
302}
303
304// static
305bool FullscreenControllerStateTest::IsPersistentState(State state) {
306  switch (state) {
307    case STATE_NORMAL:
308    case STATE_BROWSER_FULLSCREEN_NO_CHROME:
309    case STATE_BROWSER_FULLSCREEN_WITH_CHROME:
310    case STATE_METRO_SNAP:
311    case STATE_TAB_FULLSCREEN:
312    case STATE_TAB_BROWSER_FULLSCREEN:
313    case STATE_TAB_BROWSER_FULLSCREEN_CHROME:
314      return true;
315    case STATE_TO_NORMAL:
316    case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME:
317    case STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME:
318    case STATE_TO_TAB_FULLSCREEN:
319      return false;
320    default:
321      NOTREACHED();
322      return false;
323  }
324}
325
326void FullscreenControllerStateTest::TransitionToState(State final_state) {
327  int max_steps = NUM_STATES;
328  while (max_steps-- && TransitionAStepTowardState(final_state))
329    continue;
330  ASSERT_GE(max_steps, 0) << "TransitionToState was unable to achieve desired "
331      << "target state. TransitionAStepTowardState iterated too many times."
332      << GetAndClearDebugLog();
333  ASSERT_EQ(final_state, state_) << "TransitionToState was unable to achieve "
334      << "desired target state. TransitionAStepTowardState returned false."
335      << GetAndClearDebugLog();
336}
337
338bool FullscreenControllerStateTest::TransitionAStepTowardState(
339    State destination_state) {
340  State source_state = state_;
341  if (source_state == destination_state)
342    return false;
343
344  StateTransitionInfo next = NextTransitionInShortestPath(source_state,
345                                                          destination_state,
346                                                          NUM_STATES);
347  if (next.state == STATE_INVALID) {
348    NOTREACHED() << "TransitionAStepTowardState unable to transition. "
349        << "NextTransitionInShortestPath("
350        << GetStateString(source_state) << ", "
351        << GetStateString(destination_state) << ") returned STATE_INVALID."
352        << GetAndClearDebugLog();
353    return false;
354  }
355
356  return InvokeEvent(next.event);
357}
358
359const char* FullscreenControllerStateTest::GetWindowStateString() {
360  return NULL;
361}
362
363bool FullscreenControllerStateTest::InvokeEvent(Event event) {
364  if (!fullscreen_notification_observer_.get()) {
365    // Start observing NOTIFICATION_FULLSCREEN_CHANGED. Construct the
366    // notification observer here instead of in
367    // FullscreenControllerStateTest::FullscreenControllerStateTest() so that we
368    // listen to notifications on the proper thread.
369    fullscreen_notification_observer_.reset(
370        new FullscreenNotificationObserver());
371  }
372
373  State source_state = state_;
374  State next_state = transition_table_[source_state][event];
375
376  EXPECT_FALSE(ShouldSkipStateAndEventPair(source_state, event))
377      << GetAndClearDebugLog();
378
379  // When simulating reentrant window change calls, expect the next state
380  // automatically.
381  if (IsReentrant())
382    next_state = transition_table_[next_state][WINDOW_CHANGE];
383
384  debugging_log_ << "  InvokeEvent(" << std::left
385      << std::setw(MAX_EVENT_NAME_LENGTH) << GetEventString(event)
386      << ") to "
387      << std::setw(MAX_STATE_NAME_LENGTH) << GetStateString(next_state);
388
389  state_ = next_state;
390
391  switch (event) {
392    case TOGGLE_FULLSCREEN:
393      GetFullscreenController()->ToggleFullscreenMode();
394      break;
395    case TOGGLE_FULLSCREEN_CHROME:
396#if defined(OS_MACOSX)
397      if (chrome::mac::SupportsSystemFullscreen()) {
398        GetFullscreenController()->ToggleFullscreenWithChrome();
399        break;
400      }
401#endif
402      NOTREACHED() << GetAndClearDebugLog();
403      break;
404    case TAB_FULLSCREEN_TRUE:
405      GetFullscreenController()->ToggleFullscreenModeForTab(
406           GetBrowser()->tab_strip_model()->GetActiveWebContents(), true);
407      break;
408    case TAB_FULLSCREEN_FALSE:
409      GetFullscreenController()->ToggleFullscreenModeForTab(
410           GetBrowser()->tab_strip_model()->GetActiveWebContents(), false);
411      break;
412    case METRO_SNAP_TRUE:
413#if defined(OS_WIN)
414      GetFullscreenController()->SetMetroSnapMode(true);
415#else
416      NOTREACHED() << GetAndClearDebugLog();
417#endif
418      break;
419    case METRO_SNAP_FALSE:
420#if defined(OS_WIN)
421      GetFullscreenController()->SetMetroSnapMode(false);
422#else
423      NOTREACHED() << GetAndClearDebugLog();
424#endif
425      break;
426    case BUBBLE_EXIT_LINK:
427      GetFullscreenController()->ExitTabOrBrowserFullscreenToPreviousState();
428      break;
429    case BUBBLE_ALLOW:
430      GetFullscreenController()->OnAcceptFullscreenPermission();
431      break;
432    case BUBBLE_DENY:
433      GetFullscreenController()->OnDenyFullscreenPermission();
434      break;
435    case WINDOW_CHANGE:
436      ChangeWindowFullscreenState();
437      break;
438    default:
439      NOTREACHED() << "InvokeEvent needs a handler for event "
440          << GetEventString(event) << GetAndClearDebugLog();
441      return false;
442  }
443
444  if (GetWindowStateString())
445    debugging_log_ << " Window state now " << GetWindowStateString() << "\n";
446  else
447    debugging_log_ << "\n";
448
449  MaybeWaitForNotification();
450  VerifyWindowState();
451
452  return true;
453}
454
455void FullscreenControllerStateTest::VerifyWindowState() {
456  switch (state_) {
457    case STATE_NORMAL:
458#if defined(OS_MACOSX)
459      EXPECT_FALSE(GetBrowser()->window()->IsFullscreenWithChrome())
460          << GetAndClearDebugLog();
461      EXPECT_FALSE(GetBrowser()->window()->IsFullscreenWithoutChrome())
462          << GetAndClearDebugLog();
463#endif
464      EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser())
465          << GetAndClearDebugLog();
466      EXPECT_FALSE(GetFullscreenController()->IsFullscreenForTabOrPending())
467          << GetAndClearDebugLog();
468      EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
469          << GetAndClearDebugLog();
470      break;
471    case STATE_BROWSER_FULLSCREEN_NO_CHROME:
472#if defined(OS_MACOSX)
473      EXPECT_FALSE(GetBrowser()->window()->IsFullscreenWithChrome())
474          << GetAndClearDebugLog();
475      EXPECT_TRUE(GetBrowser()->window()->IsFullscreenWithoutChrome())
476          << GetAndClearDebugLog();
477#endif
478      EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser())
479          << GetAndClearDebugLog();
480      EXPECT_FALSE(GetFullscreenController()->IsFullscreenForTabOrPending())
481          << GetAndClearDebugLog();
482      EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
483          << GetAndClearDebugLog();
484      break;
485    case STATE_BROWSER_FULLSCREEN_WITH_CHROME:
486#if defined(OS_MACOSX)
487      EXPECT_TRUE(GetBrowser()->window()->IsFullscreenWithChrome())
488          << GetAndClearDebugLog();
489      EXPECT_FALSE(GetBrowser()->window()->IsFullscreenWithoutChrome())
490          << GetAndClearDebugLog();
491#endif
492      EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser())
493          << GetAndClearDebugLog();
494      EXPECT_FALSE(GetFullscreenController()->IsFullscreenForTabOrPending())
495          << GetAndClearDebugLog();
496      EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
497          << GetAndClearDebugLog();
498      break;
499    case STATE_METRO_SNAP:
500#if defined(OS_WIN)
501      // http://crbug.com/169138
502      // No expectation for IsFullscreenWithChrome() or
503      // IsFullscreenWithoutChrome()
504
505      // TODO(scheib) IsFullscreenForBrowser and IsFullscreenForTabOrPending
506      // are returning true and false in interactive tests with real window.
507      // With only a single Metro Snap state in this test framework it isn't
508      // fair to try to have an expectation anyway.
509      //
510      // No expectation for IsFullscreenForBrowser.
511      // No expectation for IsFullscreenForTabOrPending.
512      EXPECT_TRUE(GetFullscreenController()->IsInMetroSnapMode())
513          << GetAndClearDebugLog();
514#else
515      NOTREACHED() << GetAndClearDebugLog();
516#endif
517      break;
518    case STATE_TAB_FULLSCREEN:
519#if defined(OS_MACOSX)
520      EXPECT_FALSE(GetBrowser()->window()->IsFullscreenWithChrome())
521          << GetAndClearDebugLog();
522      EXPECT_TRUE(GetBrowser()->window()->IsFullscreenWithoutChrome())
523          << GetAndClearDebugLog();
524#endif
525      EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser())
526          << GetAndClearDebugLog();
527      EXPECT_TRUE(GetFullscreenController()->IsFullscreenForTabOrPending())
528          << GetAndClearDebugLog();
529      EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
530          << GetAndClearDebugLog();
531      break;
532    case STATE_TAB_BROWSER_FULLSCREEN:
533#if defined(OS_MACOSX)
534      EXPECT_FALSE(GetBrowser()->window()->IsFullscreenWithChrome())
535          << GetAndClearDebugLog();
536      EXPECT_TRUE(GetBrowser()->window()->IsFullscreenWithoutChrome())
537          << GetAndClearDebugLog();
538#endif
539      EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser())
540          << GetAndClearDebugLog();
541      EXPECT_TRUE(GetFullscreenController()->IsFullscreenForTabOrPending())
542          << GetAndClearDebugLog();
543      EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
544          << GetAndClearDebugLog();
545      break;
546    case STATE_TAB_BROWSER_FULLSCREEN_CHROME:
547#if defined(OS_MACOSX)
548      EXPECT_FALSE(GetBrowser()->window()->IsFullscreenWithChrome())
549          << GetAndClearDebugLog();
550      EXPECT_TRUE(GetBrowser()->window()->IsFullscreenWithoutChrome())
551          << GetAndClearDebugLog();
552#endif
553      EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser())
554          << GetAndClearDebugLog();
555      EXPECT_TRUE(GetFullscreenController()->IsFullscreenForTabOrPending())
556          << GetAndClearDebugLog();
557      EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
558          << GetAndClearDebugLog();
559      break;
560    case STATE_TO_NORMAL:
561#if defined(OS_MACOSX)
562      EXPECT_FALSE(GetBrowser()->window()->IsFullscreenWithChrome())
563          << GetAndClearDebugLog();
564      EXPECT_FALSE(GetBrowser()->window()->IsFullscreenWithoutChrome())
565          << GetAndClearDebugLog();
566#endif
567      // No expectation for IsFullscreenForBrowser.
568      // No expectation for IsFullscreenForTabOrPending.
569      EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
570          << GetAndClearDebugLog();
571      break;
572    case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME:
573#if defined(OS_MACOSX)
574      EXPECT_FALSE(GetBrowser()->window()->IsFullscreenWithChrome())
575          << GetAndClearDebugLog();
576      EXPECT_TRUE(GetBrowser()->window()->IsFullscreenWithoutChrome())
577          << GetAndClearDebugLog();
578      EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser())
579          << GetAndClearDebugLog();
580#else
581      EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser())
582          << GetAndClearDebugLog();
583#endif
584      // No expectation for IsFullscreenForTabOrPending.
585      EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
586          << GetAndClearDebugLog();
587      break;
588    case STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME:
589#if defined(OS_MACOSX)
590      EXPECT_TRUE(GetBrowser()->window()->IsFullscreenWithChrome())
591          << GetAndClearDebugLog();
592      EXPECT_FALSE(GetBrowser()->window()->IsFullscreenWithoutChrome())
593          << GetAndClearDebugLog();
594      EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser())
595          << GetAndClearDebugLog();
596#else
597      EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser())
598          << GetAndClearDebugLog();
599#endif
600      // No expectation for IsFullscreenForTabOrPending.
601      EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
602          << GetAndClearDebugLog();
603      break;
604    case STATE_TO_TAB_FULLSCREEN:
605#if defined(OS_MACOSX)
606      // TODO(scheib) InPresentationMode returns false when invoking events:
607      // TAB_FULLSCREEN_TRUE, TOGGLE_FULLSCREEN. http://crbug.com/156645
608      // It may be that a new testing state TO_TAB_BROWSER_FULLSCREEN
609      // would help work around this http://crbug.com/154196
610      // Test with: STATE_TO_TAB_FULLSCREEN__TOGGLE_FULLSCREEN
611      //
612      // EXPECT_TRUE(GetBrowser()->window()->InPresentationMode())
613      //     << GetAndClearDebugLog();
614#endif
615      EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser())
616          << GetAndClearDebugLog();
617      EXPECT_TRUE(GetFullscreenController()->IsFullscreenForTabOrPending())
618          << GetAndClearDebugLog();
619      EXPECT_FALSE(GetFullscreenController()->IsInMetroSnapMode())
620          << GetAndClearDebugLog();
621      break;
622    default:
623      NOTREACHED() << GetAndClearDebugLog();
624  }
625}
626
627void FullscreenControllerStateTest::MaybeWaitForNotification() {
628  // We should get a fullscreen notification each time we get to a new
629  // persistent state. If we don't get a notification, the test will
630  // fail by timing out.
631  if (state_ != last_notification_received_state_ &&
632      IsPersistentState(state_)) {
633    fullscreen_notification_observer_->Wait();
634    last_notification_received_state_ = state_;
635    fullscreen_notification_observer_.reset(
636        new FullscreenNotificationObserver());
637  }
638}
639
640void FullscreenControllerStateTest::TestTransitionsForEachState() {
641  for (int source_int = 0; source_int < NUM_STATES; source_int++) {
642    for (int event1_int = 0; event1_int < NUM_EVENTS; event1_int++) {
643      State state = static_cast<State>(source_int);
644      Event event1 = static_cast<Event>(event1_int);
645
646      // Early out if skipping all tests for this state, reduces log noise.
647      if (ShouldSkipTest(state, event1))
648        continue;
649
650      for (int event2_int = 0; event2_int < NUM_EVENTS; event2_int++) {
651        for (int event3_int = 0; event3_int < NUM_EVENTS; event3_int++) {
652          Event event2 = static_cast<Event>(event2_int);
653          Event event3 = static_cast<Event>(event3_int);
654
655          // Test each state and each event.
656          ASSERT_NO_FATAL_FAILURE(TestStateAndEvent(state, event1))
657              << GetAndClearDebugLog();
658
659          // Then, add an additional event to the sequence.
660          if (ShouldSkipStateAndEventPair(state_, event2))
661            continue;
662          ASSERT_TRUE(InvokeEvent(event2)) << GetAndClearDebugLog();
663
664          // Then, add an additional event to the sequence.
665          if (ShouldSkipStateAndEventPair(state_, event3))
666            continue;
667          ASSERT_TRUE(InvokeEvent(event3)) << GetAndClearDebugLog();
668        }
669      }
670    }
671  }
672}
673
674FullscreenControllerStateTest::StateTransitionInfo
675    FullscreenControllerStateTest::NextTransitionInShortestPath(
676        State source, State destination, int search_limit) {
677  if (search_limit == 0)
678    return StateTransitionInfo();  // Return a default (invalid) state.
679
680  if (state_transitions_[source][destination].state == STATE_INVALID) {
681    // Don't know the next state yet, do a depth first search.
682    StateTransitionInfo result;
683
684    // Consider all states reachable via each event from the source state.
685    for (int event_int = 0; event_int < NUM_EVENTS; event_int++) {
686      Event event = static_cast<Event>(event_int);
687      State next_state_candidate = transition_table_[source][event];
688
689      if (ShouldSkipStateAndEventPair(source, event))
690        continue;
691
692      // Recurse.
693      StateTransitionInfo candidate = NextTransitionInShortestPath(
694          next_state_candidate, destination, search_limit - 1);
695
696      if (candidate.distance + 1 < result.distance) {
697        result.event = event;
698        result.state = next_state_candidate;
699        result.distance = candidate.distance + 1;
700      }
701    }
702
703    // Cache result so that a search is not required next time.
704    state_transitions_[source][destination] = result;
705  }
706
707  return state_transitions_[source][destination];
708}
709
710std::string FullscreenControllerStateTest::GetAndClearDebugLog() {
711  debugging_log_ << "(End of Debugging Log)\n";
712  std::string output_log = "\nDebugging Log:\n" + debugging_log_.str();
713  debugging_log_.str(std::string());
714  return output_log;
715}
716
717bool FullscreenControllerStateTest::ShouldSkipStateAndEventPair(State state,
718                                                                Event event) {
719  // TODO(scheib) Toggling Tab fullscreen while pending Tab or
720  // Browser fullscreen is broken currently http://crbug.com/154196
721  if ((state == STATE_TO_BROWSER_FULLSCREEN_NO_CHROME ||
722       state == STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME ||
723       state == STATE_TO_TAB_FULLSCREEN) &&
724      (event == TAB_FULLSCREEN_TRUE || event == TAB_FULLSCREEN_FALSE))
725    return true;
726  if (state == STATE_TO_NORMAL && event == TAB_FULLSCREEN_TRUE)
727    return true;
728
729  // Skip metro snap state and events when not on windows.
730#if !defined(OS_WIN)
731  if (state == STATE_METRO_SNAP ||
732      event == METRO_SNAP_TRUE ||
733      event == METRO_SNAP_FALSE)
734    return true;
735#endif
736
737  // Skip Mac Lion Fullscreen state and events when not on OSX 10.7+.
738  if (!SupportsMacSystemFullscreen()) {
739    if (state == STATE_BROWSER_FULLSCREEN_WITH_CHROME ||
740        state == STATE_TAB_BROWSER_FULLSCREEN_CHROME ||
741        state == STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME ||
742        event == TOGGLE_FULLSCREEN_CHROME) {
743      return true;
744    }
745  }
746
747  return false;
748}
749
750bool FullscreenControllerStateTest::ShouldSkipTest(State state, Event event) {
751  // Quietly skip metro snap tests when not on windows.
752#if !defined(OS_WIN)
753  if (state == STATE_METRO_SNAP ||
754      event == METRO_SNAP_TRUE ||
755      event == METRO_SNAP_FALSE) {
756    debugging_log_ << "\nSkipping metro snap test on non-Windows.\n";
757    return true;
758  }
759#endif
760
761  // Quietly skip Mac Lion Fullscreen tests when not on OSX 10.7+.
762  if (!SupportsMacSystemFullscreen()) {
763    if (state == STATE_BROWSER_FULLSCREEN_WITH_CHROME ||
764        event == TOGGLE_FULLSCREEN_CHROME) {
765      debugging_log_ << "\nSkipping Lion Fullscreen test on non-OSX 10.7+.\n";
766      return true;
767    }
768  }
769
770  // When testing reentrancy there are states the fullscreen controller
771  // will be unable to remain in, as they will progress due to the
772  // reentrant window change call. Skip states that will be instantly
773  // exited by the reentrant call.
774  if (IsReentrant() && (transition_table_[state][WINDOW_CHANGE] != state)) {
775    debugging_log_ << "\nSkipping reentrant test for transitory source state "
776        << GetStateString(state) << ".\n";
777    return true;
778  }
779
780  if (ShouldSkipStateAndEventPair(state, event)) {
781    debugging_log_ << "\nSkipping test due to ShouldSkipStateAndEventPair("
782        << GetStateString(state) << ", "
783        << GetEventString(event) << ").\n";
784    LOG(INFO) << "Skipping test due to ShouldSkipStateAndEventPair("
785        << GetStateString(state) << ", "
786        << GetEventString(event) << ").";
787    return true;
788  }
789
790  return false;
791}
792
793void FullscreenControllerStateTest::TestStateAndEvent(State state,
794                                                      Event event) {
795  if (ShouldSkipTest(state, event))
796    return;
797
798  debugging_log_ << "\nTest transition from state "
799      << GetStateString(state)
800      << (IsReentrant() ? " with reentrant calls.\n" : ".\n");
801
802  debugging_log_ << "First,                               from"
803      << GetStateString(state_) << "\n";
804  ASSERT_NO_FATAL_FAILURE(TransitionToState(state))
805      << GetAndClearDebugLog();
806
807  debugging_log_ << " Then,\n";
808  ASSERT_TRUE(InvokeEvent(event)) << GetAndClearDebugLog();
809}
810
811FullscreenController* FullscreenControllerStateTest::GetFullscreenController() {
812    return GetBrowser()->fullscreen_controller();
813}
814
815std::string FullscreenControllerStateTest::GetTransitionTableAsString() const {
816  std::ostringstream output;
817  output << "transition_table_[NUM_STATES = " << NUM_STATES
818      << "][NUM_EVENTS = " << NUM_EVENTS
819      << "] =\n";
820  for (int state_int = 0; state_int < NUM_STATES; state_int++) {
821    State state = static_cast<State>(state_int);
822    output << "    { // " << GetStateString(state) << ":\n";
823    for (int event_int = 0; event_int < NUM_EVENTS; event_int++) {
824      Event event = static_cast<Event>(event_int);
825      output << "      "
826          << std::left << std::setw(MAX_STATE_NAME_LENGTH+1)
827          << std::string(GetStateString(transition_table_[state][event])) + ","
828          << "// Event "
829          << GetEventString(event) << "\n";
830    }
831    output << "    },\n";
832  }
833  output << "  };\n";
834  return output.str();
835}
836
837std::string FullscreenControllerStateTest::GetStateTransitionsAsString() const {
838  std::ostringstream output;
839  output << "state_transitions_[NUM_STATES = " << NUM_STATES
840      << "][NUM_STATES = " << NUM_STATES << "] =\n";
841  for (int state1_int = 0; state1_int < NUM_STATES; state1_int++) {
842    State state1 = static_cast<State>(state1_int);
843    output << "{ // " << GetStateString(state1) << ":\n";
844    for (int state2_int = 0; state2_int < NUM_STATES; state2_int++) {
845      State state2 = static_cast<State>(state2_int);
846      const StateTransitionInfo &info = state_transitions_[state1][state2];
847      output << "  { "
848        << std::left << std::setw(MAX_EVENT_NAME_LENGTH+1)
849        << std::string(GetEventString(info.event)) + ","
850        << std::left << std::setw(MAX_STATE_NAME_LENGTH+1)
851        << std::string(GetStateString(info.state)) + ","
852        << std::right << std::setw(2)
853        << info.distance
854        << " }, // "
855        << GetStateString(state2) << "\n";
856    }
857    output << "},\n";
858  }
859  output << "};";
860  return output.str();
861}
862