// Copyright 2014 The Chromium OS 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 <algorithm>
#include <functional>
#include <string>
#include <utility>
#include <vector>

#include <brillo/any.h>
#include <gtest/gtest.h>

using brillo::Any;

TEST(Any, Empty) {
  Any val;
  EXPECT_TRUE(val.IsEmpty());

  Any val2 = val;
  EXPECT_TRUE(val.IsEmpty());
  EXPECT_TRUE(val2.IsEmpty());

  Any val3 = std::move(val);
  EXPECT_TRUE(val.IsEmpty());
  EXPECT_TRUE(val3.IsEmpty());
}

TEST(Any, SimpleTypes) {
  Any val(20);
  EXPECT_FALSE(val.IsEmpty());
  EXPECT_TRUE(val.IsTypeCompatible<int>());
  EXPECT_EQ(20, val.Get<int>());

  Any val2(3.1415926);
  EXPECT_FALSE(val2.IsEmpty());
  EXPECT_TRUE(val2.IsTypeCompatible<double>());
  EXPECT_FALSE(val2.IsTypeCompatible<int>());
  EXPECT_DOUBLE_EQ(3.1415926, val2.Get<double>());

  Any val3(std::string("blah"));
  EXPECT_TRUE(val3.IsTypeCompatible<std::string>());
  EXPECT_EQ("blah", val3.Get<std::string>());
}

TEST(Any, Clear) {
  Any val('x');
  EXPECT_FALSE(val.IsEmpty());
  EXPECT_EQ('x', val.Get<char>());

  val.Clear();
  EXPECT_TRUE(val.IsEmpty());
}

TEST(Any, Assignments) {
  Any val(20);
  EXPECT_EQ(20, val.Get<int>());

  val = 3.1415926;
  EXPECT_FALSE(val.IsEmpty());
  EXPECT_TRUE(val.IsTypeCompatible<double>());
  EXPECT_DOUBLE_EQ(3.1415926, val.Get<double>());

  val = std::string("blah");
  EXPECT_EQ("blah", val.Get<std::string>());

  Any val2;
  EXPECT_TRUE(val2.IsEmpty());
  val2 = val;
  EXPECT_FALSE(val.IsEmpty());
  EXPECT_FALSE(val2.IsEmpty());
  EXPECT_EQ("blah", val.Get<std::string>());
  EXPECT_EQ("blah", val2.Get<std::string>());
  val.Clear();
  EXPECT_TRUE(val.IsEmpty());
  EXPECT_EQ("blah", val2.Get<std::string>());
  val2.Clear();
  EXPECT_TRUE(val2.IsEmpty());

  val = std::vector<int>{100, 20, 3};
  auto v = val.Get<std::vector<int>>();
  EXPECT_EQ(100, v[0]);
  EXPECT_EQ(20, v[1]);
  EXPECT_EQ(3, v[2]);

  val2 = std::move(val);
  EXPECT_TRUE(val.IsEmpty());
  EXPECT_TRUE(val2.IsTypeCompatible<std::vector<int>>());
  EXPECT_EQ(3, val2.Get<std::vector<int>>().size());

  val = val2;
  EXPECT_TRUE(val.IsTypeCompatible<std::vector<int>>());
  EXPECT_TRUE(val2.IsTypeCompatible<std::vector<int>>());
  EXPECT_EQ(3, val.Get<std::vector<int>>().size());
  EXPECT_EQ(3, val2.Get<std::vector<int>>().size());
}

TEST(Any, Enums) {
  enum class Dummy { foo, bar, baz };
  Any val(Dummy::bar);
  EXPECT_FALSE(val.IsEmpty());
  EXPECT_TRUE(val.IsConvertibleToInteger());
  EXPECT_EQ(Dummy::bar, val.Get<Dummy>());
  EXPECT_EQ(1, val.GetAsInteger());

  val = Dummy::baz;
  EXPECT_EQ(2, val.GetAsInteger());

  val = Dummy::foo;
  EXPECT_EQ(0, val.GetAsInteger());
}

TEST(Any, Integers) {
  Any val(14);
  EXPECT_TRUE(val.IsConvertibleToInteger());
  EXPECT_EQ(14, val.Get<int>());
  EXPECT_EQ(14, val.GetAsInteger());

  val = '\x40';
  EXPECT_TRUE(val.IsConvertibleToInteger());
  EXPECT_EQ(64, val.Get<char>());
  EXPECT_EQ(64, val.GetAsInteger());

  val = static_cast<uint16_t>(65535);
  EXPECT_TRUE(val.IsConvertibleToInteger());
  EXPECT_EQ(65535, val.Get<uint16_t>());
  EXPECT_EQ(65535, val.GetAsInteger());

  val = static_cast<uint64_t>(0xFFFFFFFFFFFFFFFFULL);
  EXPECT_TRUE(val.IsConvertibleToInteger());
  EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, val.Get<uint64_t>());
  EXPECT_EQ(-1, val.GetAsInteger());

  val = "abc";
  EXPECT_FALSE(val.IsConvertibleToInteger());

  int a = 5;
  val = &a;
  EXPECT_FALSE(val.IsConvertibleToInteger());
}

TEST(Any, Pointers) {
  Any val("abc");  // const char*
  EXPECT_FALSE(val.IsTypeCompatible<char*>());
  EXPECT_TRUE(val.IsTypeCompatible<const char*>());
  EXPECT_FALSE(val.IsTypeCompatible<volatile char*>());
  EXPECT_TRUE(val.IsTypeCompatible<volatile const char*>());
  EXPECT_STREQ("abc", val.Get<const char*>());

  int a = 10;
  val = &a;
  EXPECT_TRUE(val.IsTypeCompatible<int*>());
  EXPECT_TRUE(val.IsTypeCompatible<const int*>());
  EXPECT_TRUE(val.IsTypeCompatible<volatile int*>());
  EXPECT_TRUE(val.IsTypeCompatible<volatile const int*>());
  EXPECT_EQ(10, *val.Get<const int*>());
  *val.Get<int*>() = 3;
  EXPECT_EQ(3, a);
}

TEST(Any, Arrays) {
  // The following test are here to validate the array-to-pointer decay rules.
  // Since Any does not store the contents of a C-style array, just a pointer
  // to the data, putting array data into Any could be dangerous.
  // Make sure the array's lifetime exceeds that of an Any containing the
  // pointer to the array data.
  // If you want to store the array with data, use corresponding value types
  // such as std::vector or a struct containing C-style array as a member.

  int int_array[] = {1, 2, 3};  // int*
  Any val = int_array;
  EXPECT_TRUE(val.IsTypeCompatible<int*>());
  EXPECT_TRUE(val.IsTypeCompatible<const int*>());
  EXPECT_TRUE(val.IsTypeCompatible<int[]>());
  EXPECT_TRUE(val.IsTypeCompatible<const int[]>());
  EXPECT_EQ(3, val.Get<int*>()[2]);

  const int const_int_array[] = {10, 20, 30};  // const int*
  val = const_int_array;
  EXPECT_FALSE(val.IsTypeCompatible<int*>());
  EXPECT_TRUE(val.IsTypeCompatible<const int*>());
  EXPECT_FALSE(val.IsTypeCompatible<int[]>());
  EXPECT_TRUE(val.IsTypeCompatible<const int[]>());
  EXPECT_EQ(30, val.Get<const int*>()[2]);
}

TEST(Any, References) {
  // Passing references to object via Any might be error-prone or the
  // semantics could be unfamiliar to other developers. In many cases,
  // using pointers instead of references are more conventional and easier
  // to understand. Even though the cases of passing references are quite
  // explicit on both storing and retrieving ends, you might want to
  // use pointers instead anyway.

  int a = 5;
  Any val(std::ref(a));  // int&
  EXPECT_EQ(5, val.Get<std::reference_wrapper<int>>().get());
  val.Get<std::reference_wrapper<int>>().get() = 7;
  EXPECT_EQ(7, val.Get<std::reference_wrapper<int>>().get());
  EXPECT_EQ(7, a);

  Any val2(std::cref(a));  // const int&
  EXPECT_EQ(7, val2.Get<std::reference_wrapper<const int>>().get());

  a = 10;
  EXPECT_EQ(10, val.Get<std::reference_wrapper<int>>().get());
  EXPECT_EQ(10, val2.Get<std::reference_wrapper<const int>>().get());
}

TEST(Any, CustomTypes) {
  struct Person {
    std::string name;
    int age;
  };
  Any val(Person{"Jack", 40});
  Any val2 = val;
  EXPECT_EQ("Jack", val.Get<Person>().name);
  val.GetPtr<Person>()->name = "Joe";
  val.GetPtr<Person>()->age /= 2;
  EXPECT_EQ("Joe", val.Get<Person>().name);
  EXPECT_EQ(20, val.Get<Person>().age);
  EXPECT_EQ("Jack", val2.Get<Person>().name);
  EXPECT_EQ(40, val2.Get<Person>().age);
}

TEST(Any, Swap) {
  Any val(12);
  Any val2(2.7);
  EXPECT_EQ(12, val.Get<int>());
  EXPECT_EQ(2.7, val2.Get<double>());

  val.Swap(val2);
  EXPECT_EQ(2.7, val.Get<double>());
  EXPECT_EQ(12, val2.Get<int>());

  std::swap(val, val2);
  EXPECT_EQ(12, val.Get<int>());
  EXPECT_EQ(2.7, val2.Get<double>());
}

TEST(Any, TypeMismatch) {
  Any val(12);
  EXPECT_DEATH(val.Get<double>(),
               "Requesting value of type 'double' from variant containing "
               "'int'");

  val = std::string("123");
  EXPECT_DEATH(val.GetAsInteger(),
               "Unable to convert value of type 'std::.*' to integer");

  Any empty;
  EXPECT_DEATH(empty.GetAsInteger(), "Must not be called on an empty Any");
}

TEST(Any, TryGet) {
  Any val(12);
  Any empty;
  EXPECT_EQ("dummy", val.TryGet<std::string>("dummy"));
  EXPECT_EQ(12, val.TryGet<int>(17));
  EXPECT_EQ(17, empty.TryGet<int>(17));
}

TEST(Any, Compare_Int) {
  Any int1{12};
  Any int2{12};
  Any int3{20};
  EXPECT_EQ(int1, int2);
  EXPECT_NE(int2, int3);
}

TEST(Any, Compare_String) {
  Any str1{std::string{"foo"}};
  Any str2{std::string{"foo"}};
  Any str3{std::string{"bar"}};
  EXPECT_EQ(str1, str2);
  EXPECT_NE(str2, str3);
}

TEST(Any, Compare_Array) {
  Any vec1{std::vector<int>{1, 2}};
  Any vec2{std::vector<int>{1, 2}};
  Any vec3{std::vector<int>{1, 2, 3}};
  EXPECT_EQ(vec1, vec2);
  EXPECT_NE(vec2, vec3);
}

TEST(Any, Compare_Empty) {
  Any empty1;
  Any empty2;
  Any int1{1};
  EXPECT_EQ(empty1, empty2);
  EXPECT_NE(int1, empty1);
  EXPECT_NE(empty2, int1);
}

TEST(Any, Compare_NonComparable) {
  struct Person {
    std::string name;
    int age;
  };
  Any person1(Person{"Jack", 40});
  Any person2 = person1;
  Any person3(Person{"Jill", 20});
  EXPECT_NE(person1, person2);
  EXPECT_NE(person1, person3);
  EXPECT_NE(person2, person3);
}

TEST(Any, GetUndecoratedTypeName) {
  Any val;
  EXPECT_TRUE(val.GetUndecoratedTypeName().empty());

  val = 1;
  EXPECT_EQ(brillo::GetUndecoratedTypeName<int>(),
            val.GetUndecoratedTypeName());

  val = 3.1415926;
  EXPECT_EQ(brillo::GetUndecoratedTypeName<double>(),
            val.GetUndecoratedTypeName());

  val = std::string("blah");
  EXPECT_EQ(brillo::GetUndecoratedTypeName<std::string>(),
            val.GetUndecoratedTypeName());
}
