| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/apple/scoped_cftyperef.h" |
| |
| #include <CoreFoundation/CoreFoundation.h> |
| |
| #include <utility> |
| |
| #include "base/memory/scoped_policy.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| // This is effectively a unit test of ScopedTypeRef rather than ScopedCFTypeRef, |
| // but because ScopedTypeRef is parameterized, the CFType version is a great |
| // test subject because it uses all the features. |
| // |
| // Note that CFMutableArray is used for testing, even when subtypes aren't |
| // needed, because it is never optimized into immortal constant values, unlike |
| // other types. |
| |
| namespace base::apple { |
| namespace { |
| |
| TEST(ScopedCFTypeRefTest, ConstructionSameType) { |
| CFMutableArrayRef array = |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks); |
| EXPECT_EQ(1, CFGetRetainCount(array)); |
| |
| ScopedCFTypeRef<CFMutableArrayRef> retain_scoper(array, |
| base::scoped_policy::RETAIN); |
| EXPECT_EQ(array, retain_scoper.get()); |
| EXPECT_EQ(2, CFGetRetainCount(array)); |
| |
| ScopedCFTypeRef<CFMutableArrayRef> assume_scoper(array, |
| base::scoped_policy::ASSUME); |
| EXPECT_EQ(array, assume_scoper.get()); |
| EXPECT_EQ(2, CFGetRetainCount(array)); |
| |
| CFMutableArrayRef array2 = |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks); |
| EXPECT_EQ(1, CFGetRetainCount(array2)); |
| ScopedCFTypeRef<CFMutableArrayRef> assume_scoper2( |
| array2 /* with implicit ASSUME */); |
| EXPECT_EQ(array2, assume_scoper2.get()); |
| EXPECT_EQ(1, CFGetRetainCount(array2)); |
| } |
| |
| TEST(ScopedCFTypeRefTest, ConstructionSubType) { |
| CFMutableArrayRef array = |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks); |
| EXPECT_EQ(1, CFGetRetainCount(array)); |
| |
| ScopedCFTypeRef<CFArrayRef> scoper(array); |
| EXPECT_EQ(array, scoper.get()); |
| EXPECT_EQ(1, CFGetRetainCount(array)); |
| } |
| |
| TEST(ScopedCFTypeRefTest, CopyConstructionSameType) { |
| ScopedCFTypeRef<CFMutableArrayRef> original( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| EXPECT_EQ(1, CFGetRetainCount(original.get())); |
| |
| ScopedCFTypeRef<CFMutableArrayRef> copy(original); |
| EXPECT_EQ(original.get(), copy.get()); |
| EXPECT_EQ(2, CFGetRetainCount(original.get())); |
| } |
| |
| TEST(ScopedCFTypeRefTest, CopyConstructionSubType) { |
| ScopedCFTypeRef<CFMutableArrayRef> original( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| EXPECT_EQ(1, CFGetRetainCount(original.get())); |
| |
| ScopedCFTypeRef<CFArrayRef> copy(original); |
| EXPECT_EQ(original.get(), copy.get()); |
| EXPECT_EQ(2, CFGetRetainCount(original.get())); |
| } |
| |
| TEST(ScopedCFTypeRefTest, CopyConstructionReturnSubType) { |
| auto subtype_returner = []() -> ScopedCFTypeRef<CFArrayRef> { |
| ScopedCFTypeRef<CFMutableArrayRef> original( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| return original; |
| }; |
| EXPECT_TRUE(subtype_returner()); |
| } |
| |
| TEST(ScopedCFTypeRefTest, CopyAssignmentSameType) { |
| ScopedCFTypeRef<CFMutableArrayRef> original( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| EXPECT_EQ(1, CFGetRetainCount(original.get())); |
| |
| ScopedCFTypeRef<CFMutableArrayRef> new_object( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| EXPECT_EQ(1, CFGetRetainCount(new_object.get())); |
| |
| original = new_object; |
| EXPECT_EQ(original.get(), new_object.get()); |
| EXPECT_EQ(2, CFGetRetainCount(original.get())); |
| } |
| |
| TEST(ScopedCFTypeRefTest, CopyAssignmentSubType) { |
| ScopedCFTypeRef<CFArrayRef> original( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| EXPECT_EQ(1, CFGetRetainCount(original.get())); |
| |
| ScopedCFTypeRef<CFMutableArrayRef> new_object( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| EXPECT_EQ(1, CFGetRetainCount(new_object.get())); |
| |
| original = new_object; |
| EXPECT_EQ(original.get(), new_object.get()); |
| EXPECT_EQ(2, CFGetRetainCount(original.get())); |
| } |
| |
| TEST(ScopedCFTypeRefTest, MoveConstructionSameType) { |
| ScopedCFTypeRef<CFMutableArrayRef> original( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| CFMutableArrayRef original_ref = original.get(); |
| EXPECT_EQ(1, CFGetRetainCount(original.get())); |
| |
| ScopedCFTypeRef<CFMutableArrayRef> copy(std::move(original)); |
| EXPECT_EQ(nullptr, original.get()); |
| EXPECT_EQ(original_ref, copy.get()); |
| EXPECT_EQ(1, CFGetRetainCount(copy.get())); |
| } |
| |
| TEST(ScopedCFTypeRefTest, MoveConstructionSubType) { |
| ScopedCFTypeRef<CFMutableArrayRef> original( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| CFMutableArrayRef original_ref = original.get(); |
| EXPECT_EQ(1, CFGetRetainCount(original.get())); |
| |
| ScopedCFTypeRef<CFArrayRef> copy(std::move(original)); |
| EXPECT_EQ(nullptr, original.get()); |
| EXPECT_EQ(original_ref, copy.get()); |
| EXPECT_EQ(1, CFGetRetainCount(copy.get())); |
| } |
| |
| class MoveConstructionReturnTest { |
| public: |
| MoveConstructionReturnTest() |
| : array_(CFArrayCreateMutable(nullptr, |
| /*capacity=*/0, |
| &kCFTypeArrayCallBacks)) {} |
| |
| base::apple::ScopedCFTypeRef<CFMutableArrayRef> take_array() { |
| return std::move(array_); |
| } |
| |
| bool has_array() { return array_.get() != nullptr; } |
| |
| private: |
| base::apple::ScopedCFTypeRef<CFMutableArrayRef> array_; |
| }; |
| |
| TEST(ScopedCFTypeRefTest, MoveConstructionReturn) { |
| MoveConstructionReturnTest test; |
| ASSERT_TRUE(test.has_array()); |
| ASSERT_TRUE(test.take_array()); |
| ASSERT_FALSE(test.has_array()); |
| ASSERT_FALSE(test.take_array()); |
| } |
| |
| TEST(ScopedCFTypeRefTest, MoveAssignmentSameType) { |
| ScopedCFTypeRef<CFMutableArrayRef> original( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| EXPECT_EQ(1, CFGetRetainCount(original.get())); |
| |
| ScopedCFTypeRef<CFMutableArrayRef> new_object( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| CFMutableArrayRef new_ref = new_object.get(); |
| EXPECT_EQ(1, CFGetRetainCount(new_object.get())); |
| |
| original = std::move(new_object); |
| EXPECT_EQ(nullptr, new_object.get()); |
| EXPECT_EQ(new_ref, original.get()); |
| EXPECT_EQ(1, CFGetRetainCount(original.get())); |
| } |
| |
| TEST(ScopedCFTypeRefTest, MoveAssignmentSubType) { |
| ScopedCFTypeRef<CFArrayRef> original( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| EXPECT_EQ(1, CFGetRetainCount(original.get())); |
| |
| ScopedCFTypeRef<CFMutableArrayRef> new_object( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| CFMutableArrayRef new_ref = new_object.get(); |
| EXPECT_EQ(1, CFGetRetainCount(new_object.get())); |
| |
| original = std::move(new_object); |
| EXPECT_EQ(nullptr, new_object.get()); |
| EXPECT_EQ(new_ref, original.get()); |
| EXPECT_EQ(1, CFGetRetainCount(original.get())); |
| } |
| |
| TEST(ScopedCFTypeRefTest, ResetSameType) { |
| CFMutableArrayRef array = |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks); |
| EXPECT_EQ(1, CFGetRetainCount(array)); |
| |
| ScopedCFTypeRef<CFMutableArrayRef> retain_scoper; |
| retain_scoper.reset(array, base::scoped_policy::RETAIN); |
| EXPECT_EQ(array, retain_scoper.get()); |
| EXPECT_EQ(2, CFGetRetainCount(array)); |
| |
| ScopedCFTypeRef<CFMutableArrayRef> assume_scoper; |
| assume_scoper.reset(array, base::scoped_policy::ASSUME); |
| EXPECT_EQ(array, assume_scoper.get()); |
| EXPECT_EQ(2, CFGetRetainCount(array)); |
| |
| CFMutableArrayRef array2 = |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks); |
| EXPECT_EQ(1, CFGetRetainCount(array2)); |
| ScopedCFTypeRef<CFMutableArrayRef> assume_scoper2; |
| assume_scoper2.reset(array2 /* with implicit ASSUME */); |
| EXPECT_EQ(array2, assume_scoper2.get()); |
| EXPECT_EQ(1, CFGetRetainCount(array2)); |
| } |
| |
| TEST(ScopedCFTypeRefTest, ResetSubType) { |
| CFMutableArrayRef array = |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks); |
| EXPECT_EQ(1, CFGetRetainCount(array)); |
| |
| ScopedCFTypeRef<CFArrayRef> scoper; |
| scoper.reset(array); |
| EXPECT_EQ(array, scoper.get()); |
| EXPECT_EQ(1, CFGetRetainCount(array)); |
| } |
| |
| TEST(ScopedCFTypeRefTest, ResetFromScoperSameType) { |
| ScopedCFTypeRef<CFMutableArrayRef> original( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| EXPECT_EQ(1, CFGetRetainCount(original.get())); |
| |
| ScopedCFTypeRef<CFMutableArrayRef> copy; |
| copy.reset(original); |
| EXPECT_EQ(original.get(), copy.get()); |
| EXPECT_EQ(2, CFGetRetainCount(original.get())); |
| } |
| |
| TEST(ScopedCFTypeRefTest, ResetFromScoperSubType) { |
| ScopedCFTypeRef<CFMutableArrayRef> original( |
| CFArrayCreateMutable(nullptr, /*capacity=*/0, &kCFTypeArrayCallBacks)); |
| EXPECT_EQ(1, CFGetRetainCount(original.get())); |
| |
| ScopedCFTypeRef<CFArrayRef> copy; |
| copy.reset(original); |
| EXPECT_EQ(original.get(), copy.get()); |
| EXPECT_EQ(2, CFGetRetainCount(original.get())); |
| } |
| |
| } // namespace |
| } // namespace base::apple |