text: // Copyright 2019 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "ui/ozone/platform/drm/gpu/drm_overlay_manager.h"
  
  #include "base/bind.h"
  #include "base/callback.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:
    TestDrmOverlayManager() : DrmOverlayManager(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 {
      return {};
    }
  
   private:
    std::vector<std::vector<OverlaySurfaceCandidate>> requests_;
  };
  
  OverlaySurfaceCandidate CreateCandidate(const gfx::Rect& rect,
                                          int plane_z_order) {
    ui::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;
  }
  
  }  // namespace
  
  TEST(DrmOverlayManagerTest, CacheLogic) {
    TestDrmOverlayManager manager;
  
    // 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(DrmOverlayManagerTest, CropRectCacheLogic) {
    TestDrmOverlayManager manager;
  
    // 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(DrmOverlayManagerTest, DifferentWidgetCache) {
    TestDrmOverlayManager manager;
  
    // 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(DrmOverlayManagerTest, MultipleWidgetCacheSupport) {
    TestDrmOverlayManager manager;
  
    // 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(DrmOverlayManagerTest, DifferentWidgetsSameCandidatesAreDistinct) {
    TestDrmOverlayManager manager;
  
    // 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(DrmOverlayManagerTest, NonIntegerDisplayRect) {
    TestDrmOverlayManager manager;
  
    // 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);
  }
  
  }  // namespace ui
  