| // Copyright 2014 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "ui/display/manager/display_configurator.h" | 
 |  | 
 | #include <stddef.h> | 
 | #include <stdint.h> | 
 |  | 
 | #include "base/command_line.h" | 
 | #include "base/macros.h" | 
 | #include "base/message_loop/message_loop.h" | 
 | #include "base/run_loop.h" | 
 | #include "chromeos/chromeos_switches.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 | #include "ui/display/manager/fake_display_snapshot.h" | 
 | #include "ui/display/manager/test/action_logger_util.h" | 
 | #include "ui/display/manager/test/test_native_display_delegate.h" | 
 | #include "ui/display/util/display_util.h" | 
 |  | 
 | namespace display { | 
 | namespace test { | 
 |  | 
 | namespace { | 
 |  | 
 | constexpr int64_t kDisplayIds[3] = {123, 456, 789}; | 
 |  | 
 | std::unique_ptr<DisplayMode> MakeDisplayMode(int width, | 
 |                                              int height, | 
 |                                              bool is_interlaced, | 
 |                                              float refresh_rate) { | 
 |   return std::make_unique<DisplayMode>(gfx::Size(width, height), is_interlaced, | 
 |                                        refresh_rate); | 
 | } | 
 |  | 
 | enum CallbackResult { | 
 |   CALLBACK_FAILURE, | 
 |   CALLBACK_SUCCESS, | 
 |   CALLBACK_NOT_CALLED, | 
 | }; | 
 |  | 
 | // Expected immediate configurations should be done without any delays. | 
 | constexpr base::TimeDelta kNoDelay = base::TimeDelta::FromMilliseconds(0); | 
 |  | 
 | // The expected configuration delay when resuming from suspend while in 2+ | 
 | // display mode. | 
 | constexpr base::TimeDelta kLongDelay = base::TimeDelta::FromMilliseconds( | 
 |     DisplayConfigurator::kResumeConfigureMultiDisplayDelayMs); | 
 |  | 
 | class TestObserver : public DisplayConfigurator::Observer { | 
 |  public: | 
 |   explicit TestObserver(DisplayConfigurator* configurator) | 
 |       : configurator_(configurator) { | 
 |     Reset(); | 
 |     configurator_->AddObserver(this); | 
 |   } | 
 |   ~TestObserver() override { configurator_->RemoveObserver(this); } | 
 |  | 
 |   int num_changes() const { return num_changes_; } | 
 |   int num_failures() const { return num_failures_; } | 
 |   const DisplayConfigurator::DisplayStateList& latest_outputs() const { | 
 |     return latest_outputs_; | 
 |   } | 
 |   MultipleDisplayState latest_failed_state() const { | 
 |     return latest_failed_state_; | 
 |   } | 
 |  | 
 |   void Reset() { | 
 |     num_changes_ = 0; | 
 |     num_failures_ = 0; | 
 |     latest_outputs_.clear(); | 
 |     latest_failed_state_ = MULTIPLE_DISPLAY_STATE_INVALID; | 
 |   } | 
 |  | 
 |   // DisplayConfigurator::Observer overrides: | 
 |   void OnDisplayModeChanged( | 
 |       const DisplayConfigurator::DisplayStateList& outputs) override { | 
 |     num_changes_++; | 
 |     latest_outputs_ = outputs; | 
 |   } | 
 |  | 
 |   void OnDisplayModeChangeFailed( | 
 |       const DisplayConfigurator::DisplayStateList& outputs, | 
 |       MultipleDisplayState failed_new_state) override { | 
 |     num_failures_++; | 
 |     latest_failed_state_ = failed_new_state; | 
 |   } | 
 |  | 
 |  private: | 
 |   DisplayConfigurator* configurator_;  // Not owned. | 
 |  | 
 |   // Number of times that OnDisplayMode*() has been called. | 
 |   int num_changes_; | 
 |   int num_failures_; | 
 |  | 
 |   // Parameters most recently passed to OnDisplayMode*(). | 
 |   DisplayConfigurator::DisplayStateList latest_outputs_; | 
 |   MultipleDisplayState latest_failed_state_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(TestObserver); | 
 | }; | 
 |  | 
 | class TestStateController : public DisplayConfigurator::StateController { | 
 |  public: | 
 |   TestStateController() : state_(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED) {} | 
 |   ~TestStateController() override {} | 
 |  | 
 |   void set_state(MultipleDisplayState state) { state_ = state; } | 
 |  | 
 |   // DisplayConfigurator::StateController overrides: | 
 |   MultipleDisplayState GetStateForDisplayIds( | 
 |       const DisplayConfigurator::DisplayStateList& outputs) override { | 
 |     return state_; | 
 |   } | 
 |   bool GetResolutionForDisplayId(int64_t display_id, | 
 |                                  gfx::Size* size) const override { | 
 |     return false; | 
 |   } | 
 |  | 
 |  private: | 
 |   MultipleDisplayState state_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(TestStateController); | 
 | }; | 
 |  | 
 | class TestMirroringController | 
 |     : public DisplayConfigurator::SoftwareMirroringController { | 
 |  public: | 
 |   TestMirroringController() : software_mirroring_enabled_(false) {} | 
 |   ~TestMirroringController() override {} | 
 |  | 
 |   void SetSoftwareMirroring(bool enabled) override { | 
 |     software_mirroring_enabled_ = enabled; | 
 |   } | 
 |  | 
 |   bool SoftwareMirroringEnabled() const override { | 
 |     return software_mirroring_enabled_; | 
 |   } | 
 |  | 
 |   bool IsSoftwareMirroringEnforced() const override { return false; } | 
 |  | 
 |  private: | 
 |   bool software_mirroring_enabled_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(TestMirroringController); | 
 | }; | 
 |  | 
 | // Abstracts waiting for the display configuration to be completed and getting | 
 | // the time it took to complete. | 
 | class ConfigurationWaiter { | 
 |  public: | 
 |   explicit ConfigurationWaiter(DisplayConfigurator::TestApi* test_api) | 
 |       : on_configured_callback_(base::Bind(&ConfigurationWaiter::OnConfigured, | 
 |                                            base::Unretained(this))), | 
 |         test_api_(test_api), | 
 |         callback_result_(CALLBACK_NOT_CALLED) {} | 
 |  | 
 |   ~ConfigurationWaiter() = default; | 
 |  | 
 |   const DisplayConfigurator::ConfigurationCallback& on_configuration_callback() | 
 |       const { | 
 |     return on_configured_callback_; | 
 |   } | 
 |  | 
 |   CallbackResult callback_result() const { return callback_result_; } | 
 |  | 
 |   void Reset() { callback_result_ = CALLBACK_NOT_CALLED; } | 
 |  | 
 |   // Simulates waiting for the next configuration. If an async task is pending, | 
 |   // runs it and returns base::TimeDelta(). Otherwise, triggers the | 
 |   // configuration timer and returns its delay. If the timer wasn't running, | 
 |   // returns base::TimeDelta::Max(). | 
 |   base::TimeDelta Wait() WARN_UNUSED_RESULT { | 
 |     base::RunLoop().RunUntilIdle(); | 
 |     if (callback_result_ != CALLBACK_NOT_CALLED) | 
 |       return base::TimeDelta(); | 
 |  | 
 |     const base::TimeDelta delay = test_api_->GetConfigureDelay(); | 
 |     if (!test_api_->TriggerConfigureTimeout()) | 
 |       return base::TimeDelta::Max(); | 
 |  | 
 |     return delay; | 
 |   } | 
 |  | 
 |  private: | 
 |   void OnConfigured(bool status) { | 
 |     CHECK_EQ(callback_result_, CALLBACK_NOT_CALLED); | 
 |     callback_result_ = status ? CALLBACK_SUCCESS : CALLBACK_FAILURE; | 
 |   } | 
 |  | 
 |   // Passed with configuration requests to run OnConfigured(). | 
 |   const DisplayConfigurator::ConfigurationCallback on_configured_callback_; | 
 |  | 
 |   DisplayConfigurator::TestApi* test_api_;  // Not owned. | 
 |  | 
 |   // The status of the display configuration. | 
 |   CallbackResult callback_result_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(ConfigurationWaiter); | 
 | }; | 
 |  | 
 | class DisplayConfiguratorTest : public testing::Test { | 
 |  public: | 
 |   DisplayConfiguratorTest() | 
 |       : small_mode_(gfx::Size(1366, 768), false, 60.0f), | 
 |         big_mode_(gfx::Size(2560, 1600), false, 60.0f), | 
 |         observer_(&configurator_), | 
 |         test_api_(&configurator_), | 
 |         config_waiter_(&test_api_), | 
 |         set_content_protection_status_(false), | 
 |         set_content_protection_call_count_(0), | 
 |         query_content_protection_response_success_(false), | 
 |         query_content_protection_response_link_mask_(0), | 
 |         query_content_protection_response_protection_mask_(0), | 
 |         query_content_protection_call_count_(0), | 
 |         display_control_result_(CALLBACK_NOT_CALLED) {} | 
 |   ~DisplayConfiguratorTest() override {} | 
 |  | 
 |   void SetUp() override { | 
 |     log_.reset(new ActionLogger()); | 
 |  | 
 |     // Force system compositor mode to simulate on-device configurator behavior. | 
 |     base::CommandLine::ForCurrentProcess()->AppendSwitch( | 
 |         chromeos::switches::kForceSystemCompositorMode); | 
 |  | 
 |     native_display_delegate_ = new TestNativeDisplayDelegate(log_.get()); | 
 |     configurator_.SetDelegateForTesting( | 
 |         std::unique_ptr<NativeDisplayDelegate>(native_display_delegate_)); | 
 |  | 
 |     configurator_.set_state_controller(&state_controller_); | 
 |     configurator_.set_mirroring_controller(&mirroring_controller_); | 
 |  | 
 |     outputs_[0] = FakeDisplaySnapshot::Builder() | 
 |                       .SetId(kDisplayIds[0]) | 
 |                       .SetNativeMode(small_mode_.Clone()) | 
 |                       .SetCurrentMode(small_mode_.Clone()) | 
 |                       .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL) | 
 |                       .SetIsAspectPerservingScaling(true) | 
 |                       .Build(); | 
 |  | 
 |     outputs_[1] = FakeDisplaySnapshot::Builder() | 
 |                       .SetId(kDisplayIds[1]) | 
 |                       .SetNativeMode(big_mode_.Clone()) | 
 |                       .SetCurrentMode(big_mode_.Clone()) | 
 |                       .AddMode(small_mode_.Clone()) | 
 |                       .SetType(DISPLAY_CONNECTION_TYPE_HDMI) | 
 |                       .SetIsAspectPerservingScaling(true) | 
 |                       .Build(); | 
 |  | 
 |     outputs_[2] = FakeDisplaySnapshot::Builder() | 
 |                       .SetId(kDisplayIds[2]) | 
 |                       .SetNativeMode(small_mode_.Clone()) | 
 |                       .SetCurrentMode(small_mode_.Clone()) | 
 |                       .SetType(DISPLAY_CONNECTION_TYPE_HDMI) | 
 |                       .SetIsAspectPerservingScaling(true) | 
 |                       .Build(); | 
 |  | 
 |     UpdateOutputs(2, false); | 
 |   } | 
 |  | 
 |   void OnDisplayControlUpdated(bool status) { | 
 |     display_control_result_ = (status ? CALLBACK_SUCCESS : CALLBACK_FAILURE); | 
 |   } | 
 |  | 
 |   void SetContentProtectionCallback(bool status) { | 
 |     set_content_protection_status_ = status; | 
 |     set_content_protection_call_count_++; | 
 |   } | 
 |  | 
 |   void QueryContentProtectionCallback(bool success, | 
 |                                       uint32_t link_mask, | 
 |                                       uint32_t protection_mask) { | 
 |     query_content_protection_response_success_ = success; | 
 |     query_content_protection_response_link_mask_ = link_mask; | 
 |     query_content_protection_response_protection_mask_ = protection_mask; | 
 |     query_content_protection_call_count_++; | 
 |   } | 
 |  | 
 |   // Predefined modes that can be used by outputs. | 
 |   const DisplayMode small_mode_; | 
 |   const DisplayMode big_mode_; | 
 |  | 
 |  protected: | 
 |   // Configures |native_display_delegate_| to return the first |num_outputs| | 
 |   // entries from | 
 |   // |outputs_|. If |send_events| is true, also sends screen-change and | 
 |   // output-change events to |configurator_| and triggers the configure | 
 |   // timeout if one was scheduled. | 
 |   void UpdateOutputs(size_t num_outputs, bool send_events) { | 
 |     ASSERT_LE(num_outputs, arraysize(outputs_)); | 
 |     std::vector<DisplaySnapshot*> outputs; | 
 |     for (size_t i = 0; i < num_outputs; ++i) | 
 |       outputs.push_back(outputs_[i].get()); | 
 |     native_display_delegate_->set_outputs(outputs); | 
 |  | 
 |     if (send_events) { | 
 |       configurator_.OnConfigurationChanged(); | 
 |       EXPECT_TRUE(test_api_.TriggerConfigureTimeout()); | 
 |     } | 
 |   } | 
 |  | 
 |   void Init(bool panel_fitting_enabled) { | 
 |     configurator_.Init(nullptr, panel_fitting_enabled); | 
 |   } | 
 |  | 
 |   // Initializes |configurator_| with a single internal display. | 
 |   void InitWithSingleOutput() { | 
 |     UpdateOutputs(1, false); | 
 |     EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |     configurator_.Init(nullptr, false); | 
 |  | 
 |     EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |     configurator_.ForceInitialConfigure(); | 
 |     EXPECT_EQ( | 
 |         JoinActions( | 
 |             kInit, | 
 |             GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |             nullptr), | 
 |         log_->GetActionsAndClear()); | 
 |   } | 
 |  | 
 |   CallbackResult PopDisplayControlResult() { | 
 |     CallbackResult result = display_control_result_; | 
 |     display_control_result_ = CALLBACK_NOT_CALLED; | 
 |     return result; | 
 |   } | 
 |  | 
 |   base::MessageLoop message_loop_; | 
 |   TestStateController state_controller_; | 
 |   TestMirroringController mirroring_controller_; | 
 |   DisplayConfigurator configurator_; | 
 |   TestObserver observer_; | 
 |   std::unique_ptr<ActionLogger> log_; | 
 |   TestNativeDisplayDelegate* native_display_delegate_;  // not owned | 
 |   DisplayConfigurator::TestApi test_api_; | 
 |   ConfigurationWaiter config_waiter_; | 
 |   bool set_content_protection_status_; | 
 |   int set_content_protection_call_count_; | 
 |   bool query_content_protection_response_success_; | 
 |   uint32_t query_content_protection_response_link_mask_; | 
 |   uint32_t query_content_protection_response_protection_mask_; | 
 |   int query_content_protection_call_count_; | 
 |  | 
 |   std::unique_ptr<DisplaySnapshot> outputs_[3]; | 
 |  | 
 |   CallbackResult display_control_result_; | 
 |  | 
 |  private: | 
 |   DISALLOW_COPY_AND_ASSIGN(DisplayConfiguratorTest); | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, FindDisplayModeMatchingSize) { | 
 |   std::unique_ptr<DisplaySnapshot> output = | 
 |       FakeDisplaySnapshot::Builder() | 
 |           .SetId(kDisplayIds[0]) | 
 |           .AddMode(MakeDisplayMode(1920, 1200, false, 60.0)) | 
 |           .SetNativeMode(MakeDisplayMode(1920, 1200, false, 50.0)) | 
 |           // Different rates. | 
 |           .AddMode(MakeDisplayMode(1920, 1080, false, 30.0)) | 
 |           .AddMode(MakeDisplayMode(1920, 1080, false, 50.0)) | 
 |           .AddMode(MakeDisplayMode(1920, 1080, false, 40.0)) | 
 |           .AddMode(MakeDisplayMode(1920, 1080, false, 0.0)) | 
 |           // Interlaced vs non-interlaced. | 
 |           .AddMode(MakeDisplayMode(1280, 720, true, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1280, 720, false, 40.0)) | 
 |           // Interlaced only. | 
 |           .AddMode(MakeDisplayMode(1024, 768, true, 0.0)) | 
 |           .AddMode(MakeDisplayMode(1024, 768, true, 40.0)) | 
 |           .AddMode(MakeDisplayMode(1024, 768, true, 60.0)) | 
 |           // Mixed. | 
 |           .AddMode(MakeDisplayMode(1024, 600, true, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1024, 600, false, 40.0)) | 
 |           .AddMode(MakeDisplayMode(1024, 600, false, 50.0)) | 
 |           // Just one interlaced mode. | 
 |           .AddMode(MakeDisplayMode(640, 480, true, 60.0)) | 
 |           // Refresh rate not available. | 
 |           .AddMode(MakeDisplayMode(320, 200, false, 0.0)) | 
 |           .Build(); | 
 |  | 
 |   const std::vector<std::unique_ptr<const DisplayMode>>& modes = | 
 |       output->modes(); | 
 |  | 
 |   // Should pick native over highest refresh rate. | 
 |   EXPECT_EQ(modes[1].get(), DisplayConfigurator::FindDisplayModeMatchingSize( | 
 |                                 *output, gfx::Size(1920, 1200))); | 
 |  | 
 |   // Should pick highest refresh rate. | 
 |   EXPECT_EQ(modes[3].get(), DisplayConfigurator::FindDisplayModeMatchingSize( | 
 |                                 *output, gfx::Size(1920, 1080))); | 
 |  | 
 |   // Should pick non-interlaced mode. | 
 |   EXPECT_EQ(modes[7].get(), DisplayConfigurator::FindDisplayModeMatchingSize( | 
 |                                 *output, gfx::Size(1280, 720))); | 
 |  | 
 |   // Interlaced only. Should pick one with the highest refresh rate in | 
 |   // interlaced mode. | 
 |   EXPECT_EQ(modes[10].get(), DisplayConfigurator::FindDisplayModeMatchingSize( | 
 |                                  *output, gfx::Size(1024, 768))); | 
 |  | 
 |   // Mixed: Should pick one with the highest refresh rate in | 
 |   // interlaced mode. | 
 |   EXPECT_EQ(modes[13].get(), DisplayConfigurator::FindDisplayModeMatchingSize( | 
 |                                  *output, gfx::Size(1024, 600))); | 
 |  | 
 |   // Just one interlaced mode. | 
 |   EXPECT_EQ(modes[14].get(), DisplayConfigurator::FindDisplayModeMatchingSize( | 
 |                                  *output, gfx::Size(640, 480))); | 
 |  | 
 |   // Refresh rate not available. | 
 |   EXPECT_EQ(modes[15].get(), DisplayConfigurator::FindDisplayModeMatchingSize( | 
 |                                  *output, gfx::Size(320, 200))); | 
 |  | 
 |   // No mode found. | 
 |   EXPECT_EQ(nullptr, DisplayConfigurator::FindDisplayModeMatchingSize( | 
 |                          *output, gfx::Size(1440, 900))); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, ConnectSecondOutput) { | 
 |   InitWithSingleOutput(); | 
 |  | 
 |   // Connect a second output and check that the configurator enters | 
 |   // extended mode. | 
 |   observer_.Reset(); | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); | 
 |   UpdateOutputs(2, true); | 
 |  | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |  | 
 |   observer_.Reset(); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |  | 
 |   // Disconnect the second output. | 
 |   observer_.Reset(); | 
 |   UpdateOutputs(1, true); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |  | 
 |   // Get rid of shared modes to force software mirroring. | 
 |   outputs_[1] = FakeDisplaySnapshot::Builder() | 
 |                     .SetId(kDisplayIds[1]) | 
 |                     .SetNativeMode(big_mode_.Clone()) | 
 |                     .SetCurrentMode(big_mode_.Clone()) | 
 |                     .SetType(DISPLAY_CONNECTION_TYPE_HDMI) | 
 |                     .SetIsAspectPerservingScaling(true) | 
 |                     .Build(); | 
 |  | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); | 
 |   UpdateOutputs(2, true); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |  | 
 |   observer_.Reset(); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, | 
 |             configurator_.display_state()); | 
 |   EXPECT_TRUE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |  | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |  | 
 |   // Setting MULTIPLE_DISPLAY_STATE_DUAL_MIRROR should try to reconfigure. | 
 |   observer_.Reset(); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |   EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |  | 
 |   // Set back to software mirror mode. | 
 |   observer_.Reset(); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, | 
 |             configurator_.display_state()); | 
 |   EXPECT_TRUE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |  | 
 |   // Disconnect the second output. | 
 |   observer_.Reset(); | 
 |   UpdateOutputs(1, true); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, SetDisplayPower) { | 
 |   InitWithSingleOutput(); | 
 |  | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |   observer_.Reset(); | 
 |   UpdateOutputs(2, true); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |  | 
 |   // Turning off the internal display should switch the external display to | 
 |   // its native mode. | 
 |   observer_.Reset(); | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower( | 
 |       chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON, | 
 |       DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |       config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_SINGLE, configurator_.display_state()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |  | 
 |   // When all displays are turned off, the framebuffer should switch back | 
 |   // to the mirrored size. | 
 |   observer_.Reset(); | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_OFF, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(JoinActions( | 
 |                 GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |                 GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, 0)).c_str(), | 
 |                 nullptr), | 
 |             log_->GetActionsAndClear()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state()); | 
 |   EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |  | 
 |   // Turn all displays on and check that mirroring is still used. | 
 |   observer_.Reset(); | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state()); | 
 |   EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |  | 
 |   // Get rid of shared modes to force software mirroring. | 
 |   outputs_[1] = FakeDisplaySnapshot::Builder() | 
 |                     .SetId(kDisplayIds[1]) | 
 |                     .SetNativeMode(big_mode_.Clone()) | 
 |                     .SetCurrentMode(big_mode_.Clone()) | 
 |                     .SetType(DISPLAY_CONNECTION_TYPE_HDMI) | 
 |                     .SetIsAspectPerservingScaling(true) | 
 |                     .Build(); | 
 |  | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |   observer_.Reset(); | 
 |   UpdateOutputs(2, true); | 
 |  | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, | 
 |             configurator_.display_state()); | 
 |   EXPECT_TRUE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |  | 
 |   // Turning off the internal display should switch the external display to | 
 |   // its native mode. | 
 |   observer_.Reset(); | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower( | 
 |       chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON, | 
 |       DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |       config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_SINGLE, configurator_.display_state()); | 
 |   EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |  | 
 |   // When all displays are turned off, the framebuffer should switch back | 
 |   // to the extended + software mirroring. | 
 |   observer_.Reset(); | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_OFF, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], nullptr, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, | 
 |             configurator_.display_state()); | 
 |   EXPECT_TRUE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |  | 
 |   // Turn all displays on and check that mirroring is still used. | 
 |   observer_.Reset(); | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, | 
 |             configurator_.display_state()); | 
 |   EXPECT_TRUE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, SuspendAndResume) { | 
 |   InitWithSingleOutput(); | 
 |  | 
 |   // Set the initial power state to on. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |  | 
 |   // No preparation is needed before suspending when the display is already | 
 |   // on.  The configurator should still reprobe on resume in case a display | 
 |   // was connected while suspended. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SuspendDisplays(config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   // No resume delay in single display mode. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.ResumeDisplays(); | 
 |   // The timer should not be running. | 
 |   EXPECT_EQ(base::TimeDelta::Max(), config_waiter_.Wait()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   // Now turn the display off before suspending and check that the | 
 |   // configurator turns it back on and syncs with the server. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_OFF, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SuspendDisplays(config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   config_waiter_.Reset(); | 
 |   configurator_.ResumeDisplays(); | 
 |   // The timer should not be running. | 
 |   EXPECT_EQ(base::TimeDelta::Max(), config_waiter_.Wait()); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |   UpdateOutputs(2, true); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_OFF, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state()); | 
 |   EXPECT_EQ(JoinActions( | 
 |                 GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |                 GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, 0)).c_str(), | 
 |                 nullptr), | 
 |             log_->GetActionsAndClear()); | 
 |  | 
 |   // No delay in suspend. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SuspendDisplays(config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_OFF, | 
 |             configurator_.current_power_state()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state()); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   // If a display is disconnected while suspended, the configurator should | 
 |   // pick up the change and only turn on the internal display. The should be | 
 |   // a longer configuration delay when we set the displays back to on. | 
 |   UpdateOutputs(1, false); | 
 |   config_waiter_.Reset(); | 
 |   configurator_.ResumeDisplays(); | 
 |   // Since we were in dual display mirror mode before suspend, the timer should | 
 |   // be running with kMinLongDelayMs. | 
 |   EXPECT_EQ(kLongDelay, test_api_.GetConfigureDelay()); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(CALLBACK_NOT_CALLED, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(kLongDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, Headless) { | 
 |   UpdateOutputs(0, false); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |   Init(false); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |   configurator_.ForceInitialConfigure(); | 
 |   EXPECT_EQ(JoinActions(kInit, nullptr), log_->GetActionsAndClear()); | 
 |  | 
 |   // Not much should happen when the display power state is changed while | 
 |   // no displays are connected. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_OFF, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   // Connect an external display and check that it's configured correctly. | 
 |   outputs_[0] = FakeDisplaySnapshot::Builder() | 
 |                     .SetId(kDisplayIds[0]) | 
 |                     .SetNativeMode(big_mode_.Clone()) | 
 |                     .SetCurrentMode(big_mode_.Clone()) | 
 |                     .AddMode(small_mode_.Clone()) | 
 |                     .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL) | 
 |                     .SetIsAspectPerservingScaling(true) | 
 |                     .Build(); | 
 |  | 
 |   UpdateOutputs(1, true); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &big_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   UpdateOutputs(0, true); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, StartWithTwoOutputs) { | 
 |   UpdateOutputs(2, false); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |   Init(false); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |   configurator_.ForceInitialConfigure(); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           kInit, | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, InvalidMultipleDisplayStates) { | 
 |   UpdateOutputs(0, false); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |   Init(false); | 
 |   configurator_.ForceInitialConfigure(); | 
 |   observer_.Reset(); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_HEADLESS); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |   EXPECT_EQ(0, observer_.num_failures()); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_SINGLE); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |   EXPECT_EQ(3, observer_.num_failures()); | 
 |  | 
 |   UpdateOutputs(1, true); | 
 |   observer_.Reset(); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_HEADLESS); | 
 |   EXPECT_EQ(0, observer_.num_changes()); | 
 |   EXPECT_EQ(1, observer_.num_failures()); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_SINGLE); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |   EXPECT_EQ(1, observer_.num_failures()); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |   EXPECT_EQ(3, observer_.num_failures()); | 
 |  | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); | 
 |   UpdateOutputs(2, true); | 
 |   observer_.Reset(); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_HEADLESS); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_SINGLE); | 
 |   EXPECT_EQ(0, observer_.num_changes()); | 
 |   EXPECT_EQ(2, observer_.num_failures()); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); | 
 |   EXPECT_EQ(2, observer_.num_changes()); | 
 |   EXPECT_EQ(2, observer_.num_failures()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, GetMultipleDisplayStateForMirroredDisplays) { | 
 |   UpdateOutputs(2, false); | 
 |   Init(false); | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |   configurator_.ForceInitialConfigure(); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, UpdateCachedOutputsEvenAfterFailure) { | 
 |   InitWithSingleOutput(); | 
 |   const DisplayConfigurator::DisplayStateList& cached = | 
 |       configurator_.cached_displays(); | 
 |   ASSERT_EQ(static_cast<size_t>(1), cached.size()); | 
 |   EXPECT_EQ(outputs_[0]->current_mode(), cached[0]->current_mode()); | 
 |  | 
 |   // After connecting a second output, check that it shows up in | 
 |   // |cached_displays_| even if an invalid state is requested. | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_SINGLE); | 
 |   UpdateOutputs(2, true); | 
 |   ASSERT_EQ(static_cast<size_t>(2), cached.size()); | 
 |   EXPECT_EQ(outputs_[0]->current_mode(), cached[0]->current_mode()); | 
 |   EXPECT_EQ(outputs_[1]->current_mode(), cached[1]->current_mode()); | 
 | } | 
 |  | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, ContentProtection) { | 
 |   Init(false); | 
 |   configurator_.ForceInitialConfigure(); | 
 |   EXPECT_NE(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   uint64_t id = configurator_.RegisterContentProtectionClient(); | 
 |   EXPECT_NE(0u, id); | 
 |  | 
 |   // One output. | 
 |   UpdateOutputs(1, true); | 
 |   EXPECT_NE(kNoActions, log_->GetActionsAndClear()); | 
 |   configurator_.QueryContentProtectionStatus( | 
 |       id, outputs_[0]->display_id(), | 
 |       base::Bind(&DisplayConfiguratorTest::QueryContentProtectionCallback, | 
 |                  base::Unretained(this))); | 
 |   EXPECT_EQ(1, query_content_protection_call_count_); | 
 |   EXPECT_TRUE(query_content_protection_response_success_); | 
 |   EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_INTERNAL), | 
 |             query_content_protection_response_link_mask_); | 
 |   EXPECT_EQ(static_cast<uint32_t>(CONTENT_PROTECTION_METHOD_NONE), | 
 |             query_content_protection_response_protection_mask_); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   // Two outputs. | 
 |   UpdateOutputs(2, true); | 
 |   EXPECT_NE(kNoActions, log_->GetActionsAndClear()); | 
 |   configurator_.QueryContentProtectionStatus( | 
 |       id, outputs_[1]->display_id(), | 
 |       base::Bind(&DisplayConfiguratorTest::QueryContentProtectionCallback, | 
 |                  base::Unretained(this))); | 
 |   EXPECT_EQ(2, query_content_protection_call_count_); | 
 |   EXPECT_TRUE(query_content_protection_response_success_); | 
 |   EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), | 
 |             query_content_protection_response_link_mask_); | 
 |   EXPECT_EQ(static_cast<uint32_t>(CONTENT_PROTECTION_METHOD_NONE), | 
 |             query_content_protection_response_protection_mask_); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   configurator_.SetContentProtection( | 
 |       id, outputs_[1]->display_id(), CONTENT_PROTECTION_METHOD_HDCP, | 
 |       base::Bind(&DisplayConfiguratorTest::SetContentProtectionCallback, | 
 |                  base::Unretained(this))); | 
 |   EXPECT_EQ(1, set_content_protection_call_count_); | 
 |   EXPECT_TRUE(set_content_protection_status_); | 
 |   EXPECT_EQ(GetSetHDCPStateAction(*outputs_[1], HDCP_STATE_DESIRED), | 
 |             log_->GetActionsAndClear()); | 
 |  | 
 |   // Enable protection. | 
 |   native_display_delegate_->set_hdcp_state(HDCP_STATE_ENABLED); | 
 |   configurator_.QueryContentProtectionStatus( | 
 |       id, outputs_[1]->display_id(), | 
 |       base::Bind(&DisplayConfiguratorTest::QueryContentProtectionCallback, | 
 |                  base::Unretained(this))); | 
 |   EXPECT_EQ(3, query_content_protection_call_count_); | 
 |   EXPECT_TRUE(query_content_protection_response_success_); | 
 |   EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), | 
 |             query_content_protection_response_link_mask_); | 
 |   EXPECT_EQ(static_cast<uint32_t>(CONTENT_PROTECTION_METHOD_HDCP), | 
 |             query_content_protection_response_protection_mask_); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   // Protections should be disabled after unregister. | 
 |   configurator_.UnregisterContentProtectionClient(id); | 
 |   EXPECT_EQ(GetSetHDCPStateAction(*outputs_[1], HDCP_STATE_UNDESIRED), | 
 |             log_->GetActionsAndClear()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, DoNotConfigureWithSuspendedDisplays) { | 
 |   InitWithSingleOutput(); | 
 |  | 
 |   // The DisplayConfigurator may occasionally receive OnConfigurationChanged() | 
 |   // after the displays have been suspended.  This event should be ignored since | 
 |   // the DisplayConfigurator will force a probe and reconfiguration of displays | 
 |   // at resume time. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SuspendDisplays(config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   // The configuration timer should not be started when the displays | 
 |   // are suspended. | 
 |   configurator_.OnConfigurationChanged(); | 
 |   EXPECT_FALSE(test_api_.TriggerConfigureTimeout()); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   // Calls to SetDisplayPower should do nothing if the power state doesn't | 
 |   // change. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_OFF, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   UpdateOutputs(2, false); | 
 |   configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   // The DisplayConfigurator should do nothing at resume time if there is no | 
 |   // state change. | 
 |   config_waiter_.Reset(); | 
 |   UpdateOutputs(1, false); | 
 |   configurator_.ResumeDisplays(); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   // If a configuration task is pending when the displays are suspended, that | 
 |   // task should not run either and the timer should be stopped. The displays | 
 |   // should be turned off by suspend. | 
 |   configurator_.OnConfigurationChanged(); | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SuspendDisplays(config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   EXPECT_FALSE(test_api_.TriggerConfigureTimeout()); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   config_waiter_.Reset(); | 
 |   configurator_.ResumeDisplays(); | 
 |   // The timer should not be running. | 
 |   EXPECT_EQ(base::TimeDelta::Max(), config_waiter_.Wait()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, ContentProtectionTwoClients) { | 
 |   uint64_t client1 = configurator_.RegisterContentProtectionClient(); | 
 |   uint64_t client2 = configurator_.RegisterContentProtectionClient(); | 
 |   EXPECT_NE(client1, client2); | 
 |  | 
 |   Init(false); | 
 |   configurator_.ForceInitialConfigure(); | 
 |   UpdateOutputs(2, true); | 
 |   EXPECT_NE(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   // Clients never know state enableness for methods that they didn't request. | 
 |   configurator_.SetContentProtection( | 
 |       client1, outputs_[1]->display_id(), CONTENT_PROTECTION_METHOD_HDCP, | 
 |       base::Bind(&DisplayConfiguratorTest::SetContentProtectionCallback, | 
 |                  base::Unretained(this))); | 
 |   EXPECT_EQ(1, set_content_protection_call_count_); | 
 |   EXPECT_TRUE(set_content_protection_status_); | 
 |   EXPECT_EQ(GetSetHDCPStateAction(*outputs_[1], HDCP_STATE_DESIRED).c_str(), | 
 |             log_->GetActionsAndClear()); | 
 |   native_display_delegate_->set_hdcp_state(HDCP_STATE_ENABLED); | 
 |  | 
 |   configurator_.QueryContentProtectionStatus( | 
 |       client1, outputs_[1]->display_id(), | 
 |       base::Bind(&DisplayConfiguratorTest::QueryContentProtectionCallback, | 
 |                  base::Unretained(this))); | 
 |   EXPECT_EQ(1, query_content_protection_call_count_); | 
 |   EXPECT_TRUE(query_content_protection_response_success_); | 
 |   EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), | 
 |             query_content_protection_response_link_mask_); | 
 |   EXPECT_EQ(CONTENT_PROTECTION_METHOD_HDCP, | 
 |             query_content_protection_response_protection_mask_); | 
 |  | 
 |   configurator_.QueryContentProtectionStatus( | 
 |       client2, outputs_[1]->display_id(), | 
 |       base::Bind(&DisplayConfiguratorTest::QueryContentProtectionCallback, | 
 |                  base::Unretained(this))); | 
 |   EXPECT_EQ(2, query_content_protection_call_count_); | 
 |   EXPECT_TRUE(query_content_protection_response_success_); | 
 |   EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), | 
 |             query_content_protection_response_link_mask_); | 
 |   EXPECT_EQ(CONTENT_PROTECTION_METHOD_NONE, | 
 |             query_content_protection_response_protection_mask_); | 
 |  | 
 |   // Protections will be disabled only if no more clients request them. | 
 |   configurator_.SetContentProtection( | 
 |       client2, outputs_[1]->display_id(), CONTENT_PROTECTION_METHOD_NONE, | 
 |       base::Bind(&DisplayConfiguratorTest::SetContentProtectionCallback, | 
 |                  base::Unretained(this))); | 
 |   EXPECT_EQ(2, set_content_protection_call_count_); | 
 |   EXPECT_TRUE(set_content_protection_status_); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   configurator_.SetContentProtection( | 
 |       client1, outputs_[1]->display_id(), CONTENT_PROTECTION_METHOD_NONE, | 
 |       base::Bind(&DisplayConfiguratorTest::SetContentProtectionCallback, | 
 |                  base::Unretained(this))); | 
 |   EXPECT_EQ(3, set_content_protection_call_count_); | 
 |   EXPECT_TRUE(set_content_protection_status_); | 
 |   EXPECT_EQ(GetSetHDCPStateAction(*outputs_[1], HDCP_STATE_UNDESIRED).c_str(), | 
 |             log_->GetActionsAndClear()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, ContentProtectionTwoClientsEnable) { | 
 |   uint64_t client1 = configurator_.RegisterContentProtectionClient(); | 
 |   uint64_t client2 = configurator_.RegisterContentProtectionClient(); | 
 |   EXPECT_NE(client1, client2); | 
 |  | 
 |   Init(false); | 
 |   configurator_.ForceInitialConfigure(); | 
 |   UpdateOutputs(2, true); | 
 |   log_->GetActionsAndClear(); | 
 |  | 
 |   // Only enable once if HDCP is enabling. | 
 |   configurator_.SetContentProtection( | 
 |       client1, outputs_[1]->display_id(), CONTENT_PROTECTION_METHOD_HDCP, | 
 |       base::Bind(&DisplayConfiguratorTest::SetContentProtectionCallback, | 
 |                  base::Unretained(this))); | 
 |   EXPECT_EQ(1, set_content_protection_call_count_); | 
 |   EXPECT_TRUE(set_content_protection_status_); | 
 |   native_display_delegate_->set_hdcp_state(HDCP_STATE_DESIRED); | 
 |   configurator_.SetContentProtection( | 
 |       client2, outputs_[1]->display_id(), CONTENT_PROTECTION_METHOD_HDCP, | 
 |       base::Bind(&DisplayConfiguratorTest::SetContentProtectionCallback, | 
 |                  base::Unretained(this))); | 
 |   EXPECT_EQ(2, set_content_protection_call_count_); | 
 |   EXPECT_TRUE(set_content_protection_status_); | 
 |   EXPECT_EQ(GetSetHDCPStateAction(*outputs_[1], HDCP_STATE_DESIRED).c_str(), | 
 |             log_->GetActionsAndClear()); | 
 |   native_display_delegate_->set_hdcp_state(HDCP_STATE_ENABLED); | 
 |  | 
 |   // Don't enable again if HDCP is already active. | 
 |   configurator_.SetContentProtection( | 
 |       client1, outputs_[1]->display_id(), CONTENT_PROTECTION_METHOD_HDCP, | 
 |       base::Bind(&DisplayConfiguratorTest::SetContentProtectionCallback, | 
 |                  base::Unretained(this))); | 
 |   EXPECT_EQ(3, set_content_protection_call_count_); | 
 |   EXPECT_TRUE(set_content_protection_status_); | 
 |   configurator_.SetContentProtection( | 
 |       client2, outputs_[1]->display_id(), CONTENT_PROTECTION_METHOD_HDCP, | 
 |       base::Bind(&DisplayConfiguratorTest::SetContentProtectionCallback, | 
 |                  base::Unretained(this))); | 
 |   EXPECT_EQ(4, set_content_protection_call_count_); | 
 |   EXPECT_TRUE(set_content_protection_status_); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) { | 
 |   InitWithSingleOutput(); | 
 |  | 
 |   std::vector<std::unique_ptr<const DisplayMode>> modes; | 
 |   // The first mode is the mode we are requesting DisplayConfigurator to choose. | 
 |   // The test will be setup so that this mode will fail and it will have to | 
 |   // choose the next best option. | 
 |   modes.push_back(MakeDisplayMode(2560, 1600, false, 60.0)); | 
 |   modes.push_back(MakeDisplayMode(1024, 768, false, 60.0)); | 
 |   modes.push_back(MakeDisplayMode(1280, 720, false, 60.0)); | 
 |   modes.push_back(MakeDisplayMode(1920, 1080, false, 60.0)); | 
 |   modes.push_back(MakeDisplayMode(1920, 1080, false, 40.0)); | 
 |  | 
 |   outputs_[0] = FakeDisplaySnapshot::Builder() | 
 |                     .SetId(kDisplayIds[0]) | 
 |                     .SetNativeMode(modes[0]->Clone()) | 
 |                     .SetCurrentMode(modes[0]->Clone()) | 
 |                     .AddMode(modes[1]->Clone()) | 
 |                     .AddMode(modes[2]->Clone()) | 
 |                     .AddMode(modes[3]->Clone()) | 
 |                     .AddMode(modes[4]->Clone()) | 
 |                     .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL) | 
 |                     .SetIsAspectPerservingScaling(true) | 
 |                     .Build(); | 
 |  | 
 |   // First test simply fails in MULTIPLE_DISPLAY_STATE_SINGLE mode. This is | 
 |   // probably unrealistic but we want to make sure any assumptions don't creep | 
 |   // in. | 
 |   native_display_delegate_->set_max_configurable_pixels( | 
 |       modes[2]->size().GetArea()); | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_SINGLE); | 
 |   UpdateOutputs(1, true); | 
 |  | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], modes[0].get(), gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[0], modes[3].get(), gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[0], modes[2].get(), gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   outputs_[1] = FakeDisplaySnapshot::Builder() | 
 |                     .SetId(kDisplayIds[1]) | 
 |                     .SetNativeMode(modes[0]->Clone()) | 
 |                     .SetCurrentMode(modes[0]->Clone()) | 
 |                     .AddMode(modes[1]->Clone()) | 
 |                     .AddMode(modes[2]->Clone()) | 
 |                     .AddMode(modes[3]->Clone()) | 
 |                     .AddMode(modes[4]->Clone()) | 
 |                     .SetType(DISPLAY_CONNECTION_TYPE_HDMI) | 
 |                     .SetIsAspectPerservingScaling(true) | 
 |                     .Build(); | 
 |  | 
 |   // This test should attempt to configure a mirror mode that will not succeed | 
 |   // and should end up in extended mode. | 
 |   native_display_delegate_->set_max_configurable_pixels( | 
 |       modes[3]->size().GetArea()); | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |   UpdateOutputs(2, true); | 
 |  | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], modes[0].get(), gfx::Point(0, 0)).c_str(), | 
 |           // Then attempt to configure crtc1 with the first mode. | 
 |           GetCrtcAction(*outputs_[1], modes[0].get(), gfx::Point(0, 0)).c_str(), | 
 |           // First mode tried is expected to fail and it will | 
 |           // retry wil the 4th mode in the list. | 
 |           GetCrtcAction(*outputs_[0], modes[3].get(), gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], modes[3].get(), gfx::Point(0, 0)).c_str(), | 
 |           // Since it was requested to go into mirror mode | 
 |           // and the configured modes were different, it | 
 |           // should now try and setup a valid configurable | 
 |           // extended mode. | 
 |           GetCrtcAction(*outputs_[0], modes[0].get(), gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], modes[0].get(), | 
 |                         gfx::Point(0, modes[0]->size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           GetCrtcAction(*outputs_[0], modes[3].get(), gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], modes[3].get(), | 
 |                         gfx::Point(0, modes[0]->size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 | } | 
 |  | 
 | // Tests that power state requests are saved after failed configuration attempts | 
 | // so they can be reused later: http://crosbug.com/p/31571 | 
 | TEST_F(DisplayConfiguratorTest, SaveDisplayPowerStateOnConfigFailure) { | 
 |   // Start out with two displays in extended mode. | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); | 
 |   Init(false); | 
 |   configurator_.ForceInitialConfigure(); | 
 |   log_->GetActionsAndClear(); | 
 |   observer_.Reset(); | 
 |  | 
 |   // Turn off the internal display, simulating docked mode. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower( | 
 |       chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON, | 
 |       DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |       config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |   EXPECT_EQ(0, observer_.num_failures()); | 
 |   log_->GetActionsAndClear(); | 
 |  | 
 |   // Make all subsequent configuration requests fail and try to turn the | 
 |   // internal display back on. | 
 |   config_waiter_.Reset(); | 
 |   native_display_delegate_->set_max_configurable_pixels(1); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_FAILURE, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |   EXPECT_EQ(1, observer_.num_failures()); | 
 |   log_->GetActionsAndClear(); | 
 |  | 
 |   // Simulate the external display getting disconnected and check that the | 
 |   // internal display is turned on (i.e. DISPLAY_POWER_ALL_ON is used) rather | 
 |   // than the earlier DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON state. | 
 |   native_display_delegate_->set_max_configurable_pixels(0); | 
 |   UpdateOutputs(1, true); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 | } | 
 |  | 
 | // Tests that the SetDisplayPowerState() task posted by HandleResume() doesn't | 
 | // use a stale state if a new state is requested before it runs: | 
 | // http://crosbug.com/p/32393 | 
 | TEST_F(DisplayConfiguratorTest, DontRestoreStalePowerStateAfterResume) { | 
 |   // Start out with two displays in mirrored mode. | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |   Init(false); | 
 |   configurator_.ForceInitialConfigure(); | 
 |   log_->GetActionsAndClear(); | 
 |   observer_.Reset(); | 
 |  | 
 |   // Turn off the internal display, simulating docked mode. | 
 |   configurator_.SetDisplayPower( | 
 |       chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON, | 
 |       DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |       config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |   EXPECT_EQ(0, observer_.num_failures()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   // Suspend and resume the system. Resuming should restore the previous power | 
 |   // state and force a probe. Suspend should turn off the displays since an | 
 |   // external monitor is connected. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SuspendDisplays(config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(2, observer_.num_changes()); | 
 |   EXPECT_EQ(JoinActions( | 
 |                 GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |                 GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, 0)).c_str(), | 
 |                 nullptr), | 
 |             log_->GetActionsAndClear()); | 
 |  | 
 |   // Before the task runs, exit docked mode. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(3, observer_.num_changes()); | 
 |   EXPECT_EQ(0, observer_.num_failures()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   // Check that the display states are not changed after resuming. | 
 |   config_waiter_.Reset(); | 
 |   // Since we are in dual display mode, a configuration task is scheduled after | 
 |   // kMinLongDelayMs delay. | 
 |   configurator_.ResumeDisplays(); | 
 |   EXPECT_EQ(kLongDelay, test_api_.GetConfigureDelay()); | 
 |   EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, | 
 |             configurator_.current_power_state()); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |   // Now trigger that delayed configuration. | 
 |   EXPECT_EQ(kLongDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, ExternalControl) { | 
 |   InitWithSingleOutput(); | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_SINGLE); | 
 |  | 
 |   // Set the initial power state and verify that it is restored when control is | 
 |   // taken. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |  | 
 |   configurator_.RelinquishControl( | 
 |       base::BindOnce(&DisplayConfiguratorTest::OnDisplayControlUpdated, | 
 |                      base::Unretained(this))); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, PopDisplayControlResult()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |           kRelinquishDisplayControl, nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   configurator_.TakeControl( | 
 |       base::BindOnce(&DisplayConfiguratorTest::OnDisplayControlUpdated, | 
 |                      base::Unretained(this))); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, PopDisplayControlResult()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           kTakeDisplayControl, | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, | 
 |        SetDisplayPowerWhilePendingConfigurationTaskRunning) { | 
 |   // Start out with two displays in extended mode. | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); | 
 |   Init(false); | 
 |   configurator_.ForceInitialConfigure(); | 
 |   log_->GetActionsAndClear(); | 
 |   observer_.Reset(); | 
 |  | 
 |   native_display_delegate_->set_run_async(true); | 
 |  | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_OFF, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(CALLBACK_NOT_CALLED, config_waiter_.callback_result()); | 
 |  | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |  | 
 |   EXPECT_EQ(CALLBACK_NOT_CALLED, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |   EXPECT_EQ(0, observer_.num_failures()); | 
 |  | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], nullptr, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   config_waiter_.Reset(); | 
 |   EXPECT_EQ( | 
 |       base::TimeDelta::FromMilliseconds(DisplayConfigurator::kConfigureDelayMs), | 
 |       config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_NOT_CALLED, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(2, observer_.num_changes()); | 
 |   EXPECT_EQ(0, observer_.num_failures()); | 
 |  | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, | 
 |        SetDisplayPowerAfterFailedDisplayConfiguration) { | 
 |   // Start out with two displays in extended mode. | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); | 
 |   Init(false); | 
 |   configurator_.ForceInitialConfigure(); | 
 |   log_->GetActionsAndClear(); | 
 |   observer_.Reset(); | 
 |  | 
 |   // Fail display configuration. | 
 |   native_display_delegate_->set_max_configurable_pixels(-1); | 
 |  | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_OFF, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_FAILURE, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(0, observer_.num_changes()); | 
 |   EXPECT_EQ(1, observer_.num_failures()); | 
 |  | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], nullptr, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   // This configuration should trigger a display configuration since the | 
 |   // previous configuration failed. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |  | 
 |   EXPECT_EQ(0, observer_.num_changes()); | 
 |   EXPECT_EQ(2, observer_.num_failures()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           GetCrtcAction(*outputs_[1], &small_mode_, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   // Allow configuration to succeed. | 
 |   native_display_delegate_->set_max_configurable_pixels(0); | 
 |  | 
 |   // Validate that a configuration event has the proper power state (displays | 
 |   // should be on). | 
 |   configurator_.OnConfigurationChanged(); | 
 |   EXPECT_TRUE(test_api_.TriggerConfigureTimeout()); | 
 |  | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |   EXPECT_EQ(2, observer_.num_failures()); | 
 |  | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorTest, TestWithThreeDisplays) { | 
 |   // Start out with two displays in extended mode. | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); | 
 |   Init(false); | 
 |   configurator_.ForceInitialConfigure(); | 
 |   log_->GetActionsAndClear(); | 
 |   observer_.Reset(); | 
 |  | 
 |   UpdateOutputs(3, true); | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); | 
 |  | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           GetCrtcAction( | 
 |               *outputs_[2], &small_mode_, | 
 |               gfx::Point(0, small_mode_.size().height() + | 
 |                                 big_mode_.size().height() + | 
 |                                 2 * DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   // Verify that turning the power off works. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_OFF, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], nullptr, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           GetCrtcAction( | 
 |               *outputs_[2], nullptr, | 
 |               gfx::Point(0, small_mode_.size().height() + | 
 |                                 big_mode_.size().height() + | 
 |                                 2 * DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           GetCrtcAction( | 
 |               *outputs_[2], &small_mode_, | 
 |               gfx::Point(0, small_mode_.size().height() + | 
 |                                 big_mode_.size().height() + | 
 |                                 2 * DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   // Disconnect the third output. | 
 |   observer_.Reset(); | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); | 
 |   UpdateOutputs(2, true); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 | } | 
 |  | 
 | // Tests the suspend and resume behavior when in dual or multi display modes. | 
 | TEST_F(DisplayConfiguratorTest, SuspendResumeWithMultipleDisplays) { | 
 |   InitWithSingleOutput(); | 
 |  | 
 |   // Set the initial power state and verify that it is restored on resume. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, | 
 |                                 DisplayConfigurator::kSetDisplayPowerNoFlags, | 
 |                                 config_waiter_.on_configuration_callback()); | 
 |  | 
 |   state_controller_.set_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); | 
 |   observer_.Reset(); | 
 |   UpdateOutputs(2, true); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, | 
 |             configurator_.display_state()); | 
 |   EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |   EXPECT_EQ(1, observer_.num_changes()); | 
 |   EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, | 
 |             configurator_.current_power_state()); | 
 |  | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   // Suspending displays should result in an immediate configuration without | 
 |   // delays, even in dual display mode. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SuspendDisplays(config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_OFF, | 
 |             configurator_.current_power_state()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, | 
 |             configurator_.display_state()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], nullptr, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   // Resuming from suspend with dual displays. Configuration should be done | 
 |   // after a long delay. Afterwards, we should still expect to be in a dual | 
 |   // display mode. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.ResumeDisplays(); | 
 |   EXPECT_EQ(kLongDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, | 
 |             configurator_.current_power_state()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, | 
 |             configurator_.display_state()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], &big_mode_, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   // Suspend displays and disconnect one of them while in suspend. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SuspendDisplays(config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, | 
 |             configurator_.display_state()); | 
 |   EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_OFF, | 
 |             configurator_.current_power_state()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), | 
 |           GetCrtcAction(*outputs_[1], nullptr, | 
 |                         gfx::Point(0, small_mode_.size().height() + | 
 |                                           DisplayConfigurator::kVerticalGap)) | 
 |               .c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |   UpdateOutputs(1, false); | 
 |   EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |  | 
 |   // Now resume, and expect that we'll still have a long delay since we were in | 
 |   // dual mode before suspend. The configurator should pick up the change and | 
 |   // detect that we are in single display mode now. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.ResumeDisplays(); | 
 |   EXPECT_EQ(kLongDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, | 
 |             configurator_.current_power_state()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_SINGLE, configurator_.display_state()); | 
 |   EXPECT_EQ( | 
 |       JoinActions( | 
 |           GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), | 
 |           nullptr), | 
 |       log_->GetActionsAndClear()); | 
 |  | 
 |   // Verify that the above is the exact same behavior for 3+ displays. | 
 |   UpdateOutputs(3, true); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, | 
 |             configurator_.display_state()); | 
 |  | 
 |   // Suspend. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.SuspendDisplays(config_waiter_.on_configuration_callback()); | 
 |   EXPECT_EQ(kNoDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, | 
 |             configurator_.display_state()); | 
 |   EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_OFF, | 
 |             configurator_.current_power_state()); | 
 |  | 
 |   // Resume and expect the correct delay. | 
 |   config_waiter_.Reset(); | 
 |   configurator_.ResumeDisplays(); | 
 |   EXPECT_EQ(kLongDelay, config_waiter_.Wait()); | 
 |   EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, | 
 |             configurator_.current_power_state()); | 
 |   EXPECT_EQ(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, | 
 |             configurator_.display_state()); | 
 | } | 
 |  | 
 | class DisplayConfiguratorMultiMirroringTest : public DisplayConfiguratorTest { | 
 |  public: | 
 |   DisplayConfiguratorMultiMirroringTest() = default; | 
 |   ~DisplayConfiguratorMultiMirroringTest() override = default; | 
 |  | 
 |   void SetUp() override { | 
 |     configurator_.set_is_multi_mirroring_enabled_for_test(true); | 
 |     DisplayConfiguratorTest::SetUp(); | 
 |   } | 
 |  | 
 |   // Test that setting mirror mode with current outputs, all displays are set to | 
 |   // expected mirror mode. | 
 |   void TestHardwareMirrorModeExist( | 
 |       std::unique_ptr<DisplayMode> expected_mirror_mode) { | 
 |     UpdateOutputs(3, true); | 
 |     log_->GetActionsAndClear(); | 
 |     observer_.Reset(); | 
 |     configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |     EXPECT_EQ( | 
 |         JoinActions(GetCrtcAction(*outputs_[0], expected_mirror_mode.get(), | 
 |                                   gfx::Point(0, 0)) | 
 |                         .c_str(), | 
 |                     GetCrtcAction(*outputs_[1], expected_mirror_mode.get(), | 
 |                                   gfx::Point(0, 0)) | 
 |                         .c_str(), | 
 |                     GetCrtcAction(*outputs_[2], expected_mirror_mode.get(), | 
 |                                   gfx::Point(0, 0)) | 
 |                         .c_str(), | 
 |                     nullptr), | 
 |         log_->GetActionsAndClear()); | 
 |     EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |     EXPECT_EQ(1, observer_.num_changes()); | 
 |   } | 
 |  | 
 |   // Test that setting mirror mode with current outputs, no matching mirror mode | 
 |   // is found. | 
 |   void TestHardwareMirrorModeNotExist() { | 
 |     UpdateOutputs(3, true); | 
 |     log_->GetActionsAndClear(); | 
 |     observer_.Reset(); | 
 |     configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); | 
 |     EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); | 
 |     EXPECT_TRUE(mirroring_controller_.SoftwareMirroringEnabled()); | 
 |     EXPECT_EQ(1, observer_.num_changes()); | 
 |   } | 
 |  | 
 |  private: | 
 |   DISALLOW_COPY_AND_ASSIGN(DisplayConfiguratorMultiMirroringTest); | 
 | }; | 
 |  | 
 | TEST_F(DisplayConfiguratorMultiMirroringTest, | 
 |        FindMirrorModeWithInternalDisplay) { | 
 |   // Initialize with one internal display and two external displays. | 
 |   outputs_[0] = FakeDisplaySnapshot::Builder() | 
 |                     .SetId(kDisplayIds[0]) | 
 |                     .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL) | 
 |                     .SetNativeMode(MakeDisplayMode(1920, 1600, false, 60.0)) | 
 |                     .AddMode(MakeDisplayMode(1920, 1600, false, 60.0)) | 
 |                     .AddMode(MakeDisplayMode(1920, 1200, false, 60.0)) | 
 |                     .AddMode(MakeDisplayMode(1920, 1080, true, 60.0)) | 
 |                     .AddMode(MakeDisplayMode(1440, 900, true, 60.0)) | 
 |                     .Build(); | 
 |   outputs_[1] = | 
 |       FakeDisplaySnapshot::Builder() | 
 |           .SetId(kDisplayIds[1]) | 
 |           .SetType(DISPLAY_CONNECTION_TYPE_HDMI) | 
 |           .SetNativeMode(MakeDisplayMode(1920, 1200, true, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1920, 1200, true, 60.0))  // same AR | 
 |           .AddMode(MakeDisplayMode(1920, 1080, true, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1680, 1050, false, 60.0))  // same AR | 
 |           .AddMode(MakeDisplayMode(1440, 900, true, 60.0))    // same AR | 
 |           .AddMode(MakeDisplayMode(500, 500, false, 60.0)) | 
 |           .Build(); | 
 |   outputs_[2] = | 
 |       FakeDisplaySnapshot::Builder() | 
 |           .SetId(kDisplayIds[2]) | 
 |           .SetType(DISPLAY_CONNECTION_TYPE_HDMI) | 
 |           .SetNativeMode(MakeDisplayMode(1920, 1200, false, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1920, 1200, false, 60.0))  // same AR | 
 |           .AddMode(MakeDisplayMode(1920, 1080, true, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1680, 1050, false, 60.0))  // same AR | 
 |           .AddMode(MakeDisplayMode(1440, 900, true, 60.0))    // same AR | 
 |           .Build(); | 
 |  | 
 |   // Find an exactly matching mirror mode while preserving aspect. | 
 |   TestHardwareMirrorModeExist(MakeDisplayMode(1440, 900, true, 60.0)); | 
 |  | 
 |   // Find an exactly matching mirror mode while not preserving aspect. | 
 |   outputs_[2] = | 
 |       FakeDisplaySnapshot::Builder() | 
 |           .SetId(kDisplayIds[2]) | 
 |           .SetType(DISPLAY_CONNECTION_TYPE_HDMI) | 
 |           .SetNativeMode(MakeDisplayMode(1920, 1200, false, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1920, 1200, false, 60.0))  // same AR | 
 |           .AddMode(MakeDisplayMode(1920, 1080, true, 60.0)) | 
 |           .Build(); | 
 |   TestHardwareMirrorModeExist(MakeDisplayMode(1920, 1080, true, 60.0)); | 
 |  | 
 |   // Cannot find a matching mirror mode, so enable software mirroring. | 
 |   outputs_[2] = | 
 |       FakeDisplaySnapshot::Builder() | 
 |           .SetId(kDisplayIds[2]) | 
 |           .SetType(DISPLAY_CONNECTION_TYPE_HDMI) | 
 |           .SetNativeMode(MakeDisplayMode(1920, 1200, false, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1920, 1200, false, 60.0))  // same AR | 
 |           .AddMode(MakeDisplayMode(500, 500, true, 60.0)) | 
 |           .Build(); | 
 |   TestHardwareMirrorModeNotExist(); | 
 | } | 
 |  | 
 | TEST_F(DisplayConfiguratorMultiMirroringTest, | 
 |        FindMirrorModeWithoutInternalDisplay) { | 
 |   // Initialize with 3 external displays. | 
 |   outputs_[0] = | 
 |       FakeDisplaySnapshot::Builder() | 
 |           .SetId(kDisplayIds[1]) | 
 |           .SetType(DISPLAY_CONNECTION_TYPE_HDMI) | 
 |           .SetNativeMode(MakeDisplayMode(1920, 1200, true, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1920, 1200, true, 60.0))  // same AR | 
 |           .AddMode(MakeDisplayMode(1920, 1080, false, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1680, 1050, true, 60.0))  // same AR | 
 |           .Build(); | 
 |   outputs_[1] = | 
 |       FakeDisplaySnapshot::Builder() | 
 |           .SetId(kDisplayIds[2]) | 
 |           .SetType(DISPLAY_CONNECTION_TYPE_HDMI) | 
 |           .SetNativeMode(MakeDisplayMode(1920, 1200, false, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1920, 1200, false, 60.0))  // same AR | 
 |           .AddMode(MakeDisplayMode(1920, 1080, false, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1680, 1050, true, 60.0))  // same AR | 
 |           .Build(); | 
 |   outputs_[2] = | 
 |       FakeDisplaySnapshot::Builder() | 
 |           .SetId(kDisplayIds[2]) | 
 |           .SetType(DISPLAY_CONNECTION_TYPE_HDMI) | 
 |           .SetNativeMode(MakeDisplayMode(1920, 1200, false, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1920, 1200, false, 60.0))  // same AR | 
 |           .AddMode(MakeDisplayMode(1920, 1080, false, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1680, 1050, true, 60.0))  // same AR | 
 |           .Build(); | 
 |  | 
 |   // Find an exactly matching mirror mode while preserving aspect. | 
 |   TestHardwareMirrorModeExist(MakeDisplayMode(1680, 1050, true, 60.0)); | 
 |  | 
 |   // Find an exactly matching mirror mode while not preserving aspect. | 
 |   outputs_[2] = | 
 |       FakeDisplaySnapshot::Builder() | 
 |           .SetId(kDisplayIds[0]) | 
 |           .SetType(DISPLAY_CONNECTION_TYPE_HDMI) | 
 |           .SetNativeMode(MakeDisplayMode(1920, 1600, false, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1920, 1600, false, 60.0))  // same AR | 
 |           .AddMode(MakeDisplayMode(1920, 1200, false, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1920, 1080, false, 60.0)) | 
 |           .Build(); | 
 |   TestHardwareMirrorModeExist(MakeDisplayMode(1920, 1080, false, 60.0)); | 
 |  | 
 |   // Cannot find a matching mirror mode, so enable software mirroring. | 
 |   outputs_[2] = | 
 |       FakeDisplaySnapshot::Builder() | 
 |           .SetId(kDisplayIds[0]) | 
 |           .SetType(DISPLAY_CONNECTION_TYPE_HDMI) | 
 |           .SetNativeMode(MakeDisplayMode(1920, 1600, false, 60.0)) | 
 |           .AddMode(MakeDisplayMode(1920, 1600, false, 60.0))  // same AR | 
 |           .AddMode(MakeDisplayMode(1920, 1200, false, 60.0)) | 
 |           .Build(); | 
 |   TestHardwareMirrorModeNotExist(); | 
 | } | 
 |  | 
 | }  // namespace test | 
 | }  // namespace display |