1/*
2 * Copyright (C) 2015 Square, Inc.
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 */
16package com.squareup.okhttp;
17
18import com.squareup.okhttp.WebPlatformTestRun.SubtestResult;
19import com.squareup.okhttp.internal.Util;
20import java.io.IOException;
21import java.net.MalformedURLException;
22import java.net.URL;
23import java.util.ArrayList;
24import java.util.List;
25import okio.BufferedSource;
26import okio.Okio;
27import org.junit.Ignore;
28import org.junit.Test;
29import org.junit.runner.RunWith;
30import org.junit.runners.Parameterized;
31import org.junit.runners.Parameterized.Parameter;
32
33import static org.junit.Assert.assertEquals;
34import static org.junit.Assert.assertNotNull;
35import static org.junit.Assert.assertNull;
36
37/** Runs the web platform URL tests against Java URL models. */
38@RunWith(Parameterized.class)
39public final class WebPlatformUrlTest {
40  @Parameterized.Parameters(name = "{0}")
41  public static List<Object[]> parameters() {
42    try {
43      List<WebPlatformUrlTestData> tests = loadTests();
44
45      // The web platform tests are run in both HTML and XHTML variants. Major browsers pass more
46      // tests in HTML mode, so that's what we'll attempt to match.
47      String testName = "/url/a-element.html";
48      WebPlatformTestRun firefoxTestRun
49          = loadTestRun("/web-platform-test-results-url-firefox-37.0.json");
50      WebPlatformTestRun chromeTestRun
51          = loadTestRun("/web-platform-test-results-url-chrome-42.0.json");
52      WebPlatformTestRun safariTestRun
53          = loadTestRun("/web-platform-test-results-url-safari-7.1.json");
54
55      List<Object[]> result = new ArrayList<>();
56      for (WebPlatformUrlTestData urlTestData : tests) {
57        String subtestName = urlTestData.toString();
58        SubtestResult firefoxResult = firefoxTestRun.get(testName, subtestName);
59        SubtestResult chromeResult = chromeTestRun.get(testName, subtestName);
60        SubtestResult safariResult = safariTestRun.get(testName, subtestName);
61        result.add(new Object[] { urlTestData, firefoxResult, chromeResult, safariResult });
62      }
63      return result;
64    } catch (IOException e) {
65      throw new AssertionError();
66    }
67  }
68
69  @Parameter(0)
70  public WebPlatformUrlTestData testData;
71
72  @Parameter(1)
73  public SubtestResult firefoxResult;
74
75  @Parameter(2)
76  public SubtestResult chromeResultResult;
77
78  @Parameter(3)
79  public SubtestResult safariResult;
80
81  private static final List<String> JAVA_NET_URL_SCHEMES
82      = Util.immutableList("file", "ftp", "http", "https", "mailto");
83  private static final List<String> HTTP_URL_SCHEMES
84      = Util.immutableList("http", "https");
85
86  /** Test how {@link URL} does against the web platform test suite. */
87  @Ignore // java.net.URL is broken. Not much we can do about that.
88  @Test public void javaNetUrl() throws Exception {
89    if (!testData.scheme.isEmpty() && !JAVA_NET_URL_SCHEMES.contains(testData.scheme)) {
90      System.out.println("Ignoring unsupported scheme " + testData.scheme);
91      return;
92    }
93
94    try {
95      testJavaNetUrl();
96    } catch (AssertionError e) {
97      if (tolerateFailure()) {
98        System.out.println("Tolerable failure: " + e.getMessage());
99        return;
100      }
101      throw e;
102    }
103  }
104
105  private void testJavaNetUrl() {
106    URL url = null;
107    String failureMessage = "";
108    try {
109      if (testData.base.equals("about:blank")) {
110        url = new URL(testData.input);
111      } else {
112        URL baseUrl = new URL(testData.base);
113        url = new URL(baseUrl, testData.input);
114      }
115    } catch (MalformedURLException e) {
116      failureMessage = e.getMessage();
117    }
118
119    if (testData.expectParseFailure()) {
120      assertNull("Expected URL to fail parsing", url);
121    } else {
122      assertNotNull("Expected URL to parse successfully, but was " + failureMessage, url);
123      String effectivePort = url.getPort() != -1 ? Integer.toString(url.getPort()) : "";
124      String effectiveQuery = url.getQuery() != null ? "?" + url.getQuery() : "";
125      String effectiveFragment = url.getRef() != null ? "#" + url.getRef() : "";
126      assertEquals("scheme", testData.scheme, url.getProtocol());
127      assertEquals("host", testData.host, url.getHost());
128      assertEquals("port", testData.port, effectivePort);
129      assertEquals("path", testData.path, url.getPath());
130      assertEquals("query", testData.query, effectiveQuery);
131      assertEquals("fragment", testData.fragment, effectiveFragment);
132    }
133  }
134
135  /** Test how {@link HttpUrl} does against the web platform test suite. */
136  @Ignore // TODO(jwilson): implement character encoding.
137  @Test public void httpUrl() throws Exception {
138    if (!testData.scheme.isEmpty() && !HTTP_URL_SCHEMES.contains(testData.scheme)) {
139      System.out.println("Ignoring unsupported scheme " + testData.scheme);
140      return;
141    }
142    if (!testData.base.startsWith("https:") && !testData.base.startsWith("http:")) {
143      System.out.println("Ignoring unsupported base " + testData.base);
144      return;
145    }
146
147    try {
148      testHttpUrl();
149    } catch (AssertionError e) {
150      if (tolerateFailure()) {
151        System.out.println("Tolerable failure: " + e.getMessage());
152        return;
153      }
154      throw e;
155    }
156  }
157
158  private void testHttpUrl() {
159    HttpUrl url;
160    if (testData.base.equals("about:blank")) {
161      url = HttpUrl.parse(testData.input);
162    } else {
163      HttpUrl baseUrl = HttpUrl.parse(testData.base);
164      url = baseUrl.resolve(testData.input);
165    }
166
167    if (testData.expectParseFailure()) {
168      assertNull("Expected URL to fail parsing", url);
169    } else {
170      assertNotNull("Expected URL to parse successfully, but was null", url);
171      String effectivePort = url.port() != HttpUrl.defaultPort(url.scheme())
172          ? Integer.toString(url.port())
173          : "";
174      String effectiveQuery = url.query() != null ? "?" + url.query() : "";
175      String effectiveFragment = url.fragment() != null ? "#" + url.fragment() : "";
176      assertEquals("scheme", testData.scheme, url.scheme());
177      assertEquals("host", testData.host, url.host());
178      assertEquals("port", testData.port, effectivePort);
179      assertEquals("path", testData.path, url.path());
180      assertEquals("query", testData.query, effectiveQuery);
181      assertEquals("fragment", testData.fragment, effectiveFragment);
182    }
183  }
184
185  /**
186   * Returns true if several major browsers also fail this test, in which case the test itself is
187   * questionable.
188   */
189  private boolean tolerateFailure() {
190    return !firefoxResult.isPass()
191        && !chromeResultResult.isPass()
192        && !safariResult.isPass();
193  }
194
195  private static List<WebPlatformUrlTestData> loadTests() throws IOException {
196    BufferedSource source = Okio.buffer(Okio.source(
197        WebPlatformUrlTest.class.getResourceAsStream("/web-platform-test-urltestdata.txt")));
198    return WebPlatformUrlTestData.load(source);
199  }
200
201  private static WebPlatformTestRun loadTestRun(String name) throws IOException {
202    return WebPlatformTestRun.load(WebPlatformUrlTest.class.getResourceAsStream(name));
203  }
204}
205