123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- // Copyright 2013 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.
- #include "ios/chrome/common/string_util.h"
- #import <UIKit/UIKit.h>
- #include "base/ios/ns_range.h"
- #include "base/test/gtest_util.h"
- #import "ios/chrome/common/ui/colors/semantic_color_names.h"
- #import "ios/chrome/common/ui/util/text_view_util.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 {
- using StringUtilTest = PlatformTest;
- TEST_F(StringUtilTest, AttributedStringFromStringWithLink) {
- struct TestCase {
- NSString* input;
- NSDictionary* textAttributes;
- NSDictionary* linkAttributes;
- NSString* expectedString;
- NSRange expectedTextRange;
- NSRange expectedLinkRange;
- };
- const TestCase kAllTestCases[] = {
- TestCase{@"Text with valid BEGIN_LINK link END_LINK and spaces.", @{},
- @{NSLinkAttributeName : @"google.com"},
- @"Text with valid link and spaces.", NSRange{0, 16},
- NSRange{16, 4}},
- TestCase{
- @"Text with valid BEGIN_LINK link END_LINK and spaces.",
- @{NSForegroundColorAttributeName : [UIColor colorNamed:kBlueColor]},
- @{}, @"Text with valid link and spaces.", NSRange{0, 32},
- NSRange{0, 32}},
- TestCase{
- @"Text with valid BEGIN_LINK link END_LINK and spaces.",
- @{NSForegroundColorAttributeName : [UIColor colorNamed:kBlueColor]},
- @{NSLinkAttributeName : @"google.com"},
- @"Text with valid link and spaces.",
- NSRange{0, 16},
- NSRange{16, 4},
- },
- TestCase{
- @"Text with valid BEGIN_LINKlinkEND_LINK and no spaces.",
- @{NSForegroundColorAttributeName : [UIColor colorNamed:kBlueColor]},
- @{NSLinkAttributeName : @"google.com"},
- @"Text with valid link and no spaces.",
- NSRange{0, 16},
- NSRange{16, 4},
- },
- };
- for (const TestCase& test_case : kAllTestCases) {
- const NSAttributedString* result = AttributedStringFromStringWithLink(
- test_case.input, test_case.textAttributes, test_case.linkAttributes);
- EXPECT_NSEQ(result.string, test_case.expectedString);
- // Text at index 0 has text attributes applied until the link location.
- NSRange textRange;
- NSDictionary* resultTextAttributes = [result attributesAtIndex:0
- effectiveRange:&textRange];
- EXPECT_TRUE(NSEqualRanges(test_case.expectedTextRange, textRange));
- EXPECT_NSEQ(test_case.textAttributes, resultTextAttributes);
- // Text at index |expectedRange.location| has link attributes applied.
- NSRange linkRange;
- NSDictionary* resultLinkAttributes =
- [result attributesAtIndex:test_case.expectedLinkRange.location
- effectiveRange:&linkRange];
- EXPECT_TRUE(NSEqualRanges(test_case.expectedLinkRange, linkRange));
- NSMutableDictionary* combinedAttributes =
- [[NSMutableDictionary alloc] init];
- [combinedAttributes addEntriesFromDictionary:test_case.textAttributes];
- [combinedAttributes addEntriesFromDictionary:test_case.linkAttributes];
- EXPECT_NSEQ(combinedAttributes, resultLinkAttributes);
- }
- }
- TEST_F(StringUtilTest, AttributedStringFromStringWithLinkFailures) {
- struct TestCase {
- NSString* input;
- NSDictionary* textAttributes;
- NSDictionary* linkAttributes;
- };
- const TestCase kAllTestCases[] = {
- TestCase{
- @"Text without link.",
- @{NSForegroundColorAttributeName : [UIColor colorNamed:kBlueColor]},
- @{NSLinkAttributeName : @"google.com"},
- },
- TestCase{
- @"Text with BEGIN_LINK and no end link.",
- @{NSForegroundColorAttributeName : [UIColor colorNamed:kBlueColor]},
- @{NSLinkAttributeName : @"google.com"},
- },
- TestCase{
- @"Text with no begin link and END_LINK.",
- @{NSForegroundColorAttributeName : [UIColor colorNamed:kBlueColor]},
- @{NSLinkAttributeName : @"google.com"},
- },
- TestCase{
- @"Text with END_LINK before BEGIN_LINK.",
- @{NSForegroundColorAttributeName : [UIColor colorNamed:kBlueColor]},
- @{NSLinkAttributeName : @"google.com"},
- },
- };
- for (const TestCase& test_case : kAllTestCases) {
- EXPECT_CHECK_DEATH(AttributedStringFromStringWithLink(
- test_case.input, test_case.textAttributes, test_case.linkAttributes));
- }
- }
- TEST_F(StringUtilTest, AttributedStringFromStringWithLinkWithEmptyLink) {
- struct TestCase {
- NSString* input;
- NSDictionary* textAttributes;
- NSDictionary* linkAttributes;
- NSString* expectedString;
- };
- const TestCase test_case = TestCase {
- @"Text with empty link BEGIN_LINK END_LINK.",
- @{NSForegroundColorAttributeName : [UIColor colorNamed:kBlueColor]},
- @{NSLinkAttributeName : @"google.com"}, @"Text with empty link .",
- };
- const NSAttributedString* result = AttributedStringFromStringWithLink(
- test_case.input, test_case.textAttributes, test_case.linkAttributes);
- EXPECT_NSEQ(result.string, test_case.expectedString);
- // Text attributes apply to the full range of the result string.
- NSRange textRange;
- NSDictionary* resultTextAttributes = [result attributesAtIndex:0
- effectiveRange:&textRange];
- EXPECT_TRUE(NSEqualRanges(NSMakeRange(0, test_case.expectedString.length),
- textRange));
- EXPECT_NSEQ(test_case.textAttributes, resultTextAttributes);
- }
- TEST_F(StringUtilTest, ParseStringWithLinks) {
- struct TestCase {
- NSString* input;
- StringWithTags expected;
- };
- const TestCase kAllTestCases[] = {
- TestCase{
- @"Text without link.",
- StringWithTags{
- @"Text without link.",
- {},
- },
- },
- TestCase{
- @"Text with empty link BEGIN_LINK END_LINK.",
- StringWithTags{
- @"Text with empty link .",
- {NSRange{21, 0}},
- },
- },
- TestCase{
- @"Text with BEGIN_LINK and no end link.",
- StringWithTags{
- @"Text with BEGIN_LINK and no end link.",
- {},
- },
- },
- TestCase{
- @"Text with no begin link and END_LINK.",
- StringWithTags{
- @"Text with no begin link and END_LINK.",
- {},
- },
- },
- TestCase{@"Text with END_LINK before BEGIN_LINK.",
- StringWithTags{
- @"Text with END_LINK before BEGIN_LINK.",
- {},
- }},
- TestCase{
- @"Text with valid BEGIN_LINK link END_LINK and spaces.",
- StringWithTags{
- @"Text with valid link and spaces.",
- {NSRange{16, 4}},
- },
- },
- TestCase{
- @"Text with valid BEGIN_LINKlinkEND_LINK and no spaces.",
- StringWithTags{
- @"Text with valid link and no spaces.",
- {NSRange{16, 4}},
- },
- },
- TestCase{
- @"Text with multiple tags: BEGIN_LINKlink1END_LINK, "
- @"BEGIN_LINKlink2END_LINK and BEGIN_LINKlink3END_LINK.",
- StringWithTags{
- @"Text with multiple tags: link1, link2 and link3.",
- {NSRange{25, 5}, NSRange{32, 5}, NSRange{42, 5}},
- },
- },
- };
- for (const TestCase& test_case : kAllTestCases) {
- const StringWithTags result = ParseStringWithLinks(test_case.input);
- EXPECT_NSEQ(result.string, test_case.expected.string);
- ASSERT_EQ(result.ranges.size(), test_case.expected.ranges.size());
- for (size_t i = 0; i < test_case.expected.ranges.size(); i++) {
- EXPECT_EQ(result.ranges[i], test_case.expected.ranges[i]);
- }
- }
- }
- TEST_F(StringUtilTest, ParseStringWithTag) {
- struct TestCase {
- NSString* input;
- StringWithTag expected;
- };
- const TestCase kAllTestCases[] = {
- TestCase{
- @"Text without tag.",
- StringWithTag{
- @"Text without tag.",
- NSRange{NSNotFound, 0},
- },
- },
- TestCase{
- @"Text with empty tag BEGIN_TAG END_TAG.",
- StringWithTag{
- @"Text with empty tag .",
- NSRange{20, 1},
- },
- },
- TestCase{
- @"Text with BEGIN_TAG and no end tag.",
- StringWithTag{
- @"Text with BEGIN_TAG and no end tag.",
- NSRange{NSNotFound, 0},
- },
- },
- TestCase{
- @"Text with no begin tag and END_TAG.",
- StringWithTag{
- @"Text with no begin tag and END_TAG.",
- NSRange{NSNotFound, 0},
- },
- },
- TestCase{@"Text with END_TAG before BEGIN_TAG.",
- StringWithTag{
- @"Text with END_TAG before BEGIN_TAG.",
- NSRange{NSNotFound, 0},
- }},
- TestCase{
- @"Text with valid BEGIN_TAG tag END_TAG and spaces.",
- StringWithTag{
- @"Text with valid tag and spaces.",
- NSRange{16, 5},
- },
- },
- TestCase{
- @"Text with valid BEGIN_TAGtagEND_TAG and no spaces.",
- StringWithTag{
- @"Text with valid tag and no spaces.",
- NSRange{16, 3},
- },
- },
- };
- for (const TestCase& test_case : kAllTestCases) {
- const StringWithTag result =
- ParseStringWithTag(test_case.input, @"BEGIN_TAG", @"END_TAG");
- EXPECT_NSEQ(result.string, test_case.expected.string);
- EXPECT_EQ(result.range, test_case.expected.range);
- }
- }
- TEST_F(StringUtilTest, ParseStringWithTags) {
- struct TestCase {
- NSString* input;
- StringWithTags expected;
- };
- const TestCase kAllTestCases[] = {
- TestCase{
- @"Text without tag.",
- StringWithTags{
- @"Text without tag.",
- {},
- },
- },
- TestCase{
- @"Text with empty tag BEGIN_TAG END_TAG.",
- StringWithTags{
- @"Text with empty tag .",
- {NSRange{20, 1}},
- },
- },
- TestCase{
- @"Text with BEGIN_TAG and no end tag.",
- StringWithTags{
- @"Text with BEGIN_TAG and no end tag.",
- {},
- },
- },
- TestCase{
- @"Text with no begin tag and END_TAG.",
- StringWithTags{
- @"Text with no begin tag and END_TAG.",
- {},
- },
- },
- TestCase{@"Text with END_TAG before BEGIN_TAG.",
- StringWithTags{
- @"Text with END_TAG before BEGIN_TAG.",
- {},
- }},
- TestCase{
- @"Text with valid BEGIN_TAG tag END_TAG and spaces.",
- StringWithTags{
- @"Text with valid tag and spaces.",
- {NSRange{16, 5}},
- },
- },
- TestCase{
- @"Text with valid BEGIN_TAGtagEND_TAG and no spaces.",
- StringWithTags{
- @"Text with valid tag and no spaces.",
- {NSRange{16, 3}},
- },
- },
- TestCase{
- @"Text with multiple tags: BEGIN_TAGtag1END_TAG, "
- @"BEGIN_TAGtag2END_TAG and BEGIN_TAGtag3END_TAG.",
- StringWithTags{
- @"Text with multiple tags: tag1, tag2 and tag3.",
- {NSRange{25, 4}, NSRange{31, 4}, NSRange{40, 4}},
- },
- },
- };
- for (const TestCase& test_case : kAllTestCases) {
- const StringWithTags result =
- ParseStringWithTags(test_case.input, @"BEGIN_TAG", @"END_TAG");
- EXPECT_NSEQ(result.string, test_case.expected.string);
- ASSERT_EQ(result.ranges.size(), test_case.expected.ranges.size());
- for (size_t i = 0; i < test_case.expected.ranges.size(); i++) {
- EXPECT_EQ(result.ranges[i], test_case.expected.ranges[i]);
- }
- }
- }
- TEST_F(StringUtilTest, CleanNSStringForDisplay) {
- NSArray* const all_test_data = @[
- @{
- @"input" : @"Clean String",
- @"remove_graphic_chars" : @NO,
- @"expected" : @"Clean String"
- },
- @{
- @"input" : @" \t String with leading and trailing whitespaces ",
- @"remove_graphic_chars" : @NO,
- @"expected" : @"String with leading and trailing whitespaces"
- },
- @{
- @"input" : @" \n\n\r String with \n\n\r\n\r newline characters \n\n\r",
- @"remove_graphic_chars" : @NO,
- @"expected" : @"String with newline characters"
- },
- @{
- @"input" : @"String with an arrow ⟰ that remains intact",
- @"remove_graphic_chars" : @NO,
- @"expected" : @"String with an arrow ⟰ that remains intact"
- },
- @{
- @"input" : @"String with an arrow ⟰ that gets cleaned up",
- @"remove_graphic_chars" : @YES,
- @"expected" : @"String with an arrow that gets cleaned up"
- },
- ];
- for (NSDictionary* test_data : all_test_data) {
- NSString* input_text = test_data[@"input"];
- NSString* expected_text = test_data[@"expected"];
- BOOL remove_graphic_chars = [test_data[@"remove_graphic_chars"] boolValue];
- EXPECT_NSEQ(expected_text,
- CleanNSStringForDisplay(input_text, remove_graphic_chars));
- }
- }
- TEST_F(StringUtilTest, SubstringOfWidth) {
- // returns nil for zero-length strings
- EXPECT_NSEQ(SubstringOfWidth(@"", @{}, 100, NO), nil);
- EXPECT_NSEQ(SubstringOfWidth(nil, @{}, 100, NO), nil);
- // This font should always exist
- UIFont* sys_font = [UIFont systemFontOfSize:18.0f];
- NSDictionary* attributes = @{NSFontAttributeName : sys_font};
- EXPECT_NSEQ(SubstringOfWidth(@"asdf", attributes, 100, NO), @"asdf");
- // construct some string of known lengths
- NSString* leading = @"some text";
- NSString* trailing = @"some other text";
- NSString* mid = @"some text for the method to do some actual work";
- NSString* long_string =
- [[leading stringByAppendingString:mid] stringByAppendingString:trailing];
- CGFloat leading_width = [leading sizeWithAttributes:attributes].width;
- CGFloat trailing_width = [trailing sizeWithAttributes:attributes].width;
- NSString* leading_calculated =
- SubstringOfWidth(long_string, attributes, leading_width, NO);
- EXPECT_NSEQ(leading, leading_calculated);
- NSString* trailing_calculated =
- SubstringOfWidth(long_string, attributes, trailing_width, YES);
- EXPECT_NSEQ(trailing, trailing_calculated);
- }
- // Verifies when it should return CGRectNull and when it shouldn't.
- TEST_F(StringUtilTest, TextViewLinkBound) {
- UITextView* text_view = CreateUITextViewWithTextKit1();
- text_view.text = @"Some text.";
- // Returns CGRectNull for empty NSRange.
- EXPECT_TRUE(CGRectEqualToRect(
- TextViewLinkBound(text_view, NSMakeRange(-1, -1)), CGRectNull));
- // Returns CGRectNull for a range out of bound.
- EXPECT_TRUE(CGRectEqualToRect(
- TextViewLinkBound(text_view, NSMakeRange(20, 5)), CGRectNull));
- // Returns a CGRect diffent from CGRectNull when there is a range in bound.
- EXPECT_FALSE(CGRectEqualToRect(
- TextViewLinkBound(text_view, NSMakeRange(0, 5)), CGRectNull));
- }
- } // namespace
|