1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #import <Foundation/Foundation.h> 6 #include <objc/runtime.h> 7 8 #include "base/logging.h" 9 #import "base/mac/scoped_nsobject.h" 10 #import "chrome/common/mac/objc_zombie.h" 11 #include "testing/gtest/include/gtest/gtest.h" 12 #include "testing/platform_test.h" 13 14 @interface ZombieCxxDestructTest : NSObject 15 { 16 base::scoped_nsobject<id> aRef_; 17 } 18 - (id)initWith:(id)anObject; 19 @end 20 21 @implementation ZombieCxxDestructTest 22 - (id)initWith:(id)anObject { 23 self = [super init]; 24 if (self) { 25 aRef_.reset([anObject retain]); 26 } 27 return self; 28 } 29 @end 30 31 @interface ZombieAssociatedObjectTest : NSObject 32 - (id)initWithAssociatedObject:(id)anObject; 33 @end 34 35 @implementation ZombieAssociatedObjectTest 36 37 - (id)initWithAssociatedObject:(id)anObject { 38 if ((self = [super init])) { 39 // The address of the variable itself is the unique key, the 40 // contents don't matter. 41 static char kAssociatedObjectKey = 'x'; 42 objc_setAssociatedObject( 43 self, &kAssociatedObjectKey, anObject, OBJC_ASSOCIATION_RETAIN); 44 } 45 return self; 46 } 47 48 @end 49 50 namespace { 51 52 // Verify that the C++ destructors run when the last reference to the 53 // object is released. 54 // NOTE(shess): To test the negative, comment out the |g_objectDestruct()| 55 // call in |ZombieDealloc()|. 56 TEST(ObjcZombieTest, CxxDestructors) { 57 base::scoped_nsobject<id> anObject([[NSObject alloc] init]); 58 EXPECT_EQ(1u, [anObject retainCount]); 59 60 ASSERT_TRUE(ObjcEvilDoers::ZombieEnable(YES, 100)); 61 62 base::scoped_nsobject<ZombieCxxDestructTest> soonInfected( 63 [[ZombieCxxDestructTest alloc] initWith:anObject]); 64 EXPECT_EQ(2u, [anObject retainCount]); 65 66 // When |soonInfected| becomes a zombie, the C++ destructors should 67 // run and release a reference to |anObject|. 68 soonInfected.reset(); 69 EXPECT_EQ(1u, [anObject retainCount]); 70 71 // The local reference should remain (C++ destructors aren't re-run). 72 ObjcEvilDoers::ZombieDisable(); 73 EXPECT_EQ(1u, [anObject retainCount]); 74 } 75 76 // Verify that the associated objects are released when the object is 77 // released. 78 TEST(ObjcZombieTest, AssociatedObjectsReleased) { 79 base::scoped_nsobject<id> anObject([[NSObject alloc] init]); 80 EXPECT_EQ(1u, [anObject retainCount]); 81 82 ASSERT_TRUE(ObjcEvilDoers::ZombieEnable(YES, 100)); 83 84 base::scoped_nsobject<ZombieAssociatedObjectTest> soonInfected( 85 [[ZombieAssociatedObjectTest alloc] initWithAssociatedObject:anObject]); 86 EXPECT_EQ(2u, [anObject retainCount]); 87 88 // When |soonInfected| becomes a zombie, the associated object 89 // should be released. 90 soonInfected.reset(); 91 EXPECT_EQ(1u, [anObject retainCount]); 92 93 // The local reference should remain (associated objects not re-released). 94 ObjcEvilDoers::ZombieDisable(); 95 EXPECT_EQ(1u, [anObject retainCount]); 96 } 97 98 } // namespace 99