blob: ae2db275a94e513424e23bc5ad845a9b9277204d [file] [log] [blame]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <type_traits>
#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "cc_bindings_from_rs/test/known_traits/drop/drop_cc_api.h"
namespace crubit {
namespace {
template <typename T>
class CustomDropWithDefaultTest : public testing::Test {};
using MyTypes =
::testing::Types<drop::drop_impl_with_default::DropImplWithDefault,
drop::drop_glue_with_default::DropGlueWithDefault>;
TYPED_TEST_SUITE(CustomDropWithDefaultTest, MyTypes);
TYPED_TEST(CustomDropWithDefaultTest, StaticAsserts) {
using TypeUnderTest = TypeParam;
static_assert(std::is_default_constructible_v<TypeUnderTest>);
static_assert(!std::is_trivially_default_constructible_v<TypeUnderTest>);
static_assert(std::is_move_constructible_v<TypeUnderTest>);
static_assert(!std::is_trivially_move_constructible_v<TypeUnderTest>);
static_assert(std::is_move_assignable_v<TypeUnderTest>);
static_assert(!std::is_trivially_move_assignable_v<TypeUnderTest>);
static_assert(std::is_destructible_v<TypeUnderTest>);
static_assert(!std::is_trivially_destructible_v<TypeUnderTest>);
static_assert(!std::is_copy_constructible_v<TypeUnderTest>);
static_assert(!std::is_copy_assignable_v<TypeUnderTest>);
}
TYPED_TEST(CustomDropWithDefaultTest, Destructor) {
using TypeUnderTest = TypeParam;
drop::counters::reset_counts();
{
TypeUnderTest s;
EXPECT_EQ(0, s.get_int());
EXPECT_EQ(1, drop::counters::get_default_count());
EXPECT_EQ(0, drop::counters::get_drop_count());
} // `TypeUnderTest`'s destructor runs when `s` goes out of scope.
EXPECT_EQ(1, drop::counters::get_default_count());
EXPECT_EQ(1, drop::counters::get_drop_count());
}
TYPED_TEST(CustomDropWithDefaultTest, MoveConstructor) {
using TypeUnderTest = TypeParam;
drop::counters::reset_counts();
{
TypeUnderTest s;
EXPECT_EQ(1, drop::counters::get_default_count());
EXPECT_EQ(0, drop::counters::get_drop_count());
s.set_int(123);
// After move construction, we expect the "moved from" object to be in the
// `Default` state (i.e., have a value of 0).
TypeUnderTest s2(std::move(s));
EXPECT_EQ(123, s2.get_int());
EXPECT_EQ(0, s.get_int()); // NOLINT(bugprone-use-after-move)
EXPECT_EQ(2, drop::counters::get_default_count());
EXPECT_EQ(0, drop::counters::get_drop_count());
} // `TypeUnderTest`'s destructor runs when `s` goes out of scope.
EXPECT_EQ(2, drop::counters::get_default_count());
EXPECT_EQ(2, drop::counters::get_drop_count());
}
TYPED_TEST(CustomDropWithDefaultTest, MoveAssignmentOperator) {
using TypeUnderTest = TypeParam;
drop::counters::reset_counts();
{
TypeUnderTest s1;
TypeUnderTest s2;
EXPECT_EQ(2, drop::counters::get_default_count());
EXPECT_EQ(0, drop::counters::get_drop_count());
s1.set_int(123);
s2.set_int(456);
// After move assignment we expect the values to be swapped.
s2 = std::move(s1);
EXPECT_EQ(456, s1.get_int()); // NOLINT(bugprone-use-after-move)
EXPECT_EQ(123, s2.get_int());
EXPECT_EQ(2, drop::counters::get_default_count());
EXPECT_EQ(0, drop::counters::get_drop_count());
// Okay to assign a value to itself.
s2 = std::move(s2);
EXPECT_EQ(456, s1.get_int());
EXPECT_EQ(123, s2.get_int());
EXPECT_EQ(2, drop::counters::get_default_count());
EXPECT_EQ(0, drop::counters::get_drop_count());
} // `TypeUnderTest`'s destructor runs when `s1` and `s2` go out of scope.
EXPECT_EQ(2, drop::counters::get_default_count());
EXPECT_EQ(2, drop::counters::get_drop_count());
}
TEST(DropTest, DropImplWithClone) {
using TypeUnderTest = drop::drop_impl_with_clone::DropImplWithClone;
static_assert(!std::is_default_constructible_v<TypeUnderTest>);
static_assert(std::is_move_constructible_v<TypeUnderTest>);
static_assert(!std::is_trivially_move_constructible_v<TypeUnderTest>);
static_assert(std::is_move_assignable_v<TypeUnderTest>);
static_assert(!std::is_trivially_move_assignable_v<TypeUnderTest>);
static_assert(std::is_destructible_v<TypeUnderTest>);
static_assert(!std::is_trivially_destructible_v<TypeUnderTest>);
static_assert(std::is_copy_constructible_v<TypeUnderTest>);
static_assert(std::is_copy_assignable_v<TypeUnderTest>);
// Test the destructor.
drop::counters::reset_counts();
{
// Non-zero clone and drop count come from the temporary created by
// `create_from_int`.
TypeUnderTest s = TypeUnderTest::create_from_int(123);
EXPECT_EQ(123, s.get_int());
EXPECT_EQ(1, drop::counters::get_clone_count());
EXPECT_EQ(1, drop::counters::get_drop_count());
} // `TypeUnderTest`'s destructor runs when `s` goes out of scope.
EXPECT_EQ(1, drop::counters::get_clone_count());
EXPECT_EQ(2, drop::counters::get_drop_count());
// Testing the move constructor.
drop::counters::reset_counts();
{
TypeUnderTest s = TypeUnderTest::create_from_int(123);
EXPECT_EQ(123, s.get_int());
EXPECT_EQ(1, drop::counters::get_clone_count());
EXPECT_EQ(1, drop::counters::get_drop_count());
// We expect the move to be implemented in terms of copy, so we expect
// a corresponding increase in clone counters.
TypeUnderTest s2(std::move(s));
EXPECT_EQ(123, s2.get_int());
EXPECT_EQ(123, s.get_int()); // NOLINT(bugprone-use-after-move)
EXPECT_EQ(2, drop::counters::get_clone_count());
EXPECT_EQ(1, drop::counters::get_drop_count());
}
EXPECT_EQ(2, drop::counters::get_clone_count());
EXPECT_EQ(3, drop::counters::get_drop_count());
// Testing the move assignment operator.
drop::counters::reset_counts();
{
TypeUnderTest s1 = TypeUnderTest::create_from_int(123);
TypeUnderTest s2 = TypeUnderTest::create_from_int(456);
EXPECT_EQ(2, drop::counters::get_clone_count());
EXPECT_EQ(0, drop::counters::get_clone_from_count());
EXPECT_EQ(2, drop::counters::get_drop_count());
// We expect the move to be implemented in terms of copy, so we expect
// a corresponding increase in clone counters.
s2 = std::move(s1);
EXPECT_EQ(123, s1.get_int()); // NOLINT(bugprone-use-after-move)
EXPECT_EQ(123, s2.get_int());
EXPECT_EQ(2, drop::counters::get_clone_count());
EXPECT_EQ(1, drop::counters::get_clone_from_count());
EXPECT_EQ(2, drop::counters::get_drop_count());
// Okay to assign a value to itself. `clone_from` should *not* be called
// (it would lead to aliasing-related UB in Rust when `self` and `source`
// point to the same object).
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wself-move"
s2 = std::move(s2);
#pragma clang diagnostic pop
EXPECT_EQ(123, s1.get_int());
EXPECT_EQ(123, s2.get_int());
EXPECT_EQ(2, drop::counters::get_clone_count());
EXPECT_EQ(1, drop::counters::get_clone_from_count());
EXPECT_EQ(2, drop::counters::get_drop_count());
}
EXPECT_EQ(2, drop::counters::get_clone_count());
EXPECT_EQ(1, drop::counters::get_clone_from_count());
EXPECT_EQ(4, drop::counters::get_drop_count());
}
TEST(DropTest, DropImplWithNothingElse) {
using TypeUnderTest =
drop::drop_impl_with_nothing_else::DropImplWithNothingElse;
static_assert(!std::is_default_constructible_v<TypeUnderTest>);
static_assert(!std::is_move_constructible_v<TypeUnderTest>);
static_assert(!std::is_trivially_move_constructible_v<TypeUnderTest>);
static_assert(!std::is_move_assignable_v<TypeUnderTest>);
static_assert(!std::is_trivially_move_assignable_v<TypeUnderTest>);
static_assert(std::is_destructible_v<TypeUnderTest>);
static_assert(!std::is_trivially_destructible_v<TypeUnderTest>);
static_assert(!std::is_copy_constructible_v<TypeUnderTest>);
static_assert(!std::is_copy_assignable_v<TypeUnderTest>);
using WrappedTypeUnderTest =
drop::drop_impl_with_nothing_else::WrappedDropImplWithNothingElse;
// Test the destructor of `WrappedTypeUnderTest`. Test that instance methods
// of `TypeUnderTest` work (e.g. `get_int`).
//
// TODO(lukasza): Figure out how to test the destructor of `TypeUnderTest`.
// Maybe this needs to wait until `TypeUnderTest` can be constructed without
// having a `Default` or `Clone` impl (e.g. once `From<T>` is mapped to a
// C++ constructor - see b/286941486).
drop::counters::reset_counts();
{
WrappedTypeUnderTest wrapper;
EXPECT_EQ(0, drop::counters::get_drop_count());
EXPECT_EQ(123, wrapper.field.get_int());
}
EXPECT_EQ(1, drop::counters::get_drop_count());
}
} // namespace
} // namespace crubit