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

#include "base/types/optional_ref.h"

#include <cstddef>
#include <optional>
#include <type_traits>
#include <utility>

#include "base/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace base {

namespace {

// Construction from `std::nullptr_t` is disallowed; `std::nullopt` must be
// used to construct an empty `optional_ref`.
static_assert(!std::is_constructible_v<optional_ref<int>, std::nullptr_t>);

// No-compile asserts for various const -> mutable conversions.
static_assert(
    !std::is_constructible_v<optional_ref<int>, const std::optional<int>&>);
static_assert(!std::is_constructible_v<optional_ref<int>, const int*>);
static_assert(!std::is_constructible_v<optional_ref<int>, const int&>);
static_assert(!std::is_constructible_v<optional_ref<int>, int&&>);
static_assert(!std::is_constructible_v<optional_ref<int>, const int>);
static_assert(
    !std::is_constructible_v<optional_ref<int>, optional_ref<const int>>);

// No-compile asserts for implicit conversions.
static_assert(!std::is_constructible_v<optional_ref<bool>, int>);
static_assert(!std::is_constructible_v<optional_ref<bool>, const int&>);
static_assert(!std::is_constructible_v<optional_ref<bool>, int&>);
static_assert(!std::is_constructible_v<optional_ref<bool>, const int*>);
static_assert(!std::is_constructible_v<optional_ref<bool>, int*>);

class ImplicitInt {
 public:
  // NOLINTNEXTLINE(google-explicit-constructor)
  ImplicitInt(int) {}
};

static_assert(!std::is_constructible_v<optional_ref<ImplicitInt>, int>);
static_assert(!std::is_constructible_v<optional_ref<ImplicitInt>, const int&>);
static_assert(!std::is_constructible_v<optional_ref<ImplicitInt>, int&>);
static_assert(!std::is_constructible_v<optional_ref<ImplicitInt>, const int*>);
static_assert(!std::is_constructible_v<optional_ref<ImplicitInt>, int*>);

class TestClass {
 public:
  void ConstMethod() const {}
  void MutableMethod() {}
};

TEST(OptionalRefTest, FromNullopt) {
  [](optional_ref<const int> r) { EXPECT_FALSE(r.has_value()); }(std::nullopt);

  [](optional_ref<int> r) { EXPECT_FALSE(r.has_value()); }(std::nullopt);
}

TEST(OptionalRefTest, FromConstEmptyOptional) {
  const std::optional<int> optional_int;

  [](optional_ref<const int> r) { EXPECT_FALSE(r.has_value()); }(optional_int);

  // Mutable case covered by static_assert test above.
}

TEST(OptionalRefTest, FromMutableEmptyOptional) {
  std::optional<int> optional_int;

  [](optional_ref<const int> r) { EXPECT_FALSE(r.has_value()); }(optional_int);

  [](optional_ref<int> r) { EXPECT_FALSE(r.has_value()); }(optional_int);
}

TEST(OptionalRefTest, FromConstOptional) {
  const std::optional<int> optional_int(6);

  [](optional_ref<const int> r) {
    EXPECT_TRUE(r.has_value());
    EXPECT_EQ(6, r.value());
  }(optional_int);

  // Mutable case covered by static_assert test above.
}

TEST(OptionalRefTest, FromMutableOptional) {
  std::optional<int> optional_int(6);

  [](optional_ref<const int> r) {
    EXPECT_TRUE(r.has_value());
    EXPECT_EQ(6, r.value());
  }(optional_int);

  [](optional_ref<int> r) {
    EXPECT_TRUE(r.has_value());
    EXPECT_EQ(6, r.value());
  }(optional_int);
}

// The From*NullPointer tests intentionally avoid passing nullptr directly,
// which is explicitly disallowed to reduce ambiguity when constructing an empty
// `optional_ref`.
TEST(OptionalRefTest, FromConstNullPointer) {
  const int* ptr = nullptr;

  [](optional_ref<const int> r) { EXPECT_FALSE(r.has_value()); }(ptr);

  // Mutable case covered by static_assert test above.
}

TEST(OptionalRefTest, FromMutableNullPointer) {
  int* ptr = nullptr;

  [](optional_ref<const int> r) { EXPECT_FALSE(r.has_value()); }(ptr);

  [](optional_ref<int> r) { EXPECT_FALSE(r.has_value()); }(ptr);
}

TEST(OptionalRefTest, FromConstPointer) {
  int value = 6;
  const int* ptr = &value;

  [](optional_ref<const int> r) {
    EXPECT_TRUE(r.has_value());
    EXPECT_EQ(6, r.value());
  }(ptr);

  // Mutable case covered by static_assert test above.
}

TEST(OptionalRefTest, FromPointer) {
  int value = 6;
  int* ptr = &value;

  [](optional_ref<const int> r) {
    EXPECT_TRUE(r.has_value());
    EXPECT_EQ(6, r.value());
  }(ptr);

  [](optional_ref<int> r) {
    EXPECT_TRUE(r.has_value());
    EXPECT_EQ(6, r.value());
  }(ptr);
}

TEST(OptionalRefTest, FromConstRef) {
  int value = 6;
  const int& ref = value;

  [](optional_ref<const int> r) {
    EXPECT_TRUE(r.has_value());
    EXPECT_EQ(6, r.value());
  }(ref);

  // Mutable case covered by static_assert test above.
}

TEST(OptionalRefTest, FromMutableRef) {
  int value = 6;
  int& ref = value;

  [](optional_ref<const int> r) {
    EXPECT_TRUE(r.has_value());
    EXPECT_EQ(6, r.value());
  }(ref);

  [](optional_ref<int> r) {
    EXPECT_TRUE(r.has_value());
    EXPECT_EQ(6, r.value());
  }(ref);
}

TEST(OptionalRefTest, FromConstValue) {
  const int value = 6;

  [](optional_ref<const int> r) {
    EXPECT_TRUE(r.has_value());
    EXPECT_EQ(6, r.value());
  }(value);

  [](optional_ref<const int> r) {
    EXPECT_TRUE(r.has_value());
    EXPECT_EQ(6, r.value());
  }(6);

  // Mutable case covered by static_assert test above.
}

TEST(OptionalRefTest, FromMutableValue) {
  int value = 6;

  [](optional_ref<const int> r) {
    EXPECT_TRUE(r.has_value());
    EXPECT_EQ(6, r.value());
  }(value);

  [](optional_ref<int> r) {
    EXPECT_TRUE(r.has_value());
    EXPECT_EQ(6, r.value());
  }(value);
}

TEST(OptionalRefTest, FromMutableEmptyOptionalRefTest) {
  {
    optional_ref<int> r1;
    [](optional_ref<const int> r2) { EXPECT_FALSE(r2.has_value()); }(r1);
  }

  {
    optional_ref<int> r1(std::nullopt);
    [](optional_ref<int> r2) { EXPECT_FALSE(r2.has_value()); }(r1);
  }
}

TEST(OptionalRefTest, FromMutableOptionalRefTest) {
  int value = 6;
  optional_ref<int> r1(value);

  [](optional_ref<const int> r2) {
    EXPECT_TRUE(r2.has_value());
    EXPECT_EQ(6, r2.value());
  }(r1);

  [](optional_ref<int> r2) {
    EXPECT_TRUE(r2.has_value());
    EXPECT_EQ(6, r2.value());
  }(r1);
}

TEST(OptionalRefTest, FromCopyConstructorConst) {
  [](optional_ref<const int> r) {
    EXPECT_FALSE(r.has_value());
  }(optional_ref<const int>());

  int value = 6;
  optional_ref<const int> r1(value);
  [](optional_ref<const int> r2) {
    EXPECT_TRUE(r2.has_value());
    EXPECT_EQ(6, r2.value());
  }(r1);

  // Mutable case covered by static_assert test above.
}

TEST(OptionalRefTest, FromCopyConstructorMutable) {
  [](optional_ref<int> r) { EXPECT_FALSE(r.has_value()); }(optional_ref<int>());

  int value = 6;
  optional_ref<int> r1(value);
  [](optional_ref<int> r2) {
    EXPECT_TRUE(r2.has_value());
    EXPECT_EQ(6, r2.value());
  }(r1);
}

TEST(OptionalRefTest, Arrow) {
  int uninitialized_value;

  {
    const optional_ref<const int> r(uninitialized_value);
    static_assert(std::is_same_v<decltype(r.operator->()), const int*>);
    EXPECT_EQ(&uninitialized_value, r.operator->());
  }

  {
    optional_ref<const int> r(uninitialized_value);
    static_assert(std::is_same_v<decltype(r.operator->()), const int*>);
    EXPECT_EQ(&uninitialized_value, r.operator->());
  }

  {
    const optional_ref<int> r(uninitialized_value);
    static_assert(std::is_same_v<decltype(r.operator->()), int*>);
    EXPECT_EQ(&uninitialized_value, r.operator->());
  }

  {
    optional_ref<int> r(uninitialized_value);
    static_assert(std::is_same_v<decltype(r.operator->()), int*>);
    EXPECT_EQ(&uninitialized_value, r.operator->());
  }
}

TEST(OptionalRefTest, Star) {
  int uninitialized_value;

  {
    const optional_ref<const int> r(uninitialized_value);
    static_assert(std::is_same_v<decltype(r.operator*()), const int&>);
    EXPECT_EQ(&uninitialized_value, &r.operator*());
  }

  {
    optional_ref<const int> r(uninitialized_value);
    static_assert(std::is_same_v<decltype(r.operator*()), const int&>);
    EXPECT_EQ(&uninitialized_value, &r.operator*());
  }

  {
    const optional_ref<int> r(uninitialized_value);
    static_assert(std::is_same_v<decltype(r.operator*()), int&>);
    EXPECT_EQ(&uninitialized_value, &r.operator*());
  }

  {
    optional_ref<int> r(uninitialized_value);
    static_assert(std::is_same_v<decltype(r.operator*()), int&>);
    EXPECT_EQ(&uninitialized_value, &r.operator*());
  }
}

TEST(OptionalRefTest, Value) {
  // has_value() and value() are generally covered by the construction tests.
  // Make sure value() doesn't somehow break const-ness here.
  {
    const optional_ref<const int> r;
    static_assert(std::is_same_v<decltype(r.value()), const int&>);
  }

  {
    optional_ref<const int> r;
    static_assert(std::is_same_v<decltype(r.value()), const int&>);
  }

  {
    const optional_ref<int> r;
    static_assert(std::is_same_v<decltype(r.value()), int&>);
  }

  {
    optional_ref<int> r;
    static_assert(std::is_same_v<decltype(r.value()), int&>);
  }
}

TEST(OptionalRefTest, AsPtr) {
  optional_ref<int> r1;
  EXPECT_EQ(nullptr, r1.as_ptr());

  int uninitialized_value;
  {
    const optional_ref<const int> r(uninitialized_value);
    static_assert(std::is_same_v<decltype(r.as_ptr()), const int*>);
    EXPECT_EQ(&uninitialized_value, r.as_ptr());
  }

  {
    optional_ref<const int> r(uninitialized_value);
    static_assert(std::is_same_v<decltype(r.as_ptr()), const int*>);
    EXPECT_EQ(&uninitialized_value, r.as_ptr());
  }

  {
    const optional_ref<int> r(uninitialized_value);
    static_assert(std::is_same_v<decltype(r.as_ptr()), int*>);
    EXPECT_EQ(&uninitialized_value, r.as_ptr());
  }

  {
    optional_ref<int> r(uninitialized_value);
    static_assert(std::is_same_v<decltype(r.as_ptr()), int*>);
    EXPECT_EQ(&uninitialized_value, r.as_ptr());
  }
}

TEST(OptionalRefTest, CopyAsOptional) {
  optional_ref<int> r1;
  std::optional<int> o1 = r1.CopyAsOptional();
  EXPECT_EQ(std::nullopt, o1);

  int value = 6;
  optional_ref<int> r2(value);
  std::optional<int> o2 = r2.CopyAsOptional();
  EXPECT_EQ(6, o2);
}

TEST(OptionalRefTest, EqualityComparisonWithNullOpt) {
  {
    optional_ref<int> r;
    EXPECT_EQ(r, std::nullopt);
    EXPECT_EQ(std::nullopt, r);
  }

  {
    int value = 5;
    optional_ref<int> r(value);
    EXPECT_NE(r, std::nullopt);
    EXPECT_NE(std::nullopt, r);
  }
}

TEST(OptionalRefDeathTest, ArrowOnEmpty) {
  [](optional_ref<const TestClass> r) {
    EXPECT_CHECK_DEATH(r->ConstMethod());
  }(std::nullopt);

  [](optional_ref<TestClass> r) {
    EXPECT_CHECK_DEATH(r->ConstMethod());
    EXPECT_CHECK_DEATH(r->MutableMethod());
  }(std::nullopt);
}

TEST(OptionalRefDeathTest, StarOnEmpty) {
  [](optional_ref<const TestClass> r) {
    EXPECT_CHECK_DEATH((*r).ConstMethod());
  }(std::nullopt);

  [](optional_ref<TestClass> r) {
    EXPECT_CHECK_DEATH((*r).ConstMethod());
    EXPECT_CHECK_DEATH((*r).MutableMethod());
  }(std::nullopt);
}

TEST(OptionalRefDeathTest, ValueOnEmpty) {
  [](optional_ref<const TestClass> r) {
    EXPECT_CHECK_DEATH(r.value());
  }(std::nullopt);

  [](optional_ref<TestClass> r) {
    EXPECT_CHECK_DEATH(r.value());
  }(std::nullopt);
}

TEST(OptionalRefTest, ClassTemplateArgumentDeduction) {
  static_assert(
      std::is_same_v<decltype(optional_ref{int()}), optional_ref<const int>>);

  {
    const int i = 0;
    static_assert(
        std::is_same_v<decltype(optional_ref(i)), optional_ref<const int>>);
  }

  {
    int i = 0;
    static_assert(std::is_same_v<decltype(optional_ref(i)), optional_ref<int>>);
  }

  static_assert(std::is_same_v<decltype(optional_ref(std::optional<int>())),
                               optional_ref<const int>>);

  {
    const std::optional<int> o;
    static_assert(
        std::is_same_v<decltype(optional_ref(o)), optional_ref<const int>>);
  }

  {
    std::optional<int> o;
    static_assert(std::is_same_v<decltype(optional_ref(o)), optional_ref<int>>);
  }

  {
    const int* p = nullptr;
    static_assert(
        std::is_same_v<decltype(optional_ref(p)), optional_ref<const int>>);
  }

  {
    int* p = nullptr;
    static_assert(std::is_same_v<decltype(optional_ref(p)), optional_ref<int>>);
  }
}

}  // namespace

}  // namespace base
