blob: 9288b06a089c122f77ab4d29787cb6d9605b2203 [file] [log] [blame]
// Copyright 2018 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 "base/threading/sequence_bound.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
class SequenceBoundTest : public ::testing::Test {
public:
// Helpful values that our test classes use.
enum Value {
kInitialValue = 0,
kDifferentValue = 1,
// Values used by the Derived class.
kDerivedCtorValue = 111,
kDerivedDtorValue = 222,
// Values used by the Other class.
kOtherCtorValue = 333,
kOtherDtorValue = 444,
};
void SetUp() override { task_runner_ = base::ThreadTaskRunnerHandle::Get(); }
void TearDown() override { scoped_task_environment_.RunUntilIdle(); }
// Do-nothing base class, just so we can test assignment of derived classes.
// It introduces a virtual destructor, so that casting derived classes to
// Base should still use the appropriate (virtual) destructor.
class Base {
public:
virtual ~Base() {}
};
// Handy class to set an int ptr to different values, to verify that things
// are being run properly.
class Derived : public Base {
public:
Derived(Value* ptr) : ptr_(ptr) { *ptr_ = kDerivedCtorValue; }
~Derived() override { *ptr_ = kDerivedDtorValue; }
void SetValue(Value value) { *ptr_ = value; }
Value* ptr_;
};
// Another base class, which sets ints to different values.
class Other {
public:
Other(Value* ptr) : ptr_(ptr) { *ptr = kOtherCtorValue; };
virtual ~Other() { *ptr_ = kOtherDtorValue; }
void SetValue(Value value) { *ptr_ = value; }
Value* ptr_;
};
class MultiplyDerived : public Other, public Derived {
public:
MultiplyDerived(Value* ptr1, Value* ptr2) : Other(ptr1), Derived(ptr2) {}
};
struct VirtuallyDerived : public virtual Base {};
base::test::ScopedTaskEnvironment scoped_task_environment_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
Value value = kInitialValue;
};
TEST_F(SequenceBoundTest, ConstructThenPostThenReset) {
auto derived = SequenceBound<Derived>(task_runner_, &value);
EXPECT_FALSE(derived.is_null());
// Nothing should happen until we run the message loop.
EXPECT_EQ(value, kInitialValue);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kDerivedCtorValue);
// Post now that the object has been constructed.
derived.Post(FROM_HERE, &Derived::SetValue, kDifferentValue);
EXPECT_EQ(value, kDerivedCtorValue);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kDifferentValue);
// Reset it, and make sure that destruction is posted. The owner should
// report that it is null immediately.
derived.Reset();
EXPECT_TRUE(derived.is_null());
EXPECT_EQ(value, kDifferentValue);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kDerivedDtorValue);
}
TEST_F(SequenceBoundTest, PostBeforeConstruction) {
// Construct an object and post a message to it, before construction has been
// run on |task_runner_|.
auto derived = SequenceBound<Derived>(task_runner_, &value);
derived.Post(FROM_HERE, &Derived::SetValue, kDifferentValue);
EXPECT_EQ(value, kInitialValue);
// Both construction and SetValue should run.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kDifferentValue);
}
TEST_F(SequenceBoundTest, MoveConstructionFromSameClass) {
// Verify that we can move-construct with the same class.
auto derived_old = SequenceBound<Derived>(task_runner_, &value);
auto derived_new = std::move(derived_old);
EXPECT_TRUE(derived_old.is_null());
EXPECT_FALSE(derived_new.is_null());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kDerivedCtorValue);
// Verify that |derived_new| owns the object now, and that the virtual
// destructor is called.
derived_new.Reset();
EXPECT_EQ(value, kDerivedCtorValue);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kDerivedDtorValue);
}
TEST_F(SequenceBoundTest, MoveConstructionFromDerivedClass) {
// Verify that we can move-construct to a base class from a derived class.
auto derived = SequenceBound<Derived>(task_runner_, &value);
SequenceBound<Base> base(std::move(derived));
EXPECT_TRUE(derived.is_null());
EXPECT_FALSE(base.is_null());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kDerivedCtorValue);
// Verify that |base| owns the object now, and that destruction still destroys
// Derived properly.
base.Reset();
EXPECT_EQ(value, kDerivedCtorValue);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kDerivedDtorValue);
}
TEST_F(SequenceBoundTest, MultiplyDerivedDestructionWorksLeftSuper) {
// Verify that everything works when we're casting around in ways that might
// change the address. We cast to the left side of MultiplyDerived and then
// reset the owner. ASAN will catch free() errors.
Value value2 = kInitialValue;
auto mderived = SequenceBound<MultiplyDerived>(task_runner_, &value, &value2);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kOtherCtorValue);
EXPECT_EQ(value2, kDerivedCtorValue);
SequenceBound<Other> other = std::move(mderived);
other.Reset();
base::RunLoop().RunUntilIdle();
// Both destructors should have run.
EXPECT_EQ(value, kOtherDtorValue);
EXPECT_EQ(value2, kDerivedDtorValue);
}
TEST_F(SequenceBoundTest, MultiplyDerivedDestructionWorksRightSuper) {
// Verify that everything works when we're casting around in ways that might
// change the address. We cast to the right side of MultiplyDerived and then
// reset the owner. ASAN will catch free() errors.
Value value2 = kInitialValue;
auto mderived = SequenceBound<MultiplyDerived>(task_runner_, &value, &value2);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kOtherCtorValue);
EXPECT_EQ(value2, kDerivedCtorValue);
SequenceBound<Base> base = std::move(mderived);
base.Reset();
base::RunLoop().RunUntilIdle();
// Both destructors should have run.
EXPECT_EQ(value, kOtherDtorValue);
EXPECT_EQ(value2, kDerivedDtorValue);
}
TEST_F(SequenceBoundTest, MoveAssignmentFromSameClass) {
// Test move-assignment using the same classes.
auto derived_old = SequenceBound<Derived>(task_runner_, &value);
SequenceBound<Derived> derived_new;
derived_new = std::move(derived_old);
EXPECT_TRUE(derived_old.is_null());
EXPECT_FALSE(derived_new.is_null());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kDerivedCtorValue);
// Verify that |derived_new| owns the object now. Also verifies that move
// assignment from the same class deletes the outgoing object.
derived_new = SequenceBound<Derived>();
EXPECT_EQ(value, kDerivedCtorValue);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kDerivedDtorValue);
}
TEST_F(SequenceBoundTest, MoveAssignmentFromDerivedClass) {
// Move-assignment from a derived class to a base class.
auto derived = SequenceBound<Derived>(task_runner_, &value);
SequenceBound<Base> base;
base = std::move(derived);
EXPECT_TRUE(derived.is_null());
EXPECT_FALSE(base.is_null());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kDerivedCtorValue);
// Verify that |base| owns the object now, and that destruction still destroys
// Derived properly.
base.Reset();
EXPECT_EQ(value, kDerivedCtorValue);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kDerivedDtorValue);
}
TEST_F(SequenceBoundTest, MoveAssignmentFromDerivedClassDestroysOldObject) {
// Verify that move-assignment from a derived class runs the dtor of the
// outgoing object.
auto derived = SequenceBound<Derived>(task_runner_, &value);
Value value1 = kInitialValue;
Value value2 = kInitialValue;
auto mderived =
SequenceBound<MultiplyDerived>(task_runner_, &value1, &value2);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kDerivedCtorValue);
// Assign |mderived|, and verify that the original object in |derived| is
// destroyed properly.
derived = std::move(mderived);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value, kDerivedDtorValue);
// Delete |derived|, since it has pointers to local vars.
derived.Reset();
base::RunLoop().RunUntilIdle();
}
TEST_F(SequenceBoundTest, MultiplyDerivedPostToLeftBaseClass) {
// Cast and call methods on the left base class.
Value value1 = kInitialValue;
Value value2 = kInitialValue;
auto mderived =
SequenceBound<MultiplyDerived>(task_runner_, &value1, &value2);
// Cast to Other, the left base.
SequenceBound<Other> other(std::move(mderived));
other.Post(FROM_HERE, &Other::SetValue, kDifferentValue);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value1, kDifferentValue);
EXPECT_EQ(value2, kDerivedCtorValue);
other.Reset();
base::RunLoop().RunUntilIdle();
}
TEST_F(SequenceBoundTest, MultiplyDerivedPostToRightBaseClass) {
// Cast and call methods on the right base class.
Value value1 = kInitialValue;
Value value2 = kInitialValue;
auto mderived =
SequenceBound<MultiplyDerived>(task_runner_, &value1, &value2);
SequenceBound<Derived> derived(std::move(mderived));
derived.Post(FROM_HERE, &Derived::SetValue, kDifferentValue);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(value1, kOtherCtorValue);
EXPECT_EQ(value2, kDifferentValue);
derived.Reset();
base::RunLoop().RunUntilIdle();
}
TEST_F(SequenceBoundTest, MoveConstructionFromNullWorks) {
// Verify that this doesn't crash.
SequenceBound<Derived> derived1;
SequenceBound<Derived> derived2(std::move(derived1));
}
TEST_F(SequenceBoundTest, MoveAssignmentFromNullWorks) {
// Verify that this doesn't crash.
SequenceBound<Derived> derived1;
SequenceBound<Derived> derived2;
derived2 = std::move(derived1);
}
TEST_F(SequenceBoundTest, ResetOnNullObjectWorks) {
// Verify that this doesn't crash.
SequenceBound<Derived> derived;
derived.Reset();
}
TEST_F(SequenceBoundTest, IsVirtualBaseClassOf) {
// Check that is_virtual_base_of<> works properly.
// Neither |Base| nor |Derived| is a virtual base of the other.
static_assert(!internal::is_virtual_base_of<Base, Derived>::value,
"|Base| shouldn't be a virtual base of |Derived|");
static_assert(!internal::is_virtual_base_of<Derived, Base>::value,
"|Derived| shouldn't be a virtual base of |Base|");
// |Base| should be a virtual base class of |VirtuallyDerived|, but not the
// other way.
static_assert(internal::is_virtual_base_of<Base, VirtuallyDerived>::value,
"|Base| should be a virtual base of |VirtuallyDerived|");
static_assert(!internal::is_virtual_base_of<VirtuallyDerived, Base>::value,
"|VirtuallyDerived shouldn't be a virtual base of |Base|");
}
} // namespace base