[go: nahoru, domu]

blob: c119a16401adc8f7e9d22f010f3c19fe01804be0 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/shared/ui/util/util_swift.h"
#import <UIKit/UIKit.h>
#import "base/apple/foundation_util.h"
#import "base/test/ios/wait_util.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/platform_test.h"
using base::test::ios::kWaitForUIElementTimeout;
using base::test::ios::WaitUntilConditionOrTimeout;
// Sets up layout guide center.
class LayoutGuideCenterTest : public PlatformTest {
protected:
LayoutGuideCenterTest() : center_([[LayoutGuideCenter alloc] init]) {}
LayoutGuideCenter* center_;
};
// Checks that the correct view is referenced.
TEST_F(LayoutGuideCenterTest, TestReferenced) {
UIView* reference_view = [[UIView alloc] init];
[center_ referenceView:reference_view underName:@"view"];
EXPECT_EQ([center_ referencedViewUnderName:@"view"], reference_view);
}
// Checks that the view is no longer referenced.
TEST_F(LayoutGuideCenterTest, TestDereferenced) {
UIView* reference_view = [[UIView alloc] init];
[center_ referenceView:reference_view underName:@"view"];
[center_ referenceView:nil underName:@"view"];
EXPECT_EQ([center_ referencedViewUnderName:@"view"], nil);
}
// Checks that a tracking layout guide is correctly updated to match the
// reference view's frame.
TEST_F(LayoutGuideCenterTest, LayoutGuideMatchesReferenceView) {
CGRect rect = CGRectMake(10, 20, 30, 40);
UIView* reference_view = [[UIView alloc] initWithFrame:rect];
[center_ referenceView:reference_view underName:@"view"];
// Set up the tracking layout guide.
UILayoutGuide* layout_guide = [center_ makeLayoutGuideNamed:@"view"];
UIView* view = [[UIView alloc] init];
[view addLayoutGuide:layout_guide];
UIWindow* window = [[UIWindow alloc] init];
[window addSubview:reference_view];
[window addSubview:view];
EXPECT_TRUE(CGRectEqualToRect(layout_guide.layoutFrame, rect));
EXPECT_EQ([center_ referencedViewUnderName:@"view"], reference_view);
}
// Checks that a tracking layout guide is correctly updated to track the
// reference view's frame.
TEST_F(LayoutGuideCenterTest, LayoutGuideTracksReferenceView) {
UIView* reference_view = [[UIView alloc] init];
[center_ referenceView:reference_view underName:@"view"];
// Set up the tracking layout guide.
UILayoutGuide* layout_guide = [center_ makeLayoutGuideNamed:@"view"];
UIView* view = [[UIView alloc] init];
[view addLayoutGuide:layout_guide];
UIWindow* window = [[UIWindow alloc] init];
[window addSubview:reference_view];
[window addSubview:view];
EXPECT_TRUE(CGRectEqualToRect(layout_guide.layoutFrame, CGRectZero));
for (NSValue* rectValue in @[
@(CGRectMake(0, -10, 20, 30)), @(CGRectMake(3, 14, 15, 93)),
@(CGRectZero)
]) {
CGRect rect = rectValue.CGRectValue;
reference_view.frame = rect;
[window setNeedsLayout];
[window layoutIfNeeded];
// Wait until the frame is updated.
EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^{
return CGRectEqualToRect(layout_guide.layoutFrame, rect);
}));
}
}
// Checks that a tracking layout guide is correctly updated to match the
// reference view's frame in a different window.
TEST_F(LayoutGuideCenterTest,
LayoutGuideMatchesReferenceViewInDifferentWindow) {
CGRect rect = CGRectMake(10, 20, 30, 40);
UIView* reference_view = [[UIView alloc] initWithFrame:rect];
[center_ referenceView:reference_view underName:@"view"];
// Set up the tracking layout guide.
UILayoutGuide* layout_guide = [center_ makeLayoutGuideNamed:@"view"];
UIView* view = [[UIView alloc] init];
[view addLayoutGuide:layout_guide];
// Set up windows in the same scene.
UIWindowScene* scene = base::apple::ObjCCastStrict<UIWindowScene>(
[UIApplication.sharedApplication.connectedScenes anyObject]);
UIWindow* reference_window = [[UIWindow alloc] init];
reference_window.windowScene = scene;
UIWindow* window = [[UIWindow alloc] init];
window.windowScene = scene;
[reference_window addSubview:reference_view];
[window addSubview:view];
EXPECT_TRUE(CGRectEqualToRect(layout_guide.layoutFrame, rect));
}
// Checks that LayoutGuideCenter only keeps weak references to referenced views.
TEST_F(LayoutGuideCenterTest, WeakReferenceView) {
UIView* reference_view = [[UIView alloc] init];
// Create a weak reference to the view, to check it gets nilled out.
__weak UIView* weak_reference_view = reference_view;
@autoreleasepool {
[center_ referenceView:reference_view underName:@"view"];
reference_view = nil;
}
// The center should not have kept the view around.
EXPECT_EQ(weak_reference_view, nil);
}
// Checks that LayoutGuideCenter only keeps weak references to layout guides.
// Warning: this test may fail while debugging, because of intricacies of
// NSHashTable. See `HashTableWeakReference` below for more context.
TEST_F(LayoutGuideCenterTest, WeakLayoutGuide) {
UILayoutGuide* layout_guide = [center_ makeLayoutGuideNamed:@"view"];
EXPECT_NE(layout_guide, nil);
// Create a weak reference to the layout guide, to check it gets nilled out.
__weak UILayoutGuide* weak_layout_guide = layout_guide;
@autoreleasepool {
layout_guide = nil;
}
// This can fail if you are breaking in the debugger and inspecting the hash
// table. This is due to NSHashTable not providing any guarantee as for when
// the elements are released.
EXPECT_EQ(weak_layout_guide, nil);
}
// Checks that NSHashTable references its content weakly.
// Warning: NSHashTable's behavior is not really deterministic (from a client
// perspective), in that it decides when it's removing the weak references. When
// breaking in the debugger in this test and poking around the hash table, the
// test fails, for example.
TEST_F(LayoutGuideCenterTest, HashTableWeakReference) {
UILayoutGuide* layout_guide = [[UILayoutGuide alloc] init];
__weak UILayoutGuide* weak_layout_guide = layout_guide;
NSHashTable<UILayoutGuide*>* hash_table = [NSHashTable weakObjectsHashTable];
[hash_table addObject:layout_guide];
@autoreleasepool {
layout_guide = nil;
}
// This can fail if you are breaking in the debugger and inspecting the hash
// table. This is due to NSHashTable not providing any guarantee as for when
// the elements are released.
EXPECT_EQ(weak_layout_guide, nil);
}
// Checks that if `referenceView:underName:` is called twice with the same
// arguments, there are no changes
TEST_F(LayoutGuideCenterTest, TestReferenceViewNoChangesIfSameView) {
CGRect rect = CGRectMake(10, 20, 30, 40);
UIView* reference_view = [[UIView alloc] initWithFrame:rect];
[center_ referenceView:reference_view underName:@"view"];
// Override reference_view's cr_onWindowCoordinatesChanged to later verify
// that it hasn't changed.
__block BOOL windowCoordinatesChangedCalled = NO;
reference_view.cr_onWindowCoordinatesChanged = ^(UIView* view) {
windowCoordinatesChangedCalled = YES;
};
UIView* view = [[UIView alloc] init];
reference_view.cr_onWindowCoordinatesChanged(view);
EXPECT_TRUE(windowCoordinatesChangedCalled);
windowCoordinatesChangedCalled = NO;
// Re-reference reference_view. This should not change the view's
// cr_onWindowCoordinatesChanged block.
[center_ referenceView:reference_view underName:@"view"];
reference_view.cr_onWindowCoordinatesChanged(view);
EXPECT_TRUE(windowCoordinatesChangedCalled);
}