607ac690e9bd74aa6faf8f7ca0a9ab94ebdc2409
[platform/framework/web/crosswalk.git] / src / xwalk / test / android / util / runtime_client / src / org / xwalk / test / util / RuntimeClientApiTestBase.java
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Copyright (c) 2013 Intel Corporation. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 package org.xwalk.test.util;
7
8 import android.annotation.TargetApi;
9 import android.app.Activity;
10 import android.content.Context;
11 import android.content.Intent;
12 import android.os.Build;
13 import android.test.ActivityInstrumentationTestCase2;
14
15 import java.io.IOException;
16 import java.lang.Process;
17 import java.lang.Runtime;
18 import java.lang.StringBuffer;
19 import java.util.concurrent.atomic.AtomicReference;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
22 import java.util.Timer;
23 import java.util.TimerTask;
24
25 import org.chromium.content.browser.test.util.Criteria;
26 import org.chromium.content.browser.test.util.CriteriaHelper;
27 import org.chromium.net.test.util.TestWebServer;
28 /**
29  * Test helper for Runtime client APIs.
30  */
31 public class RuntimeClientApiTestBase<T extends Activity> {
32     private XWalkRuntimeClientTestUtilBase mTestUtil;
33     private ActivityInstrumentationTestCase2<T> mTestCase;
34     private Timer mTimer = new Timer();
35     private String mSocketName;
36     private String mUrl = "file:///android_asset/index.html";
37     enum Relation {
38         EQUAL,
39         GREATERTHAN,
40         LESSSTHAN,
41         NONE
42     }
43
44     public RuntimeClientApiTestBase(XWalkRuntimeClientTestUtilBase testUtil,
45             ActivityInstrumentationTestCase2<T> testCase) {
46         mTestUtil = testUtil;
47         mTestCase = testCase;
48     }
49
50     public String getTitleOnUiThread() {
51         mTestCase.getInstrumentation().waitForIdleSync();
52         final AtomicReference<String> title = new AtomicReference<String>();
53         mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
54             @Override
55             public void run() {
56                 title.set(mTestUtil.getTestedView().getTitleForTest());
57             }
58         });
59         return title.get();
60     }
61
62     public void compareTitle(String prevTitle, String title, String msg, Relation relation) {
63         if (prevTitle == null) prevTitle = "";
64         if (title == null) title = "";
65
66         switch (relation) {
67             case EQUAL:
68                 mTestCase.assertTrue(msg, title.equals(prevTitle));
69                 break;
70             case GREATERTHAN:
71                 mTestCase.assertTrue(msg, title.compareTo(prevTitle) > 0);
72                 break;
73             default:
74                 break;
75         }
76     }
77
78     public void waitForTimerFinish(int timer) throws Throwable {
79         Object notify = new Object();
80         synchronized (notify) {
81             NotifyTask testTask = new NotifyTask(notify);
82             mTimer.schedule(testTask, timer);
83             notify.wait();
84         }
85     }
86
87     public class NotifyTask extends TimerTask {
88         private Object mObj;
89
90         public NotifyTask(Object obj) {
91             super();
92             mObj = obj;
93         }
94
95         @Override
96         public void run() {
97             synchronized (mObj) {
98                 mObj.notify();
99             }
100         }
101     }
102
103     public void sendBroadCast(Activity activity, Context context, String extra) throws Throwable {
104         mSocketName = activity.getPackageName();
105         Intent intent = new Intent().setAction("org.xwalk.intent").putExtra("remotedebugging", extra);
106         context.sendBroadcast(intent);
107     }
108
109     public int getSocketNameIndex() {
110         try {
111             // Try to find the abstract socket name opened for remote debugging
112             // from the output of 'cat /proc/net/unix' command. Actually, the
113             // best way to test DevToolsServer is to connect the server socket
114             // by android.net.LocalSocket and communicate with it (e.g. send
115             // http request to query all inspectable pages). However, since
116             // the socket of devtools server is enforced to be connected only
117             // if the user is authenticated. On a non-rooted device, it only
118             // authenticates 'shell' user which is reserved for adb connecction.
119             Process process = Runtime.getRuntime().exec("cat /proc/net/unix");
120
121             final int BUFFER_SIZE = 1024;
122             byte[] bytes = new byte[BUFFER_SIZE];
123             StringBuffer buffer = new StringBuffer(4 * BUFFER_SIZE);
124
125             int bytesReceived = process.getInputStream().read(bytes, 0, BUFFER_SIZE);
126             while (bytesReceived > 0) {
127                 String tmp = new String(bytes, 0, bytesReceived);
128                 buffer.append(tmp);
129                 bytesReceived = process.getInputStream().read(bytes, 0, BUFFER_SIZE);
130             }
131
132             process.destroy();
133
134             String contents = new String(buffer);
135             int index = contents.indexOf(mSocketName + "_devtools_remote");
136             return index;
137         } catch (IOException e) {
138             mTestCase.fail("error occurs in testDevTools: " + e);
139             return 0;
140         }
141     }
142
143     // For loadAppFromUrl.
144     public void testLoadAppFromUrl() throws Throwable {
145         final String expectedTitle = "Crosswalk Sample Application";
146
147         mTestUtil.loadUrlSync("file:///android_asset/index.html");
148         mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
149             @Override
150             public void run() {
151                 String title = mTestUtil.getTestedView().getTitleForTest();
152                 mTestCase.assertEquals(expectedTitle, title);
153             }
154         });
155     }
156
157     // For loadAppFromManifest.
158     public void testLoadAppFromManifest() throws Throwable {
159         final String expectedTitle = "Crosswalk Sample Application";
160
161         mTestUtil.loadManifestSync("file:///android_asset/manifest.json");
162
163         mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
164             @Override
165             public void run() {
166                 String title = mTestUtil.getTestedView().getTitleForTest();
167                 mTestCase.assertEquals(expectedTitle, title);
168             }
169         });
170     }
171
172     // For testCSP.
173     public void testCSP() throws Throwable {
174         final String originalTitle = "Original Title";
175         final String newTitle = "New Title";
176         final String host = mTestCase.getActivity().getPackageName();
177         mTestUtil.loadManifestSync("file:///android_asset/www/manifest_self.json");
178         mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
179             @Override
180             public void run() {
181                 String title = mTestUtil.getTestedView().getTitleForTest();
182                 mTestCase.assertEquals(originalTitle, title);
183             }
184         });
185
186         mTestUtil.loadManifestSync("app://" + host + "/manifest_self.json");
187         Thread.sleep(2000);
188         mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
189             @Override
190             public void run() {
191                 String title = mTestUtil.getTestedView().getTitleForTest();
192                 mTestCase.assertEquals(originalTitle, title);
193             }
194         });
195
196         mTestUtil.loadManifestSync("file:///android_asset/www/manifest_inline_script.json");
197         mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
198             @Override
199             public void run() {
200                 String title = mTestUtil.getTestedView().getTitleForTest();
201                 mTestCase.assertEquals(newTitle, title);
202             }
203         });
204
205         mTestUtil.loadManifestSync("app://" + host + "/manifest_inline_script.json");
206         Thread.sleep(2000);
207         mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
208             @Override
209             public void run() {
210                 String title = mTestUtil.getTestedView().getTitleForTest();
211                 mTestCase.assertEquals(newTitle, title);
212             }
213         });
214     }
215
216     // For Cross-Origin XHR.
217     public void testCrossOriginXhr() throws Throwable {
218         TestWebServer webServer = null;
219         try {
220             // The server will be accessed by XMLHttpRequest from js.
221             webServer = new TestWebServer(false);
222             final String path = "/cross_origin_xhr_test.html";
223             final String responseStr = "Cross-Origin XHR";
224             final String url = webServer.setResponse(path, responseStr, null);
225             mTestCase.assertEquals("http://localhost:4444/cross_origin_xhr_test.html", url);
226
227             // The original title of the cross_origin.html.
228             final String originalTitle = "Original Title";
229
230             // Test without the xwalk_hosts member.
231             mTestUtil.loadManifestSync("file:///android_asset/www/manifest_without_xwalk_hosts.json");
232             Thread.sleep(1000);
233             mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
234                 @Override
235                 public void run() {
236                     String title = mTestUtil.getTestedView().getTitleForTest();
237                     // XHR in page should be failed, and the title should be "Original Title".
238                     mTestCase.assertEquals(originalTitle, title);
239                 }
240             });
241
242             // Test with the manifest which has a xwalk_host member.
243             mTestUtil.loadManifestSync("file:///android_asset/www/manifest_xwalk_hosts.json");
244             Thread.sleep(1000);
245             mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
246                 @Override
247                 public void run() {
248                     String title = mTestUtil.getTestedView().getTitleForTest();
249                     // XHR in page should be success, and the title should be "Cross-Origin XHR".
250                     mTestCase.assertEquals(responseStr, title);
251                 }
252             });
253
254             // Retry with app:// scheme.
255             final String host = mTestCase.getActivity().getPackageName();
256             mTestUtil.loadManifestSync("app://" + host + "/manifest_without_xwalk_hosts.json");
257             Thread.sleep(1000);
258             mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
259                 @Override
260                 public void run() {
261                     String title = mTestUtil.getTestedView().getTitleForTest();
262                     mTestCase.assertEquals(originalTitle, title);
263                 }
264             });
265
266             mTestUtil.loadManifestSync("app://" + host + "/manifest_xwalk_hosts.json");
267             Thread.sleep(1000);
268             mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
269                 @Override
270                 public void run() {
271                     String title = mTestUtil.getTestedView().getTitleForTest();
272                     mTestCase.assertEquals(responseStr, title);
273                 }
274             });
275         } finally {
276             if (webServer != null) webServer.shutdown();
277         }
278     }
279
280     // For internal extension implementation of Contacts.
281     public void testContacts() throws Throwable {
282         String title = mTestUtil.loadAssetFileAndWaitForTitle("contacts.html");
283         mTestCase.assertEquals("Pass", title);
284     }
285
286     // For internal extension implementation of DeviceCapabilities.
287     public void testDeviceCapabilities() throws Throwable {
288         String title = mTestUtil.loadAssetFileAndWaitForTitle("device_capabilities.html");
289         mTestCase.assertEquals("Pass", title);
290     }
291
292     // For internal extension implementation of ScreenOrientation.
293     public void testScreenOrientation() throws Throwable {
294         String title = mTestUtil.loadAssetFileAndWaitForTitle("screen_orientation.html");
295         mTestCase.assertEquals("Pass", title);
296     }
297
298     // For internal extension implementation of Presentation.
299     public void testPresentationDisplayAvailable() throws Throwable {
300         String title = mTestUtil.loadAssetFileAndWaitForTitle("displayAvailableTest.html");
301         if (isSecondaryDisplayAvailable()) {
302             mTestCase.assertEquals("Available", title);
303         } else {
304             mTestCase.assertEquals("Unavailable", title);
305         }
306     }
307
308     // For external extension mechanism: async mode.
309     public void testExternalExtensionAsync() throws Throwable {
310         String title = mTestUtil.loadAssetFileAndWaitForTitle("echo.html");
311         mTestCase.assertEquals("Pass", title);
312     }
313
314     // For external extension mechanism: sync mode.
315     public void testExternalExtensionSync() throws Throwable {
316         mTestUtil.loadAssetFile("echoSync.html");
317         mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
318             @Override
319             public void run() {
320                 String title = mTestUtil.getTestedView().getTitleForTest();
321                 mTestCase.assertEquals("Pass", title);
322             }
323         });
324     }
325
326     // For internal extension implementation of Messaging.
327     public void testMessaging() throws Throwable {
328         String title = mTestUtil.loadAssetFileAndWaitForTitle("messaging_mini.html");
329         mTestCase.assertEquals("Pass", title);
330     }
331     // For onPause, onResume.
332     public void testPauseAndResume() throws Throwable {
333         String title = "";
334         String prevTitle = "";
335         String nextTitle = "";
336         String msg = "";
337
338         title = mTestUtil.loadAssetFileAndWaitForTitle("timer.html");
339         mTestCase.assertNotNull(title);
340
341         mTestUtil.onPause();
342         msg = "The second title should be equal to the first title.";
343         // wait for the pause is finished.
344         waitForTimerFinish(5000);
345         prevTitle = getTitleOnUiThread();
346         waitForTimerFinish(200);
347         nextTitle = getTitleOnUiThread();
348         compareTitle(prevTitle, nextTitle, msg, Relation.EQUAL);
349
350         mTestUtil.onResume();
351         msg = "The second title should be greater than the first title.";
352         // wait for the resume is finished.
353         waitForTimerFinish(5000);
354         prevTitle = getTitleOnUiThread();
355         waitForTimerFinish(200);
356         nextTitle = getTitleOnUiThread();
357         compareTitle(prevTitle, nextTitle, msg, Relation.GREATERTHAN);
358     }
359
360     // For enable the remote debugging.
361     public void testEnableRemoteDebugging(Activity activity, Context context) throws Throwable {
362         sendBroadCast(activity, context, "true");
363         mTestUtil.loadUrlSync(mUrl);
364         int index = getSocketNameIndex();
365         mTestCase.assertTrue (index != -1);
366         sendBroadCast(activity, context, "false");
367     }
368
369     //  For disable the remote debugging.
370     public void testDisableRemoteDebugging(Activity activity, Context context) throws Throwable {
371         sendBroadCast(activity, context, "false");
372         mTestUtil.loadUrlSync(mUrl);
373         int index = getSocketNameIndex();
374         mTestCase.assertTrue (index < 0);
375     }
376
377     // For getVersion.
378     public void testGetVersion() throws Throwable {
379         String version = mTestUtil.getTestedView().getVersion();
380
381         Pattern pattern = Pattern.compile("\\d+\\.\\d+\\.\\d+\\.\\d+");
382         Matcher matcher = pattern.matcher(version);
383         mTestCase.assertTrue("The version is invalid.", matcher.find());
384     }
385
386     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
387     private boolean isSecondaryDisplayAvailable() {
388         String value;
389         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
390             value =  android.provider.Settings.Global.getString(
391                              mTestCase.getActivity().getContentResolver(),
392                              "overlay_display_devices");
393         } else {
394             value = null;
395         }
396
397         if (value != null && value.length() > 0) {
398             return true;
399         }
400         return false;
401     }
402 }