1 // Copyright 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.content.browser;
6 
7 import androidx.test.filters.SmallTest;
8 
9 import org.junit.Assert;
10 import org.junit.Before;
11 import org.junit.Rule;
12 import org.junit.Test;
13 import org.junit.runner.RunWith;
14 
15 import org.chromium.base.test.params.BaseJUnit4RunnerDelegate;
16 import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameter;
17 import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameterBefore;
18 import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
19 import org.chromium.base.test.params.ParameterizedRunner;
20 import org.chromium.base.test.util.Batch;
21 import org.chromium.base.test.util.Feature;
22 import org.chromium.content.browser.JavaBridgeActivityTestRule.Controller;
23 
24 /**
25  * Part of the test suite for the Java Bridge. This class tests the general use of arrays.
26  *
27  * The conversions should follow
28  * http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS. Places in
29  * which the implementation differs from the spec are marked with
30  * LIVECONNECT_COMPLIANCE.
31  * FIXME: Consider making our implementation more compliant, if it will not
32  * break backwards-compatibility. See b/4408210.
33  */
34 @RunWith(ParameterizedRunner.class)
35 @UseRunnerDelegate(BaseJUnit4RunnerDelegate.class)
36 @Batch(JavaBridgeActivityTestRule.BATCH)
37 public class JavaBridgeArrayTest {
38     @Rule
39     public JavaBridgeActivityTestRule mActivityTestRule = new JavaBridgeActivityTestRule();
40 
41     private static class TestObject extends Controller {
42         private boolean mBooleanValue;
43         private int mIntValue;
44         private String mStringValue;
45 
46         private int[] mIntArray;
47         private int[][] mIntIntArray;
48 
49         private boolean mWasArrayMethodCalled;
50 
setBooleanValue(boolean x)51         public synchronized void setBooleanValue(boolean x) {
52             mBooleanValue = x;
53             notifyResultIsReady();
54         }
setIntValue(int x)55         public synchronized void setIntValue(int x) {
56             mIntValue = x;
57             notifyResultIsReady();
58         }
setStringValue(String x)59         public synchronized void setStringValue(String x) {
60             mStringValue = x;
61             notifyResultIsReady();
62         }
63 
waitForBooleanValue()64         public synchronized boolean waitForBooleanValue() {
65             waitForResult();
66             return mBooleanValue;
67         }
waitForIntValue()68         public synchronized int waitForIntValue() {
69             waitForResult();
70             return mIntValue;
71         }
waitForStringValue()72         public synchronized String waitForStringValue() {
73             waitForResult();
74             return mStringValue;
75         }
76 
setIntArray(int[] x)77         public synchronized void setIntArray(int[] x) {
78             mIntArray = x;
79             notifyResultIsReady();
80         }
setIntIntArray(int[][] x)81         public synchronized void setIntIntArray(int[][] x) {
82             mIntIntArray = x;
83             notifyResultIsReady();
84         }
85 
waitForIntArray()86         public synchronized int[] waitForIntArray() {
87             waitForResult();
88             return mIntArray;
89         }
waitForIntIntArray()90         public synchronized int[][] waitForIntIntArray() {
91             waitForResult();
92             return mIntIntArray;
93         }
94 
arrayMethod()95         public synchronized int[] arrayMethod() {
96             mWasArrayMethodCalled = true;
97             return new int[] {42, 43, 44};
98         }
99 
wasArrayMethodCalled()100         public synchronized boolean wasArrayMethodCalled() {
101             return mWasArrayMethodCalled;
102         }
103     }
104 
105     @UseMethodParameterBefore(JavaBridgeActivityTestRule.MojoTestParams.class)
setupMojoTest(boolean useMojo)106     public void setupMojoTest(boolean useMojo) {
107         mActivityTestRule.setupMojoTest(useMojo);
108     }
109 
110     private TestObject mTestObject;
111 
112     @Before
setUp()113     public void setUp() {
114         mTestObject = new TestObject();
115         mActivityTestRule.injectObjectAndReload(mTestObject, "testObject");
116     }
117 
118     @Test
119     @SmallTest
120     @Feature({"AndroidWebView", "Android-JavaBridge"})
121     @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
testArrayLength(boolean useMojo)122     public void testArrayLength(boolean useMojo) throws Throwable {
123         mActivityTestRule.executeJavaScript("testObject.setIntArray([42, 43, 44]);");
124         int[] result = mTestObject.waitForIntArray();
125         Assert.assertEquals(3, result.length);
126         Assert.assertEquals(42, result[0]);
127         Assert.assertEquals(43, result[1]);
128         Assert.assertEquals(44, result[2]);
129     }
130 
131     @Test
132     @SmallTest
133     @Feature({"AndroidWebView", "Android-JavaBridge"})
134     @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
testPassNull(boolean useMojo)135     public void testPassNull(boolean useMojo) throws Throwable {
136         mActivityTestRule.executeJavaScript("testObject.setIntArray(null);");
137         Assert.assertNull(mTestObject.waitForIntArray());
138     }
139 
140     @Test
141     @SmallTest
142     @Feature({"AndroidWebView", "Android-JavaBridge"})
143     @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
testPassUndefined(boolean useMojo)144     public void testPassUndefined(boolean useMojo) throws Throwable {
145         mActivityTestRule.executeJavaScript("testObject.setIntArray(undefined);");
146         Assert.assertNull(mTestObject.waitForIntArray());
147     }
148 
149     @Test
150     @SmallTest
151     @Feature({"AndroidWebView", "Android-JavaBridge"})
152     @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
testPassEmptyArray(boolean useMojo)153     public void testPassEmptyArray(boolean useMojo) throws Throwable {
154         mActivityTestRule.executeJavaScript("testObject.setIntArray([]);");
155         Assert.assertEquals(0, mTestObject.waitForIntArray().length);
156     }
157 
158     // Note that this requires being able to pass a string from JavaScript to
159     // Java.
160     @Test
161     @SmallTest
162     @Feature({"AndroidWebView", "Android-JavaBridge"})
163     @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
testPassArrayToStringMethod(boolean useMojo)164     public void testPassArrayToStringMethod(boolean useMojo) throws Throwable {
165         // LIVECONNECT_COMPLIANCE: Should call toString() on array.
166         mActivityTestRule.executeJavaScript("testObject.setStringValue([42, 42, 42]);");
167         Assert.assertEquals("undefined", mTestObject.waitForStringValue());
168     }
169 
170     // Note that this requires being able to pass an integer from JavaScript to
171     // Java.
172     @Test
173     @SmallTest
174     @Feature({"AndroidWebView", "Android-JavaBridge"})
175     @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
testPassArrayToNonStringNonArrayMethod(boolean useMojo)176     public void testPassArrayToNonStringNonArrayMethod(boolean useMojo) throws Throwable {
177         // LIVECONNECT_COMPLIANCE: Should raise JavaScript exception.
178         mActivityTestRule.executeJavaScript("testObject.setIntValue([42, 42, 42]);");
179         Assert.assertEquals(0, mTestObject.waitForIntValue());
180     }
181 
182     @Test
183     @SmallTest
184     @Feature({"AndroidWebView", "Android-JavaBridge"})
185     @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
testPassNonArrayToArrayMethod(boolean useMojo)186     public void testPassNonArrayToArrayMethod(boolean useMojo) throws Throwable {
187         // LIVECONNECT_COMPLIANCE: Should raise JavaScript exception.
188         mActivityTestRule.executeJavaScript("testObject.setIntArray(42);");
189         Assert.assertNull(mTestObject.waitForIntArray());
190     }
191 
192     @Test
193     @SmallTest
194     @Feature({"AndroidWebView", "Android-JavaBridge"})
195     @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
testObjectWithLengthProperty(boolean useMojo)196     public void testObjectWithLengthProperty(boolean useMojo) throws Throwable {
197         mActivityTestRule.executeJavaScript("testObject.setIntArray({length: 3, 1: 42});");
198         int[] result = mTestObject.waitForIntArray();
199         Assert.assertEquals(3, result.length);
200         Assert.assertEquals(0, result[0]);
201         Assert.assertEquals(42, result[1]);
202         Assert.assertEquals(0, result[2]);
203     }
204 
205     @Test
206     @SmallTest
207     @Feature({"AndroidWebView", "Android-JavaBridge"})
208     @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
testNonNumericLengthProperty(boolean useMojo)209     public void testNonNumericLengthProperty(boolean useMojo) throws Throwable {
210         // LIVECONNECT_COMPLIANCE: This should not count as an array, so we
211         // should raise a JavaScript exception.
212         mActivityTestRule.executeJavaScript("testObject.setIntArray({length: \"foo\"});");
213         Assert.assertNull(mTestObject.waitForIntArray());
214     }
215 
216     @Test
217     @SmallTest
218     @Feature({"AndroidWebView", "Android-JavaBridge"})
219     @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
testLengthOutOfBounds(boolean useMojo)220     public void testLengthOutOfBounds(boolean useMojo) throws Throwable {
221         // LIVECONNECT_COMPLIANCE: This should not count as an array, so we
222         // should raise a JavaScript exception.
223         mActivityTestRule.executeJavaScript("testObject.setIntArray({length: -1});");
224         Assert.assertNull(mTestObject.waitForIntArray());
225 
226         // LIVECONNECT_COMPLIANCE: This should not count as an array, so we
227         // should raise a JavaScript exception.
228         long length = Integer.MAX_VALUE + 1L;
229         mActivityTestRule.executeJavaScript("testObject.setIntArray({length: " + length + "});");
230         Assert.assertNull(mTestObject.waitForIntArray());
231 
232         // LIVECONNECT_COMPLIANCE: This should not count as an array, so we
233         // should raise a JavaScript exception.
234         length = Integer.MAX_VALUE + 1L - Integer.MIN_VALUE + 1L;
235         mActivityTestRule.executeJavaScript("testObject.setIntArray({length: " + length + "});");
236         Assert.assertNull(mTestObject.waitForIntArray());
237     }
238 
239     @Test
240     @SmallTest
241     @Feature({"AndroidWebView", "Android-JavaBridge"})
242     @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
testSparseArray(boolean useMojo)243     public void testSparseArray(boolean useMojo) throws Throwable {
244         mActivityTestRule.executeJavaScript(
245                 "var x = [42, 43]; x[3] = 45; testObject.setIntArray(x);");
246         int[] result = mTestObject.waitForIntArray();
247         Assert.assertEquals(4, result.length);
248         Assert.assertEquals(42, result[0]);
249         Assert.assertEquals(43, result[1]);
250         Assert.assertEquals(0, result[2]);
251         Assert.assertEquals(45, result[3]);
252     }
253 
254     // Note that this requires being able to pass a boolean from JavaScript to
255     // Java.
256     @Test
257     @SmallTest
258     @Feature({"AndroidWebView", "Android-JavaBridge"})
259     @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
testMethodReturningArrayNotCalled(boolean useMojo)260     public void testMethodReturningArrayNotCalled(boolean useMojo) throws Throwable {
261         // We don't invoke methods which return arrays, but note that no
262         // exception is raised.
263         // LIVECONNECT_COMPLIANCE: Should call method and convert result to
264         // JavaScript array.
265         mActivityTestRule.executeJavaScript(
266                 "testObject.setBooleanValue(undefined === testObject.arrayMethod())");
267         Assert.assertTrue(mTestObject.waitForBooleanValue());
268         Assert.assertFalse(mTestObject.wasArrayMethodCalled());
269     }
270 
271     @Test
272     @SmallTest
273     @Feature({"AndroidWebView", "Android-JavaBridge"})
274     @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
testMultiDimensionalArrayMethod(boolean useMojo)275     public void testMultiDimensionalArrayMethod(boolean useMojo) throws Throwable {
276         // LIVECONNECT_COMPLIANCE: Should handle multi-dimensional arrays.
277         mActivityTestRule.executeJavaScript("testObject.setIntIntArray([ [42, 43], [44, 45] ]);");
278         Assert.assertNull(mTestObject.waitForIntIntArray());
279     }
280 
281     @Test
282     @SmallTest
283     @Feature({"AndroidWebView", "Android-JavaBridge"})
284     @UseMethodParameter(JavaBridgeActivityTestRule.MojoTestParams.class)
testPassMultiDimensionalArray(boolean useMojo)285     public void testPassMultiDimensionalArray(boolean useMojo) throws Throwable {
286         // LIVECONNECT_COMPLIANCE: Should handle multi-dimensional arrays.
287         mActivityTestRule.executeJavaScript("testObject.setIntArray([ [42, 43], [44, 45] ]);");
288         int[] result = mTestObject.waitForIntArray();
289         Assert.assertEquals(2, result.length);
290         Assert.assertEquals(0, result[0]);
291         Assert.assertEquals(0, result[1]);
292     }
293 
294     // Verify that ArrayBuffers are not converted into arrays when passed to Java.
295     // The LiveConnect spec doesn't mention ArrayBuffers, so it doesn't seem to
296     // be a compliance issue.
297     @Test
298     @SmallTest
299     @Feature({"AndroidWebView", "Android-JavaBridge"})
300     @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
testPassArrayBuffer(boolean useMojo)301     public void testPassArrayBuffer(boolean useMojo) throws Throwable {
302         mActivityTestRule.executeJavaScript("buffer = new ArrayBuffer(16);");
303         mActivityTestRule.executeJavaScript("testObject.setIntArray(buffer);");
304         Assert.assertNull(mTestObject.waitForIntArray());
305     }
306 
307     // Verify that ArrayBufferViews are not converted into arrays when passed to Java.
308     // The LiveConnect spec doesn't mention ArrayBufferViews, so it doesn't seem to
309     // be a compliance issue.
310     // Here, a DataView is used as an ArrayBufferView instance (since the latter is
311     // an interface and can't be instantiated directly). See also JavaBridgeArrayCoercionTest
312     // for typed arrays (that also subclass ArrayBufferView) tests.
313     @Test
314     @SmallTest
315     @Feature({"AndroidWebView", "Android-JavaBridge"})
316     @UseMethodParameter(JavaBridgeActivityTestRule.LegacyTestParams.class)
testPassDataView(boolean useMojo)317     public void testPassDataView(boolean useMojo) throws Throwable {
318         mActivityTestRule.executeJavaScript("buffer = new ArrayBuffer(16);");
319         mActivityTestRule.executeJavaScript("testObject.setIntArray(new DataView(buffer));");
320         Assert.assertNull(mTestObject.waitForIntArray());
321     }
322 }
323