blob: 319450a9b4789517fc12377fdad257c0ad6e4f1a [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <UIKit/UIKit.h>
#include "base/mac/foundation_util.h"
#include "ios/chrome/browser/ui/util/text_region_mapper.h"
#import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#include "testing/platform_test.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
class TextRegionMapperTest : public PlatformTest {
protected:
void InitMapper(NSAttributedString* string, CGRect bounds) {
_string = [string copy];
_textMapper =
[[CoreTextRegionMapper alloc] initWithAttributedString:_string
bounds:bounds];
}
CGRect RectAtIndex(NSArray* array, NSUInteger index) {
NSValue* value = base::mac::ObjCCastStrict<NSValue>(array[index]);
return [value CGRectValue];
}
NSDictionary* AttributesForTextAlignment(NSTextAlignment alignment) {
NSMutableParagraphStyle* style =
[[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
[style setAlignment:alignment];
return @{NSParagraphStyleAttributeName : style};
}
NSAttributedString* _string;
CoreTextRegionMapper* _textMapper;
};
}
TEST_F(TextRegionMapperTest, SimpleTextTest) {
CGRect bounds = CGRectMake(0, 0, 500, 100);
NSString* string = @"Simple Test";
InitMapper([[NSAttributedString alloc] initWithString:string], bounds);
// Simple case: a single word in a string in a large bounding rect.
// Rect bounding word should be inside bounding rect, and should be
// wider than it is tall.
NSRange range = NSMakeRange(0, 6); // "Simple".
NSArray* rects = [_textMapper rectsForRange:range];
ASSERT_EQ(1UL, [rects count]);
CGRect rect = RectAtIndex(rects, 0);
EXPECT_TRUE(CGRectContainsRect(bounds, rect));
EXPECT_GT(CGRectGetWidth(rect), CGRectGetHeight(rect));
// Range that ends at end of string still generates a rect.
range = NSMakeRange(9, 2);
rects = [_textMapper rectsForRange:range];
ASSERT_EQ(1UL, [rects count]);
rect = RectAtIndex(rects, 0);
EXPECT_TRUE(CGRectContainsRect(bounds, rect));
// Simple null cases: range of zero length, range outside of string.
range = NSMakeRange(6, 0);
rects = [_textMapper rectsForRange:range];
EXPECT_EQ(0UL, [rects count]) << "Range has length 0.";
range = NSMakeRange([_string length] + 1, 5);
rects = [_textMapper rectsForRange:range];
EXPECT_EQ(0UL, [rects count]) << "Range starts beyond string.";
range = NSMakeRange([_string length] - 2, 3);
rects = [_textMapper rectsForRange:range];
EXPECT_EQ(0UL, [rects count]) << "Range ends beyond string.";
}
TEST_F(TextRegionMapperTest, TextAlignmentTest) {
CGRect bounds = CGRectMake(0, 0, 500, 100);
NSAttributedString* string;
string = [[NSAttributedString alloc]
initWithString:@"Simple Test"
attributes:AttributesForTextAlignment(NSTextAlignmentLeft)];
InitMapper(string, bounds);
// First word of left-aligned string should at the left edge of the bounds.
NSRange range = NSMakeRange(0, 6); // "Simple".
NSArray* rects = [_textMapper rectsForRange:range];
ASSERT_EQ(1UL, [rects count]);
CGRect rect = RectAtIndex(rects, 0);
EXPECT_TRUE(CGRectContainsRect(bounds, rect));
EXPECT_GT(CGRectGetWidth(rect), CGRectGetHeight(rect));
EXPECT_EQ(CGRectGetMinX(rect), CGRectGetMinX(bounds));
string = [[NSAttributedString alloc]
initWithString:@"Simple Test"
attributes:AttributesForTextAlignment(NSTextAlignmentRight)];
InitMapper(string, bounds);
// Last word of right-aligned string should at the right edge of the bounds.
range = NSMakeRange(6, 5); // "Test".
rects = [_textMapper rectsForRange:range];
ASSERT_EQ(1UL, [rects count]);
rect = RectAtIndex(rects, 0);
EXPECT_TRUE(CGRectContainsRect(bounds, rect));
EXPECT_GT(CGRectGetWidth(rect), CGRectGetHeight(rect));
EXPECT_EQ(CGRectGetMaxX(rect), CGRectGetMaxX(bounds));
}
TEST_F(TextRegionMapperTest, CJKTest) {
CGRect bounds = CGRectMake(0, 0, 345, 65);
// clang-format off
NSString* CJKString =
@"“触摸搜索”会将所选字词和当前页面(作为上下文)一起发送给 Google 搜索。"
@"您可以在设置中停用此功能。";
// clang-format on
NSMutableDictionary* attributes = [NSMutableDictionary
dictionaryWithDictionary:AttributesForTextAlignment(NSTextAlignmentLeft)];
attributes[NSFontAttributeName] =
[[MDFRobotoFontLoader sharedInstance] regularFontOfSize:16];
NSAttributedString* string =
[[NSAttributedString alloc] initWithString:CJKString
attributes:attributes];
InitMapper(string, bounds);
NSRange range = NSMakeRange(0, 6); // "“触摸搜索”".
NSArray* rects = [_textMapper rectsForRange:range];
ASSERT_EQ(1UL, [rects count]);
}
/*
Further unit tests should cover additional cases:
- In several languages.
- Range split across line break == two rects
- Bidi text with text range across scripts gets two rects.
- Various string attributions.
- Isolate buggy CoreText case.
*/