/* * Copyright (c) 2015, Ericsson AB. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ package com.ericsson.research.owr.sdk; import android.graphics.SurfaceTexture; import android.os.Handler; import android.os.HandlerThread; import android.view.TextureView; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class TestUtils { public static class SynchronousCallBuilder { private int mTimeout; private int mLatchCount; private Handler mAsyncHandler; private SynchronousCallBuilder(Handler asyncHandler) { mTimeout = 5; mLatchCount = 1; if (asyncHandler != null) { mAsyncHandler = asyncHandler; } else { HandlerThread thread = new HandlerThread("TestUtils-async-thread"); thread.start(); mAsyncHandler = new Handler(thread.getLooper()); } } public SynchronousCallBuilder timeout(int seconds) { mTimeout = seconds; return this; } public SynchronousCallBuilder latchCount(int count) { mLatchCount = count; return this; } public SynchronousCallBuilder run(final SynchronousBlock block) { final CountDownLatch latch = new CountDownLatch(mLatchCount); block.run(latch); waitForLatch(latch, 0); return new SynchronousCallBuilder(mAsyncHandler); } public SynchronousCallBuilder run(final Runnable runnable) { final CountDownLatch latch = new CountDownLatch(mLatchCount); mAsyncHandler.post(new Runnable() { @Override public void run() { runnable.run(); latch.countDown(); } }); waitForLatch(latch, 0); return new SynchronousCallBuilder(mAsyncHandler); } public SynchronousCallBuilder delay(int delay, final Runnable runnable) { if (delay <= 0) { throw new IllegalArgumentException("delay should be >= 0"); } final CountDownLatch latch = new CountDownLatch(1); mAsyncHandler.postDelayed(new Runnable() { @Override public void run() { runnable.run(); latch.countDown(); } }, delay); waitForLatch(latch, delay); return new SynchronousCallBuilder(mAsyncHandler); } public SynchronousCallBuilder delay(int delay, final SynchronousBlock block) { if (delay <= 0) { throw new IllegalArgumentException("delay should be >= 0"); } final CountDownLatch latch = new CountDownLatch(mLatchCount); mAsyncHandler.postDelayed(new Runnable() { @Override public void run() { block.run(latch); } }, delay); waitForLatch(latch, delay); return new SynchronousCallBuilder(mAsyncHandler); } private void waitForLatch(CountDownLatch latch, int extraMilliseconds) { try { if (!latch.await(extraMilliseconds + mTimeout * 1000, TimeUnit.MILLISECONDS)) { throw new RuntimeException("synchronous block timed out"); } } catch (InterruptedException e) { throw new RuntimeException(e); } } } public static void sleep(int milliseconds) { try { Thread.sleep(milliseconds); } catch (InterruptedException e) { throw new RuntimeException(e); } } public static SynchronousCallBuilder synchronous() { return new SynchronousCallBuilder(null); } public interface SynchronousBlock { void run(CountDownLatch latch); } public static void waitForNUpdates(final TextureView textureView, int count) { TextureView.SurfaceTextureListener previousListener = textureView.getSurfaceTextureListener(); final TextureViewAsserter textureViewAsserter = new TextureViewAsserter(previousListener); textureView.setSurfaceTextureListener(textureViewAsserter); TestUtils.synchronous().latchCount(count).timeout(15).run(new TestUtils.SynchronousBlock() { @Override public void run(final CountDownLatch latch) { textureViewAsserter.waitForUpdates(latch); } }); textureView.setSurfaceTextureListener(previousListener); } private static class TextureViewAsserter implements TextureView.SurfaceTextureListener { private final TextureView.SurfaceTextureListener mListener; private CountDownLatch mLatch; public TextureViewAsserter(final TextureView.SurfaceTextureListener surfaceTextureListener) { mListener = surfaceTextureListener; } public void waitForUpdates(CountDownLatch latch) { mLatch = latch; } @Override public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width, final int height) { mListener.onSurfaceTextureAvailable(surface, width, height); } @Override public void onSurfaceTextureSizeChanged(final SurfaceTexture surface, final int width, final int height) { mListener.onSurfaceTextureSizeChanged(surface, width, height); } @Override public boolean onSurfaceTextureDestroyed(final SurfaceTexture surface) { return mListener.onSurfaceTextureDestroyed(surface); } @Override public void onSurfaceTextureUpdated(final SurfaceTexture surface) { if (mLatch != null) { mLatch.countDown(); } mListener.onSurfaceTextureUpdated(surface); } } }