Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
273 changes: 260 additions & 13 deletions shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,24 @@ - (void)testCanCreatePlatformViewWithoutFlutterView {
}

- (void)testChildClippingViewHitTests {
ChildClippingView* childCilppingView =
ChildClippingView* childClippingView =
[[[ChildClippingView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)] autorelease];
UIView* childView = [[[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)] autorelease];
[childCilppingView addSubview:childView];

XCTAssertFalse([childCilppingView pointInside:CGPointMake(50, 50) withEvent:nil]);
XCTAssertTrue([childCilppingView pointInside:CGPointMake(150, 150) withEvent:nil]);
[childClippingView addSubview:childView];

XCTAssertFalse([childClippingView pointInside:CGPointMake(50, 50) withEvent:nil]);
XCTAssertFalse([childClippingView pointInside:CGPointMake(99, 100) withEvent:nil]);
XCTAssertFalse([childClippingView pointInside:CGPointMake(100, 99) withEvent:nil]);
XCTAssertFalse([childClippingView pointInside:CGPointMake(201, 200) withEvent:nil]);
XCTAssertFalse([childClippingView pointInside:CGPointMake(200, 201) withEvent:nil]);
XCTAssertFalse([childClippingView pointInside:CGPointMake(99, 200) withEvent:nil]);
XCTAssertFalse([childClippingView pointInside:CGPointMake(200, 299) withEvent:nil]);

XCTAssertTrue([childClippingView pointInside:CGPointMake(150, 150) withEvent:nil]);
XCTAssertTrue([childClippingView pointInside:CGPointMake(100, 100) withEvent:nil]);
XCTAssertTrue([childClippingView pointInside:CGPointMake(199, 100) withEvent:nil]);
XCTAssertTrue([childClippingView pointInside:CGPointMake(100, 199) withEvent:nil]);
XCTAssertTrue([childClippingView pointInside:CGPointMake(199, 199) withEvent:nil]);
}

- (void)testCompositePlatformView {
Expand Down Expand Up @@ -266,16 +277,252 @@ - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView {
// The childclippingview's frame is set based on flow, but the platform view's frame is set based
// on quartz. Although they should be the same, but we should tolerate small floating point
// errors.
XCTAssertTrue(fabs(platformViewRectInFlutterView.origin.x - childClippingView.frame.origin.x) <
kFloatCompareEpsilon);
XCTAssertTrue(fabs(platformViewRectInFlutterView.origin.y - childClippingView.frame.origin.y) <
kFloatCompareEpsilon);
XCTAssertTrue(fabs(platformViewRectInFlutterView.size.width -
childClippingView.frame.size.width) < kFloatCompareEpsilon);
XCTAssertTrue(fabs(platformViewRectInFlutterView.size.height -
childClippingView.frame.size.height) < kFloatCompareEpsilon);
XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.x - childClippingView.frame.origin.x),
kFloatCompareEpsilon);
XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.y - childClippingView.frame.origin.y),
kFloatCompareEpsilon);
XCTAssertLessThan(
fabs(platformViewRectInFlutterView.size.width - childClippingView.frame.size.width),
kFloatCompareEpsilon);
XCTAssertLessThan(
fabs(platformViewRectInFlutterView.size.height - childClippingView.frame.size.height),
kFloatCompareEpsilon);

flutterPlatformViewsController->Reset();
}

- (void)testClipRect {
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
/*platform=*/thread_task_runner,
/*raster=*/thread_task_runner,
/*ui=*/thread_task_runner,
/*io=*/thread_task_runner);
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
/*delegate=*/mock_delegate,
/*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
/*task_runners=*/runners);

auto flutterPlatformViewsController = std::make_unique<flutter::FlutterPlatformViewsController>();

FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
[[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease];
flutterPlatformViewsController->RegisterViewFactory(
factory, @"MockFlutterPlatformView",
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
FlutterResult result = ^(id result) {
};
flutterPlatformViewsController->OnMethodCall(
[FlutterMethodCall
methodCallWithMethodName:@"create"
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
result);

XCTAssertNotNil(gMockPlatformView);

UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)] autorelease];
flutterPlatformViewsController->SetFlutterView(mockFlutterView);
// Create embedded view params
flutter::MutatorsStack stack;
// Layer tree always pushes a screen scale factor to the stack
SkMatrix screenScaleMatrix =
SkMatrix::MakeScale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
stack.PushTransform(screenScaleMatrix);
Comment on lines +329 to +331
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: does it need this to test clip specifically?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. as the clip paths will need to get scaled as well.

// Push a clip rect
SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3);
stack.PushClipRect(rect);

auto embeddedViewParams =
std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);

flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
flutterPlatformViewsController->CompositeEmbeddedView(2);
gMockPlatformView.backgroundColor = UIColor.redColor;
XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
[mockFlutterView addSubview:childClippingView];

[mockFlutterView setNeedsLayout];
[mockFlutterView layoutIfNeeded];

for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
CGPoint point = CGPointMake(i, j);
int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView];
// Edges of the clipping might have a semi transparent pixel, we only check the pixels that
// are fully inside the clipped area.
CGRect insideClipping = CGRectMake(3, 3, 1, 1);
if (CGRectContainsPoint(insideClipping, point)) {
XCTAssertEqual(alpha, 255);
} else {
XCTAssertLessThan(alpha, 255);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there an exact value for the alpha?

}
}
}
flutterPlatformViewsController->Reset();
}

- (void)testClipRRect {
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
/*platform=*/thread_task_runner,
/*raster=*/thread_task_runner,
/*ui=*/thread_task_runner,
/*io=*/thread_task_runner);
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
/*delegate=*/mock_delegate,
/*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
/*task_runners=*/runners);

auto flutterPlatformViewsController = std::make_unique<flutter::FlutterPlatformViewsController>();

FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
[[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease];
flutterPlatformViewsController->RegisterViewFactory(
factory, @"MockFlutterPlatformView",
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
FlutterResult result = ^(id result) {
};
flutterPlatformViewsController->OnMethodCall(
[FlutterMethodCall
methodCallWithMethodName:@"create"
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
result);

XCTAssertNotNil(gMockPlatformView);

UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)] autorelease];
flutterPlatformViewsController->SetFlutterView(mockFlutterView);
// Create embedded view params
flutter::MutatorsStack stack;
// Layer tree always pushes a screen scale factor to the stack
SkMatrix screenScaleMatrix =
SkMatrix::MakeScale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
stack.PushTransform(screenScaleMatrix);
// Push a clip rrect
SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1);
stack.PushClipRRect(rrect);

auto embeddedViewParams =
std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);

flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
flutterPlatformViewsController->CompositeEmbeddedView(2);
gMockPlatformView.backgroundColor = UIColor.redColor;
XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
[mockFlutterView addSubview:childClippingView];

[mockFlutterView setNeedsLayout];
[mockFlutterView layoutIfNeeded];

for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
CGPoint point = CGPointMake(i, j);
int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView];
// Edges of the clipping might have a semi transparent pixel, we only check the pixels that
// are fully inside the clipped area.
CGRect insideClipping = CGRectMake(3, 3, 4, 4);
if (CGRectContainsPoint(insideClipping, point)) {
XCTAssertEqual(alpha, 255);
} else {
XCTAssertLessThan(alpha, 255);
}
}
}
flutterPlatformViewsController->Reset();
}

- (void)testClipPath {
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
/*platform=*/thread_task_runner,
/*raster=*/thread_task_runner,
/*ui=*/thread_task_runner,
/*io=*/thread_task_runner);
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
/*delegate=*/mock_delegate,
/*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
/*task_runners=*/runners);

auto flutterPlatformViewsController = std::make_unique<flutter::FlutterPlatformViewsController>();

FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
[[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease];
flutterPlatformViewsController->RegisterViewFactory(
factory, @"MockFlutterPlatformView",
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
FlutterResult result = ^(id result) {
};
flutterPlatformViewsController->OnMethodCall(
[FlutterMethodCall
methodCallWithMethodName:@"create"
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
result);

XCTAssertNotNil(gMockPlatformView);

UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)] autorelease];
flutterPlatformViewsController->SetFlutterView(mockFlutterView);
// Create embedded view params
flutter::MutatorsStack stack;
// Layer tree always pushes a screen scale factor to the stack
SkMatrix screenScaleMatrix =
SkMatrix::MakeScale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
stack.PushTransform(screenScaleMatrix);
// Push a clip path
SkPath path;
path.addRoundRect(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1);
stack.PushClipPath(path);

auto embeddedViewParams =
std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);

flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
flutterPlatformViewsController->CompositeEmbeddedView(2);
gMockPlatformView.backgroundColor = UIColor.redColor;
XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
[mockFlutterView addSubview:childClippingView];

[mockFlutterView setNeedsLayout];
[mockFlutterView layoutIfNeeded];

for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
CGPoint point = CGPointMake(i, j);
int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView];
// Edges of the clipping might have a semi transparent pixel, we only check the pixels that
// are fully inside the clipped area.
CGRect insideClipping = CGRectMake(3, 3, 4, 4);
if (CGRectContainsPoint(insideClipping, point)) {
XCTAssertEqual(alpha, 255);
} else {
XCTAssertLessThan(alpha, 255);
}
}
}
flutterPlatformViewsController->Reset();
}

- (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view {
unsigned char pixel[4] = {0};

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

// Draw the pixel on `point` in the context.
CGContextRef context = CGBitmapContextCreate(
pixel, 1, 1, 8, 4, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast);
CGContextTranslateCTM(context, -point.x, -point.y);
[view.layer renderInContext:context];

CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
// Get the alpha from the pixel that we just rendered.
return pixel[3];
}

@end