|
| 1 | +#import <XCTest/XCTest.h> |
| 2 | +#import <OCMock/OCMock.h> |
| 3 | +#import <PrivateViewApi.h> |
| 4 | +#import <Flutter/Flutter.h> |
| 5 | +#import "FlutterPluginRegistrar+FlutterEngine.h" |
| 6 | + |
| 7 | + |
| 8 | +@interface MockFlutterPluginRegistrar : NSObject <FlutterPluginRegistrar> |
| 9 | +@end |
| 10 | + |
| 11 | +@implementation MockFlutterPluginRegistrar |
| 12 | + |
| 13 | +@end |
| 14 | + |
| 15 | + |
| 16 | +@interface PrivateViewApiTests : XCTestCase |
| 17 | +@property (nonatomic, strong) PrivateViewApi *api; |
| 18 | +@property (nonatomic, strong) id mockFlutterApi; |
| 19 | +@property (nonatomic, strong) id mockRegistrar; |
| 20 | +@property (nonatomic, strong) id mockFlutterViewController; |
| 21 | +@property (nonatomic, strong) id mockEngine; |
| 22 | + |
| 23 | +@end |
| 24 | + |
| 25 | +@implementation PrivateViewApiTests |
| 26 | + |
| 27 | +#pragma mark - Setup / Teardown |
| 28 | + |
| 29 | +- (void)setUp { |
| 30 | + [super setUp]; |
| 31 | + |
| 32 | + |
| 33 | + self.mockFlutterApi = OCMClassMock([InstabugPrivateViewApi class]); |
| 34 | + |
| 35 | + |
| 36 | + MockFlutterPluginRegistrar *mockRegistrar = [[MockFlutterPluginRegistrar alloc] init]; |
| 37 | + |
| 38 | + self.mockRegistrar = OCMPartialMock(mockRegistrar); |
| 39 | + |
| 40 | + self.mockEngine = OCMClassMock([FlutterEngine class]); |
| 41 | + OCMStub([self.mockRegistrar flutterEngine]).andReturn(self.mockEngine); |
| 42 | + |
| 43 | + self.mockFlutterViewController = OCMClassMock([UIViewController class]); |
| 44 | + |
| 45 | + OCMStub([self.mockEngine viewController]).andReturn(_mockFlutterViewController); |
| 46 | + |
| 47 | + self.api = OCMPartialMock([[PrivateViewApi alloc] initWithFlutterApi:self.mockFlutterApi registrar: self.mockRegistrar]); |
| 48 | +} |
| 49 | + |
| 50 | +- (void)tearDown { |
| 51 | + [self.mockFlutterApi stopMocking]; |
| 52 | + [self.mockRegistrar stopMocking]; |
| 53 | + [self.mockFlutterViewController stopMocking]; |
| 54 | + [self.mockEngine stopMocking]; |
| 55 | + |
| 56 | + self.api = nil; |
| 57 | + |
| 58 | + [super tearDown]; |
| 59 | +} |
| 60 | + |
| 61 | +#pragma mark - Tests |
| 62 | + |
| 63 | +- (void)testMask_Success { |
| 64 | + XCTestExpectation *expectation = [self expectationWithDescription:@"Mask method success"]; |
| 65 | + |
| 66 | + CGSize imageSize = CGSizeMake(100, 100); // 100x100 pixels |
| 67 | + |
| 68 | + // Step 2: Create the image using UIGraphicsImageRenderer |
| 69 | + UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:imageSize]; |
| 70 | + |
| 71 | + UIImage *screenshot = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) { |
| 72 | + // Draw a red rectangle as an example |
| 73 | + [[UIColor redColor] setFill]; |
| 74 | + CGRect rect = CGRectMake(0, 0, imageSize.width, imageSize.height); |
| 75 | + UIRectFill(rect); |
| 76 | + }]; |
| 77 | + |
| 78 | + NSArray<NSNumber *> *rectangles = @[@10, @20, @30, @40]; |
| 79 | + UIView *mockView = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 30, 40)]; |
| 80 | + |
| 81 | + OCMStub([self.mockFlutterApi getPrivateViewsWithCompletion:([OCMArg invokeBlockWithArgs:rectangles, [NSNull null], nil])]); |
| 82 | + |
| 83 | + |
| 84 | + |
| 85 | + OCMStub([self.mockFlutterViewController view]).andReturn(mockView); |
| 86 | + |
| 87 | + |
| 88 | + [self.api mask:screenshot completion:^(UIImage *result) { |
| 89 | + XCTAssertNotNil(result, @"Masked image should be returned."); |
| 90 | + [expectation fulfill]; |
| 91 | + }]; |
| 92 | + |
| 93 | + [self waitForExpectationsWithTimeout:1 handler:nil]; |
| 94 | +} |
| 95 | + |
| 96 | +- (void)testMask_Error { |
| 97 | + XCTestExpectation *expectation = [self expectationWithDescription:@"Mask method with error"]; |
| 98 | + |
| 99 | + UIImage *screenshot = [UIImage new]; |
| 100 | + FlutterError *error = [FlutterError errorWithCode:@"ERROR" message:@"Test error" details:nil]; |
| 101 | + |
| 102 | + OCMStub([self.mockFlutterApi getPrivateViewsWithCompletion:([OCMArg invokeBlockWithArgs:[NSNull null], error, nil])]); |
| 103 | + |
| 104 | + [self.api mask:screenshot completion:^(UIImage *result) { |
| 105 | + XCTAssertEqual(result, screenshot, @"Original screenshot should be returned on error."); |
| 106 | + [expectation fulfill]; |
| 107 | + }]; |
| 108 | + |
| 109 | + [self waitForExpectationsWithTimeout:1 handler:nil]; |
| 110 | +} |
| 111 | + |
| 112 | +- (void)testGetFlutterViewOrigin_ValidView { |
| 113 | + UIView *mockView = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 100, 100)]; |
| 114 | + |
| 115 | + OCMStub([self.mockFlutterViewController view]).andReturn(mockView); |
| 116 | + |
| 117 | + UIWindow* testWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; |
| 118 | + [testWindow addSubview:mockView]; |
| 119 | + |
| 120 | + CGPoint origin = [self.api getFlutterViewOrigin]; |
| 121 | + |
| 122 | + XCTAssertEqual(origin.x, 10); |
| 123 | + XCTAssertEqual(origin.y, 20); |
| 124 | +} |
| 125 | + |
| 126 | +- (void)testGetFlutterViewOrigin_NilView { |
| 127 | + |
| 128 | + OCMStub([self.mockFlutterViewController view]).andReturn(nil); |
| 129 | +// |
| 130 | + CGPoint origin = [self.api getFlutterViewOrigin]; |
| 131 | + |
| 132 | + XCTAssertEqual(origin.x, 0); |
| 133 | + XCTAssertEqual(origin.y, 0); |
| 134 | +} |
| 135 | + |
| 136 | +- (void)testDrawMaskedImage { |
| 137 | + CGSize size = CGSizeMake(100, 100); |
| 138 | + UIGraphicsBeginImageContext(size); |
| 139 | + UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext(); |
| 140 | + UIGraphicsEndImageContext(); |
| 141 | + |
| 142 | + NSArray<NSValue *> *privateViews = @[ |
| 143 | + [NSValue valueWithCGRect:CGRectMake(10, 10, 20, 20)], |
| 144 | + [NSValue valueWithCGRect:CGRectMake(30, 30, 10, 10)] |
| 145 | + ]; |
| 146 | + |
| 147 | + UIImage *result = [self.api drawMaskedImage:screenshot withPrivateViews:privateViews]; |
| 148 | + |
| 149 | + XCTAssertNotNil(result); |
| 150 | + XCTAssertEqual(result.size.width, 100); |
| 151 | + XCTAssertEqual(result.size.height, 100); |
| 152 | +} |
| 153 | + |
| 154 | +- (void)testConvertToRectangles_ValidInput { |
| 155 | + NSArray<NSNumber *> *rectangles = @[@10, @20, @30, @40]; |
| 156 | + UIView *mockView = [[UIView alloc] initWithFrame:CGRectMake(5, 5, 100, 100)]; |
| 157 | + OCMStub([self.mockFlutterViewController view]).andReturn(mockView); |
| 158 | + |
| 159 | + |
| 160 | + NSArray<NSValue *> *converted = [self.api convertToRectangles:rectangles]; |
| 161 | + |
| 162 | + XCTAssertEqual(converted.count, 1); |
| 163 | + CGRect rect = [converted[0] CGRectValue]; |
| 164 | + XCTAssertTrue(CGRectEqualToRect(rect, CGRectMake(10, 20, 21, 21))); |
| 165 | +} |
| 166 | + |
| 167 | +- (void)testConcurrentMaskCalls { |
| 168 | + XCTestExpectation *expectation = [self expectationWithDescription:@"Handle concurrent calls"]; |
| 169 | + |
| 170 | + CGSize imageSize = CGSizeMake(100, 100); // 100x100 pixels |
| 171 | + |
| 172 | + // Step 2: Create the image using UIGraphicsImageRenderer |
| 173 | + UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:imageSize]; |
| 174 | + |
| 175 | + UIImage *screenshot = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) { |
| 176 | + // Draw a red rectangle as an example |
| 177 | + [[UIColor redColor] setFill]; |
| 178 | + CGRect rect = CGRectMake(0, 0, imageSize.width, imageSize.height); |
| 179 | + UIRectFill(rect); |
| 180 | + }]; |
| 181 | + |
| 182 | + NSArray<NSNumber *> *rectangles = @[@10, @20, @30, @40]; |
| 183 | + |
| 184 | + |
| 185 | + OCMStub([self.mockFlutterApi getPrivateViewsWithCompletion:([OCMArg invokeBlockWithArgs:rectangles, [NSNull null], nil])]); |
| 186 | + |
| 187 | + |
| 188 | + dispatch_group_t group = dispatch_group_create(); |
| 189 | + |
| 190 | + for (int i = 0; i < 5; i++) { |
| 191 | + dispatch_group_enter(group); |
| 192 | + |
| 193 | + [self.api mask:screenshot completion:^(UIImage *result) { |
| 194 | + XCTAssertNotNil(result, @"Each call should return a valid image."); |
| 195 | + dispatch_group_leave(group); |
| 196 | + }]; |
| 197 | + } |
| 198 | + |
| 199 | + dispatch_group_notify(group, dispatch_get_main_queue(), ^{ |
| 200 | + [expectation fulfill]; |
| 201 | + }); |
| 202 | + |
| 203 | + [self waitForExpectationsWithTimeout:2 handler:nil]; |
| 204 | +} |
| 205 | + |
| 206 | +@end |
0 commit comments