| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/exclusive_access/fullscreen_controller_state_test.h" |
| |
| #include <iomanip> |
| #include <iostream> |
| |
| #include "build/build_config.h" |
| #include "chrome/browser/fullscreen.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_window/public/browser_window_features.h" |
| #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h" |
| #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h" |
| #include "chrome/browser/ui/exclusive_access/exclusive_access_test.h" |
| #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/url_constants.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| using FState = FullscreenControllerStateTest::State; |
| // Human specified state machine data. |
| // For each state, for each event, define the resulting state. |
| constexpr auto kTransitionTableData = std::to_array< |
| std::array<FState, FullscreenControllerStateTest::NUM_EVENTS>>({ |
| { |
| // STATE_NORMAL: |
| FState::STATE_TO_BROWSER_FULLSCREEN, // Event TOGGLE_FULLSCREEN |
| FState::STATE_TO_TAB_FULLSCREEN, // Event ENTER_TAB_FULLSCREEN |
| FState::STATE_NORMAL, // Event EXIT_TAB_FULLSCREEN |
| FState::STATE_NORMAL, // Event BUBBLE_EXIT_LINK |
| FState::STATE_NORMAL, // Event WINDOW_CHANGE |
| }, |
| { |
| // STATE_BROWSER_FULLSCREEN: |
| FState::STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN |
| FState::STATE_TAB_BROWSER_FULLSCREEN, // Event ENTER_TAB_FULLSCREEN |
| FState::STATE_BROWSER_FULLSCREEN, // Event EXIT_TAB_FULLSCREEN |
| FState::STATE_TO_NORMAL, // Event BUBBLE_EXIT_LINK |
| FState::STATE_BROWSER_FULLSCREEN, // Event WINDOW_CHANGE |
| }, |
| { |
| // STATE_TAB_FULLSCREEN: |
| FState::STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN |
| FState::STATE_TAB_FULLSCREEN, // Event ENTER_TAB_FULLSCREEN |
| FState::STATE_TO_NORMAL, // Event EXIT_TAB_FULLSCREEN |
| FState::STATE_TO_NORMAL, // Event BUBBLE_EXIT_LINK |
| FState::STATE_TAB_FULLSCREEN, // Event WINDOW_CHANGE |
| }, |
| { |
| // STATE_TAB_BROWSER_FULLSCREEN: |
| FState::STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN |
| FState::STATE_TAB_BROWSER_FULLSCREEN, // Event ENTER_TAB_FULLSCREEN |
| FState::STATE_BROWSER_FULLSCREEN, // Event EXIT_TAB_FULLSCREEN |
| FState::STATE_BROWSER_FULLSCREEN, // Event BUBBLE_EXIT_LINK |
| FState::STATE_TAB_BROWSER_FULLSCREEN, // Event WINDOW_CHANGE |
| }, |
| { |
| // STATE_TO_NORMAL: |
| FState::STATE_TO_BROWSER_FULLSCREEN, // Event TOGGLE_FULLSCREEN |
| // TODO(crbug.com/40951066) |
| // Should be a route back to TAB |
| FState::STATE_TO_NORMAL, // Event ENTER_TAB_FULLSCREEN |
| FState::STATE_TO_NORMAL, // Event EXIT_TAB_FULLSCREEN |
| FState::STATE_TO_NORMAL, // Event BUBBLE_EXIT_LINK |
| FState::STATE_NORMAL, // Event WINDOW_CHANGE |
| }, |
| { |
| // STATE_TO_BROWSER_FULLSCREEN: |
| FState::STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN |
| // TODO(crbug.com/40951066): Should be a |
| // route to TAB_BROWSER |
| FState::STATE_TO_BROWSER_FULLSCREEN, // Event ENTER_TAB_FULLSCREEN |
| FState::STATE_TO_BROWSER_FULLSCREEN, // Event EXIT_TAB_FULLSCREEN |
| #if BUILDFLAG(IS_MAC) |
| // Mac window reports fullscreen |
| // immediately and an exit |
| // triggers exit. |
| FState::STATE_TO_NORMAL, // Event BUBBLE_EXIT_LINK |
| #else |
| FState::STATE_TO_BROWSER_FULLSCREEN, // Event BUBBLE_EXIT_LINK |
| #endif |
| FState::STATE_BROWSER_FULLSCREEN, // Event WINDOW_CHANGE |
| }, |
| { |
| // STATE_TO_TAB_FULLSCREEN: |
| // TODO(crbug.com/40951066): Should be a route to TAB_BROWSER |
| FState::STATE_TO_TAB_FULLSCREEN, // Event TOGGLE_FULLSCREEN |
| FState::STATE_TO_TAB_FULLSCREEN, // Event ENTER_TAB_FULLSCREEN |
| #if BUILDFLAG(IS_MAC) |
| // Mac runs as expected due to a |
| // forced |
| // NotifyTabOfExitIfNecessary(); |
| FState::STATE_TO_NORMAL, // Event EXIT_TAB_FULLSCREEN |
| #else |
| // TODO(crbug.com/40951066): Should |
| // be a route back to NORMAL |
| FState::STATE_TO_BROWSER_FULLSCREEN, // Event EXIT_TAB_FULLSCREEN |
| #endif |
| #if BUILDFLAG(IS_MAC) |
| // Mac window reports fullscreen |
| // immediately and an exit triggers |
| // exit. |
| FState::STATE_TO_NORMAL, // Event BUBBLE_EXIT_LINK |
| #else |
| FState::STATE_TO_TAB_FULLSCREEN, // Event BUBBLE_EXIT_LINK |
| #endif |
| FState::STATE_TAB_FULLSCREEN, // Event WINDOW_CHANGE |
| }, |
| }); |
| |
| } // namespace |
| |
| FullscreenControllerStateTest::FullscreenControllerStateTest() { |
| for (int source = 0; source < NUM_STATES; ++source) { |
| for (int event = 0; event < NUM_EVENTS; ++event) { |
| if (ShouldSkipStateAndEventPair(static_cast<State>(source), |
| static_cast<Event>(event))) { |
| continue; |
| } |
| const State destination = kTransitionTableData[source][event]; |
| state_transitions_[source][destination].event = static_cast<Event>(event); |
| state_transitions_[source][destination].state = destination; |
| state_transitions_[source][destination].distance = 1; |
| } |
| } |
| } |
| |
| FullscreenControllerStateTest::~FullscreenControllerStateTest() = default; |
| |
| // static |
| const char* FullscreenControllerStateTest::GetStateString(State state) { |
| switch (state) { |
| ENUM_TO_STRING(STATE_NORMAL); |
| ENUM_TO_STRING(STATE_BROWSER_FULLSCREEN); |
| ENUM_TO_STRING(STATE_TAB_FULLSCREEN); |
| ENUM_TO_STRING(STATE_TAB_BROWSER_FULLSCREEN); |
| ENUM_TO_STRING(STATE_TO_NORMAL); |
| ENUM_TO_STRING(STATE_TO_BROWSER_FULLSCREEN); |
| ENUM_TO_STRING(STATE_TO_TAB_FULLSCREEN); |
| ENUM_TO_STRING(STATE_INVALID); |
| default: |
| NOTREACHED() << "No string for state " << state; |
| } |
| } |
| |
| // static |
| const char* FullscreenControllerStateTest::GetEventString(Event event) { |
| switch (event) { |
| ENUM_TO_STRING(TOGGLE_FULLSCREEN); |
| ENUM_TO_STRING(ENTER_TAB_FULLSCREEN); |
| ENUM_TO_STRING(EXIT_TAB_FULLSCREEN); |
| ENUM_TO_STRING(BUBBLE_EXIT_LINK); |
| ENUM_TO_STRING(WINDOW_CHANGE); |
| ENUM_TO_STRING(EVENT_INVALID); |
| default: |
| NOTREACHED() << "No string for event " << event; |
| } |
| } |
| |
| // static |
| bool FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant() { |
| #if BUILDFLAG(IS_MAC) |
| return false; |
| #else |
| return true; |
| #endif |
| } |
| |
| void FullscreenControllerStateTest::TransitionToState(State final_state) { |
| int max_steps = NUM_STATES; |
| while (max_steps-- && TransitionAStepTowardState(final_state)) { |
| continue; |
| } |
| ASSERT_GE(max_steps, 0) |
| << "TransitionToState was unable to achieve desired " |
| << "target state. TransitionAStepTowardState iterated too many times." |
| << GetAndClearDebugLog(); |
| ASSERT_EQ(final_state, state_) |
| << "TransitionToState was unable to achieve " |
| << "desired target state. TransitionAStepTowardState returned false." |
| << GetAndClearDebugLog(); |
| } |
| |
| bool FullscreenControllerStateTest::TransitionAStepTowardState( |
| State destination_state) { |
| const State source_state = state_; |
| if (source_state == destination_state) { |
| return false; |
| } |
| |
| const StateTransitionInfo next = |
| NextTransitionInShortestPath(source_state, destination_state, NUM_STATES); |
| if (next.state == STATE_INVALID) { |
| NOTREACHED() << "TransitionAStepTowardState unable to transition. " |
| << "NextTransitionInShortestPath(" |
| << GetStateString(source_state) << ", " |
| << GetStateString(destination_state) |
| << ") returned STATE_INVALID." << GetAndClearDebugLog(); |
| } |
| |
| return InvokeEvent(next.event); |
| } |
| |
| const char* FullscreenControllerStateTest::GetWindowStateString() { |
| return nullptr; |
| } |
| |
| bool FullscreenControllerStateTest::InvokeEvent(Event event) { |
| const State source_state = state_; |
| State next_state = kTransitionTableData[source_state][event]; |
| |
| EXPECT_FALSE(ShouldSkipStateAndEventPair(source_state, event)) |
| << GetAndClearDebugLog(); |
| |
| // When simulating reentrant window change calls, expect the next state |
| // automatically. |
| if (IsWindowFullscreenStateChangedReentrant()) { |
| next_state = kTransitionTableData[next_state][WINDOW_CHANGE]; |
| } |
| |
| // Figure out the fullscreen mode expectation. |
| ui_test_utils::FullscreenWaiter::Expectation expectation; |
| content::WebContents* const active_tab = |
| GetBrowser()->tab_strip_model()->GetActiveWebContents(); |
| // If event is {ENTER,EXIT}_TAB_FULLSCREEN and `active_tab` is |
| // being captured, fullscreen mode won't be updated. |
| if ((event != ENTER_TAB_FULLSCREEN && event != EXIT_TAB_FULLSCREEN) || |
| !active_tab->IsBeingVisiblyCaptured()) { |
| switch (next_state) { |
| case STATE_NORMAL: |
| expectation.browser_fullscreen = false; |
| expectation.tab_fullscreen = false; |
| break; |
| case STATE_BROWSER_FULLSCREEN: |
| expectation.browser_fullscreen = true; |
| expectation.tab_fullscreen = false; |
| break; |
| case STATE_TAB_FULLSCREEN: |
| expectation.browser_fullscreen = false; |
| expectation.tab_fullscreen = true; |
| break; |
| case STATE_TAB_BROWSER_FULLSCREEN: |
| expectation.browser_fullscreen = true; |
| expectation.tab_fullscreen = true; |
| break; |
| default: |
| // Do nothing. |
| break; |
| } |
| } |
| ui_test_utils::FullscreenWaiter waiter(GetBrowser(), expectation); |
| |
| debugging_log_ << " InvokeEvent(" << std::left |
| << std::setw(kMaxStateNameLength) << GetEventString(event) |
| << ") to " << std::setw(kMaxStateNameLength) |
| << GetStateString(next_state); |
| |
| state_ = next_state; |
| |
| switch (event) { |
| case TOGGLE_FULLSCREEN: |
| GetFullscreenController()->ToggleBrowserFullscreenMode( |
| /*user_initiated=*/false); |
| break; |
| case ENTER_TAB_FULLSCREEN: |
| case EXIT_TAB_FULLSCREEN: { |
| if (event == ENTER_TAB_FULLSCREEN) { |
| if (GetFullscreenController()->CanEnterFullscreenModeForTab( |
| active_tab->GetPrimaryMainFrame())) { |
| GetFullscreenController()->EnterFullscreenModeForTab( |
| active_tab->GetPrimaryMainFrame()); |
| } |
| } else { |
| GetFullscreenController()->ExitFullscreenModeForTab(active_tab); |
| } |
| |
| // Activating/Deactivating tab fullscreen on a visibly captured tab |
| // should not evoke a state change in the browser window. |
| if (active_tab->IsBeingVisiblyCaptured()) { |
| state_ = source_state; |
| } |
| break; |
| } |
| |
| case BUBBLE_EXIT_LINK: |
| GetFullscreenController()->ExitExclusiveAccessToPreviousState(); |
| break; |
| |
| case WINDOW_CHANGE: |
| ChangeWindowFullscreenState(); |
| break; |
| |
| default: |
| NOTREACHED() << "InvokeEvent needs a handler for event " |
| << GetEventString(event) << GetAndClearDebugLog(); |
| } |
| |
| if (GetWindowStateString()) { |
| debugging_log_ << " Window state now " << GetWindowStateString() << "\n"; |
| } else { |
| debugging_log_ << "\n"; |
| } |
| |
| waiter.Wait(); |
| VerifyWindowState(); |
| |
| return true; |
| } |
| |
| void FullscreenControllerStateTest::VerifyWindowState() { |
| switch (state_) { |
| case STATE_NORMAL: |
| VerifyWindowStateExpectations(FULLSCREEN_FOR_BROWSER_FALSE, |
| FULLSCREEN_FOR_TAB_FALSE); |
| break; |
| case STATE_BROWSER_FULLSCREEN: |
| VerifyWindowStateExpectations(FULLSCREEN_FOR_BROWSER_TRUE, |
| FULLSCREEN_FOR_TAB_FALSE); |
| break; |
| case STATE_TAB_FULLSCREEN: |
| VerifyWindowStateExpectations(FULLSCREEN_FOR_BROWSER_FALSE, |
| FULLSCREEN_FOR_TAB_TRUE); |
| break; |
| case STATE_TAB_BROWSER_FULLSCREEN: |
| VerifyWindowStateExpectations(FULLSCREEN_FOR_BROWSER_TRUE, |
| FULLSCREEN_FOR_TAB_TRUE); |
| break; |
| case STATE_TO_NORMAL: |
| VerifyWindowStateExpectations(FULLSCREEN_FOR_BROWSER_NO_EXPECTATION, |
| FULLSCREEN_FOR_TAB_NO_EXPECTATION); |
| break; |
| |
| case STATE_TO_BROWSER_FULLSCREEN: |
| VerifyWindowStateExpectations( |
| #if BUILDFLAG(IS_MAC) |
| FULLSCREEN_FOR_BROWSER_TRUE, |
| #else |
| FULLSCREEN_FOR_BROWSER_FALSE, |
| #endif |
| FULLSCREEN_FOR_TAB_NO_EXPECTATION); |
| break; |
| case STATE_TO_TAB_FULLSCREEN: |
| VerifyWindowStateExpectations(FULLSCREEN_FOR_BROWSER_FALSE, |
| FULLSCREEN_FOR_TAB_TRUE); |
| break; |
| |
| default: |
| NOTREACHED() << GetAndClearDebugLog(); |
| } |
| } |
| |
| void FullscreenControllerStateTest::TestTransitionsForEachState() { |
| for (int source_int = 0; source_int < NUM_STATES; ++source_int) { |
| for (int event1_int = 0; event1_int < NUM_EVENTS; ++event1_int) { |
| const State state = static_cast<State>(source_int); |
| const Event event1 = static_cast<Event>(event1_int); |
| |
| // Early out if skipping all tests for this state, reduces log noise. |
| if (ShouldSkipTest(state, event1)) { |
| continue; |
| } |
| |
| for (int event2_int = 0; event2_int < NUM_EVENTS; ++event2_int) { |
| for (int event3_int = 0; event3_int < NUM_EVENTS; ++event3_int) { |
| const Event event2 = static_cast<Event>(event2_int); |
| const Event event3 = static_cast<Event>(event3_int); |
| |
| // Test each state and each event. |
| ASSERT_NO_FATAL_FAILURE(TestStateAndEvent(state, event1)) |
| << GetAndClearDebugLog(); |
| |
| // Then, add an additional event to the sequence. |
| if (ShouldSkipStateAndEventPair(state_, event2)) { |
| continue; |
| } |
| ASSERT_TRUE(InvokeEvent(event2)) << GetAndClearDebugLog(); |
| |
| // Then, add an additional event to the sequence. |
| if (ShouldSkipStateAndEventPair(state_, event3)) { |
| continue; |
| } |
| ASSERT_TRUE(InvokeEvent(event3)) << GetAndClearDebugLog(); |
| } |
| } |
| } |
| } |
| } |
| |
| FullscreenControllerStateTest::StateTransitionInfo |
| FullscreenControllerStateTest::NextTransitionInShortestPath(State source, |
| State destination, |
| int search_limit) { |
| if (search_limit <= 0) { |
| return StateTransitionInfo(); // Return a default (invalid) state. |
| } |
| |
| if (state_transitions_[source][destination].state == STATE_INVALID) { |
| // Don't know the next state yet, do a depth first search. |
| StateTransitionInfo result; |
| |
| // Consider all states reachable via each event from the source state. |
| for (int event_int = 0; event_int < NUM_EVENTS; ++event_int) { |
| const Event event = static_cast<Event>(event_int); |
| const State next_state_candidate = kTransitionTableData[source][event]; |
| |
| if (ShouldSkipStateAndEventPair(source, event)) { |
| continue; |
| } |
| |
| // Recurse. |
| StateTransitionInfo candidate = NextTransitionInShortestPath( |
| next_state_candidate, destination, search_limit - 1); |
| |
| if (candidate.distance + 1 < result.distance) { |
| result.event = event; |
| result.state = next_state_candidate; |
| result.distance = candidate.distance + 1; |
| } |
| } |
| |
| // Cache result so that a search is not required next time. |
| state_transitions_[source][destination] = result; |
| } |
| |
| return state_transitions_[source][destination]; |
| } |
| |
| std::string FullscreenControllerStateTest::GetAndClearDebugLog() { |
| debugging_log_ << "(End of Debugging Log)\n"; |
| std::string output_log = "\nDebugging Log:\n" + debugging_log_.str(); |
| debugging_log_.str(std::string()); |
| return output_log; |
| } |
| |
| bool FullscreenControllerStateTest::ShouldSkipStateAndEventPair(State state, |
| Event event) { |
| // TODO(scheib) Toggling Tab fullscreen while pending Tab or |
| // Browser fullscreen is broken currently http://crbug.com/154196 |
| if ((state == STATE_TO_BROWSER_FULLSCREEN || |
| state == STATE_TO_TAB_FULLSCREEN) && |
| (event == ENTER_TAB_FULLSCREEN || event == EXIT_TAB_FULLSCREEN)) { |
| return true; |
| } |
| |
| if (state == STATE_TO_NORMAL && event == ENTER_TAB_FULLSCREEN) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool FullscreenControllerStateTest::ShouldSkipTest(State state, Event event) { |
| // When testing reentrancy there are states the fullscreen controller |
| // will be unable to remain in, as they will progress due to the |
| // reentrant window change call. Skip states that will be instantly |
| // exited by the reentrant call. |
| if (IsWindowFullscreenStateChangedReentrant() && |
| (kTransitionTableData[state][WINDOW_CHANGE] != state)) { |
| debugging_log_ << "\nSkipping reentrant test for transitory source state " |
| << GetStateString(state) << ".\n"; |
| return true; |
| } |
| |
| if (ShouldSkipStateAndEventPair(state, event)) { |
| debugging_log_ << "\nSkipping test due to ShouldSkipStateAndEventPair(" |
| << GetStateString(state) << ", " << GetEventString(event) |
| << ").\n"; |
| LOG(INFO) << "Skipping test due to ShouldSkipStateAndEventPair(" |
| << GetStateString(state) << ", " << GetEventString(event) << ")."; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void FullscreenControllerStateTest::TestStateAndEvent(State state, |
| Event event) { |
| if (ShouldSkipTest(state, event)) { |
| return; |
| } |
| |
| debugging_log_ << "\nTest transition from state " << GetStateString(state) |
| << (IsWindowFullscreenStateChangedReentrant() |
| ? " with reentrant calls.\n" |
| : ".\n"); |
| |
| // Spaced out text to line up with columns printed in InvokeEvent(). |
| debugging_log_ << "First, from " |
| << GetStateString(state_) << "\n"; |
| ASSERT_NO_FATAL_FAILURE(TransitionToState(state)) << GetAndClearDebugLog(); |
| |
| debugging_log_ << " Then,\n"; |
| ASSERT_TRUE(InvokeEvent(event)) << GetAndClearDebugLog(); |
| } |
| |
| void FullscreenControllerStateTest::VerifyWindowStateExpectations( |
| FullscreenForBrowserExpectation fullscreen_for_browser, |
| FullscreenForTabExpectation fullscreen_for_tab) { |
| if (fullscreen_for_browser != FULLSCREEN_FOR_BROWSER_NO_EXPECTATION) { |
| EXPECT_EQ(GetFullscreenController()->IsFullscreenForBrowser(), |
| !!fullscreen_for_browser) |
| << GetAndClearDebugLog(); |
| } |
| if (fullscreen_for_tab != FULLSCREEN_FOR_TAB_NO_EXPECTATION) { |
| EXPECT_EQ(GetFullscreenController()->IsWindowFullscreenForTabOrPending(), |
| !!fullscreen_for_tab) |
| << GetAndClearDebugLog(); |
| if (auto* tab = GetFullscreenController()->exclusive_access_tab()) { |
| const content::FullscreenState state = |
| GetFullscreenController()->GetFullscreenState(tab); |
| EXPECT_EQ( |
| state.target_mode == content::FullscreenMode::kContent || |
| state.target_mode == content::FullscreenMode::kPseudoContent, |
| !!fullscreen_for_tab) |
| << GetAndClearDebugLog(); |
| } |
| } |
| } |
| |
| void FullscreenControllerStateTest::TearDown() {} |
| |
| FullscreenController* FullscreenControllerStateTest::GetFullscreenController() { |
| return GetBrowser() |
| ->GetFeatures() |
| .exclusive_access_manager() |
| ->fullscreen_controller(); |
| } |
| |
| std::string FullscreenControllerStateTest::GetTransitionTableAsString() const { |
| std::ostringstream output; |
| output << "kTransitionTableData[NUM_STATES = " << NUM_STATES |
| << "][NUM_EVENTS = " << NUM_EVENTS << "] =\n"; |
| for (int state_int = 0; state_int < NUM_STATES; ++state_int) { |
| State state = static_cast<State>(state_int); |
| output << " { // " << GetStateString(state) << ":\n"; |
| for (int event_int = 0; event_int < NUM_EVENTS; ++event_int) { |
| Event event = static_cast<Event>(event_int); |
| output << " " << std::left << std::setw(kMaxStateNameLength + 1) |
| << std::string( |
| GetStateString(kTransitionTableData[state][event])) + |
| "," |
| << "// Event " << GetEventString(event) << "\n"; |
| } |
| output << " },\n"; |
| } |
| output << " };\n"; |
| return output.str(); |
| } |
| |
| std::string FullscreenControllerStateTest::GetStateTransitionsAsString() const { |
| std::ostringstream output; |
| output << "state_transitions_[NUM_STATES = " << NUM_STATES |
| << "][NUM_STATES = " << NUM_STATES << "] =\n"; |
| for (int state1_int = 0; state1_int < NUM_STATES; ++state1_int) { |
| State state1 = static_cast<State>(state1_int); |
| output << "{ // " << GetStateString(state1) << ":\n"; |
| for (int state2_int = 0; state2_int < NUM_STATES; ++state2_int) { |
| State state2 = static_cast<State>(state2_int); |
| const StateTransitionInfo& info = state_transitions_[state1][state2]; |
| output << " { " << std::left << std::setw(kMaxStateNameLength + 1) |
| << std::string(GetEventString(info.event)) + "," << std::left |
| << std::setw(kMaxStateNameLength + 1) |
| << std::string(GetStateString(info.state)) + "," << std::right |
| << std::setw(2) << info.distance << " }, // " |
| << GetStateString(state2) << "\n"; |
| } |
| output << "},\n"; |
| } |
| output << "};"; |
| return output.str(); |
| } |