5 #import <OCMock/OCMock.h>
9 #include "third_party/googletest/googletest/include/gtest/gtest.h"
15 @property(readonly, nonatomic, nonnull) NSMutableArray<NSView*>*
pathClipViews;
25 const std::vector<FlutterPlatformViewMutation>& mutations) {
40 void ExpectTransform3DEqual(
const CATransform3D& t,
const CATransform3D& u) {
41 EXPECT_NEAR(t.m11, u.m11,
kMaxErr);
42 EXPECT_NEAR(t.m12, u.m12,
kMaxErr);
43 EXPECT_NEAR(t.m13, u.m13,
kMaxErr);
44 EXPECT_NEAR(t.m14, u.m14,
kMaxErr);
46 EXPECT_NEAR(t.m21, u.m21,
kMaxErr);
47 EXPECT_NEAR(t.m22, u.m22,
kMaxErr);
48 EXPECT_NEAR(t.m23, u.m23,
kMaxErr);
49 EXPECT_NEAR(t.m24, u.m24,
kMaxErr);
51 EXPECT_NEAR(t.m31, u.m31,
kMaxErr);
52 EXPECT_NEAR(t.m32, u.m32,
kMaxErr);
53 EXPECT_NEAR(t.m33, u.m33,
kMaxErr);
54 EXPECT_NEAR(t.m34, u.m34,
kMaxErr);
56 EXPECT_NEAR(t.m41, u.m41,
kMaxErr);
57 EXPECT_NEAR(t.m42, u.m42,
kMaxErr);
58 EXPECT_NEAR(t.m43, u.m43,
kMaxErr);
59 EXPECT_NEAR(t.m44, u.m44,
kMaxErr);
63 TEST(FlutterMutatorViewTest, BasicFrameIsCorrect) {
64 NSView* platformView = [[NSView alloc] init];
69 std::vector<FlutterPlatformViewMutation> mutations{
71 .type = kFlutterPlatformViewMutationTypeTransformation,
73 FlutterTransformation{
82 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
84 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(100, 50, 30, 20)));
85 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
86 EXPECT_EQ(mutatorView.pathClipViews.count, 0ull);
87 EXPECT_NE(mutatorView.platformViewContainer, nil);
90 TEST(FlutterMutatorViewTest, ClipsToBounds) {
91 NSView* platformView = [[NSView alloc] init];
93 EXPECT_TRUE(mutatorView.clipsToBounds);
96 TEST(FlutterMutatorViewTest, TransformedFrameIsCorrect) {
97 NSView* platformView = [[NSView alloc] init];
99 NSView* mutatorViewParent = [[NSView alloc] init];
100 mutatorViewParent.wantsLayer = YES;
101 mutatorViewParent.layer.contentsScale = 2.0;
102 [mutatorViewParent addSubview:mutatorView];
104 std::vector<FlutterPlatformViewMutation> mutations{
106 .type = kFlutterPlatformViewMutationTypeTransformation,
108 FlutterTransformation{
114 .type = kFlutterPlatformViewMutationTypeTransformation,
116 FlutterTransformation{
124 .type = kFlutterPlatformViewMutationTypeTransformation,
126 FlutterTransformation{
136 ApplyFlutterLayer(mutatorView, FlutterSize{30 * 2, 20 * 2}, mutations);
137 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(92.5, 45, 45, 30)));
138 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
140 ExpectTransform3DEqual(mutatorView.platformViewContainer.layer.sublayerTransform,
141 CATransform3DMakeScale(1.5, 1.5, 1));
144 TEST(FlutterMutatorViewTest, FrameWithLooseClipIsCorrect) {
145 NSView* platformView = [[NSView alloc] init];
150 std::vector<FlutterPlatformViewMutation> mutations{
152 .type = kFlutterPlatformViewMutationTypeClipRect,
153 .clip_rect = FlutterRect{80, 40, 200, 100},
156 .type = kFlutterPlatformViewMutationTypeTransformation,
158 FlutterTransformation{
167 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
169 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(100, 50, 30, 20)));
170 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
173 TEST(FlutterMutatorViewTest, FrameWithTightClipIsCorrect) {
174 NSView* platformView = [[NSView alloc] init];
179 std::vector<FlutterPlatformViewMutation> mutations{
181 .type = kFlutterPlatformViewMutationTypeClipRect,
182 .clip_rect = FlutterRect{80, 40, 200, 100},
185 .type = kFlutterPlatformViewMutationTypeClipRect,
186 .clip_rect = FlutterRect{110, 55, 120, 65},
189 .type = kFlutterPlatformViewMutationTypeTransformation,
191 FlutterTransformation{
200 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
202 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 55, 10, 10)));
204 CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -5, 30, 20)));
205 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
208 TEST(FlutterMutatorViewTest, FrameWithTightClipAndTransformIsCorrect) {
209 NSView* platformView = [[NSView alloc] init];
211 NSView* mutatorViewParent = [[NSView alloc] init];
212 mutatorViewParent.wantsLayer = YES;
213 mutatorViewParent.layer.contentsScale = 2.0;
214 [mutatorViewParent addSubview:mutatorView];
216 std::vector<FlutterPlatformViewMutation> mutations{
218 .type = kFlutterPlatformViewMutationTypeTransformation,
220 FlutterTransformation{
226 .type = kFlutterPlatformViewMutationTypeClipRect,
227 .clip_rect = FlutterRect{80, 40, 200, 100},
230 .type = kFlutterPlatformViewMutationTypeClipRect,
231 .clip_rect = FlutterRect{110, 55, 120, 65},
234 .type = kFlutterPlatformViewMutationTypeTransformation,
236 FlutterTransformation{
244 .type = kFlutterPlatformViewMutationTypeTransformation,
246 FlutterTransformation{
255 ApplyFlutterLayer(mutatorView, FlutterSize{30 * 2, 20 * 2}, mutations);
257 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 55, 10, 10)));
259 CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-17.5, -10, 45, 30)));
260 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
264 TEST(FlutterMutatorViewTest, RoundRectClipsToSimpleRectangle) {
265 NSView* platformView = [[NSView alloc] init];
268 std::vector<FlutterPlatformViewMutation> mutations{
270 .type = kFlutterPlatformViewMutationTypeClipRoundedRect,
273 .rect = FlutterRect{110, 30, 120, 90},
274 .upper_left_corner_radius = FlutterSize{10, 10},
275 .upper_right_corner_radius = FlutterSize{10, 10},
276 .lower_right_corner_radius = FlutterSize{10, 10},
277 .lower_left_corner_radius = FlutterSize{10, 10},
281 .type = kFlutterPlatformViewMutationTypeTransformation,
283 FlutterTransformation{
292 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
294 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 50, 10, 20)));
296 CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, 0, 30, 20)));
297 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
298 EXPECT_EQ(mutatorView.pathClipViews.count, 0ul);
304 TEST(FlutterMutatorViewTest, ViewsSetIsFlipped) {
305 NSView* platformView = [[NSView alloc] init];
308 std::vector<FlutterPlatformViewMutation> mutations{
310 .type = kFlutterPlatformViewMutationTypeClipRoundedRect,
313 .rect = FlutterRect{110, 60, 150, 150},
314 .upper_left_corner_radius = FlutterSize{10, 10},
315 .upper_right_corner_radius = FlutterSize{10, 10},
316 .lower_right_corner_radius = FlutterSize{10, 10},
317 .lower_left_corner_radius = FlutterSize{10, 10},
321 .type = kFlutterPlatformViewMutationTypeTransformation,
323 FlutterTransformation{
332 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
334 EXPECT_TRUE(mutatorView.isFlipped);
335 ASSERT_EQ(mutatorView.pathClipViews.count, 1ul);
336 EXPECT_TRUE(mutatorView.pathClipViews.firstObject.isFlipped);
337 EXPECT_TRUE(mutatorView.platformViewContainer.isFlipped);
340 TEST(FlutterMutatorViewTest, RectsClipsToPathWhenRotated) {
341 NSView* platformView = [[NSView alloc] init];
343 std::vector<FlutterPlatformViewMutation> mutations{
345 .type = kFlutterPlatformViewMutationTypeTransformation,
348 FlutterTransformation{
349 .scaleX = 0.9238795325112867,
350 .skewX = -0.3826834323650898,
351 .skewY = 0.3826834323650898,
352 .scaleY = 0.9238795325112867,
356 .type = kFlutterPlatformViewMutationTypeClipRect,
357 .clip_rect = FlutterRect{110, 60, 150, 150},
360 .type = kFlutterPlatformViewMutationTypeTransformation,
362 FlutterTransformation{
370 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
371 EXPECT_EQ(mutatorView.pathClipViews.count, 1ul);
372 EXPECT_NEAR(mutatorView.platformViewContainer.frame.size.width, 35.370054622640396,
kMaxErr);
373 EXPECT_NEAR(mutatorView.platformViewContainer.frame.size.height, 29.958093621178421,
kMaxErr);
376 TEST(FlutterMutatorViewTest, RoundRectClipsToPath) {
377 NSView* platformView = [[NSView alloc] init];
380 std::vector<FlutterPlatformViewMutation> mutations{
382 .type = kFlutterPlatformViewMutationTypeClipRoundedRect,
385 .rect = FlutterRect{110, 60, 150, 150},
386 .upper_left_corner_radius = FlutterSize{10, 10},
387 .upper_right_corner_radius = FlutterSize{10, 10},
388 .lower_right_corner_radius = FlutterSize{10, 10},
389 .lower_left_corner_radius = FlutterSize{10, 10},
393 .type = kFlutterPlatformViewMutationTypeTransformation,
395 FlutterTransformation{
404 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
406 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 60, 20, 10)));
408 CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -10, 30, 20)));
409 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
410 EXPECT_EQ(mutatorView.pathClipViews.count, 1ul);
411 ExpectTransform3DEqual(mutatorView.pathClipViews.firstObject.layer.mask.transform,
412 CATransform3DMakeTranslation(-100, -50, 0));
415 TEST(FlutterMutatorViewTest, PathClipViewsAreAddedAndRemoved) {
416 NSView* platformView = [[NSView alloc] init];
419 std::vector<FlutterPlatformViewMutation> mutations{
421 .type = kFlutterPlatformViewMutationTypeTransformation,
423 FlutterTransformation{
432 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
434 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(100, 50, 30, 20)));
435 EXPECT_EQ(mutatorView.pathClipViews.count, 0ull);
437 std::vector<FlutterPlatformViewMutation> mutations2{
439 .type = kFlutterPlatformViewMutationTypeClipRoundedRect,
442 .rect = FlutterRect{110, 60, 150, 150},
443 .upper_left_corner_radius = FlutterSize{10, 10},
444 .upper_right_corner_radius = FlutterSize{10, 10},
445 .lower_right_corner_radius = FlutterSize{10, 10},
446 .lower_left_corner_radius = FlutterSize{10, 10},
450 .type = kFlutterPlatformViewMutationTypeTransformation,
452 FlutterTransformation{
461 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations2);
463 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 60, 20, 10)));
465 CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -10, 30, 20)));
466 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
467 EXPECT_EQ(mutatorView.pathClipViews.count, 1ul);
469 EXPECT_EQ(platformView.superview, mutatorView.platformViewContainer);
470 EXPECT_EQ(mutatorView.platformViewContainer.superview, mutatorView.pathClipViews[0]);
471 EXPECT_EQ(mutatorView.pathClipViews[0].superview, mutatorView);
473 std::vector<FlutterPlatformViewMutation> mutations3{
475 .type = kFlutterPlatformViewMutationTypeClipRoundedRect,
478 .rect = FlutterRect{110, 55, 150, 150},
479 .upper_left_corner_radius = FlutterSize{10, 10},
480 .upper_right_corner_radius = FlutterSize{10, 10},
481 .lower_right_corner_radius = FlutterSize{10, 10},
482 .lower_left_corner_radius = FlutterSize{10, 10},
486 .type = kFlutterPlatformViewMutationTypeClipRoundedRect,
489 .rect = FlutterRect{30, 30, 120, 65},
490 .upper_left_corner_radius = FlutterSize{10, 10},
491 .upper_right_corner_radius = FlutterSize{10, 10},
492 .lower_right_corner_radius = FlutterSize{10, 10},
493 .lower_left_corner_radius = FlutterSize{10, 10},
497 .type = kFlutterPlatformViewMutationTypeTransformation,
499 FlutterTransformation{
508 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations3);
510 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 55, 10, 10)));
512 CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -5, 30, 20)));
513 EXPECT_EQ(mutatorView.pathClipViews.count, 2ul);
515 EXPECT_EQ(platformView.superview, mutatorView.platformViewContainer);
516 EXPECT_EQ(mutatorView.platformViewContainer.superview, mutatorView.pathClipViews[1]);
517 EXPECT_EQ(mutatorView.pathClipViews[1].superview, mutatorView.pathClipViews[0]);
518 EXPECT_EQ(mutatorView.pathClipViews[0].superview, mutatorView);
520 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations2);
522 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 60, 20, 10)));
524 CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -10, 30, 20)));
525 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
526 EXPECT_EQ(mutatorView.pathClipViews.count, 1ul);
528 EXPECT_EQ(platformView.superview, mutatorView.platformViewContainer);
529 EXPECT_EQ(mutatorView.platformViewContainer.superview, mutatorView.pathClipViews[0]);
530 EXPECT_EQ(mutatorView.pathClipViews[0].superview, mutatorView);
532 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
534 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(100, 50, 30, 20)));
535 EXPECT_EQ(mutatorView.pathClipViews.count, 0ull);
538 TEST(FlutterMutatorViewTest, HitTestIgnoreRegion) {
539 NSView* platformView = [[NSView alloc] init];
541 ApplyFlutterLayer(mutatorView, FlutterSize{100, 100}, {});
542 EXPECT_EQ([mutatorView hitTest:NSMakePoint(10, 10)], platformView);
543 EXPECT_EQ([mutatorView hitTest:NSMakePoint(50, 10)], platformView);
549 EXPECT_EQ([mutatorView hitTest:NSMakePoint(10, 10)], nil);
550 EXPECT_EQ([mutatorView hitTest:NSMakePoint(49, 10)], nil);
551 EXPECT_EQ([mutatorView hitTest:NSMakePoint(10, 49)], nil);
552 EXPECT_EQ([mutatorView hitTest:NSMakePoint(50, 50)], nil);
553 EXPECT_EQ([mutatorView hitTest:NSMakePoint(50, 10)], platformView);
554 EXPECT_EQ([mutatorView hitTest:NSMakePoint(10, 50)], platformView);
557 EXPECT_EQ([mutatorView hitTest:NSMakePoint(10, 10)], platformView);
558 EXPECT_EQ([mutatorView hitTest:NSMakePoint(49, 10)], platformView);
559 EXPECT_EQ([mutatorView hitTest:NSMakePoint(10, 49)], platformView);
560 EXPECT_EQ([mutatorView hitTest:NSMakePoint(50, 50)], platformView);
561 EXPECT_EQ([mutatorView hitTest:NSMakePoint(50, 10)], platformView);
562 EXPECT_EQ([mutatorView hitTest:NSMakePoint(10, 50)], platformView);
565 TEST(FlutterMutatorViewTest, ReparentingPlatformView) {
566 NSView* platformView = [[NSView alloc] init];
568 ApplyFlutterLayer(mutatorView, FlutterSize{100, 100}, {});
569 EXPECT_TRUE(platformView.superview == mutatorView.platformViewContainer);
570 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 100, 100)));
573 NSView* newParent = [[NSView alloc] init];
574 [newParent addSubview:platformView];
575 platformView.frame = CGRectMake(10, 10, 200, 200);
577 NSView* placeholderView = [[NSView alloc] init];
578 [mutatorView.platformViewContainer addSubview:placeholderView];
579 ApplyFlutterLayer(mutatorView, FlutterSize{100, 100}, {});
582 EXPECT_TRUE(platformView.superview == newParent);
583 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(10, 10, 200, 200)));
584 EXPECT_TRUE(CGRectEqualToRect(placeholderView.frame, CGRectMake(0, 0, 100, 100)));
592 - (void)testCoordinatorEventWithinFlutterContent {
593 id flutterView = OCMClassMock([
FlutterView class]);
597 id platformView = OCMClassMock([NSView
class]);
598 OCMStub([flutterView cursorUpdate:[OCMArg any]]);
600 OCMStub([mutatorView platformView]).andReturn(platformView);
601 CGPoint location = NSMakePoint(50, 50);
602 OCMStub([mutatorView convertPoint:location fromView:[OCMArg any]]).andReturn(location);
603 NSEvent*
event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved
615 OCMVerify([flutterView cursorUpdate:event]);
618 id platformView = OCMClassMock([NSView
class]);
620 OCMReject([flutterView cursorUpdate:[OCMArg any]]);
622 OCMStub([mutatorView platformView]).andReturn(platformView);
623 CGPoint location = NSMakePoint(50, 50);
624 OCMStub([mutatorView convertPoint:location fromView:[OCMArg any]]).andReturn(location);
625 NSEvent*
event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved
639 [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
643 - (void)testCoordinatorEventOutsideFlutterContent {
644 id flutterView = OCMClassMock([
FlutterView class]);
645 OCMReject([flutterView cursorUpdate:[OCMArg any]]);
648 id platformViewWindow = OCMClassMock([NSWindow
class]);
650 id platformView = OCMClassMock([NSView
class]);
651 OCMStub([platformViewWindow invalidateCursorRectsForView:platformView]);
652 OCMStub([platformView window]).andReturn(platformViewWindow);
653 OCMStub([flutterView cursorUpdate:[OCMArg any]]);
655 OCMStub([mutatorView platformView]).andReturn(platformView);
656 CGPoint location = NSMakePoint(150, 150);
657 OCMStub([mutatorView convertPoint:location fromView:[OCMArg any]]).andReturn(location);
658 NSEvent*
event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved
670 OCMVerify([platformViewWindow invalidateCursorRectsForView:platformView]);
674 OCMReject([platformViewWindow invalidateCursorRectsForView:[OCMArg any]]);
676 id platformView = OCMClassMock([NSView
class]);
677 OCMStub([platformViewWindow invalidateCursorRectsForView:platformView]);
678 OCMStub([platformView window]).andReturn(platformViewWindow);
679 OCMStub([flutterView cursorUpdate:[OCMArg any]]);
681 OCMStub([mutatorView platformView]).andReturn(platformView);
682 CGPoint location = NSMakePoint(150, 150);
683 OCMStub([mutatorView convertPoint:location fromView:[OCMArg any]]).andReturn(location);
684 NSEvent*
event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved
698 [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
702 platformViewWindow = OCMClassMock([NSWindow
class]);
704 id platformView = OCMClassMock([NSView
class]);
705 OCMStub([platformViewWindow invalidateCursorRectsForView:platformView]);
706 OCMStub([platformView window]).andReturn(platformViewWindow);
707 OCMStub([flutterView cursorUpdate:[OCMArg any]]);
709 OCMStub([mutatorView platformView]).andReturn(platformView);
710 CGPoint location = NSMakePoint(150, 150);
711 OCMStub([mutatorView convertPoint:location fromView:[OCMArg any]]).andReturn(location);
712 NSEvent*
event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved
724 OCMVerify([platformViewWindow invalidateCursorRectsForView:platformView]);
726 [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
730 TEST(FlutterMutatorViewTest, CursorCoordinator) {