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