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.annotations.GwtIncompatible;
21import com.google.common.base.Ascii;
22import com.google.common.base.Strings;
23import com.google.common.collect.ImmutableSet;
24import com.google.common.collect.Iterables;
25import com.google.common.testing.EqualsTester;
26import com.google.common.testing.NullPointerTester;
27
28import junit.framework.TestCase;
29
30/**
31 * {@link TestCase} for {@link InternetDomainName}.
32 *
33 * @author Craig Berry
34 */
35@GwtCompatible(emulated = true)
36public 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
196  public void testValid() {
197    for (String name : VALID_NAME) {
198      InternetDomainName.from(name);
199    }
200  }
201
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
398  private static InternetDomainName idn(String domain) {
399    return InternetDomainName.from(domain);
400  }
401
402  @GwtIncompatible("NullPointerTester")
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