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