chromium/ui/ozone/platform/drm/gpu/drm_overlay_manager_unittest.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/ozone/platform/drm/gpu/drm_overlay_manager.h"

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/test/task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/ozone/public/overlay_surface_candidate.h"

namespace ui {

namespace {

constexpr gfx::AcceleratedWidget kPrimaryWidget = 1;
constexpr gfx::AcceleratedWidget kSecondaryWidget = 2;

class TestDrmOverlayManager : public DrmOverlayManager {
 public:
  explicit TestDrmOverlayManager(
      bool allow_sync_and_real_buffer_page_flip_testing)
      : DrmOverlayManager(/*handle_overlays_swap_failure=*/false,
                          allow_sync_and_real_buffer_page_flip_testing) {}
  TestDrmOverlayManager(bool handle_overlays_swap_failure,
                        bool allow_sync_and_real_buffer_page_flip_testing)
      : DrmOverlayManager(handle_overlays_swap_failure,
                          allow_sync_and_real_buffer_page_flip_testing) {}
  TestDrmOverlayManager() : TestDrmOverlayManager(false) {}
  ~TestDrmOverlayManager() override = default;

  using DrmOverlayManager::UpdateCacheForOverlayCandidates;

  std::vector<std::vector<OverlaySurfaceCandidate>>& requests() {
    return requests_;
  }

  // DrmOverlayManager:
  void SendOverlayValidationRequest(
      const std::vector<OverlaySurfaceCandidate>& candidates,
      gfx::AcceleratedWidget widget) override {
    requests_.push_back(candidates);
  }
  std::vector<OverlayStatus> SendOverlayValidationRequestSync(
      const std::vector<OverlaySurfaceCandidate>& candidates,
      gfx::AcceleratedWidget widget) override {
    requests_.push_back(candidates);
    std::vector<OverlayStatus> status;
    status.reserve(candidates.size());
    for (size_t i = 0; i < candidates.size(); i++) {
      status.emplace_back(candidates[i].overlay_handled
                              ? OverlayStatus::OVERLAY_STATUS_ABLE
                              : OverlayStatus::OVERLAY_STATUS_NOT);
    }
    return status;
  }
  void GetHardwareCapabilities(
      gfx::AcceleratedWidget widget,
      HardwareCapabilitiesCallback& receive_callback) override {
    HardwareCapabilities hardware_capabilities;
    hardware_capabilities.num_overlay_capable_planes = num_planes_response_;
    // Immediately respond to the callback.
    receive_callback.Run(hardware_capabilities);
  }

  base::TimeTicks GetDisallowFullscreenOverlaysEndTime() const {
    return disallow_fullscreen_overlays_end_time();
  }

  int num_planes_response_ = 0;

 private:
  std::vector<std::vector<OverlaySurfaceCandidate>> requests_;
};

OverlaySurfaceCandidate CreateCandidate(const gfx::Rect& rect,
                                        int plane_z_order) {
  OverlaySurfaceCandidate candidate;
  candidate.transform = gfx::OVERLAY_TRANSFORM_NONE;
  candidate.format = gfx::BufferFormat::YUV_420_BIPLANAR;
  candidate.plane_z_order = plane_z_order;
  candidate.buffer_size = rect.size();
  candidate.display_rect = gfx::RectF(rect);
  candidate.crop_rect = gfx::RectF(rect);
  return candidate;
}

class DrmOverlayManagerTest : public testing::Test {
 public:
  DrmOverlayManagerTest() = default;

  void SetUp() override {
    manager_.SetSupportedBufferFormats(kPrimaryWidget,
                                       {gfx::BufferFormat::YUV_420_BIPLANAR});
    manager_.SetSupportedBufferFormats(kSecondaryWidget,
                                       {gfx::BufferFormat::YUV_420_BIPLANAR});
  }

 protected:
  TestDrmOverlayManager manager_;
};

}  // namespace

TEST_F(DrmOverlayManagerTest, CacheLogic) {
  // Candidates for output surface and single-on-top quad.
  std::vector<OverlaySurfaceCandidate> candidates = {
      CreateCandidate(gfx::Rect(0, 0, 100, 100), 0),
      CreateCandidate(gfx::Rect(10, 10, 20, 20), 1)};

  // The first three times that CheckOverlaySupport() is called for an overlay
  // configuration it won't send a validation request.
  for (int i = 0; i < 3; ++i) {
    manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
    EXPECT_FALSE(candidates[0].overlay_handled);
    EXPECT_FALSE(candidates[1].overlay_handled);
    EXPECT_EQ(manager_.requests().size(), 0u);
  }

  // The fourth call with the same overlay configuration should trigger a
  // request to validate the configuration. Still assume the overlay
  // configuration won't work until we get a response.
  manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
  EXPECT_FALSE(candidates[0].overlay_handled);
  EXPECT_FALSE(candidates[1].overlay_handled);
  EXPECT_EQ(manager_.requests().size(), 1u);

  // While waiting for a response we shouldn't send the same request.
  manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
  EXPECT_FALSE(candidates[0].overlay_handled);
  EXPECT_FALSE(candidates[1].overlay_handled);
  ASSERT_EQ(manager_.requests().size(), 1u);

  // Receive response that the overlay configuration will work.
  manager_.UpdateCacheForOverlayCandidates(
      manager_.requests().front(), kPrimaryWidget,
      std::vector<OverlayStatus>(candidates.size(), OVERLAY_STATUS_ABLE));
  manager_.requests().clear();

  // CheckOverlaySupport() should now indicate the overlay configuration will
  // work.
  manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
  EXPECT_TRUE(candidates[0].overlay_handled);
  EXPECT_TRUE(candidates[1].overlay_handled);
  EXPECT_EQ(manager_.requests().size(), 0u);
}

// Tests that the crop rect changing will make a new request.
TEST_F(DrmOverlayManagerTest, CropRectCacheLogic) {
  // Candidates fo single-on-top quad.
  std::vector<OverlaySurfaceCandidate> candidates = {
      CreateCandidate(gfx::Rect(0, 0, 100, 100), 0)};

  // The first three times won't send a validation request. The fourth will send
  // a request.
  for (int i = 0; i < 3; ++i) {
    manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
    EXPECT_FALSE(candidates.back().overlay_handled);
    EXPECT_EQ(manager_.requests().size(), 0u);
  }
  manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
  EXPECT_EQ(manager_.requests().size(), 1u);

  // Receive response that the overlay configuration will work.
  manager_.UpdateCacheForOverlayCandidates(
      manager_.requests().front(), kPrimaryWidget,
      std::vector<OverlayStatus>(candidates.size(), OVERLAY_STATUS_ABLE));
  manager_.requests().clear();

  // Overlay configuration should work.
  manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
  EXPECT_TRUE(candidates.back().overlay_handled);

  // Now, adjust the crop rect.
  candidates.back().overlay_handled = false;
  candidates.back().crop_rect = gfx::RectF(0, 0, 0.5f, 0.5f);

  // The first three times won't send a validation request. The fourth will send
  // a request.
  for (int i = 0; i < 3; ++i) {
    manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
    EXPECT_FALSE(candidates.back().overlay_handled);
    EXPECT_EQ(manager_.requests().size(), 0u);
  }
  manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
  EXPECT_EQ(manager_.requests().size(), 1u);

  // Receive response that the overlay configuration won't work.
  manager_.UpdateCacheForOverlayCandidates(
      manager_.requests().front(), kPrimaryWidget,
      std::vector<OverlayStatus>(candidates.size(), OVERLAY_STATUS_NOT));
  manager_.requests().clear();

  // Overlay configuration should not work.
  manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
  EXPECT_FALSE(candidates.back().overlay_handled);
}

TEST_F(DrmOverlayManagerTest, DifferentWidgetCache) {
  // Candidates for output surface and single-on-top quad.
  std::vector<OverlaySurfaceCandidate> candidates = {
      CreateCandidate(gfx::Rect(0, 0, 100, 100), 0),
      CreateCandidate(gfx::Rect(10, 10, 20, 20), 1)};

  // Call 4 Times to go beyond the Throttle Request Size
  ASSERT_EQ(manager_.requests().size(), 0u);
  for (int i = 0; i < 4; ++i) {
    manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
    EXPECT_FALSE(candidates[0].overlay_handled);
    EXPECT_FALSE(candidates[1].overlay_handled);
  }
  ASSERT_EQ(manager_.requests().size(), 1u);

  // Receive response that the overlay configuration on kPrimaryWidget will
  // work.
  manager_.UpdateCacheForOverlayCandidates(
      manager_.requests().front(), kPrimaryWidget,
      std::vector<OverlayStatus>(candidates.size(), OVERLAY_STATUS_ABLE));
  manager_.requests().clear();

  // Overlay should not be handled when using a different widget
  manager_.CheckOverlaySupport(&candidates, kSecondaryWidget);
  EXPECT_FALSE(candidates[0].overlay_handled);
  EXPECT_FALSE(candidates[1].overlay_handled);
  EXPECT_EQ(manager_.requests().size(), 0u);
}

TEST_F(DrmOverlayManagerTest, MultipleWidgetCacheSupport) {
  // Candidates for output surface and single-on-top quad.
  std::vector<OverlaySurfaceCandidate> candidates = {
      CreateCandidate(gfx::Rect(0, 0, 100, 100), 0),
      CreateCandidate(gfx::Rect(10, 10, 20, 20), 1)};

  // Call 4 Times to go beyond the Throttle Request Size
  ASSERT_EQ(manager_.requests().size(), 0u);
  for (int i = 0; i < 4; ++i) {
    manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
    EXPECT_FALSE(candidates[0].overlay_handled);
    EXPECT_FALSE(candidates[1].overlay_handled);
    manager_.CheckOverlaySupport(&candidates, kSecondaryWidget);
    EXPECT_FALSE(candidates[0].overlay_handled);
    EXPECT_FALSE(candidates[1].overlay_handled);
  }
  EXPECT_EQ(manager_.requests().size(), 2u);

  // Receive response that the overlay configuration on kPrimaryWidget will
  // work.
  manager_.UpdateCacheForOverlayCandidates(
      manager_.requests().front(), kPrimaryWidget,
      std::vector<OverlayStatus>(candidates.size(), OVERLAY_STATUS_ABLE));

  manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
  EXPECT_TRUE(candidates[0].overlay_handled);
  EXPECT_TRUE(candidates[1].overlay_handled);

  // Receive response that the overlay configuration on kSecondaryWidget will
  // work.
  manager_.UpdateCacheForOverlayCandidates(
      manager_.requests().front(), kSecondaryWidget,
      std::vector<OverlayStatus>(candidates.size(), OVERLAY_STATUS_ABLE));
  manager_.requests().clear();

  // Both Widgets should be handled
  manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
  EXPECT_TRUE(candidates[0].overlay_handled);
  EXPECT_TRUE(candidates[1].overlay_handled);
  manager_.CheckOverlaySupport(&candidates, kSecondaryWidget);
  EXPECT_TRUE(candidates[0].overlay_handled);
  EXPECT_TRUE(candidates[1].overlay_handled);
  EXPECT_EQ(manager_.requests().size(), 0u);
}

TEST_F(DrmOverlayManagerTest, DifferentWidgetsSameCandidatesAreDistinct) {
  // Candidates for output surface and single-on-top quad.
  std::vector<OverlaySurfaceCandidate> candidates = {
      CreateCandidate(gfx::Rect(0, 0, 100, 100), 0),
      CreateCandidate(gfx::Rect(10, 10, 20, 20), 1)};

  // Looping 4 times, but each widget is only checked twice, below the Throttle
  // Request Size (3).
  for (int i = 0; i < 4; ++i) {
    manager_.CheckOverlaySupport(&candidates,
                                 i % 2 ? kPrimaryWidget : kSecondaryWidget);
  }
  EXPECT_EQ(manager_.requests().size(), 0u);
}

TEST_F(DrmOverlayManagerTest, NonIntegerDisplayRect) {
  // Candidates for output surface and single-on-top quad.
  std::vector<OverlaySurfaceCandidate> candidates = {
      CreateCandidate(gfx::Rect(0, 0, 100, 100), 0),
      CreateCandidate(gfx::Rect(10, 10, 20, 20), 1)};

  // Submit a set of candidates that could potentially be displayed in an
  // overlay so they are stored in the cache. This ensures a comparison gets
  // made adding the next value to the cache.
  manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);

  // Modify the display_rect for the second candidate so it's non-integer. We
  // will never try to promote this to an overlay but something will get stored
  // in the cache. This verifies we don't try to convert the non-integer RectF
  // into a Rect which DCHECKs.
  candidates[1].display_rect = gfx::RectF(9.4, 10.43, 20.11, 20.99);
  manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);
}

TEST_F(DrmOverlayManagerTest, RequiredOverlayMultiDisplay) {
  // Primary has a requirement, secondary does not, should only make a request
  // on the primary.
  std::vector<OverlaySurfaceCandidate> candidates1 = {
      CreateCandidate(gfx::Rect(0, 0, 100, 100), 0)};

  manager_.RegisterOverlayRequirement(kPrimaryWidget, true);
  manager_.RegisterOverlayRequirement(kSecondaryWidget, false);
  // Call 4 Times to go beyond the Throttle Request Size
  for (int i = 0; i < 4; ++i)
    manager_.CheckOverlaySupport(&candidates1, kPrimaryWidget);
  EXPECT_EQ(manager_.requests().size(), 1u);
  // Call 4 Times to go beyond the Throttle Request Size
  for (int i = 0; i < 4; ++i)
    manager_.CheckOverlaySupport(&candidates1, kSecondaryWidget);
  EXPECT_EQ(manager_.requests().size(), 1u);
  manager_.requests().clear();

  // Secondary has a requirement, primary does not, should only make a request
  // on the secondary.
  std::vector<OverlaySurfaceCandidate> candidates2 = {
      CreateCandidate(gfx::Rect(0, 0, 200, 200), 0)};

  manager_.RegisterOverlayRequirement(kPrimaryWidget, false);
  manager_.RegisterOverlayRequirement(kSecondaryWidget, true);
  // Call 4 Times to go beyond the Throttle Request Size
  for (int i = 0; i < 4; ++i)
    manager_.CheckOverlaySupport(&candidates2, kPrimaryWidget);
  EXPECT_TRUE(manager_.requests().empty());
  // Call 4 Times to go beyond the Throttle Request Size
  for (int i = 0; i < 4; ++i)
    manager_.CheckOverlaySupport(&candidates2, kSecondaryWidget);
  EXPECT_EQ(manager_.requests().size(), 1u);
}

TEST_F(DrmOverlayManagerTest, ObservingHardwareCapabilities) {
  manager_.num_planes_response_ = 2;

  int primary_calls = 0;
  HardwareCapabilitiesCallback primary_callback = base::BindRepeating(
      [](int* calls, HardwareCapabilities hc) {
        (*calls)++;
        EXPECT_EQ(hc.num_overlay_capable_planes, 2);
      },
      &primary_calls);
  manager_.StartObservingHardwareCapabilities(kPrimaryWidget, primary_callback);
  EXPECT_EQ(primary_calls, 1);

  manager_.DisplaysConfigured();

  EXPECT_EQ(primary_calls, 2);

  int secondary_calls = 0;
  HardwareCapabilitiesCallback secondary_callback = base::BindRepeating(
      [](int* calls, HardwareCapabilities hc) {
        (*calls)++;
        EXPECT_EQ(hc.num_overlay_capable_planes, 2);
      },
      &secondary_calls);
  manager_.StartObservingHardwareCapabilities(kSecondaryWidget,
                                              secondary_callback);
  // Only the secondary callback should be called.
  EXPECT_EQ(primary_calls, 2);
  EXPECT_EQ(secondary_calls, 1);

  manager_.DisplaysConfigured();

  // Both callbacks are called.
  EXPECT_EQ(primary_calls, 3);
  EXPECT_EQ(secondary_calls, 2);

  manager_.StopObservingHardwareCapabilities(kPrimaryWidget);
  manager_.DisplaysConfigured();
  manager_.DisplaysConfigured();

  // The primary callback won't be called anymore.
  EXPECT_EQ(primary_calls, 3);
  EXPECT_EQ(secondary_calls, 4);
}

TEST_F(DrmOverlayManagerTest, SingleClipRectUnderlaySupport) {
  // Candidates for output surface and underlay quad.
  std::vector<OverlaySurfaceCandidate> candidates = {
      CreateCandidate(gfx::Rect(0, 0, 100, 100), 0),
      CreateCandidate(gfx::Rect(10, 10, 20, 20), -1)};

  // Set a clip rect that imposes a restriction on |display_rect|.
  candidates[1].clip_rect = gfx::Rect(10, 10, 15, 15);

  for (int i = 0; i < 4; i++)
    manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);

  EXPECT_EQ(manager_.requests().size(), 1u);
  EXPECT_TRUE(manager_.requests()[0][1].overlay_handled);

  manager_.requests().clear();
  // Now make the overlay candidate a single-on-top overlay. Single-on-top
  // overlays with restrictive clip rects are not supported.
  candidates[1].plane_z_order = 1;
  for (int i = 0; i < 4; i++)
    manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);

  EXPECT_EQ(manager_.requests().size(), 1u);
  EXPECT_FALSE(manager_.requests()[0][1].overlay_handled);
}

TEST_F(DrmOverlayManagerTest, MultiClipRectUnderlaySupport) {
  // Two underlay quads who's |display_rect| overlap. The order here is
  // important; even though the -2 underlay comes first in the list it will be
  // occluded by the -1 underlay and when -1 clipped the -2 underlay should
  // fail.
  std::vector<OverlaySurfaceCandidate> candidates = {
      CreateCandidate(gfx::Rect(0, 0, 100, 100), 0),
      CreateCandidate(gfx::Rect(10, 10, 20, 20), -2),
      CreateCandidate(gfx::Rect(20, 20, 20, 20), -1)};

  // Set a clip rect that imposes a restriction on |display_rect|.
  candidates[1].clip_rect = gfx::Rect(10, 10, 15, 15);
  candidates[2].clip_rect = gfx::Rect(20, 20, 15, 15);

  for (int i = 0; i < 4; i++)
    manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);

  EXPECT_EQ(manager_.requests().size(), 1u);
  EXPECT_FALSE(manager_.requests()[0][1].overlay_handled);
  EXPECT_TRUE(manager_.requests()[0][2].overlay_handled);

  manager_.requests().clear();
  // Now remove the clipping constraint on the -1 underlay which should allow
  // the -2 underlay to be handled.
  candidates[2].clip_rect = gfx::Rect(20, 20, 50, 50);
  for (int i = 0; i < 4; i++)
    manager_.CheckOverlaySupport(&candidates, kPrimaryWidget);

  EXPECT_EQ(manager_.requests().size(), 1u);
  EXPECT_TRUE(manager_.requests()[0][1].overlay_handled);
  EXPECT_TRUE(manager_.requests()[0][2].overlay_handled);
}

TEST_F(DrmOverlayManagerTest, SupportedBufferFormat) {
  // Make the manager to use sync testing for convenience.
  TestDrmOverlayManager manager(true);
  manager.SetSupportedBufferFormats(
      kPrimaryWidget,
      {gfx::BufferFormat::BGRA_8888, gfx::BufferFormat::RGBA_8888});
  manager.SetSupportedBufferFormats(kSecondaryWidget,
                                    {gfx::BufferFormat::YUV_420_BIPLANAR});

  std::vector<OverlaySurfaceCandidate> candidates = {
      CreateCandidate(gfx::Rect(0, 0, 150, 150), -1),
      CreateCandidate(gfx::Rect(0, 0, 100, 100), 0),
      CreateCandidate(gfx::Rect(10, 10, 20, 20), 1)};
  candidates[0].format = gfx::BufferFormat::RGBA_8888;
  candidates[1].format = gfx::BufferFormat::YUV_420_BIPLANAR;
  candidates[2].format = gfx::BufferFormat::BGRA_8888;

  // Primary widget supports BGRA/RGBA only.
  manager.CheckOverlaySupport(&candidates, kPrimaryWidget);
  EXPECT_TRUE(candidates[0].overlay_handled);
  EXPECT_FALSE(candidates[1].overlay_handled);
  EXPECT_TRUE(candidates[2].overlay_handled);
  EXPECT_EQ(manager.requests().size(), 1u);

  auto reset_candidates = [](std::vector<OverlaySurfaceCandidate>& candidates) {
    for (auto& candidate : candidates) {
      candidate.overlay_handled = false;
    }
  };

  manager.requests().clear();
  reset_candidates(candidates);

  // Secondary widget supports N12 only.
  manager.CheckOverlaySupport(&candidates, kSecondaryWidget);
  EXPECT_FALSE(candidates[0].overlay_handled);
  EXPECT_TRUE(candidates[1].overlay_handled);
  EXPECT_FALSE(candidates[2].overlay_handled);
  EXPECT_EQ(manager.requests().size(), 1u);

  manager.requests().clear();
  reset_candidates(candidates);

  // Make primary widget support more buffer formats.
  manager.SetSupportedBufferFormats(
      kPrimaryWidget,
      {gfx::BufferFormat::YUV_420_BIPLANAR, gfx::BufferFormat::BGRA_8888,
       gfx::BufferFormat::RGBA_8888});

  // Primary widget supports BGRA/RGBA and NV12 now.
  manager.CheckOverlaySupport(&candidates, kPrimaryWidget);
  EXPECT_TRUE(candidates[0].overlay_handled);
  EXPECT_TRUE(candidates[1].overlay_handled);
  EXPECT_TRUE(candidates[2].overlay_handled);
  EXPECT_EQ(manager.requests().size(), 1u);
}

// Verifies that the |TestDrmOverlayManager| uses fast path for fullscreen
// overlays. That is, if |handle_overlays_swap_failure| is enabled, it marks
// fullscreen overlays as suitable candidates, but once it gets a swap failure
// notification, it fallbacks to drm testing.
TEST_F(DrmOverlayManagerTest, HandleFastPathFullScreenOverlays) {
  base::test::SingleThreadTaskEnvironment env(
      base::test::TaskEnvironment::TimeSource::MOCK_TIME);
  TestDrmOverlayManager manager(
      /*handle_overlays_swap_failure=*/true,
      /*allow_sync_and_real_buffer_page_flip_testing=*/true);
  manager.SetSupportedBufferFormats(kPrimaryWidget,
                                    {gfx::BufferFormat::YUV_420_BIPLANAR});

  // Check overlay support and expect fullscreen is handled without any requests
  // for overlays' validation sent.
  std::vector<OverlaySurfaceCandidate> candidates = {
      CreateCandidate(gfx::Rect(0, 0, 100, 100), 0)};
  candidates.front().overlay_type = gfx::OverlayType::kFullScreen;

  manager.CheckOverlaySupport(&candidates, kPrimaryWidget);

  EXPECT_EQ(manager.requests().size(), 0u);
  EXPECT_TRUE(candidates.front().overlay_handled);

  // Notify the manager that the fullscreen overlay were promoted and the
  // next swap is a fullscreen one.
  manager.OnPromotedOverlayTypes({gfx::OverlayType::kFullScreen});

  // Store the current time and use it later to fast forward it.
  const auto time_now = base::TimeTicks::Now();
  // The swap has failed. The manager must stop fullscreen overlays' promotion.
  manager.OnSwapBuffersComplete(
      gfx::SwapResult::SWAP_NON_SIMPLE_OVERLAYS_FAILED);
  EXPECT_TRUE(!manager.GetDisallowFullscreenOverlaysEndTime().is_null());

  // Now that the previous fullscreen overlay's swap failed, the manager must
  // fallback to drm test for these overlays as well.
  std::vector<OverlaySurfaceCandidate> candidates2 = {
      CreateCandidate(gfx::Rect(0, 0, 100, 100), 0)};
  candidates2.front().overlay_type = gfx::OverlayType::kFullScreen;

  manager.CheckOverlaySupport(&candidates, kPrimaryWidget);

  // As expected, there are validation requests.
  EXPECT_EQ(manager.requests().size(), 1u);
  EXPECT_TRUE(candidates.front().overlay_handled);
  manager.requests().clear();

  // Fast forward the time as the manager waits X hours until it can promote
  // the fullscreen overlays again.
  size_t kFastForwardAttempts = 5;
  while (!manager.GetDisallowFullscreenOverlaysEndTime().is_null()) {
    env.FastForwardBy(manager.GetDisallowFullscreenOverlaysEndTime() -
                      time_now);
    // Break in case if something goes very wrong.
    if (--kFastForwardAttempts <= 0) {
      break;
    }
  }
  std::vector<OverlaySurfaceCandidate> candidates3 = {
      CreateCandidate(gfx::Rect(0, 0, 100, 100), 0)};
  candidates3.front().overlay_type = gfx::OverlayType::kFullScreen;

  manager.CheckOverlaySupport(&candidates, kPrimaryWidget);

  // Sanity check.
  ASSERT_TRUE(manager.GetDisallowFullscreenOverlaysEndTime().is_null());

  // As expected, there are no validation requests now and fullscreen overlays
  // can be promoted without validation now.
  EXPECT_EQ(manager.requests().size(), 0u);
  EXPECT_TRUE(candidates.front().overlay_handled);
}

}  // namespace ui