Index: chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManagerTest.java |
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManagerTest.java |
deleted file mode 100644 |
index 4ed9c7cbde5251c82acd5d8f46f651910898db02..0000000000000000000000000000000000000000 |
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManagerTest.java |
+++ /dev/null |
@@ -1,440 +0,0 @@ |
-// 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.chrome.browser.compositor; |
- |
-import android.app.Activity; |
-import android.graphics.PixelFormat; |
-import android.view.Surface; |
-import android.view.SurfaceHolder; |
-import android.view.SurfaceView; |
-import android.widget.FrameLayout; |
- |
-import static org.hamcrest.Matchers.lessThan; |
-import static org.junit.Assert.assertEquals; |
-import static org.junit.Assert.assertNotNull; |
-import static org.junit.Assert.assertThat; |
-import static org.junit.Assert.assertTrue; |
-import static org.mockito.ArgumentMatchers.anyInt; |
-import static org.mockito.ArgumentMatchers.eq; |
-import static org.mockito.Mockito.times; |
-import static org.mockito.Mockito.verify; |
- |
-import org.junit.Before; |
-import org.junit.Test; |
-import org.junit.runner.RunWith; |
-import org.mockito.ArgumentMatchers; |
-import org.mockito.Mock; |
-import org.mockito.MockitoAnnotations; |
-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.ShadowLooper; |
-import org.robolectric.shadows.ShadowSurfaceView; |
- |
-import org.chromium.base.test.util.Feature; |
-import org.chromium.testing.local.LocalRobolectricTestRunner; |
- |
-import java.util.Set; |
- |
-/** |
- * Unit tests for the CompositorSurfaceManager. |
- */ |
-@RunWith(LocalRobolectricTestRunner.class) |
-@Config(manifest = Config.NONE) |
-public class CompositorSurfaceManagerTest { |
- @Mock |
- private SurfaceHolder.Callback mCallback; |
- |
- private CompositorSurfaceManager mManager; |
- |
- private FrameLayout mLayout; |
- |
- /** |
- * Implementation of a SurfaceView shadow that provides additional functionality for controlling |
- * the state of the underlying (fake) Surface. |
- */ |
- @Implements(SurfaceView.class) |
- public static class MyShadowSurfaceView extends ShadowSurfaceView { |
- private final MyFakeSurfaceHolder mHolder = new MyFakeSurfaceHolder(); |
- |
- /** |
- * Robolectric's FakeSurfaceHolder doesn't keep track of the format, etc. |
- */ |
- public static class MyFakeSurfaceHolder extends ShadowSurfaceView.FakeSurfaceHolder { |
- /** |
- * Fake surface that lets us control whether it's valid or not. |
- */ |
- public static class MyFakeSurface extends Surface { |
- public boolean valid = false; |
- |
- @Override |
- public boolean isValid() { |
- return valid; |
- } |
- } |
- |
- private int mFormat = PixelFormat.UNKNOWN; |
- private final MyFakeSurface mSurface = new MyFakeSurface(); |
- |
- @Implementation |
- public void setFormat(int format) { |
- mFormat = format; |
- } |
- |
- public int getFormat() { |
- return mFormat; |
- } |
- |
- // Return a surface that we can control if it's valid or not. |
- @Override |
- public Surface getSurface() { |
- return getFakeSurface(); |
- } |
- |
- public MyFakeSurface getFakeSurface() { |
- return mSurface; |
- } |
- } |
- |
- public MyShadowSurfaceView() {} |
- |
- @Implementation |
- public SurfaceHolder getHolder() { |
- return getMyFakeSurfaceHolder(); |
- } |
- |
- @Override |
- public FakeSurfaceHolder getFakeSurfaceHolder() { |
- return getMyFakeSurfaceHolder(); |
- } |
- |
- public MyFakeSurfaceHolder getMyFakeSurfaceHolder() { |
- return mHolder; |
- } |
- } |
- |
- @Before |
- public void beforeTest() { |
- MockitoAnnotations.initMocks(this); |
- Activity activity = Robolectric.buildActivity(Activity.class).setup().get(); |
- mLayout = new FrameLayout(activity); |
- mManager = new CompositorSurfaceManager(mLayout, mCallback); |
- } |
- |
- private void runDelayedTasks() { |
- ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); |
- } |
- |
- /** |
- * Return the callback for |view|, or null. Will get mad if there's more than one. |
- */ |
- private SurfaceHolder.Callback callbackFor(SurfaceView view) { |
- MyShadowSurfaceView viewShadow = (MyShadowSurfaceView) Shadows.shadowOf(view); |
- ShadowSurfaceView.FakeSurfaceHolder viewHolder = viewShadow.getFakeSurfaceHolder(); |
- Set<SurfaceHolder.Callback> callbacks = viewHolder.getCallbacks(); |
- // Zero or one is okay. |
- assertThat(callbacks.size(), lessThan(2)); |
- |
- if (callbacks.size() == 1) return callbacks.iterator().next(); |
- |
- return null; |
- } |
- |
- private MyShadowSurfaceView.MyFakeSurfaceHolder fakeHolderFor(SurfaceView view) { |
- MyShadowSurfaceView viewShadow = (MyShadowSurfaceView) Shadows.shadowOf(view); |
- return viewShadow.getMyFakeSurfaceHolder(); |
- } |
- |
- private void setSurfaceValid(SurfaceView view, boolean valid) { |
- fakeHolderFor(view).getFakeSurface().valid = valid; |
- } |
- |
- /** |
- * Find and return the SurfaceView with format |format|. |
- */ |
- private SurfaceView findSurface(int format) { |
- final int childCount = mLayout.getChildCount(); |
- for (int i = 0; i < childCount; i++) { |
- final SurfaceView child = (SurfaceView) mLayout.getChildAt(i); |
- if (fakeHolderFor(child).getFormat() == format) return child; |
- } |
- |
- return null; |
- } |
- |
- /** |
- * Request the pixel format |format|, and return the SurfaceView for it if it's attached. You |
- * are responsible for sending surfaceCreated / Changed to |mManager| if you want it to think |
- * that Android has provided the Surface. |
- */ |
- private SurfaceView requestSurface(int format) { |
- mManager.requestSurface(format); |
- runDelayedTasks(); |
- |
- return findSurface(format); |
- } |
- |
- /** |
- * Request format |format|, and send created / changed callbacks to |mManager| as if Android |
- * had provided the underlying Surface. |
- */ |
- private SurfaceView requestThenCreateSurface(int format) { |
- SurfaceView view = requestSurface(format); |
- setSurfaceValid(view, true); |
- callbackFor(view).surfaceCreated(view.getHolder()); |
- final int actualFormat = |
- (format == PixelFormat.OPAQUE) ? PixelFormat.RGB_565 : PixelFormat.RGBA_8888; |
- final int width = 320; |
- final int height = 240; |
- callbackFor(view).surfaceChanged(view.getHolder(), actualFormat, width, height); |
- |
- return view; |
- } |
- |
- @Test |
- @Feature("Compositor") |
- @Config(shadows = {MyShadowSurfaceView.class}) |
- public void testRequestOpaqueSurface() { |
- // Request a SurfaceView, and test in detail that it worked. |
- SurfaceView opaque = requestSurface(PixelFormat.OPAQUE); |
- verify(mCallback, times(0)).surfaceCreated(ArgumentMatchers.<SurfaceHolder>any()); |
- verify(mCallback, times(0)) |
- .surfaceChanged( |
- ArgumentMatchers.<SurfaceHolder>any(), anyInt(), anyInt(), anyInt()); |
- verify(mCallback, times(0)).surfaceDestroyed(ArgumentMatchers.<SurfaceHolder>any()); |
- |
- // Check that there's an opaque SurfaceView . |
- assertEquals(1, mLayout.getChildCount()); |
- assertTrue(fakeHolderFor(opaque).getFormat() == PixelFormat.OPAQUE); |
- |
- // Verify that we are notified when the surface is created. |
- callbackFor(opaque).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(0)).surfaceDestroyed(ArgumentMatchers.<SurfaceHolder>any()); |
- |
- // Verify that we are notified when the surface is changed. |
- final int format = PixelFormat.RGB_565; |
- final int width = 320; |
- final int height = 240; |
- callbackFor(opaque).surfaceChanged(opaque.getHolder(), format, width, height); |
- verify(mCallback, times(1)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceChanged(opaque.getHolder(), format, width, height); |
- verify(mCallback, times(0)).surfaceDestroyed(ArgumentMatchers.<SurfaceHolder>any()); |
- |
- // Verify that we are notified when the surface is destroyed. |
- callbackFor(opaque).surfaceDestroyed(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceChanged(opaque.getHolder(), format, width, height); |
- verify(mCallback, times(1)).surfaceDestroyed(opaque.getHolder()); |
- } |
- |
- @Test |
- @Feature("Compositor") |
- @Config(shadows = {MyShadowSurfaceView.class}) |
- public void testRequestOpaqueThenTranslucentSurface() { |
- // Request opaque then translucent. |
- SurfaceView opaque = requestThenCreateSurface(PixelFormat.OPAQUE); |
- SurfaceView translucent = requestThenCreateSurface(PixelFormat.TRANSLUCENT); |
- |
- // Verify that we received a destroy for |opaque| and created / changed for |translucent|. |
- verify(mCallback, times(1)).surfaceDestroyed(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceCreated(translucent.getHolder()); |
- verify(mCallback, times(1)) |
- .surfaceChanged(eq(translucent.getHolder()), anyInt(), anyInt(), anyInt()); |
- |
- // Both views should be present. |
- assertEquals(2, mLayout.getChildCount()); |
- |
- // Only the translucent surface should be left. Note that the old view is still valid. |
- mManager.doneWithUnownedSurface(); |
- runDelayedTasks(); |
- assertEquals(1, mLayout.getChildCount()); |
- assertNotNull(findSurface(PixelFormat.TRANSLUCENT)); |
- } |
- |
- @Test |
- @Feature("Compositor") |
- @Config(shadows = {MyShadowSurfaceView.class}) |
- public void testRequestSameSurface() { |
- // Request an opaque surface, get it, then request it again. Verify that we get synthetic |
- // create / destroy callbacks. |
- SurfaceView opaque = requestThenCreateSurface(PixelFormat.OPAQUE); |
- verify(mCallback, times(1)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(1)) |
- .surfaceChanged(eq(opaque.getHolder()), anyInt(), anyInt(), anyInt()); |
- verify(mCallback, times(0)).surfaceDestroyed(opaque.getHolder()); |
- |
- // Surface is curerntly valid. Request again. We should get back a destroy and create. |
- assertEquals(opaque, requestSurface(PixelFormat.OPAQUE)); |
- verify(mCallback, times(2)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(2)) |
- .surfaceChanged(eq(opaque.getHolder()), anyInt(), anyInt(), anyInt()); |
- verify(mCallback, times(1)).surfaceDestroyed(opaque.getHolder()); |
- assertEquals(1, mLayout.getChildCount()); |
- } |
- |
- @Test |
- @Feature("Compositor") |
- @Config(shadows = {MyShadowSurfaceView.class}) |
- public void testRequestSameSurfaceBeforeReady() { |
- // Request an opaque surface, then request it again before the first one shows up. |
- SurfaceView opaque = requestSurface(PixelFormat.OPAQUE); |
- verify(mCallback, times(0)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(0)) |
- .surfaceChanged(eq(opaque.getHolder()), anyInt(), anyInt(), anyInt()); |
- verify(mCallback, times(0)).surfaceDestroyed(opaque.getHolder()); |
- |
- // Request again. We shouldn't get any callbacks, since the surface is still pending. |
- assertEquals(opaque, requestSurface(PixelFormat.OPAQUE)); |
- verify(mCallback, times(0)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(0)) |
- .surfaceChanged(eq(opaque.getHolder()), anyInt(), anyInt(), anyInt()); |
- verify(mCallback, times(0)).surfaceDestroyed(opaque.getHolder()); |
- |
- // Only the opaque view should be attached. |
- assertEquals(1, mLayout.getChildCount()); |
- |
- // When the surface is created, we should get notified created / changed, but not destroyed. |
- callbackFor(opaque).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceCreated(opaque.getHolder()); |
- |
- callbackFor(opaque).surfaceChanged(opaque.getHolder(), PixelFormat.RGB_565, 320, 240); |
- verify(mCallback, times(1)) |
- .surfaceChanged(eq(opaque.getHolder()), anyInt(), anyInt(), anyInt()); |
- verify(mCallback, times(0)).surfaceDestroyed(opaque.getHolder()); |
- } |
- |
- @Test |
- @Feature("Compositor") |
- @Config(shadows = {MyShadowSurfaceView.class}) |
- public void testRequestDifferentSurfacesBeforeReady() { |
- // Request an opaque surface, then request the translucent one before the it one shows up. |
- SurfaceView opaque = requestSurface(PixelFormat.OPAQUE); |
- verify(mCallback, times(0)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(0)) |
- .surfaceChanged(eq(opaque.getHolder()), anyInt(), anyInt(), anyInt()); |
- verify(mCallback, times(0)).surfaceDestroyed(opaque.getHolder()); |
- |
- // Request translucent. We should get no callbacks, but both views should be attached. |
- SurfaceView translucent = requestSurface(PixelFormat.TRANSLUCENT); |
- verify(mCallback, times(0)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(0)).surfaceCreated(translucent.getHolder()); |
- assertEquals(2, mLayout.getChildCount()); |
- |
- // If the opaque surface arrives, we shouldn't hear about it. It should be detached, since |
- // we've requested the other one. |
- callbackFor(opaque).surfaceCreated(opaque.getHolder()); |
- runDelayedTasks(); |
- assertEquals(1, mLayout.getChildCount()); |
- verify(mCallback, times(0)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(0)).surfaceCreated(translucent.getHolder()); |
- verify(mCallback, times(0)).surfaceDestroyed(opaque.getHolder()); |
- |
- // When we create the translucent surface, we should be notified. |
- callbackFor(translucent).surfaceCreated(translucent.getHolder()); |
- verify(mCallback, times(0)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceCreated(translucent.getHolder()); |
- } |
- |
- @Test |
- @Feature("Compositor") |
- @Config(shadows = {MyShadowSurfaceView.class}) |
- public void testPendingSurfaceChangedCallback() { |
- // Request an opaque surface, and request it again between 'created' and 'changed'. We |
- // should get a synthetic 'created', but a real 'changed' callback. |
- SurfaceView opaque = requestSurface(PixelFormat.OPAQUE); |
- callbackFor(opaque).surfaceCreated(opaque.getHolder()); |
- runDelayedTasks(); |
- |
- // Sanity check. |
- verify(mCallback, times(1)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(0)) |
- .surfaceChanged(eq(opaque.getHolder()), anyInt(), anyInt(), anyInt()); |
- |
- // Re-request while 'changed' is still pending. We should get a synthetic 'destroyed' and |
- // synthetic 'created'. |
- assertEquals(opaque, requestSurface(PixelFormat.OPAQUE)); |
- verify(mCallback, times(2)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceDestroyed(opaque.getHolder()); |
- verify(mCallback, times(0)) |
- .surfaceChanged(eq(opaque.getHolder()), anyInt(), anyInt(), anyInt()); |
- |
- // Send 'changed', and expect that we'll receive it. |
- callbackFor(opaque).surfaceChanged(opaque.getHolder(), PixelFormat.RGB_565, 320, 240); |
- verify(mCallback, times(1)) |
- .surfaceChanged(eq(opaque.getHolder()), anyInt(), anyInt(), anyInt()); |
- } |
- |
- @Test |
- @Feature("Compositor") |
- @Config(shadows = {MyShadowSurfaceView.class}) |
- public void testJellyBeanWorkaround() { |
- // See if recreateSurfaceForJellyBean destroys / re-creates the surface. |
- // should get a synthetic 'created', but a real 'changed' callback. |
- SurfaceView opaque = requestThenCreateSurface(PixelFormat.OPAQUE); |
- verify(mCallback, times(1)).surfaceCreated(opaque.getHolder()); |
- assertEquals(1, mLayout.getChildCount()); |
- |
- // We should be notified that the surface was destroyed via synthetic callback, and the |
- // surface should be detached. |
- mManager.recreateSurfaceForJellyBean(); |
- verify(mCallback, times(1)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceDestroyed(opaque.getHolder()); |
- assertEquals(0, mLayout.getChildCount()); |
- |
- // When the surface really is destroyed, it should be re-attached. We should not be |
- // notified again, though. |
- callbackFor(opaque).surfaceDestroyed(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceDestroyed(opaque.getHolder()); |
- assertEquals(1, mLayout.getChildCount()); |
- |
- // When the surface is re-created, we should be notified. |
- callbackFor(opaque).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(2)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceDestroyed(opaque.getHolder()); |
- assertEquals(1, mLayout.getChildCount()); |
- } |
- |
- @Test |
- @Feature("Compositor") |
- @Config(shadows = {MyShadowSurfaceView.class}) |
- public void testRequestSurfaceDuringDestruction() { |
- // If we re-request a surface while we're tearing it down, it should be re-attached and |
- // given back to us once the destruction completes. |
- SurfaceView opaque = requestThenCreateSurface(PixelFormat.OPAQUE); |
- SurfaceView translucent = requestThenCreateSurface(PixelFormat.TRANSLUCENT); |
- mManager.doneWithUnownedSurface(); |
- |
- // The transparent surface should be attached, and the opaque one detached. |
- assertEquals(1, mLayout.getChildCount()); |
- assertNotNull(findSurface(PixelFormat.TRANSLUCENT)); |
- |
- // Re-request the opaque surface. Nothing should happen until it's destroyed. It should |
- // not be re-attached, since that is also deferred until destruction. |
- assertEquals(null, requestSurface(PixelFormat.OPAQUE)); |
- assertEquals(1, mLayout.getChildCount()); |
- assertNotNull(findSurface(PixelFormat.TRANSLUCENT)); |
- |
- // When the opaque surface is destroyed, then it should be re-attached. No callbacks shoud |
- // have arrived yet, except for initial creation and (synthetic) destroyed when we got the |
- // translucent surface. |
- callbackFor(opaque).surfaceDestroyed(opaque.getHolder()); |
- assertEquals(2, mLayout.getChildCount()); |
- verify(mCallback, times(1)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceDestroyed(opaque.getHolder()); |
- verify(mCallback, times(0)).surfaceDestroyed(translucent.getHolder()); |
- |
- // When the opaque surface becomes available, we'll get the synthetic destroy for the |
- // translucent one that we lost ownership of, and the real create for the opaque one. |
- callbackFor(opaque).surfaceCreated(opaque.getHolder()); |
- assertEquals(2, mLayout.getChildCount()); |
- verify(mCallback, times(2)).surfaceCreated(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceDestroyed(opaque.getHolder()); |
- verify(mCallback, times(1)).surfaceDestroyed(translucent.getHolder()); |
- } |
-} |