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