1 /*
2  * Copyright (C) 2009 The Guava Authors
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 
17 package com.google.common.net;
18 
19 import com.google.common.annotations.GwtCompatible;
20 import com.google.common.annotations.GwtIncompatible;
21 import com.google.common.base.Ascii;
22 import com.google.common.base.Strings;
23 import com.google.common.collect.ImmutableSet;
24 import com.google.common.collect.Iterables;
25 import com.google.common.testing.EqualsTester;
26 import com.google.common.testing.NullPointerTester;
27 
28 import junit.framework.TestCase;
29 
30 /**
31  * {@link TestCase} for {@link InternetDomainName}.
32  *
33  * @author Craig Berry
34  */
35 @GwtCompatible(emulated = true)
36 public final class InternetDomainNameTest extends TestCase {
37   private static final InternetDomainName UNICODE_EXAMPLE =
38       InternetDomainName.from("j\u00f8rpeland.no");
39   private static final InternetDomainName PUNYCODE_EXAMPLE =
40       InternetDomainName.from("xn--jrpeland-54a.no");
41 
42   /**
43    * The Greek letter delta, used in unicode testing.
44    */
45   private static final String DELTA = "\u0394";
46 
47   /**
48    * A domain part which is valid under lenient validation, but invalid under
49    * strict validation.
50    */
51   static final String LOTS_OF_DELTAS = Strings.repeat(DELTA, 62);
52 
53   private static final String ALMOST_TOO_MANY_LEVELS =
54       Strings.repeat("a.", 127);
55 
56   private static final String ALMOST_TOO_LONG =
57       Strings.repeat("aaaaa.", 40) + "1234567890.c";
58 
59   private static final ImmutableSet<String> VALID_NAME = ImmutableSet.of(
60       "foo.com",
61       "f-_-o.cOM",
62       "f--1.com",
63       "f11-1.com",
64       "www",
65       "abc.a23",
66       "biz.com.ua",
67       "x",
68       "fOo",
69       "f--o",
70       "f_a",
71       "foo.net.us\uFF61ocm",
72       "woo.com.",
73       "a" + DELTA + "b.com",
74       ALMOST_TOO_MANY_LEVELS,
75       ALMOST_TOO_LONG);
76 
77   private static final ImmutableSet<String> INVALID_NAME = ImmutableSet.of(
78       "",
79       " ",
80       "127.0.0.1",
81       "::1", "13",
82       "abc.12c",
83       "foo-.com",
84       "_bar.quux",
85       "foo+bar.com",
86       "foo!bar.com",
87       ".foo.com",
88       "..bar.com",
89       "baz..com",
90       "..quiffle.com",
91       "fleeb.com..",
92       ".",
93       "..",
94       "...",
95       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com",
96       "a" + DELTA + " .com",
97       ALMOST_TOO_MANY_LEVELS + "com",
98       ALMOST_TOO_LONG + ".c");
99 
100   private static final ImmutableSet<String> PS = ImmutableSet.of(
101       "com",
102       "co.uk",
103       "foo.bd",
104       "xxxxxx.bd",
105       "org.mK",
106       "us",
107       "uk\uFF61com.",  // Alternate dot character
108       "\u7f51\u7edc.Cn",  // "网络.Cn"
109       "j\u00f8rpeland.no",  // "jorpeland.no" (first o slashed)
110       "xn--jrpeland-54a.no"  // IDNA (punycode) encoding of above
111   );
112 
113   private static final ImmutableSet<String> NO_PS = ImmutableSet.of(
114       "www", "foo.google", "x.y.z");
115 
116   private static final ImmutableSet<String> NON_PS = ImmutableSet.of(
117       "foo.bar.com", "foo.ca", "foo.bar.ca",
118       "foo.bar.co.il", "state.CA.us", "www.state.pa.us", "pvt.k12.ca.us",
119       "www.google.com", "www4.yahoo.co.uk", "home.netscape.com",
120       "web.MIT.edu", "foo.eDu.au", "utenti.blah.IT", "dominio.com.co");
121 
122   private static final ImmutableSet<String> TOP_PRIVATE_DOMAIN =
123       ImmutableSet.of("google.com", "foo.Co.uk", "foo.ca.us.");
124 
125   private static final ImmutableSet<String> UNDER_PRIVATE_DOMAIN =
126       ImmutableSet.of("foo.bar.google.com", "a.b.co.uk", "x.y.ca.us");
127 
128   private static final ImmutableSet<String> VALID_IP_ADDRS = ImmutableSet.of(
129       "1.2.3.4", "127.0.0.1", "::1", "2001:db8::1");
130 
131   private static final ImmutableSet<String> INVALID_IP_ADDRS = ImmutableSet.of(
132       "", "1", "1.2.3", "...", "1.2.3.4.5", "400.500.600.700",
133       ":", ":::1", "2001:db8:");
134 
135   private static final ImmutableSet<String> SOMEWHERE_UNDER_PS =
136       ImmutableSet.of(
137           "foo.bar.google.com",
138           "a.b.c.1.2.3.ca.us",
139           "site.jp",
140           "uomi-online.kir.jp",
141           "jprs.co.jp",
142           "site.quick.jp",
143           "site.tenki.jp",
144           "site.or.jp",
145           "site.gr.jp",
146           "site.ne.jp",
147           "site.ac.jp",
148           "site.ad.jp",
149           "site.ed.jp",
150           "site.geo.jp",
151           "site.go.jp",
152           "site.lg.jp",
153           "1.fm",
154           "site.cc",
155           "site.ee",
156           "site.fi",
157           "site.fm",
158           "site.gr",
159           "www.leguide.ma",
160           "site.ma",
161           "some.org.mk",
162           "site.mk",
163           "site.tv",
164           "site.us",
165           "www.odev.us",
166           "www.GOOGLE.com",
167           "www.com",
168           "google.com",
169           "www7.google.co.uk",
170           "google.Co.uK",
171           "jobs.kt.com.",
172           "home.netscape.com",
173           "web.stanford.edu",
174           "stanford.edu",
175           "state.ca.us",
176           "www.state.ca.us",
177           "state.ca.us",
178           "pvt.k12.ca.us",
179           "www.rave.ca.",
180           "cnn.ca",
181           "ledger-enquirer.com",
182           "it-trace.ch",
183           "cool.dk",
184           "cool.co.uk",
185           "cool.de",
186           "cool.es",
187           "cool\uFF61fr", // Alternate dot character
188           "cool.nl",
189           "members.blah.nl.",
190           "cool.se",
191           "utenti.blah.it",
192           "kt.co",
193           "a\u7f51\u7edcA.\u7f51\u7edc.Cn"  // "a网络A.网络.Cn"
194       );
195 
testValid()196   public void testValid() {
197     for (String name : VALID_NAME) {
198       InternetDomainName.from(name);
199     }
200   }
201 
testInvalid()202   public void testInvalid() {
203     for (String name : INVALID_NAME) {
204       try {
205         InternetDomainName.from(name);
206         fail("Should have been invalid: '" + name + "'");
207       } catch (IllegalArgumentException expected) {
208         // Expected case
209       }
210     }
211   }
212 
testPublicSuffix()213   public void testPublicSuffix() {
214     for (String name : PS) {
215       final InternetDomainName domain = InternetDomainName.from(name);
216       assertTrue(name, domain.isPublicSuffix());
217       assertTrue(name, domain.hasPublicSuffix());
218       assertFalse(name, domain.isUnderPublicSuffix());
219       assertFalse(name, domain.isTopPrivateDomain());
220       assertEquals(domain, domain.publicSuffix());
221     }
222 
223     for (String name : NO_PS) {
224       final InternetDomainName domain = InternetDomainName.from(name);
225       assertFalse(name, domain.isPublicSuffix());
226       assertFalse(name, domain.hasPublicSuffix());
227       assertFalse(name, domain.isUnderPublicSuffix());
228       assertFalse(name, domain.isTopPrivateDomain());
229       assertNull(domain.publicSuffix());
230     }
231 
232     for (String name : NON_PS) {
233       final InternetDomainName domain = InternetDomainName.from(name);
234       assertFalse(name, domain.isPublicSuffix());
235       assertTrue(name, domain.hasPublicSuffix());
236       assertTrue(name, domain.isUnderPublicSuffix());
237     }
238   }
239 
testUnderPublicSuffix()240   public void testUnderPublicSuffix() {
241     for (String name : SOMEWHERE_UNDER_PS) {
242       final InternetDomainName domain = InternetDomainName.from(name);
243       assertFalse(name, domain.isPublicSuffix());
244       assertTrue(name, domain.hasPublicSuffix());
245       assertTrue(name, domain.isUnderPublicSuffix());
246     }
247   }
248 
testTopPrivateDomain()249   public void testTopPrivateDomain() {
250     for (String name : TOP_PRIVATE_DOMAIN) {
251       final InternetDomainName domain = InternetDomainName.from(name);
252       assertFalse(name, domain.isPublicSuffix());
253       assertTrue(name, domain.hasPublicSuffix());
254       assertTrue(name, domain.isUnderPublicSuffix());
255       assertTrue(name, domain.isTopPrivateDomain());
256       assertEquals(domain.parent(), domain.publicSuffix());
257     }
258   }
259 
testUnderPrivateDomain()260   public void testUnderPrivateDomain() {
261     for (String name : UNDER_PRIVATE_DOMAIN) {
262       final InternetDomainName domain = InternetDomainName.from(name);
263       assertFalse(name, domain.isPublicSuffix());
264       assertTrue(name, domain.hasPublicSuffix());
265       assertTrue(name, domain.isUnderPublicSuffix());
266       assertFalse(name, domain.isTopPrivateDomain());
267     }
268   }
269 
testParent()270   public void testParent() {
271     assertEquals(
272         "com",
273         InternetDomainName.from("google.com").parent().toString());
274     assertEquals(
275         "uk",
276         InternetDomainName.from("co.uk").parent().toString());
277     assertEquals(
278         "google.com",
279         InternetDomainName.from("www.google.com").parent().toString());
280 
281     try {
282       InternetDomainName.from("com").parent();
283       fail("'com' should throw ISE on .parent() call");
284     } catch (IllegalStateException expected) {
285     }
286   }
287 
testChild()288   public void testChild() {
289     InternetDomainName domain = InternetDomainName.from("foo.com");
290 
291     assertEquals("www.foo.com", domain.child("www").toString());
292 
293     try {
294       domain.child("www.");
295       fail("www..google.com should have been invalid");
296     } catch (IllegalArgumentException expected) {
297       // Expected outcome
298     }
299   }
300 
testParentChild()301   public void testParentChild() {
302     InternetDomainName origin = InternetDomainName.from("foo.com");
303     InternetDomainName parent = origin.parent();
304     assertEquals("com", parent.toString());
305 
306     // These would throw an exception if leniency were not preserved during parent() and child()
307     // calls.
308     InternetDomainName child = parent.child(LOTS_OF_DELTAS);
309     child.child(LOTS_OF_DELTAS);
310   }
311 
testValidTopPrivateDomain()312   public void testValidTopPrivateDomain() {
313     InternetDomainName googleDomain = InternetDomainName.from("google.com");
314 
315     assertEquals(googleDomain, googleDomain.topPrivateDomain());
316     assertEquals(googleDomain, googleDomain.child("mail").topPrivateDomain());
317     assertEquals(googleDomain, googleDomain.child("foo.bar").topPrivateDomain());
318   }
319 
testInvalidTopPrivateDomain()320   public void testInvalidTopPrivateDomain() {
321     ImmutableSet<String> badCookieDomains =
322         ImmutableSet.of("co.uk", "foo", "com");
323 
324     for (String domain : badCookieDomains) {
325       try {
326         InternetDomainName.from(domain).topPrivateDomain();
327         fail(domain);
328       } catch (IllegalStateException expected) {
329       }
330     }
331   }
332 
testIsValid()333   public void testIsValid() {
334     final Iterable<String> validCases = Iterables.concat(
335         VALID_NAME, PS, NO_PS, NON_PS);
336     final Iterable<String> invalidCases = Iterables.concat(
337         INVALID_NAME, VALID_IP_ADDRS, INVALID_IP_ADDRS);
338 
339     for (String valid : validCases) {
340       assertTrue(valid, InternetDomainName.isValid(valid));
341     }
342 
343     for (String invalid : invalidCases) {
344       assertFalse(invalid, InternetDomainName.isValid(invalid));
345     }
346   }
347 
testToString()348   public void testToString() {
349     for (String inputName : SOMEWHERE_UNDER_PS) {
350       InternetDomainName domain = InternetDomainName.from(inputName);
351 
352       /*
353        * We would ordinarily use constants for the expected results, but
354        * doing it by derivation allows us to reuse the test case definitions
355        * used in other tests.
356        */
357 
358       String expectedName = Ascii.toLowerCase(inputName);
359       expectedName = expectedName.replaceAll("[\u3002\uFF0E\uFF61]", ".");
360 
361       if (expectedName.endsWith(".")) {
362         expectedName = expectedName.substring(0, expectedName.length() - 1);
363       }
364 
365       assertEquals(expectedName, domain.toString());
366     }
367   }
368 
testExclusion()369   public void testExclusion() {
370     InternetDomainName domain = InternetDomainName.from("foo.teledata.mz");
371     assertTrue(domain.hasPublicSuffix());
372     assertEquals("mz", domain.publicSuffix().toString());
373 
374     // Behold the weirdness!
375     assertFalse(domain.publicSuffix().isPublicSuffix());
376   }
377 
testMultipleUnders()378   public void testMultipleUnders() {
379     // PSL has both *.uk and *.sch.uk; the latter should win.
380     // See http://code.google.com/p/guava-libraries/issues/detail?id=1176
381 
382     InternetDomainName domain = InternetDomainName.from("www.essex.sch.uk");
383     assertTrue(domain.hasPublicSuffix());
384     assertEquals("essex.sch.uk", domain.publicSuffix().toString());
385     assertEquals("www.essex.sch.uk", domain.topPrivateDomain().toString());
386   }
387 
testEquality()388   public void testEquality() {
389     new EqualsTester()
390         .addEqualityGroup(
391             idn("google.com"), idn("google.com"), idn("GOOGLE.COM"))
392         .addEqualityGroup(idn("www.google.com"))
393         .addEqualityGroup(UNICODE_EXAMPLE)
394         .addEqualityGroup(PUNYCODE_EXAMPLE)
395         .testEquals();
396   }
397 
idn(String domain)398   private static InternetDomainName idn(String domain) {
399     return InternetDomainName.from(domain);
400   }
401 
402   @GwtIncompatible("NullPointerTester")
testNulls()403   public void testNulls() {
404     final NullPointerTester tester = new NullPointerTester();
405 
406     tester.testAllPublicStaticMethods(InternetDomainName.class);
407     tester.testAllPublicInstanceMethods(InternetDomainName.from("google.com"));
408   }
409 }
410