// Copyright 2015 PDFium 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 #include #include "testing/gtest/include/gtest/gtest.h" #include "macros.h" #include "nonstd_unique_ptr.h" using nonstd::unique_ptr; namespace { // Used to test depth subtyping. class CtorDtorLoggerParent { public: virtual ~CtorDtorLoggerParent() {} virtual void SetPtr(int* ptr) = 0; virtual int SomeMeth(int x) const = 0; }; class CtorDtorLogger : public CtorDtorLoggerParent { public: CtorDtorLogger() : ptr_(nullptr) {} explicit CtorDtorLogger(int* ptr) { SetPtr(ptr); } ~CtorDtorLogger() override { --*ptr_; } void SetPtr(int* ptr) override { ptr_ = ptr; ++*ptr_; } int SomeMeth(int x) const override { return x; } private: int* ptr_; // Disallow evil constructors. CtorDtorLogger(const CtorDtorLogger&) = delete; void operator=(const CtorDtorLogger&) = delete; }; struct CountingDeleter { explicit CountingDeleter(int* count) : count_(count) {} inline void operator()(double* ptr) const { (*count_)++; } int* count_; }; // Do not delete this function! It's existence is to test that you can // return a temporarily constructed version of the scoper. unique_ptr TestReturnOfType(int* constructed) { return unique_ptr(new CtorDtorLogger(constructed)); } } // namespace TEST(UniquePtrTest, MoveTest) { int constructed = 0; int constructed4 = 0; { unique_ptr ptr1(new CtorDtorLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(ptr1); unique_ptr ptr2(std::move(ptr1)); EXPECT_EQ(1, constructed); EXPECT_FALSE(ptr1); EXPECT_TRUE(ptr2); unique_ptr ptr3; ptr3 = std::move(ptr2); EXPECT_EQ(1, constructed); EXPECT_FALSE(ptr2); EXPECT_TRUE(ptr3); unique_ptr ptr4(new CtorDtorLogger(&constructed4)); EXPECT_EQ(1, constructed4); ptr4 = std::move(ptr3); EXPECT_EQ(0, constructed4); EXPECT_FALSE(ptr3); EXPECT_TRUE(ptr4); } EXPECT_EQ(0, constructed); } TEST(UniquePtrTest, UniquePtr) { int constructed = 0; // Ensure size of unique_ptr<> doesn't increase unexpectedly. static_assert(sizeof(int*) >= sizeof(unique_ptr), "unique_ptr_larger_than_raw_ptr"); { unique_ptr scoper(new CtorDtorLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); EXPECT_EQ(10, scoper->SomeMeth(10)); EXPECT_EQ(10, scoper.get()->SomeMeth(10)); EXPECT_EQ(10, (*scoper).SomeMeth(10)); } EXPECT_EQ(0, constructed); // Test reset() and release() { unique_ptr scoper(new CtorDtorLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); scoper.reset(new CtorDtorLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); scoper.reset(); EXPECT_EQ(0, constructed); EXPECT_FALSE(scoper.get()); scoper.reset(new CtorDtorLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); CtorDtorLogger* take = scoper.release(); EXPECT_EQ(1, constructed); EXPECT_FALSE(scoper.get()); delete take; EXPECT_EQ(0, constructed); scoper.reset(new CtorDtorLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); } EXPECT_EQ(0, constructed); // Test swap(), == and != { unique_ptr scoper1; unique_ptr scoper2; EXPECT_TRUE(scoper1 == scoper2.get()); EXPECT_FALSE(scoper1 != scoper2.get()); CtorDtorLogger* logger = new CtorDtorLogger(&constructed); scoper1.reset(logger); EXPECT_EQ(logger, scoper1.get()); EXPECT_FALSE(scoper2.get()); EXPECT_FALSE(scoper1 == scoper2.get()); EXPECT_TRUE(scoper1 != scoper2.get()); scoper2.swap(scoper1); EXPECT_EQ(logger, scoper2.get()); EXPECT_FALSE(scoper1.get()); EXPECT_FALSE(scoper1 == scoper2.get()); EXPECT_TRUE(scoper1 != scoper2.get()); } EXPECT_EQ(0, constructed); } TEST(UniquePtrTest, UniquePtrWithArray) { static const int kNumLoggers = 12; int constructed = 0; { unique_ptr scoper(new CtorDtorLogger[kNumLoggers]); EXPECT_TRUE(scoper); EXPECT_EQ(&scoper[0], scoper.get()); for (int i = 0; i < kNumLoggers; ++i) { scoper[i].SetPtr(&constructed); } EXPECT_EQ(12, constructed); EXPECT_EQ(10, scoper.get()->SomeMeth(10)); EXPECT_EQ(10, scoper[2].SomeMeth(10)); } EXPECT_EQ(0, constructed); // Test reset() and release() { unique_ptr scoper; EXPECT_FALSE(scoper.get()); EXPECT_FALSE(scoper.release()); EXPECT_FALSE(scoper.get()); scoper.reset(); EXPECT_FALSE(scoper.get()); scoper.reset(new CtorDtorLogger[kNumLoggers]); for (int i = 0; i < kNumLoggers; ++i) { scoper[i].SetPtr(&constructed); } EXPECT_EQ(12, constructed); scoper.reset(); EXPECT_EQ(0, constructed); scoper.reset(new CtorDtorLogger[kNumLoggers]); for (int i = 0; i < kNumLoggers; ++i) { scoper[i].SetPtr(&constructed); } EXPECT_EQ(12, constructed); CtorDtorLogger* ptr = scoper.release(); EXPECT_EQ(12, constructed); delete[] ptr; EXPECT_EQ(0, constructed); } EXPECT_EQ(0, constructed); // Test swap(), ==, !=, and type-safe Boolean. { unique_ptr scoper1; unique_ptr scoper2; EXPECT_TRUE(scoper1 == scoper2.get()); EXPECT_FALSE(scoper1 != scoper2.get()); CtorDtorLogger* loggers = new CtorDtorLogger[kNumLoggers]; for (int i = 0; i < kNumLoggers; ++i) { loggers[i].SetPtr(&constructed); } scoper1.reset(loggers); EXPECT_TRUE(scoper1); EXPECT_EQ(loggers, scoper1.get()); EXPECT_FALSE(scoper2); EXPECT_FALSE(scoper2.get()); EXPECT_FALSE(scoper1 == scoper2.get()); EXPECT_TRUE(scoper1 != scoper2.get()); scoper2.swap(scoper1); EXPECT_EQ(loggers, scoper2.get()); EXPECT_FALSE(scoper1.get()); EXPECT_FALSE(scoper1 == scoper2.get()); EXPECT_TRUE(scoper1 != scoper2.get()); } EXPECT_EQ(0, constructed); } TEST(UniquePtrTest, ReturnTypeBehavior) { int constructed = 0; // Test that we can return a unique_ptr. { CtorDtorLogger* logger = new CtorDtorLogger(&constructed); unique_ptr scoper(logger); EXPECT_EQ(1, constructed); } EXPECT_EQ(0, constructed); // Test uncaught return type not leak. { CtorDtorLogger* logger = new CtorDtorLogger(&constructed); unique_ptr scoper(logger); EXPECT_EQ(1, constructed); } EXPECT_EQ(0, constructed); // Call TestReturnOfType() so the compiler doesn't warn for an unused // function. { TestReturnOfType(&constructed); } EXPECT_EQ(0, constructed); } TEST(UniquePtrTest, CustomDeleter) { double dummy_value; // Custom deleter never touches this value. int deletes = 0; int alternate_deletes = 0; // Normal delete support. { deletes = 0; unique_ptr scoper(&dummy_value, CountingDeleter(&deletes)); EXPECT_EQ(0, deletes); EXPECT_TRUE(scoper.get()); } EXPECT_EQ(1, deletes); // Test reset() and release(). deletes = 0; { unique_ptr scoper(nullptr, CountingDeleter(&deletes)); EXPECT_FALSE(scoper.get()); EXPECT_FALSE(scoper.release()); EXPECT_FALSE(scoper.get()); scoper.reset(); EXPECT_FALSE(scoper.get()); EXPECT_EQ(0, deletes); scoper.reset(&dummy_value); scoper.reset(); EXPECT_EQ(1, deletes); scoper.reset(&dummy_value); EXPECT_EQ(&dummy_value, scoper.release()); } EXPECT_EQ(1, deletes); // Test get_deleter(). deletes = 0; alternate_deletes = 0; { unique_ptr scoper(&dummy_value, CountingDeleter(&deletes)); // Call deleter manually. EXPECT_EQ(0, deletes); scoper.get_deleter()(&dummy_value); EXPECT_EQ(1, deletes); // Deleter is still there after reset. scoper.reset(); EXPECT_EQ(2, deletes); scoper.get_deleter()(&dummy_value); EXPECT_EQ(3, deletes); // Deleter can be assigned into (matches C++11 unique_ptr<> spec). scoper.get_deleter() = CountingDeleter(&alternate_deletes); scoper.reset(&dummy_value); EXPECT_EQ(0, alternate_deletes); } EXPECT_EQ(3, deletes); EXPECT_EQ(1, alternate_deletes); // Test swap(), ==, !=, and type-safe Boolean. { unique_ptr scoper1(nullptr, CountingDeleter(&deletes)); unique_ptr scoper2(nullptr, CountingDeleter(&deletes)); EXPECT_TRUE(scoper1 == scoper2.get()); EXPECT_FALSE(scoper1 != scoper2.get()); scoper1.reset(&dummy_value); EXPECT_TRUE(scoper1); EXPECT_EQ(&dummy_value, scoper1.get()); EXPECT_FALSE(scoper2); EXPECT_FALSE(scoper2.get()); EXPECT_FALSE(scoper1 == scoper2.get()); EXPECT_TRUE(scoper1 != scoper2.get()); scoper2.swap(scoper1); EXPECT_EQ(&dummy_value, scoper2.get()); EXPECT_FALSE(scoper1.get()); EXPECT_FALSE(scoper1 == scoper2.get()); EXPECT_TRUE(scoper1 != scoper2.get()); } } unique_ptr NullIntReturn() { return nullptr; } TEST(UniquePtrTest, Nullptr) { unique_ptr scoper1(nullptr); unique_ptr scoper2(new int); scoper2 = nullptr; unique_ptr scoper3(NullIntReturn()); unique_ptr scoper4 = NullIntReturn(); EXPECT_EQ(nullptr, scoper1.get()); EXPECT_EQ(nullptr, scoper2.get()); EXPECT_EQ(nullptr, scoper3.get()); EXPECT_EQ(nullptr, scoper4.get()); } unique_ptr NullIntArrayReturn() { return nullptr; } TEST(UniquePtrTest, NullptrArray) { unique_ptr scoper1(nullptr); unique_ptr scoper2(new int[3]); scoper2 = nullptr; unique_ptr scoper3(NullIntArrayReturn()); unique_ptr scoper4 = NullIntArrayReturn(); EXPECT_EQ(nullptr, scoper1.get()); EXPECT_EQ(nullptr, scoper2.get()); EXPECT_EQ(nullptr, scoper3.get()); EXPECT_EQ(nullptr, scoper4.get()); } // Logging a unique_ptr to an ostream shouldn't convert it to a boolean // value first. TEST(ScopedPtrTest, LoggingDoesntConvertToBoolean) { unique_ptr x(new int); std::stringstream s1; s1 << x; std::stringstream s2; s2 << x.get(); EXPECT_EQ(s2.str(), s1.str()); }