Add a CTS test for a WebView crash due to AppCache

This crash occurred when AppCache was used without having set the
database path.

Bug: 3305684
Change-Id: I90209f63c77b731f98280ea6da8c453e02840f5a
diff --git a/tests/src/android/webkit/cts/CtsTestServer.java b/tests/src/android/webkit/cts/CtsTestServer.java
index 87b3d1d..075f98d 100644
--- a/tests/src/android/webkit/cts/CtsTestServer.java
+++ b/tests/src/android/webkit/cts/CtsTestServer.java
@@ -81,6 +81,8 @@
     public static final String USERAGENT_PATH = "/useragent.html";
     public static final String ASSET_PREFIX = "/assets/";
     public static final String FAVICON_ASSET_PATH = ASSET_PREFIX + "webkit/favicon.png";
+    public static final String APPCACHE_PATH = "/appcache.html";
+    public static final String APPCACHE_MANIFEST_PATH = "/appcache.manifest";
     public static final String REDIRECT_PREFIX = "/redirect";
     public static final String DELAY_PREFIX = "/delayed";
     public static final String BINARY_PREFIX = "/binary";
@@ -342,6 +344,12 @@
         return sb.toString();
     }
 
+    public String getAppCacheUrl() {
+        StringBuilder sb = new StringBuilder(getBaseUri());
+        sb.append(APPCACHE_PATH);
+        return sb.toString();
+    }
+
     public String getLastRequestUrl() {
         return mLastQuery;
     }
@@ -500,6 +508,38 @@
             // We cannot close the socket here, because we need to respond.
             // Status must be set to OK, or else the test will fail due to
             // a RunTimeException.
+        } else if (path.equals(APPCACHE_PATH)) {
+            response = createResponse(HttpStatus.SC_OK);
+            response.setEntity(createEntity("<!DOCTYPE HTML>" +
+                    "<html manifest=\"appcache.manifest\">" +
+                    "  <head>" +
+                    "    <title>Waiting</title>" +
+                    "    <script>" +
+                    "      function updateTitle() { document.title = \"Done\"; }" +
+                    "      window.applicationCache.onnoupdate = updateTitle;" +
+                    "      window.applicationCache.oncached = updateTitle;" +
+                    "      window.applicationCache.onupdateready = updateTitle;" +
+                    "      window.applicationCache.onobsolete = updateTitle;" +
+                    "      window.applicationCache.onerror = updateTitle;" +
+                    "    </script>" +
+                    "  </head>" +
+                    "  <body>AppCache test</body>" +
+                    "</html>"));
+        } else if (path.equals(APPCACHE_MANIFEST_PATH)) {
+            response = createResponse(HttpStatus.SC_OK);
+            try {
+                StringEntity entity = new StringEntity("CACHE MANIFEST");
+                // This entity property is not used when constructing the response, (See
+                // AbstractMessageWriter.write(), which is called by
+                // AbstractHttpServerConnection.sendResponseHeader()) so we have to set this header
+                // manually.
+                // TODO: Should we do this for all responses from this server?
+                entity.setContentType("text/cache-manifest");
+                response.setEntity(entity);
+                response.setHeader("Content-Type", "text/cache-manifest");
+            } catch (UnsupportedEncodingException e) {
+                Log.w(TAG, "Unexpected UnsupportedEncodingException");
+            }
         }
         if (response == null) {
             response = createResponse(HttpStatus.SC_NOT_FOUND);
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index de80713..6b9f9dc 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -996,6 +996,37 @@
         assertTrue(mSettings.getBuiltInZoomControls());
     }
 
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setAppCacheEnabled",
+            args = {}
+        )
+    })
+    public void testSetAppCacheEnabled() throws Exception {
+        // Tests that when AppCache is enabled and used, but the database path
+        // is not set or is set to an inaccessible path, the WebView does not crash.
+        startWebServer();
+        String url = mWebServer.getAppCacheUrl();
+        mSettings.setAppCacheEnabled(true);
+        mSettings.setJavaScriptEnabled(true);
+
+        mWebView.loadUrl(url);
+        new DelayedCheck(10000) {
+            protected boolean check() {
+                return mWebView.getTitle().equals("Done");
+            }
+        }.run();
+
+        mSettings.setAppCachePath("/data/foo");
+        mWebView.loadUrl(url);
+        new DelayedCheck(10000) {
+            protected boolean check() {
+                return mWebView.getTitle().equals("Done");
+            }
+        }.run();
+    }
+
     /**
      * Starts the internal web server. The server will be shut down automatically
      * during tearDown().