FLEXTests.m 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. //
  2. // FLEXTests.m
  3. // FLEXTests
  4. //
  5. // Created by Tanner Bennett on 8/27/19.
  6. // Copyright © 2019 Flipboard. All rights reserved.
  7. //
  8. #import <XCTest/XCTest.h>
  9. #import <objc/runtime.h>
  10. #import "NSObject+FLEX_Reflection.h"
  11. #import "NSArray+FLEX.h"
  12. #import "FLEXPropertyAttributes.h"
  13. #import "FLEXProperty.h"
  14. #import "FLEXUtility.h"
  15. #import "FLEXRuntimeUtility.h"
  16. #import "FLEXMethod.h"
  17. #import "FLEXIvar.h"
  18. #import "FLEXNewRootClass.h"
  19. @interface Subclass : NSObject {
  20. @public
  21. NSUInteger *_indexes;
  22. }
  23. @end
  24. @implementation Subclass @end
  25. @interface FLEXTests : XCTestCase
  26. @property (nonatomic, setter=setMyFoo:) id foo;
  27. @end
  28. @implementation FLEXTests
  29. - (void)testRuntimeAdditions {
  30. XCTAssertEqual(1, NSObject.flex_classHierarchy.count);
  31. XCTAssertEqual(4, FLEXTests.flex_classHierarchy.count);
  32. XCTAssertEqual(FLEXTests.flex_classHierarchy.firstObject, [self class]);
  33. XCTAssertEqual(FLEXTests.flex_classHierarchy.lastObject, [NSObject class]);
  34. }
  35. - (void)testAssumptionsAboutClasses {
  36. Class cls = [self class];
  37. Class meta = objc_getMetaClass(NSStringFromClass(cls).UTF8String);
  38. Class rootMeta = object_getClass(meta);
  39. // Subsequent `class` calls yield self
  40. XCTAssertEqual(cls, [cls class]);
  41. XCTAssertEqual(meta, [meta class]);
  42. // A class's superclass is NOT a metaclass
  43. XCTAssertFalse(class_isMetaClass([cls superclass]));
  44. // A metaclass's superclass IS a metaclass
  45. XCTAssertTrue(class_isMetaClass([meta superclass]));
  46. // Subsequent object_getClass calls yield metaclass
  47. XCTAssertEqual(object_getClass(cls), meta);
  48. XCTAssertEqual(object_getClass(object_getClass(meta)), rootMeta);
  49. // Superclass of a root class is nil
  50. XCTAssertNil(NSObject.superclass);
  51. }
  52. - (void)testAssumptionsAboutMessageSending {
  53. // "instances respond to selector" works with metaclasses targeting class objects
  54. Class meta = object_getClass(NSBundle.class);
  55. XCTAssertTrue([meta instancesRespondToSelector:@selector(mainBundle)]);
  56. XCTAssertFalse([meta respondsToSelector:@selector(mainBundle)]);
  57. }
  58. - (void)testAssumptionsAboutRuntimeMethodFunctions {
  59. Class cls = [NSBundle class];
  60. Class meta = object_getClass(cls);
  61. Method bundleID = class_getInstanceMethod(cls, @selector(bundleIdentifier));
  62. Method mainBundle = class_getClassMethod(cls, @selector(mainBundle));
  63. // Preconditions...
  64. XCTAssert(class_isMetaClass(meta));
  65. XCTAssert(bundleID != nil);
  66. XCTAssert(mainBundle != nil);
  67. // Metaclasses cannot find instance methods
  68. XCTAssertEqual(nil, class_getInstanceMethod(meta, @selector(bundleIdentifier)));
  69. // Metaclasses can find class methods as both class methods and instance methods,
  70. // and the methods found by metaclasses are equal regardless of which function is used
  71. XCTAssertEqual(mainBundle, class_getClassMethod(meta, @selector(mainBundle)));
  72. // Metaclasses can find class methods as instance methods
  73. XCTAssertEqual(mainBundle, class_getInstanceMethod(meta, @selector(mainBundle)));
  74. }
  75. - (void)testAbilitiesOfKVC {
  76. [self setValue:@5 forKey:@"foo"];
  77. XCTAssertEqualObjects(self.foo, @5);
  78. }
  79. - (void)testCPPTypeEncoding {
  80. const char *type = "{basic_string<char, std::__1::char_traits<char>, "
  81. "std::__1::allocator<char> >={__compressed_pair<std::__1::basic_string<char, "
  82. "std::__1::char_traits<char>, std::__1::allocator<char> >::__rep, "
  83. "std::__1::allocator<char> >={__rep}}}";
  84. XCTAssertThrows(NSGetSizeAndAlignment(type, nil, nil));
  85. }
  86. - (void)testGetClassProperties {
  87. NSArray *props = NSBundle.flex_allClassProperties;
  88. props = [props flex_filtered:^BOOL(FLEXProperty *obj, NSUInteger idx) {
  89. return [obj.name isEqualToString:@"mainBundle"];
  90. }];
  91. XCTAssert(props.count == 1);
  92. }
  93. - (void)testIvarUnboxing {
  94. NSUInteger array[4] = { 0xaa, 0xbb, 0xcc, 0x00 };
  95. Subclass *obj = [Subclass new];
  96. obj->_indexes = array;
  97. FLEXIvar *ivar = [Subclass flex_ivarNamed:@"_indexes"];
  98. NSValue *arrayValue = [ivar getPotentiallyUnboxedValue:obj];
  99. NSUInteger *pointerValue = arrayValue.pointerValue;
  100. XCTAssert(pointerValue != nil);
  101. XCTAssertEqual(pointerValue, (NSUInteger *)&array);
  102. XCTAssertEqual(pointerValue[0], 0xaa);
  103. }
  104. - (void)testSafeRespondsToSelector {
  105. XCTAssertFalse([FLEXRuntimeUtility
  106. safeObject:[NSObject class] respondsToSelector:@selector(testSafeRespondsToSelector)
  107. ]);
  108. Class root = NSClassFromString(@"FLEXNewRootClass");
  109. XCTAssertTrue([FLEXRuntimeUtility safeObject:root respondsToSelector:@selector(theOnlyMethod)]);
  110. XCTAssertFalse([FLEXRuntimeUtility safeObject:root respondsToSelector:@selector(class)]);
  111. }
  112. - (void)testSafeGetClassName {
  113. id instance = [NSClassFromString(@"FLEXNewRootClass") alloc];
  114. NSString *className = [FLEXRuntimeUtility safeClassNameForObject:instance];
  115. XCTAssertEqualObjects(@"FLEXNewRootClass", className);
  116. }
  117. @end