| // Copyright 2016 The PDFium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "core/fxcrt/maybe_owned.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "core/fxcrt/unowned_ptr.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace fxcrt { |
| namespace { |
| |
| class PseudoDeletable { |
| public: |
| explicit PseudoDeletable(int id, int* count_location) |
| : id_(id), count_location_(count_location) {} |
| ~PseudoDeletable() { ++(*count_location_); } |
| int GetID() const { return id_; } |
| |
| private: |
| int id_; |
| int* count_location_; |
| }; |
| |
| } // namespace |
| |
| TEST(MaybeOwned, Null) { |
| MaybeOwned<PseudoDeletable> ptr1; |
| EXPECT_FALSE(ptr1.IsOwned()); |
| EXPECT_FALSE(ptr1); |
| EXPECT_FALSE(ptr1.Get()); |
| |
| MaybeOwned<PseudoDeletable> ptr2; |
| EXPECT_TRUE(ptr1 == ptr2); |
| EXPECT_FALSE(ptr1 != ptr2); |
| } |
| |
| TEST(MaybeOwned, NotOwned) { |
| int delete_count = 0; |
| PseudoDeletable thing1(100, &delete_count); |
| { |
| MaybeOwned<PseudoDeletable> ptr(&thing1); |
| EXPECT_FALSE(ptr.IsOwned()); |
| EXPECT_EQ(ptr.Get(), &thing1); |
| EXPECT_EQ(100, ptr->GetID()); |
| EXPECT_TRUE(ptr == &thing1); |
| EXPECT_FALSE(ptr != &thing1); |
| |
| MaybeOwned<PseudoDeletable> empty; |
| EXPECT_FALSE(ptr == empty); |
| EXPECT_TRUE(ptr != empty); |
| } |
| EXPECT_EQ(0, delete_count); |
| |
| delete_count = 0; |
| PseudoDeletable thing2(200, &delete_count); |
| { |
| MaybeOwned<PseudoDeletable> ptr(&thing1); |
| ptr = &thing2; |
| EXPECT_FALSE(ptr.IsOwned()); |
| EXPECT_EQ(ptr.Get(), &thing2); |
| EXPECT_EQ(200, ptr->GetID()); |
| } |
| EXPECT_EQ(0, delete_count); |
| |
| delete_count = 0; |
| int owned_delete_count = 0; |
| { |
| MaybeOwned<PseudoDeletable> ptr(&thing1); |
| EXPECT_EQ(100, ptr->GetID()); |
| ptr = std::make_unique<PseudoDeletable>(300, &owned_delete_count); |
| EXPECT_TRUE(ptr.IsOwned()); |
| EXPECT_EQ(300, ptr->GetID()); |
| } |
| EXPECT_EQ(0, delete_count); |
| EXPECT_EQ(1, owned_delete_count); |
| } |
| |
| TEST(MaybeOwned, UnownedPtr) { |
| int delete_count = 0; |
| PseudoDeletable thing1(100, &delete_count); |
| PseudoDeletable thing2(200, &delete_count); |
| UnownedPtr<PseudoDeletable> unowned1(&thing1); |
| UnownedPtr<PseudoDeletable> unowned2(&thing2); |
| { |
| MaybeOwned<PseudoDeletable> ptr1(unowned1); |
| MaybeOwned<PseudoDeletable> ptr2(unowned2); |
| ptr2 = unowned1; |
| ptr1 = unowned2; |
| } |
| EXPECT_EQ(0, delete_count); |
| } |
| |
| TEST(MaybeOwned, Owned) { |
| int delete_count = 0; |
| { |
| MaybeOwned<PseudoDeletable> ptr( |
| std::make_unique<PseudoDeletable>(100, &delete_count)); |
| EXPECT_TRUE(ptr.IsOwned()); |
| EXPECT_EQ(100, ptr->GetID()); |
| |
| MaybeOwned<PseudoDeletable> empty; |
| EXPECT_FALSE(ptr == empty); |
| EXPECT_TRUE(ptr != empty); |
| } |
| EXPECT_EQ(1, delete_count); |
| |
| delete_count = 0; |
| { |
| MaybeOwned<PseudoDeletable> ptr( |
| std::make_unique<PseudoDeletable>(200, &delete_count)); |
| ptr = std::make_unique<PseudoDeletable>(300, &delete_count); |
| EXPECT_TRUE(ptr.IsOwned()); |
| EXPECT_EQ(300, ptr->GetID()); |
| EXPECT_EQ(1, delete_count); |
| } |
| EXPECT_EQ(2, delete_count); |
| |
| delete_count = 0; |
| int unowned_delete_count = 0; |
| PseudoDeletable thing2(400, &unowned_delete_count); |
| { |
| MaybeOwned<PseudoDeletable> ptr( |
| std::make_unique<PseudoDeletable>(500, &delete_count)); |
| ptr = &thing2; |
| EXPECT_FALSE(ptr.IsOwned()); |
| EXPECT_EQ(400, ptr->GetID()); |
| EXPECT_EQ(1, delete_count); |
| EXPECT_EQ(0, unowned_delete_count); |
| } |
| EXPECT_EQ(1, delete_count); |
| EXPECT_EQ(0, unowned_delete_count); |
| } |
| |
| TEST(MaybeOwned, Release) { |
| int delete_count = 0; |
| { |
| std::unique_ptr<PseudoDeletable> stolen; |
| { |
| MaybeOwned<PseudoDeletable> ptr( |
| std::make_unique<PseudoDeletable>(100, &delete_count)); |
| EXPECT_TRUE(ptr.IsOwned()); |
| stolen = ptr.Release(); |
| EXPECT_FALSE(ptr.IsOwned()); |
| EXPECT_EQ(ptr, stolen); |
| EXPECT_EQ(0, delete_count); |
| } |
| EXPECT_EQ(0, delete_count); |
| } |
| EXPECT_EQ(1, delete_count); |
| } |
| |
| TEST(MaybeOwned, Move) { |
| int delete_count = 0; |
| PseudoDeletable thing1(100, &delete_count); |
| { |
| MaybeOwned<PseudoDeletable> ptr1(&thing1); |
| MaybeOwned<PseudoDeletable> ptr2( |
| std::make_unique<PseudoDeletable>(200, &delete_count)); |
| EXPECT_FALSE(ptr1.IsOwned()); |
| EXPECT_TRUE(ptr2.IsOwned()); |
| |
| MaybeOwned<PseudoDeletable> ptr3(std::move(ptr1)); |
| MaybeOwned<PseudoDeletable> ptr4(std::move(ptr2)); |
| EXPECT_FALSE(ptr1.IsOwned()); // Unowned and null. |
| EXPECT_FALSE(ptr1.Get()); |
| EXPECT_TRUE(ptr2.IsOwned()); // Owned but null. |
| EXPECT_FALSE(ptr2.Get()); |
| EXPECT_FALSE(ptr3.IsOwned()); |
| EXPECT_TRUE(ptr4.IsOwned()); |
| EXPECT_EQ(0, delete_count); |
| EXPECT_EQ(100, ptr3->GetID()); |
| EXPECT_EQ(200, ptr4->GetID()); |
| |
| MaybeOwned<PseudoDeletable> ptr5; |
| MaybeOwned<PseudoDeletable> ptr6; |
| ptr5 = std::move(ptr3); |
| ptr6 = std::move(ptr4); |
| EXPECT_FALSE(ptr3.IsOwned()); // Unowned and null. |
| EXPECT_FALSE(ptr3.Get()); |
| EXPECT_TRUE(ptr4.IsOwned()); // Owned but null. |
| EXPECT_FALSE(ptr4.Get()); |
| EXPECT_FALSE(ptr5.IsOwned()); |
| EXPECT_TRUE(ptr6.IsOwned()); |
| EXPECT_EQ(0, delete_count); |
| EXPECT_EQ(100, ptr5->GetID()); |
| EXPECT_EQ(200, ptr6->GetID()); |
| } |
| EXPECT_EQ(1, delete_count); |
| } |
| |
| namespace { |
| |
| class Thing { |
| public: |
| int x = 42; |
| }; |
| |
| class Owner { |
| public: |
| explicit Owner(std::unique_ptr<Thing> thing) : thing_(std::move(thing)) {} |
| |
| private: |
| std::unique_ptr<Thing> thing_; |
| }; |
| |
| class Manager { |
| public: |
| Manager() |
| : transient_(std::make_unique<Thing>()), |
| owner_(std::make_unique<Owner>(transient_.Release())), |
| thing_(std::move(transient_).Get()) {} |
| |
| bool has_transient() const { return !!transient_.Get(); } |
| |
| private: |
| MaybeOwned<Thing> transient_; // For initializng next two members. |
| const std::unique_ptr<Owner> owner_; // Must outlive thing_. |
| const UnownedPtr<Thing> thing_; |
| }; |
| |
| } // namespace |
| |
| TEST(MaybeOwned, MoveElisionThwarted) { |
| // Test fails if the std::move() in Manager::Manager() is elided. |
| Manager manager; |
| EXPECT_FALSE(manager.has_transient()); |
| } |
| |
| } // namespace fxcrt |