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.
6 package org.xwalk.test.util;
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;
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;
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;
29 * Test helper for Runtime client APIs.
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";
44 public RuntimeClientApiTestBase(XWalkRuntimeClientTestUtilBase testUtil,
45 ActivityInstrumentationTestCase2<T> testCase) {
50 public String getTitleOnUiThread() {
51 mTestCase.getInstrumentation().waitForIdleSync();
52 final AtomicReference<String> title = new AtomicReference<String>();
53 mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
56 title.set(mTestUtil.getTestedView().getTitleForTest());
62 public void compareTitle(String prevTitle, String title, String msg, Relation relation) {
63 if (prevTitle == null) prevTitle = "";
64 if (title == null) title = "";
68 mTestCase.assertTrue(msg, title.equals(prevTitle));
71 mTestCase.assertTrue(msg, title.compareTo(prevTitle) > 0);
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);
87 public class NotifyTask extends TimerTask {
90 public NotifyTask(Object obj) {
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);
109 public int getSocketNameIndex() {
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");
121 final int BUFFER_SIZE = 1024;
122 byte[] bytes = new byte[BUFFER_SIZE];
123 StringBuffer buffer = new StringBuffer(4 * BUFFER_SIZE);
125 int bytesReceived = process.getInputStream().read(bytes, 0, BUFFER_SIZE);
126 while (bytesReceived > 0) {
127 String tmp = new String(bytes, 0, bytesReceived);
129 bytesReceived = process.getInputStream().read(bytes, 0, BUFFER_SIZE);
134 String contents = new String(buffer);
135 int index = contents.indexOf(mSocketName + "_devtools_remote");
137 } catch (IOException e) {
138 mTestCase.fail("error occurs in testDevTools: " + e);
143 // For loadAppFromUrl.
144 public void testLoadAppFromUrl() throws Throwable {
145 final String expectedTitle = "Crosswalk Sample Application";
147 mTestUtil.loadUrlSync("file:///android_asset/index.html");
148 mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
151 String title = mTestUtil.getTestedView().getTitleForTest();
152 mTestCase.assertEquals(expectedTitle, title);
157 // For loadAppFromManifest.
158 public void testLoadAppFromManifest() throws Throwable {
159 final String expectedTitle = "Crosswalk Sample Application";
161 mTestUtil.loadManifestSync("file:///android_asset/manifest.json");
163 mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
166 String title = mTestUtil.getTestedView().getTitleForTest();
167 mTestCase.assertEquals(expectedTitle, title);
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() {
181 String title = mTestUtil.getTestedView().getTitleForTest();
182 mTestCase.assertEquals(originalTitle, title);
186 mTestUtil.loadManifestSync("app://" + host + "/manifest_self.json");
188 mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
191 String title = mTestUtil.getTestedView().getTitleForTest();
192 mTestCase.assertEquals(originalTitle, title);
196 mTestUtil.loadManifestSync("file:///android_asset/www/manifest_inline_script.json");
197 mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
200 String title = mTestUtil.getTestedView().getTitleForTest();
201 mTestCase.assertEquals(newTitle, title);
205 mTestUtil.loadManifestSync("app://" + host + "/manifest_inline_script.json");
207 mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
210 String title = mTestUtil.getTestedView().getTitleForTest();
211 mTestCase.assertEquals(newTitle, title);
216 // For Cross-Origin XHR.
217 public void testCrossOriginXhr() throws Throwable {
218 TestWebServer webServer = null;
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);
227 // The original title of the cross_origin.html.
228 final String originalTitle = "Original Title";
230 // Test without the xwalk_hosts member.
231 mTestUtil.loadManifestSync("file:///android_asset/www/manifest_without_xwalk_hosts.json");
233 mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
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);
242 // Test with the manifest which has a xwalk_host member.
243 mTestUtil.loadManifestSync("file:///android_asset/www/manifest_xwalk_hosts.json");
245 mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
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);
254 // Retry with app:// scheme.
255 final String host = mTestCase.getActivity().getPackageName();
256 mTestUtil.loadManifestSync("app://" + host + "/manifest_without_xwalk_hosts.json");
258 mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
261 String title = mTestUtil.getTestedView().getTitleForTest();
262 mTestCase.assertEquals(originalTitle, title);
266 mTestUtil.loadManifestSync("app://" + host + "/manifest_xwalk_hosts.json");
268 mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
271 String title = mTestUtil.getTestedView().getTitleForTest();
272 mTestCase.assertEquals(responseStr, title);
276 if (webServer != null) webServer.shutdown();
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);
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);
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);
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);
304 mTestCase.assertEquals("Unavailable", title);
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);
314 // For external extension mechanism: sync mode.
315 public void testExternalExtensionSync() throws Throwable {
316 mTestUtil.loadAssetFile("echoSync.html");
317 mTestCase.getInstrumentation().runOnMainSync(new Runnable() {
320 String title = mTestUtil.getTestedView().getTitleForTest();
321 mTestCase.assertEquals("Pass", title);
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);
331 // For onPause, onResume.
332 public void testPauseAndResume() throws Throwable {
334 String prevTitle = "";
335 String nextTitle = "";
338 title = mTestUtil.loadAssetFileAndWaitForTitle("timer.html");
339 mTestCase.assertNotNull(title);
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);
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);
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");
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);
378 public void testGetVersion() throws Throwable {
379 String version = mTestUtil.getTestedView().getVersion();
381 Pattern pattern = Pattern.compile("\\d+\\.\\d+\\.\\d+\\.\\d+");
382 Matcher matcher = pattern.matcher(version);
383 mTestCase.assertTrue("The version is invalid.", matcher.find());
386 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
387 private boolean isSecondaryDisplayAvailable() {
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");
397 if (value != null && value.length() > 0) {