Index: content/public/android/javatests/src/org/chromium/content/browser/androidoverlay/DialogOverlayImplTest.java |
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/androidoverlay/DialogOverlayImplTest.java b/content/public/android/javatests/src/org/chromium/content/browser/androidoverlay/DialogOverlayImplTest.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..151c181f99c801881a83742d74b242037342ef5f |
--- /dev/null |
+++ b/content/public/android/javatests/src/org/chromium/content/browser/androidoverlay/DialogOverlayImplTest.java |
@@ -0,0 +1,304 @@ |
+// Copyright 2017 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.content.browser.androidoverlay; |
+ |
+import android.os.Handler; |
+import android.os.HandlerThread; |
+import android.support.test.filters.SmallTest; |
+ |
+import org.junit.Assert; |
+ |
+import org.chromium.base.test.util.CallbackHelper; |
+import org.chromium.base.test.util.Feature; |
+import org.chromium.content.browser.framehost.RenderFrameHostImpl; |
+import org.chromium.content_shell_apk.ContentShellTestBase; |
+import org.chromium.media.mojom.AndroidOverlayClient; |
+import org.chromium.media.mojom.AndroidOverlayConfig; |
+import org.chromium.mojo.common.mojom.UnguessableToken; |
+import org.chromium.mojo.system.MojoException; |
+ |
+import java.util.concurrent.ArrayBlockingQueue; |
+import java.util.concurrent.TimeUnit; |
+ |
+/** |
+ * Tests for DialogOverlayImpl. |
+ */ |
+public class DialogOverlayImplTest extends ContentShellTestBase { |
+ private static final String BLANK_URL = "about://blank"; |
+ |
+ // The routing token that we'll use to create overlays. |
+ UnguessableToken mRoutingToken; |
+ |
+ // overlay-ui thread. |
+ HandlerThread mOverlayUiThread; |
+ Handler mOverlayUiHandler; |
+ |
+ // Runnable that will be called on the browser UI thread when an overlay is released. |
+ Runnable mReleasedRunnable; |
+ |
+ // True if we should create a secure overlay. |
+ boolean mSecure; |
+ |
+ /** |
+ * AndroidOverlay client that supports waiting operations for callbacks. One may call |
+ * nextEvent() to get the next callback, waiting if needed. |
+ */ |
+ public static class Client implements AndroidOverlayClient { |
+ // AndroidOverlayClient |
+ public static final int SURFACE_READY = 0; |
+ public static final int DESTROYED = 1; |
+ public static final int CLOSE = 2; |
+ public static final int CONNECTION_ERROR = 2; |
+ // AndroidOverlayProviderImpl.Callbacks |
+ public static final int RELEASED = 100; |
+ |
+ /** |
+ * Records one callback event. |
+ */ |
+ public static class Event { |
+ public Event(int which) { |
+ this.which = which; |
+ } |
+ |
+ public Event(int which, long surfaceKey) { |
+ this.which = which; |
+ this.surfaceKey = surfaceKey; |
+ } |
+ |
+ public Event(int which, MojoException exception) { |
+ this.which = which; |
+ } |
+ |
+ public int which; |
+ public long surfaceKey; |
+ } |
+ |
+ private ArrayBlockingQueue<Event> mPending; |
+ |
+ public Client() { |
+ mPending = new ArrayBlockingQueue<Event>(10); |
+ } |
+ |
+ @Override |
+ public void onSurfaceReady(long surfaceKey) { |
+ mPending.add(new Event(SURFACE_READY, surfaceKey)); |
+ } |
+ |
+ @Override |
+ public void onDestroyed() { |
+ mPending.add(new Event(DESTROYED)); |
+ } |
+ |
+ @Override |
+ public void close() { |
+ mPending.add(new Event(CLOSE)); |
+ } |
+ |
+ @Override |
+ public void onConnectionError(MojoException exception) { |
+ mPending.add(new Event(CONNECTION_ERROR, exception)); |
+ } |
+ |
+ // This isn't part of the overlay client. It's called by the overlay to indicate that it |
+ // has been released by the client, but it's routed to us anyway. It's on the Browser UI |
+ // thread, and it's convenient for us to keep track of it here. |
+ public void notifyReleased() { |
+ mPending.add(new Event(RELEASED)); |
+ } |
+ |
+ // Wait for something to happen. We enforce a timeout, since the test harness doesn't |
+ // always seem to fail the test when it times out. Plus, it takes ~minute for that, but |
+ // none of our messages should take that long. |
+ Event nextEvent() { |
+ try { |
+ return mPending.poll(500, TimeUnit.MILLISECONDS); |
+ } catch (InterruptedException e) { |
+ Assert.fail(e.toString()); |
+ } |
+ |
+ // NOTREACHED |
+ return null; |
+ } |
+ |
+ boolean isEmpty() { |
+ return mPending.size() == 0; |
+ } |
+ } |
+ |
+ private Client mClient = new Client(); |
+ |
+ @Override |
+ public void setUp() throws Exception { |
+ super.setUp(); |
+ |
+ startActivityWithTestUrl(BLANK_URL); |
+ |
+ // Fetch the routing token. |
+ mRoutingToken = new UIAction<UnguessableToken>() { |
+ public UnguessableToken runOnUiThread() { |
+ RenderFrameHostImpl host = (RenderFrameHostImpl) getWebContents().getMainFrame(); |
+ org.chromium.base.UnguessableToken routingToken = |
+ host.getAndroidOverlayRoutingToken(); |
+ UnguessableToken mojoToken = new UnguessableToken(); |
+ mojoToken.high = routingToken.getHighForSerialization(); |
+ mojoToken.low = routingToken.getLowForSerialization(); |
+ return mojoToken; |
+ } |
+ }.run(); |
+ |
+ // Set up the overlay UI thread |
+ mOverlayUiThread = new HandlerThread("TestOverlayUI"); |
+ mOverlayUiThread.start(); |
+ mOverlayUiHandler = new Handler(mOverlayUiThread.getLooper()); |
+ |
+ // Just delegate to |mClient| when an overlay is released. |
+ mReleasedRunnable = new Runnable() { |
+ @Override |
+ public void run() { |
+ mClient.notifyReleased(); |
+ } |
+ }; |
+ } |
+ |
+ // Handly class to compute something on the UI thread and return a value. To use: |
+ // T result = new UIAction<T> { |
+ // @Override |
+ // public T runOnUiThread() { |
+ // // Do stuff. |
+ // return (something of type T); |
+ // }}.run(); |
+ abstract class UIAction<T> { |
+ private CallbackHelper mCallbackHelper; |
+ private Runnable mRunnable; |
+ private T mReturnValue; |
+ |
+ public UIAction() { |
+ mCallbackHelper = new CallbackHelper(); |
+ mRunnable = new Runnable() { |
+ @Override |
+ public void run() { |
+ mReturnValue = UIAction.this.runOnUiThread(); |
+ mCallbackHelper.notifyCalled(); |
+ } |
+ }; |
+ } |
+ |
+ // Fill this in with whatever you'd like to run on the UI thread. |
+ public abstract T runOnUiThread(); |
+ |
+ // Run runOnUiThread() on the UI thread, and return the result. For convenience, we don't |
+ // send along errors. We just fail. |
+ public T run() { |
+ try { |
+ handleBlockingCallbackAction(mCallbackHelper, mRunnable); |
+ } catch (Throwable t) { |
+ Assert.fail(t.toString()); |
+ } |
+ return mReturnValue; |
+ } |
+ } |
+ |
+ // Create an overlay with the given parameters and return it. |
+ DialogOverlayImpl createOverlay(final int x, final int y, final int width, final int height) { |
+ return new UIAction<DialogOverlayImpl>() { |
+ @Override |
+ public DialogOverlayImpl runOnUiThread() { |
+ AndroidOverlayConfig config = new AndroidOverlayConfig(); |
+ config.routingToken = mRoutingToken; |
+ config.rect = new org.chromium.gfx.mojom.Rect(); |
+ config.rect.x = x; |
+ config.rect.y = y; |
+ config.rect.width = width; |
+ config.rect.height = height; |
+ config.secure = mSecure; |
+ HandlerThread overlayUiThread = new HandlerThread("TestOverlayUI"); |
+ overlayUiThread.start(); |
+ DialogOverlayOperations ops = |
+ new DialogOverlayOperationsImpl(mOverlayUiHandler, mReleasedRunnable); |
+ DialogOverlayImpl impl = new DialogOverlayImpl(mClient, config, ops); |
+ |
+ return impl; |
+ } |
+ } |
+ .run(); |
+ } |
+ |
+ @SmallTest |
+ @Feature({"AndroidOverlay"}) |
+ public void testCreateDestroyOverlay() { |
+ final DialogOverlayImpl overlay = createOverlay(0, 0, 10, 10); |
+ |
+ // We should get a new overlay with a valid surface key. |
+ Client.Event event = mClient.nextEvent(); |
+ Assert.assertEquals(Client.SURFACE_READY, event.which); |
+ Assert.assertTrue(event.surfaceKey > 0); |
+ |
+ // Close the overlay, and make sure that the provider is notified. |
+ // Note that we should not get a 'destroyed' message when we close it. |
+ new UIAction<Boolean>() { |
+ @Override |
+ public Boolean runOnUiThread() { |
+ overlay.close(); |
+ return true; |
+ } |
+ } |
+ .run(); |
+ Assert.assertEquals(Client.RELEASED, mClient.nextEvent().which); |
+ } |
+ |
+ @SmallTest |
+ @Feature({"AndroidOverlay"}) |
+ public void testCreateOverlayFailsIfUnknownRoutingToken() { |
+ // Try to create an overlay with a bad routing token. |
+ mRoutingToken.high++; |
+ DialogOverlayImpl overlay = createOverlay(0, 0, 10, 10); |
+ Assert.assertNotNull(overlay); |
+ |
+ // We should be notified that the overlay is destroyed. |
+ Client.Event event = mClient.nextEvent(); |
+ Assert.assertEquals(Client.DESTROYED, event.which); |
+ } |
+ |
+ @SmallTest |
+ @Feature({"AndroidOverlay"}) |
+ public void testScheduleLayoutDoesntCrash() { |
+ // Make sure that we don't get any messages due to scheduleLayout, and we don't crash. |
+ final DialogOverlayImpl overlay = createOverlay(0, 0, 10, 10); |
+ |
+ // Wait for the surface. |
+ Assert.assertEquals(Client.SURFACE_READY, mClient.nextEvent().which); |
+ final org.chromium.gfx.mojom.Rect rect = new org.chromium.gfx.mojom.Rect(); |
+ rect.x = 100; |
+ rect.y = 200; |
+ rect.width = 100; |
+ rect.height = 100; |
+ new UIAction<Boolean>() { |
+ @Override |
+ public Boolean runOnUiThread() { |
+ overlay.scheduleLayout(rect); |
+ return true; |
+ } |
+ } |
+ .run(); |
+ |
+ // No additional messages should have arrived. |
+ Assert.assertTrue(mClient.isEmpty()); |
+ } |
+ |
+ @SmallTest |
+ @Feature({"AndroidOverlay"}) |
+ public void testCreateSecureSurface() { |
+ // Test that creating a secure overlay creates an overlay. We can't really tell if it's |
+ // secure or not, until we can do a screen shot test. |
+ mSecure = true; |
+ final DialogOverlayImpl overlay = createOverlay(0, 0, 10, 10); |
+ Assert.assertNotNull(overlay); |
+ |
+ // We should get a new overlay with a valid surface key. |
+ Client.Event event = mClient.nextEvent(); |
+ Assert.assertEquals(Client.SURFACE_READY, event.which); |
+ Assert.assertTrue(event.surfaceKey > 0); |
+ } |
+} |