| Index: content/public/android/junit/src/org/chromium/content/browser/androidoverlay/DialogOverlayCoreTest.java
|
| diff --git a/content/public/android/junit/src/org/chromium/content/browser/androidoverlay/DialogOverlayCoreTest.java b/content/public/android/junit/src/org/chromium/content/browser/androidoverlay/DialogOverlayCoreTest.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0867ae9e7ed10a37821e4e08d60ad7b0c7ccafdb
|
| --- /dev/null
|
| +++ b/content/public/android/junit/src/org/chromium/content/browser/androidoverlay/DialogOverlayCoreTest.java
|
| @@ -0,0 +1,280 @@
|
| +// 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 static org.junit.Assert.assertEquals;
|
| +import static org.junit.Assert.assertNotNull;
|
| +import static org.junit.Assert.assertTrue;
|
| +
|
| +import android.app.Activity;
|
| +import android.app.Dialog;
|
| +import android.os.Binder;
|
| +import android.os.IBinder;
|
| +import android.view.Surface;
|
| +import android.view.SurfaceHolder;
|
| +
|
| +// TODO(liberato): prior to M, this was ...policy.impl.PhoneWindow
|
| +import com.android.internal.policy.PhoneWindow;
|
| +
|
| +import org.junit.Before;
|
| +import org.junit.Test;
|
| +import org.junit.runner.RunWith;
|
| +import org.robolectric.Robolectric;
|
| +import org.robolectric.Shadows;
|
| +import org.robolectric.annotation.Config;
|
| +import org.robolectric.annotation.Implementation;
|
| +import org.robolectric.annotation.Implements;
|
| +import org.robolectric.shadows.ShadowDialog;
|
| +import org.robolectric.shadows.ShadowPhoneWindow;
|
| +import org.robolectric.shadows.ShadowSurfaceView;
|
| +
|
| +import org.chromium.gfx.mojom.Rect;
|
| +import org.chromium.media.mojom.AndroidOverlayConfig;
|
| +import org.chromium.testing.local.LocalRobolectricTestRunner;
|
| +
|
| +/**
|
| + * Tests for DialogOverlayCore.
|
| + */
|
| +@RunWith(LocalRobolectricTestRunner.class)
|
| +@Config(manifest = Config.NONE)
|
| +public class DialogOverlayCoreTest {
|
| + private Activity mActivity;
|
| +
|
| + AndroidOverlayConfig mConfig = new AndroidOverlayConfig();
|
| +
|
| + // DialogCore under test.
|
| + DialogOverlayCore mCore;
|
| +
|
| + // The dialog that we've provided to |mCore|.
|
| + Dialog mDialog;
|
| +
|
| + // Fake window token that we'll send to |mCore|.
|
| + IBinder mWindowToken = new Binder();
|
| +
|
| + // Surface that will be provided by |mDialog|.
|
| + Surface mSurface = new Surface();
|
| +
|
| + // SurfaceHolder that will be provided by |mDialog|.
|
| + SurfaceHolder mHolder = new MyFakeSurfaceHolder(mSurface);
|
| +
|
| + /**
|
| + * Robolectric shadow for PhoneWindow. This one keeps track of takeSurface() calls.
|
| + * TODO(liberato): the @Impl specifies 'minSdk=M' in the robolectric source.
|
| + */
|
| + @Implements(value = PhoneWindow.class, isInAndroidSdk = false)
|
| + public static class MyPhoneWindowShadow extends ShadowPhoneWindow {
|
| + public MyPhoneWindowShadow() {}
|
| +
|
| + private SurfaceHolder.Callback2 mCallback;
|
| +
|
| + @Implementation
|
| + public void takeSurface(SurfaceHolder.Callback2 callback) {
|
| + mCallback = callback;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * The default fake surface holder doesn't let us provide a surface.
|
| + */
|
| + public static class MyFakeSurfaceHolder extends ShadowSurfaceView.FakeSurfaceHolder {
|
| + private Surface mSurface;
|
| +
|
| + // @param surface The Surface that we'll provide via getSurface.
|
| + public MyFakeSurfaceHolder(Surface surface) {
|
| + mSurface = surface;
|
| + }
|
| +
|
| + @Override
|
| + public Surface getSurface() {
|
| + return mSurface;
|
| + }
|
| + }
|
| +
|
| + @Before
|
| + public void setUp() {
|
| + mActivity = Robolectric.buildActivity(Activity.class).setup().get();
|
| +
|
| + mConfig = new AndroidOverlayConfig();
|
| + mConfig.rect = new Rect();
|
| + mConfig.rect.x = 0;
|
| + mConfig.rect.y = 1;
|
| + mConfig.rect.width = 2;
|
| + mConfig.rect.height = 3;
|
| +
|
| + mCore = new DialogOverlayCore();
|
| + mDialog = new Dialog(mActivity);
|
| + mCore.initialize(mDialog, mConfig, mHost);
|
| +
|
| + // Nothing should be called yet.
|
| + checkOverlayDidntCall();
|
| +
|
| + // The dialog should not be shown yet.
|
| + checkDialogIsNotShown();
|
| + }
|
| +
|
| + // Make sure that the overlay didn't provide us with a surface, or notify us that it was
|
| + // destroyed, or wait for cleanup.
|
| + void checkOverlayDidntCall() {
|
| + assertEquals(null, mHost.surface());
|
| + assertEquals(0, mHost.destroyedCount());
|
| + assertEquals(0, mHost.cleanupCount());
|
| + }
|
| +
|
| + // Return the SurfaceHolder callback that was provided to takeSurface(), if any.
|
| + SurfaceHolder.Callback2 holderCallback() {
|
| + return ((MyPhoneWindowShadow) Shadows.shadowOf(mDialog.getWindow())).mCallback;
|
| + }
|
| +
|
| + /**
|
| + * Host impl that counts calls to it.
|
| + */
|
| + class HostMock implements DialogOverlayCore.Host {
|
| + private Surface mSurface;
|
| + private int mDestroyedCount;
|
| + private int mCleanupCount;
|
| +
|
| + @Override
|
| + public void onSurfaceReady(Surface surface) {
|
| + mSurface = surface;
|
| + }
|
| +
|
| + @Override
|
| + public void onOverlayDestroyed() {
|
| + mDestroyedCount++;
|
| + }
|
| +
|
| + @Override
|
| + public void waitForCleanup() {
|
| + mCleanupCount++;
|
| + }
|
| +
|
| + public Surface surface() {
|
| + return mSurface;
|
| + }
|
| +
|
| + public int destroyedCount() {
|
| + return mDestroyedCount;
|
| + }
|
| +
|
| + public int cleanupCount() {
|
| + return mCleanupCount;
|
| + }
|
| + };
|
| +
|
| + HostMock mHost = new HostMock();
|
| +
|
| + // Send a window token and provide the surface, so that the overlay is ready for use.
|
| + void sendTokenAndSurface() {
|
| + mCore.onWindowToken(mWindowToken);
|
| + // Make sure that somebody called takeSurface.
|
| + assertNotNull(holderCallback());
|
| +
|
| + checkDialogIsShown();
|
| +
|
| + // Provide the Android Surface.
|
| + holderCallback().surfaceCreated(mHolder);
|
| +
|
| + // The host should have been told about the surface.
|
| + assertEquals(mSurface, mHost.surface());
|
| + }
|
| +
|
| + // Verify that the dialog has been shown.
|
| + void checkDialogIsShown() {
|
| + assertEquals(mDialog, ShadowDialog.getShownDialogs().get(0));
|
| + }
|
| +
|
| + // Verify that the dialog is not currently shown. Note that dismiss() doesn't remove it from
|
| + // the shown dialog list in Robolectric, so we check for "was never shown or was dismissed".
|
| + void checkDialogIsNotShown() {
|
| + assertTrue(ShadowDialog.getShownDialogs().size() == 0
|
| + || Shadows.shadowOf(mDialog).hasBeenDismissed());
|
| + }
|
| +
|
| + // Verify that |mCore| signaled that the overlay was lost to|mHost|.
|
| + void checkOverlayWasDestroyed() {
|
| + // |mCore| should have notified the host that it has been destroyed, and also waited for
|
| + // the host to signal that the client released it.
|
| + assertEquals(1, mHost.destroyedCount());
|
| + checkDialogIsNotShown();
|
| + }
|
| +
|
| + // Check that releasing an overlay before getting a window token works.
|
| + @Test
|
| + @Config(shadows = {MyPhoneWindowShadow.class})
|
| + public void testReleaseImmediately() {
|
| + // Release the overlay. |mCore| shouldn't notify us, since we released it.
|
| + mCore.release();
|
| + checkOverlayDidntCall();
|
| + checkDialogIsNotShown();
|
| + }
|
| +
|
| + // Create a dialog, then send it a token. Verify that it's shown.
|
| + @Test
|
| + @Config(shadows = {MyPhoneWindowShadow.class})
|
| + public void testTokenThenRelease() {
|
| + mCore.onWindowToken(mWindowToken);
|
| + checkDialogIsShown();
|
| +
|
| + // Release the surface. |mHost| shouldn't be notified, nor should it wait for cleanup.
|
| + // Note: it might be okay if it checks for cleanup, since cleanup would be complete after
|
| + // we call release(). However, it's not needed, so we enforce that it isn't.
|
| + mCore.release();
|
| + checkOverlayDidntCall();
|
| + checkDialogIsNotShown();
|
| + }
|
| +
|
| + // Create a dialog, send a token, send a surface, then release it.
|
| + @Test
|
| + @Config(shadows = {MyPhoneWindowShadow.class})
|
| + public void testSurfaceThenRelease() {
|
| + sendTokenAndSurface();
|
| +
|
| + mCore.release();
|
| + assertEquals(0, mHost.destroyedCount());
|
| + assertEquals(0, mHost.cleanupCount());
|
| + checkDialogIsNotShown();
|
| + }
|
| +
|
| + // Create a dialog, send a surface, then destroy the surface.
|
| + @Test
|
| + @Config(shadows = {MyPhoneWindowShadow.class})
|
| + public void testSurfaceThenDestroy() {
|
| + sendTokenAndSurface();
|
| +
|
| + // Destroy the surface.
|
| + holderCallback().surfaceDestroyed(mHolder);
|
| + // |mCore| should have waited for cleanup during surfaceDestroyed.
|
| + assertEquals(1, mHost.cleanupCount());
|
| + // Since we waited for cleanup, also pretend that the release was posted during the wait and
|
| + // will arrive after the wait completes.
|
| + mCore.release();
|
| +
|
| + checkOverlayWasDestroyed();
|
| + }
|
| +
|
| + // Test that we're notified when the window token changes.
|
| + @Test
|
| + @Config(shadows = {MyPhoneWindowShadow.class})
|
| + public void testChangeWindowToken() {
|
| + sendTokenAndSurface();
|
| +
|
| + // Change the window token.
|
| + mCore.onWindowToken(new Binder());
|
| +
|
| + checkOverlayWasDestroyed();
|
| + }
|
| +
|
| + // Test that we're notified when the window token is lost.
|
| + @Test
|
| + @Config(shadows = {MyPhoneWindowShadow.class})
|
| + public void testLoseWindowToken() {
|
| + sendTokenAndSurface();
|
| +
|
| + // Remove the window token.
|
| + mCore.onWindowToken(null);
|
| +
|
| + checkOverlayWasDestroyed();
|
| + }
|
| +}
|
|
|