| // 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 <sstream> |
| |
| #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<CtorDtorLogger> TestReturnOfType(int* constructed) { |
| return unique_ptr<CtorDtorLogger>(new CtorDtorLogger(constructed)); |
| } |
| |
| } // namespace |
| |
| TEST(UniquePtrTest, MoveTest) { |
| int constructed = 0; |
| int constructed4 = 0; |
| { |
| unique_ptr<CtorDtorLogger> ptr1(new CtorDtorLogger(&constructed)); |
| EXPECT_EQ(1, constructed); |
| EXPECT_TRUE(ptr1); |
| |
| unique_ptr<CtorDtorLogger> ptr2(nonstd::move(ptr1)); |
| EXPECT_EQ(1, constructed); |
| EXPECT_FALSE(ptr1); |
| EXPECT_TRUE(ptr2); |
| |
| unique_ptr<CtorDtorLogger> ptr3; |
| ptr3 = nonstd::move(ptr2); |
| EXPECT_EQ(1, constructed); |
| EXPECT_FALSE(ptr2); |
| EXPECT_TRUE(ptr3); |
| |
| unique_ptr<CtorDtorLogger> ptr4(new CtorDtorLogger(&constructed4)); |
| EXPECT_EQ(1, constructed4); |
| ptr4 = nonstd::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<int>), |
| "unique_ptr_larger_than_raw_ptr"); |
| |
| { |
| unique_ptr<CtorDtorLogger> 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<CtorDtorLogger> 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<CtorDtorLogger> scoper1; |
| unique_ptr<CtorDtorLogger> 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<CtorDtorLogger[]> 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<CtorDtorLogger[]> 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<CtorDtorLogger[]> scoper1; |
| unique_ptr<CtorDtorLogger[]> 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<CtorDtorLogger> scoper(logger); |
| EXPECT_EQ(1, constructed); |
| } |
| EXPECT_EQ(0, constructed); |
| |
| // Test uncaught return type not leak. |
| { |
| CtorDtorLogger* logger = new CtorDtorLogger(&constructed); |
| unique_ptr<CtorDtorLogger> 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<double, CountingDeleter> 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<double, CountingDeleter> 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<double, CountingDeleter> 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<double, CountingDeleter> scoper1(nullptr, |
| CountingDeleter(&deletes)); |
| unique_ptr<double, CountingDeleter> 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<int> NullIntReturn() { |
| return nullptr; |
| } |
| |
| TEST(UniquePtrTest, Nullptr) { |
| unique_ptr<int> scoper1(nullptr); |
| unique_ptr<int> scoper2(new int); |
| scoper2 = nullptr; |
| unique_ptr<int> scoper3(NullIntReturn()); |
| unique_ptr<int> scoper4 = NullIntReturn(); |
| EXPECT_EQ(nullptr, scoper1.get()); |
| EXPECT_EQ(nullptr, scoper2.get()); |
| EXPECT_EQ(nullptr, scoper3.get()); |
| EXPECT_EQ(nullptr, scoper4.get()); |
| } |
| |
| unique_ptr<int[]> NullIntArrayReturn() { |
| return nullptr; |
| } |
| |
| TEST(UniquePtrTest, NullptrArray) { |
| unique_ptr<int[]> scoper1(nullptr); |
| unique_ptr<int[]> scoper2(new int[3]); |
| scoper2 = nullptr; |
| unique_ptr<int[]> scoper3(NullIntArrayReturn()); |
| unique_ptr<int[]> 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<T> to an ostream shouldn't convert it to a boolean |
| // value first. |
| TEST(ScopedPtrTest, LoggingDoesntConvertToBoolean) { |
| unique_ptr<int> x(new int); |
| std::stringstream s1; |
| s1 << x; |
| |
| std::stringstream s2; |
| s2 << x.get(); |
| |
| EXPECT_EQ(s2.str(), s1.str()); |
| } |