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}