// Copyright 2014 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 "mojo/public/cpp/bindings/array.h"

#include "mojo/public/cpp/bindings/lib/serialization.h"
#include "mojo/public/cpp/bindings/tests/array_common_test.h"
#include "mojo/public/cpp/bindings/tests/container_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace mojo {
namespace test {
namespace {

using ArrayTest = testing::Test;

ARRAY_COMMON_TEST(Array, NullAndEmpty)
ARRAY_COMMON_TEST(Array, Basic)
ARRAY_COMMON_TEST(Array, Bool)
ARRAY_COMMON_TEST(Array, Handle)
ARRAY_COMMON_TEST(Array, HandlesAreClosed)
ARRAY_COMMON_TEST(Array, Clone)
ARRAY_COMMON_TEST(Array, Serialization_ArrayOfPOD)
ARRAY_COMMON_TEST(Array, Serialization_EmptyArrayOfPOD)
ARRAY_COMMON_TEST(Array, Serialization_ArrayOfArrayOfPOD)
ARRAY_COMMON_TEST(Array, Serialization_ArrayOfBool)
ARRAY_COMMON_TEST(Array, Serialization_ArrayOfString)
ARRAY_COMMON_TEST(Array, Resize_Copyable)
ARRAY_COMMON_TEST(Array, Resize_MoveOnly)

TEST_F(ArrayTest, PushBack_Copyable) {
  ASSERT_EQ(0u, CopyableType::num_instances());
  Array<CopyableType> array(2);
  array = nullptr;
  std::vector<CopyableType*> value_ptrs;
  size_t capacity = array.storage().capacity();
  for (size_t i = 0; i < capacity; i++) {
    CopyableType value;
    value_ptrs.push_back(value.ptr());
    array.push_back(value);
    ASSERT_EQ(i + 1, array.size());
    ASSERT_EQ(i + 1, value_ptrs.size());
    EXPECT_EQ(array.size() + 1, CopyableType::num_instances());
    EXPECT_TRUE(array[i].copied());
    EXPECT_EQ(value_ptrs[i], array[i].ptr());
    array[i].ResetCopied();
    EXPECT_TRUE(array);
  }
  {
    CopyableType value;
    value_ptrs.push_back(value.ptr());
    array.push_back(value);
    EXPECT_EQ(array.size() + 1, CopyableType::num_instances());
  }
  ASSERT_EQ(capacity + 1, array.size());
  EXPECT_EQ(array.size(), CopyableType::num_instances());

  for (size_t i = 0; i < array.size(); i++) {
    EXPECT_TRUE(array[i].copied());
    EXPECT_EQ(value_ptrs[i], array[i].ptr());
  }
  array = nullptr;
  EXPECT_EQ(0u, CopyableType::num_instances());
}

TEST_F(ArrayTest, PushBack_MoveOnly) {
  ASSERT_EQ(0u, MoveOnlyType::num_instances());
  Array<MoveOnlyType> array(2);
  array = nullptr;
  std::vector<MoveOnlyType*> value_ptrs;
  size_t capacity = array.storage().capacity();
  for (size_t i = 0; i < capacity; i++) {
    MoveOnlyType value;
    value_ptrs.push_back(value.ptr());
    array.push_back(std::move(value));
    ASSERT_EQ(i + 1, array.size());
    ASSERT_EQ(i + 1, value_ptrs.size());
    EXPECT_EQ(array.size() + 1, MoveOnlyType::num_instances());
    EXPECT_TRUE(array[i].moved());
    EXPECT_EQ(value_ptrs[i], array[i].ptr());
    array[i].ResetMoved();
    EXPECT_TRUE(array);
  }
  {
    MoveOnlyType value;
    value_ptrs.push_back(value.ptr());
    array.push_back(std::move(value));
    EXPECT_EQ(array.size() + 1, MoveOnlyType::num_instances());
  }
  ASSERT_EQ(capacity + 1, array.size());
  EXPECT_EQ(array.size(), MoveOnlyType::num_instances());

  for (size_t i = 0; i < array.size(); i++) {
    EXPECT_TRUE(array[i].moved());
    EXPECT_EQ(value_ptrs[i], array[i].ptr());
  }
  array = nullptr;
  EXPECT_EQ(0u, MoveOnlyType::num_instances());
}

TEST_F(ArrayTest, MoveFromAndToSTLVector_Copyable) {
  std::vector<CopyableType> vec1(1);
  Array<CopyableType> arr(std::move(vec1));
  ASSERT_EQ(1u, arr.size());
  ASSERT_FALSE(arr[0].copied());

  std::vector<CopyableType> vec2(arr.PassStorage());
  ASSERT_EQ(1u, vec2.size());
  ASSERT_FALSE(vec2[0].copied());

  ASSERT_EQ(0u, arr.size());
  ASSERT_TRUE(arr.is_null());
}

TEST_F(ArrayTest, MoveFromAndToSTLVector_MoveOnly) {
  std::vector<MoveOnlyType> vec1(1);
  Array<MoveOnlyType> arr(std::move(vec1));

  ASSERT_EQ(1u, arr.size());

  std::vector<MoveOnlyType> vec2(arr.PassStorage());
  ASSERT_EQ(1u, vec2.size());

  ASSERT_EQ(0u, arr.size());
  ASSERT_TRUE(arr.is_null());
}

}  // namespace
}  // namespace test
}  // namespace mojo