|
|
@@ -0,0 +1,104 @@
|
|
|
+//
|
|
|
+// FLEXTests.m
|
|
|
+// FLEXTests
|
|
|
+//
|
|
|
+// Created by Tanner Bennett on 8/27/19.
|
|
|
+// Copyright © 2019 Flipboard. All rights reserved.
|
|
|
+//
|
|
|
+
|
|
|
+#import <XCTest/XCTest.h>
|
|
|
+#import <objc/runtime.h>
|
|
|
+#import "NSObject+Reflection.h"
|
|
|
+#import "NSArray+Functional.h"
|
|
|
+#import "FLEXPropertyAttributes.h"
|
|
|
+#import "FLEXProperty.h"
|
|
|
+#import "FLEXUtility.h"
|
|
|
+#import "FLEXMethod.h"
|
|
|
+
|
|
|
+@interface Subclass : NSObject @end
|
|
|
+@implementation Subclass @end
|
|
|
+
|
|
|
+@interface FLEXTests : XCTestCase
|
|
|
+@property (nonatomic, setter=setMyFoo:) id foo;
|
|
|
+@end
|
|
|
+
|
|
|
+@implementation FLEXTests
|
|
|
+
|
|
|
+- (void)testRuntimeAdditions {
|
|
|
+ XCTAssertEqual(1, [NSObject flex_classHierarchy].count);
|
|
|
+ XCTAssertEqual(4, [FLEXTests flex_classHierarchy].count);
|
|
|
+ XCTAssertEqual([FLEXTests flex_classHierarchy].firstObject, [self class]);
|
|
|
+ XCTAssertEqual([FLEXTests flex_classHierarchy].lastObject, [NSObject class]);
|
|
|
+}
|
|
|
+
|
|
|
+- (void)testAssumptionsAboutClasses {
|
|
|
+ Class cls = [self class];
|
|
|
+ Class meta = objc_getMetaClass(NSStringFromClass(cls).UTF8String);
|
|
|
+
|
|
|
+ // Subsequent `class` calls yield self
|
|
|
+ XCTAssertEqual(cls, [cls class]);
|
|
|
+ XCTAssertEqual(meta, [meta class]);
|
|
|
+ // A class's superclass is NOT a metaclass
|
|
|
+ XCTAssertFalse(class_isMetaClass([cls superclass]));
|
|
|
+ // A metaclass's superclass IS a metaclass
|
|
|
+ XCTAssertTrue(class_isMetaClass([meta superclass]));
|
|
|
+
|
|
|
+ // Subsequent object_getClass calls yield metaclass
|
|
|
+ XCTAssertEqual(object_getClass(cls), meta);
|
|
|
+ XCTAssertEqual(object_getClass(meta), meta);
|
|
|
+
|
|
|
+ // Superclass of a root class is nil
|
|
|
+ XCTAssertNil([NSObject superclass]);
|
|
|
+}
|
|
|
+
|
|
|
+- (void)testAssumptionsAboutMessageSending {
|
|
|
+ // "instances respond to selector" works with metaclasses targeting class objects
|
|
|
+ Class meta = object_getClass(NSBundle.class);
|
|
|
+ XCTAssertTrue([meta instancesRespondToSelector:@selector(mainBundle)]);
|
|
|
+ XCTAssertFalse([meta respondsToSelector:@selector(mainBundle)]);
|
|
|
+}
|
|
|
+
|
|
|
+- (void)testAssumptionsAboutRuntimeMethodFunctions {
|
|
|
+ Class cls = [NSBundle class];
|
|
|
+ Class meta = object_getClass(cls);
|
|
|
+
|
|
|
+ Method bundleID = class_getInstanceMethod(cls, @selector(bundleIdentifier));
|
|
|
+ Method mainBundle = class_getClassMethod(cls, @selector(mainBundle));
|
|
|
+
|
|
|
+ // Preconditions...
|
|
|
+ XCTAssert(class_isMetaClass(meta));
|
|
|
+ XCTAssert(bundleID != nil);
|
|
|
+ XCTAssert(mainBundle != nil);
|
|
|
+
|
|
|
+ // Metaclasses cannot find instance methods
|
|
|
+ XCTAssertEqual(nil, class_getInstanceMethod(meta, @selector(bundleIdentifier)));
|
|
|
+ // Metaclasses can find class methods as both class methods and instance methods,
|
|
|
+ // and the methods found by metaclasses are equal regardless of which function is used
|
|
|
+ XCTAssertEqual(mainBundle, class_getClassMethod(meta, @selector(mainBundle)));
|
|
|
+ // Metaclasses can find class methods as instance methods
|
|
|
+ XCTAssertEqual(mainBundle, class_getInstanceMethod(meta, @selector(mainBundle)));
|
|
|
+}
|
|
|
+
|
|
|
+- (void)testAbilitiesOfKVC {
|
|
|
+ [self setValue:@5 forKey:@"foo"];
|
|
|
+ XCTAssertEqualObjects(self.foo, @5);
|
|
|
+}
|
|
|
+
|
|
|
+- (void)testCPPTypeEncoding {
|
|
|
+ const char *type = "{basic_string<char, std::__1::char_traits<char>, "
|
|
|
+ "std::__1::allocator<char> >={__compressed_pair<std::__1::basic_string<char, "
|
|
|
+ "std::__1::char_traits<char>, std::__1::allocator<char> >::__rep, "
|
|
|
+ "std::__1::allocator<char> >={__rep}}}";
|
|
|
+
|
|
|
+ XCTAssertThrows(NSGetSizeAndAlignment(type, nil, nil));
|
|
|
+}
|
|
|
+
|
|
|
+- (void)testGetClassProperties {
|
|
|
+ NSArray *props = [NSBundle flex_allClassProperties];
|
|
|
+ props = [props flex_filtered:^BOOL(FLEXProperty *obj, NSUInteger idx) {
|
|
|
+ return [obj.name isEqualToString:@"mainBundle"];
|
|
|
+ }];
|
|
|
+ XCTAssert(props.count == 1);
|
|
|
+}
|
|
|
+
|
|
|
+@end
|