- add sources.
[platform/framework/web/crosswalk.git] / src / android_webview / javatests / src / org / chromium / android_webview / test / AwContentsTest.java
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.android_webview.test;
6
7 import android.graphics.Bitmap;
8 import android.graphics.BitmapFactory;
9 import android.os.Handler;
10 import android.os.Looper;
11 import android.os.Message;
12 import android.test.UiThreadTest;
13 import android.test.suitebuilder.annotation.LargeTest;
14 import android.test.suitebuilder.annotation.SmallTest;
15 import android.util.Pair;
16
17 import org.chromium.android_webview.AwContents;
18 import org.chromium.android_webview.AwSettings;
19 import org.chromium.android_webview.test.util.CommonResources;
20 import org.chromium.base.test.util.Feature;
21 import org.chromium.content.browser.test.util.CallbackHelper;
22 import org.chromium.net.test.util.TestWebServer;
23
24 import java.io.InputStream;
25 import java.net.URL;
26 import java.util.ArrayList;
27 import java.util.concurrent.Callable;
28 import java.util.concurrent.atomic.AtomicInteger;
29 import java.util.concurrent.Callable;
30 import java.util.concurrent.Semaphore;
31 import java.util.concurrent.TimeUnit;
32 import java.util.List;
33
34 /**
35  * AwContents tests.
36  */
37 public class AwContentsTest extends AwTestBase {
38     public static class OnDownloadStartHelper extends CallbackHelper {
39         String mUrl;
40         String mUserAgent;
41         String mContentDisposition;
42         String mMimeType;
43         long mContentLength;
44
45         public String getUrl() {
46             assert getCallCount() > 0;
47             return mUrl;
48         }
49
50         public String getUserAgent() {
51             assert getCallCount() > 0;
52             return mUserAgent;
53         }
54
55         public String getContentDisposition() {
56             assert getCallCount() > 0;
57             return mContentDisposition;
58         }
59
60        public String getMimeType() {
61             assert getCallCount() > 0;
62             return mMimeType;
63         }
64
65         public long getContentLength() {
66             assert getCallCount() > 0;
67             return mContentLength;
68         }
69
70         public void notifyCalled(String url, String userAgent, String contentDisposition,
71                 String mimeType, long contentLength) {
72             mUrl = url;
73             mUserAgent = userAgent;
74             mContentDisposition = contentDisposition;
75             mMimeType = mimeType;
76             mContentLength = contentLength;
77             notifyCalled();
78         }
79     }
80
81     private static class TestAwContentsClient
82             extends org.chromium.android_webview.test.TestAwContentsClient {
83
84         private OnDownloadStartHelper mOnDownloadStartHelper;
85
86         public TestAwContentsClient() {
87             mOnDownloadStartHelper = new OnDownloadStartHelper();
88         }
89
90         public OnDownloadStartHelper getOnDownloadStartHelper() {
91             return mOnDownloadStartHelper;
92         }
93
94         @Override
95         public void onDownloadStart(String url,
96                                     String userAgent,
97                                     String contentDisposition,
98                                     String mimeType,
99                                     long contentLength) {
100             getOnDownloadStartHelper().notifyCalled(url, userAgent, contentDisposition, mimeType,
101                     contentLength);
102         }
103     }
104
105     private TestAwContentsClient mContentsClient = new TestAwContentsClient();
106
107     @SmallTest
108     @Feature({"AndroidWebView"})
109     @UiThreadTest
110     public void testCreateDestroy() throws Throwable {
111         // NOTE this test runs on UI thread, so we cannot call any async methods.
112         createAwTestContainerView(mContentsClient).getAwContents().destroy();
113     }
114
115     @SmallTest
116     @Feature({"AndroidWebView"})
117     public void testCreateLoadPageDestroy() throws Throwable {
118         AwTestContainerView awTestContainerView =
119                 createAwTestContainerViewOnMainSync(mContentsClient);
120         loadUrlSync(awTestContainerView.getAwContents(),
121                 mContentsClient.getOnPageFinishedHelper(), CommonResources.ABOUT_HTML);
122         destroyAwContentsOnMainSync(awTestContainerView.getAwContents());
123         // It should be safe to call destroy multiple times.
124         destroyAwContentsOnMainSync(awTestContainerView.getAwContents());
125     }
126
127     @LargeTest
128     @Feature({"AndroidWebView"})
129     public void testCreateLoadDestroyManyTimes() throws Throwable {
130         final int CREATE_AND_DESTROY_REPEAT_COUNT = 10;
131         for (int i = 0; i < CREATE_AND_DESTROY_REPEAT_COUNT; ++i) {
132             AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
133             AwContents awContents = testView.getAwContents();
134
135             loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), "about:blank");
136             destroyAwContentsOnMainSync(awContents);
137         }
138     }
139
140     @LargeTest
141     @Feature({"AndroidWebView"})
142     public void testCreateLoadDestroyManyAtOnce() throws Throwable {
143         final int CREATE_AND_DESTROY_REPEAT_COUNT = 10;
144         AwTestContainerView views[] = new AwTestContainerView[CREATE_AND_DESTROY_REPEAT_COUNT];
145
146         for (int i = 0; i < views.length; ++i) {
147             views[i] = createAwTestContainerViewOnMainSync(mContentsClient);
148             loadUrlSync(views[i].getAwContents(), mContentsClient.getOnPageFinishedHelper(),
149                     "about:blank");
150         }
151
152         for (int i = 0; i < views.length; ++i) {
153             destroyAwContentsOnMainSync(views[i].getAwContents());
154             views[i] = null;
155         }
156     }
157
158     public void testCreateAndGcManyTimes() throws Throwable {
159         final int CONCURRENT_INSTANCES = 4;
160         final int REPETITIONS = 16;
161         // The system retains a strong ref to the last focused view (in InputMethodManager)
162         // so allow for 1 'leaked' instance.
163         final int MAX_IDLE_INSTANCES = 1;
164
165         System.gc();
166
167         assertTrue(pollOnUiThread(new Callable<Boolean>() {
168             @Override
169             public Boolean call() {
170                 return AwContents.getNativeInstanceCount() <= MAX_IDLE_INSTANCES;
171             }
172         }));
173         for (int i = 0; i < REPETITIONS; ++i) {
174             for (int j = 0; j < CONCURRENT_INSTANCES; ++j) {
175                 AwTestContainerView view = createAwTestContainerViewOnMainSync(mContentsClient);
176                 loadUrlAsync(view.getAwContents(), "about:blank");
177             }
178             assertTrue(AwContents.getNativeInstanceCount() >= CONCURRENT_INSTANCES);
179             assertTrue(AwContents.getNativeInstanceCount() <= (i + 1) * CONCURRENT_INSTANCES);
180             runTestOnUiThread(new Runnable() {
181                 @Override
182                 public void run() {
183                     getActivity().removeAllViews();
184                 }
185             });
186         }
187
188         System.gc();
189
190         assertTrue(pollOnUiThread(new Callable<Boolean>() {
191             @Override
192             public Boolean call() {
193                 return AwContents.getNativeInstanceCount() <= MAX_IDLE_INSTANCES;
194             }
195         }));
196     }
197
198     private int callDocumentHasImagesSync(final AwContents awContents)
199             throws Throwable, InterruptedException {
200         // Set up a container to hold the result object and a semaphore to
201         // make the test wait for the result.
202         final AtomicInteger val = new AtomicInteger();
203         final Semaphore s = new Semaphore(0);
204         final Message msg = Message.obtain(new Handler(Looper.getMainLooper()) {
205             @Override
206             public void handleMessage(Message msg) {
207                 val.set(msg.arg1);
208                 s.release();
209             }
210         });
211         runTestOnUiThread(new Runnable() {
212             @Override
213             public void run() {
214               awContents.documentHasImages(msg);
215             }
216         });
217         assertTrue(s.tryAcquire(WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS));
218         int result = val.get();
219         return result;
220     }
221
222     @SmallTest
223     @Feature({"AndroidWebView"})
224     public void testDocumentHasImages() throws Throwable {
225         AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
226         AwContents awContents = testView.getAwContents();
227
228         final CallbackHelper loadHelper = mContentsClient.getOnPageFinishedHelper();
229
230         final String mime = "text/html";
231         final String emptyDoc = "<head/><body/>";
232         final String imageDoc = "<head/><body><img/><img/></body>";
233
234         // Make sure a document that does not have images returns 0
235         loadDataSync(awContents, loadHelper, emptyDoc, mime, false);
236         int result = callDocumentHasImagesSync(awContents);
237         assertEquals(0, result);
238
239         // Make sure a document that does have images returns 1
240         loadDataSync(awContents, loadHelper, imageDoc, mime, false);
241         result = callDocumentHasImagesSync(awContents);
242         assertEquals(1, result);
243     }
244
245     @SmallTest
246     @Feature({"AndroidWebView"})
247     public void testClearCacheMemoryAndDisk() throws Throwable {
248         final AwTestContainerView testContainer =
249                 createAwTestContainerViewOnMainSync(mContentsClient);
250         final AwContents awContents = testContainer.getAwContents();
251
252         TestWebServer webServer = null;
253         try {
254             webServer = new TestWebServer(false);
255             final String pagePath = "/clear_cache_test.html";
256             List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>();
257             // Set Cache-Control headers to cache this request. One century should be long enough.
258             headers.add(Pair.create("Cache-Control", "max-age=3153600000"));
259             headers.add(Pair.create("Last-Modified", "Wed, 3 Oct 2012 00:00:00 GMT"));
260             final String pageUrl = webServer.setResponse(
261                     pagePath, "<html><body>foo</body></html>", headers);
262
263             // First load to populate cache.
264             clearCacheOnUiThread(awContents, true);
265             loadUrlSync(awContents,
266                         mContentsClient.getOnPageFinishedHelper(),
267                         pageUrl);
268             assertEquals(1, webServer.getRequestCount(pagePath));
269
270             // Load about:blank so next load is not treated as reload by webkit and force
271             // revalidate with the server.
272             loadUrlSync(awContents,
273                         mContentsClient.getOnPageFinishedHelper(),
274                         "about:blank");
275
276             // No clearCache call, so should be loaded from cache.
277             loadUrlSync(awContents,
278                         mContentsClient.getOnPageFinishedHelper(),
279                         pageUrl);
280             assertEquals(1, webServer.getRequestCount(pagePath));
281
282             // Same as above.
283             loadUrlSync(awContents,
284                         mContentsClient.getOnPageFinishedHelper(),
285                         "about:blank");
286
287             // Clear cache, so should hit server again.
288             clearCacheOnUiThread(awContents, true);
289             loadUrlSync(awContents,
290                         mContentsClient.getOnPageFinishedHelper(),
291                         pageUrl);
292             assertEquals(2, webServer.getRequestCount(pagePath));
293         } finally {
294             if (webServer != null) webServer.shutdown();
295         }
296     }
297
298     @SmallTest
299     @Feature({"AndroidWebView"})
300     public void testClearCacheInQuickSuccession() throws Throwable {
301         final AwTestContainerView testContainer =
302                 createAwTestContainerViewOnMainSync(new TestAwContentsClient());
303         final AwContents awContents = testContainer.getAwContents();
304
305         runTestOnUiThread(new Runnable() {
306             @Override
307             public void run() {
308               for (int i = 0; i < 10; ++i) {
309                   awContents.clearCache(true);
310               }
311             }
312         });
313     }
314
315     private static final long TEST_TIMEOUT = 20000L;
316     private static final int CHECK_INTERVAL = 100;
317
318     @SmallTest
319     @Feature({"AndroidWebView"})
320     public void testGetFavicon() throws Throwable {
321         final AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
322         final AwContents awContents = testView.getAwContents();
323
324         TestWebServer webServer = null;
325         try {
326             webServer = new TestWebServer(false);
327
328             final String faviconUrl = webServer.setResponseBase64(
329                     "/" + CommonResources.FAVICON_FILENAME, CommonResources.FAVICON_DATA_BASE64,
330                     CommonResources.getImagePngHeaders(false));
331             final String pageUrl = webServer.setResponse("/favicon.html",
332                     CommonResources.FAVICON_STATIC_HTML, null);
333
334             // The getFavicon will return the right icon a certain time after
335             // the page load completes which makes it slightly hard to test.
336             final Bitmap defaultFavicon = awContents.getFavicon();
337
338             getAwSettingsOnUiThread(awContents).setImagesEnabled(true);
339             loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
340
341             assertTrue(pollOnUiThread(new Callable<Boolean>() {
342                 @Override
343                 public Boolean call() {
344                     return awContents.getFavicon() != null &&
345                         !awContents.getFavicon().sameAs(defaultFavicon);
346                 }
347             }));
348
349             final Object originalFaviconSource = (new URL(faviconUrl)).getContent();
350             final Bitmap originalFavicon =
351                 BitmapFactory.decodeStream((InputStream)originalFaviconSource);
352             assertNotNull(originalFavicon);
353
354             assertTrue(awContents.getFavicon().sameAs(originalFavicon));
355
356         } finally {
357             if (webServer != null) webServer.shutdown();
358         }
359     }
360
361     @Feature({"AndroidWebView", "Downloads"})
362     @SmallTest
363     public void testDownload() throws Throwable {
364         AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
365         AwContents awContents = testView.getAwContents();
366
367         final String data = "download data";
368         final String contentDisposition = "attachment;filename=\"download.txt\"";
369         final String mimeType = "text/plain";
370
371         List<Pair<String, String>> downloadHeaders = new ArrayList<Pair<String, String>>();
372         downloadHeaders.add(Pair.create("Content-Disposition", contentDisposition));
373         downloadHeaders.add(Pair.create("Content-Type", mimeType));
374         downloadHeaders.add(Pair.create("Content-Length", Integer.toString(data.length())));
375
376         TestWebServer webServer = null;
377         try {
378             webServer = new TestWebServer(false);
379             final String pageUrl = webServer.setResponse(
380                     "/download.txt", data, downloadHeaders);
381             final OnDownloadStartHelper downloadStartHelper =
382                 mContentsClient.getOnDownloadStartHelper();
383             final int callCount = downloadStartHelper.getCallCount();
384             loadUrlAsync(awContents, pageUrl);
385             downloadStartHelper.waitForCallback(callCount);
386
387             assertEquals(pageUrl, downloadStartHelper.getUrl());
388             assertEquals(contentDisposition, downloadStartHelper.getContentDisposition());
389             assertEquals(mimeType, downloadStartHelper.getMimeType());
390             assertEquals(data.length(), downloadStartHelper.getContentLength());
391         } finally {
392             if (webServer != null) webServer.shutdown();
393         }
394     }
395
396     @Feature({"AndroidWebView", "setNetworkAvailable"})
397     @SmallTest
398     public void testSetNetworkAvailable() throws Throwable {
399         AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
400         AwContents awContents = testView.getAwContents();
401         String SCRIPT = "navigator.onLine";
402
403         enableJavaScriptOnUiThread(awContents);
404         loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), "about:blank");
405
406         // Default to "online".
407         assertEquals("true", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
408               SCRIPT));
409
410         // Forcing "offline".
411         awContents.setNetworkAvailable(false);
412         assertEquals("false", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
413               SCRIPT));
414
415         // Forcing "online".
416         awContents.setNetworkAvailable(true);
417         assertEquals("true", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
418               SCRIPT));
419     }
420
421
422     static class JavaScriptObject {
423         private CallbackHelper mCallbackHelper;
424         public JavaScriptObject(CallbackHelper callbackHelper) {
425             mCallbackHelper = callbackHelper;
426         }
427
428         public void run() {
429             mCallbackHelper.notifyCalled();
430         }
431     };
432
433     @Feature({"AndroidWebView", "JavaBridge"})
434     @SmallTest
435     public void testJavaBridge() throws Throwable {
436         final AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
437         final CallbackHelper callback = new CallbackHelper();
438
439         runTestOnUiThread(new Runnable() {
440             @Override
441             public void run() {
442                 AwContents awContents = testView.getAwContents();
443                 AwSettings awSettings = awContents.getSettings();
444                 awSettings.setJavaScriptEnabled(true);
445                 awContents.addPossiblyUnsafeJavascriptInterface(
446                         new JavaScriptObject(callback), "bridge", null);
447                 awContents.evaluateJavaScriptEvenIfNotYetNavigated(
448                         "javascript:window.bridge.run();");
449             }
450         });
451         callback.waitForCallback(0, 1, 20, TimeUnit.SECONDS);
452     }
453 }