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

#include "media/gpu/chromeos/native_pixmap_frame_resource.h"

#include <stddef.h>
#include <stdint.h>

#include <optional>

#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "media/base/color_plane_layout.h"
#include "media/base/format_utils.h"
#include "media/base/video_frame.h"
#include "media/base/video_frame_layout.h"
#include "media/base/video_types.h"
#include "media/gpu/chromeos/mock_native_pixmap_dmabuf.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"

namespace media {

namespace {

// Creates a vector of file descriptors. Used to simulate a vector of DMABuf
// FDs.
std::vector<base::ScopedFD> CreateMockDMABufs(size_t num_planes) {
  std::vector<base::ScopedFD> dmabuf_fds;
  for (size_t i = 0; i < num_planes; i++) {
    // Instead of creating an FD of a DMABuf, this makes an FD for /dev/null.
    base::File file(base::FilePath("/dev/null"),
                    base::File::FLAG_OPEN | base::File::FLAG_READ);
    if (!file.IsValid()) {
      LOG(ERROR) << "Failed to open a file";
      return std::vector<base::ScopedFD>();
    }
    dmabuf_fds.emplace_back(file.TakePlatformFile());
    if (!dmabuf_fds.back().is_valid()) {
      LOG(ERROR) << "The FD taken from file is not valid";
      return std::vector<base::ScopedFD>();
    }
  }
  return dmabuf_fds;
}

}  // namespace

class CreateTest : public ::testing::Test,
                   public ::testing::WithParamInterface<
                       std::tuple<gfx::Size, gfx::Rect, gfx::Size, bool>> {
 public:
  void SetUp() override {}
  void TearDown() override {}

  // The parameters.
  static constexpr size_t kCodedSizeIndex = 0;
  static constexpr size_t kVisibleRectIndex = 1;
  static constexpr size_t kNaturalSizeIndex = 2;
  static constexpr size_t kShouldSucceedIndex = 3;
};

// Tests the size checks of NativePixmapFrameResource's DMABuf FD-based factory
// function.
TEST_P(CreateTest, CreateFromDMABufs) {
  constexpr VideoPixelFormat kPixelFormat = PIXEL_FORMAT_NV12;
  constexpr base::TimeDelta kTimestamp;

  const gfx::Size coded_size = std::get<kCodedSizeIndex>(GetParam());
  const gfx::Rect visible_rect = std::get<kVisibleRectIndex>(GetParam());
  const gfx::Size natural_size = std::get<kNaturalSizeIndex>(GetParam());
  const bool should_succeed = std::get<kShouldSucceedIndex>(GetParam());

  const std::optional<VideoFrameLayout> layout =
      VideoFrameLayout::Create(kPixelFormat, coded_size);
  ASSERT_TRUE(layout.has_value()) << "Failed to create video frame layout";

  auto dmabuf_fds = CreateMockDMABufs(layout->num_planes());
  ASSERT_FALSE(dmabuf_fds.empty());

  auto frame = NativePixmapFrameResource::Create(
      *layout, visible_rect, natural_size, std::move(dmabuf_fds), kTimestamp);
  if (should_succeed) {
    EXPECT_TRUE(!!frame);
  } else {
    EXPECT_EQ(frame, nullptr);
  }
}

// Tests the size checks of NativePixmapFrameResource's NativePixmapDmaBuf-based
// factory function.
TEST_P(CreateTest, CreateFromNativePixmapDmabuf) {
  constexpr VideoPixelFormat kPixelFormat = PIXEL_FORMAT_NV12;
  constexpr gfx::BufferUsage kBufferUsage = gfx::BufferUsage::GPU_READ;
  constexpr base::TimeDelta kTimestamp;

  const gfx::Size coded_size = std::get<kCodedSizeIndex>(GetParam());
  const gfx::Rect visible_rect = std::get<kVisibleRectIndex>(GetParam());
  const gfx::Size natural_size = std::get<kNaturalSizeIndex>(GetParam());
  const bool should_succeed = std::get<kShouldSucceedIndex>(GetParam());

  scoped_refptr<const gfx::NativePixmapDmaBuf> pixmap =
      CreateMockNativePixmapDmaBuf(kPixelFormat, coded_size);
  ASSERT_TRUE(!!pixmap);
  auto frame = NativePixmapFrameResource::Create(
      visible_rect, natural_size, kTimestamp, kBufferUsage, std::move(pixmap));
  if (should_succeed) {
    EXPECT_TRUE(!!frame);
  } else {
    EXPECT_EQ(frame, nullptr);
  }
}

INSTANTIATE_TEST_SUITE_P(NativePixmapFrameResourceValidSize,
                         CreateTest,
                         ::testing::Values(std::make_tuple(gfx::Size(320, 240),
                                                           gfx::Rect(320, 240),
                                                           gfx::Size(320, 240),
                                                           true)));

INSTANTIATE_TEST_SUITE_P(NativePixmapFrameResourceInvalidSize,
                         CreateTest,
                         ::testing::Values(std::make_tuple(gfx::Size(),
                                                           gfx::Rect(),
                                                           gfx::Size(),
                                                           false),
                                           std::make_tuple(gfx::Size(320, 240),
                                                           gfx::Rect(),
                                                           gfx::Size(320, 240),
                                                           false),
                                           std::make_tuple(gfx::Size(320, 240),
                                                           gfx::Rect(320, 240),
                                                           gfx::Size(),
                                                           false)));

}  // namespace media
