summaryrefslogtreecommitdiff
path: root/mojo/android/javatests/src/org/chromium/mojo/bindings
diff options
context:
space:
mode:
Diffstat (limited to 'mojo/android/javatests/src/org/chromium/mojo/bindings')
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java55
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java241
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTestUtils.java108
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java211
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java59
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java108
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java104
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/InterfaceControlMessageTest.java129
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java284
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java69
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java109
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java231
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java175
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java237
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java68
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java151
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterfaceTestHelper.java31
17 files changed, 2370 insertions, 0 deletions
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java
new file mode 100644
index 0000000000..38bd3482e4
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import java.nio.charset.Charset;
+
+/**
+ * Testing {@link BindingsHelper}.
+ */
+public class BindingsHelperTest extends TestCase {
+
+ /**
+ * Testing {@link BindingsHelper#utf8StringSizeInBytes(String)}.
+ */
+ @SmallTest
+ public void testUTF8StringLength() {
+ String[] stringsToTest = {
+ "",
+ "a",
+ "hello world",
+ "éléphant",
+ "𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕",
+ "你午饭想吃什么",
+ "你午饭想吃什么\0éléphant",
+ };
+ for (String s : stringsToTest) {
+ assertEquals(s.getBytes(Charset.forName("utf8")).length,
+ BindingsHelper.utf8StringSizeInBytes(s));
+ }
+ assertEquals(1, BindingsHelper.utf8StringSizeInBytes("\0"));
+ String s = new StringBuilder().appendCodePoint(0x0).appendCodePoint(0x80)
+ .appendCodePoint(0x800).appendCodePoint(0x10000).toString();
+ assertEquals(10, BindingsHelper.utf8StringSizeInBytes(s));
+ assertEquals(10, s.getBytes(Charset.forName("utf8")).length);
+ }
+
+ /**
+ * Testing {@link BindingsHelper#align(int)}.
+ */
+ @SmallTest
+ public void testAlign() {
+ for (int i = 0; i < 3 * BindingsHelper.ALIGNMENT; ++i) {
+ int j = BindingsHelper.align(i);
+ assertTrue(j >= i);
+ assertTrue(j % BindingsHelper.ALIGNMENT == 0);
+ assertTrue(j - i < BindingsHelper.ALIGNMENT);
+ }
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java
new file mode 100644
index 0000000000..d280c774c6
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java
@@ -0,0 +1,241 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.chromium.mojo.HandleMock;
+import org.chromium.mojo.bindings.test.mojom.imported.Color;
+import org.chromium.mojo.bindings.test.mojom.imported.Point;
+import org.chromium.mojo.bindings.test.mojom.imported.Shape;
+import org.chromium.mojo.bindings.test.mojom.imported.Thing;
+import org.chromium.mojo.bindings.test.mojom.sample.Bar;
+import org.chromium.mojo.bindings.test.mojom.sample.Bar.Type;
+import org.chromium.mojo.bindings.test.mojom.sample.DefaultsTest;
+import org.chromium.mojo.bindings.test.mojom.sample.Enum;
+import org.chromium.mojo.bindings.test.mojom.sample.Foo;
+import org.chromium.mojo.bindings.test.mojom.sample.InterfaceConstants;
+import org.chromium.mojo.bindings.test.mojom.sample.SampleServiceConstants;
+import org.chromium.mojo.bindings.test.mojom.test_structs.EmptyStruct;
+import org.chromium.mojo.bindings.test.mojom.test_structs.Rect;
+import org.chromium.mojo.system.DataPipe.ConsumerHandle;
+import org.chromium.mojo.system.DataPipe.ProducerHandle;
+import org.chromium.mojo.system.MessagePipeHandle;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Testing generated classes and associated features.
+ */
+public class BindingsTest extends TestCase {
+
+ /**
+ * Create a new typical Bar instance.
+ */
+ private static Bar newBar() {
+ Bar bar = new Bar();
+ bar.alpha = (byte) 0x01;
+ bar.beta = (byte) 0x02;
+ bar.gamma = (byte) 0x03;
+ bar.type = Type.BOTH;
+ return bar;
+ }
+
+ /**
+ * Create a new typical Foo instance.
+ */
+ private static Foo createFoo() {
+ Foo foo = new Foo();
+ foo.name = "HELLO WORLD";
+ foo.arrayOfArrayOfBools = new boolean[][] {
+ { true, false, true }, { }, { }, { false }, { true } };
+ foo.bar = newBar();
+ foo.a = true;
+ foo.c = true;
+ foo.data = new byte[] { 0x01, 0x02, 0x03 };
+ foo.extraBars = new Bar[] { newBar(), newBar() };
+ String[][][] strings = new String[3][2][1];
+ for (int i0 = 0; i0 < strings.length; ++i0) {
+ for (int i1 = 0; i1 < strings[i0].length; ++i1) {
+ for (int i2 = 0; i2 < strings[i0][i1].length; ++i2) {
+ strings[i0][i1][i2] = "Hello(" + i0 + ", " + i1 + ", " + i2 + ")";
+ }
+ }
+ }
+ foo.multiArrayOfStrings = strings;
+ ConsumerHandle[] inputStreams = new ConsumerHandle[5];
+ for (int i = 0; i < inputStreams.length; ++i) {
+ inputStreams[i] = new HandleMock();
+ }
+ foo.inputStreams = inputStreams;
+ ProducerHandle[] outputStreams = new ProducerHandle[3];
+ for (int i = 0; i < outputStreams.length; ++i) {
+ outputStreams[i] = new HandleMock();
+ }
+ foo.outputStreams = outputStreams;
+ foo.source = new HandleMock();
+ return foo;
+ }
+
+ private static Rect createRect(int x, int y, int width, int height) {
+ Rect rect = new Rect();
+ rect.x = x;
+ rect.y = y;
+ rect.width = width;
+ rect.height = height;
+ return rect;
+ }
+
+ private static <T> void checkConstantField(
+ Field field, Class<T> expectedClass, T value) throws IllegalAccessException {
+ assertEquals(expectedClass, field.getType());
+ assertEquals(Modifier.FINAL, field.getModifiers() & Modifier.FINAL);
+ assertEquals(Modifier.STATIC, field.getModifiers() & Modifier.STATIC);
+ assertEquals(value, field.get(null));
+ }
+
+ private static <T> void checkField(Field field, Class<T> expectedClass,
+ Object object, T value) throws IllegalArgumentException, IllegalAccessException {
+ assertEquals(expectedClass, field.getType());
+ assertEquals(0, field.getModifiers() & Modifier.FINAL);
+ assertEquals(0, field.getModifiers() & Modifier.STATIC);
+ assertEquals(value, field.get(object));
+ }
+
+ /**
+ * Testing constants are correctly generated.
+ */
+ @SmallTest
+ public void testConstants() throws NoSuchFieldException, SecurityException,
+ IllegalAccessException {
+ checkConstantField(SampleServiceConstants.class.getField("TWELVE"), byte.class, (byte) 12);
+ checkConstantField(InterfaceConstants.class.getField("LONG"), long.class, 4405L);
+ }
+
+ /**
+ * Testing enums are correctly generated.
+ */
+ @SmallTest
+ public void testEnums() throws NoSuchFieldException, SecurityException,
+ IllegalAccessException {
+ checkConstantField(Color.class.getField("RED"), int.class, 0);
+ checkConstantField(Color.class.getField("BLACK"), int.class, 1);
+
+ checkConstantField(Enum.class.getField("VALUE"), int.class, 0);
+
+ checkConstantField(Shape.class.getField("RECTANGLE"), int.class, 1);
+ checkConstantField(Shape.class.getField("CIRCLE"), int.class, 2);
+ checkConstantField(Shape.class.getField("TRIANGLE"), int.class, 3);
+ }
+
+ /**
+ * Testing default values on structs.
+ *
+ * @throws IllegalAccessException
+ * @throws IllegalArgumentException
+ */
+ @SmallTest
+ public void testStructDefaults() throws NoSuchFieldException, SecurityException,
+ IllegalArgumentException, IllegalAccessException {
+ // Check default values.
+ DefaultsTest test = new DefaultsTest();
+
+ checkField(DefaultsTest.class.getField("a0"), byte.class, test, (byte) -12);
+ checkField(DefaultsTest.class.getField("a1"), byte.class, test, (byte) 12);
+ checkField(DefaultsTest.class.getField("a2"), short.class, test, (short) 1234);
+ checkField(DefaultsTest.class.getField("a3"), short.class, test, (short) 34567);
+ checkField(DefaultsTest.class.getField("a4"), int.class, test, 123456);
+ checkField(DefaultsTest.class.getField("a5"), int.class, test, (int) 3456789012L);
+ checkField(DefaultsTest.class.getField("a6"), long.class, test, -111111111111L);
+ // -8446744073709551617 == 9999999999999999999 - 2 ^ 64.
+ checkField(DefaultsTest.class.getField("a7"), long.class, test, -8446744073709551617L);
+ checkField(DefaultsTest.class.getField("a8"), int.class, test, 0x12345);
+ checkField(DefaultsTest.class.getField("a9"), int.class, test, -0x12345);
+ checkField(DefaultsTest.class.getField("a10"), int.class, test, 1234);
+ checkField(DefaultsTest.class.getField("a11"), boolean.class, test, true);
+ checkField(DefaultsTest.class.getField("a12"), boolean.class, test, false);
+ checkField(DefaultsTest.class.getField("a13"), float.class, test, (float) 123.25);
+ checkField(DefaultsTest.class.getField("a14"), double.class, test, 1234567890.123);
+ checkField(DefaultsTest.class.getField("a15"), double.class, test, 1E10);
+ checkField(DefaultsTest.class.getField("a16"), double.class, test, -1.2E+20);
+ checkField(DefaultsTest.class.getField("a17"), double.class, test, +1.23E-20);
+ checkField(DefaultsTest.class.getField("a18"), byte[].class, test, null);
+ checkField(DefaultsTest.class.getField("a19"), String.class, test, null);
+ checkField(DefaultsTest.class.getField("a20"), int.class, test, Bar.Type.BOTH);
+ checkField(DefaultsTest.class.getField("a21"), Point.class, test, null);
+
+ assertNotNull(test.a22);
+ checkField(DefaultsTest.class.getField("a22"), Thing.class, test, test.a22);
+ checkField(DefaultsTest.class.getField("a23"), long.class, test, -1L);
+ checkField(DefaultsTest.class.getField("a24"), long.class, test, 0x123456789L);
+ checkField(DefaultsTest.class.getField("a25"), long.class, test, -0x123456789L);
+ }
+
+ /**
+ * Testing generation of the Foo class.
+ *
+ * @throws IllegalAccessException
+ */
+ @SmallTest
+ public void testFooGeneration() throws NoSuchFieldException, SecurityException,
+ IllegalAccessException {
+ // Checking Foo constants.
+ checkConstantField(Foo.class.getField("FOOBY"), String.class, "Fooby");
+
+ // Checking Foo default values.
+ Foo foo = new Foo();
+ checkField(Foo.class.getField("name"), String.class, foo, Foo.FOOBY);
+
+ assertNotNull(foo.source);
+ assertFalse(foo.source.isValid());
+ checkField(Foo.class.getField("source"), MessagePipeHandle.class, foo, foo.source);
+ }
+
+ /**
+ * Testing serialization of the Foo class.
+ */
+ @SmallTest
+ public void testFooSerialization() {
+ // Checking serialization and deserialization of a Foo object.
+ Foo typicalFoo = createFoo();
+ Message serializedFoo = typicalFoo.serialize(null);
+ Foo deserializedFoo = Foo.deserialize(serializedFoo);
+ assertEquals(typicalFoo, deserializedFoo);
+ }
+
+ /**
+ * Testing serialization of the EmptyStruct class.
+ */
+ @SmallTest
+ public void testEmptyStructSerialization() {
+ // Checking serialization and deserialization of a EmptyStruct object.
+ Message serializedStruct = new EmptyStruct().serialize(null);
+ EmptyStruct emptyStruct = EmptyStruct.deserialize(serializedStruct);
+ assertNotNull(emptyStruct);
+ }
+
+ // In testing maps we want to make sure that the key used when inserting an
+ // item the key used when looking it up again are different objects. Java
+ // has default implementations of equals and hashCode that use reference
+ // equality and hashing, respectively, and that's not what we want for our
+ // mojom values.
+ @SmallTest
+ public void testHashMapStructKey() {
+ Map<Rect, Integer> map = new HashMap<>();
+ map.put(createRect(1, 2, 3, 4), 123);
+
+ Rect key = createRect(1, 2, 3, 4);
+ assertNotNull(map.get(key));
+ assertEquals(123, map.get(key).intValue());
+
+ map.remove(key);
+ assertTrue(map.isEmpty());
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTestUtils.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTestUtils.java
new file mode 100644
index 0000000000..5554f805a7
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTestUtils.java
@@ -0,0 +1,108 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.mojo.TestUtils;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.Pair;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+import java.io.Closeable;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class for bindings tests.
+ */
+public class BindingsTestUtils {
+
+ /**
+ * {@link MessageReceiver} that records any message it receives.
+ */
+ public static class RecordingMessageReceiver extends SideEffectFreeCloseable
+ implements MessageReceiver {
+
+ public final List<Message> messages = new ArrayList<Message>();
+
+ /**
+ * @see MessageReceiver#accept(Message)
+ */
+ @Override
+ public boolean accept(Message message) {
+ messages.add(message);
+ return true;
+ }
+ }
+
+ /**
+ * {@link MessageReceiverWithResponder} that records any message it receives.
+ */
+ public static class RecordingMessageReceiverWithResponder extends RecordingMessageReceiver
+ implements MessageReceiverWithResponder {
+
+ public final List<Pair<Message, MessageReceiver>> messagesWithReceivers =
+ new ArrayList<Pair<Message, MessageReceiver>>();
+
+ /**
+ * @see MessageReceiverWithResponder#acceptWithResponder(Message, MessageReceiver)
+ */
+ @Override
+ public boolean acceptWithResponder(Message message, MessageReceiver responder) {
+ messagesWithReceivers.add(Pair.create(message, responder));
+ return true;
+ }
+ }
+
+ /**
+ * {@link ConnectionErrorHandler} that records any error it received.
+ */
+ public static class CapturingErrorHandler implements ConnectionErrorHandler {
+
+ private MojoException mLastMojoException = null;
+
+ /**
+ * @see ConnectionErrorHandler#onConnectionError(MojoException)
+ */
+ @Override
+ public void onConnectionError(MojoException e) {
+ mLastMojoException = e;
+ }
+
+ /**
+ * Returns the last recorded exception.
+ */
+ public MojoException getLastMojoException() {
+ return mLastMojoException;
+ }
+
+ }
+
+ /**
+ * Creates a new valid {@link Message}. The message will have a valid header.
+ */
+ public static Message newRandomMessage(int size) {
+ assert size > 16;
+ ByteBuffer message = TestUtils.newRandomBuffer(size);
+ int[] headerAsInts = {16, 2, 0, 0};
+ for (int i = 0; i < 4; ++i) {
+ message.putInt(4 * i, headerAsInts[i]);
+ }
+ message.position(0);
+ return new Message(message, new ArrayList<Handle>());
+ }
+
+ public static <I extends Interface, P extends Interface.Proxy> P newProxyOverPipe(
+ Interface.Manager<I, P> manager, I impl, List<Closeable> toClose) {
+ Pair<MessagePipeHandle, MessagePipeHandle> handles =
+ CoreImpl.getInstance().createMessagePipe(null);
+ P proxy = manager.attachProxy(handles.first, 0);
+ toClose.add(proxy);
+ manager.bind(impl, handles.second);
+ return proxy;
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java
new file mode 100644
index 0000000000..eea92ab7b8
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java
@@ -0,0 +1,211 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.support.test.filters.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.bindings.test.mojom.test_structs.MultiVersionStruct;
+import org.chromium.mojo.bindings.test.mojom.test_structs.MultiVersionStructV0;
+import org.chromium.mojo.bindings.test.mojom.test_structs.MultiVersionStructV1;
+import org.chromium.mojo.bindings.test.mojom.test_structs.MultiVersionStructV3;
+import org.chromium.mojo.bindings.test.mojom.test_structs.MultiVersionStructV5;
+import org.chromium.mojo.bindings.test.mojom.test_structs.MultiVersionStructV7;
+import org.chromium.mojo.bindings.test.mojom.test_structs.Rect;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+/**
+ * Testing generated classes with the [MinVersion] annotation. Struct in this test are from:
+ * mojo/public/interfaces/bindings/tests/rect.mojom and
+ * mojo/public/interfaces/bindings/tests/test_structs.mojom
+ */
+public class BindingsVersioningTest extends MojoTestCase {
+ private static Rect newRect(int factor) {
+ Rect rect = new Rect();
+ rect.x = factor;
+ rect.y = 2 * factor;
+ rect.width = 10 * factor;
+ rect.height = 20 * factor;
+ return rect;
+ }
+
+ private static MultiVersionStruct newStruct() {
+ MultiVersionStruct struct = new MultiVersionStruct();
+ struct.fInt32 = 123;
+ struct.fRect = newRect(5);
+ struct.fString = "hello";
+ struct.fArray = new byte[] {10, 9, 8};
+ struct.fBool = true;
+ struct.fInt16 = 256;
+ return struct;
+ }
+
+ /**
+ * Testing serializing old struct version to newer one.
+ */
+ @SmallTest
+ public void testOldToNew() {
+ {
+ MultiVersionStructV0 v0 = new MultiVersionStructV0();
+ v0.fInt32 = 123;
+ MultiVersionStruct expected = new MultiVersionStruct();
+ expected.fInt32 = 123;
+
+ MultiVersionStruct output = MultiVersionStruct.deserialize(v0.serialize(null));
+ assertEquals(expected, output);
+ assertEquals(0, v0.getVersion());
+ assertEquals(0, output.getVersion());
+ }
+
+ {
+ MultiVersionStructV1 v1 = new MultiVersionStructV1();
+ v1.fInt32 = 123;
+ v1.fRect = newRect(5);
+ MultiVersionStruct expected = new MultiVersionStruct();
+ expected.fInt32 = 123;
+ expected.fRect = newRect(5);
+
+ MultiVersionStruct output = MultiVersionStruct.deserialize(v1.serialize(null));
+ assertEquals(expected, output);
+ assertEquals(1, v1.getVersion());
+ assertEquals(1, output.getVersion());
+ }
+
+ {
+ MultiVersionStructV3 v3 = new MultiVersionStructV3();
+ v3.fInt32 = 123;
+ v3.fRect = newRect(5);
+ v3.fString = "hello";
+ MultiVersionStruct expected = new MultiVersionStruct();
+ expected.fInt32 = 123;
+ expected.fRect = newRect(5);
+ expected.fString = "hello";
+
+ MultiVersionStruct output = MultiVersionStruct.deserialize(v3.serialize(null));
+ assertEquals(expected, output);
+ assertEquals(3, v3.getVersion());
+ assertEquals(3, output.getVersion());
+ }
+
+ {
+ MultiVersionStructV5 v5 = new MultiVersionStructV5();
+ v5.fInt32 = 123;
+ v5.fRect = newRect(5);
+ v5.fString = "hello";
+ v5.fArray = new byte[] {10, 9, 8};
+ MultiVersionStruct expected = new MultiVersionStruct();
+ expected.fInt32 = 123;
+ expected.fRect = newRect(5);
+ expected.fString = "hello";
+ expected.fArray = new byte[] {10, 9, 8};
+
+ MultiVersionStruct output = MultiVersionStruct.deserialize(v5.serialize(null));
+ assertEquals(expected, output);
+ assertEquals(5, v5.getVersion());
+ assertEquals(5, output.getVersion());
+ }
+
+ {
+ int expectedHandle = 42;
+ MultiVersionStructV7 v7 = new MultiVersionStructV7();
+ v7.fInt32 = 123;
+ v7.fRect = newRect(5);
+ v7.fString = "hello";
+ v7.fArray = new byte[] {10, 9, 8};
+ v7.fMessagePipe = CoreImpl.getInstance()
+ .acquireNativeHandle(expectedHandle)
+ .toMessagePipeHandle();
+ v7.fBool = true;
+ MultiVersionStruct expected = new MultiVersionStruct();
+ expected.fInt32 = 123;
+ expected.fRect = newRect(5);
+ expected.fString = "hello";
+ expected.fArray = new byte[] {10, 9, 8};
+ expected.fBool = true;
+
+ MultiVersionStruct output = MultiVersionStruct.deserialize(v7.serialize(null));
+
+ // Handles must be tested separately.
+ assertEquals(expectedHandle, output.fMessagePipe.releaseNativeHandle());
+ output.fMessagePipe = expected.fMessagePipe;
+
+ assertEquals(expected, output);
+ assertEquals(7, v7.getVersion());
+ assertEquals(7, output.getVersion());
+ }
+ }
+
+ /**
+ * Testing serializing new struct version to older one.
+ */
+ @SmallTest
+ public void testNewToOld() {
+ MultiVersionStruct struct = newStruct();
+ {
+ MultiVersionStructV0 expected = new MultiVersionStructV0();
+ expected.fInt32 = 123;
+
+ MultiVersionStructV0 output = MultiVersionStructV0.deserialize(struct.serialize(null));
+ assertEquals(expected, output);
+ assertEquals(9, output.getVersion());
+ }
+
+ {
+ MultiVersionStructV1 expected = new MultiVersionStructV1();
+ expected.fInt32 = 123;
+ expected.fRect = newRect(5);
+
+ MultiVersionStructV1 output = MultiVersionStructV1.deserialize(struct.serialize(null));
+ assertEquals(expected, output);
+ assertEquals(9, output.getVersion());
+ }
+
+ {
+ MultiVersionStructV3 expected = new MultiVersionStructV3();
+ expected.fInt32 = 123;
+ expected.fRect = newRect(5);
+ expected.fString = "hello";
+
+ MultiVersionStructV3 output = MultiVersionStructV3.deserialize(struct.serialize(null));
+ assertEquals(expected, output);
+ assertEquals(9, output.getVersion());
+ }
+
+ {
+ MultiVersionStructV5 expected = new MultiVersionStructV5();
+ expected.fInt32 = 123;
+ expected.fRect = newRect(5);
+ expected.fString = "hello";
+ expected.fArray = new byte[] {10, 9, 8};
+
+ MultiVersionStructV5 output = MultiVersionStructV5.deserialize(struct.serialize(null));
+ assertEquals(expected, output);
+ assertEquals(9, output.getVersion());
+ }
+
+ {
+ int expectedHandle = 42;
+ MultiVersionStructV7 expected = new MultiVersionStructV7();
+ expected.fInt32 = 123;
+ expected.fRect = newRect(5);
+ expected.fString = "hello";
+ expected.fArray = new byte[] {10, 9, 8};
+ expected.fBool = true;
+
+ MultiVersionStruct input = struct;
+ input.fMessagePipe = CoreImpl.getInstance()
+ .acquireNativeHandle(expectedHandle)
+ .toMessagePipeHandle();
+
+ MultiVersionStructV7 output = MultiVersionStructV7.deserialize(input.serialize(null));
+
+ assertEquals(expectedHandle, output.fMessagePipe.releaseNativeHandle());
+ output.fMessagePipe = expected.fMessagePipe;
+
+ assertEquals(expected, output);
+ assertEquals(9, output.getVersion());
+ }
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java
new file mode 100644
index 0000000000..497be65af2
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.chromium.mojo.bindings.Callbacks.Callback1;
+import org.chromium.mojo.bindings.Callbacks.Callback7;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Testing generated callbacks
+ */
+public class CallbacksTest extends TestCase {
+
+ /**
+ * Testing {@link Callback1}.
+ */
+ @SmallTest
+ public void testCallback1() {
+ final List<Integer> parameters = new ArrayList<Integer>();
+ new Callback1<Integer>() {
+ @Override
+ public void call(Integer i1) {
+ parameters.add(i1);
+ }
+ }.call(1);
+ assertEquals(Arrays.asList(1), parameters);
+ }
+
+ /**
+ * Testing {@link Callback7}.
+ */
+ @SmallTest
+ public void testCallback7() {
+ final List<Integer> parameters = new ArrayList<Integer>();
+ new Callback7<Integer, Integer, Integer, Integer, Integer, Integer, Integer>() {
+ @Override
+ public void call(Integer i1, Integer i2, Integer i3, Integer i4, Integer i5, Integer i6,
+ Integer i7) {
+ parameters.add(i1);
+ parameters.add(i2);
+ parameters.add(i3);
+ parameters.add(i4);
+ parameters.add(i5);
+ parameters.add(i6);
+ parameters.add(i7);
+ }
+ }.call(1, 2, 3, 4, 5, 6, 7);
+ assertEquals(Arrays.asList(1, 2, 3, 4, 5, 6, 7), parameters);
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java
new file mode 100644
index 0000000000..15f9f1fa33
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java
@@ -0,0 +1,108 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.support.test.filters.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.bindings.BindingsTestUtils.CapturingErrorHandler;
+import org.chromium.mojo.bindings.BindingsTestUtils.RecordingMessageReceiver;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MojoResult;
+import org.chromium.mojo.system.Pair;
+import org.chromium.mojo.system.ResultAnd;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * Testing the {@link Connector} class.
+ */
+public class ConnectorTest extends MojoTestCase {
+
+ private static final int DATA_LENGTH = 1024;
+
+ private MessagePipeHandle mHandle;
+ private Connector mConnector;
+ private Message mTestMessage;
+ private RecordingMessageReceiver mReceiver;
+ private CapturingErrorHandler mErrorHandler;
+
+ /**
+ * @see MojoTestCase#setUp()
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Core core = CoreImpl.getInstance();
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(
+ new MessagePipeHandle.CreateOptions());
+ mHandle = handles.first;
+ mConnector = new Connector(handles.second);
+ mReceiver = new RecordingMessageReceiver();
+ mConnector.setIncomingMessageReceiver(mReceiver);
+ mErrorHandler = new CapturingErrorHandler();
+ mConnector.setErrorHandler(mErrorHandler);
+ mConnector.start();
+ mTestMessage = BindingsTestUtils.newRandomMessage(DATA_LENGTH);
+ assertNull(mErrorHandler.getLastMojoException());
+ assertEquals(0, mReceiver.messages.size());
+ }
+
+ /**
+ * @see MojoTestCase#tearDown()
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ mConnector.close();
+ mHandle.close();
+ super.tearDown();
+ }
+
+ /**
+ * Test sending a message through a {@link Connector}.
+ */
+ @SmallTest
+ public void testSendingMessage() {
+ mConnector.accept(mTestMessage);
+ assertNull(mErrorHandler.getLastMojoException());
+ ByteBuffer received = ByteBuffer.allocateDirect(DATA_LENGTH);
+ ResultAnd<MessagePipeHandle.ReadMessageResult> result =
+ mHandle.readMessage(received, 0, MessagePipeHandle.ReadFlags.NONE);
+ assertEquals(MojoResult.OK, result.getMojoResult());
+ assertEquals(DATA_LENGTH, result.getValue().getMessageSize());
+ assertEquals(mTestMessage.getData(), received);
+ }
+
+ /**
+ * Test receiving a message through a {@link Connector}
+ */
+ @SmallTest
+ public void testReceivingMessage() {
+ mHandle.writeMessage(mTestMessage.getData(), new ArrayList<Handle>(),
+ MessagePipeHandle.WriteFlags.NONE);
+ runLoopUntilIdle();
+ assertNull(mErrorHandler.getLastMojoException());
+ assertEquals(1, mReceiver.messages.size());
+ Message received = mReceiver.messages.get(0);
+ assertEquals(0, received.getHandles().size());
+ assertEquals(mTestMessage.getData(), received.getData());
+ }
+
+ /**
+ * Test receiving an error through a {@link Connector}.
+ */
+ @SmallTest
+ public void testErrors() {
+ mHandle.close();
+ runLoopUntilIdle();
+ assertNotNull(mErrorHandler.getLastMojoException());
+ assertEquals(MojoResult.FAILED_PRECONDITION,
+ mErrorHandler.getLastMojoException().getMojoResult());
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java
new file mode 100644
index 0000000000..cabe2306b8
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java
@@ -0,0 +1,104 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.support.test.filters.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Testing the executor factory.
+ */
+public class ExecutorFactoryTest extends MojoTestCase {
+
+ private static final long RUN_LOOP_TIMEOUT_MS = 50;
+ private static final int CONCURRENCY_LEVEL = 5;
+ private static final ExecutorService WORKERS = Executors.newFixedThreadPool(CONCURRENCY_LEVEL);
+
+ private Executor mExecutor;
+ private List<Thread> mThreadContainer;
+
+ /**
+ * @see MojoTestCase#setUp()
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mExecutor = ExecutorFactory.getExecutorForCurrentThread(CoreImpl.getInstance());
+ mThreadContainer = new ArrayList<Thread>();
+ }
+
+ /**
+ * Testing the {@link Executor} when called from the executor thread.
+ */
+ @SmallTest
+ public void testExecutorOnCurrentThread() {
+ Runnable action = new Runnable() {
+ @Override
+ public void run() {
+ mThreadContainer.add(Thread.currentThread());
+ }
+ };
+ mExecutor.execute(action);
+ mExecutor.execute(action);
+ assertEquals(0, mThreadContainer.size());
+ runLoop(RUN_LOOP_TIMEOUT_MS);
+ assertEquals(2, mThreadContainer.size());
+ for (Thread thread : mThreadContainer) {
+ assertEquals(Thread.currentThread(), thread);
+ }
+ }
+
+ /**
+ * Testing the {@link Executor} when called from another thread.
+ */
+ @SmallTest
+ public void testExecutorOnOtherThread() {
+ final CyclicBarrier barrier = new CyclicBarrier(CONCURRENCY_LEVEL + 1);
+ for (int i = 0; i < CONCURRENCY_LEVEL; ++i) {
+ WORKERS.execute(new Runnable() {
+ @Override
+ public void run() {
+ mExecutor.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ mThreadContainer.add(Thread.currentThread());
+ }
+ });
+ try {
+ barrier.await();
+ } catch (InterruptedException e) {
+ fail("Unexpected exception: " + e.getMessage());
+ } catch (BrokenBarrierException e) {
+ fail("Unexpected exception: " + e.getMessage());
+ }
+ }
+ });
+ }
+ try {
+ barrier.await();
+ } catch (InterruptedException e) {
+ fail("Unexpected exception: " + e.getMessage());
+ } catch (BrokenBarrierException e) {
+ fail("Unexpected exception: " + e.getMessage());
+ }
+ assertEquals(0, mThreadContainer.size());
+ runLoop(RUN_LOOP_TIMEOUT_MS);
+ assertEquals(CONCURRENCY_LEVEL, mThreadContainer.size());
+ for (Thread thread : mThreadContainer) {
+ assertEquals(Thread.currentThread(), thread);
+ }
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfaceControlMessageTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfaceControlMessageTest.java
new file mode 100644
index 0000000000..8cdd4abf29
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfaceControlMessageTest.java
@@ -0,0 +1,129 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.support.test.filters.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.bindings.Callbacks.Callback1;
+import org.chromium.mojo.bindings.test.mojom.sample.Enum;
+import org.chromium.mojo.bindings.test.mojom.sample.IntegerAccessor;
+import org.chromium.mojo.system.MojoException;
+
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests for interface control messages.
+ */
+public class InterfaceControlMessageTest extends MojoTestCase {
+ private final List<Closeable> mCloseablesToClose = new ArrayList<Closeable>();
+
+ /**
+ * See mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.
+ */
+ class IntegerAccessorImpl extends SideEffectFreeCloseable implements IntegerAccessor {
+ private long mValue = 0;
+ private int mEnum = 0;
+ private boolean mEncounteredError = false;
+
+ /**
+ * @see ConnectionErrorHandler#onConnectionError(MojoException)
+ */
+ @Override
+ public void onConnectionError(MojoException e) {
+ mEncounteredError = true;
+ }
+
+ /**
+ * @see IntegerAccessor#getInteger(IntegerAccessor.GetIntegerResponse)
+ */
+ @Override
+ public void getInteger(GetIntegerResponse response) {
+ response.call(mValue, mEnum);
+ }
+
+ /**
+ * @see IntegerAccessor#setInteger(long, int)
+ */
+ @Override
+ public void setInteger(long value, int enumValue) {
+ mValue = value;
+ mEnum = enumValue;
+ }
+
+ public long getValue() {
+ return mValue;
+ }
+
+ public boolean encounteredError() {
+ return mEncounteredError;
+ }
+ }
+
+ /**
+ * @see MojoTestCase#tearDown()
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ // Close the elements in the reverse order they were added. This is needed because it is an
+ // error to close the handle of a proxy without closing the proxy first.
+ Collections.reverse(mCloseablesToClose);
+ for (Closeable c : mCloseablesToClose) {
+ c.close();
+ }
+ super.tearDown();
+ }
+
+ @SmallTest
+ public void testQueryVersion() {
+ IntegerAccessor.Proxy p = BindingsTestUtils.newProxyOverPipe(
+ IntegerAccessor.MANAGER, new IntegerAccessorImpl(), mCloseablesToClose);
+ assertEquals(0, p.getProxyHandler().getVersion());
+ p.getProxyHandler().queryVersion(new Callback1<Integer>() {
+ @Override
+ public void call(Integer version) {
+ assertEquals(3, version.intValue());
+ }
+ });
+ runLoopUntilIdle();
+ assertEquals(3, p.getProxyHandler().getVersion());
+ }
+
+ @SmallTest
+ public void testRequireVersion() {
+ IntegerAccessorImpl impl = new IntegerAccessorImpl();
+ IntegerAccessor.Proxy p = BindingsTestUtils.newProxyOverPipe(
+ IntegerAccessor.MANAGER, impl, mCloseablesToClose);
+
+ assertEquals(0, p.getProxyHandler().getVersion());
+
+ p.getProxyHandler().requireVersion(1);
+ assertEquals(1, p.getProxyHandler().getVersion());
+ p.setInteger(123, Enum.VALUE);
+ runLoopUntilIdle();
+ assertFalse(impl.encounteredError());
+ assertEquals(123, impl.getValue());
+
+ p.getProxyHandler().requireVersion(3);
+ assertEquals(3, p.getProxyHandler().getVersion());
+ p.setInteger(456, Enum.VALUE);
+ runLoopUntilIdle();
+ assertFalse(impl.encounteredError());
+ assertEquals(456, impl.getValue());
+
+ // Require a version that is not supported by the implementation side.
+ p.getProxyHandler().requireVersion(4);
+ // getVersion() is updated synchronously.
+ assertEquals(4, p.getProxyHandler().getVersion());
+ p.setInteger(789, Enum.VALUE);
+ runLoopUntilIdle();
+ assertTrue(impl.encounteredError());
+ // The call to setInteger() after requireVersion() is ignored.
+ assertEquals(456, impl.getValue());
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java
new file mode 100644
index 0000000000..d8bd3e85ad
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java
@@ -0,0 +1,284 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.support.test.filters.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.bindings.BindingsTestUtils.CapturingErrorHandler;
+import org.chromium.mojo.bindings.test.mojom.imported.ImportedInterface;
+import org.chromium.mojo.bindings.test.mojom.sample.Factory;
+import org.chromium.mojo.bindings.test.mojom.sample.NamedObject;
+import org.chromium.mojo.bindings.test.mojom.sample.NamedObject.GetNameResponse;
+import org.chromium.mojo.bindings.test.mojom.sample.Request;
+import org.chromium.mojo.bindings.test.mojom.sample.Response;
+import org.chromium.mojo.system.DataPipe.ConsumerHandle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.Pair;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests for interfaces / proxies / stubs generated for sample_factory.mojom.
+ */
+public class InterfacesTest extends MojoTestCase {
+
+ private static final String OBJECT_NAME = "hello world";
+
+ private final List<Closeable> mCloseablesToClose = new ArrayList<Closeable>();
+
+ /**
+ * Basic implementation of {@link NamedObject}.
+ */
+ public static class MockNamedObjectImpl extends CapturingErrorHandler implements NamedObject {
+
+ private String mName = "";
+
+ /**
+ * @see org.chromium.mojo.bindings.Interface#close()
+ */
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public void setName(String name) {
+ mName = name;
+ }
+
+ @Override
+ public void getName(GetNameResponse callback) {
+ callback.call(mName);
+ }
+
+ public String getNameSynchronously() {
+ return mName;
+ }
+ }
+
+ /**
+ * Implementation of {@link GetNameResponse} keeping track of usage.
+ */
+ public static class RecordingGetNameResponse implements GetNameResponse {
+ private String mName;
+ private boolean mCalled;
+
+ public RecordingGetNameResponse() {
+ reset();
+ }
+
+ @Override
+ public void call(String name) {
+ mName = name;
+ mCalled = true;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public boolean wasCalled() {
+ return mCalled;
+ }
+
+ public void reset() {
+ mName = null;
+ mCalled = false;
+ }
+ }
+
+ /**
+ * Basic implementation of {@link Factory}.
+ */
+ public class MockFactoryImpl extends CapturingErrorHandler implements Factory {
+
+ private boolean mClosed = false;
+
+ public boolean isClosed() {
+ return mClosed;
+ }
+
+ /**
+ * @see org.chromium.mojo.bindings.Interface#close()
+ */
+ @Override
+ public void close() {
+ mClosed = true;
+ }
+
+ @Override
+ public void doStuff(Request request, MessagePipeHandle pipe, DoStuffResponse callback) {
+ if (pipe != null) {
+ pipe.close();
+ }
+ Response response = new Response();
+ response.x = 42;
+ callback.call(response, "Hello");
+ }
+
+ @Override
+ public void doStuff2(ConsumerHandle pipe, DoStuff2Response callback) {
+ callback.call("World");
+ }
+
+ @Override
+ public void createNamedObject(InterfaceRequest<NamedObject> obj) {
+ NamedObject.MANAGER.bind(new MockNamedObjectImpl(), obj);
+ }
+
+ @Override
+ public void requestImportedInterface(InterfaceRequest<ImportedInterface> obj,
+ RequestImportedInterfaceResponse callback) {
+ throw new UnsupportedOperationException("Not implemented.");
+ }
+
+ @Override
+ public void takeImportedInterface(ImportedInterface obj,
+ TakeImportedInterfaceResponse callback) {
+ throw new UnsupportedOperationException("Not implemented.");
+ }
+ }
+
+ /**
+ * Implementation of DoStuffResponse that keeps track of if the response is called.
+ */
+ public static class DoStuffResponseImpl implements Factory.DoStuffResponse {
+ private boolean mResponseCalled = false;
+
+ public boolean wasResponseCalled() {
+ return mResponseCalled;
+ }
+
+ @Override
+ public void call(Response response, String string) {
+ mResponseCalled = true;
+ }
+ }
+
+ /**
+ * @see MojoTestCase#tearDown()
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ // Close the elements in the reverse order they were added. This is needed because it is an
+ // error to close the handle of a proxy without closing the proxy first.
+ Collections.reverse(mCloseablesToClose);
+ for (Closeable c : mCloseablesToClose) {
+ c.close();
+ }
+ super.tearDown();
+ }
+
+ /**
+ * Check that the given proxy receives the calls. If |impl| is not null, also check that the
+ * calls are forwared to |impl|.
+ */
+ private void checkProxy(NamedObject.Proxy proxy, MockNamedObjectImpl impl) {
+ RecordingGetNameResponse callback = new RecordingGetNameResponse();
+ CapturingErrorHandler errorHandler = new CapturingErrorHandler();
+ proxy.getProxyHandler().setErrorHandler(errorHandler);
+
+ if (impl != null) {
+ assertNull(impl.getLastMojoException());
+ assertEquals("", impl.getNameSynchronously());
+ }
+
+ proxy.getName(callback);
+ runLoopUntilIdle();
+
+ assertNull(errorHandler.getLastMojoException());
+ assertTrue(callback.wasCalled());
+ assertEquals("", callback.getName());
+
+ callback.reset();
+ proxy.setName(OBJECT_NAME);
+ runLoopUntilIdle();
+
+ assertNull(errorHandler.getLastMojoException());
+ if (impl != null) {
+ assertNull(impl.getLastMojoException());
+ assertEquals(OBJECT_NAME, impl.getNameSynchronously());
+ }
+
+ proxy.getName(callback);
+ runLoopUntilIdle();
+
+ assertNull(errorHandler.getLastMojoException());
+ assertTrue(callback.wasCalled());
+ assertEquals(OBJECT_NAME, callback.getName());
+ }
+
+ @SmallTest
+ public void testName() {
+ assertEquals("sample::NamedObject", NamedObject.MANAGER.getName());
+ }
+
+ @SmallTest
+ public void testProxyAndStub() {
+ MockNamedObjectImpl impl = new MockNamedObjectImpl();
+ NamedObject.Proxy proxy =
+ NamedObject.MANAGER.buildProxy(null, NamedObject.MANAGER.buildStub(null, impl));
+
+ checkProxy(proxy, impl);
+ }
+
+ @SmallTest
+ public void testProxyAndStubOverPipe() {
+ MockNamedObjectImpl impl = new MockNamedObjectImpl();
+ NamedObject.Proxy proxy =
+ BindingsTestUtils.newProxyOverPipe(NamedObject.MANAGER, impl, mCloseablesToClose);
+
+ checkProxy(proxy, impl);
+ }
+
+ @SmallTest
+ public void testFactoryOverPipe() {
+ Factory.Proxy proxy = BindingsTestUtils.newProxyOverPipe(
+ Factory.MANAGER, new MockFactoryImpl(), mCloseablesToClose);
+ Pair<NamedObject.Proxy, InterfaceRequest<NamedObject>> request =
+ NamedObject.MANAGER.getInterfaceRequest(CoreImpl.getInstance());
+ mCloseablesToClose.add(request.first);
+ proxy.createNamedObject(request.second);
+
+ checkProxy(request.first, null);
+ }
+
+ @SmallTest
+ public void testInterfaceClosing() {
+ MockFactoryImpl impl = new MockFactoryImpl();
+ Factory.Proxy proxy =
+ BindingsTestUtils.newProxyOverPipe(Factory.MANAGER, impl, mCloseablesToClose);
+
+ assertFalse(impl.isClosed());
+
+ proxy.close();
+ runLoopUntilIdle();
+
+ assertTrue(impl.isClosed());
+ }
+
+ @SmallTest
+ public void testResponse() {
+ MockFactoryImpl impl = new MockFactoryImpl();
+ Factory.Proxy proxy =
+ BindingsTestUtils.newProxyOverPipe(Factory.MANAGER, impl, mCloseablesToClose);
+ Request request = new Request();
+ request.x = 42;
+ Pair<MessagePipeHandle, MessagePipeHandle> handles =
+ CoreImpl.getInstance().createMessagePipe(null);
+ DoStuffResponseImpl response = new DoStuffResponseImpl();
+ proxy.doStuff(request, handles.first, response);
+
+ assertFalse(response.wasResponseCalled());
+
+ runLoopUntilIdle();
+
+ assertTrue(response.wasResponseCalled());
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java
new file mode 100644
index 0000000000..b2e6ac8521
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java
@@ -0,0 +1,69 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.chromium.mojo.bindings.test.mojom.imported.Point;
+
+/**
+ * Testing internal classes of interfaces.
+ */
+public class MessageHeaderTest extends TestCase {
+
+ /**
+ * Testing that headers are identical after being serialized/deserialized.
+ */
+ @SmallTest
+ public void testSimpleMessageHeader() {
+ final int xValue = 1;
+ final int yValue = 2;
+ final int type = 6;
+ Point p = new Point();
+ p.x = xValue;
+ p.y = yValue;
+ ServiceMessage message = p.serializeWithHeader(null, new MessageHeader(type));
+
+ MessageHeader header = message.getHeader();
+ assertTrue(header.validateHeader(type, 0));
+ assertEquals(type, header.getType());
+ assertEquals(0, header.getFlags());
+
+ Point p2 = Point.deserialize(message.getPayload());
+ assertNotNull(p2);
+ assertEquals(p.x, p2.x);
+ assertEquals(p.y, p2.y);
+ }
+
+ /**
+ * Testing that headers are identical after being serialized/deserialized.
+ */
+ @SmallTest
+ public void testMessageWithRequestIdHeader() {
+ final int xValue = 1;
+ final int yValue = 2;
+ final int type = 6;
+ final long requestId = 0x1deadbeafL;
+ Point p = new Point();
+ p.x = xValue;
+ p.y = yValue;
+ ServiceMessage message = p.serializeWithHeader(null,
+ new MessageHeader(type, MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, 0));
+ message.setRequestId(requestId);
+
+ MessageHeader header = message.getHeader();
+ assertTrue(header.validateHeader(type, MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG));
+ assertEquals(type, header.getType());
+ assertEquals(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, header.getFlags());
+ assertEquals(requestId, header.getRequestId());
+
+ Point p2 = Point.deserialize(message.getPayload());
+ assertNotNull(p2);
+ assertEquals(p.x, p2.x);
+ assertEquals(p.y, p2.y);
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java
new file mode 100644
index 0000000000..51dbd22800
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.support.test.filters.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.bindings.BindingsTestUtils.RecordingMessageReceiver;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.DataPipe;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.MojoResult;
+import org.chromium.mojo.system.Pair;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Testing {@link Connector#readAndDispatchMessage}.
+ */
+public class ReadAndDispatchMessageTest extends MojoTestCase {
+
+ private static final int DATA_SIZE = 1024;
+
+ private ByteBuffer mData;
+ private Pair<MessagePipeHandle, MessagePipeHandle> mHandles;
+ private List<Handle> mHandlesToSend = new ArrayList<Handle>();
+ private List<Handle> mHandlesToClose = new ArrayList<Handle>();
+ private RecordingMessageReceiver mMessageReceiver;
+
+ /**
+ * @see org.chromium.mojo.MojoTestCase#setUp()
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Core core = CoreImpl.getInstance();
+ mData = BindingsTestUtils.newRandomMessage(DATA_SIZE).getData();
+ mMessageReceiver = new RecordingMessageReceiver();
+ mHandles = core.createMessagePipe(new MessagePipeHandle.CreateOptions());
+ Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> datapipe = core.createDataPipe(null);
+ mHandlesToSend.addAll(Arrays.asList(datapipe.first, datapipe.second));
+ mHandlesToClose.addAll(Arrays.asList(mHandles.first, mHandles.second));
+ mHandlesToClose.addAll(mHandlesToSend);
+ }
+
+ /**
+ * @see org.chromium.mojo.MojoTestCase#tearDown()
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ for (Handle handle : mHandlesToClose) {
+ handle.close();
+ }
+ super.tearDown();
+ }
+
+ /**
+ * Testing {@link Connector#readAndDispatchMessage(MessagePipeHandle, MessageReceiver)}
+ */
+ @SmallTest
+ public void testReadAndDispatchMessage() {
+ mHandles.first.writeMessage(mData, mHandlesToSend, MessagePipeHandle.WriteFlags.NONE);
+ assertEquals(MojoResult.OK, Connector.readAndDispatchMessage(mHandles.second,
+ mMessageReceiver).getMojoResult());
+ assertEquals(1, mMessageReceiver.messages.size());
+ Message message = mMessageReceiver.messages.get(0);
+ mHandlesToClose.addAll(message.getHandles());
+ assertEquals(mData, message.getData());
+ assertEquals(2, message.getHandles().size());
+ for (Handle handle : message.getHandles()) {
+ assertTrue(handle.isValid());
+ }
+ }
+
+ /**
+ * Testing {@link Connector#readAndDispatchMessage(MessagePipeHandle, MessageReceiver)}
+ * with no message available.
+ */
+ @SmallTest
+ public void testReadAndDispatchMessageOnEmptyHandle() {
+ assertEquals(MojoResult.SHOULD_WAIT, Connector.readAndDispatchMessage(mHandles.second,
+ mMessageReceiver).getMojoResult());
+ assertEquals(0, mMessageReceiver.messages.size());
+ }
+
+ /**
+ * Testing {@link Connector#readAndDispatchMessage(MessagePipeHandle, MessageReceiver)}
+ * on closed handle.
+ */
+ @SmallTest
+ public void testReadAndDispatchMessageOnClosedHandle() {
+ mHandles.first.close();
+ try {
+ Connector.readAndDispatchMessage(mHandles.second, mMessageReceiver);
+ fail("MojoException should have been thrown");
+ } catch (MojoException expected) {
+ assertEquals(MojoResult.FAILED_PRECONDITION, expected.getMojoResult());
+ }
+ assertEquals(0, mMessageReceiver.messages.size());
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
new file mode 100644
index 0000000000..6aa1726b4c
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
@@ -0,0 +1,231 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.support.test.filters.SmallTest;
+
+import org.chromium.base.annotations.SuppressFBWarnings;
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.bindings.BindingsTestUtils.CapturingErrorHandler;
+import org.chromium.mojo.bindings.BindingsTestUtils.RecordingMessageReceiverWithResponder;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.Core.HandleSignals;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MojoResult;
+import org.chromium.mojo.system.Pair;
+import org.chromium.mojo.system.ResultAnd;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * Testing {@link Router}
+ */
+public class RouterTest extends MojoTestCase {
+
+ private MessagePipeHandle mHandle;
+ private Router mRouter;
+ private RecordingMessageReceiverWithResponder mReceiver;
+ private CapturingErrorHandler mErrorHandler;
+
+ /**
+ * @see MojoTestCase#setUp()
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Core core = CoreImpl.getInstance();
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+ mHandle = handles.first;
+ mRouter = new RouterImpl(handles.second);
+ mReceiver = new RecordingMessageReceiverWithResponder();
+ mRouter.setIncomingMessageReceiver(mReceiver);
+ mErrorHandler = new CapturingErrorHandler();
+ mRouter.setErrorHandler(mErrorHandler);
+ mRouter.start();
+ }
+
+ /**
+ * Testing sending a message via the router that expected a response.
+ */
+ @SmallTest
+ public void testSendingToRouterWithResponse() {
+ final int requestMessageType = 0xdead;
+ final int responseMessageType = 0xbeaf;
+
+ // Sending a message expecting a response.
+ MessageHeader header = new MessageHeader(requestMessageType,
+ MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, 0);
+ Encoder encoder = new Encoder(CoreImpl.getInstance(), header.getSize());
+ header.encode(encoder);
+ mRouter.acceptWithResponder(encoder.getMessage(), mReceiver);
+ ByteBuffer receiveBuffer = ByteBuffer.allocateDirect(header.getSize());
+ ResultAnd<MessagePipeHandle.ReadMessageResult> result =
+ mHandle.readMessage(receiveBuffer, 0, MessagePipeHandle.ReadFlags.NONE);
+
+ assertEquals(MojoResult.OK, result.getMojoResult());
+ MessageHeader receivedHeader = new Message(
+ receiveBuffer, new ArrayList<Handle>()).asServiceMessage().getHeader();
+
+ assertEquals(header.getType(), receivedHeader.getType());
+ assertEquals(header.getFlags(), receivedHeader.getFlags());
+ assertTrue(receivedHeader.getRequestId() != 0);
+
+ // Sending the response.
+ MessageHeader responseHeader = new MessageHeader(responseMessageType,
+ MessageHeader.MESSAGE_IS_RESPONSE_FLAG, receivedHeader.getRequestId());
+ encoder = new Encoder(CoreImpl.getInstance(), header.getSize());
+ responseHeader.encode(encoder);
+ Message responseMessage = encoder.getMessage();
+ mHandle.writeMessage(responseMessage.getData(), new ArrayList<Handle>(),
+ MessagePipeHandle.WriteFlags.NONE);
+ runLoopUntilIdle();
+
+ assertEquals(1, mReceiver.messages.size());
+ ServiceMessage receivedResponseMessage = mReceiver.messages.get(0).asServiceMessage();
+ assertEquals(MessageHeader.MESSAGE_IS_RESPONSE_FLAG,
+ receivedResponseMessage.getHeader().getFlags());
+ assertEquals(responseMessage.getData(), receivedResponseMessage.getData());
+ }
+
+ /**
+ * Sends a message to the Router.
+ *
+ * @param messageIndex Used when sending multiple messages to indicate the index of this
+ * message.
+ * @param requestMessageType The message type to use in the header of the sent message.
+ * @param requestId The requestId to use in the header of the sent message.
+ */
+ private void sendMessageToRouter(int messageIndex, int requestMessageType, int requestId) {
+ MessageHeader header = new MessageHeader(
+ requestMessageType, MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, requestId);
+ Encoder encoder = new Encoder(CoreImpl.getInstance(), header.getSize());
+ header.encode(encoder);
+ Message headerMessage = encoder.getMessage();
+ mHandle.writeMessage(headerMessage.getData(), new ArrayList<Handle>(),
+ MessagePipeHandle.WriteFlags.NONE);
+ runLoopUntilIdle();
+
+ assertEquals(messageIndex + 1, mReceiver.messagesWithReceivers.size());
+ Pair<Message, MessageReceiver> receivedMessage =
+ mReceiver.messagesWithReceivers.get(messageIndex);
+ assertEquals(headerMessage.getData(), receivedMessage.first.getData());
+ }
+
+ /**
+ * Sends a response message from the Router.
+ *
+ * @param messageIndex Used when sending responses to multiple messages to indicate the index
+ * of the message that this message is a response to.
+ * @param responseMessageType The message type to use in the header of the response message.
+ */
+ private void sendResponseFromRouter(int messageIndex, int responseMessageType) {
+ Pair<Message, MessageReceiver> receivedMessage =
+ mReceiver.messagesWithReceivers.get(messageIndex);
+
+ long requestId = receivedMessage.first.asServiceMessage().getHeader().getRequestId();
+
+ MessageHeader responseHeader = new MessageHeader(
+ responseMessageType, MessageHeader.MESSAGE_IS_RESPONSE_FLAG, requestId);
+ Encoder encoder = new Encoder(CoreImpl.getInstance(), responseHeader.getSize());
+ responseHeader.encode(encoder);
+ Message message = encoder.getMessage();
+ receivedMessage.second.accept(message);
+
+ ByteBuffer receivedResponseMessage = ByteBuffer.allocateDirect(responseHeader.getSize());
+ ResultAnd<MessagePipeHandle.ReadMessageResult> result =
+ mHandle.readMessage(receivedResponseMessage, 0, MessagePipeHandle.ReadFlags.NONE);
+
+ assertEquals(MojoResult.OK, result.getMojoResult());
+ assertEquals(message.getData(), receivedResponseMessage);
+ }
+
+ /**
+ * Clears {@code mReceiver.messagesWithReceivers} allowing all message receivers to be
+ * finalized.
+ * <p>
+ * Since there is no way to force the Garbage Collector to actually call finalize and we want to
+ * test the effects of the finalize() method, we explicitly call finalize() on all of the
+ * message receivers. We do this in a custom thread to better approximate what the JVM does.
+ */
+ private void clearAllMessageReceivers() {
+ Thread myFinalizerThread = new Thread() {
+ @Override
+ @SuppressFBWarnings("FI_EXPLICIT_INVOCATION")
+ public void run() {
+ for (Pair<Message, MessageReceiver> receivedMessage :
+ mReceiver.messagesWithReceivers) {
+ RouterImpl.ResponderThunk thunk =
+ (RouterImpl.ResponderThunk) receivedMessage.second;
+ try {
+ thunk.finalize();
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ };
+ myFinalizerThread.start();
+ try {
+ myFinalizerThread.join();
+ } catch (InterruptedException e) {
+ // ignore.
+ }
+ mReceiver.messagesWithReceivers.clear();
+ }
+
+ /**
+ * Testing receiving a message via the router that expected a response.
+ */
+ @SmallTest
+ public void testReceivingViaRouterWithResponse() {
+ final int requestMessageType = 0xdead;
+ final int responseMessageType = 0xbeef;
+ final int requestId = 0xdeadbeaf;
+
+ // Send a message expecting a response.
+ sendMessageToRouter(0, requestMessageType, requestId);
+
+ // Sending the response.
+ sendResponseFromRouter(0, responseMessageType);
+ }
+
+ /**
+ * Tests that if a callback is dropped (i.e. becomes unreachable and is finalized
+ * without being used), then the message pipe will be closed.
+ */
+ @SmallTest
+ public void testDroppingReceiverWithoutUsingIt() {
+ // Send 10 messages to the router without sending a response.
+ for (int i = 0; i < 10; i++) {
+ sendMessageToRouter(i, i, i);
+ }
+
+ // Now send the 10 responses. This should work fine.
+ for (int i = 0; i < 10; i++) {
+ sendResponseFromRouter(i, i);
+ }
+
+ // Clear all MessageRecievers so that the ResponderThunks will
+ // be finalized.
+ clearAllMessageReceivers();
+
+ // Send another message to the router without sending a response.
+ sendMessageToRouter(0, 0, 0);
+
+ // Clear the MessageReciever so that the ResponderThunk will
+ // be finalized. Since the RespondeThunk was never used, this
+ // should close the pipe.
+ clearAllMessageReceivers();
+ // The close() occurs asynchronously on this thread.
+ runLoopUntilIdle();
+
+ // Confirm that the pipe was closed on the Router side.
+ HandleSignals closedFlag = HandleSignals.none().setPeerClosed(true);
+ assertEquals(closedFlag, mHandle.querySignalsState().getSatisfiedSignals());
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java
new file mode 100644
index 0000000000..2c17e3a194
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java
@@ -0,0 +1,175 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.chromium.mojo.HandleMock;
+import org.chromium.mojo.bindings.test.mojom.mojo.Struct1;
+import org.chromium.mojo.bindings.test.mojom.mojo.Struct2;
+import org.chromium.mojo.bindings.test.mojom.mojo.Struct3;
+import org.chromium.mojo.bindings.test.mojom.mojo.Struct4;
+import org.chromium.mojo.bindings.test.mojom.mojo.Struct5;
+import org.chromium.mojo.bindings.test.mojom.mojo.Struct6;
+import org.chromium.mojo.bindings.test.mojom.mojo.StructOfNullables;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Tests for the serialization logic of the generated structs, using structs defined in
+ * mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom .
+ */
+public class SerializationTest extends TestCase {
+
+ private static void assertThrowsSerializationException(Struct struct) {
+ try {
+ struct.serialize(null);
+ fail("Serialization of invalid struct should have thrown an exception.");
+ } catch (SerializationException ex) {
+ // Expected.
+ }
+ }
+
+ /**
+ * Verifies that serializing a struct with an invalid handle of a non-nullable type throws an
+ * exception.
+ */
+ @SmallTest
+ public void testHandle() {
+ Struct2 struct = new Struct2();
+ assertFalse(struct.hdl.isValid());
+ assertThrowsSerializationException(struct);
+
+ // Make the struct valid and verify that it serializes without an exception.
+ struct.hdl = new HandleMock();
+ struct.serialize(null);
+ }
+
+ /**
+ * Verifies that serializing a struct with a null struct pointer throws an exception.
+ */
+ @SmallTest
+ public void testStructPointer() {
+ Struct3 struct = new Struct3();
+ assertNull(struct.struct1);
+ assertThrowsSerializationException(struct);
+
+ // Make the struct valid and verify that it serializes without an exception.
+ struct.struct1 = new Struct1();
+ struct.serialize(null);
+ }
+
+ /**
+ * Verifies that serializing a struct with an array of structs throws an exception when the
+ * struct is invalid.
+ */
+ @SmallTest
+ public void testStructArray() {
+ Struct4 struct = new Struct4();
+ assertNull(struct.data);
+ assertThrowsSerializationException(struct);
+
+ // Create the (1-element) array but have the element null.
+ struct.data = new Struct1[1];
+ assertThrowsSerializationException(struct);
+
+ // Create the array element, struct should serialize now.
+ struct.data[0] = new Struct1();
+ struct.serialize(null);
+ }
+
+ /**
+ * Verifies that serializing a struct with a fixed-size array of incorrect length throws an
+ * exception.
+ */
+ @SmallTest
+ public void testFixedSizeArray() {
+ Struct5 struct = new Struct5();
+ assertNull(struct.pair);
+ assertThrowsSerializationException(struct);
+
+ // Create the (1-element) array, 2-element array is required.
+ struct.pair = new Struct1[1];
+ struct.pair[0] = new Struct1();
+ assertThrowsSerializationException(struct);
+
+ // Create the array of a correct size, struct should serialize now.
+ struct.pair = new Struct1[2];
+ struct.pair[0] = new Struct1();
+ struct.pair[1] = new Struct1();
+ struct.serialize(null);
+ }
+
+ /**
+ * Verifies that serializing a struct with a null string throws an exception.
+ */
+ @SmallTest
+ public void testString() {
+ Struct6 struct = new Struct6();
+ assertNull(struct.str);
+ assertThrowsSerializationException(struct);
+
+ // Make the struct valid and verify that it serializes without an exception.
+ struct.str = "";
+ struct.serialize(null);
+ }
+
+ /**
+ * Verifies that a struct with an invalid nullable handle, null nullable struct pointer and null
+ * nullable string serializes without an exception.
+ */
+ @SmallTest
+ public void testNullableFields() {
+ StructOfNullables struct = new StructOfNullables();
+ assertFalse(struct.hdl.isValid());
+ assertNull(struct.struct1);
+ assertNull(struct.str);
+ struct.serialize(null);
+ }
+
+ /**
+ * Verifies that a struct can be serialized to and deserialized from a ByteBuffer.
+ */
+ @SmallTest
+ public void testByteBufferSerialization() {
+ Struct1 input = new Struct1();
+ input.i = 0x7F;
+
+ ByteBuffer buf = input.serialize();
+
+ byte[] expected_raw_bytes = {16, 0, 0, 0, 0, 0, 0, 0, 0x7F, 0, 0, 0, 0, 0, 0, 0};
+ ByteBuffer expected_buf = ByteBuffer.wrap(expected_raw_bytes);
+ assertEquals(expected_buf, buf);
+
+ Struct1 output = Struct1.deserialize(buf);
+ assertEquals(0x7F, output.i);
+ }
+
+ /**
+ * Verifies that a struct with handles cannot be serialized to a ByteBuffer.
+ */
+ @SmallTest
+ public void testByteBufferSerializationWithHandles() {
+ StructOfNullables struct = new StructOfNullables();
+ assertFalse(struct.hdl.isValid());
+ assertNull(struct.struct1);
+ assertNull(struct.str);
+
+ // It is okay to serialize invalid handles.
+ struct.serialize();
+
+ struct.hdl = new HandleMock();
+
+ try {
+ struct.serialize();
+ fail("Serializing a struct with handles to a ByteBuffer should have thrown an "
+ + "exception.");
+ } catch (UnsupportedOperationException ex) {
+ // Expected.
+ }
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java
new file mode 100644
index 0000000000..84246188d3
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java
@@ -0,0 +1,237 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.support.test.filters.SmallTest;
+
+import org.chromium.base.test.util.UrlUtils;
+import org.chromium.mojo.HandleMock;
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.bindings.test.mojom.mojo.ConformanceTestInterface;
+import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface;
+import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterfaceTestHelper;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.impl.CoreImpl;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+/**
+ * Testing validation upon deserialization using the interfaces defined in the
+ * mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom file.
+ * <p>
+ * One needs to pass '--test_data=bindings:{path to mojo/public/interfaces/bindings/tests/data}' to
+ * the test_runner script for this test to find the validation data it needs.
+ */
+public class ValidationTest extends MojoTestCase {
+
+ /**
+ * The path where validation test data is.
+ */
+ private static final File VALIDATION_TEST_DATA_PATH =
+ new File(UrlUtils.getIsolatedTestFilePath(
+ "mojo/public/interfaces/bindings/tests/data/validation"));
+
+ /**
+ * The data needed for a validation test.
+ */
+ private static class TestData {
+ public File dataFile;
+ public ValidationTestUtil.Data inputData;
+ public String expectedResult;
+ }
+
+ private static class DataFileFilter implements FileFilter {
+ private final String mPrefix;
+
+ public DataFileFilter(String prefix) {
+ this.mPrefix = prefix;
+ }
+
+ @Override
+ public boolean accept(File pathname) {
+ // TODO(yzshen, qsr): skip some interface versioning tests.
+ if (pathname.getName().startsWith("conformance_mthd13_good_2")) {
+ return false;
+ }
+ return pathname.isFile() && pathname.getName().startsWith(mPrefix)
+ && pathname.getName().endsWith(".data");
+ }
+ }
+
+ private static String getStringContent(File f) throws FileNotFoundException {
+ try (Scanner scanner = new Scanner(f)) {
+ scanner.useDelimiter("\\Z");
+ StringBuilder result = new StringBuilder();
+ while (scanner.hasNext()) {
+ result.append(scanner.next());
+ }
+ return result.toString().trim();
+ }
+ }
+
+ private static List<TestData> getTestData(String prefix)
+ throws FileNotFoundException {
+ List<TestData> results = new ArrayList<TestData>();
+
+ // Fail if the test data is not present.
+ if (!VALIDATION_TEST_DATA_PATH.isDirectory()) {
+ fail("No test data directory found. "
+ + "Expected directory at: " + VALIDATION_TEST_DATA_PATH);
+ }
+
+ File[] files = VALIDATION_TEST_DATA_PATH.listFiles(new DataFileFilter(prefix));
+ if (files != null) {
+ for (File dataFile : files) {
+ File resultFile = new File(dataFile.getParent(),
+ dataFile.getName().replaceFirst("\\.data$", ".expected"));
+ TestData testData = new TestData();
+ testData.dataFile = dataFile;
+ testData.inputData = ValidationTestUtil.parseData(getStringContent(dataFile));
+ testData.expectedResult = getStringContent(resultFile);
+ results.add(testData);
+ }
+ }
+ return results;
+ }
+
+ /**
+ * Runs all the test with the given prefix on the given {@link MessageReceiver}.
+ */
+ private static void runTest(String prefix, MessageReceiver messageReceiver)
+ throws FileNotFoundException {
+ List<TestData> testData = getTestData(prefix);
+ for (TestData test : testData) {
+ assertNull("Unable to read: " + test.dataFile.getName()
+ + ": " + test.inputData.getErrorMessage(),
+ test.inputData.getErrorMessage());
+ List<Handle> handles = new ArrayList<Handle>();
+ for (int i = 0; i < test.inputData.getHandlesCount(); ++i) {
+ handles.add(new HandleMock());
+ }
+ Message message = new Message(test.inputData.getData(), handles);
+ boolean passed = messageReceiver.accept(message);
+ if (passed && !test.expectedResult.equals("PASS")) {
+ fail("Input: " + test.dataFile.getName()
+ + ": The message should have been refused. Expected error: "
+ + test.expectedResult);
+ }
+ if (!passed && test.expectedResult.equals("PASS")) {
+ fail("Input: " + test.dataFile.getName()
+ + ": The message should have been accepted.");
+ }
+ }
+ }
+
+ private static class RoutingMessageReceiver implements MessageReceiver {
+ private final MessageReceiverWithResponder mRequest;
+ private final MessageReceiver mResponse;
+
+ private RoutingMessageReceiver(MessageReceiverWithResponder request,
+ MessageReceiver response) {
+ this.mRequest = request;
+ this.mResponse = response;
+ }
+
+ /**
+ * @see MessageReceiver#accept(Message)
+ */
+ @Override
+ public boolean accept(Message message) {
+ try {
+ MessageHeader header = message.asServiceMessage().getHeader();
+ if (header.hasFlag(MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) {
+ return mResponse.accept(message);
+ } else {
+ return mRequest.acceptWithResponder(message, new SinkMessageReceiver());
+ }
+ } catch (DeserializationException e) {
+ return false;
+ }
+ }
+
+ /**
+ * @see MessageReceiver#close()
+ */
+ @Override
+ public void close() {
+ }
+
+ }
+
+ /**
+ * A trivial message receiver that refuses all messages it receives.
+ */
+ private static class SinkMessageReceiver implements MessageReceiverWithResponder {
+
+ @Override
+ public boolean accept(Message message) {
+ return true;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public boolean acceptWithResponder(Message message, MessageReceiver responder) {
+ return true;
+ }
+ }
+
+ /**
+ * Testing the conformance suite.
+ */
+ @SmallTest
+ public void testConformance() throws FileNotFoundException {
+ runTest("conformance_",
+ ConformanceTestInterface.MANAGER.buildStub(CoreImpl.getInstance(),
+ ConformanceTestInterface.MANAGER.buildProxy(
+ CoreImpl.getInstance(), new SinkMessageReceiver())));
+ }
+
+ /**
+ * Testing the integration suite for message headers.
+ */
+ @SmallTest
+ public void testIntegrationMessageHeader() throws FileNotFoundException {
+ runTest("integration_msghdr_",
+ new RoutingMessageReceiver(IntegrationTestInterface.MANAGER.buildStub(null,
+ IntegrationTestInterface.MANAGER.buildProxy(null,
+ new SinkMessageReceiver())),
+ IntegrationTestInterfaceTestHelper
+ .newIntegrationTestInterfaceMethodCallback()));
+ }
+
+ /**
+ * Testing the integration suite for request messages.
+ */
+ @SmallTest
+ public void testIntegrationRequestMessage() throws FileNotFoundException {
+ runTest("integration_intf_rqst_",
+ new RoutingMessageReceiver(IntegrationTestInterface.MANAGER.buildStub(null,
+ IntegrationTestInterface.MANAGER.buildProxy(null,
+ new SinkMessageReceiver())),
+ IntegrationTestInterfaceTestHelper
+ .newIntegrationTestInterfaceMethodCallback()));
+ }
+
+ /**
+ * Testing the integration suite for response messages.
+ */
+ @SmallTest
+ public void testIntegrationResponseMessage() throws FileNotFoundException {
+ runTest("integration_intf_resp_",
+ new RoutingMessageReceiver(IntegrationTestInterface.MANAGER.buildStub(null,
+ IntegrationTestInterface.MANAGER.buildProxy(null,
+ new SinkMessageReceiver())),
+ IntegrationTestInterfaceTestHelper
+ .newIntegrationTestInterfaceMethodCallback()));
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java
new file mode 100644
index 0000000000..91b993c3b6
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java
@@ -0,0 +1,68 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Utility class for testing message validation. The file format used to describe a message is
+ * described in The format is described in
+ * mojo/public/cpp/bindings/tests/validation_test_input_parser.h
+ */
+@JNINamespace("mojo::android")
+public class ValidationTestUtil {
+
+ /**
+ * Content of a '.data' file.
+ */
+ public static class Data {
+ private final ByteBuffer mData;
+ private final int mHandlesCount;
+ private final String mErrorMessage;
+
+ public ByteBuffer getData() {
+ return mData;
+ }
+
+ public int getHandlesCount() {
+ return mHandlesCount;
+ }
+
+ public String getErrorMessage() {
+ return mErrorMessage;
+ }
+
+ private Data(ByteBuffer data, int handlesCount, String errorMessage) {
+ this.mData = data;
+ this.mHandlesCount = handlesCount;
+ this.mErrorMessage = errorMessage;
+ }
+ }
+
+ /**
+ * Parse a '.data' file.
+ */
+ public static Data parseData(String dataAsString) {
+ return nativeParseData(dataAsString);
+ }
+
+ private static native Data nativeParseData(String dataAsString);
+
+ @CalledByNative
+ private static Data buildData(ByteBuffer data, int handlesCount, String errorMessage) {
+ ByteBuffer copiedData = null;
+ if (data != null) {
+ copiedData = ByteBuffer.allocateDirect(data.limit());
+ copiedData.order(ByteOrder.LITTLE_ENDIAN);
+ copiedData.put(data);
+ copiedData.flip();
+ }
+ return new Data(copiedData, handlesCount, errorMessage);
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java
new file mode 100644
index 0000000000..623abc3ed2
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java
@@ -0,0 +1,151 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings;
+
+import android.support.test.filters.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Testing {@link ValidationTestUtil}.
+ */
+public class ValidationTestUtilTest extends MojoTestCase {
+
+ /**
+ * Check that the input parser is correct on a given input.
+ */
+ public static void checkInputParser(
+ String input, boolean isInputValid, ByteBuffer expectedData, int expectedHandlesCount) {
+ ValidationTestUtil.Data data = ValidationTestUtil.parseData(input);
+ if (isInputValid) {
+ assertNull(data.getErrorMessage());
+ assertEquals(expectedData, data.getData());
+ assertEquals(expectedHandlesCount, data.getHandlesCount());
+ } else {
+ assertNotNull(data.getErrorMessage());
+ assertNull(data.getData());
+ }
+ }
+
+ /**
+ * Testing {@link ValidationTestUtil#parseData(String)}.
+ */
+ @SmallTest
+ public void testCorrectMessageParsing() {
+ {
+ // Test empty input.
+ String input = "";
+ ByteBuffer expected = ByteBuffer.allocateDirect(0);
+ expected.order(ByteOrder.LITTLE_ENDIAN);
+
+ checkInputParser(input, true, expected, 0);
+ }
+ {
+ // Test input that only consists of comments and whitespaces.
+ String input = " \t // hello world \n\r \t// the answer is 42 ";
+ ByteBuffer expected = ByteBuffer.allocateDirect(0);
+ expected.order(ByteOrder.nativeOrder());
+
+ checkInputParser(input, true, expected, 0);
+ }
+ {
+ String input = "[u1]0x10// hello world !! \n\r \t [u2]65535 \n"
+ + "[u4]65536 [u8]0xFFFFFFFFFFFFFFFF 0 0Xff";
+ ByteBuffer expected = ByteBuffer.allocateDirect(17);
+ expected.order(ByteOrder.nativeOrder());
+ expected.put((byte) 0x10);
+ expected.putShort((short) 65535);
+ expected.putInt(65536);
+ expected.putLong(-1);
+ expected.put((byte) 0);
+ expected.put((byte) 0xff);
+ expected.flip();
+
+ checkInputParser(input, true, expected, 0);
+ }
+ {
+ String input = "[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40";
+ ByteBuffer expected = ByteBuffer.allocateDirect(15);
+ expected.order(ByteOrder.nativeOrder());
+ expected.putLong(-0x800);
+ expected.put((byte) -128);
+ expected.putShort((short) 0);
+ expected.putInt(-40);
+ expected.flip();
+
+ checkInputParser(input, true, expected, 0);
+ }
+ {
+ String input = "[b]00001011 [b]10000000 // hello world\r [b]00000000";
+ ByteBuffer expected = ByteBuffer.allocateDirect(3);
+ expected.order(ByteOrder.nativeOrder());
+ expected.put((byte) 11);
+ expected.put((byte) 128);
+ expected.put((byte) 0);
+ expected.flip();
+
+ checkInputParser(input, true, expected, 0);
+ }
+ {
+ String input = "[f]+.3e9 [d]-10.03";
+ ByteBuffer expected = ByteBuffer.allocateDirect(12);
+ expected.order(ByteOrder.nativeOrder());
+ expected.putFloat(+.3e9f);
+ expected.putDouble(-10.03);
+ expected.flip();
+
+ checkInputParser(input, true, expected, 0);
+ }
+ {
+ String input = "[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar";
+ ByteBuffer expected = ByteBuffer.allocateDirect(14);
+ expected.order(ByteOrder.nativeOrder());
+ expected.putInt(14);
+ expected.put((byte) 0);
+ expected.putLong(9);
+ expected.put((byte) 0);
+ expected.flip();
+
+ checkInputParser(input, true, expected, 0);
+ }
+ {
+ String input = "// This message has handles! \n[handles]50 [u8]2";
+ ByteBuffer expected = ByteBuffer.allocateDirect(8);
+ expected.order(ByteOrder.nativeOrder());
+ expected.putLong(2);
+ expected.flip();
+
+ checkInputParser(input, true, expected, 50);
+ }
+
+ // Test some failure cases.
+ {
+ String error_inputs[] = {
+ "/ hello world",
+ "[u1]x",
+ "[u2]-1000",
+ "[u1]0x100",
+ "[s2]-0x8001",
+ "[b]1",
+ "[b]1111111k",
+ "[dist4]unmatched",
+ "[anchr]hello [dist8]hello",
+ "[dist4]a [dist4]a [anchr]a",
+ "[dist4]a [anchr]a [dist4]a [anchr]a",
+ "0 [handles]50"
+ };
+
+ for (String input : error_inputs) {
+ ByteBuffer expected = ByteBuffer.allocateDirect(0);
+ expected.order(ByteOrder.nativeOrder());
+ checkInputParser(input, false, expected, 0);
+ }
+ }
+
+ }
+}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterfaceTestHelper.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterfaceTestHelper.java
new file mode 100644
index 0000000000..8fb79d7edf
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterfaceTestHelper.java
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.bindings.test.mojom.mojo;
+
+import org.chromium.mojo.bindings.MessageReceiver;
+import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface.Method0Response;
+import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface_Internal.IntegrationTestInterfaceMethod0ResponseParamsForwardToCallback;
+
+/**
+ * Helper class to access {@link IntegrationTestInterface_Internal} package protected method for
+ * tests.
+ */
+public class IntegrationTestInterfaceTestHelper {
+
+ private static final class SinkMethod0Response implements Method0Response {
+ @Override
+ public void call(byte[] arg1) {
+ }
+ }
+
+ /**
+ * Creates a new {@link MessageReceiver} to use for the callback of
+ * |IntegrationTestInterface#method0(Method0Response)|.
+ */
+ public static MessageReceiver newIntegrationTestInterfaceMethodCallback() {
+ return new IntegrationTestInterfaceMethod0ResponseParamsForwardToCallback(
+ new SinkMethod0Response());
+ }
+}