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