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