diff options
author | Steve Block <steveblock@google.com> | 2011-11-07 07:37:53 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-11-07 07:37:53 -0800 |
commit | 2abb2abea05fddd9ec2a1d6c831f1e62c37c7fa3 (patch) | |
tree | 408e18111c17e23c20786b07ba750937b7cb8633 /tests | |
parent | 9039e43ab1edac9e81f1308a7f3380cff4e9fa27 (diff) | |
parent | 2dc592a4ae56c1a6d5d5619811cdf8817f342e8d (diff) | |
download | frameworks_base-2abb2abea05fddd9ec2a1d6c831f1e62c37c7fa3.zip frameworks_base-2abb2abea05fddd9ec2a1d6c831f1e62c37c7fa3.tar.gz frameworks_base-2abb2abea05fddd9ec2a1d6c831f1e62c37c7fa3.tar.bz2 |
Merge changes If1bdd547,Ie43c16ae,I62b45f8b,I32b7fdc0
* changes:
Add tests for the fields of objects in the WebView's Java Bridge.
Adds a set of basic test for WebView's Java Bridge
Add tests for returning values from WebView's Java Bridge
Adds a new test for type coercion in WebView's Java Bridge
Diffstat (limited to 'tests')
9 files changed, 1531 insertions, 0 deletions
diff --git a/tests/WebViewTests/Android.mk b/tests/WebViewTests/Android.mk new file mode 100644 index 0000000..b118845 --- /dev/null +++ b/tests/WebViewTests/Android.mk @@ -0,0 +1,27 @@ +# +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_JAVA_LIBRARIES := android.test.runner + +LOCAL_PACKAGE_NAME := WebViewTests + +include $(BUILD_PACKAGE) diff --git a/tests/WebViewTests/AndroidManifest.xml b/tests/WebViewTests/AndroidManifest.xml new file mode 100644 index 0000000..8b080c1 --- /dev/null +++ b/tests/WebViewTests/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2011 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.webviewtests"> + <application> + <uses-library android:name="android.test.runner" /> + <activity android:name="WebViewStubActivity" android:label="WebViewStubActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.TEST" /> + </intent-filter> + </activity> + </application> + + <instrumentation android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.webviewtests" + android:label="Tests for android.webkit.WebView" /> +</manifest> diff --git a/tests/WebViewTests/res/layout/webview_layout.xml b/tests/WebViewTests/res/layout/webview_layout.xml new file mode 100644 index 0000000..d266d21 --- /dev/null +++ b/tests/WebViewTests/res/layout/webview_layout.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <WebView android:id="@+id/web_page" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout> diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeBasicsTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeBasicsTest.java new file mode 100644 index 0000000..143221d --- /dev/null +++ b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeBasicsTest.java @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Part of the test suite for the WebView's Java Bridge. Tests a number of features including ... + * - The type of injected objects + * - The type of their methods + * - Replacing objects + * - Removing objects + * - Access control + * - Calling methods on returned objects + * - Multiply injected objects + * - Threading + * - Inheritance + * + * To run this test ... + * adb shell am instrument -w -e class com.android.webviewtests.JavaBridgeBasicsTest \ + * com.android.webviewtests/android.test.InstrumentationTestRunner + */ + +package com.android.webviewtests; + +public class JavaBridgeBasicsTest extends JavaBridgeTestBase { + private class TestController extends Controller { + private int mIntValue; + private long mLongValue; + private String mStringValue; + + public synchronized void setIntValue(int x) { + mIntValue = x; + notifyResultIsReady(); + } + public synchronized void setLongValue(long x) { + mLongValue = x; + notifyResultIsReady(); + } + public synchronized void setStringValue(String x) { + mStringValue = x; + notifyResultIsReady(); + } + + public synchronized int waitForIntValue() { + waitForResult(); + return mIntValue; + } + public synchronized long waitForLongValue() { + waitForResult(); + return mLongValue; + } + public synchronized String waitForStringValue() { + waitForResult(); + return mStringValue; + } + } + + TestController mTestController; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mTestController = new TestController(); + setUpWebView(mTestController, "testController"); + } + + // Note that this requires that we can pass a JavaScript string to Java. + protected String executeJavaScriptAndGetStringResult(String script) throws Throwable { + executeJavaScript("testController.setStringValue(" + script + ");"); + return mTestController.waitForStringValue(); + } + + protected void injectObjectAndReload(final Object object, final String name) throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + getWebView().addJavascriptInterface(object, name); + getWebView().reload(); + } + }); + mWebViewClient.waitForOnPageFinished(); + } + + public void testTypeOfInjectedObject() throws Throwable { + assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController")); + } + + public void testAdditionNotReflectedUntilReload() throws Throwable { + assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject")); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + getWebView().addJavascriptInterface(new Object(), "testObject"); + } + }); + assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject")); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + getWebView().reload(); + } + }); + mWebViewClient.waitForOnPageFinished(); + assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject")); + } + + public void testRemovalNotReflectedUntilReload() throws Throwable { + injectObjectAndReload(new Object(), "testObject"); + assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject")); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + getWebView().removeJavascriptInterface("testObject"); + } + }); + assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject")); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + getWebView().reload(); + } + }); + mWebViewClient.waitForOnPageFinished(); + assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject")); + } + + public void testRemoveObjectNotAdded() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + getWebView().removeJavascriptInterface("foo"); + getWebView().reload(); + } + }); + mWebViewClient.waitForOnPageFinished(); + assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof foo")); + } + + public void testTypeOfMethod() throws Throwable { + assertEquals("function", + executeJavaScriptAndGetStringResult("typeof testController.setStringValue")); + } + + public void testPrivateMethodNotExposed() throws Throwable { + injectObjectAndReload(new Object() { + private void method() {} + }, "testObject"); + assertEquals("undefined", + executeJavaScriptAndGetStringResult("typeof testObject.method")); + } + + public void testReplaceInjectedObject() throws Throwable { + injectObjectAndReload(new Object() { + public void method() { mTestController.setStringValue("object 1"); } + }, "testObject"); + executeJavaScript("testObject.method()"); + assertEquals("object 1", mTestController.waitForStringValue()); + + injectObjectAndReload(new Object() { + public void method() { mTestController.setStringValue("object 2"); } + }, "testObject"); + executeJavaScript("testObject.method()"); + assertEquals("object 2", mTestController.waitForStringValue()); + } + + public void testInjectNullObjectIsIgnored() throws Throwable { + injectObjectAndReload(null, "testObject"); + assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject")); + } + + public void testReplaceInjectedObjectWithNullObjectIsIgnored() throws Throwable { + injectObjectAndReload(new Object(), "testObject"); + assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject")); + injectObjectAndReload(null, "testObject"); + assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject")); + } + + public void testCallOverloadedMethodWithDifferentNumberOfArguments() throws Throwable { + injectObjectAndReload(new Object() { + public void method() { mTestController.setStringValue("0 args"); } + public void method(int x) { mTestController.setStringValue("1 arg"); } + public void method(int x, int y) { mTestController.setStringValue("2 args"); } + }, "testObject"); + executeJavaScript("testObject.method()"); + assertEquals("0 args", mTestController.waitForStringValue()); + executeJavaScript("testObject.method(42)"); + assertEquals("1 arg", mTestController.waitForStringValue()); + executeJavaScript("testObject.method(null)"); + assertEquals("1 arg", mTestController.waitForStringValue()); + executeJavaScript("testObject.method(undefined)"); + assertEquals("1 arg", mTestController.waitForStringValue()); + executeJavaScript("testObject.method(42, 42)"); + assertEquals("2 args", mTestController.waitForStringValue()); + } + + public void testObjectPersistsAcrossPageLoads() throws Throwable { + assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController")); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + getWebView().reload(); + } + }); + mWebViewClient.waitForOnPageFinished(); + assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController")); + } + + public void testSameObjectInjectedMultipleTimes() throws Throwable { + class TestObject { + private int mNumMethodInvocations; + public void method() { mTestController.setIntValue(++mNumMethodInvocations); } + } + final TestObject testObject = new TestObject(); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + getWebView().addJavascriptInterface(testObject, "testObject1"); + getWebView().addJavascriptInterface(testObject, "testObject2"); + getWebView().reload(); + } + }); + mWebViewClient.waitForOnPageFinished(); + executeJavaScript("testObject1.method()"); + assertEquals(1, mTestController.waitForIntValue()); + executeJavaScript("testObject2.method()"); + assertEquals(2, mTestController.waitForIntValue()); + } + + public void testCallMethodOnReturnedObject() throws Throwable { + injectObjectAndReload(new Object() { + public Object getInnerObject() { + return new Object() { + public void method(int x) { mTestController.setIntValue(x); } + }; + } + }, "testObject"); + executeJavaScript("testObject.getInnerObject().method(42)"); + assertEquals(42, mTestController.waitForIntValue()); + } + + public void testReturnedObjectInjectedElsewhere() throws Throwable { + class InnerObject { + private int mNumMethodInvocations; + public void method() { mTestController.setIntValue(++mNumMethodInvocations); } + } + final InnerObject innerObject = new InnerObject(); + final Object object = new Object() { + public InnerObject getInnerObject() { + return innerObject; + } + }; + runTestOnUiThread(new Runnable() { + @Override + public void run() { + getWebView().addJavascriptInterface(object, "testObject"); + getWebView().addJavascriptInterface(innerObject, "innerObject"); + getWebView().reload(); + } + }); + mWebViewClient.waitForOnPageFinished(); + executeJavaScript("testObject.getInnerObject().method()"); + assertEquals(1, mTestController.waitForIntValue()); + executeJavaScript("innerObject.method()"); + assertEquals(2, mTestController.waitForIntValue()); + } + + public void testMethodInvokedOnBackgroundThread() throws Throwable { + injectObjectAndReload(new Object() { + public void captureThreadId() { + mTestController.setLongValue(Thread.currentThread().getId()); + } + }, "testObject"); + executeJavaScript("testObject.captureThreadId()"); + final long threadId = mTestController.waitForLongValue(); + assertFalse(threadId == Thread.currentThread().getId()); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertFalse(threadId == Thread.currentThread().getId()); + } + }); + } + + public void testPublicInheritedMethod() throws Throwable { + class Base { + public void method(int x) { mTestController.setIntValue(x); } + } + class Derived extends Base { + } + injectObjectAndReload(new Derived(), "testObject"); + assertEquals("function", executeJavaScriptAndGetStringResult("typeof testObject.method")); + executeJavaScript("testObject.method(42)"); + assertEquals(42, mTestController.waitForIntValue()); + } + + public void testPrivateInheritedMethod() throws Throwable { + class Base { + private void method() {} + } + class Derived extends Base { + } + injectObjectAndReload(new Derived(), "testObject"); + assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject.method")); + } + + public void testOverriddenMethod() throws Throwable { + class Base { + public void method() { mTestController.setStringValue("base"); } + } + class Derived extends Base { + public void method() { mTestController.setStringValue("derived"); } + } + injectObjectAndReload(new Derived(), "testObject"); + executeJavaScript("testObject.method()"); + assertEquals("derived", mTestController.waitForStringValue()); + } + + public void testEnumerateMembers() throws Throwable { + injectObjectAndReload(new Object() { + public void method() {} + private void privateMethod() {} + public int field; + private int privateField; + }, "testObject"); + executeJavaScript( + "var result = \"\"; " + + "for (x in testObject) { result += \" \" + x } " + + "testController.setStringValue(result);"); + // LIVECONNECT_COMPLIANCE: Should be able to enumerate members. + assertEquals("", mTestController.waitForStringValue()); + } +} diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeCoercionTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeCoercionTest.java new file mode 100644 index 0000000..34b3432 --- /dev/null +++ b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeCoercionTest.java @@ -0,0 +1,650 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Part of the test suite for the WebView's Java Bridge. This class tests that + * we correctly convert JavaScript values to Java values when passing them to + * the methods of injected Java objects. + * + * The conversions should follow + * http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS. Places in + * which the implementation differs from the spec are marked with + * LIVECONNECT_COMPLIANCE. + * FIXME: Consider making our implementation more compliant, if it will not + * break backwards-compatibility. See b/4408210. + * + * To run this test ... + * adb shell am instrument -w -e class com.android.webviewtests.JavaBridgeCoercionTest \ + * com.android.webviewtests/android.test.InstrumentationTestRunner + */ + +package com.android.webviewtests; + +public class JavaBridgeCoercionTest extends JavaBridgeTestBase { + private class TestObject extends Controller { + private Object objectInstance; + private CustomType customTypeInstance; + private CustomType2 customType2Instance; + + private boolean mBooleanValue; + private byte mByteValue; + private char mCharValue; + private short mShortValue; + private int mIntValue; + private long mLongValue; + private float mFloatValue; + private double mDoubleValue; + // TODO: Test passing to methods with array parameters. + private String mStringValue; + private Object mObjectValue; + private CustomType mCustomTypeValue; + + public TestObject() { + objectInstance = new Object(); + customTypeInstance = new CustomType(); + customType2Instance = new CustomType2(); + } + + public Object getObjectInstance() { + return objectInstance; + } + public CustomType getCustomTypeInstance() { + return customTypeInstance; + } + public CustomType2 getCustomType2Instance() { + return customType2Instance; + } + + public synchronized void setBooleanValue(boolean x) { + mBooleanValue = x; + notifyResultIsReady(); + } + public synchronized void setByteValue(byte x) { + mByteValue = x; + notifyResultIsReady(); + } + public synchronized void setCharValue(char x) { + mCharValue = x; + notifyResultIsReady(); + } + public synchronized void setShortValue(short x) { + mShortValue = x; + notifyResultIsReady(); + } + public synchronized void setIntValue(int x) { + mIntValue = x; + notifyResultIsReady(); + } + public synchronized void setLongValue(long x) { + mLongValue = x; + notifyResultIsReady(); + } + public synchronized void setFloatValue(float x) { + mFloatValue = x; + notifyResultIsReady(); + } + public synchronized void setDoubleValue(double x) { + mDoubleValue = x; + notifyResultIsReady(); + } + // TODO: Test passing to methods with array parameters. + public synchronized void setStringValue(String x) { + mStringValue = x; + notifyResultIsReady(); + } + public synchronized void setObjectValue(Object x) { + mObjectValue = x; + notifyResultIsReady(); + } + public synchronized void setCustomTypeValue(CustomType x) { + mCustomTypeValue = x; + notifyResultIsReady(); + } + + public synchronized boolean waitForBooleanValue() { + waitForResult(); + return mBooleanValue; + } + public synchronized byte waitForByteValue() { + waitForResult(); + return mByteValue; + } + public synchronized char waitForCharValue() { + waitForResult(); + return mCharValue; + } + public synchronized short waitForShortValue() { + waitForResult(); + return mShortValue; + } + public synchronized int waitForIntValue() { + waitForResult(); + return mIntValue; + } + public synchronized long waitForLongValue() { + waitForResult(); + return mLongValue; + } + public synchronized float waitForFloatValue() { + waitForResult(); + return mFloatValue; + } + public synchronized double waitForDoubleValue() { + waitForResult(); + return mDoubleValue; + } + // TODO: Test passing to methods with array parameters. + public synchronized String waitForStringValue() { + waitForResult(); + return mStringValue; + } + public synchronized Object waitForObjectValue() { + waitForResult(); + return mObjectValue; + } + public synchronized CustomType waitForCustomTypeValue() { + waitForResult(); + return mCustomTypeValue; + } + } + + // Two custom types used when testing passing objects. + private static class CustomType { + } + private static class CustomType2 { + } + + private TestObject mTestObject; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mTestObject = new TestObject(); + setUpWebView(mTestObject, "testObject"); + } + + // Test passing a JavaScript number in the int32 range to a method of an + // injected object. + public void testPassNumberInt32() throws Throwable { + executeJavaScript("testObject.setByteValue(42);"); + assertEquals(42, mTestObject.waitForByteValue()); + executeJavaScript("testObject.setByteValue(" + Byte.MAX_VALUE + " + 42);"); + assertEquals(Byte.MIN_VALUE + 42 - 1, mTestObject.waitForByteValue()); + + // LIVECONNECT_COMPLIANCE: Should convert to numeric char value. + executeJavaScript("testObject.setCharValue(42);"); + assertEquals('\u0000', mTestObject.waitForCharValue()); + + executeJavaScript("testObject.setShortValue(42);"); + assertEquals(42, mTestObject.waitForShortValue()); + executeJavaScript("testObject.setShortValue(" + Short.MAX_VALUE + " + 42);"); + assertEquals(Short.MIN_VALUE + 42 - 1, mTestObject.waitForShortValue()); + + executeJavaScript("testObject.setIntValue(42);"); + assertEquals(42, mTestObject.waitForIntValue()); + + executeJavaScript("testObject.setLongValue(42);"); + assertEquals(42, mTestObject.waitForLongValue()); + + executeJavaScript("testObject.setFloatValue(42);"); + assertEquals(42.0f, mTestObject.waitForFloatValue()); + + executeJavaScript("testObject.setDoubleValue(42);"); + assertEquals(42.0, mTestObject.waitForDoubleValue()); + + // LIVECONNECT_COMPLIANCE: Should create an instance of java.lang.Number. + executeJavaScript("testObject.setObjectValue(42);"); + assertNull(mTestObject.waitForObjectValue()); + + // The spec allows the JS engine flexibility in how to format the number. + executeJavaScript("testObject.setStringValue(42);"); + String str = mTestObject.waitForStringValue(); + assertTrue("42".equals(str) || "42.0".equals(str)); + + executeJavaScript("testObject.setBooleanValue(0);"); + assertFalse(mTestObject.waitForBooleanValue()); + // LIVECONNECT_COMPLIANCE: Should be true; + executeJavaScript("testObject.setBooleanValue(42);"); + assertFalse(mTestObject.waitForBooleanValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setCustomTypeValue(42);"); + assertNull(mTestObject.waitForCustomTypeValue()); + } + + // Test passing a JavaScript number in the double range to a method of an + // injected object. + public void testPassNumberDouble() throws Throwable { + executeJavaScript("testObject.setByteValue(42.1);"); + assertEquals(42, mTestObject.waitForByteValue()); + executeJavaScript("testObject.setByteValue(" + Byte.MAX_VALUE + " + 42.1);"); + assertEquals(Byte.MIN_VALUE + 42 - 1, mTestObject.waitForByteValue()); + executeJavaScript("testObject.setByteValue(" + Integer.MAX_VALUE + " + 42.1);"); + assertEquals(-1, mTestObject.waitForByteValue()); + + // LIVECONNECT_COMPLIANCE: Should convert to numeric char value. + executeJavaScript("testObject.setCharValue(42.1);"); + assertEquals('\u0000', mTestObject.waitForCharValue()); + + executeJavaScript("testObject.setShortValue(42.1);"); + assertEquals(42, mTestObject.waitForShortValue()); + executeJavaScript("testObject.setShortValue(" + Short.MAX_VALUE + " + 42.1);"); + assertEquals(Short.MIN_VALUE + 42 - 1, mTestObject.waitForShortValue()); + executeJavaScript("testObject.setShortValue(" + Integer.MAX_VALUE + " + 42.1);"); + assertEquals(-1, mTestObject.waitForShortValue()); + + executeJavaScript("testObject.setIntValue(42.1);"); + assertEquals(42, mTestObject.waitForIntValue()); + executeJavaScript("testObject.setIntValue(" + Integer.MAX_VALUE + " + 42.1);"); + assertEquals(Integer.MAX_VALUE, mTestObject.waitForIntValue()); + + executeJavaScript("testObject.setLongValue(42.1);"); + assertEquals(42, mTestObject.waitForLongValue()); + // LIVECONNECT_COMPLIANCE: Should be Long.MAX_VALUE. + executeJavaScript("testObject.setLongValue(" + Long.MAX_VALUE + " + 42.1);"); + assertEquals(Long.MIN_VALUE, mTestObject.waitForLongValue()); + + executeJavaScript("testObject.setFloatValue(42.1);"); + assertEquals(42.1f, mTestObject.waitForFloatValue()); + + executeJavaScript("testObject.setDoubleValue(42.1);"); + assertEquals(42.1, mTestObject.waitForDoubleValue()); + + // LIVECONNECT_COMPLIANCE: Should create an instance of java.lang.Number. + executeJavaScript("testObject.setObjectValue(42.1);"); + assertNull(mTestObject.waitForObjectValue()); + + executeJavaScript("testObject.setStringValue(42.1);"); + assertEquals("42.1", mTestObject.waitForStringValue()); + + executeJavaScript("testObject.setBooleanValue(0.0);"); + assertFalse(mTestObject.waitForBooleanValue()); + // LIVECONNECT_COMPLIANCE: Should be true. + executeJavaScript("testObject.setBooleanValue(42.1);"); + assertFalse(mTestObject.waitForBooleanValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setCustomTypeValue(42.1);"); + assertNull(mTestObject.waitForCustomTypeValue()); + } + + // Test passing JavaScript NaN to a method of an injected object. + public void testPassNumberNaN() throws Throwable { + executeJavaScript("testObject.setByteValue(Number.NaN);"); + assertEquals(0, mTestObject.waitForByteValue()); + + executeJavaScript("testObject.setCharValue(Number.NaN);"); + assertEquals('\u0000', mTestObject.waitForCharValue()); + + executeJavaScript("testObject.setShortValue(Number.NaN);"); + assertEquals(0, mTestObject.waitForShortValue()); + + executeJavaScript("testObject.setIntValue(Number.NaN);"); + assertEquals(0, mTestObject.waitForIntValue()); + + executeJavaScript("testObject.setLongValue(Number.NaN);"); + assertEquals(0, mTestObject.waitForLongValue()); + + executeJavaScript("testObject.setFloatValue(Number.NaN);"); + assertEquals(Float.NaN, mTestObject.waitForFloatValue()); + + executeJavaScript("testObject.setDoubleValue(Number.NaN);"); + assertEquals(Double.NaN, mTestObject.waitForDoubleValue()); + + // LIVECONNECT_COMPLIANCE: Should create an instance of java.lang.Number. + executeJavaScript("testObject.setObjectValue(Number.NaN);"); + assertNull(mTestObject.waitForObjectValue()); + + executeJavaScript("testObject.setStringValue(Number.NaN);"); + assertEquals("NaN", mTestObject.waitForStringValue()); + + executeJavaScript("testObject.setBooleanValue(Number.NaN);"); + assertFalse(mTestObject.waitForBooleanValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setCustomTypeValue(Number.NaN);"); + assertNull(mTestObject.waitForCustomTypeValue()); + } + + // Test passing JavaScript infinity to a method of an injected object. + public void testPassNumberInfinity() throws Throwable { + executeJavaScript("testObject.setByteValue(Infinity);"); + assertEquals(-1, mTestObject.waitForByteValue()); + + // LIVECONNECT_COMPLIANCE: Should convert to maximum numeric char value. + executeJavaScript("testObject.setCharValue(Infinity);"); + assertEquals('\u0000', mTestObject.waitForCharValue()); + + executeJavaScript("testObject.setShortValue(Infinity);"); + assertEquals(-1, mTestObject.waitForShortValue()); + + executeJavaScript("testObject.setIntValue(Infinity);"); + assertEquals(Integer.MAX_VALUE, mTestObject.waitForIntValue()); + + // LIVECONNECT_COMPLIANCE: Should be Long.MAX_VALUE. + executeJavaScript("testObject.setLongValue(Infinity);"); + assertEquals(-1, mTestObject.waitForLongValue()); + + executeJavaScript("testObject.setFloatValue(Infinity);"); + assertEquals(Float.POSITIVE_INFINITY, mTestObject.waitForFloatValue()); + + executeJavaScript("testObject.setDoubleValue(Infinity);"); + assertEquals(Double.POSITIVE_INFINITY, mTestObject.waitForDoubleValue()); + + // LIVECONNECT_COMPLIANCE: Should create an instance of java.lang.Number. + executeJavaScript("testObject.setObjectValue(Infinity);"); + assertNull(mTestObject.waitForObjectValue()); + + executeJavaScript("testObject.setStringValue(Infinity);"); + assertEquals("Inf", mTestObject.waitForStringValue()); + + executeJavaScript("testObject.setBooleanValue(Infinity);"); + assertFalse(mTestObject.waitForBooleanValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setCustomTypeValue(Infinity);"); + assertNull(mTestObject.waitForCustomTypeValue()); + } + + // Test passing a JavaScript boolean to a method of an injected object. + public void testPassBoolean() throws Throwable { + executeJavaScript("testObject.setBooleanValue(true);"); + assertTrue(mTestObject.waitForBooleanValue()); + executeJavaScript("testObject.setBooleanValue(false);"); + assertFalse(mTestObject.waitForBooleanValue()); + + // LIVECONNECT_COMPLIANCE: Should create an instance of java.lang.Boolean. + executeJavaScript("testObject.setObjectValue(true);"); + assertNull(mTestObject.waitForObjectValue()); + + executeJavaScript("testObject.setStringValue(false);"); + assertEquals("false", mTestObject.waitForStringValue()); + executeJavaScript("testObject.setStringValue(true);"); + assertEquals("true", mTestObject.waitForStringValue()); + + // LIVECONNECT_COMPLIANCE: Should be 1. + executeJavaScript("testObject.setByteValue(true);"); + assertEquals(0, mTestObject.waitForByteValue()); + executeJavaScript("testObject.setByteValue(false);"); + assertEquals(0, mTestObject.waitForByteValue()); + + // LIVECONNECT_COMPLIANCE: Should convert to numeric char value 1. + executeJavaScript("testObject.setCharValue(true);"); + assertEquals('\u0000', mTestObject.waitForCharValue()); + executeJavaScript("testObject.setCharValue(false);"); + assertEquals('\u0000', mTestObject.waitForCharValue()); + + // LIVECONNECT_COMPLIANCE: Should be 1. + executeJavaScript("testObject.setShortValue(true);"); + assertEquals(0, mTestObject.waitForShortValue()); + executeJavaScript("testObject.setShortValue(false);"); + assertEquals(0, mTestObject.waitForShortValue()); + + // LIVECONNECT_COMPLIANCE: Should be 1. + executeJavaScript("testObject.setIntValue(true);"); + assertEquals(0, mTestObject.waitForIntValue()); + executeJavaScript("testObject.setIntValue(false);"); + assertEquals(0, mTestObject.waitForIntValue()); + + // LIVECONNECT_COMPLIANCE: Should be 1. + executeJavaScript("testObject.setLongValue(true);"); + assertEquals(0, mTestObject.waitForLongValue()); + executeJavaScript("testObject.setLongValue(false);"); + assertEquals(0, mTestObject.waitForLongValue()); + + // LIVECONNECT_COMPLIANCE: Should be 1.0. + executeJavaScript("testObject.setFloatValue(true);"); + assertEquals(0.0f, mTestObject.waitForFloatValue()); + executeJavaScript("testObject.setFloatValue(false);"); + assertEquals(0.0f, mTestObject.waitForFloatValue()); + + // LIVECONNECT_COMPLIANCE: Should be 1.0. + executeJavaScript("testObject.setDoubleValue(true);"); + assertEquals(0.0, mTestObject.waitForDoubleValue()); + executeJavaScript("testObject.setDoubleValue(false);"); + assertEquals(0.0, mTestObject.waitForDoubleValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setCustomTypeValue(true);"); + assertNull(mTestObject.waitForCustomTypeValue()); + } + + // Test passing a JavaScript string to a method of an injected object. + public void testPassString() throws Throwable { + executeJavaScript("testObject.setStringValue(\"+042.10\");"); + assertEquals("+042.10", mTestObject.waitForStringValue()); + + // Make sure that we distinguish between the empty string and NULL. + executeJavaScript("testObject.setStringValue(\"\");"); + assertEquals("", mTestObject.waitForStringValue()); + + // LIVECONNECT_COMPLIANCE: Should create an instance of java.lang.String. + executeJavaScript("testObject.setObjectValue(\"+042.10\");"); + assertNull(mTestObject.waitForObjectValue()); + + // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type. + executeJavaScript("testObject.setByteValue(\"+042.10\");"); + assertEquals(0, mTestObject.waitForByteValue()); + + // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type. + executeJavaScript("testObject.setShortValue(\"+042.10\");"); + assertEquals(0, mTestObject.waitForShortValue()); + + // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type. + executeJavaScript("testObject.setIntValue(\"+042.10\");"); + assertEquals(0, mTestObject.waitForIntValue()); + + // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type. + executeJavaScript("testObject.setLongValue(\"+042.10\");"); + assertEquals(0, mTestObject.waitForLongValue()); + + // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type. + executeJavaScript("testObject.setFloatValue(\"+042.10\");"); + assertEquals(0.0f, mTestObject.waitForFloatValue()); + + // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type. + executeJavaScript("testObject.setDoubleValue(\"+042.10\");"); + assertEquals(0.0, mTestObject.waitForDoubleValue()); + + // LIVECONNECT_COMPLIANCE: Should decode and convert to numeric char value. + executeJavaScript("testObject.setCharValue(\"+042.10\");"); + assertEquals('\u0000', mTestObject.waitForCharValue()); + + executeJavaScript("testObject.setBooleanValue(\"+042.10\");"); + assertFalse(mTestObject.waitForBooleanValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setCustomTypeValue(\"+042.10\");"); + assertNull(mTestObject.waitForCustomTypeValue()); + } + + // TODO: Test passing arrays. + + // Test passing a JavaScript object to a method of an injected object. + public void testPassJavaScriptObject() throws Throwable { + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setObjectValue({foo: 42});"); + assertNull(mTestObject.waitForObjectValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setCustomTypeValue({foo: 42});"); + assertNull(mTestObject.waitForCustomTypeValue()); + + // LIVECONNECT_COMPLIANCE: Should call toString() on object. + executeJavaScript("testObject.setStringValue({foo: 42});"); + assertEquals("undefined", mTestObject.waitForStringValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setByteValue({foo: 42});"); + assertEquals(0, mTestObject.waitForByteValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setCharValue({foo: 42});"); + assertEquals('\u0000', mTestObject.waitForCharValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setShortValue({foo: 42});"); + assertEquals(0, mTestObject.waitForShortValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setIntValue({foo: 42});"); + assertEquals(0, mTestObject.waitForIntValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setLongValue({foo: 42});"); + assertEquals(0, mTestObject.waitForLongValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setFloatValue({foo: 42});"); + assertEquals(0.0f, mTestObject.waitForFloatValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setDoubleValue({foo: 42});"); + assertEquals(0.0, mTestObject.waitForDoubleValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setBooleanValue({foo: 42});"); + assertFalse(mTestObject.waitForBooleanValue()); + } + + // Test passing a Java object to a method of an injected object. Note that + // this test requires being able to return objects from the methods of + // injected objects. This is tested elsewhere. + public void testPassJavaObject() throws Throwable { + executeJavaScript("testObject.setObjectValue(testObject.getObjectInstance());"); + assertTrue(mTestObject.getObjectInstance() == mTestObject.waitForObjectValue()); + executeJavaScript("testObject.setObjectValue(testObject.getCustomTypeInstance());"); + assertTrue(mTestObject.getCustomTypeInstance() == mTestObject.waitForObjectValue()); + + executeJavaScript("testObject.setCustomTypeValue(testObject.getObjectInstance());"); + assertTrue(mTestObject.getObjectInstance() == mTestObject.waitForCustomTypeValue()); + executeJavaScript("testObject.setCustomTypeValue(testObject.getCustomTypeInstance());"); + assertTrue(mTestObject.getCustomTypeInstance() == mTestObject.waitForCustomTypeValue()); + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception, as the types are unrelated. + executeJavaScript("testObject.setCustomTypeValue(testObject.getCustomType2Instance());"); + assertTrue(mTestObject.getCustomType2Instance() == + (Object)mTestObject.waitForCustomTypeValue()); + + // LIVECONNECT_COMPLIANCE: Should call toString() on object. + executeJavaScript("testObject.setStringValue(testObject.getObjectInstance());"); + assertEquals("undefined", mTestObject.waitForStringValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setByteValue(testObject.getObjectInstance());"); + assertEquals(0, mTestObject.waitForByteValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setCharValue(testObject.getObjectInstance());"); + assertEquals('\u0000', mTestObject.waitForCharValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setShortValue(testObject.getObjectInstance());"); + assertEquals(0, mTestObject.waitForShortValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setIntValue(testObject.getObjectInstance());"); + assertEquals(0, mTestObject.waitForIntValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setLongValue(testObject.getObjectInstance());"); + assertEquals(0, mTestObject.waitForLongValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setFloatValue(testObject.getObjectInstance());"); + assertEquals(0.0f, mTestObject.waitForFloatValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setDoubleValue(testObject.getObjectInstance());"); + assertEquals(0.0, mTestObject.waitForDoubleValue()); + + // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception. + executeJavaScript("testObject.setBooleanValue(testObject.getObjectInstance());"); + assertFalse(mTestObject.waitForBooleanValue()); + } + + // Test passing JavaScript null to a method of an injected object. + public void testPassNull() throws Throwable { + executeJavaScript("testObject.setObjectValue(null);"); + assertNull(mTestObject.waitForObjectValue()); + + executeJavaScript("testObject.setCustomTypeValue(null);"); + assertNull(mTestObject.waitForCustomTypeValue()); + + executeJavaScript("testObject.setStringValue(null);"); + assertNull(mTestObject.waitForStringValue()); + + executeJavaScript("testObject.setByteValue(null);"); + assertEquals(0, mTestObject.waitForByteValue()); + + executeJavaScript("testObject.setCharValue(null);"); + assertEquals('\u0000', mTestObject.waitForCharValue()); + + executeJavaScript("testObject.setShortValue(null);"); + assertEquals(0, mTestObject.waitForShortValue()); + + executeJavaScript("testObject.setIntValue(null);"); + assertEquals(0, mTestObject.waitForIntValue()); + + executeJavaScript("testObject.setLongValue(null);"); + assertEquals(0, mTestObject.waitForLongValue()); + + executeJavaScript("testObject.setFloatValue(null);"); + assertEquals(0.0f, mTestObject.waitForFloatValue()); + + executeJavaScript("testObject.setDoubleValue(null);"); + assertEquals(0.0, mTestObject.waitForDoubleValue()); + + executeJavaScript("testObject.setBooleanValue(null);"); + assertFalse(mTestObject.waitForBooleanValue()); + } + + // Test passing JavaScript undefined to a method of an injected object. + public void testPassUndefined() throws Throwable { + executeJavaScript("testObject.setObjectValue(undefined);"); + assertNull(mTestObject.waitForObjectValue()); + + executeJavaScript("testObject.setCustomTypeValue(undefined);"); + assertNull(mTestObject.waitForCustomTypeValue()); + + // LIVECONNECT_COMPLIANCE: Should be NULL. + executeJavaScript("testObject.setStringValue(undefined);"); + assertEquals("undefined", mTestObject.waitForStringValue()); + + executeJavaScript("testObject.setByteValue(undefined);"); + assertEquals(0, mTestObject.waitForByteValue()); + + executeJavaScript("testObject.setCharValue(undefined);"); + assertEquals('\u0000', mTestObject.waitForCharValue()); + + executeJavaScript("testObject.setShortValue(undefined);"); + assertEquals(0, mTestObject.waitForShortValue()); + + executeJavaScript("testObject.setIntValue(undefined);"); + assertEquals(0, mTestObject.waitForIntValue()); + + executeJavaScript("testObject.setLongValue(undefined);"); + assertEquals(0, mTestObject.waitForLongValue()); + + executeJavaScript("testObject.setFloatValue(undefined);"); + assertEquals(0.0f, mTestObject.waitForFloatValue()); + + executeJavaScript("testObject.setDoubleValue(undefined);"); + assertEquals(0.0, mTestObject.waitForDoubleValue()); + + executeJavaScript("testObject.setBooleanValue(undefined);"); + assertFalse(mTestObject.waitForBooleanValue()); + } +} diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeFieldsTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeFieldsTest.java new file mode 100644 index 0000000..0ccd175 --- /dev/null +++ b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeFieldsTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Part of the test suite for the WebView's Java Bridge. This test tests the + * use of fields. + * + * To run this test ... + * adb shell am instrument -w -e class com.android.webviewtests.JavaBridgeFieldsTest \ + * com.android.webviewtests/android.test.InstrumentationTestRunner + */ + +package com.android.webviewtests; + +public class JavaBridgeFieldsTest extends JavaBridgeTestBase { + private class TestObject extends Controller { + private String mStringValue; + + // These methods are used to control the test. + public synchronized void setStringValue(String x) { + mStringValue = x; + notifyResultIsReady(); + } + public synchronized String waitForStringValue() { + waitForResult(); + return mStringValue; + } + + public boolean booleanField = true; + public byte byteField = 42; + public char charField = '\u002A'; + public short shortField = 42; + public int intField = 42; + public long longField = 42L; + public float floatField = 42.0f; + public double doubleField = 42.0; + public String stringField = "foo"; + public Object objectField = new Object(); + public CustomType customTypeField = new CustomType(); + } + + // A custom type used when testing passing objects. + private class CustomType { + } + + TestObject mTestObject; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mTestObject = new TestObject(); + setUpWebView(mTestObject, "testObject"); + } + + // Note that this requires that we can pass a JavaScript string to Java. + protected String executeJavaScriptAndGetStringResult(String script) throws Throwable { + executeJavaScript("testObject.setStringValue(" + script + ");"); + return mTestObject.waitForStringValue(); + } + + // The Java bridge does not provide access to fields. + // FIXME: Consider providing support for this. See See b/4408210. + public void testFieldTypes() throws Throwable { + assertEquals("undefined", + executeJavaScriptAndGetStringResult("typeof testObject.booleanField")); + assertEquals("undefined", + executeJavaScriptAndGetStringResult("typeof testObject.byteField")); + assertEquals("undefined", + executeJavaScriptAndGetStringResult("typeof testObject.charField")); + assertEquals("undefined", + executeJavaScriptAndGetStringResult("typeof testObject.shortField")); + assertEquals("undefined", + executeJavaScriptAndGetStringResult("typeof testObject.intField")); + assertEquals("undefined", + executeJavaScriptAndGetStringResult("typeof testObject.longField")); + assertEquals("undefined", + executeJavaScriptAndGetStringResult("typeof testObject.floatField")); + assertEquals("undefined", + executeJavaScriptAndGetStringResult("typeof testObject.doubleField")); + assertEquals("undefined", + executeJavaScriptAndGetStringResult("typeof testObject.objectField")); + assertEquals("undefined", + executeJavaScriptAndGetStringResult("typeof testObject.stringField")); + assertEquals("undefined", + executeJavaScriptAndGetStringResult("typeof testObject.customTypeField")); + } +} diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeReturnValuesTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeReturnValuesTest.java new file mode 100644 index 0000000..44d5cc6 --- /dev/null +++ b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeReturnValuesTest.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Part of the test suite for the WebView's Java Bridge. This test checks that + * we correctly convert Java values to JavaScript values when returning them + * from the methods of injected Java objects. + * + * The conversions should follow + * http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS. Places in + * which the implementation differs from the spec are marked with + * LIVECONNECT_COMPLIANCE. + * FIXME: Consider making our implementation more compliant, if it will not + * break backwards-compatibility. See b/4408210. + * + * To run this test ... + * adb shell am instrument -w -e class com.android.webviewtests.JavaBridgeReturnValuesTest \ + * com.android.webviewtests/android.test.InstrumentationTestRunner + */ + +package com.android.webviewtests; + +public class JavaBridgeReturnValuesTest extends JavaBridgeTestBase { + // An instance of this class is injected into the page to test returning + // Java values to JavaScript. + private class TestObject extends Controller { + private String mStringValue; + private boolean mBooleanValue; + + // These four methods are used to control the test. + public synchronized void setStringValue(String x) { + mStringValue = x; + notifyResultIsReady(); + } + public synchronized String waitForStringValue() { + waitForResult(); + return mStringValue; + } + public synchronized void setBooleanValue(boolean x) { + mBooleanValue = x; + notifyResultIsReady(); + } + public synchronized boolean waitForBooleanValue() { + waitForResult(); + return mBooleanValue; + } + + public boolean getBooleanValue() { + return true; + } + public byte getByteValue() { + return 42; + } + public char getCharValue() { + return '\u002A'; + } + public short getShortValue() { + return 42; + } + public int getIntValue() { + return 42; + } + public long getLongValue() { + return 42L; + } + public float getFloatValue() { + return 42.1f; + } + public float getFloatValueNoDecimal() { + return 42.0f; + } + public double getDoubleValue() { + return 42.1; + } + public double getDoubleValueNoDecimal() { + return 42.0; + } + public String getStringValue() { + return "foo"; + } + public String getEmptyStringValue() { + return ""; + } + public String getNullStringValue() { + return null; + } + public Object getObjectValue() { + return new Object(); + } + public Object getNullObjectValue() { + return null; + } + public CustomType getCustomTypeValue() { + return new CustomType(); + } + public void getVoidValue() { + } + } + + // A custom type used when testing passing objects. + private class CustomType { + } + + TestObject mTestObject; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mTestObject = new TestObject(); + setUpWebView(mTestObject, "testObject"); + } + + // Note that this requires that we can pass a JavaScript string to Java. + protected String executeJavaScriptAndGetStringResult(String script) throws Throwable { + executeJavaScript("testObject.setStringValue(" + script + ");"); + return mTestObject.waitForStringValue(); + } + + // Note that this requires that we can pass a JavaScript boolean to Java. + private boolean executeJavaScriptAndGetBooleanResult(String script) throws Throwable { + executeJavaScript("testObject.setBooleanValue(" + script + ");"); + return mTestObject.waitForBooleanValue(); + } + + public void testMethodReturnTypes() throws Throwable { + assertEquals("boolean", + executeJavaScriptAndGetStringResult("typeof testObject.getBooleanValue()")); + assertEquals("number", + executeJavaScriptAndGetStringResult("typeof testObject.getByteValue()")); + // char values are returned to JavaScript as numbers. + assertEquals("number", + executeJavaScriptAndGetStringResult("typeof testObject.getCharValue()")); + assertEquals("number", + executeJavaScriptAndGetStringResult("typeof testObject.getShortValue()")); + assertEquals("number", + executeJavaScriptAndGetStringResult("typeof testObject.getIntValue()")); + assertEquals("number", + executeJavaScriptAndGetStringResult("typeof testObject.getLongValue()")); + assertEquals("number", + executeJavaScriptAndGetStringResult("typeof testObject.getFloatValue()")); + assertEquals("number", + executeJavaScriptAndGetStringResult("typeof testObject.getFloatValueNoDecimal()")); + assertEquals("number", + executeJavaScriptAndGetStringResult("typeof testObject.getDoubleValue()")); + assertEquals("number", + executeJavaScriptAndGetStringResult("typeof testObject.getDoubleValueNoDecimal()")); + assertEquals("string", + executeJavaScriptAndGetStringResult("typeof testObject.getStringValue()")); + assertEquals("string", + executeJavaScriptAndGetStringResult("typeof testObject.getEmptyStringValue()")); + // LIVECONNECT_COMPLIANCE: This should have type object. + assertEquals("undefined", + executeJavaScriptAndGetStringResult("typeof testObject.getNullStringValue()")); + assertEquals("object", + executeJavaScriptAndGetStringResult("typeof testObject.getObjectValue()")); + assertEquals("object", + executeJavaScriptAndGetStringResult("typeof testObject.getNullObjectValue()")); + assertEquals("object", + executeJavaScriptAndGetStringResult("typeof testObject.getCustomTypeValue()")); + assertEquals("undefined", + executeJavaScriptAndGetStringResult("typeof testObject.getVoidValue()")); + } + + public void testMethodReturnValues() throws Throwable { + // We do the string comparison in JavaScript, to avoid relying on the + // coercion algorithm from JavaScript to Java. + assertTrue(executeJavaScriptAndGetBooleanResult("testObject.getBooleanValue()")); + assertTrue(executeJavaScriptAndGetBooleanResult("42 === testObject.getByteValue()")); + // char values are returned to JavaScript as numbers. + assertTrue(executeJavaScriptAndGetBooleanResult("42 === testObject.getCharValue()")); + assertTrue(executeJavaScriptAndGetBooleanResult("42 === testObject.getShortValue()")); + assertTrue(executeJavaScriptAndGetBooleanResult("42 === testObject.getIntValue()")); + assertTrue(executeJavaScriptAndGetBooleanResult("42 === testObject.getLongValue()")); + assertTrue(executeJavaScriptAndGetBooleanResult( + "Math.abs(42.1 - testObject.getFloatValue()) < 0.001")); + assertTrue(executeJavaScriptAndGetBooleanResult( + "42.0 === testObject.getFloatValueNoDecimal()")); + assertTrue(executeJavaScriptAndGetBooleanResult( + "Math.abs(42.1 - testObject.getDoubleValue()) < 0.001")); + assertTrue(executeJavaScriptAndGetBooleanResult( + "42.0 === testObject.getDoubleValueNoDecimal()")); + assertEquals("foo", executeJavaScriptAndGetStringResult("testObject.getStringValue()")); + assertEquals("", executeJavaScriptAndGetStringResult("testObject.getEmptyStringValue()")); + assertTrue(executeJavaScriptAndGetBooleanResult("undefined === testObject.getVoidValue()")); + } +} diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeTestBase.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeTestBase.java new file mode 100644 index 0000000..a9ab3b7 --- /dev/null +++ b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeTestBase.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common functionality for testing the WebView's Java Bridge. + */ + +package com.android.webviewtests; + +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import junit.framework.Assert; + +public class JavaBridgeTestBase extends ActivityInstrumentationTestCase2<WebViewStubActivity> { + protected class TestWebViewClient extends WebViewClient { + private boolean mIsPageFinished; + @Override + public synchronized void onPageFinished(WebView webView, String url) { + mIsPageFinished = true; + notify(); + } + public synchronized void waitForOnPageFinished() throws RuntimeException { + while (!mIsPageFinished) { + try { + wait(5000); + } catch (Exception e) { + continue; + } + if (!mIsPageFinished) { + throw new RuntimeException("Timed out waiting for onPageFinished()"); + } + } + mIsPageFinished = false; + } + } + + protected class Controller { + private boolean mIsResultReady; + + protected synchronized void notifyResultIsReady() { + mIsResultReady = true; + notify(); + } + protected synchronized void waitForResult() { + while (!mIsResultReady) { + try { + wait(5000); + } catch (Exception e) { + continue; + } + if (!mIsResultReady) { + Assert.fail("Wait timed out"); + } + } + mIsResultReady = false; + } + } + + protected TestWebViewClient mWebViewClient; + + public JavaBridgeTestBase() { + super(WebViewStubActivity.class); + } + + // Sets up the WebView and injects the supplied object. Intended to be called from setUp(). + protected void setUpWebView(final Object object, final String name) throws Exception { + mWebViewClient = new TestWebViewClient(); + // This starts the activity, so must be called on the test thread. + final WebViewStubActivity activity = getActivity(); + // On the UI thread, load an empty page and wait for it to finish + // loading so that the Java object is injected. + try { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + WebView webView = activity.getWebView(); + webView.addJavascriptInterface(object, name); + webView.getSettings().setJavaScriptEnabled(true); + webView.setWebViewClient(mWebViewClient); + webView.loadData("<html><head></head><body></body></html>", "text/html", null); + } + }); + mWebViewClient.waitForOnPageFinished(); + } catch (Throwable e) { + throw new RuntimeException("Failed to set up WebView: " + Log.getStackTraceString(e)); + } + } + + protected void executeJavaScript(final String script) throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + getWebView().loadUrl("javascript:" + script); + } + }); + } + + protected WebView getWebView() { + return getActivity().getWebView(); + } +} diff --git a/tests/WebViewTests/src/com/android/webviewtests/WebViewStubActivity.java b/tests/WebViewTests/src/com/android/webviewtests/WebViewStubActivity.java new file mode 100644 index 0000000..ccfd3d5 --- /dev/null +++ b/tests/WebViewTests/src/com/android/webviewtests/WebViewStubActivity.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.webviewtests; + +import com.android.webviewtests.R; + +import android.app.Activity; +import android.os.Bundle; +import android.webkit.WebView; + +public class WebViewStubActivity extends Activity { + private WebView mWebView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.webview_layout); + mWebView = (WebView) findViewById(R.id.web_page); + } + + public WebView getWebView() { + return mWebView; + } +} |