| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stddef.h> |
| #include <vector> |
| |
| #include "base/functional/bind.h" |
| #include "base/run_loop.h" |
| #include "base/test/task_environment.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/display/manager/configure_displays_task.h" |
| #include "ui/display/manager/test/action_logger_util.h" |
| #include "ui/display/manager/test/fake_display_snapshot.h" |
| #include "ui/display/manager/test/test_native_display_delegate.h" |
| #include "ui/display/types/display_constants.h" |
| #include "ui/display/types/display_mode.h" |
| #include "ui/gfx/geometry/point.h" |
| #include "ui/gfx/geometry/size.h" |
| |
| namespace display::test { |
| |
| namespace { |
| |
| // Non-zero generic connector IDs. |
| constexpr uint64_t kEdpConnectorId = 71u; |
| constexpr uint64_t kSecondConnectorId = kEdpConnectorId + 10u; |
| constexpr uint64_t kThirdConnectorId = kEdpConnectorId + 20u; |
| |
| // Invalid PATH topology parse connector ID. |
| constexpr uint64_t kInvalidConnectorId = 0u; |
| |
| std::string GetDisableCrtcAction( |
| const std::unique_ptr<DisplaySnapshot>& display) { |
| return GetCrtcAction({display->display_id(), gfx::Point(), nullptr}); |
| } |
| |
| class ConfigureDisplaysTaskTest : public testing::Test { |
| public: |
| ConfigureDisplaysTaskTest() |
| : delegate_(&log_), |
| small_mode_60hz_({1366, 768}, false, 60.0f), |
| small_mode_30hz_({1366, 768}, false, 30.0f), |
| medium_mode_60hz_({1920, 1080}, false, 60.0f), |
| medium_mode_29_98hz_({1920, 1080}, false, 29.98f), |
| medium_interlaced_mode_60hz_({1920, 1080}, true, 60.0f), |
| big_mode_60hz_({2560, 1600}, false, 60.0f), |
| big_mode_29_97hz_({2560, 1600}, false, 29.97f) {} |
| |
| ConfigureDisplaysTaskTest(const ConfigureDisplaysTaskTest&) = delete; |
| ConfigureDisplaysTaskTest& operator=(const ConfigureDisplaysTaskTest&) = |
| delete; |
| |
| ~ConfigureDisplaysTaskTest() override = default; |
| |
| void SetUp() override { |
| displays_.push_back( |
| FakeDisplaySnapshot::Builder() |
| .SetId(123) |
| .SetNativeMode(medium_mode_60hz_.Clone()) |
| .SetCurrentMode(medium_mode_60hz_.Clone()) |
| .AddMode(medium_mode_29_98hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .AddMode(small_mode_30hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL) |
| .SetBaseConnectorId(kEdpConnectorId) |
| .SetVariableRefreshRateState(VariableRefreshRateState::kVrrDisabled) |
| .Build()); |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(456) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(big_mode_29_97hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .AddMode(small_mode_30hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .SetVariableRefreshRateState( |
| VariableRefreshRateState::kVrrNotCapable) |
| .Build()); |
| } |
| |
| void ConfigureCallback(ConfigureDisplaysTask::Status status) { |
| callback_called_ = true; |
| status_ = status; |
| } |
| |
| protected: |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| ActionLogger log_; |
| TestNativeDisplayDelegate delegate_; |
| |
| bool callback_called_ = false; |
| ConfigureDisplaysTask::Status status_ = ConfigureDisplaysTask::ERROR; |
| |
| const DisplayMode small_mode_60hz_; |
| const DisplayMode small_mode_30hz_; |
| const DisplayMode medium_mode_60hz_; |
| const DisplayMode medium_mode_29_98hz_; |
| const DisplayMode medium_interlaced_mode_60hz_; |
| const DisplayMode big_mode_60hz_; |
| const DisplayMode big_mode_29_97hz_; |
| std::vector<std::unique_ptr<DisplaySnapshot>> displays_; |
| }; |
| |
| } // namespace |
| |
| /************************************************** |
| * Cases that report ConfigureDisplaysTask::SUCCESS |
| **************************************************/ |
| |
| TEST_F(ConfigureDisplaysTaskTest, ConfigureInternalDisplay) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| std::vector<DisplayConfigureRequest> requests( |
| 1, DisplayConfigureRequest(displays_[0].get(), |
| displays_[0]->native_mode(), gfx::Point())); |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_); |
| EXPECT_EQ(JoinActions(kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| kModesetOutcomeSuccess, kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that and an internal + one external display pass modeset. Note that |
| // this case covers an external display connected via MST as well. |
| TEST_F(ConfigureDisplaysTaskTest, ConfigureInternalAndOneExternalDisplays) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_); |
| EXPECT_EQ(JoinActions(kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction({displays_[1]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction({displays_[1]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that one external display (with no internal display present; |
| // e.g. chromebox) pass modeset. Note that this case covers an external display |
| // connected via MST as well. |
| TEST_F(ConfigureDisplaysTaskTest, ConfigureOneExternalDisplay) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| std::vector<DisplayConfigureRequest> requests( |
| 1, DisplayConfigureRequest(displays_[1].get(), |
| displays_[1]->native_mode(), gfx::Point())); |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_); |
| EXPECT_EQ(JoinActions(kTestModesetStr, |
| GetCrtcAction({displays_[1]->display_id(), gfx::Point(), |
| displays_[1]->native_mode()}) |
| .c_str(), |
| kModesetOutcomeSuccess, kCommitModesetStr, |
| GetCrtcAction({displays_[1]->display_id(), gfx::Point(), |
| displays_[1]->native_mode()}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that two external MST displays (with no internal display present; e.g. |
| // chromebox) pass modeset. |
| TEST_F(ConfigureDisplaysTaskTest, ConfigureTwoMstDisplays) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| // Two displays sharing the same base connector via MST. |
| displays_[0] = FakeDisplaySnapshot::Builder() |
| .SetId(456) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build(); |
| displays_[1] = FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build(); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_); |
| EXPECT_EQ(JoinActions(kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({displays_[1]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({displays_[1]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Although most devices do not support more than three displays in total |
| // (including the internal display), this tests that this configuration can pass |
| // all displays in a single request. |
| TEST_F(ConfigureDisplaysTaskTest, ConfigureInternalAndTwoMstAndHdmiDisplays) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| // Add an additional display to base connector kSecondConnectorId via MST. |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build()); |
| |
| // Additional independent HDMI display (has its own connector). |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(101112) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(medium_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_HDMI) |
| .SetBaseConnectorId(kThirdConnectorId) |
| .Build()); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_); |
| EXPECT_EQ(JoinActions(kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction({displays_[1]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({displays_[2]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({displays_[3]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction({displays_[1]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({displays_[2]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({displays_[3]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| /************************************************ |
| * Cases that report ConfigureDisplaysTask::ERROR |
| ************************************************/ |
| |
| TEST_F(ConfigureDisplaysTaskTest, DisableInternalDisplayFails) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| // Force a failed configuration. |
| delegate_.set_max_configurable_pixels(-1); |
| |
| std::vector<DisplayConfigureRequest> requests( |
| 1, DisplayConfigureRequest(displays_[0].get(), nullptr, gfx::Point())); |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); |
| EXPECT_EQ( |
| JoinActions(kTestModesetStr, |
| // Initial test-modeset fails. Initiate retry logic. |
| GetDisableCrtcAction(displays_[0]).c_str(), |
| kModesetOutcomeFailure, |
| // There is no way to downgrade a disable request. |
| // Configuration fails. |
| kTestModesetStr, GetDisableCrtcAction(displays_[0]).c_str(), |
| kModesetOutcomeFailure, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that the internal display does not attempt to fallback to alternative |
| // modes upon failure to modeset with preferred mode. |
| TEST_F(ConfigureDisplaysTaskTest, NoModeChangeAttemptWhenInternalDisplayFails) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| delegate_.set_max_configurable_pixels(1); |
| |
| std::vector<DisplayConfigureRequest> requests( |
| 1, DisplayConfigureRequest(displays_[0].get(), |
| displays_[0]->native_mode(), gfx::Point())); |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); |
| EXPECT_EQ(JoinActions( |
| kTestModesetStr, |
| // Initial test-modeset fails. Initiate retry logic. |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // Retry logic fails to modeset internal display. Since internal |
| // displays are restricted to their preferred mode, there are no |
| // other modes to try. The configuration fails completely. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| kModesetOutcomeFailure, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that an external display (with no internal display present; e.g. |
| // chromebox) attempts to fallback to alternative modes upon failure to modeset |
| // to the original request before completely failing. Note that this case |
| // applies to a single external display over MST as well. |
| TEST_F(ConfigureDisplaysTaskTest, ConfigureOneExternalNoInternalDisplayFails) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| delegate_.set_max_configurable_pixels(1); |
| |
| std::vector<DisplayConfigureRequest> requests( |
| 1, DisplayConfigureRequest(displays_[1].get(), &big_mode_60hz_, |
| gfx::Point())); |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); |
| EXPECT_EQ( |
| JoinActions( |
| kTestModesetStr, |
| // Initial modeset fails. Initiate retry logic. |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // External display will fail, downgrade twice, and fail completely. |
| kTestModesetStr, |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_30hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that two external non-MST displays (with no internal display present; |
| // e.g. chromebox) attempt to fallback to alternative modes upon failure to |
| // modeset to the original request before completely failing. |
| TEST_F(ConfigureDisplaysTaskTest, ConfigureTwoNoneMstDisplaysNoInternalFail) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| displays_[0] = FakeDisplaySnapshot::Builder() |
| .SetId(456) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build(); |
| displays_[1] = FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(medium_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kThirdConnectorId) |
| .Build(); |
| |
| delegate_.set_max_configurable_pixels(small_mode_60hz_.size().GetArea()); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); |
| EXPECT_EQ( |
| JoinActions( |
| kTestModesetStr, |
| // All displays will fail to modeset together. Initiate retry logic. |
| GetCrtcAction( |
| {displays_[0]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first test-modeset |displays_[0] with all other displays |
| // disabled. It will fail and downgrade once before passing. |
| kTestModesetStr, |
| GetCrtcAction( |
| {displays_[0]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), kModesetOutcomeFailure, |
| kTestModesetStr, |
| GetCrtcAction( |
| {displays_[0]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), kModesetOutcomeSuccess, |
| // |displays_[1]| will fail, downgrade once, and fail completely. |
| kTestModesetStr, |
| GetCrtcAction( |
| {displays_[0]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction( |
| {displays_[0]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We commit the last passing test-modeset configuration. |
| kCommitModesetStr, |
| GetCrtcAction( |
| {displays_[0]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), kModesetOutcomeSuccess, |
| nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that two external MST displays (with no internal display present; e.g. |
| // chromebox) attempt to fallback to alternative modes upon failure to modeset |
| // to the original request before completely failing. |
| TEST_F(ConfigureDisplaysTaskTest, ConfigureTwoMstDisplaysNoInternalFail) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| // Two displays sharing the same base connector. |
| displays_[0] = FakeDisplaySnapshot::Builder() |
| .SetId(456) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build(); |
| displays_[1] = FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(medium_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build(); |
| |
| delegate_.set_max_configurable_pixels(1); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction( |
| {displays_[0]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // MST displays will be tested (and fail) together. |
| kTestModesetStr, |
| GetCrtcAction( |
| {displays_[0]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // |displays_[0]| will downgrade first. Configuration will fail. |
| kTestModesetStr, |
| GetCrtcAction( |
| {displays_[0]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // |displays_[1] will downgrade next. Configuration still fails. |
| kTestModesetStr, |
| GetCrtcAction( |
| {displays_[0]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // Since |displays_[1]| is still the largest and has one more mode, it |
| // downgrades again. Configuration fails completely. |
| kTestModesetStr, |
| GetCrtcAction( |
| {displays_[0]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that the internal display does not attempt to fallback to alternative |
| // modes upon failure to modeset with preferred mode while an external display |
| // is present. Note that this case applies for an internal + a single external |
| // display over MST as well. |
| TEST_F(ConfigureDisplaysTaskTest, |
| ConfigureInternalAndOneExternalDisplaysFailsDueToInternal) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| delegate_.set_max_configurable_pixels(1); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first attempt to modeset the internal display with all other |
| // displays disabled, which will fail. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), kModesetOutcomeFailure, |
| // Since internal displays are restricted to their preferred mode, |
| // there are no other modes to try. Disable the internal display so we |
| // can attempt to modeset displays that are connected to other |
| // connectors. Next, the external display will attempt to modeset. |
| kTestModesetStr, GetDisableCrtcAction(displays_[0]).c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetDisableCrtcAction(displays_[0]).c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetDisableCrtcAction(displays_[0]).c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetDisableCrtcAction(displays_[0]).c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_30hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that an external display attempts to fallback to alternative modes upon |
| // failure to modeset to the original request after the internal display modeset |
| // successfully. Note that this case applies for an internal + a single MST |
| // display as well. |
| TEST_F(ConfigureDisplaysTaskTest, |
| ConfigureInternalAndOneExternalDisplaysFailsDueToExternal) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| displays_[0] = FakeDisplaySnapshot::Builder() |
| .SetId(123) |
| .SetNativeMode(small_mode_60hz_.Clone()) |
| .SetCurrentMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL) |
| .SetBaseConnectorId(kEdpConnectorId) |
| .Build(); |
| displays_[1] = FakeDisplaySnapshot::Builder() |
| .SetId(456) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(medium_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build(); |
| |
| delegate_.set_max_configurable_pixels(small_mode_60hz_.size().GetArea()); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first test-modeset the internal display with all other displays |
| // disabled, which will succeed. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), kModesetOutcomeSuccess, |
| // External display fails, downgrades once, and fails completely. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We commit the last passing test-modeset configuration. |
| kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), kModesetOutcomeSuccess, |
| nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that the internal display does not attempt to fallback to alternative |
| // modes upon failure to modeset with preferred mode while two external MST |
| // displays are present. |
| TEST_F(ConfigureDisplaysTaskTest, |
| ConfigureInternalAndTwoMstExternalDisplaysFailsDueToInternal) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| // Add an additional display to base connector kSecondConnectorId via MST. |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build()); |
| |
| delegate_.set_max_configurable_pixels(1); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first attempt to modeset the internal display with all other |
| // displays disabled, which will fail. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), kModesetOutcomeFailure, |
| // Since internal displays are restricted to their preferred mode, |
| // there are no other modes to try. Disable the internal display so we |
| // can attempt to modeset displays that are connected to other |
| // connectors. Next, modeset the external displays which are connected |
| // to the same port via MST. |
| kTestModesetStr, GetDisableCrtcAction(displays_[0]).c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| // |displays_[1] & displays_[2] will cycle through all available |
| // modes, but configuration will eventually completely fail. |
| GetDisableCrtcAction(displays_[0]).c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetDisableCrtcAction(displays_[0]).c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetDisableCrtcAction(displays_[0]).c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetDisableCrtcAction(displays_[0]).c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_30hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that two external MST displays attempt to fallback to alternative modes |
| // upon failure to modeset to the original request after the internal display |
| // succeeded to modeset |
| TEST_F(ConfigureDisplaysTaskTest, |
| ConfigureInternalAndTwoMstExternalDisplaysFailsDueToExternals) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| displays_[0] = FakeDisplaySnapshot::Builder() |
| .SetId(123) |
| .SetNativeMode(small_mode_60hz_.Clone()) |
| .SetCurrentMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL) |
| .SetBaseConnectorId(kEdpConnectorId) |
| .Build(); |
| // Two MST displays sharing the same base connector. |
| displays_[1] = FakeDisplaySnapshot::Builder() |
| .SetId(456) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build(); |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(medium_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build()); |
| |
| delegate_.set_max_configurable_pixels(small_mode_60hz_.size().GetArea()); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first test-modeset the internal display with all other displays |
| // disabled, which will succeed. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), kModesetOutcomeSuccess, |
| // Next, MST displays will test-modeset, downgrade, and eventually |
| // fail completely. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We commit the last successful test-modeset configuration. |
| kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), kModesetOutcomeSuccess, |
| nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that the internal display does not attempt to fallback to alternative |
| // modes upon failure to modeset with preferred mode while two MST and one HDMI |
| // displays are present. |
| TEST_F(ConfigureDisplaysTaskTest, |
| ConfigureInternalAndTwoMstAndHdmiDisplaysFailsDueToInternal) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| // Add an additional display to kSecondConnectorId (via MST). |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build()); |
| |
| // Additional independent HDMI display (has its own connector). |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(101112) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(big_mode_29_97hz_.Clone()) |
| .AddMode(medium_mode_60hz_.Clone()) |
| .AddMode(medium_mode_29_98hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .AddMode(small_mode_30hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_HDMI) |
| .SetBaseConnectorId(kThirdConnectorId) |
| .Build()); |
| |
| delegate_.set_max_configurable_pixels(1); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first attempt to modeset the internal display with all other |
| // displays disabled, which will fail. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeFailure, |
| // Since internal displays are restricted to their preferred mode, |
| // there are no other modes to try. Disable the internal display so we |
| // can attempt to modeset displays that are connected to other |
| // connectors. Next, the two displays connected via MST will attempt |
| // to modeset and fail. |
| kTestModesetStr, GetDisableCrtcAction(displays_[0]).c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeFailure, |
| kTestModesetStr, GetDisableCrtcAction(displays_[0]).c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeFailure, |
| kTestModesetStr, GetDisableCrtcAction(displays_[0]).c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeFailure, |
| kTestModesetStr, GetDisableCrtcAction(displays_[0]).c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeFailure, |
| kTestModesetStr, GetDisableCrtcAction(displays_[0]).c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_30hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeFailure, |
| // Next, the HDMI display will attempt to modeset and cycle through |
| // its six available modes. |
| kTestModesetStr, GetDisableCrtcAction(displays_[0]).c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetDisableCrtcAction(displays_[0]).c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetDisableCrtcAction(displays_[0]).c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetDisableCrtcAction(displays_[0]).c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &medium_mode_29_98hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetDisableCrtcAction(displays_[0]).c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetDisableCrtcAction(displays_[0]).c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &small_mode_30hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that two external MST displays attempt to fallback to alternative modes |
| // upon failure to modeset to the original request after the internal display |
| // succeeded to modeset. |
| TEST_F(ConfigureDisplaysTaskTest, |
| ConfigureInternalAndTwoMstAndHdmiDisplaysFailsDueToMst) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| // Add an additional display to kSecondConnectorId (via MST). |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build()); |
| |
| // Additional independent HDMI display (has its own connector). |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(101112) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(big_mode_29_97hz_.Clone()) |
| .AddMode(medium_mode_60hz_.Clone()) |
| .AddMode(medium_mode_29_98hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .AddMode(small_mode_30hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_HDMI) |
| .SetBaseConnectorId(kThirdConnectorId) |
| .Build()); |
| |
| delegate_.set_max_configurable_pixels(medium_mode_60hz_.size().GetArea()); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first test-modeset the internal display with all other displays |
| // disabled, which will pass. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeSuccess, |
| // MST displays will be tested next with the HDMI display disabled. |
| // They will fail. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeFailure, |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeFailure, |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeFailure, |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_30hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeFailure, |
| // HDMI display attempts to modeset, fails, downgrades twice, and |
| // passes modeset. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, |
| // We commit the last successful test-modeset configuration, which |
| // enables the internal display together with the HDMI display. |
| kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that the HDMI display attempts to fallback to alternative modes upon |
| // failure to modeset to the original request after the internal and two MST |
| // displays succeeded to modeset. |
| TEST_F(ConfigureDisplaysTaskTest, |
| ConfigureInternalAndTwoMstAndHdmiDisplaysFailsDueToHDMI) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| // Two displays sharing the same base connector. |
| displays_[1] = FakeDisplaySnapshot::Builder() |
| .SetId(456) |
| .SetNativeMode(medium_mode_60hz_.Clone()) |
| .SetCurrentMode(medium_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build(); |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(medium_mode_60hz_.Clone()) |
| .SetCurrentMode(medium_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build()); |
| |
| // Additional independent HDMI display (has its own connector). |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(101112) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_HDMI) |
| .SetBaseConnectorId(kThirdConnectorId) |
| .Build()); |
| |
| delegate_.set_max_configurable_pixels(medium_mode_60hz_.size().GetArea()); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first test-modeset the internal display with all other displays |
| // disabled, which will succeed. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeSuccess, |
| // MST displays will be tested and pass together. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeSuccess, |
| // HDMI display will fail modeset, but since there are no other modes |
| // available for fallback configuration fails completely. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We commit the last successful test-modeset configuration, which |
| // enables the internal display together with the two MST displays. |
| kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeSuccess, |
| nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that two external displays that share a bad MST hub are tested and fail |
| // together, since they are grouped under kInvalidConnectorId. Also test that |
| // this does not affect the internal display's ability configured separately |
| // during retry and passes modeset. |
| TEST_F(ConfigureDisplaysTaskTest, |
| ConfigureInternalAndOneBadMstHubWithTwoDisplaysFails) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| // Two displays sharing a bad MST Hub that did not report its PATH topology |
| // correctly. |
| displays_[1] = FakeDisplaySnapshot::Builder() |
| .SetId(456) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kInvalidConnectorId) |
| .Build(); |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kInvalidConnectorId) |
| .Build()); |
| |
| delegate_.set_max_configurable_pixels(medium_mode_60hz_.size().GetArea()); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first test-modeset the internal display with all other displays |
| // disabled, which will succeed. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), kModesetOutcomeSuccess, |
| // displays_[1] and displays_[2] will be tested and fail together |
| // under connector kInvalidConnectorId. Since neither expose any |
| // alternative modes to try, configuration completely fails. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We commit the last successful test-modeset configuration, which |
| // only enables the internal display. |
| kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), kModesetOutcomeSuccess, |
| nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that four external displays that share two separate bad MST hubs are |
| // tested and fail together, since they are grouped under kInvalidConnectorId. |
| // Also test that this does not affect the internal display's ability configured |
| // separately during retry and passes modeset. |
| TEST_F(ConfigureDisplaysTaskTest, |
| ConfigureInternalAndTwoBadMstHubsWithFourDisplaysFails) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| // Four displays sharing two bad MST Hubs that did not report their PATH |
| // topology correctly. First two: |
| displays_[1] = FakeDisplaySnapshot::Builder() |
| .SetId(456) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kInvalidConnectorId) |
| .Build(); |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(medium_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kInvalidConnectorId) |
| .Build()); |
| |
| // Last two: |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(101112) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(medium_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kInvalidConnectorId) |
| .Build()); |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(131415) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kInvalidConnectorId) |
| .Build()); |
| |
| delegate_.set_max_configurable_pixels(medium_mode_60hz_.size().GetArea()); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::ERROR, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[4]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first test-modeset the internal display with all other displays |
| // disabled, which will succeed. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), |
| GetDisableCrtcAction(displays_[4]).c_str(), kModesetOutcomeSuccess, |
| // displays_[1-4] will be tested and downgraded as a group, since they |
| // share kInvalidConnectorId due to bad MST hubs. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[4]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // displays_[2] will downgrade first, since it is the next largest |
| // display with available alternative modes. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[4]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // displays_[3] will downgrade next, and fail. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[4]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // Same downgrade process as above will repeat for displays_[2] and |
| // displays_[3] before failing completely. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[4]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[4]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We commit the last successful test-modeset configuration, which |
| // only enables the internal display. |
| kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), |
| GetDisableCrtcAction(displays_[4]).c_str(), kModesetOutcomeSuccess, |
| nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| /********************************************************** |
| * Cases that report ConfigureDisplaysTask::PARTIAL_SUCCESS |
| **********************************************************/ |
| |
| // Tests that the last display (in order of available displays) attempts and |
| // succeeds to fallback after it fails to modeset the initial request. |
| TEST_F(ConfigureDisplaysTaskTest, ConfigureLastDisplayPartialSuccess) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| delegate_.set_max_configurable_pixels(medium_mode_60hz_.size().GetArea()); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first attempt to modeset the internal display with all other |
| // displays disabled, which will succeed. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), kModesetOutcomeSuccess, |
| // Last display will fail, downgrade twice, and pass. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, |
| // Commit the last successful test-modeset configuration. |
| kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that the second display (in order of available displays) attempts and |
| // succeeds to fallback after it fails to modeset the initial request. |
| TEST_F(ConfigureDisplaysTaskTest, ConfigureMiddleDisplayPartialSuccess) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(small_mode_60hz_.Clone()) |
| .SetCurrentMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_HDMI) |
| .SetBaseConnectorId(kThirdConnectorId) |
| .Build()); |
| |
| delegate_.set_max_configurable_pixels(medium_mode_60hz_.size().GetArea()); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first test-modeset the internal display with all other displays |
| // disabled, which will succeed. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), kModesetOutcomeSuccess, |
| // Second display will downgrade twice and pass. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), kModesetOutcomeFailure, |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), kModesetOutcomeFailure, |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), kModesetOutcomeSuccess, |
| // Third external display will succeed to modeset on first attempt. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, |
| // Commit the last successful test-modeset configuration. |
| kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that both MST displays fail initial configuration and are tested, |
| // downgraded, and eventually pass modeset as a group and separately from the |
| // internal display. |
| TEST_F(ConfigureDisplaysTaskTest, ConfigureTwoMstDisplaysPartialSuccess) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| // Add an additional display to the base connector kSecondConnectorId via MST. |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build()); |
| |
| delegate_.set_max_configurable_pixels(medium_mode_60hz_.size().GetArea()); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first test-modeset the internal display with all other |
| // displays disabled, which will succeed. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), kModesetOutcomeSuccess, |
| // MST displays will be tested (and fail) together. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // |displays_[1]| will downgrade first. Configuration will fail. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // |displays_[2] will downgrade next. Configuration will fail. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // |displays_[1]| will downgrade again and pass test-modeset. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, |
| // Commit the last successful test-modeset configuration. |
| kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests that the two MST displays, and then the HDMI display fail initial |
| // configuration, are tested, downgraded, and eventually pass modeset as |
| // separate groups. |
| TEST_F(ConfigureDisplaysTaskTest, |
| ConfigureInternalAndTwoMstAndHdmiDisplaysPartialSuccess) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| // Add an additional display to the base connector kSecondConnectorId via MST. |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build()); |
| |
| // Additional independent HDMI display (has its own connector). |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(101112) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(medium_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_HDMI) |
| .SetBaseConnectorId(kThirdConnectorId) |
| .Build()); |
| |
| delegate_.set_max_configurable_pixels(medium_mode_60hz_.size().GetArea()); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first test-modeset the internal display with all other displays |
| // disabled, which will succeed. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeSuccess, |
| // Both MST displays will be tested (and fail) together. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeFailure, |
| // |displays_[1]| will downgrade first. Configuration will fail. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeFailure, |
| // |displays_[2] will downgrade next. Configuration still fails. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeFailure, |
| // |displays_[1]| will downgrade again and pass test-modeset. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeSuccess, |
| // HDMI display will fail test-modeset, downgrade once and pass. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, |
| // Commit the last successful test-modeset configuration. |
| kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &medium_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests a nested MST configuration in which after a successful modeset on the |
| // root branch device (i.e. two external displays connected to a single MST hub) |
| // one display is removed from the original MST hub, connected to a second MST |
| // hub together with a third display, and then the second MST hub is connected |
| // to the first. The tests ensures that the three MST displays are grouped, |
| // tested, and fallback together appropriately before passing modeset. |
| TEST_F(ConfigureDisplaysTaskTest, |
| ConfigureInternalAndMstThenNestAnotherMstForThreeExternalDisplays) { |
| // We now have one internal display + two external displays connected via MST. |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(789) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build()); |
| |
| // Initial configuration succeeds modeset. |
| { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), |
| gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_); |
| EXPECT_EQ( |
| JoinActions(kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction({displays_[1]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({displays_[2]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction({displays_[1]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({displays_[2]->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Add an additional display to kSecondConnectorId. This is akin to unplugging |
| // One display from the first MST hub, attaching it to a second one, together |
| // with a third display, and plugging the second MST hub to the first. |
| displays_.push_back(FakeDisplaySnapshot::Builder() |
| .SetId(101112) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build()); |
| |
| // Simulate bandwidth pressure by reducing configurable pixels. |
| delegate_.set_max_configurable_pixels(medium_mode_60hz_.size().GetArea()); |
| |
| // This configuration requires that all displays connected via the nested |
| // MST setup downgrade, so we test that all three displays are grouped, |
| // fallback appropriately, and eventually succeed modeset. |
| { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), |
| gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. Initiate retry logic. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first test-modeset the internal display with all other |
| // displays disabled, which will succeed. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), |
| GetDisableCrtcAction(displays_[2]).c_str(), |
| GetDisableCrtcAction(displays_[3]).c_str(), kModesetOutcomeSuccess, |
| // All MST displays will fail test-modeset together. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // displays_[1] will downgrade first, then displays_[2], followed by |
| // displays_[3], and finally displays_[1] will downgrade one last |
| // time. Then the configuration will pass test-modeset. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, |
| // Commit the last successful test-modeset configuration. |
| kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[2]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[3]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| } |
| |
| // Tests that an internal display with one external display pass modeset |
| // asynchronously after the external display fallback once. |
| TEST_F(ConfigureDisplaysTaskTest, AsyncConfigureWithTwoDisplaysPartialSuccess) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| delegate_.set_run_async(true); |
| delegate_.set_max_configurable_pixels(medium_mode_60hz_.size().GetArea()); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point()); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_FALSE(callback_called_); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // All displays will fail to modeset together. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first test-modeset the internal display with all other |
| // displays disabled, which will succeed. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetDisableCrtcAction(displays_[1]).c_str(), kModesetOutcomeSuccess, |
| // External display will fail twice, downgrade, and pass. |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &big_mode_29_97hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, |
| // Commit the last successful test-modeset configuration. |
| kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| GetCrtcAction( |
| {displays_[1]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Tests requiring a resources cleanup for an internal display to succeed after |
| // it was closed and the system bandwidth can't handle all displays at big mode. |
| TEST_F(ConfigureDisplaysTaskTest, CloseLidThenOpenLid) { |
| std::unique_ptr<DisplaySnapshot> internal_display = |
| FakeDisplaySnapshot::Builder() |
| .SetId(100) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_INTERNAL) |
| .SetBaseConnectorId(kEdpConnectorId) |
| .Build(); |
| std::unique_ptr<DisplaySnapshot> external_display1 = |
| FakeDisplaySnapshot::Builder() |
| .SetId(200) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build(); |
| std::unique_ptr<DisplaySnapshot> external_display2 = |
| FakeDisplaySnapshot::Builder() |
| .SetId(external_display1->display_id() + 1) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build(); |
| |
| // Turn on all displays at the same time. |
| std::vector<DisplayConfigureRequest> requests; |
| requests.emplace_back(internal_display.get(), internal_display->native_mode(), |
| gfx::Point()); |
| requests.emplace_back(external_display1.get(), |
| external_display1->native_mode(), gfx::Point()); |
| requests.emplace_back(external_display2.get(), |
| external_display2->native_mode(), gfx::Point()); |
| // Set the bandwidth which the system can handle. it should be based on the |
| // area of the mode. |
| // For this test, we wanna support either just 2 monitors at big mode or 1 |
| // internal display + 2 monitors at small mode. This is to mean we can't have |
| // all 3 running at max mode. |
| int supported_option_1 = big_mode_60hz_.size().GetArea() * 2; |
| int supported_option_2 = |
| big_mode_60hz_.size().GetArea() + small_mode_60hz_.size().GetArea() * 2; |
| // pick the biggest one. |
| int supported_bw = std::max(supported_option_1, supported_option_2); |
| // but also make sure we can't run all 3 at max mode. |
| ASSERT_LT(supported_bw, big_mode_60hz_.size().GetArea() * 3); |
| delegate_.set_system_bandwidth_limit(supported_bw); |
| |
| // Run Task turning on all displays. |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| ConfigureDisplaysTask turn_on_all_displays_task(&delegate_, requests, |
| std::move(callback)); |
| turn_on_all_displays_task.Run(); |
| // This case has been checked in all other tests. Just verify and work to move |
| // on to the case we're interested in in this test. |
| ASSERT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_); |
| log_.GetActionsAndClear(); |
| |
| // Simulate closing the lid |
| requests.clear(); |
| requests.emplace_back(internal_display.get(), nullptr, gfx::Point()); |
| requests.emplace_back(external_display1.get(), |
| external_display1->native_mode(), gfx::Point()); |
| requests.emplace_back(external_display2.get(), |
| external_display2->native_mode(), gfx::Point()); |
| callback = base::BindOnce(&ConfigureDisplaysTaskTest::ConfigureCallback, |
| base::Unretained(this)); |
| ConfigureDisplaysTask close_lid(&delegate_, requests, std::move(callback)); |
| close_lid.Run(); |
| EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_); |
| EXPECT_EQ(JoinActions(kTestModesetStr, |
| GetDisableCrtcAction(internal_display).c_str(), |
| GetCrtcAction({external_display1->display_id(), |
| gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({external_display2->display_id(), |
| gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, kCommitModesetStr, |
| GetDisableCrtcAction(internal_display).c_str(), |
| GetCrtcAction({external_display1->display_id(), |
| gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({external_display2->display_id(), |
| gfx::Point(), &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| |
| // Simulate opening the lid as the 2 external displays are already running at |
| // big mode. |
| requests.clear(); |
| requests.emplace_back(internal_display.get(), internal_display->native_mode(), |
| gfx::Point()); |
| requests.emplace_back(external_display1.get(), |
| external_display1->native_mode(), gfx::Point()); |
| requests.emplace_back(external_display2.get(), |
| external_display2->native_mode(), gfx::Point()); |
| callback = base::BindOnce(&ConfigureDisplaysTaskTest::ConfigureCallback, |
| base::Unretained(this)); |
| ConfigureDisplaysTask open_lid(&delegate_, requests, std::move(callback)); |
| open_lid.Run(); |
| ASSERT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_); |
| EXPECT_EQ(JoinActions( |
| // Attempt to turn everything on with the highest mode. |
| kTestModesetStr, |
| GetCrtcAction({internal_display->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({external_display1->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({external_display2->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first test-modeset the internal display with all other |
| // displays disabled, which will succeed. |
| kTestModesetStr, |
| GetCrtcAction({internal_display->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetDisableCrtcAction(external_display1).c_str(), |
| GetDisableCrtcAction(external_display2).c_str(), |
| kModesetOutcomeSuccess, |
| // External displays will attempt to be turned on at big mode. |
| kTestModesetStr, |
| GetCrtcAction({internal_display->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({external_display1->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({external_display2->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // Fallback until success as small mode. |
| kTestModesetStr, |
| GetCrtcAction({internal_display->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({external_display1->display_id(), gfx::Point(), |
| &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({external_display2->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeFailure, kTestModesetStr, |
| GetCrtcAction({internal_display->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({external_display1->display_id(), gfx::Point(), |
| &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({external_display2->display_id(), gfx::Point(), |
| &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, |
| // Commit the last successful test-modeset configuration. |
| kCommitModesetStr, |
| GetCrtcAction({internal_display->display_id(), gfx::Point(), |
| &big_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({external_display1->display_id(), gfx::Point(), |
| &small_mode_60hz_}) |
| .c_str(), |
| GetCrtcAction({external_display2->display_id(), gfx::Point(), |
| &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| TEST_F(ConfigureDisplaysTaskTest, ConfigureVrr) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| for (const auto& display : displays_) { |
| requests.emplace_back(display.get(), display->native_mode(), gfx::Point(), |
| /*enable_vrr=*/true); |
| } |
| |
| ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); |
| task.Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_); |
| EXPECT_EQ(displays_[0]->variable_refresh_rate_state(), |
| VariableRefreshRateState::kVrrEnabled); |
| EXPECT_EQ(displays_[1]->variable_refresh_rate_state(), |
| VariableRefreshRateState::kVrrNotCapable); |
| EXPECT_EQ( |
| JoinActions( |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode(), /*enable_vrr=*/true}) |
| .c_str(), |
| GetCrtcAction({displays_[1]->display_id(), gfx::Point(), |
| &big_mode_60hz_, /*enable_vrr=*/true}) |
| .c_str(), |
| kModesetOutcomeSuccess, kCommitModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode(), /*enable_vrr=*/true}) |
| .c_str(), |
| GetCrtcAction({displays_[1]->display_id(), gfx::Point(), |
| &big_mode_60hz_, /*enable_vrr=*/true}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| // Ensure that progressive modes are prioritized over interlaced modes |
| TEST_F(ConfigureDisplaysTaskTest, TestPrioritizeProgressiveOverInterlaced) { |
| ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( |
| &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); |
| |
| // Only need one display for this test |
| displays_.pop_back(); |
| |
| displays_[0] = FakeDisplaySnapshot::Builder() |
| .SetId(0xBADC0FFEE) |
| .SetNativeMode(big_mode_60hz_.Clone()) |
| .SetCurrentMode(big_mode_60hz_.Clone()) |
| .AddMode(medium_interlaced_mode_60hz_.Clone()) |
| .AddMode(small_mode_60hz_.Clone()) |
| .SetType(DISPLAY_CONNECTION_TYPE_DISPLAYPORT) |
| .SetBaseConnectorId(kSecondConnectorId) |
| .Build(); |
| |
| delegate_.set_max_configurable_pixels( |
| medium_interlaced_mode_60hz_.size().GetArea()); |
| |
| std::vector<DisplayConfigureRequest> requests; |
| requests.emplace_back(displays_[0].get(), displays_[0]->native_mode(), |
| gfx::Point(), /*enable_vrr=*/false); |
| |
| ConfigureDisplaysTask(&delegate_, requests, std::move(callback)).Run(); |
| |
| EXPECT_TRUE(callback_called_); |
| EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_); |
| EXPECT_EQ( |
| JoinActions( |
| // First modeset will fail |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| // We first test-modeset the display with big mode which will fail |
| kTestModesetStr, |
| GetCrtcAction({displays_[0]->display_id(), gfx::Point(), |
| displays_[0]->native_mode()}) |
| .c_str(), |
| kModesetOutcomeFailure, |
| |
| // Next, we should modeset to the small progressive mode, skipping |
| // the interlaced mode which has a higher area |
| kTestModesetStr, |
| GetCrtcAction( |
| {displays_[0]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, kCommitModesetStr, |
| GetCrtcAction( |
| {displays_[0]->display_id(), gfx::Point(), &small_mode_60hz_}) |
| .c_str(), |
| kModesetOutcomeSuccess, nullptr), |
| log_.GetActionsAndClear()); |
| } |
| |
| } // namespace display::test |