• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
50namespace {
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()|.
56TEST(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.
78TEST(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