summaryrefslogtreecommitdiffstats
path: root/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java')
-rw-r--r--android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java387
1 files changed, 387 insertions, 0 deletions
diff --git a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java
new file mode 100644
index 0000000..93b0029
--- /dev/null
+++ b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java
@@ -0,0 +1,387 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.webview_shell.test;
+
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import junit.framework.ComparisonFailure;
+
+import org.chromium.base.Log;
+import org.chromium.webview_shell.WebViewLayoutTestActivity;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Tests running end-to-end layout tests.
+ */
+public class WebViewLayoutTest
+ extends ActivityInstrumentationTestCase2<WebViewLayoutTestActivity> {
+
+ private static final String TAG = "WebViewLayoutTest";
+
+ private static final String EXTERNAL_PREFIX =
+ Environment.getExternalStorageDirectory().getAbsolutePath() + "/";
+ private static final String BASE_WEBVIEW_TEST_PATH =
+ "android_webview/tools/system_webview_shell/test/data/";
+ private static final String BASE_BLINK_TEST_PATH = "third_party/WebKit/LayoutTests/";
+ private static final String BASE_BLINK_STABLE_TEST_PATH =
+ BASE_BLINK_TEST_PATH + "virtual/stable/";
+ private static final String PATH_WEBVIEW_PREFIX = EXTERNAL_PREFIX + BASE_WEBVIEW_TEST_PATH;
+ private static final String PATH_BLINK_PREFIX = EXTERNAL_PREFIX + BASE_BLINK_TEST_PATH;
+ private static final String PATH_BLINK_STABLE_PREFIX =
+ EXTERNAL_PREFIX + BASE_BLINK_STABLE_TEST_PATH;
+
+ private static final long TIMEOUT_SECONDS = 20;
+
+ private WebViewLayoutTestActivity mTestActivity;
+
+ public WebViewLayoutTest() {
+ super(WebViewLayoutTestActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mTestActivity = (WebViewLayoutTestActivity) getActivity();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mTestActivity.finish();
+ super.tearDown();
+ }
+
+ @Override
+ public WebViewLayoutTestRunner getInstrumentation() {
+ return (WebViewLayoutTestRunner) super.getInstrumentation();
+ }
+
+ @MediumTest
+ public void testSimple() throws Exception {
+ runWebViewLayoutTest("experimental/basic-logging.html",
+ "experimental/basic-logging-expected.txt");
+ }
+
+ // This is a non-failing test because it tends to require frequent rebaselines.
+ @MediumTest
+ public void testGlobalInterfaceNoFail() throws Exception {
+ runBlinkLayoutTest("webexposed/global-interface-listing.html",
+ "webexposed/global-interface-listing-expected.txt", true);
+ }
+
+ // This is a non-failing test to avoid 'blind' rebaselines by the sheriff
+ // (see crbug.com/564765).
+ @MediumTest
+ public void testNoUnexpectedInterfaces() throws Exception {
+ ensureJsTestCopied();
+ loadUrlWebViewAsync("file://" + PATH_BLINK_PREFIX
+ + "webexposed/global-interface-listing.html", mTestActivity);
+ String webviewExpected = readFile(PATH_WEBVIEW_PREFIX
+ + "webexposed/global-interface-listing-expected.txt");
+ mTestActivity.waitForFinish(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ String result = mTestActivity.getTestResult();
+
+ HashMap<String, HashSet<String>> webviewInterfacesMap = buildHashMap(result);
+ HashMap<String, HashSet<String>> webviewExpectedInterfacesMap =
+ buildHashMap(webviewExpected);
+ StringBuilder newInterfaces = new StringBuilder();
+
+ // Check that each current webview interface is one of webview expected interfaces.
+ for (String interfaceS : webviewInterfacesMap.keySet()) {
+ if (webviewExpectedInterfacesMap.get(interfaceS) == null) {
+ newInterfaces.append(interfaceS + "\n");
+ }
+ }
+
+ if (newInterfaces.length() > 0) {
+ Log.w(TAG, "Unexpected WebView interfaces found: " + newInterfaces.toString());
+ }
+ }
+
+ @MediumTest
+ public void testWebViewExcludedInterfaces() throws Exception {
+ ensureJsTestCopied();
+ loadUrlWebViewAsync("file://" + PATH_BLINK_PREFIX
+ + "webexposed/global-interface-listing.html", mTestActivity);
+ String blinkExpected = readFile(PATH_BLINK_PREFIX
+ + "webexposed/global-interface-listing-expected.txt");
+ String webviewExcluded = readFile(PATH_WEBVIEW_PREFIX
+ + "webexposed/not-webview-exposed.txt");
+ mTestActivity.waitForFinish(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ String result = mTestActivity.getTestResult();
+
+ HashMap<String, HashSet<String>> webviewExcludedInterfacesMap =
+ buildHashMap(webviewExcluded);
+ HashMap<String, HashSet<String>> webviewInterfacesMap = buildHashMap(result);
+ HashMap<String, HashSet<String>> blinkInterfacesMap = buildHashMap(blinkExpected);
+ StringBuilder unexpected = new StringBuilder();
+
+ // Check that each excluded interface and its properties are present in blinkInterfaceMap
+ // but not in webviewInterfacesMap.
+ for (HashMap.Entry<String, HashSet<String>> entry :
+ webviewExcludedInterfacesMap.entrySet()) {
+ String interfaceS = entry.getKey();
+ HashSet<String> subsetBlink = blinkInterfacesMap.get(interfaceS);
+ assertNotNull("Interface " + interfaceS + " not exposed in blink", subsetBlink);
+
+ HashSet<String> subsetWebView = webviewInterfacesMap.get(interfaceS);
+ HashSet<String> subsetExcluded = entry.getValue();
+ if (subsetExcluded.isEmpty() && subsetWebView != null) {
+ unexpected.append(interfaceS + "\n");
+ continue;
+ }
+
+ for (String property : subsetExcluded) {
+ assertTrue("Interface " + interfaceS + "." + property + " not exposed in blink",
+ subsetBlink.contains(property));
+ if (subsetWebView != null && subsetWebView.contains(property)) {
+ unexpected.append(interfaceS + "." + property + "\n");
+ }
+ }
+ }
+ assertEquals("Unexpected webview interfaces found", "", unexpected.toString());
+ }
+
+ @MediumTest
+ public void testWebViewIncludedStableInterfaces() throws Exception {
+ ensureJsTestCopied();
+ loadUrlWebViewAsync("file://" + PATH_BLINK_PREFIX
+ + "webexposed/global-interface-listing.html", mTestActivity);
+ String blinkStableExpected = readFile(PATH_BLINK_STABLE_PREFIX
+ + "webexposed/global-interface-listing-expected.txt");
+ String webviewExcluded = readFile(PATH_WEBVIEW_PREFIX
+ + "webexposed/not-webview-exposed.txt");
+ mTestActivity.waitForFinish(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ String result = mTestActivity.getTestResult();
+
+ HashMap<String, HashSet<String>> webviewExcludedInterfacesMap =
+ buildHashMap(webviewExcluded);
+ HashMap<String, HashSet<String>> webviewInterfacesMap = buildHashMap(result);
+ HashMap<String, HashSet<String>> blinkStableInterfacesMap =
+ buildHashMap(blinkStableExpected);
+ StringBuilder missing = new StringBuilder();
+
+ // Check that each stable blink interface and its properties are present in webview
+ // except the excluded interfaces/properties.
+ for (HashMap.Entry<String, HashSet<String>> entry : blinkStableInterfacesMap.entrySet()) {
+ String interfaceS = entry.getKey();
+ HashSet<String> subsetExcluded = webviewExcludedInterfacesMap.get(interfaceS);
+ if (subsetExcluded != null && subsetExcluded.isEmpty()) continue;
+
+ HashSet<String> subsetBlink = entry.getValue();
+ HashSet<String> subsetWebView = webviewInterfacesMap.get(interfaceS);
+
+ if (subsetWebView == null) {
+ // interface is missing completely
+ missing.append(interfaceS + "\n");
+ continue;
+ }
+
+ for (String propertyBlink : subsetBlink) {
+ if (subsetExcluded != null && subsetExcluded.contains(propertyBlink)) continue;
+ if (!subsetWebView.contains(propertyBlink)) {
+ missing.append(interfaceS + "." + propertyBlink + "\n");
+ }
+ }
+ }
+ assertEquals("Missing webview interfaces found", "", missing.toString());
+ }
+
+ @MediumTest
+ public void testRequestMIDIAccess() throws Exception {
+ mTestActivity.setGrantPermission(true);
+ runWebViewLayoutTest("blink-apis/webmidi/requestmidiaccess.html",
+ "blink-apis/webmidi/requestmidiaccess-expected.txt");
+ mTestActivity.setGrantPermission(false);
+ }
+
+ @MediumTest
+ public void testRequestMIDIAccessDenyPermission() throws Exception {
+ runWebViewLayoutTest("blink-apis/webmidi/requestmidiaccess-permission-denied.html",
+ "blink-apis/webmidi/requestmidiaccess-permission-denied-expected.html");
+ }
+
+ // Blink platform API tests
+
+ @MediumTest
+ public void testGeolocationCallbacks() throws Exception {
+ runWebViewLayoutTest("blink-apis/geolocation/geolocation-permission-callbacks.html",
+ "blink-apis/geolocation/geolocation-permission-callbacks-expected.txt");
+ }
+
+ @MediumTest
+ public void testMediaStreamApiDenyPermission() throws Exception {
+ runWebViewLayoutTest("blink-apis/webrtc/mediastream-permission-denied-callbacks.html",
+ "blink-apis/webrtc/mediastream-permission-denied-callbacks-expected.txt");
+ }
+
+ @MediumTest
+ public void testMediaStreamApi() throws Exception {
+ mTestActivity.setGrantPermission(true);
+ runWebViewLayoutTest("blink-apis/webrtc/mediastream-callbacks.html",
+ "blink-apis/webrtc/mediastream-callbacks-expected.txt");
+ mTestActivity.setGrantPermission(false);
+ }
+
+ @MediumTest
+ public void testBatteryApi() throws Exception {
+ runWebViewLayoutTest("blink-apis/battery-status/battery-callback.html",
+ "blink-apis/battery-status/battery-callback-expected.txt");
+ }
+
+ // test helper methods
+
+ private void runWebViewLayoutTest(final String fileName, final String fileNameExpected)
+ throws Exception {
+ runTest(PATH_WEBVIEW_PREFIX + fileName, PATH_WEBVIEW_PREFIX + fileNameExpected, false);
+ }
+
+ private void runBlinkLayoutTest(final String fileName, final String fileNameExpected,
+ boolean noFail) throws Exception {
+ ensureJsTestCopied();
+ runTest(PATH_BLINK_PREFIX + fileName, PATH_WEBVIEW_PREFIX + fileNameExpected, noFail);
+ }
+
+ private void runTest(final String fileName, final String fileNameExpected, boolean noFail)
+ throws FileNotFoundException, IOException, InterruptedException, TimeoutException {
+ loadUrlWebViewAsync("file://" + fileName, mTestActivity);
+
+ if (getInstrumentation().isRebaseline()) {
+ // this is the rebaseline process
+ mTestActivity.waitForFinish(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ String result = mTestActivity.getTestResult();
+ writeFile(fileNameExpected, result, true);
+ Log.i(TAG, "file: " + fileNameExpected + " --> rebaselined, length=" + result.length());
+ } else {
+ String expected = readFile(fileNameExpected);
+ mTestActivity.waitForFinish(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ String result = mTestActivity.getTestResult();
+ if (noFail && !expected.equals(result)) {
+ ComparisonFailure cf = new ComparisonFailure("Unexpected result", expected, result);
+ Log.e(TAG, cf.toString());
+ } else {
+ assertEquals(expected, result);
+ }
+ }
+ }
+
+ private void loadUrlWebViewAsync(final String fileUrl,
+ final WebViewLayoutTestActivity activity) {
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ activity.loadUrl(fileUrl);
+ }
+ });
+ }
+
+ private static void ensureJsTestCopied() throws IOException {
+ File jsTestFile = new File(PATH_BLINK_PREFIX + "resources/js-test.js");
+ if (jsTestFile.exists()) return;
+ String original = readFile(PATH_WEBVIEW_PREFIX + "resources/js-test.js");
+ writeFile(PATH_BLINK_PREFIX + "resources/js-test.js", original, false);
+ }
+
+ /**
+ * Reads a file and returns it's contents as string.
+ */
+ private static String readFile(String fileName) throws IOException {
+ FileInputStream inputStream = new FileInputStream(new File(fileName));
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+ try {
+ StringBuilder contents = new StringBuilder();
+ String line;
+
+ while ((line = reader.readLine()) != null) {
+ contents.append(line);
+ contents.append("\n");
+ }
+ return contents.toString();
+ } finally {
+ reader.close();
+ }
+ } finally {
+ inputStream.close();
+ }
+ }
+
+ /**
+ * Writes a file with the given fileName and contents. If overwrite is true overwrites any
+ * exisiting file with the same file name. If the file does not exist any intermediate
+ * required directories are created.
+ */
+ private static void writeFile(final String fileName, final String contents, boolean overwrite)
+ throws FileNotFoundException, IOException {
+ File fileOut = new File(fileName);
+
+ if (fileOut.exists() && !overwrite) {
+ return;
+ }
+
+ String absolutePath = fileOut.getAbsolutePath();
+ File filePath = new File(absolutePath.substring(0, absolutePath.lastIndexOf("/")));
+
+ if (!filePath.exists()) {
+ if (!filePath.mkdirs())
+ throw new IOException("failed to create directories: " + filePath);
+ }
+
+ FileOutputStream outputStream = new FileOutputStream(fileOut);
+ try {
+ outputStream.write(contents.getBytes());
+ } finally {
+ outputStream.close();
+ }
+ }
+
+ private HashMap<String, HashSet<String>> buildHashMap(String contents) {
+ String[] lineByLine = contents.split("\\n");
+
+ HashSet subset = null;
+ HashMap<String, HashSet<String>> interfaces = new HashMap<String, HashSet<String>>();
+ for (String line : lineByLine) {
+ String s = trimAndRemoveComments(line);
+ if (isInterfaceOrGlobalObject(s)) {
+ subset = interfaces.get(s);
+ if (subset == null) {
+ subset = new HashSet();
+ interfaces.put(s, subset);
+ }
+ } else if (isInterfaceProperty(s) && subset != null) {
+ subset.add(s);
+ }
+ }
+ return interfaces;
+ }
+
+ private String trimAndRemoveComments(String line) {
+ String s = line.trim();
+ int commentIndex = s.indexOf("#"); // remove any potential comments
+ return (commentIndex >= 0) ? s.substring(0, commentIndex).trim() : s;
+ }
+
+ private boolean isInterfaceOrGlobalObject(String s) {
+ return s.startsWith("interface") || s.startsWith("[GLOBAL OBJECT]");
+ }
+
+ private boolean isInterfaceProperty(String s) {
+ return s.startsWith("getter") || s.startsWith("setter")
+ || s.startsWith("method") || s.startsWith("attribute");
+ }
+
+}