diff options
Diffstat (limited to 'mojo/android/javatests/src/org')
22 files changed, 3494 insertions, 0 deletions
diff --git a/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java b/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java new file mode 100644 index 0000000000..1f8de94500 --- /dev/null +++ b/mojo/android/javatests/src/org/chromium/mojo/HandleMock.java @@ -0,0 +1,226 @@ +// 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; + +import org.chromium.mojo.system.Core; +import org.chromium.mojo.system.Core.HandleSignalsState; +import org.chromium.mojo.system.DataPipe; +import org.chromium.mojo.system.DataPipe.ConsumerHandle; +import org.chromium.mojo.system.DataPipe.ProducerHandle; +import org.chromium.mojo.system.Handle; +import org.chromium.mojo.system.MessagePipeHandle; +import org.chromium.mojo.system.MojoResult; +import org.chromium.mojo.system.ResultAnd; +import org.chromium.mojo.system.SharedBufferHandle; +import org.chromium.mojo.system.UntypedHandle; +import org.chromium.mojo.system.impl.CoreImpl; + +import java.nio.ByteBuffer; +import java.util.List; + +/** + * A mock handle, that does nothing. + */ +public class HandleMock implements UntypedHandle, MessagePipeHandle, + ProducerHandle, ConsumerHandle, SharedBufferHandle { + + /** + * @see Handle#close() + */ + @Override + public void close() { + // Do nothing. + } + + /** + * @see Handle#querySignalsState() + */ + @Override + public HandleSignalsState querySignalsState() { + return null; + } + + /** + * @see Handle#isValid() + */ + @Override + public boolean isValid() { + return true; + } + + /** + * @see Handle#toUntypedHandle() + */ + @Override + public UntypedHandle toUntypedHandle() { + return this; + } + + /** + * @see org.chromium.mojo.system.Handle#getCore() + */ + @Override + public Core getCore() { + return CoreImpl.getInstance(); + } + + /** + * @see org.chromium.mojo.system.UntypedHandle#pass() + */ + @Override + public HandleMock pass() { + return this; + } + + /** + * @see Handle#releaseNativeHandle() + */ + @Override + public int releaseNativeHandle() { + return 0; + } + + /** + * @see ConsumerHandle#discardData(int, DataPipe.ReadFlags) + */ + @Override + public int discardData(int numBytes, DataPipe.ReadFlags flags) { + // Do nothing. + return 0; + } + + /** + * @see ConsumerHandle#readData(java.nio.ByteBuffer, DataPipe.ReadFlags) + */ + @Override + public ResultAnd<Integer> readData(ByteBuffer elements, DataPipe.ReadFlags flags) { + // Do nothing. + return new ResultAnd<Integer>(MojoResult.OK, 0); + } + + /** + * @see ConsumerHandle#beginReadData(int, DataPipe.ReadFlags) + */ + @Override + public ByteBuffer beginReadData(int numBytes, + DataPipe.ReadFlags flags) { + // Do nothing. + return null; + } + + /** + * @see ConsumerHandle#endReadData(int) + */ + @Override + public void endReadData(int numBytesRead) { + // Do nothing. + } + + /** + * @see ProducerHandle#writeData(java.nio.ByteBuffer, DataPipe.WriteFlags) + */ + @Override + public ResultAnd<Integer> writeData(ByteBuffer elements, DataPipe.WriteFlags flags) { + // Do nothing. + return new ResultAnd<Integer>(MojoResult.OK, 0); + } + + /** + * @see ProducerHandle#beginWriteData(int, DataPipe.WriteFlags) + */ + @Override + public ByteBuffer beginWriteData(int numBytes, + DataPipe.WriteFlags flags) { + // Do nothing. + return null; + } + + /** + * @see ProducerHandle#endWriteData(int) + */ + @Override + public void endWriteData(int numBytesWritten) { + // Do nothing. + } + + /** + * @see MessagePipeHandle#writeMessage(java.nio.ByteBuffer, java.util.List, + * MessagePipeHandle.WriteFlags) + */ + @Override + public void writeMessage(ByteBuffer bytes, List<? extends Handle> handles, + WriteFlags flags) { + // Do nothing. + } + + /** + * @see MessagePipeHandle#readMessage(java.nio.ByteBuffer, int, MessagePipeHandle.ReadFlags) + */ + @Override + public ResultAnd<ReadMessageResult> readMessage( + ByteBuffer bytes, int maxNumberOfHandles, ReadFlags flags) { + // Do nothing. + return new ResultAnd<ReadMessageResult>(MojoResult.OK, new ReadMessageResult()); + } + + /** + * @see UntypedHandle#toMessagePipeHandle() + */ + @Override + public MessagePipeHandle toMessagePipeHandle() { + return this; + } + + /** + * @see UntypedHandle#toDataPipeConsumerHandle() + */ + @Override + public ConsumerHandle toDataPipeConsumerHandle() { + return this; + } + + /** + * @see UntypedHandle#toDataPipeProducerHandle() + */ + @Override + public ProducerHandle toDataPipeProducerHandle() { + return this; + } + + /** + * @see UntypedHandle#toSharedBufferHandle() + */ + @Override + public SharedBufferHandle toSharedBufferHandle() { + return this; + } + + /** + * @see SharedBufferHandle#duplicate(SharedBufferHandle.DuplicateOptions) + */ + @Override + public SharedBufferHandle duplicate(DuplicateOptions options) { + // Do nothing. + return null; + } + + /** + * @see SharedBufferHandle#map(long, long, SharedBufferHandle.MapFlags) + */ + @Override + public ByteBuffer map(long offset, long numBytes, MapFlags flags) { + // Do nothing. + return null; + } + + /** + * @see SharedBufferHandle#unmap(java.nio.ByteBuffer) + */ + @Override + public void unmap(ByteBuffer buffer) { + // Do nothing. + } + +} diff --git a/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java b/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java new file mode 100644 index 0000000000..f4d7ab7236 --- /dev/null +++ b/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java @@ -0,0 +1,66 @@ +// 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; + +import android.test.InstrumentationTestCase; + +import org.chromium.base.ContextUtils; +import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.library_loader.LibraryLoader; +import org.chromium.base.library_loader.LibraryProcessType; + +/** + * Base class to test mojo. Setup the environment. + */ +@JNINamespace("mojo::android") +public class MojoTestCase extends InstrumentationTestCase { + + private long mTestEnvironmentPointer; + + /** + * @see junit.framework.TestCase#setUp() + */ + @Override + protected void setUp() throws Exception { + super.setUp(); + ContextUtils.initApplicationContext( + getInstrumentation().getTargetContext().getApplicationContext()); + LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER).ensureInitialized(); + nativeInit(); + mTestEnvironmentPointer = nativeSetupTestEnvironment(); + } + + /** + * @see android.test.InstrumentationTestCase#tearDown() + */ + @Override + protected void tearDown() throws Exception { + nativeTearDownTestEnvironment(mTestEnvironmentPointer); + super.tearDown(); + } + + /** + * Runs the run loop for the given time. + */ + protected void runLoop(long timeoutMS) { + nativeRunLoop(timeoutMS); + } + + /** + * Runs the run loop until no handle or task are immediately available. + */ + protected void runLoopUntilIdle() { + nativeRunLoop(0); + } + + private native void nativeInit(); + + private native long nativeSetupTestEnvironment(); + + private native void nativeTearDownTestEnvironment(long testEnvironment); + + private native void nativeRunLoop(long timeoutMS); + +} diff --git a/mojo/android/javatests/src/org/chromium/mojo/TestUtils.java b/mojo/android/javatests/src/org/chromium/mojo/TestUtils.java new file mode 100644 index 0000000000..d10d0d7558 --- /dev/null +++ b/mojo/android/javatests/src/org/chromium/mojo/TestUtils.java @@ -0,0 +1,32 @@ +// 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; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Random; + +/** + * Utilities methods for tests. + */ +public final class TestUtils { + + private static final Random RANDOM = new Random(); + + /** + * Returns a new direct ByteBuffer of the given size with random (but reproducible) data. + */ + public static ByteBuffer newRandomBuffer(int size) { + byte bytes[] = new byte[size]; + RANDOM.setSeed(size); + RANDOM.nextBytes(bytes); + ByteBuffer data = ByteBuffer.allocateDirect(size); + data.order(ByteOrder.LITTLE_ENDIAN); + data.put(bytes); + data.flip(); + return data; + } + +} 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()); + } +} diff --git a/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java b/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java new file mode 100644 index 0000000000..5120198feb --- /dev/null +++ b/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java @@ -0,0 +1,545 @@ +// 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.system.impl; + +import android.support.test.filters.SmallTest; + +import org.chromium.mojo.MojoTestCase; +import org.chromium.mojo.system.Core; +import org.chromium.mojo.system.Core.HandleSignals; +import org.chromium.mojo.system.DataPipe; +import org.chromium.mojo.system.Handle; +import org.chromium.mojo.system.InvalidHandle; +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.ResultAnd; +import org.chromium.mojo.system.SharedBufferHandle; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +/** + * Testing the core API. + */ +public class CoreImplTest extends MojoTestCase { + private static final long RUN_LOOP_TIMEOUT_MS = 5; + + private static final ScheduledExecutorService WORKER = + Executors.newSingleThreadScheduledExecutor(); + + private static final HandleSignals ALL_SIGNALS = + HandleSignals.none().setPeerClosed(true).setReadable(true).setWritable(true); + + private List<Handle> mHandlesToClose = new ArrayList<Handle>(); + + /** + * @see MojoTestCase#tearDown() + */ + @Override + protected void tearDown() throws Exception { + MojoException toThrow = null; + for (Handle handle : mHandlesToClose) { + try { + handle.close(); + } catch (MojoException e) { + if (toThrow == null) { + toThrow = e; + } + } + } + if (toThrow != null) { + throw toThrow; + } + super.tearDown(); + } + + private void addHandleToClose(Handle handle) { + mHandlesToClose.add(handle); + } + + private void addHandlePairToClose(Pair<? extends Handle, ? extends Handle> handles) { + mHandlesToClose.add(handles.first); + mHandlesToClose.add(handles.second); + } + + private static void checkSendingMessage(MessagePipeHandle in, MessagePipeHandle out) { + Random random = new Random(); + + // Writing a random 8 bytes message. + byte[] bytes = new byte[8]; + random.nextBytes(bytes); + ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length); + buffer.put(bytes); + in.writeMessage(buffer, null, MessagePipeHandle.WriteFlags.NONE); + + // Try to read into a small buffer. + ByteBuffer receiveBuffer = ByteBuffer.allocateDirect(bytes.length / 2); + ResultAnd<MessagePipeHandle.ReadMessageResult> result = + out.readMessage(receiveBuffer, 0, MessagePipeHandle.ReadFlags.NONE); + assertEquals(MojoResult.RESOURCE_EXHAUSTED, result.getMojoResult()); + assertEquals(bytes.length, result.getValue().getMessageSize()); + assertEquals(0, result.getValue().getHandlesCount()); + + // Read into a correct buffer. + receiveBuffer = ByteBuffer.allocateDirect(bytes.length); + result = out.readMessage(receiveBuffer, 0, MessagePipeHandle.ReadFlags.NONE); + assertEquals(MojoResult.OK, result.getMojoResult()); + assertEquals(bytes.length, result.getValue().getMessageSize()); + assertEquals(0, result.getValue().getHandlesCount()); + assertEquals(0, receiveBuffer.position()); + assertEquals(result.getValue().getMessageSize(), receiveBuffer.limit()); + byte[] receivedBytes = new byte[result.getValue().getMessageSize()]; + receiveBuffer.get(receivedBytes); + assertTrue(Arrays.equals(bytes, receivedBytes)); + } + + private static void checkSendingData(DataPipe.ProducerHandle in, DataPipe.ConsumerHandle out) { + Random random = new Random(); + + // Writing a random 8 bytes message. + byte[] bytes = new byte[8]; + random.nextBytes(bytes); + ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length); + buffer.put(bytes); + ResultAnd<Integer> result = in.writeData(buffer, DataPipe.WriteFlags.NONE); + assertEquals(MojoResult.OK, result.getMojoResult()); + assertEquals(bytes.length, result.getValue().intValue()); + + // Query number of bytes available. + ResultAnd<Integer> readResult = out.readData(null, DataPipe.ReadFlags.none().query(true)); + assertEquals(MojoResult.OK, readResult.getMojoResult()); + assertEquals(bytes.length, readResult.getValue().intValue()); + + // Peek data into a buffer. + ByteBuffer peekBuffer = ByteBuffer.allocateDirect(bytes.length); + readResult = out.readData(peekBuffer, DataPipe.ReadFlags.none().peek(true)); + assertEquals(MojoResult.OK, readResult.getMojoResult()); + assertEquals(bytes.length, readResult.getValue().intValue()); + assertEquals(bytes.length, peekBuffer.limit()); + byte[] peekBytes = new byte[bytes.length]; + peekBuffer.get(peekBytes); + assertTrue(Arrays.equals(bytes, peekBytes)); + + // Read into a buffer. + ByteBuffer receiveBuffer = ByteBuffer.allocateDirect(bytes.length); + readResult = out.readData(receiveBuffer, DataPipe.ReadFlags.NONE); + assertEquals(MojoResult.OK, readResult.getMojoResult()); + assertEquals(bytes.length, readResult.getValue().intValue()); + assertEquals(0, receiveBuffer.position()); + assertEquals(bytes.length, receiveBuffer.limit()); + byte[] receivedBytes = new byte[bytes.length]; + receiveBuffer.get(receivedBytes); + assertTrue(Arrays.equals(bytes, receivedBytes)); + } + + private static void checkSharing(SharedBufferHandle in, SharedBufferHandle out) { + Random random = new Random(); + + ByteBuffer buffer1 = in.map(0, 8, SharedBufferHandle.MapFlags.NONE); + assertEquals(8, buffer1.capacity()); + ByteBuffer buffer2 = out.map(0, 8, SharedBufferHandle.MapFlags.NONE); + assertEquals(8, buffer2.capacity()); + + byte[] bytes = new byte[8]; + random.nextBytes(bytes); + buffer1.put(bytes); + + byte[] receivedBytes = new byte[bytes.length]; + buffer2.get(receivedBytes); + + assertTrue(Arrays.equals(bytes, receivedBytes)); + + in.unmap(buffer1); + out.unmap(buffer2); + } + + /** + * Testing that Core can be retrieved from a handle. + */ + @SmallTest + public void testGetCore() { + Core core = CoreImpl.getInstance(); + + Pair<? extends Handle, ? extends Handle> handles = core.createMessagePipe(null); + addHandlePairToClose(handles); + assertEquals(core, handles.first.getCore()); + assertEquals(core, handles.second.getCore()); + + handles = core.createDataPipe(null); + addHandlePairToClose(handles); + assertEquals(core, handles.first.getCore()); + assertEquals(core, handles.second.getCore()); + + SharedBufferHandle handle = core.createSharedBuffer(null, 100); + SharedBufferHandle handle2 = handle.duplicate(null); + addHandleToClose(handle); + addHandleToClose(handle2); + assertEquals(core, handle.getCore()); + assertEquals(core, handle2.getCore()); + } + + private static void createAndCloseMessagePipe(MessagePipeHandle.CreateOptions options) { + Core core = CoreImpl.getInstance(); + Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(options); + handles.first.close(); + handles.second.close(); + } + + /** + * Testing {@link MessagePipeHandle} creation. + */ + @SmallTest + public void testMessagePipeCreation() { + // Test creation with null options. + createAndCloseMessagePipe(null); + // Test creation with default options. + createAndCloseMessagePipe(new MessagePipeHandle.CreateOptions()); + } + + /** + * Testing {@link MessagePipeHandle}. + */ + @SmallTest + public void testMessagePipeEmpty() { + Core core = CoreImpl.getInstance(); + Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null); + addHandlePairToClose(handles); + + // Testing read on an empty pipe. + ResultAnd<MessagePipeHandle.ReadMessageResult> readResult = + handles.first.readMessage(null, 0, MessagePipeHandle.ReadFlags.NONE); + assertEquals(MojoResult.SHOULD_WAIT, readResult.getMojoResult()); + + handles.first.close(); + handles.second.close(); + } + + /** + * Testing {@link MessagePipeHandle}. + */ + @SmallTest + public void testMessagePipeSend() { + Core core = CoreImpl.getInstance(); + Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null); + addHandlePairToClose(handles); + + checkSendingMessage(handles.first, handles.second); + checkSendingMessage(handles.second, handles.first); + } + + /** + * Testing {@link MessagePipeHandle}. + */ + @SmallTest + public void testMessagePipeReceiveOnSmallBuffer() { + Random random = new Random(); + Core core = CoreImpl.getInstance(); + Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null); + addHandlePairToClose(handles); + + // Writing a random 8 bytes message. + byte[] bytes = new byte[8]; + random.nextBytes(bytes); + ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length); + buffer.put(bytes); + handles.first.writeMessage(buffer, null, MessagePipeHandle.WriteFlags.NONE); + + ByteBuffer receiveBuffer = ByteBuffer.allocateDirect(1); + ResultAnd<MessagePipeHandle.ReadMessageResult> result = + handles.second.readMessage(receiveBuffer, 0, MessagePipeHandle.ReadFlags.NONE); + assertEquals(MojoResult.RESOURCE_EXHAUSTED, result.getMojoResult()); + assertEquals(bytes.length, result.getValue().getMessageSize()); + assertEquals(0, result.getValue().getHandlesCount()); + } + + /** + * Testing {@link MessagePipeHandle}. + */ + @SmallTest + public void testMessagePipeSendHandles() { + Core core = CoreImpl.getInstance(); + Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null); + Pair<MessagePipeHandle, MessagePipeHandle> handlesToShare = core.createMessagePipe(null); + addHandlePairToClose(handles); + addHandlePairToClose(handlesToShare); + + handles.first.writeMessage(null, Collections.<Handle>singletonList(handlesToShare.second), + MessagePipeHandle.WriteFlags.NONE); + assertFalse(handlesToShare.second.isValid()); + ResultAnd<MessagePipeHandle.ReadMessageResult> readMessageResult = + handles.second.readMessage(null, 1, MessagePipeHandle.ReadFlags.NONE); + assertEquals(1, readMessageResult.getValue().getHandlesCount()); + MessagePipeHandle newHandle = + readMessageResult.getValue().getHandles().get(0).toMessagePipeHandle(); + addHandleToClose(newHandle); + assertTrue(newHandle.isValid()); + checkSendingMessage(handlesToShare.first, newHandle); + checkSendingMessage(newHandle, handlesToShare.first); + } + + private static void createAndCloseDataPipe(DataPipe.CreateOptions options) { + Core core = CoreImpl.getInstance(); + Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> handles = + core.createDataPipe(options); + handles.first.close(); + handles.second.close(); + } + + /** + * Testing {@link DataPipe}. + */ + @SmallTest + public void testDataPipeCreation() { + // Create datapipe with null options. + createAndCloseDataPipe(null); + DataPipe.CreateOptions options = new DataPipe.CreateOptions(); + // Create datapipe with element size set. + options.setElementNumBytes(24); + createAndCloseDataPipe(options); + // Create datapipe with capacity set. + options.setCapacityNumBytes(1024 * options.getElementNumBytes()); + createAndCloseDataPipe(options); + } + + /** + * Testing {@link DataPipe}. + */ + @SmallTest + public void testDataPipeSend() { + Core core = CoreImpl.getInstance(); + + Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> handles = core.createDataPipe(null); + addHandlePairToClose(handles); + + checkSendingData(handles.first, handles.second); + } + + /** + * Testing {@link DataPipe}. + */ + @SmallTest + public void testDataPipeTwoPhaseSend() { + Random random = new Random(); + Core core = CoreImpl.getInstance(); + Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> handles = core.createDataPipe(null); + addHandlePairToClose(handles); + + // Writing a random 8 bytes message. + byte[] bytes = new byte[8]; + random.nextBytes(bytes); + ByteBuffer buffer = handles.first.beginWriteData(bytes.length, DataPipe.WriteFlags.NONE); + assertTrue(buffer.capacity() >= bytes.length); + buffer.put(bytes); + handles.first.endWriteData(bytes.length); + + // Read into a buffer. + ByteBuffer receiveBuffer = + handles.second.beginReadData(bytes.length, DataPipe.ReadFlags.NONE); + assertEquals(0, receiveBuffer.position()); + assertEquals(bytes.length, receiveBuffer.limit()); + byte[] receivedBytes = new byte[bytes.length]; + receiveBuffer.get(receivedBytes); + assertTrue(Arrays.equals(bytes, receivedBytes)); + handles.second.endReadData(bytes.length); + } + + /** + * Testing {@link DataPipe}. + */ + @SmallTest + public void testDataPipeDiscard() { + Random random = new Random(); + Core core = CoreImpl.getInstance(); + Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> handles = core.createDataPipe(null); + addHandlePairToClose(handles); + + // Writing a random 8 bytes message. + byte[] bytes = new byte[8]; + random.nextBytes(bytes); + ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length); + buffer.put(bytes); + ResultAnd<Integer> result = handles.first.writeData(buffer, DataPipe.WriteFlags.NONE); + assertEquals(MojoResult.OK, result.getMojoResult()); + assertEquals(bytes.length, result.getValue().intValue()); + + // Discard bytes. + final int nbBytesToDiscard = 4; + assertEquals(nbBytesToDiscard, + handles.second.discardData(nbBytesToDiscard, DataPipe.ReadFlags.NONE)); + + // Read into a buffer. + ByteBuffer receiveBuffer = ByteBuffer.allocateDirect(bytes.length - nbBytesToDiscard); + ResultAnd<Integer> readResult = + handles.second.readData(receiveBuffer, DataPipe.ReadFlags.NONE); + assertEquals(MojoResult.OK, readResult.getMojoResult()); + assertEquals(bytes.length - nbBytesToDiscard, readResult.getValue().intValue()); + assertEquals(0, receiveBuffer.position()); + assertEquals(bytes.length - nbBytesToDiscard, receiveBuffer.limit()); + byte[] receivedBytes = new byte[bytes.length - nbBytesToDiscard]; + receiveBuffer.get(receivedBytes); + assertTrue(Arrays.equals( + Arrays.copyOfRange(bytes, nbBytesToDiscard, bytes.length), receivedBytes)); + } + + /** + * Testing {@link SharedBufferHandle}. + */ + @SmallTest + public void testSharedBufferCreation() { + Core core = CoreImpl.getInstance(); + // Test creation with empty options. + core.createSharedBuffer(null, 8).close(); + // Test creation with default options. + core.createSharedBuffer(new SharedBufferHandle.CreateOptions(), 8).close(); + } + + /** + * Testing {@link SharedBufferHandle}. + */ + @SmallTest + public void testSharedBufferDuplication() { + Core core = CoreImpl.getInstance(); + SharedBufferHandle handle = core.createSharedBuffer(null, 8); + addHandleToClose(handle); + + // Test duplication with empty options. + handle.duplicate(null).close(); + // Test creation with default options. + handle.duplicate(new SharedBufferHandle.DuplicateOptions()).close(); + } + + /** + * Testing {@link SharedBufferHandle}. + */ + @SmallTest + public void testSharedBufferSending() { + Core core = CoreImpl.getInstance(); + SharedBufferHandle handle = core.createSharedBuffer(null, 8); + addHandleToClose(handle); + SharedBufferHandle newHandle = handle.duplicate(null); + addHandleToClose(newHandle); + + checkSharing(handle, newHandle); + checkSharing(newHandle, handle); + } + + /** + * Testing that invalid handle can be used with this implementation. + */ + @SmallTest + public void testInvalidHandle() { + Core core = CoreImpl.getInstance(); + Handle handle = InvalidHandle.INSTANCE; + + // Checking sending an invalid handle. + // Until the behavior is changed on the C++ side, handle gracefully 2 different use case: + // - Receive a INVALID_ARGUMENT exception + // - Receive an invalid handle on the other side. + Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null); + addHandlePairToClose(handles); + try { + handles.first.writeMessage(null, Collections.<Handle>singletonList(handle), + MessagePipeHandle.WriteFlags.NONE); + ResultAnd<MessagePipeHandle.ReadMessageResult> readMessageResult = + handles.second.readMessage(null, 1, MessagePipeHandle.ReadFlags.NONE); + assertEquals(1, readMessageResult.getValue().getHandlesCount()); + assertFalse(readMessageResult.getValue().getHandles().get(0).isValid()); + } catch (MojoException e) { + assertEquals(MojoResult.INVALID_ARGUMENT, e.getMojoResult()); + } + } + + /** + * Testing the pass method on message pipes. + */ + @SmallTest + public void testMessagePipeHandlePass() { + Core core = CoreImpl.getInstance(); + Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null); + addHandlePairToClose(handles); + + assertTrue(handles.first.isValid()); + MessagePipeHandle handleClone = handles.first.pass(); + + addHandleToClose(handleClone); + + assertFalse(handles.first.isValid()); + assertTrue(handleClone.isValid()); + checkSendingMessage(handleClone, handles.second); + checkSendingMessage(handles.second, handleClone); + } + + /** + * Testing the pass method on data pipes. + */ + @SmallTest + public void testDataPipeHandlePass() { + Core core = CoreImpl.getInstance(); + Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> handles = core.createDataPipe(null); + addHandlePairToClose(handles); + + DataPipe.ProducerHandle producerClone = handles.first.pass(); + DataPipe.ConsumerHandle consumerClone = handles.second.pass(); + + addHandleToClose(producerClone); + addHandleToClose(consumerClone); + + assertFalse(handles.first.isValid()); + assertFalse(handles.second.isValid()); + assertTrue(producerClone.isValid()); + assertTrue(consumerClone.isValid()); + checkSendingData(producerClone, consumerClone); + } + + /** + * Testing the pass method on shared buffers. + */ + @SmallTest + public void testSharedBufferPass() { + Core core = CoreImpl.getInstance(); + SharedBufferHandle handle = core.createSharedBuffer(null, 8); + addHandleToClose(handle); + SharedBufferHandle newHandle = handle.duplicate(null); + addHandleToClose(newHandle); + + SharedBufferHandle handleClone = handle.pass(); + SharedBufferHandle newHandleClone = newHandle.pass(); + + addHandleToClose(handleClone); + addHandleToClose(newHandleClone); + + assertFalse(handle.isValid()); + assertTrue(handleClone.isValid()); + checkSharing(handleClone, newHandleClone); + checkSharing(newHandleClone, handleClone); + } + + /** + * esting handle conversion to native and back. + */ + @SmallTest + public void testHandleConversion() { + Core core = CoreImpl.getInstance(); + Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null); + addHandlePairToClose(handles); + + MessagePipeHandle converted = + core.acquireNativeHandle(handles.first.releaseNativeHandle()).toMessagePipeHandle(); + addHandleToClose(converted); + + assertFalse(handles.first.isValid()); + + checkSendingMessage(converted, handles.second); + checkSendingMessage(handles.second, converted); + } +} diff --git a/mojo/android/javatests/src/org/chromium/mojo/system/impl/WatcherImplTest.java b/mojo/android/javatests/src/org/chromium/mojo/system/impl/WatcherImplTest.java new file mode 100644 index 0000000000..e14adb1160 --- /dev/null +++ b/mojo/android/javatests/src/org/chromium/mojo/system/impl/WatcherImplTest.java @@ -0,0 +1,255 @@ +// Copyright 2016 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.system.impl; + +import android.support.test.filters.SmallTest; + +import org.chromium.mojo.MojoTestCase; +import org.chromium.mojo.system.Core; +import org.chromium.mojo.system.Handle; +import org.chromium.mojo.system.InvalidHandle; +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.Watcher; +import org.chromium.mojo.system.Watcher.Callback; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +/** + * Testing the Watcher. + */ +public class WatcherImplTest extends MojoTestCase { + private List<Handle> mHandlesToClose = new ArrayList<Handle>(); + private Watcher mWatcher; + private Core mCore; + + /** + * @see MojoTestCase#setUp() + */ + @Override + protected void setUp() throws Exception { + super.setUp(); + mWatcher = new WatcherImpl(); + mCore = CoreImpl.getInstance(); + } + + /** + * @see MojoTestCase#tearDown() + */ + @Override + protected void tearDown() throws Exception { + mWatcher.destroy(); + MojoException toThrow = null; + for (Handle handle : mHandlesToClose) { + try { + handle.close(); + } catch (MojoException e) { + if (toThrow == null) { + toThrow = e; + } + } + } + if (toThrow != null) { + throw toThrow; + } + super.tearDown(); + } + + private void addHandlePairToClose(Pair<? extends Handle, ? extends Handle> handles) { + mHandlesToClose.add(handles.first); + mHandlesToClose.add(handles.second); + } + + private static class WatcherResult implements Callback { + private int mResult = Integer.MIN_VALUE; + private MessagePipeHandle mReadPipe; + + /** + * @param readPipe A MessagePipeHandle to read from when onResult triggers success. + */ + public WatcherResult(MessagePipeHandle readPipe) { + mReadPipe = readPipe; + } + public WatcherResult() { + this(null); + } + + /** + * @see Callback#onResult(int) + */ + @Override + public void onResult(int result) { + this.mResult = result; + + if (result == MojoResult.OK && mReadPipe != null) { + mReadPipe.readMessage( + null, 0, MessagePipeHandle.ReadFlags.none().setMayDiscard(true)); + } + } + + /** + * @return the result + */ + public int getResult() { + return mResult; + } + } + + /** + * Testing {@link Watcher} implementation. + */ + @SmallTest + public void testCorrectResult() { + // Checking a correct result. + Pair<MessagePipeHandle, MessagePipeHandle> handles = mCore.createMessagePipe(null); + addHandlePairToClose(handles); + final WatcherResult watcherResult = new WatcherResult(handles.first); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + mWatcher.start(handles.first, Core.HandleSignals.READABLE, watcherResult); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + handles.second.writeMessage( + ByteBuffer.allocateDirect(1), null, MessagePipeHandle.WriteFlags.NONE); + runLoopUntilIdle(); + assertEquals(MojoResult.OK, watcherResult.getResult()); + } + + /** + * Testing {@link Watcher} implementation. + */ + @SmallTest + public void testClosingPeerHandle() { + // Closing the peer handle. + Pair<MessagePipeHandle, MessagePipeHandle> handles = mCore.createMessagePipe(null); + addHandlePairToClose(handles); + + final WatcherResult watcherResult = new WatcherResult(); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + mWatcher.start(handles.first, Core.HandleSignals.READABLE, watcherResult); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + runLoopUntilIdle(); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + handles.second.close(); + runLoopUntilIdle(); + assertEquals(MojoResult.FAILED_PRECONDITION, watcherResult.getResult()); + } + + /** + * Testing {@link Watcher} implementation. + */ + @SmallTest + public void testClosingWatchedHandle() { + // Closing the peer handle. + Pair<MessagePipeHandle, MessagePipeHandle> handles = mCore.createMessagePipe(null); + addHandlePairToClose(handles); + + final WatcherResult watcherResult = new WatcherResult(); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + mWatcher.start(handles.first, Core.HandleSignals.READABLE, watcherResult); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + runLoopUntilIdle(); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + handles.first.close(); + runLoopUntilIdle(); + assertEquals(MojoResult.CANCELLED, watcherResult.getResult()); + } + + /** + * Testing {@link Watcher} implementation. + */ + @SmallTest + public void testInvalidHandle() { + // Closing the peer handle. + Pair<MessagePipeHandle, MessagePipeHandle> handles = mCore.createMessagePipe(null); + addHandlePairToClose(handles); + + final WatcherResult watcherResult = new WatcherResult(); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + handles.first.close(); + assertEquals(MojoResult.INVALID_ARGUMENT, + mWatcher.start(handles.first, Core.HandleSignals.READABLE, watcherResult)); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + runLoopUntilIdle(); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + } + + /** + * Testing {@link Watcher} implementation. + */ + @SmallTest + public void testDefaultInvalidHandle() { + final WatcherResult watcherResult = new WatcherResult(); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + assertEquals(MojoResult.INVALID_ARGUMENT, + mWatcher.start(InvalidHandle.INSTANCE, Core.HandleSignals.READABLE, watcherResult)); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + runLoopUntilIdle(); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + } + + /** + * Testing {@link Watcher} implementation. + */ + @SmallTest + public void testCancel() { + // Closing the peer handle. + Pair<MessagePipeHandle, MessagePipeHandle> handles = mCore.createMessagePipe(null); + addHandlePairToClose(handles); + + final WatcherResult watcherResult = new WatcherResult(); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + mWatcher.start(handles.first, Core.HandleSignals.READABLE, watcherResult); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + runLoopUntilIdle(); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + mWatcher.cancel(); + runLoopUntilIdle(); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + handles.second.writeMessage( + ByteBuffer.allocateDirect(1), null, MessagePipeHandle.WriteFlags.NONE); + runLoopUntilIdle(); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + } + + /** + * Testing {@link Watcher} implementation. + */ + @SmallTest + public void testImmediateCancelOnInvalidHandle() { + // Closing the peer handle. + Pair<MessagePipeHandle, MessagePipeHandle> handles = mCore.createMessagePipe(null); + addHandlePairToClose(handles); + + final WatcherResult watcherResult = new WatcherResult(); + handles.first.close(); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + + mWatcher.start(handles.first, Core.HandleSignals.READABLE, watcherResult); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + mWatcher.cancel(); + + runLoopUntilIdle(); + assertEquals(Integer.MIN_VALUE, watcherResult.getResult()); + } +} |