Home | History | Annotate | Download | only in mac
      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