FBRetainCycleDetectorTests/FBStructEncodingParserTests.mm (165 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 <objc/runtime.h>
#import <XCTest/XCTest.h>
#import <FBRetainCycleDetector/FBStructEncodingParser.h>
#import <FBRetainCycleDetector/Struct.h>
#import <FBRetainCycleDetector/Type.h>
#import <memory>
#import <vector>
@interface FBStructEncodingTests : XCTestCase
@end
struct _RCDTestStructWithPrimitive {
int testPrimitive;
};
struct _RCDTestStructWithObject {
NSObject *object;
};
struct _RCDTestStructWithObjectPrimitiveMixin {
int someInt;
NSObject *someObject;
float *someFloatPointer;
__weak NSObject *someWeakObject;
};
struct _RCDTestStructWithNestedStruct {
int someInt;
struct _RCDTestStructWithObjectPrimitiveMixin mixingStruct;
};
struct _RCDTestStructWithUnnamedBitfield {
unsigned : 4;
};
struct _RCDTestStructWithUnnamedStruct {
struct {
bool value;
};
};
@interface _RCDParserTestClass : NSObject
@property (nonatomic, assign) _RCDTestStructWithPrimitive structWithPrimitive;
@property (nonatomic, assign) _RCDTestStructWithObject structWithObject;
@property (nonatomic, assign) _RCDTestStructWithObjectPrimitiveMixin structWithObjectPrimitiveMixin;
@property (nonatomic, assign) _RCDTestStructWithNestedStruct structWithNestedStruct;
@property (nonatomic, assign) _RCDTestStructWithUnnamedBitfield structWithUnnamedBitfield;
@property (nonatomic, assign) _RCDTestStructWithUnnamedStruct structWithUnnamedStruct;
@end
@implementation _RCDParserTestClass
@end
@implementation FBStructEncodingTests
- (std::string)_getIvarEncodingByName:(NSString *)ivarName forClass:(Class)aCls
{
unsigned int count;
Ivar *ivars = class_copyIvarList(aCls, &count);
std::string typeEncoding = "";
for (unsigned int i = 0; i < count; ++i) {
Ivar ivar = ivars[i];
if ([@(ivar_getName(ivar)) isEqualToString:ivarName]) {
typeEncoding = std::string(ivar_getTypeEncoding(ivar));
break;
}
}
free(ivars);
return typeEncoding;
}
- (void)testThatParserWillParseStructWithPrimitive
{
std::string encoding = [self _getIvarEncodingByName:@"_structWithPrimitive" forClass:[_RCDParserTestClass class]];
XCTAssertTrue(encoding.length() > 0);
FB::RetainCycleDetector::Parser::Struct parsedStruct =
FB::RetainCycleDetector::Parser::parseStructEncoding(encoding);
XCTAssertEqual(parsedStruct.typesContainedInStruct.size(), 1);
XCTAssertEqual(parsedStruct.structTypeName, "_RCDTestStructWithPrimitive");
XCTAssertEqual(parsedStruct.typesContainedInStruct[0]->typeEncoding, "i");
XCTAssertEqual(parsedStruct.typesContainedInStruct[0]->name, "testPrimitive");
}
- (void)testThatParserWillParseStructWithObject
{
std::string encoding = [self _getIvarEncodingByName:@"_structWithObject" forClass:[_RCDParserTestClass class]];
XCTAssertTrue(encoding.length() > 0);
FB::RetainCycleDetector::Parser::Struct parsedStruct =
FB::RetainCycleDetector::Parser::parseStructEncoding(encoding);
XCTAssertEqual(parsedStruct.typesContainedInStruct.size(), 1);
XCTAssertEqual(parsedStruct.structTypeName, "_RCDTestStructWithObject");
XCTAssertEqual(parsedStruct.typesContainedInStruct[0]->typeEncoding, "@");
XCTAssertEqual(parsedStruct.typesContainedInStruct[0]->name, "object");
}
- (void)testThatParserWillParseStructWithObjectsAndPrimitives
{
std::string encoding = [self _getIvarEncodingByName:@"_structWithObjectPrimitiveMixin" forClass:[_RCDParserTestClass class]];
XCTAssertTrue(encoding.length() > 0);
FB::RetainCycleDetector::Parser::Struct parsedStruct =
FB::RetainCycleDetector::Parser::parseStructEncoding(encoding);
XCTAssertEqual(parsedStruct.typesContainedInStruct.size(), 4);
XCTAssertEqual(parsedStruct.structTypeName, "_RCDTestStructWithObjectPrimitiveMixin");
XCTAssertEqual(parsedStruct.typesContainedInStruct[0]->typeEncoding, "i");
XCTAssertEqual(parsedStruct.typesContainedInStruct[0]->name, "someInt");
XCTAssertEqual(parsedStruct.typesContainedInStruct[1]->typeEncoding, "@");
XCTAssertEqual(parsedStruct.typesContainedInStruct[1]->name, "someObject");
XCTAssertEqual(parsedStruct.typesContainedInStruct[2]->typeEncoding, "^f");
XCTAssertEqual(parsedStruct.typesContainedInStruct[2]->name, "someFloatPointer");
XCTAssertEqual(parsedStruct.typesContainedInStruct[3]->typeEncoding, "@");
XCTAssertEqual(parsedStruct.typesContainedInStruct[3]->name, "someWeakObject");
}
- (void)testThatParserWillParseStructWithNestedStruct
{
std::string encoding = [self _getIvarEncodingByName:@"_structWithNestedStruct" forClass:[_RCDParserTestClass class]];
XCTAssertTrue(encoding.length() > 0);
FB::RetainCycleDetector::Parser::Struct parsedStruct =
FB::RetainCycleDetector::Parser::parseStructEncoding(encoding);
XCTAssertEqual(parsedStruct.typesContainedInStruct.size(), 2);
XCTAssertEqual(parsedStruct.structTypeName, "_RCDTestStructWithNestedStruct");
XCTAssertEqual(parsedStruct.typesContainedInStruct[0]->typeEncoding, "i");
std::shared_ptr<FB::RetainCycleDetector::Parser::Struct> innerStruct =
std::dynamic_pointer_cast<FB::RetainCycleDetector::Parser::Struct>(parsedStruct.typesContainedInStruct[1]);
XCTAssertTrue(innerStruct);
XCTAssertEqual(innerStruct->typesContainedInStruct.size(), 4);
XCTAssertEqual(innerStruct->structTypeName, "_RCDTestStructWithObjectPrimitiveMixin");
XCTAssertEqual(innerStruct->typesContainedInStruct[0]->typeEncoding, "i");
XCTAssertEqual(innerStruct->typesContainedInStruct[0]->name, "someInt");
XCTAssertEqual(innerStruct->typesContainedInStruct[1]->typeEncoding, "@");
XCTAssertEqual(innerStruct->typesContainedInStruct[1]->name, "someObject");
XCTAssertEqual(innerStruct->typesContainedInStruct[2]->typeEncoding, "^f");
XCTAssertEqual(innerStruct->typesContainedInStruct[2]->name, "someFloatPointer");
XCTAssertEqual(innerStruct->typesContainedInStruct[3]->typeEncoding, "@");
XCTAssertEqual(innerStruct->typesContainedInStruct[3]->name, "someWeakObject");
}
- (void)testThatParserWillParseStructWithUnnamedBitfield
{
std::string encoding = [self _getIvarEncodingByName:@"_structWithUnnamedBitfield" forClass:[_RCDParserTestClass class]];
XCTAssertTrue(encoding.length() > 0);
FB::RetainCycleDetector::Parser::Struct parsedStruct =
FB::RetainCycleDetector::Parser::parseStructEncoding(encoding);
XCTAssertEqual(parsedStruct.typesContainedInStruct.size(), 1);
XCTAssertEqual(parsedStruct.structTypeName, "_RCDTestStructWithUnnamedBitfield");
XCTAssertEqual(parsedStruct.typesContainedInStruct[0]->typeEncoding, "b4");
XCTAssertEqual(parsedStruct.typesContainedInStruct[0]->name, "");
}
- (void)testThatParserWillParseStructAndPassTypePath
{
std::string encoding = [self _getIvarEncodingByName:@"_structWithNestedStruct" forClass:[_RCDParserTestClass class]];
XCTAssertTrue(encoding.length() > 0);
FB::RetainCycleDetector::Parser::Struct parsedStruct =
FB::RetainCycleDetector::Parser::parseStructEncoding(encoding);
XCTAssertEqual(parsedStruct.typesContainedInStruct.size(), 2);
std::shared_ptr<FB::RetainCycleDetector::Parser::Struct> innerStruct =
std::dynamic_pointer_cast<FB::RetainCycleDetector::Parser::Struct>(parsedStruct.typesContainedInStruct[1]);
XCTAssertTrue(innerStruct);
std::vector<std::string> expectedNamePath = {
"_RCDTestStructWithNestedStruct",
"mixingStruct",
"_RCDTestStructWithObjectPrimitiveMixin",
};
XCTAssertEqual(innerStruct->typesContainedInStruct[1]->typePath, expectedNamePath);
}
- (void)testThatParserWillParseStructWithUnnamedStruct
{
std::string encoding = [self _getIvarEncodingByName:@"_structWithUnnamedStruct"
forClass:[_RCDParserTestClass class]];
XCTAssertTrue(encoding.length() > 0);
FB::RetainCycleDetector::Parser::Struct parsedStruct =
FB::RetainCycleDetector::Parser::parseStructEncoding(encoding);
XCTAssertEqual(parsedStruct.typesContainedInStruct.size(), 1);
std::shared_ptr<FB::RetainCycleDetector::Parser::Struct> innerStruct =
std::dynamic_pointer_cast<FB::RetainCycleDetector::Parser::Struct>(parsedStruct.typesContainedInStruct[0]);
XCTAssertTrue(innerStruct);
XCTAssertEqual(innerStruct->typesContainedInStruct.size(), 1);
XCTAssertEqual(innerStruct->typesContainedInStruct[0]->name, "value");
XCTAssertEqual(innerStruct->typesContainedInStruct[0]->typeEncoding, "B");
}
@end