// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). #include "rocksdb/slice.h" #include #include "port/port.h" #include "port/stack_trace.h" #include "rocksdb/data_structure.h" #include "rocksdb/types.h" #include "test_util/testharness.h" #include "test_util/testutil.h" #include "util/cast_util.h" namespace ROCKSDB_NAMESPACE { TEST(SliceTest, StringView) { std::string s = "foo"; std::string_view sv = s; ASSERT_EQ(Slice(s), Slice(sv)); ASSERT_EQ(Slice(s), Slice(std::move(sv))); } // Use this to keep track of the cleanups that were actually performed void Multiplier(void* arg1, void* arg2) { int* res = static_cast(arg1); int* num = static_cast(arg2); *res *= *num; } class PinnableSliceTest : public testing::Test { public: void AssertSameData(const std::string& expected, const PinnableSlice& slice) { std::string got; got.assign(slice.data(), slice.size()); ASSERT_EQ(expected, got); } }; // Test that the external buffer is moved instead of being copied. TEST_F(PinnableSliceTest, MoveExternalBuffer) { Slice s("123"); std::string buf; PinnableSlice v1(&buf); v1.PinSelf(s); PinnableSlice v2(std::move(v1)); ASSERT_EQ(buf.data(), v2.data()); ASSERT_EQ(&buf, v2.GetSelf()); PinnableSlice v3; v3 = std::move(v2); ASSERT_EQ(buf.data(), v3.data()); ASSERT_EQ(&buf, v3.GetSelf()); } TEST_F(PinnableSliceTest, Move) { int n2 = 2; int res = 1; const std::string const_str1 = "123"; const std::string const_str2 = "ABC"; Slice slice1(const_str1); Slice slice2(const_str2); { // Test move constructor on a pinned slice. res = 1; PinnableSlice v1; v1.PinSlice(slice1, Multiplier, &res, &n2); PinnableSlice v2(std::move(v1)); // Since v1's Cleanable has been moved to v2, // no cleanup should happen in Reset. v1.Reset(); ASSERT_EQ(1, res); AssertSameData(const_str1, v2); } // v2 is cleaned up. ASSERT_EQ(2, res); { // Test move constructor on an unpinned slice. PinnableSlice v1; v1.PinSelf(slice1); PinnableSlice v2(std::move(v1)); AssertSameData(const_str1, v2); } { // Test move assignment from a pinned slice to // another pinned slice. res = 1; PinnableSlice v1; v1.PinSlice(slice1, Multiplier, &res, &n2); PinnableSlice v2; v2.PinSlice(slice2, Multiplier, &res, &n2); v2 = std::move(v1); // v2's Cleanable will be Reset before moving // anything from v1. ASSERT_EQ(2, res); // Since v1's Cleanable has been moved to v2, // no cleanup should happen in Reset. v1.Reset(); ASSERT_EQ(2, res); AssertSameData(const_str1, v2); } // The Cleanable moved from v1 to v2 will be Reset. ASSERT_EQ(4, res); { // Test move assignment from a pinned slice to // an unpinned slice. res = 1; PinnableSlice v1; v1.PinSlice(slice1, Multiplier, &res, &n2); PinnableSlice v2; v2.PinSelf(slice2); v2 = std::move(v1); // Since v1's Cleanable has been moved to v2, // no cleanup should happen in Reset. v1.Reset(); ASSERT_EQ(1, res); AssertSameData(const_str1, v2); } // The Cleanable moved from v1 to v2 will be Reset. ASSERT_EQ(2, res); { // Test move assignment from an upinned slice to // another unpinned slice. PinnableSlice v1; v1.PinSelf(slice1); PinnableSlice v2; v2.PinSelf(slice2); v2 = std::move(v1); AssertSameData(const_str1, v2); } { // Test move assignment from an upinned slice to // a pinned slice. res = 1; PinnableSlice v1; v1.PinSelf(slice1); PinnableSlice v2; v2.PinSlice(slice2, Multiplier, &res, &n2); v2 = std::move(v1); // v2's Cleanable will be Reset before moving // anything from v1. ASSERT_EQ(2, res); AssertSameData(const_str1, v2); } // No Cleanable is moved from v1 to v2, so no more cleanup. ASSERT_EQ(2, res); } // ***************************************************************** // // Unit test for SmallEnumSet class SmallEnumSetTest : public testing::Test { public: SmallEnumSetTest() = default; ~SmallEnumSetTest() = default; }; TEST_F(SmallEnumSetTest, SmallEnumSetTest1) { FileTypeSet fs; // based on a legacy enum type ASSERT_TRUE(fs.empty()); ASSERT_TRUE(fs.Add(FileType::kIdentityFile)); ASSERT_FALSE(fs.empty()); ASSERT_FALSE(fs.Add(FileType::kIdentityFile)); ASSERT_TRUE(fs.Add(FileType::kInfoLogFile)); ASSERT_TRUE(fs.Contains(FileType::kIdentityFile)); ASSERT_FALSE(fs.Contains(FileType::kDBLockFile)); ASSERT_FALSE(fs.empty()); ASSERT_FALSE(fs.Remove(FileType::kDBLockFile)); ASSERT_TRUE(fs.Remove(FileType::kIdentityFile)); ASSERT_FALSE(fs.empty()); ASSERT_TRUE(fs.Remove(FileType::kInfoLogFile)); ASSERT_TRUE(fs.empty()); } namespace { enum class MyEnumClass { A, B, C }; } // namespace using MyEnumClassSet = SmallEnumSet; TEST_F(SmallEnumSetTest, SmallEnumSetTest2) { MyEnumClassSet s; // based on an enum class type ASSERT_TRUE(s.Add(MyEnumClass::A)); ASSERT_TRUE(s.Contains(MyEnumClass::A)); ASSERT_FALSE(s.Contains(MyEnumClass::B)); ASSERT_TRUE(s.With(MyEnumClass::B).Contains(MyEnumClass::B)); ASSERT_TRUE(s.With(MyEnumClass::A).Contains(MyEnumClass::A)); ASSERT_FALSE(s.Contains(MyEnumClass::B)); ASSERT_FALSE(s.Without(MyEnumClass::A).Contains(MyEnumClass::A)); ASSERT_FALSE( s.With(MyEnumClass::B).Without(MyEnumClass::B).Contains(MyEnumClass::B)); ASSERT_TRUE( s.Without(MyEnumClass::B).With(MyEnumClass::B).Contains(MyEnumClass::B)); ASSERT_TRUE(s.Contains(MyEnumClass::A)); const MyEnumClassSet cs = s; ASSERT_TRUE(cs.Contains(MyEnumClass::A)); ASSERT_EQ(cs, MyEnumClassSet{MyEnumClass::A}); ASSERT_EQ(cs.Without(MyEnumClass::A), MyEnumClassSet{}); ASSERT_EQ(cs, MyEnumClassSet::All().Without(MyEnumClass::B, MyEnumClass::C)); ASSERT_EQ(cs.With(MyEnumClass::B, MyEnumClass::C), MyEnumClassSet::All()); ASSERT_EQ( MyEnumClassSet::All(), MyEnumClassSet{}.With(MyEnumClass::A, MyEnumClass::B, MyEnumClass::C)); ASSERT_NE(cs, MyEnumClassSet{MyEnumClass::B}); ASSERT_NE(cs, MyEnumClassSet::All()); int count = 0; for (MyEnumClass e : cs) { ASSERT_EQ(e, MyEnumClass::A); ++count; } ASSERT_EQ(count, 1); count = 0; for (MyEnumClass e : MyEnumClassSet::All().Without(MyEnumClass::B)) { ASSERT_NE(e, MyEnumClass::B); ++count; } ASSERT_EQ(count, 2); for (MyEnumClass e : MyEnumClassSet{}) { (void)e; assert(false); } } // ***************************************************************** // // Unit test for Status TEST(StatusTest, Update) { const Status ok = Status::OK(); const Status inc = Status::Incomplete("blah"); const Status notf = Status::NotFound("meow"); Status s = ok; ASSERT_TRUE(s.UpdateIfOk(Status::Corruption("bad")).IsCorruption()); ASSERT_TRUE(s.IsCorruption()); s = ok; ASSERT_TRUE(s.UpdateIfOk(Status::OK()).ok()); ASSERT_TRUE(s.UpdateIfOk(ok).ok()); ASSERT_TRUE(s.ok()); ASSERT_TRUE(s.UpdateIfOk(inc).IsIncomplete()); ASSERT_TRUE(s.IsIncomplete()); ASSERT_TRUE(s.UpdateIfOk(notf).IsIncomplete()); ASSERT_TRUE(s.UpdateIfOk(ok).IsIncomplete()); ASSERT_TRUE(s.IsIncomplete()); // Keeps left-most non-OK status s = ok; ASSERT_TRUE( s.UpdateIfOk(Status()).UpdateIfOk(notf).UpdateIfOk(inc).IsNotFound()); ASSERT_TRUE(s.IsNotFound()); } // ***************************************************************** // // Unit test for UnownedPtr TEST(UnownedPtrTest, Tests) { { int x = 0; UnownedPtr p(&x); ASSERT_EQ(p.get(), &x); ASSERT_EQ(*p, 0); x = 1; ASSERT_EQ(*p, 1); ASSERT_EQ(p.get(), &x); ASSERT_EQ(*p, 1); *p = 2; ASSERT_EQ(x, 2); ASSERT_EQ(*p, 2); ASSERT_EQ(p.get(), &x); ASSERT_EQ(*p, 2); } { std::unique_ptr> u = std::make_unique>(); *u = {1, 2}; UnownedPtr> p; ASSERT_FALSE(p); p = u.get(); ASSERT_TRUE(p); ASSERT_EQ(p->first, 1); // These must not compile: /* u = p; u = std::move(p); std::unique_ptr> v{p}; std::unique_ptr> v{std::move(p)}; */ // END must not compile UnownedPtr> q; q = std::move(p); ASSERT_EQ(q->first, 1); // Not committing to any moved-from semantics (on p here) } { std::shared_ptr> s = std::make_shared>(); *s = {1, 2}; UnownedPtr> p; ASSERT_FALSE(p); p = s.get(); ASSERT_TRUE(p); ASSERT_EQ(p->first, 1); // These must not compile: /* s = p; s = std::move(p); std::unique_ptr> t{p}; std::unique_ptr> t{std::move(p)}; */ // END must not compile UnownedPtr> q; q = std::move(p); ASSERT_EQ(q->first, 1); // Not committing to any moved-from semantics (on p here) } } } // namespace ROCKSDB_NAMESPACE int main(int argc, char** argv) { ROCKSDB_NAMESPACE::port::InstallStackTraceHandler(); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }