FBRetainCycleDetectorTests/FBObjectiveCObjectTests.m (196 lines of code) (raw):

/** * Copyright (c) 2016-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ #import <XCTest/XCTest.h> #import <FBRetainCycleDetector/FBObjectiveCGraphElement+Internal.h> #import <FBRetainCycleDetector/FBObjectiveCObject.h> #import <FBRetainCycleDetector/FBRetainCycleDetector.h> @interface _RCDObjectWrapperTestClass : NSObject - (instancetype)initWithOtherObject:(_RCDObjectWrapperTestClass *)object; @property (nonatomic, strong) NSObject *someObject; @property (nonatomic, copy) NSString *someString; @property (nonatomic, weak) NSObject *irrelevantObject; @property (nonatomic, strong) id aCls; @end @implementation _RCDObjectWrapperTestClass { _RCDObjectWrapperTestClass *_someTestClassInstance; } - (instancetype)initWithOtherObject:(_RCDObjectWrapperTestClass *)object { if (self = [super init]) { _someTestClassInstance = object; } return self; } @end @interface _RCDObjectWrapperTestClassSubclass : _RCDObjectWrapperTestClass @end @implementation _RCDObjectWrapperTestClassSubclass @end @interface FBObjectiveCObjectTests : XCTestCase @end @implementation FBObjectiveCObjectTests #if _INTERNAL_RCD_ENABLED - (void)testObjectsRetainedBySomeObjectWillBeFetched { NSObject *someObject = [NSObject new]; NSString *someString = @"someString"; NSObject *irrelevant = [NSObject new]; _RCDObjectWrapperTestClass *verifyObject = [_RCDObjectWrapperTestClass new]; _RCDObjectWrapperTestClass *testObject = [[_RCDObjectWrapperTestClass alloc] initWithOtherObject:verifyObject]; testObject.someObject = someObject; testObject.someString = someString; testObject.irrelevantObject = irrelevant; FBObjectiveCObject *object = [[FBObjectiveCObject alloc] initWithObject:testObject]; NSSet *retainedObjects = [object allRetainedObjects]; XCTAssertFalse([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:irrelevant]]); XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:someObject]]); XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:someString]]); XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:verifyObject]]); } - (void)testObjectsRetainedByArrayWillBeFetched { NSString *someString = @"someString"; NSObject *someObject = [NSObject new]; NSDictionary *someDictionary = [NSDictionary new]; NSArray *testedArray = @[someString, someObject, someDictionary]; FBObjectiveCObject *abstractedObject = [[FBObjectiveCObject alloc] initWithObject:testedArray]; NSSet *retainedObjects = [abstractedObject allRetainedObjects]; XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:someString]]); XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:someObject]]); XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:someDictionary]]); } - (void)testObjectsRetainedByDictionaryWillBeFetched { NSString *someString = @"someString"; NSObject *someObject = [NSObject new]; NSDictionary *someDictionary = @{someString:someObject}; FBObjectiveCObject *abstractedObject = [[FBObjectiveCObject alloc] initWithObject:someDictionary]; NSSet *retainedObjects = [abstractedObject allRetainedObjects]; XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:someString]]); XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:someObject]]); } - (void)testObjectsRetainedBySetWillBeFetched { NSString *someString = @"someString"; NSObject *someObject = [NSObject new]; NSSet *someSet = [NSSet setWithObjects:someString, someObject, nil]; FBObjectiveCObject *abstractedObject = [[FBObjectiveCObject alloc] initWithObject:someSet]; NSSet *retainedObjects = [abstractedObject allRetainedObjects]; XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:someString]]); XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:someObject]]); } - (void)testThatIfObjectHasStrongPropertyWithNilThenItWontFetchIt { _RCDObjectWrapperTestClass *someObject = [_RCDObjectWrapperTestClass new]; FBObjectiveCObject *abstractedObject = [[FBObjectiveCObject alloc] initWithObject:someObject]; NSSet *retainedObjects = [abstractedObject allRetainedObjects]; XCTAssertEqual([retainedObjects count], 0); } - (void)testObjectThatSubclassesFromObjectWithStrongPropertiesWillFetchPropertiesFromParentClass { _RCDObjectWrapperTestClassSubclass *testObject = [_RCDObjectWrapperTestClassSubclass new]; NSObject *someObject = [NSObject new]; NSObject *irrelevantObject = [NSObject new]; NSString *someString = @"someString"; testObject.someObject = someObject; testObject.irrelevantObject = irrelevantObject; testObject.someString = someString; FBObjectiveCObject *abstractedObject = [[FBObjectiveCObject alloc] initWithObject:testObject]; NSSet *retainedObjects = [abstractedObject allRetainedObjects]; XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:someString]]); XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:someObject]]); XCTAssertFalse([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:irrelevantObject]]); } - (void)testObjectRetainingClassConformingToFastEnumerationWillNotCrash { _RCDObjectWrapperTestClass *someObject = [_RCDObjectWrapperTestClass new]; someObject.aCls = [NSArray class]; FBObjectiveCObject *abstractedObject = [[FBObjectiveCObject alloc] initWithObject:someObject]; NSSet *retainedObjects = [abstractedObject allRetainedObjects]; XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:[NSArray class]]]); } - (void)testHashTableWithWeakObjectsWillNotFetchThoseObjects { NSHashTable *hashTable = [NSHashTable weakObjectsHashTable]; _RCDObjectWrapperTestClass *someObject1 = [_RCDObjectWrapperTestClass new]; _RCDObjectWrapperTestClass *someObject2 = [_RCDObjectWrapperTestClass new]; _RCDObjectWrapperTestClass *someObject3 = [_RCDObjectWrapperTestClass new]; [hashTable addObject:someObject1]; [hashTable addObject:someObject2]; [hashTable addObject:someObject3]; FBObjectiveCObject *abstractedObject = [[FBObjectiveCObject alloc] initWithObject:hashTable]; NSSet *retainedObjects = [abstractedObject allRetainedObjects]; XCTAssertEqual([retainedObjects count], 0); } - (void)testHashTableWithStrongObjectsWillFetchThoseObjects { NSHashTable *hashTable = [NSHashTable new]; _RCDObjectWrapperTestClass *someObject1 = [_RCDObjectWrapperTestClass new]; _RCDObjectWrapperTestClass *someObject2 = [_RCDObjectWrapperTestClass new]; _RCDObjectWrapperTestClass *someObject3 = [_RCDObjectWrapperTestClass new]; [hashTable addObject:someObject1]; [hashTable addObject:someObject2]; [hashTable addObject:someObject3]; FBObjectiveCObject *abstractedObject = [[FBObjectiveCObject alloc] initWithObject:hashTable]; NSSet *retainedObjects = [abstractedObject allRetainedObjects]; XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:someObject1]]); XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:someObject2]]); XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:someObject3]]); } - (void)testMapTableWithWeakKeysAndValueWillNotFetchAnything { NSMapTable *mapTable = [NSMapTable weakToWeakObjectsMapTable]; _RCDObjectWrapperTestClass *keyObject = [_RCDObjectWrapperTestClass new]; _RCDObjectWrapperTestClass *valueObject = [_RCDObjectWrapperTestClass new]; [mapTable setObject:valueObject forKey:keyObject]; FBObjectiveCObject *abstractedObject = [[FBObjectiveCObject alloc] initWithObject:mapTable]; NSSet *retainedObjects = [abstractedObject allRetainedObjects]; XCTAssertEqual([retainedObjects count], 0); } - (void)testMapTableWithWeakKeysAndStrongValuesWillFetchOnlyValues { NSMapTable *mapTable = [NSMapTable weakToStrongObjectsMapTable]; _RCDObjectWrapperTestClass *keyObject = [_RCDObjectWrapperTestClass new]; _RCDObjectWrapperTestClass *valueObject = [_RCDObjectWrapperTestClass new]; [mapTable setObject:valueObject forKey:keyObject]; FBObjectiveCObject *abstractedObject = [[FBObjectiveCObject alloc] initWithObject:mapTable]; NSSet *retainedObjects = [abstractedObject allRetainedObjects]; XCTAssertEqual([retainedObjects count], 1); XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:valueObject]]); } - (void)testMapTableWithStrongKeysAndWeakValuesWillFetchOnlyKeys { NSMapTable *mapTable = [NSMapTable strongToWeakObjectsMapTable]; _RCDObjectWrapperTestClass *keyObject = [_RCDObjectWrapperTestClass new]; _RCDObjectWrapperTestClass *valueObject = [_RCDObjectWrapperTestClass new]; [mapTable setObject:valueObject forKey:keyObject]; FBObjectiveCObject *abstractedObject = [[FBObjectiveCObject alloc] initWithObject:mapTable]; NSSet *retainedObjects = [abstractedObject allRetainedObjects]; XCTAssertEqual([retainedObjects count], 1); XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:keyObject]]); } - (void)testMapTableWithStrongKeysAndStrongValuesWillFetchBothKeysAndValues { NSMapTable *mapTable = [NSMapTable strongToStrongObjectsMapTable]; _RCDObjectWrapperTestClass *keyObject = [_RCDObjectWrapperTestClass new]; _RCDObjectWrapperTestClass *valueObject = [_RCDObjectWrapperTestClass new]; [mapTable setObject:valueObject forKey:keyObject]; FBObjectiveCObject *abstractedObject = [[FBObjectiveCObject alloc] initWithObject:mapTable]; NSSet *retainedObjects = [abstractedObject allRetainedObjects]; XCTAssertEqual([retainedObjects count], 2); XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:keyObject]]); XCTAssertTrue([retainedObjects containsObject:[[FBObjectiveCObject alloc] initWithObject:valueObject]]); } - (void)testTollFreeBridgedDictionaryWillNotCrash { CFDictionaryValueCallBacks cb = kCFTypeDictionaryValueCallBacks; cb.retain = NULL; cb.release = NULL; NSMutableDictionary *dictionary = (__bridge_transfer id)CFDictionaryCreateMutable(NULL, 0, NULL, &cb); NSInteger intV = 5; CFDictionarySetValue((CFMutableDictionaryRef)dictionary, (__bridge const void *)@"key", (const void *)intV); FBObjectiveCObject *abstractedObject = [[FBObjectiveCObject alloc] initWithObject:dictionary]; NSSet *retainedObjects = [abstractedObject allRetainedObjects]; XCTAssertEqual([retainedObjects count], 0); } #endif //_INTERNAL_RCD_ENABLED @end