LinkifyCompatTest.java revision 4bb57b2c7c5e8fa607119bd5089535d5862cb979
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.support.v4.text.util; 18 19import static org.junit.Assert.assertEquals; 20import static org.junit.Assert.assertFalse; 21import static org.junit.Assert.assertNotEquals; 22import static org.junit.Assert.assertTrue; 23import static org.junit.Assert.fail; 24 25import android.support.test.filters.SmallTest; 26import android.support.test.runner.AndroidJUnit4; 27import android.support.v4.util.PatternsCompat; 28import android.text.Spannable; 29import android.text.SpannableString; 30import android.text.style.URLSpan; 31import android.text.util.Linkify; 32import android.text.util.Linkify.MatchFilter; 33import android.text.util.Linkify.TransformFilter; 34 35import org.junit.Test; 36import org.junit.runner.RunWith; 37 38import java.util.Locale; 39import java.util.regex.Matcher; 40import java.util.regex.Pattern; 41 42/** 43 * Test {@link LinkifyCompat}. 44 */ 45@SmallTest 46@RunWith(AndroidJUnit4.class) 47public class LinkifyCompatTest { 48 private static final Pattern LINKIFY_TEST_PATTERN = Pattern.compile( 49 "(test:)?[a-zA-Z0-9]+(\\.pattern)?"); 50 51 private MatchFilter mMatchFilterStartWithDot = new MatchFilter() { 52 public final boolean acceptMatch(final CharSequence s, final int start, final int end) { 53 if (start == 0) { 54 return true; 55 } 56 57 if (s.charAt(start - 1) == '.') { 58 return false; 59 } 60 61 return true; 62 } 63 }; 64 65 private TransformFilter mTransformFilterUpperChar = new TransformFilter() { 66 public final String transformUrl(final Matcher match, String url) { 67 StringBuilder buffer = new StringBuilder(); 68 String matchingRegion = match.group(); 69 70 for (int i = 0, size = matchingRegion.length(); i < size; i++) { 71 char character = matchingRegion.charAt(i); 72 73 if (character == '.' || Character.isLowerCase(character) 74 || Character.isDigit(character)) { 75 buffer.append(character); 76 } 77 } 78 return buffer.toString(); 79 } 80 }; 81 82 @Test 83 public void testAddLinksToSpannable() { 84 // Verify URLs including the ones that have new gTLDs, and the 85 // ones that look like gTLDs (and so are accepted by linkify) 86 // and the ones that should not be linkified due to non-compliant 87 // gTLDs 88 final String longGTLD = 89 "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabc"; 90 SpannableString spannable = new SpannableString("name@gmail.com, " 91 + "www.google.com, http://www.google.com/language_tools?hl=en, " 92 + "a.bd, " // a URL with accepted TLD so should be linkified 93 + "d.e, f.1, g.12, " // not valid, so should not be linkified 94 + "http://h." + longGTLD + " " // valid, should be linkified 95 + "j." + longGTLD + "a"); // not a valid URL (gtld too long), no linkify 96 97 assertTrue(LinkifyCompat.addLinks(spannable, Linkify.WEB_URLS)); 98 URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class); 99 assertEquals(4, spans.length); 100 assertEquals("http://www.google.com", spans[0].getURL()); 101 assertEquals("http://www.google.com/language_tools?hl=en", spans[1].getURL()); 102 assertEquals("http://a.bd", spans[2].getURL()); 103 assertEquals("http://h." + longGTLD, spans[3].getURL()); 104 105 assertTrue(LinkifyCompat.addLinks(spannable, Linkify.EMAIL_ADDRESSES)); 106 spans = spannable.getSpans(0, spannable.length(), URLSpan.class); 107 assertEquals(1, spans.length); 108 assertEquals("mailto:name@gmail.com", spans[0].getURL()); 109 110 try { 111 LinkifyCompat.addLinks((Spannable) null, Linkify.WEB_URLS); 112 fail("Should throw NullPointerException!"); 113 } catch (NullPointerException e) { 114 // expect 115 } 116 117 assertFalse(LinkifyCompat.addLinks((Spannable) null, 0)); 118 } 119 120 @Test 121 public void testAddLinksToSpannableWithScheme() { 122 String text = "google.pattern, test:AZ0101.pattern"; 123 124 SpannableString spannable = new SpannableString(text); 125 LinkifyCompat.addLinks(spannable, LINKIFY_TEST_PATTERN, "Test:"); 126 URLSpan[] spans = (spannable.getSpans(0, spannable.length(), URLSpan.class)); 127 assertEquals(2, spans.length); 128 assertEquals("test:google.pattern", spans[0].getURL()); 129 assertEquals("test:AZ0101.pattern", spans[1].getURL()); 130 131 try { 132 LinkifyCompat.addLinks((Spannable) null, LINKIFY_TEST_PATTERN, "Test:"); 133 fail("Should throw NullPointerException!"); 134 } catch (NullPointerException e) { 135 } 136 137 try { 138 LinkifyCompat.addLinks(spannable, null, "Test:"); 139 fail("Should throw NullPointerException!"); 140 } catch (NullPointerException e) { 141 } 142 143 spannable = new SpannableString(text); 144 LinkifyCompat.addLinks(spannable, LINKIFY_TEST_PATTERN, null); 145 spans = (spannable.getSpans(0, spannable.length(), URLSpan.class)); 146 assertEquals(2, spans.length); 147 assertEquals("google.pattern", spans[0].getURL()); 148 assertEquals("test:AZ0101.pattern", spans[1].getURL()); 149 } 150 151 @Test 152 public void testAddLinks3() { 153 String text = "FilterUpperCase.pattern, 12.345.pattern"; 154 155 SpannableString spannable = new SpannableString(text); 156 LinkifyCompat.addLinks(spannable, LINKIFY_TEST_PATTERN, "Test:", 157 mMatchFilterStartWithDot, mTransformFilterUpperChar); 158 URLSpan[] spans = (spannable.getSpans(0, spannable.length(), URLSpan.class)); 159 assertEquals(2, spans.length); 160 assertEquals("test:ilterpperase.pattern", spans[0].getURL()); 161 assertEquals("test:12", spans[1].getURL()); 162 163 try { 164 LinkifyCompat.addLinks((Spannable)null, LINKIFY_TEST_PATTERN, "Test:", 165 mMatchFilterStartWithDot, mTransformFilterUpperChar); 166 fail("Should throw NullPointerException!"); 167 } catch (NullPointerException e) { 168 // expect 169 } 170 171 try { 172 LinkifyCompat.addLinks(spannable, null, "Test:", mMatchFilterStartWithDot, 173 mTransformFilterUpperChar); 174 fail("Should throw NullPointerException!"); 175 } catch (NullPointerException e) { 176 // expect 177 } 178 179 spannable = new SpannableString(text); 180 LinkifyCompat.addLinks(spannable, LINKIFY_TEST_PATTERN, null, mMatchFilterStartWithDot, 181 mTransformFilterUpperChar); 182 spans = (spannable.getSpans(0, spannable.length(), URLSpan.class)); 183 assertEquals(2, spans.length); 184 assertEquals("ilterpperase.pattern", spans[0].getURL()); 185 assertEquals("12", spans[1].getURL()); 186 187 spannable = new SpannableString(text); 188 LinkifyCompat.addLinks(spannable, LINKIFY_TEST_PATTERN, "Test:", null, mTransformFilterUpperChar); 189 spans = (spannable.getSpans(0, spannable.length(), URLSpan.class)); 190 assertEquals(3, spans.length); 191 assertEquals("test:ilterpperase.pattern", spans[0].getURL()); 192 assertEquals("test:12", spans[1].getURL()); 193 assertEquals("test:345.pattern", spans[2].getURL()); 194 195 spannable = new SpannableString(text); 196 LinkifyCompat.addLinks(spannable, LINKIFY_TEST_PATTERN, "Test:", mMatchFilterStartWithDot, null); 197 spans = (spannable.getSpans(0, spannable.length(), URLSpan.class)); 198 assertEquals(2, spans.length); 199 assertEquals("test:FilterUpperCase.pattern", spans[0].getURL()); 200 assertEquals("test:12", spans[1].getURL()); 201 } 202 203 @Test 204 public void testAddLinksPhoneNumbers() { 205 String numbersInvalid = "123456789 not a phone number"; 206 String numbersUKLocal = "tel:(0812)1234560 (0812)1234561"; 207 String numbersUSLocal = "tel:(812)1234562 (812)123.4563 " 208 + " tel:(800)5551210 (800)555-1211 555-1212"; 209 String numbersIntl = "tel:+4408121234564 +44-0812-123-4565" 210 + " tel:+18005551213 +1-800-555-1214"; 211 SpannableString spannable = new SpannableString( 212 numbersInvalid 213 + " " + numbersUKLocal 214 + " " + numbersUSLocal 215 + " " + numbersIntl); 216 217 // phonenumber linkify is locale-dependent 218 if (Locale.US.equals(Locale.getDefault())) { 219 assertTrue(LinkifyCompat.addLinks(spannable, Linkify.PHONE_NUMBERS)); 220 URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class); 221 // We cannot assert the contents of the spans as support library falls back to the 222 // framework libphonenumber which behaves differently for different API levels. 223 assertNotEquals("There should be more than zero phone number spans.", 0, spans.length); 224 } 225 226 try { 227 LinkifyCompat.addLinks((Spannable) null, Linkify.WEB_URLS); 228 fail("Should throw NullPointerException!"); 229 } catch (NullPointerException e) { 230 // expect 231 } 232 233 assertFalse(LinkifyCompat.addLinks((Spannable) null, 0)); 234 } 235 236 @Test 237 public void testAddLinks_spanOverlapPruning() { 238 SpannableString spannable = new SpannableString("800-555-1211@gmail.com 800-555-1222.com" 239 + " phone800-555-1233"); 240 241 // phonenumber linkify is locale-dependent 242 if (Locale.US.equals(Locale.getDefault())) { 243 assertTrue(LinkifyCompat.addLinks(spannable, Linkify.ALL)); 244 URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class); 245 assertEquals(3, spans.length); 246 assertEquals("tel:8005551233", spans[0].getURL()); 247 assertEquals("mailto:800-555-1211@gmail.com", spans[1].getURL()); 248 assertEquals("http://800-555-1222.com", spans[2].getURL()); 249 } 250 } 251 252 @Test 253 public void testAddLinks_addsLinksWhenDefaultSchemeIsNull() { 254 Spannable spannable = new SpannableString("any https://android.com any android.com any"); 255 LinkifyCompat.addLinks(spannable, PatternsCompat.AUTOLINK_WEB_URL, null, null, null); 256 257 URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class); 258 assertEquals("android.com and https://android.com should be linkified", 2, spans.length); 259 assertEquals("https://android.com", spans[0].getURL()); 260 assertEquals("android.com", spans[1].getURL()); 261 } 262 263 @Test 264 public void testAddLinks_addsLinksWhenSchemesArrayIsNull() { 265 Spannable spannable = new SpannableString("any https://android.com any android.com any"); 266 LinkifyCompat.addLinks(spannable, PatternsCompat.AUTOLINK_WEB_URL, "http://", null, null); 267 268 URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class); 269 assertEquals("android.com and https://android.com should be linkified", 2, spans.length); 270 // expected behavior, passing null schemes array means: prepend defaultScheme to all links. 271 assertEquals("http://https://android.com", spans[0].getURL()); 272 assertEquals("http://android.com", spans[1].getURL()); 273 } 274 275 @Test 276 public void testAddLinks_prependsDefaultSchemeToBeginingOfLink() { 277 Spannable spannable = new SpannableString("any android.com any"); 278 LinkifyCompat.addLinks(spannable, PatternsCompat.AUTOLINK_WEB_URL, "http://", 279 new String[] { "http://", "https://"}, null, null); 280 281 URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class); 282 assertEquals("android.com should be linkified", 1, spans.length); 283 assertEquals("http://android.com", spans[0].getURL()); 284 } 285 286 @Test 287 public void testAddLinks_doesNotPrependSchemeIfSchemeExists() { 288 Spannable spannable = new SpannableString("any https://android.com any"); 289 LinkifyCompat.addLinks(spannable, PatternsCompat.AUTOLINK_WEB_URL, "http://", 290 new String[] { "http://", "https://"}, null, null); 291 292 URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class); 293 assertEquals("android.com should be linkified", 1, spans.length); 294 assertEquals("https://android.com", spans[0].getURL()); 295 } 296 297 // WEB_URLS Related Tests 298 299 @Test 300 public void testAddLinks_doesNotAddLinksForUrlWithoutProtocolAndWithoutKnownTld() { 301 Spannable spannable = new SpannableString("hey man.its me"); 302 boolean linksAdded = LinkifyCompat.addLinks(spannable, Linkify.ALL); 303 assertFalse("Should not add link with unknown TLD", linksAdded); 304 } 305 306 @Test 307 public void testAddLinks_shouldNotAddEmailAddressAsUrl() { 308 String url = "name@gmail.com"; 309 verifyAddLinksWithWebUrlFails("Should not recognize email address as URL", url); 310 } 311 312 @Test 313 public void testAddLinks_acceptsUrlsWithCommasInRequestParameterValues() { 314 String url = "https://android.com/path?ll=37.4221,-122.0836&z=17&pll=37.4221,-122.0836"; 315 verifyAddLinksWithWebUrlSucceeds("Should accept commas", url); 316 } 317 318 @Test 319 public void testAddLinks_addsLinksForUrlWithProtocolWithoutTld() { 320 String url = "http://android/#notld///a/n/d/r/o/i/d&p1=1&p2=2"; 321 verifyAddLinksWithWebUrlSucceeds("Should accept URL starting with protocol but does not" 322 + " have TLD", url); 323 } 324 325 @Test 326 public void testAddLinks_matchesProtocolCaseInsensitive() { 327 String url = "hTtP://android.com"; 328 verifyAddLinksWithWebUrlSucceeds("Protocol matching should be case insensitive", url); 329 } 330 331 @Test 332 public void testAddLinks_matchesValidUrlWithSchemeAndHostname() { 333 String url = "http://www.android.com"; 334 verifyAddLinksWithWebUrlSucceeds("Should match valid URL with scheme and hostname", url); 335 } 336 337 @Test 338 public void testAddLinks_matchesValidUrlWithSchemeHostnameAndNewTld() { 339 String url = "http://www.android.me"; 340 verifyAddLinksWithWebUrlSucceeds("Should match valid URL with scheme hostname and new TLD", 341 url); 342 } 343 344 @Test 345 public void testAddLinks_matchesValidUrlWithHostnameAndNewTld() { 346 String url = "android.camera"; 347 verifyAddLinksWithWebUrlSucceeds("Should match valid URL with hostname and new TLD", url); 348 } 349 350 @Test 351 public void testAddLinks_matchesPunycodeUrl() { 352 String url = "http://xn--fsqu00a.xn--unup4y"; 353 verifyAddLinksWithWebUrlSucceeds("Should match Punycode URL", url); 354 } 355 356 @Test 357 public void testAddLinks_matchesPunycodeUrlWithoutProtocol() { 358 String url = "xn--fsqu00a.xn--unup4y"; 359 verifyAddLinksWithWebUrlSucceeds("Should match Punycode URL without protocol", url); 360 } 361 362 @Test 363 public void testAddLinks_doesNotMatchPunycodeTldThatStartsWithDash() { 364 String url = "xn--fsqu00a.-xn--unup4y"; 365 verifyAddLinksWithWebUrlFails("Should not match Punycode TLD that starts with dash", url); 366 } 367 368 @Test 369 public void testAddLinks_partiallyMatchesPunycodeTldThatEndsWithDash() { 370 String url = "http://xn--fsqu00a.xn--unup4y-"; 371 verifyAddLinksWithWebUrlPartiallyMatches("Should partially match Punycode TLD that ends " 372 + "with dash", "http://xn--fsqu00a.xn--unup4y", url); 373 } 374 375 @Test 376 public void testAddLinks_matchesUrlWithUnicodeDomainName() { 377 String url = "http://\uD604\uAE08\uC601\uC218\uC99D.kr"; 378 verifyAddLinksWithWebUrlSucceeds("Should match URL with Unicode domain name", url); 379 } 380 381 @Test 382 public void testAddLinks_matchesUrlWithUnicodeDomainNameWithoutProtocol() { 383 String url = "\uD604\uAE08\uC601\uC218\uC99D.kr"; 384 verifyAddLinksWithWebUrlSucceeds("Should match URL without protocol and with Unicode " 385 + "domain name", url); 386 } 387 388 @Test 389 public void testAddLinks_matchesUrlWithUnicodeDomainNameAndTld() { 390 String url = "\uB3C4\uBA54\uC778.\uD55C\uAD6D"; 391 verifyAddLinksWithWebUrlSucceeds("Should match URL with Unicode domain name and TLD", url); 392 } 393 394 @Test 395 public void testAddLinks_matchesUrlWithUnicodePath() { 396 String url = "http://android.com/\u2019/a"; 397 verifyAddLinksWithWebUrlSucceeds("Should match URL with Unicode path", url); 398 } 399 400 @Test 401 public void testAddLinks_matchesValidUrlWithPort() { 402 String url = "http://www.example.com:8080"; 403 verifyAddLinksWithWebUrlSucceeds("Should match URL with port", url); 404 } 405 406 @Test 407 public void testAddLinks_matchesUrlWithPortAndQuery() { 408 String url = "http://www.example.com:8080/?foo=bar"; 409 verifyAddLinksWithWebUrlSucceeds("Should match URL with port and query", url); 410 } 411 412 @Test 413 public void testAddLinks_matchesUrlWithTilde() { 414 String url = "http://www.example.com:8080/~user/?foo=bar"; 415 verifyAddLinksWithWebUrlSucceeds("Should match URL with tilde", url); 416 } 417 418 @Test 419 public void testAddLinks_matchesUrlStartingWithHttpAndDoesNotHaveTld() { 420 String url = "http://android/#notld///a/n/d/r/o/i/d&p1=1&p2=2"; 421 verifyAddLinksWithWebUrlSucceeds("Should match URL without a TLD and starting with http", 422 url); 423 } 424 425 @Test 426 public void testAddLinks_doesNotMatchUrlsWithoutProtocolAndWithUnknownTld() { 427 String url = "thank.you"; 428 verifyAddLinksWithWebUrlFails("Should not match URL that does not start with a protocol " 429 + "and does not contain a known TLD", url); 430 } 431 432 @Test 433 public void testAddLinks_matchesValidUrlWithEmoji() { 434 String url = "Thank\u263A.com"; 435 verifyAddLinksWithWebUrlSucceeds("Should match URL with emoji", url); 436 } 437 438 @Test 439 public void testAddLinks_doesNotMatchUrlsWithEmojiWithoutProtocolAndWithoutKnownTld() { 440 String url = "Thank\u263A.you"; 441 verifyAddLinksWithWebUrlFails("Should not match URLs containing emoji and with unknown " 442 + "TLD", url); 443 } 444 445 @Test 446 public void testAddLinks_matchesDomainNameWithSurrogatePairs() { 447 String url = "android\uD83C\uDF38.com"; 448 verifyAddLinksWithWebUrlSucceeds("Should match domain name with Unicode surrogate pairs", 449 url); 450 } 451 452 @Test 453 public void testAddLinks_matchesTldWithSurrogatePairs() { 454 String url = "http://android.\uD83C\uDF38com"; 455 verifyAddLinksWithWebUrlSucceeds("Should match TLD with Unicode surrogate pairs", url); 456 } 457 458 @Test 459 public void testAddLinks_doesNotMatchUrlWithExcludedSurrogate() { 460 String url = "android\uD83F\uDFFE.com"; 461 verifyAddLinksWithWebUrlFails("Should not match URL with excluded Unicode surrogate" 462 + " pair", url); 463 } 464 465 @Test 466 public void testAddLinks_matchesPathWithSurrogatePairs() { 467 String url = "http://android.com/path-with-\uD83C\uDF38?v=\uD83C\uDF38f"; 468 verifyAddLinksWithWebUrlSucceeds("Should match path and query with Unicode surrogate pairs", 469 url); 470 } 471 472 @Test 473 public void testAddLinks__doesNotMatchUnicodeSpaces() { 474 String part1 = "http://and"; 475 String part2 = "roid.com"; 476 String[] emptySpaces = new String[]{ 477 "\u00A0", // no-break space 478 "\u2000", // en quad 479 "\u2001", // em quad 480 "\u2002", // en space 481 "\u2003", // em space 482 "\u2004", // three-per-em space 483 "\u2005", // four-per-em space 484 "\u2006", // six-per-em space 485 "\u2007", // figure space 486 "\u2008", // punctuation space 487 "\u2009", // thin space 488 "\u200A", // hair space 489 "\u2028", // line separator 490 "\u2029", // paragraph separator 491 "\u202F", // narrow no-break space 492 "\u3000" // ideographic space 493 }; 494 495 for (String emptySpace : emptySpaces) { 496 String url = part1 + emptySpace + part2; 497 verifyAddLinksWithWebUrlPartiallyMatches("Should not include empty space with code: " 498 + emptySpace.codePointAt(0), part1, url); 499 } 500 } 501 502 @Test 503 public void testAddLinks_matchesDomainNameWithDash() { 504 String url = "http://a-nd.r-oid.com"; 505 verifyAddLinksWithWebUrlSucceeds("Should match domain name with '-'", url); 506 507 url = "a-nd.r-oid.com"; 508 verifyAddLinksWithWebUrlSucceeds("Should match domain name with '-'", url); 509 } 510 511 @Test 512 public void testAddLinks_matchesDomainNameWithUnderscore() { 513 String url = "http://a_nd.r_oid.com"; 514 verifyAddLinksWithWebUrlSucceeds("Should match domain name with '_'", url); 515 516 url = "a_nd.r_oid.com"; 517 verifyAddLinksWithWebUrlSucceeds("Should match domain name with '_'", url); 518 } 519 520 @Test 521 public void testAddLinks_matchesPathAndQueryWithDollarSign() { 522 String url = "http://android.com/path$?v=$val"; 523 verifyAddLinksWithWebUrlSucceeds("Should match path and query with '$'", url); 524 525 url = "android.com/path$?v=$val"; 526 verifyAddLinksWithWebUrlSucceeds("Should match path and query with '$'", url); 527 } 528 529 @Test 530 public void testAddLinks_matchesEmptyPathWithQueryParams() { 531 String url = "http://android.com?q=v"; 532 verifyAddLinksWithWebUrlSucceeds("Should match empty path with query params", url); 533 534 url = "android.com?q=v"; 535 verifyAddLinksWithWebUrlSucceeds("Should match empty path with query params", url); 536 537 url = "http://android.com/?q=v"; 538 verifyAddLinksWithWebUrlSucceeds("Should match empty path with query params", url); 539 540 url = "android.com/?q=v"; 541 verifyAddLinksWithWebUrlSucceeds("Should match empty path with query params", url); 542 } 543 544 // EMAIL_ADDRESSES Related Tests 545 546 @Test 547 public void testAddLinks_email_matchesShortValidEmail() { 548 String email = "a@a.co"; 549 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 550 } 551 552 @Test 553 public void testAddLinks_email_matchesRegularEmail() { 554 String email = "email@android.com"; 555 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 556 } 557 558 @Test 559 public void testAddLinks_email_matchesEmailWithMultipleSubdomains() { 560 String email = "email@e.somelongdomainnameforandroid.abc.uk"; 561 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 562 } 563 564 @Test 565 public void testAddLinks_email_matchesLocalPartWithDot() { 566 String email = "e.mail@android.com"; 567 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 568 } 569 570 @Test 571 public void testAddLinks_email_matchesLocalPartWithPlus() { 572 String email = "e+mail@android.com"; 573 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 574 } 575 576 @Test 577 public void testAddLinks_email_matchesLocalPartWithUnderscore() { 578 String email = "e_mail@android.com"; 579 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 580 } 581 582 @Test 583 public void testAddLinks_email_matchesLocalPartWithDash() { 584 String email = "e-mail@android.com"; 585 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 586 } 587 588 @Test 589 public void testAddLinks_email_matchesLocalPartWithApostrophe() { 590 String email = "e'mail@android.com"; 591 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 592 } 593 594 @Test 595 public void testAddLinks_email_matchesLocalPartWithDigits() { 596 String email = "123@android.com"; 597 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 598 } 599 600 @Test 601 public void testAddLinks_email_matchesUnicodeLocalPart() { 602 String email = "\uD604\uAE08\uC601\uC218\uC99D@android.kr"; 603 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 604 } 605 606 @Test 607 public void testAddLinks_email_matchesLocalPartWithEmoji() { 608 String email = "smiley\u263A@android.com"; 609 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 610 } 611 612 @Test 613 public void testAddLinks_email_matchesLocalPartWithSurrogatePairs() { 614 String email = "a\uD83C\uDF38a@android.com"; 615 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 616 } 617 618 @Test 619 public void testAddLinks_email_matchesDomainWithDash() { 620 String email = "email@an-droid.com"; 621 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 622 } 623 624 @Test 625 public void testAddLinks_email_matchesUnicodeDomain() { 626 String email = "email@\uD604\uAE08\uC601\uC218\uC99D.kr"; 627 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 628 } 629 630 @Test 631 public void testAddLinks_email_matchesUnicodeLocalPartAndDomain() { 632 String email = "\uD604\uAE08\uC601\uC218\uC99D@\uD604\uAE08\uC601\uC218\uC99D.kr"; 633 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 634 } 635 636 @Test 637 public void testAddLinks_email_matchesDomainWithEmoji() { 638 String email = "smiley@\u263Aandroid.com"; 639 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 640 } 641 642 @Test 643 public void testAddLinks_email_matchesDomainWithSurrogatePairs() { 644 String email = "email@\uD83C\uDF38android.com"; 645 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 646 } 647 648 @Test 649 public void testAddLinks_email_matchesLocalPartAndDomainWithSurrogatePairs() { 650 String email = "a\uD83C\uDF38a@\uD83C\uDF38android.com"; 651 verifyAddLinksWithEmailSucceeds("Should match email: " + email, email); 652 } 653 654 @Test 655 public void testAddLinks_partiallyMatchesEmailEndingWithDot() { 656 String email = "email@android.co.uk."; 657 verifyAddLinksWithEmailPartiallyMatches("Should partially match email ending with dot", 658 "mailto:email@android.co.uk", email); 659 } 660 661 @Test 662 public void testAddLinks_email_partiallyMatchesLocalPartStartingWithDot() { 663 String email = ".email@android.com"; 664 verifyAddLinksWithEmailPartiallyMatches("Should partially match email starting " 665 + "with dot", "mailto:email@android.com", email); 666 } 667 668 @Test 669 public void testAddLinks_email_doesNotMatchStringWithoutAtSign() { 670 String email = "android.com"; 671 verifyAddLinksWithEmailFails("Should not match email: " + email, email); 672 } 673 674 @Test 675 public void testAddLinks_email_doesNotMatchPlainString() { 676 String email = "email"; 677 verifyAddLinksWithEmailFails("Should not match email: " + email, email); 678 } 679 680 @Test 681 public void testAddLinks_email_doesNotMatchEmailWithoutTld() { 682 String email = "email@android"; 683 verifyAddLinksWithEmailFails("Should not match email: " + email, email); 684 } 685 686 @Test 687 public void testAddLinks_email_doesNotMatchLocalPartEndingWithDot() { 688 String email = "email.@android.com"; 689 verifyAddLinksWithEmailFails("Should not match email: " + email, email); 690 } 691 692 @Test 693 public void testAddLinks_email_doesNotMatchDomainStartingWithDash() { 694 String email = "email@-android.com"; 695 verifyAddLinksWithEmailFails("Should not match email: " + email, email); 696 } 697 698 @Test 699 public void testAddLinks_email_doesNotMatchDomainWithConsecutiveDots() { 700 String email = "email@android..com"; 701 verifyAddLinksWithEmailFails("Should not match email: " + email, email); 702 } 703 704 @Test 705 public void testAddLinks_email_doesNotMatchEmailWithIp() { 706 String email = "email@127.0.0.1"; 707 verifyAddLinksWithEmailFails("Should not match email: " + email, email); 708 } 709 710 @Test 711 public void testAddLinks_email_doesNotMatchEmailWithInvalidTld() { 712 String email = "email@android.c"; 713 verifyAddLinksWithEmailFails("Should not match email: " + email, email); 714 } 715 716 @Test 717 public void testAddLinks_email_matchesLocalPartUpTo64Chars() { 718 String localPart = ""; 719 for (int i = 0; i < 64; i++) { 720 localPart += "a"; 721 } 722 String email = localPart + "@android.com"; 723 verifyAddLinksWithEmailSucceeds("Should match email local part of length: " 724 + localPart.length(), email); 725 726 email = localPart + "a@android.com"; 727 verifyAddLinksWithEmailFails("Should not match email local part of length:" 728 + localPart.length(), email); 729 } 730 731 @Test 732 public void testAddLinks_email_matchesSubdomainUpTo63Chars() { 733 String subdomain = ""; 734 for (int i = 0; i < 63; i++) { 735 subdomain += "a"; 736 } 737 String email = "email@" + subdomain + ".com"; 738 739 verifyAddLinksWithEmailSucceeds("Should match email subdomain of length: " 740 + subdomain.length(), email); 741 742 subdomain += "a"; 743 email = "email@" + subdomain + ".com"; 744 745 verifyAddLinksWithEmailFails("Should not match email subdomain of length:" 746 + subdomain.length(), email); 747 } 748 749 @Test 750 public void testAddLinks_email_matchesDomainUpTo255Chars() { 751 String domain = ""; 752 while (domain.length() <= 250) { 753 domain += "d."; 754 } 755 domain += "com"; 756 assertEquals(255, domain.length()); 757 String email = "a@" + domain; 758 verifyAddLinksWithEmailSucceeds("Should match email domain of length: " 759 + domain.length(), email); 760 761 email = email + "m"; 762 verifyAddLinksWithEmailFails("Should not match email domain of length:" 763 + domain.length(), email); 764 } 765 766 // Utility functions 767 private static void verifyAddLinksWithWebUrlSucceeds(String msg, String url) { 768 verifyAddLinksSucceeds(msg, url, Linkify.WEB_URLS); 769 } 770 771 private static void verifyAddLinksWithWebUrlFails(String msg, String url) { 772 verifyAddLinksFails(msg, url, Linkify.WEB_URLS); 773 } 774 775 private static void verifyAddLinksWithWebUrlPartiallyMatches(String msg, String expected, 776 String url) { 777 verifyAddLinksPartiallyMatches(msg, expected, url, Linkify.WEB_URLS); 778 } 779 780 private static void verifyAddLinksWithEmailSucceeds(String msg, String url) { 781 verifyAddLinksSucceeds(msg, url, Linkify.EMAIL_ADDRESSES); 782 } 783 784 private static void verifyAddLinksWithEmailFails(String msg, String url) { 785 verifyAddLinksFails(msg, url, Linkify.EMAIL_ADDRESSES); 786 } 787 788 private static void verifyAddLinksWithEmailPartiallyMatches(String msg, String expected, 789 String url) { 790 verifyAddLinksPartiallyMatches(msg, expected, url, Linkify.EMAIL_ADDRESSES); 791 } 792 793 private static void verifyAddLinksSucceeds(String msg, String string, int type) { 794 String str = "start " + string + " end"; 795 Spannable spannable = new SpannableString(str); 796 797 boolean linksAdded = LinkifyCompat.addLinks(spannable, type); 798 URLSpan[] spans = spannable.getSpans(0, str.length(), URLSpan.class); 799 800 assertTrue(msg, linksAdded); 801 assertEquals("Span should start from the beginning of: " + string, 802 "start ".length(), spannable.getSpanStart(spans[0])); 803 assertEquals("Span should end at the end of: " + string, 804 str.length() - " end".length(), spannable.getSpanEnd(spans[0])); 805 } 806 807 private static void verifyAddLinksFails(String msg, String string, int type) { 808 Spannable spannable = new SpannableString("start " + string + " end"); 809 boolean linksAdded = LinkifyCompat.addLinks(spannable, type); 810 assertFalse(msg, linksAdded); 811 } 812 813 private static void verifyAddLinksPartiallyMatches(String msg, String expected, 814 String string, int type) { 815 Spannable spannable = new SpannableString("start " + string + " end"); 816 boolean linksAdded = LinkifyCompat.addLinks(spannable, type); 817 URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class); 818 assertTrue(msg, linksAdded); 819 assertEquals(msg, expected, spans[0].getURL().toString()); 820 } 821} 822