#include <android-base/properties.h>
#include <base/logging.h>
#include <gtest/gtest.h>
#include <log/log.h>
#include <poll.h>

#include <android/hardware_buffer.h>

#include <algorithm>
#include <array>
#include <set>
#include <thread>
#include <vector>

#include <dvr/dvr_configuration_data.h>
#include <dvr/dvr_deleter.h>
#include <dvr/dvr_display_manager.h>
#include <dvr/dvr_surface.h>

#include <pdx/status.h>

using android::pdx::ErrorStatus;
using android::pdx::Status;

namespace android {
namespace dvr {

namespace {

DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, nullptr_t) {
  DvrSurfaceAttribute attribute;
  attribute.key = key;
  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_NONE;
  return attribute;
}

DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, int32_t value) {
  DvrSurfaceAttribute attribute;
  attribute.key = key;
  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32;
  attribute.value.int32_value = value;
  return attribute;
}

DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, int64_t value) {
  DvrSurfaceAttribute attribute;
  attribute.key = key;
  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT64;
  attribute.value.int64_value = value;
  return attribute;
}

DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, bool value) {
  DvrSurfaceAttribute attribute;
  attribute.key = key;
  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL;
  attribute.value.bool_value = value;
  return attribute;
}

DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, float value) {
  DvrSurfaceAttribute attribute;
  attribute.key = key;
  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT;
  attribute.value.float_value = value;
  return attribute;
}

DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
                                  const std::array<float, 2>& value) {
  DvrSurfaceAttribute attribute;
  attribute.key = key;
  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2;
  std::copy(value.begin(), value.end(), attribute.value.float2_value);
  return attribute;
}

DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
                                  const std::array<float, 3>& value) {
  DvrSurfaceAttribute attribute;
  attribute.key = key;
  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3;
  std::copy(value.begin(), value.end(), attribute.value.float3_value);
  return attribute;
}

DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
                                  const std::array<float, 4>& value) {
  DvrSurfaceAttribute attribute;
  attribute.key = key;
  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4;
  std::copy(value.begin(), value.end(), attribute.value.float4_value);
  return attribute;
}

DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
                                  const std::array<float, 8>& value) {
  DvrSurfaceAttribute attribute;
  attribute.key = key;
  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8;
  std::copy(value.begin(), value.end(), attribute.value.float8_value);
  return attribute;
}

DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
                                  const std::array<float, 16>& value) {
  DvrSurfaceAttribute attribute;
  attribute.key = key;
  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16;
  std::copy(value.begin(), value.end(), attribute.value.float16_value);
  return attribute;
}

Status<UniqueDvrSurface> CreateApplicationSurface(bool visible = false,
                                                  int32_t z_order = 0) {
  DvrSurface* surface = nullptr;
  DvrSurfaceAttribute attributes[] = {
      MakeAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, z_order),
      MakeAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, visible)};

  const int ret = dvrSurfaceCreate(
      attributes, std::extent<decltype(attributes)>::value, &surface);
  if (ret < 0)
    return ErrorStatus(-ret);
  else
    return {UniqueDvrSurface(surface)};
}

Status<UniqueDvrWriteBufferQueue> CreateSurfaceQueue(
    const UniqueDvrSurface& surface, uint32_t width, uint32_t height,
    uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity,
    size_t metadata_size) {
  DvrWriteBufferQueue* queue;
  const int ret = dvrSurfaceCreateWriteBufferQueue(
      surface.get(), width, height, format, layer_count, usage, capacity,
      metadata_size, &queue);
  if (ret < 0)
    return ErrorStatus(-ret);
  else
    return {UniqueDvrWriteBufferQueue(queue)};
}

class TestDisplayManager {
 public:
  TestDisplayManager(UniqueDvrDisplayManager display_manager,
                     UniqueDvrSurfaceState surface_state)
      : display_manager_(std::move(display_manager)),
        surface_state_(std::move(surface_state)) {
    const int fd = dvrDisplayManagerGetEventFd(display_manager_.get());
    LOG_IF(INFO, fd < 0) << "Failed to get event fd: " << strerror(-fd);
    display_manager_event_fd_ = fd;
  }

  Status<UniqueDvrReadBufferQueue> GetReadBufferQueue(int surface_id,
                                                      int queue_id) {
    DvrReadBufferQueue* queue;
    const int ret = dvrDisplayManagerGetReadBufferQueue(
        display_manager_.get(), surface_id, queue_id, &queue);
    if (ret < 0)
      return ErrorStatus(-ret);
    else
      return {UniqueDvrReadBufferQueue(queue)};
  }

  Status<void> UpdateSurfaceState() {
    const int ret = dvrDisplayManagerGetSurfaceState(display_manager_.get(),
                                                     surface_state_.get());
    if (ret < 0)
      return ErrorStatus(-ret);
    else
      return {};
  }

  enum : int { kTimeoutMs = 10000 };  // 10s

  Status<void> WaitForUpdate(int timeout_ms = kTimeoutMs) {
    if (display_manager_event_fd_ < 0)
      return ErrorStatus(-display_manager_event_fd_);

    pollfd pfd = {display_manager_event_fd_, POLLIN, 0};
    const int count = poll(&pfd, 1, timeout_ms);
    if (count < 0)
      return ErrorStatus(errno);
    else if (count == 0)
      return ErrorStatus(ETIMEDOUT);

    int events;
    const int ret = dvrDisplayManagerTranslateEpollEventMask(
        display_manager_.get(), pfd.revents, &events);
    if (ret < 0)
      return ErrorStatus(-ret);
    else if (events & POLLIN)
      return UpdateSurfaceState();
    else
      return ErrorStatus(EPROTO);
  }

  Status<size_t> GetSurfaceCount() {
    size_t count = 0;
    const int ret =
        dvrSurfaceStateGetSurfaceCount(surface_state_.get(), &count);
    if (ret < 0)
      return ErrorStatus(-ret);
    else
      return {count};
  }

  Status<DvrSurfaceUpdateFlags> GetUpdateFlags(size_t surface_index) {
    DvrSurfaceUpdateFlags update_flags;
    const int ret = dvrSurfaceStateGetUpdateFlags(surface_state_.get(),
                                                  surface_index, &update_flags);
    if (ret < 0)
      return ErrorStatus(-ret);
    else
      return {update_flags};
  }

  Status<int> GetSurfaceId(size_t surface_index) {
    int surface_id;
    const int ret = dvrSurfaceStateGetSurfaceId(surface_state_.get(),
                                                surface_index, &surface_id);
    if (ret < 0)
      return ErrorStatus(-ret);
    else
      return {surface_id};
  }

  Status<int> GetProcessId(size_t surface_index) {
    int process_id;
    const int ret = dvrSurfaceStateGetProcessId(surface_state_.get(),
                                                surface_index, &process_id);
    if (ret < 0)
      return ErrorStatus(-ret);
    else
      return {process_id};
  }

  Status<std::vector<DvrSurfaceAttribute>> GetAttributes(size_t surface_index) {
    std::vector<DvrSurfaceAttribute> attributes;
    size_t count = 0;
    const int ret = dvrSurfaceStateGetAttributeCount(surface_state_.get(),
                                                     surface_index, &count);
    if (ret < 0)
      return ErrorStatus(-ret);

    attributes.resize(count);
    const ssize_t return_count = dvrSurfaceStateGetAttributes(
        surface_state_.get(), surface_index, attributes.data(), count);
    if (return_count < 0)
      return ErrorStatus(-return_count);

    attributes.resize(return_count);
    return {std::move(attributes)};
  }

  Status<std::vector<int>> GetQueueIds(size_t surface_index) {
    std::vector<int> queue_ids;
    size_t count = 0;
    const int ret = dvrSurfaceStateGetQueueCount(surface_state_.get(),
                                                 surface_index, &count);
    if (ret < 0)
      return ErrorStatus(-ret);

    if (count > 0) {
      queue_ids.resize(count);
      const ssize_t return_count = dvrSurfaceStateGetQueueIds(
          surface_state_.get(), surface_index, queue_ids.data(), count);
      if (return_count < 0)
        return ErrorStatus(-return_count);

      queue_ids.resize(return_count);
    }

    return {std::move(queue_ids)};
  }

  Status<std::vector<uint8_t>> GetConfigData(int config_type) {
    uint8_t* data = nullptr;
    size_t data_size = 0;
    int error = dvrConfigurationDataGet(config_type, &data, &data_size);
    if (error < 0) {
      return ErrorStatus(-error);
    }

    if (!data || data_size == 0) {
      return ErrorStatus(EINVAL);
    }
    std::vector<uint8_t> data_result(data, data + data_size);
    dvrConfigurationDataDestroy(data);
    std::string s(data, data + data_size);
    return {std::move(data_result)};
  }

 private:
  UniqueDvrDisplayManager display_manager_;
  UniqueDvrSurfaceState surface_state_;

  // Owned by object in display_manager_, do not explicitly close.
  int display_manager_event_fd_;

  TestDisplayManager(const TestDisplayManager&) = delete;
  void operator=(const TestDisplayManager&) = delete;
};

class DvrDisplayManagerTest : public ::testing::Test {
 protected:
  void SetUp() override {
    int ret;
    DvrDisplayManager* display_manager;
    DvrSurfaceState* surface_state;

    ret = dvrDisplayManagerCreate(&display_manager);
    ASSERT_EQ(0, ret) << "Failed to create display manager client";
    ASSERT_NE(nullptr, display_manager);

    ret = dvrSurfaceStateCreate(&surface_state);
    ASSERT_EQ(0, ret) << "Failed to create surface state object";
    ASSERT_NE(nullptr, surface_state);

    manager_.reset(
        new TestDisplayManager(UniqueDvrDisplayManager(display_manager),
                               UniqueDvrSurfaceState(surface_state)));
  }
  void TearDown() override {}

  std::unique_ptr<TestDisplayManager> manager_;
};

// TODO(eieio): Consider moving these somewhere more central because they are
// broadly useful.

template <typename T>
testing::AssertionResult StatusOk(const char* status_expression,
                                  const Status<T>& status) {
  if (!status.ok()) {
    return testing::AssertionFailure()
           << "(" << status_expression
           << ") expected to indicate success but actually contains error ("
           << status.error() << ")";
  } else {
    return testing::AssertionSuccess();
  }
}

template <typename T>
testing::AssertionResult StatusError(const char* status_expression,
                                     const Status<T>& status) {
  if (status.ok()) {
    return testing::AssertionFailure()
           << "(" << status_expression
           << ") expected to indicate error but instead indicates success.";
  } else {
    return testing::AssertionSuccess();
  }
}

template <typename T>
testing::AssertionResult StatusHasError(const char* status_expression,
                                        const char* /*error_code_expression*/,
                                        const Status<T>& status,
                                        int error_code) {
  if (status.ok()) {
    return StatusError(status_expression, status);
  } else if (status.error() != error_code) {
    return testing::AssertionFailure()
           << "(" << status_expression << ") expected to indicate error ("
           << error_code << ") but actually indicates error (" << status.error()
           << ")";
  } else {
    return testing::AssertionSuccess();
  }
}

template <typename T, typename U>
testing::AssertionResult StatusHasValue(const char* status_expression,
                                        const char* /*value_expression*/,
                                        const Status<T>& status,
                                        const U& value) {
  if (!status.ok()) {
    return StatusOk(status_expression, status);
  } else if (status.get() != value) {
    return testing::AssertionFailure()
           << "(" << status_expression << ") expected to contain value ("
           << testing::PrintToString(value) << ") but actually contains value ("
           << testing::PrintToString(status.get()) << ")";
  } else {
    return testing::AssertionSuccess();
  }
}

template <typename T, typename Op>
testing::AssertionResult StatusPred(const char* status_expression,
                                    const char* pred_expression,
                                    const Status<T>& status, Op pred) {
  if (!status.ok()) {
    return StatusOk(status_expression, status);
  } else if (!pred(status.get())) {
    return testing::AssertionFailure()
           << status_expression << " value ("
           << testing::PrintToString(status.get())
           << ") failed to pass predicate " << pred_expression;
  } else {
    return testing::AssertionSuccess();
  }
}

#define ASSERT_STATUS_OK(status) ASSERT_PRED_FORMAT1(StatusOk, status)
#define ASSERT_STATUS_ERROR(status) ASSERT_PRED_FORMAT1(StatusError, status)

#define ASSERT_STATUS_ERROR_VALUE(value, status) \
  ASSERT_PRED_FORMAT2(StatusHasError, status, value)

#define ASSERT_STATUS_EQ(value, status) \
  ASSERT_PRED_FORMAT2(StatusHasValue, status, value)

#define EXPECT_STATUS_OK(status) EXPECT_PRED_FORMAT1(StatusOk, status)
#define EXPECT_STATUS_ERROR(status) EXPECT_PRED_FORMAT1(StatusError, status)

#define EXPECT_STATUS_ERROR_VALUE(value, status) \
  EXPECT_PRED_FORMAT2(StatusHasError, status, value)

#define EXPECT_STATUS_EQ(value, status) \
  EXPECT_PRED_FORMAT2(StatusHasValue, status, value)

#define EXPECT_STATUS_PRED(pred, status) \
  EXPECT_PRED_FORMAT2(StatusPred, status, pred)

#if 0
// Verify utility predicate/macro functionality. This section is commented out
// because it is designed to fail in some cases to validate the helpers.
TEST_F(DvrDisplayManagerTest, ExpectVoid) {
  Status<void> status_error{ErrorStatus{EINVAL}};
  Status<void> status_ok{};

  EXPECT_STATUS_ERROR(status_error);
  EXPECT_STATUS_ERROR(status_ok);
  EXPECT_STATUS_OK(status_error);
  EXPECT_STATUS_OK(status_ok);

  EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error);
  EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error);
  EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok);
  EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok);
}

TEST_F(DvrDisplayManagerTest, ExpectInt) {
  Status<int> status_error{ErrorStatus{EINVAL}};
  Status<int> status_ok{10};

  EXPECT_STATUS_ERROR(status_error);
  EXPECT_STATUS_ERROR(status_ok);
  EXPECT_STATUS_OK(status_error);
  EXPECT_STATUS_OK(status_ok);

  EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error);
  EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error);
  EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok);
  EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok);

  EXPECT_STATUS_EQ(10, status_error);
  EXPECT_STATUS_EQ(20, status_error);
  EXPECT_STATUS_EQ(10, status_ok);
  EXPECT_STATUS_EQ(20, status_ok);

  auto pred1 = [](const auto& value) { return value < 15; };
  auto pred2 = [](const auto& value) { return value > 5; };
  auto pred3 = [](const auto& value) { return value > 15; };
  auto pred4 = [](const auto& value) { return value < 5; };

  EXPECT_STATUS_PRED(pred1, status_error);
  EXPECT_STATUS_PRED(pred2, status_error);
  EXPECT_STATUS_PRED(pred3, status_error);
  EXPECT_STATUS_PRED(pred4, status_error);
  EXPECT_STATUS_PRED(pred1, status_ok);
  EXPECT_STATUS_PRED(pred2, status_ok);
  EXPECT_STATUS_PRED(pred3, status_ok);
  EXPECT_STATUS_PRED(pred4, status_ok);
}
#endif

TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) {
  // Get surface state and verify there are no surfaces.
  ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
  ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());

  // Get flags for invalid surface index.
  EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetUpdateFlags(0));

  // Create an application surface.
  auto surface_status = CreateApplicationSurface();
  ASSERT_STATUS_OK(surface_status);
  UniqueDvrSurface surface = surface_status.take();
  ASSERT_NE(nullptr, surface.get());

  const int surface_id = dvrSurfaceGetId(surface.get());
  ASSERT_GE(surface_id, 0);

  // Now there should be one new surface.
  ASSERT_STATUS_OK(manager_->WaitForUpdate());
  EXPECT_STATUS_EQ(1u, manager_->GetSurfaceCount());

  // Verify the new surface flag is set.
  auto check_flags = [](const auto& value) {
    return value & DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE;
  };
  EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));

  // Verify the surface id matches.
  EXPECT_STATUS_EQ(surface_id, manager_->GetSurfaceId(0));

  // Verify the owning process of the surface.
  EXPECT_STATUS_EQ(getpid(), manager_->GetProcessId(0));

  surface.reset();

  ASSERT_STATUS_OK(manager_->WaitForUpdate());
  EXPECT_STATUS_EQ(0u, manager_->GetSurfaceCount());
}

TEST_F(DvrDisplayManagerTest, SurfaceAttributeEvent) {
  // Get surface state and verify there are no surfaces.
  ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
  ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());

  // Get attributes for an invalid surface index.
  EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetAttributes(0));

  const bool kInitialVisibility = true;
  const int32_t kInitialZOrder = 10;
  auto surface_status =
      CreateApplicationSurface(kInitialVisibility, kInitialZOrder);
  ASSERT_STATUS_OK(surface_status);
  auto surface = surface_status.take();
  ASSERT_NE(nullptr, surface.get());

  ASSERT_STATUS_OK(manager_->WaitForUpdate());
  ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());

  // Check the initial attribute values.
  auto attribute_status = manager_->GetAttributes(0);
  ASSERT_STATUS_OK(attribute_status);
  auto attributes = attribute_status.take();
  EXPECT_GE(attributes.size(), 2u);

  std::set<int32_t> actual_keys;
  std::set<int32_t> expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
                                     DVR_SURFACE_ATTRIBUTE_VISIBLE};

  // Collect all the keys in attributes that match the expected keys.
  auto compare_keys = [](const auto& attributes, const auto& expected_keys) {
    std::set<int32_t> keys;
    for (const auto& attribute : attributes) {
      if (expected_keys.find(attribute.key) != expected_keys.end())
        keys.emplace(attribute.key);
    }
    return keys;
  };

  // If the sets match then attributes contained at least the expected keys,
  // even if other keys were also present.
  actual_keys = compare_keys(attributes, expected_keys);
  EXPECT_EQ(expected_keys, actual_keys);

  std::vector<DvrSurfaceAttribute> attributes_to_set = {
      MakeAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, 0)};

  // Test invalid args.
  EXPECT_EQ(-EINVAL, dvrSurfaceSetAttributes(nullptr, attributes_to_set.data(),
                                             attributes_to_set.size()));
  EXPECT_EQ(-EINVAL, dvrSurfaceSetAttributes(surface.get(), nullptr,
                                             attributes_to_set.size()));

  // Test attribute change events.
  ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
                                       attributes_to_set.size()));
  ASSERT_STATUS_OK(manager_->WaitForUpdate());

  // Verify the attributes changed flag is set.
  auto check_flags = [](const auto& value) {
    return value & DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED;
  };
  EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));

  attribute_status = manager_->GetAttributes(0);
  ASSERT_STATUS_OK(attribute_status);
  attributes = attribute_status.take();
  EXPECT_GE(attributes.size(), 2u);

  expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
                   DVR_SURFACE_ATTRIBUTE_VISIBLE};

  actual_keys.clear();
  actual_keys = compare_keys(attributes, expected_keys);
  EXPECT_EQ(expected_keys, actual_keys);

  // Test setting and then deleting an attribute.
  const DvrSurfaceAttributeKey kUserKey = 1;
  attributes_to_set = {MakeAttribute(kUserKey, 1024)};

  ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
                                       attributes_to_set.size()));
  ASSERT_STATUS_OK(manager_->WaitForUpdate());
  EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));

  attribute_status = manager_->GetAttributes(0);
  ASSERT_STATUS_OK(attribute_status);
  attributes = attribute_status.take();
  EXPECT_GE(attributes.size(), 2u);

  expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE,
                   kUserKey};

  actual_keys.clear();
  actual_keys = compare_keys(attributes, expected_keys);
  EXPECT_EQ(expected_keys, actual_keys);

  // Delete the attribute.
  attributes_to_set = {MakeAttribute(kUserKey, nullptr)};

  ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
                                       attributes_to_set.size()));
  ASSERT_STATUS_OK(manager_->WaitForUpdate());
  EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));

  attribute_status = manager_->GetAttributes(0);
  ASSERT_STATUS_OK(attribute_status);
  attributes = attribute_status.take();
  EXPECT_GE(attributes.size(), 2u);

  expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE,
                   kUserKey};

  actual_keys.clear();
  actual_keys = compare_keys(attributes, expected_keys);
  EXPECT_NE(expected_keys, actual_keys);

  // Test deleting a reserved attribute.
  attributes_to_set = {MakeAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, nullptr)};

  EXPECT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
                                       attributes_to_set.size()));

  // Failed attribute operations should not trigger update events.
  const int kTimeoutMs = 100;  // 0.1s
  EXPECT_STATUS_ERROR_VALUE(ETIMEDOUT, manager_->WaitForUpdate(kTimeoutMs));

  attribute_status = manager_->GetAttributes(0);
  ASSERT_STATUS_OK(attribute_status);
  attributes = attribute_status.take();
  EXPECT_GE(attributes.size(), 2u);

  expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
                   DVR_SURFACE_ATTRIBUTE_VISIBLE};

  actual_keys.clear();
  actual_keys = compare_keys(attributes, expected_keys);
  EXPECT_EQ(expected_keys, actual_keys);
}

TEST_F(DvrDisplayManagerTest, SurfaceAttributeTypes) {
  // Create an application surface.
  auto surface_status = CreateApplicationSurface();
  ASSERT_STATUS_OK(surface_status);
  UniqueDvrSurface surface = surface_status.take();
  ASSERT_NE(nullptr, surface.get());

  enum : std::int32_t {
    kInt32Key = 1,
    kInt64Key,
    kBoolKey,
    kFloatKey,
    kFloat2Key,
    kFloat3Key,
    kFloat4Key,
    kFloat8Key,
    kFloat16Key,
  };

  const std::vector<DvrSurfaceAttribute> attributes_to_set = {
      MakeAttribute(kInt32Key, int32_t{0}),
      MakeAttribute(kInt64Key, int64_t{0}),
      MakeAttribute(kBoolKey, false),
      MakeAttribute(kFloatKey, 0.0f),
      MakeAttribute(kFloat2Key, std::array<float, 2>{{1.0f, 2.0f}}),
      MakeAttribute(kFloat3Key, std::array<float, 3>{{3.0f, 4.0f, 5.0f}}),
      MakeAttribute(kFloat4Key, std::array<float, 4>{{6.0f, 7.0f, 8.0f, 9.0f}}),
      MakeAttribute(kFloat8Key,
                    std::array<float, 8>{{10.0f, 11.0f, 12.0f, 13.0f, 14.0f,
                                          15.0f, 16.0f, 17.0f}}),
      MakeAttribute(kFloat16Key, std::array<float, 16>{
                                     {18.0f, 19.0f, 20.0f, 21.0f, 22.0f, 23.0f,
                                      24.0f, 25.0f, 26.0f, 27.0f, 28.0f, 29.0f,
                                      30.0f, 31.0f, 32.0f, 33.0f}})};

  EXPECT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
                                       attributes_to_set.size()));

  ASSERT_STATUS_OK(manager_->WaitForUpdate());
  auto attribute_status = manager_->GetAttributes(0);
  ASSERT_STATUS_OK(attribute_status);
  auto attributes = attribute_status.take();
  EXPECT_GE(attributes.size(), attributes_to_set.size());

  auto HasAttribute = [](const auto& attributes,
                         DvrSurfaceAttributeKey key) -> bool {
    for (const auto& attribute : attributes) {
      if (attribute.key == key)
        return true;
    }
    return false;
  };
  auto AttributeType =
      [](const auto& attributes,
         DvrSurfaceAttributeKey key) -> DvrSurfaceAttributeType {
    for (const auto& attribute : attributes) {
      if (attribute.key == key)
        return attribute.value.type;
    }
    return DVR_SURFACE_ATTRIBUTE_TYPE_NONE;
  };

  ASSERT_TRUE(HasAttribute(attributes, kInt32Key));
  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_INT32,
            AttributeType(attributes, kInt32Key));

  ASSERT_TRUE(HasAttribute(attributes, kInt64Key));
  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_INT64,
            AttributeType(attributes, kInt64Key));

  ASSERT_TRUE(HasAttribute(attributes, kBoolKey));
  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
            AttributeType(attributes, kBoolKey));

  ASSERT_TRUE(HasAttribute(attributes, kFloatKey));
  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT,
            AttributeType(attributes, kFloatKey));

  ASSERT_TRUE(HasAttribute(attributes, kFloat2Key));
  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2,
            AttributeType(attributes, kFloat2Key));

  ASSERT_TRUE(HasAttribute(attributes, kFloat3Key));
  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3,
            AttributeType(attributes, kFloat3Key));

  ASSERT_TRUE(HasAttribute(attributes, kFloat4Key));
  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4,
            AttributeType(attributes, kFloat4Key));

  ASSERT_TRUE(HasAttribute(attributes, kFloat8Key));
  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8,
            AttributeType(attributes, kFloat8Key));

  ASSERT_TRUE(HasAttribute(attributes, kFloat16Key));
  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16,
            AttributeType(attributes, kFloat16Key));
}

TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) {
  // Create an application surface.
  auto surface_status = CreateApplicationSurface();
  ASSERT_STATUS_OK(surface_status);
  UniqueDvrSurface surface = surface_status.take();
  ASSERT_NE(nullptr, surface.get());

  const int surface_id = dvrSurfaceGetId(surface.get());
  ASSERT_GE(surface_id, 0);
  // Get surface state and verify there is one surface.
  ASSERT_STATUS_OK(manager_->WaitForUpdate());
  ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());

  // Verify there are no queues for the surface recorded in the state
  // snapshot.
  EXPECT_STATUS_EQ(std::vector<int>{}, manager_->GetQueueIds(0));

  // Create a new queue in the surface.
  auto write_queue_status = CreateSurfaceQueue(
      surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, 1,
      AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1, 0);
  ASSERT_STATUS_OK(write_queue_status);
  UniqueDvrWriteBufferQueue write_queue = write_queue_status.take();
  ASSERT_NE(nullptr, write_queue.get());

  const int queue_id = dvrWriteBufferQueueGetId(write_queue.get());
  ASSERT_GE(queue_id, 0);

  // Update surface state.
  ASSERT_STATUS_OK(manager_->WaitForUpdate());
  ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());

  // Verify the buffers changed flag is set.
  auto check_flags = [](const auto& value) {
    return value & DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED;
  };
  EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));

  auto queue_ids_status = manager_->GetQueueIds(0);
  ASSERT_STATUS_OK(queue_ids_status);

  auto queue_ids = queue_ids_status.take();
  ASSERT_EQ(1u, queue_ids.size());
  EXPECT_EQ(queue_id, queue_ids[0]);

  auto read_queue_status = manager_->GetReadBufferQueue(surface_id, queue_id);
  ASSERT_STATUS_OK(read_queue_status);
  UniqueDvrReadBufferQueue read_queue = read_queue_status.take();
  ASSERT_NE(nullptr, read_queue.get());
  EXPECT_EQ(queue_id, dvrReadBufferQueueGetId(read_queue.get()));

  write_queue.reset();

  // Verify that destroying the queue generates a surface update event.
  ASSERT_STATUS_OK(manager_->WaitForUpdate());
  ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());

  // Verify that the buffers changed flag is set.
  EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));

  // Verify that the queue ids reflect the change.
  queue_ids_status = manager_->GetQueueIds(0);
  ASSERT_STATUS_OK(queue_ids_status);

  queue_ids = queue_ids_status.take();
  ASSERT_EQ(0u, queue_ids.size());
}

TEST_F(DvrDisplayManagerTest, MultiLayerBufferQueue) {
  // Create an application surface.
  auto surface_status = CreateApplicationSurface();
  ASSERT_STATUS_OK(surface_status);
  UniqueDvrSurface surface = surface_status.take();
  ASSERT_NE(nullptr, surface.get());

  // Get surface state and verify there is one surface.
  ASSERT_STATUS_OK(manager_->WaitForUpdate());
  ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());

  // Create a new queue in the surface.
  const uint32_t kLayerCount = 3;
  auto write_queue_status = CreateSurfaceQueue(
      surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, kLayerCount,
      AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1, 0);
  ASSERT_STATUS_OK(write_queue_status);
  UniqueDvrWriteBufferQueue write_queue = write_queue_status.take();
  ASSERT_NE(nullptr, write_queue.get());

  DvrWriteBuffer* buffer = nullptr;
  dvrWriteBufferCreateEmpty(&buffer);
  int fence_fd = -1;
  int error =
      dvrWriteBufferQueueDequeue(write_queue.get(), 1000, buffer, &fence_fd);
  ASSERT_EQ(0, error);

  AHardwareBuffer* hardware_buffer = nullptr;
  error = dvrWriteBufferGetAHardwareBuffer(buffer, &hardware_buffer);
  ASSERT_EQ(0, error);

  AHardwareBuffer_Desc desc = {};
  AHardwareBuffer_describe(hardware_buffer, &desc);
  ASSERT_EQ(kLayerCount, desc.layers);

  AHardwareBuffer_release(hardware_buffer);
  dvrWriteBufferDestroy(buffer);
}

TEST_F(DvrDisplayManagerTest, ConfigurationData) {
  // TODO(hendrikw): Move this out of the display manager tests.
  auto data1 = manager_->GetConfigData(-1);
  ASSERT_STATUS_ERROR(data1);

  const char kDvrLensMetricsProperty[] = "ro.dvr.lens_metrics";

  // This should be run on devices with and without built in metrics.
  bool has_metric = !base::GetProperty(kDvrLensMetricsProperty, "").empty();
  auto data2 = manager_->GetConfigData(DVR_CONFIGURATION_DATA_LENS_METRICS);
  if (has_metric) {
    ASSERT_STATUS_OK(data2);
    ASSERT_NE(0u, data2.get().size());
  } else {
    ASSERT_STATUS_ERROR(data2);
  }
}

}  // namespace

}  // namespace dvr
}  // namespace android
