FLEXTests.m 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  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 "FLEXMethod.h"
  16. #import "FLEXIvar.h"
  17. @interface Subclass : NSObject {
  18. @public
  19. NSUInteger *_indexes;
  20. }
  21. @end
  22. @implementation Subclass @end
  23. @interface FLEXTests : XCTestCase
  24. @property (nonatomic, setter=setMyFoo:) id foo;
  25. @end
  26. @implementation FLEXTests
  27. - (void)testRuntimeAdditions {
  28. XCTAssertEqual(1, NSObject.flex_classHierarchy.count);
  29. XCTAssertEqual(4, FLEXTests.flex_classHierarchy.count);
  30. XCTAssertEqual(FLEXTests.flex_classHierarchy.firstObject, [self class]);
  31. XCTAssertEqual(FLEXTests.flex_classHierarchy.lastObject, [NSObject class]);
  32. }
  33. - (void)testAssumptionsAboutClasses {
  34. Class cls = [self class];
  35. Class meta = objc_getMetaClass(NSStringFromClass(cls).UTF8String);
  36. // Subsequent `class` calls yield self
  37. XCTAssertEqual(cls, [cls class]);
  38. XCTAssertEqual(meta, [meta class]);
  39. // A class's superclass is NOT a metaclass
  40. XCTAssertFalse(class_isMetaClass([cls superclass]));
  41. // A metaclass's superclass IS a metaclass
  42. XCTAssertTrue(class_isMetaClass([meta superclass]));
  43. // Subsequent object_getClass calls yield metaclass
  44. XCTAssertEqual(object_getClass(cls), meta);
  45. XCTAssertEqual(object_getClass(meta), meta);
  46. // Superclass of a root class is nil
  47. XCTAssertNil(NSObject.superclass);
  48. }
  49. - (void)testAssumptionsAboutMessageSending {
  50. // "instances respond to selector" works with metaclasses targeting class objects
  51. Class meta = object_getClass(NSBundle.class);
  52. XCTAssertTrue([meta instancesRespondToSelector:@selector(mainBundle)]);
  53. XCTAssertFalse([meta respondsToSelector:@selector(mainBundle)]);
  54. }
  55. - (void)testAssumptionsAboutRuntimeMethodFunctions {
  56. Class cls = [NSBundle class];
  57. Class meta = object_getClass(cls);
  58. Method bundleID = class_getInstanceMethod(cls, @selector(bundleIdentifier));
  59. Method mainBundle = class_getClassMethod(cls, @selector(mainBundle));
  60. // Preconditions...
  61. XCTAssert(class_isMetaClass(meta));
  62. XCTAssert(bundleID != nil);
  63. XCTAssert(mainBundle != nil);
  64. // Metaclasses cannot find instance methods
  65. XCTAssertEqual(nil, class_getInstanceMethod(meta, @selector(bundleIdentifier)));
  66. // Metaclasses can find class methods as both class methods and instance methods,
  67. // and the methods found by metaclasses are equal regardless of which function is used
  68. XCTAssertEqual(mainBundle, class_getClassMethod(meta, @selector(mainBundle)));
  69. // Metaclasses can find class methods as instance methods
  70. XCTAssertEqual(mainBundle, class_getInstanceMethod(meta, @selector(mainBundle)));
  71. }
  72. - (void)testAbilitiesOfKVC {
  73. [self setValue:@5 forKey:@"foo"];
  74. XCTAssertEqualObjects(self.foo, @5);
  75. }
  76. - (void)testCPPTypeEncoding {
  77. const char *type = "{basic_string<char, std::__1::char_traits<char>, "
  78. "std::__1::allocator<char> >={__compressed_pair<std::__1::basic_string<char, "
  79. "std::__1::char_traits<char>, std::__1::allocator<char> >::__rep, "
  80. "std::__1::allocator<char> >={__rep}}}";
  81. XCTAssertThrows(NSGetSizeAndAlignment(type, nil, nil));
  82. }
  83. - (void)testGetClassProperties {
  84. NSArray *props = NSBundle.flex_allClassProperties;
  85. props = [props flex_filtered:^BOOL(FLEXProperty *obj, NSUInteger idx) {
  86. return [obj.name isEqualToString:@"mainBundle"];
  87. }];
  88. XCTAssert(props.count == 1);
  89. }
  90. - (void)testIvarUnboxing {
  91. NSUInteger array[4] = { 0xaa, 0xbb, 0xcc, 0x00 };
  92. Subclass *obj = [Subclass new];
  93. obj->_indexes = array;
  94. FLEXIvar *ivar = [Subclass flex_ivarNamed:@"_indexes"];
  95. NSValue *arrayValue = [ivar getPotentiallyUnboxedValue:obj];
  96. NSUInteger *pointerValue = arrayValue.pointerValue;
  97. XCTAssert(pointerValue != nil);
  98. XCTAssertEqual(pointerValue, (NSUInteger *)&array);
  99. XCTAssertEqual(pointerValue[0], 0xaa);
  100. }
  101. @end