Index: chrome/browser/ui/cocoa/objc_zombie_unittest.mm |
diff --git a/chrome/browser/ui/cocoa/objc_zombie_unittest.mm b/chrome/browser/ui/cocoa/objc_zombie_unittest.mm |
index 69f6ea0315806e2c83df6356598b322e2c3103f7..f997c45fdf476093738633a4707b99e2c9e813ab 100644 |
--- a/chrome/browser/ui/cocoa/objc_zombie_unittest.mm |
+++ b/chrome/browser/ui/cocoa/objc_zombie_unittest.mm |
@@ -3,12 +3,28 @@ |
// found in the LICENSE file. |
#import <Cocoa/Cocoa.h> |
+#include <dlfcn.h> |
+#include "base/logging.h" |
#import "base/memory/scoped_nsobject.h" |
#import "chrome/browser/ui/cocoa/objc_zombie.h" |
#include "testing/gtest/include/gtest/gtest.h" |
#include "testing/platform_test.h" |
+namespace { |
+ |
+// Dynamically lookup |objc_setAssociatedObject()|, which isn't |
Mark Mentovai
2011/06/07 21:42:57
look up
Scott Hess - ex-Googler
2011/06/07 23:48:21
Done.
|
+// available until the 10.6 SDK. |
+ |
+typedef void objc_setAssociatedObjectFn(id object, void *key, id value, |
+ int policy); |
+objc_setAssociatedObjectFn* LookupSetAssociatedObjectFn() { |
+ return reinterpret_cast<objc_setAssociatedObjectFn*>( |
+ dlsym(RTLD_DEFAULT, "objc_setAssociatedObject")); |
+} |
+ |
+} // namespace |
+ |
@interface ZombieCxxDestructTest : NSObject |
{ |
scoped_nsobject<id> aRef_; |
@@ -26,18 +42,54 @@ |
} |
@end |
+@interface ZombieAssociatedObjectTest : NSObject |
+{ |
Mark Mentovai
2011/06/07 21:42:57
You don’t need the { } for an instance variable-fr
Scott Hess - ex-Googler
2011/06/07 23:48:21
Done.
|
+} |
++ (BOOL)supportsAssociatedObjects; |
+ |
+- (id)initWithAssociatedObject:(id)anObject; |
+@end |
+ |
+@implementation ZombieAssociatedObjectTest |
+ |
++ (BOOL)supportsAssociatedObjects { |
+ if (LookupSetAssociatedObjectFn()) |
+ return YES; |
+ return NO; |
+} |
+ |
+- (id)initWithAssociatedObject:(id)anObject { |
+ self = [super init]; |
+ if (self) { |
+ objc_setAssociatedObjectFn* fn = LookupSetAssociatedObjectFn(); |
+ if (fn) { |
+ // Cribbed from 10.6 <objc/runtime.h>. |
+ static const int kObjcAssociationRetain = 01401; |
+ |
+ // The address of the variable itself is the unique key, the |
+ // contents don't matter. |
+ static char kAssociatedObjectKey = 'x'; |
+ |
+ (*fn)(self, &kAssociatedObjectKey, anObject, kObjcAssociationRetain); |
+ } |
+ } |
+ return self; |
+} |
+ |
+@end |
+ |
namespace { |
// Verify that the C++ destructors run when the last reference to the |
-// object is released. Unfortunately, testing the negative requires |
-// commenting out the |object_cxxDestruct()| call in |
-// |ZombieDealloc()|. |
- |
+// object is released. |
+// NOTE(shess): To test the negative, comment out the |g_objectDestruct()| |
+// call in |ZombieDealloc()|. |
TEST(ObjcZombieTest, CxxDestructors) { |
scoped_nsobject<id> anObject([[NSObject alloc] init]); |
EXPECT_EQ(1u, [anObject retainCount]); |
- ASSERT_TRUE(ObjcEvilDoers::ZombieEnable(YES, 100)); |
+ ASSERT_TRUE( |
+ ObjcEvilDoers::ZombieEnable(ObjcEvilDoers::RUNTIME_GUESS, YES, 100)); |
scoped_nsobject<ZombieCxxDestructTest> soonInfected( |
[[ZombieCxxDestructTest alloc] initWith:anObject]); |
@@ -53,4 +105,35 @@ TEST(ObjcZombieTest, CxxDestructors) { |
EXPECT_EQ(1u, [anObject retainCount]); |
} |
+// Verify that the associated objects are released when the object is |
+// released. |
+// NOTE(shess): To test the negative, enable zombies with |
+// |RUNTIME_10_5|, and run this test in isolation on 10.6. |
+TEST(ObjcZombieTest, AssociatedObjectsReleased) { |
+ if (![ZombieAssociatedObjectTest supportsAssociatedObjects]) { |
+ LOG(ERROR) |
+ << "ObjcZombieTest.AssociatedObjectsReleased not supported on 10.5"; |
+ return; |
+ } |
+ |
+ scoped_nsobject<id> anObject([[NSObject alloc] init]); |
+ EXPECT_EQ(1u, [anObject retainCount]); |
+ |
+ ASSERT_TRUE( |
+ ObjcEvilDoers::ZombieEnable(ObjcEvilDoers::RUNTIME_GUESS, YES, 100)); |
+ |
+ scoped_nsobject<ZombieAssociatedObjectTest> soonInfected( |
+ [[ZombieAssociatedObjectTest alloc] initWithAssociatedObject:anObject]); |
+ EXPECT_EQ(2u, [anObject retainCount]); |
+ |
+ // When |soonInfected| becomes a zombie, the associated object |
Mark Mentovai
2011/06/07 21:42:57
Good test.
|
+ // should be released. |
+ soonInfected.reset(); |
+ EXPECT_EQ(1u, [anObject retainCount]); |
+ |
+ // The local reference should remain (associated objects not re-released). |
+ ObjcEvilDoers::ZombieDisable(); |
+ EXPECT_EQ(1u, [anObject retainCount]); |
+} |
+ |
} // namespace |