Move Robolectric-related test utils methods to robolectricutils module

This moves TestUtil#runMainLooperUntil and
TestUtil#createRobolectricConditionVariable to a new RobolectricUtil
class.

Also move testutil classes that use Robolectric-related utils classes
(e.g. TestPlayerRunHelper, TestDownloadManagerListener).

PiperOrigin-RevId: 336864959
This commit is contained in:
ibaker 2020-10-13 14:36:12 +01:00 committed by kim-vde
parent 9e1c6321ee
commit d700627ec2
14 changed files with 137 additions and 144 deletions

View file

@ -15,14 +15,14 @@
*/
package com.google.android.exoplayer2;
import static com.google.android.exoplayer2.robolectric.RobolectricUtil.runMainLooperUntil;
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.playUntilStartOfWindow;
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilPlaybackState;
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilReceiveOffloadSchedulingEnabledNewState;
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilSleepingForOffload;
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilTimelineChanged;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
import static com.google.android.exoplayer2.testutil.TestPlayerRunHelper.playUntilStartOfWindow;
import static com.google.android.exoplayer2.testutil.TestPlayerRunHelper.runUntilPlaybackState;
import static com.google.android.exoplayer2.testutil.TestPlayerRunHelper.runUntilReceiveOffloadSchedulingEnabledNewState;
import static com.google.android.exoplayer2.testutil.TestPlayerRunHelper.runUntilSleepingForOffload;
import static com.google.android.exoplayer2.testutil.TestPlayerRunHelper.runUntilTimelineChanged;
import static com.google.android.exoplayer2.testutil.TestUtil.runMainLooperUntil;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThrows;
@ -58,6 +58,7 @@ import com.google.android.exoplayer2.drm.DrmSessionEventListener;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
import com.google.android.exoplayer2.robolectric.TestPlayerRunHelper;
import com.google.android.exoplayer2.source.ClippingMediaSource;
import com.google.android.exoplayer2.source.CompositeMediaSource;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
@ -97,7 +98,6 @@ import com.google.android.exoplayer2.testutil.FakeTrackSelection;
import com.google.android.exoplayer2.testutil.FakeTrackSelector;
import com.google.android.exoplayer2.testutil.NoUidTimeline;
import com.google.android.exoplayer2.testutil.TestExoPlayerBuilder;
import com.google.android.exoplayer2.testutil.TestPlayerRunHelper;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;

View file

@ -27,8 +27,8 @@ import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.robolectric.RandomizedMp3Decoder;
import com.google.android.exoplayer2.robolectric.TestPlayerRunHelper;
import com.google.android.exoplayer2.testutil.AutoAdvancingFakeClock;
import com.google.android.exoplayer2.testutil.TestPlayerRunHelper;
import com.google.android.exoplayer2.util.Assertions;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Bytes;

View file

@ -25,9 +25,9 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.robolectric.PlaybackOutput;
import com.google.android.exoplayer2.robolectric.ShadowMediaCodecConfig;
import com.google.android.exoplayer2.robolectric.TestPlayerRunHelper;
import com.google.android.exoplayer2.testutil.AutoAdvancingFakeClock;
import com.google.android.exoplayer2.testutil.DumpFileAsserts;
import com.google.android.exoplayer2.testutil.TestPlayerRunHelper;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

View file

@ -23,9 +23,9 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.robolectric.PlaybackOutput;
import com.google.android.exoplayer2.robolectric.ShadowMediaCodecConfig;
import com.google.android.exoplayer2.robolectric.TestPlayerRunHelper;
import com.google.android.exoplayer2.testutil.AutoAdvancingFakeClock;
import com.google.android.exoplayer2.testutil.DumpFileAsserts;
import com.google.android.exoplayer2.testutil.TestPlayerRunHelper;
import com.google.common.collect.ImmutableList;
import org.junit.Rule;
import org.junit.Test;

View file

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.offline;
import static com.google.android.exoplayer2.robolectric.RobolectricUtil.createRobolectricConditionVariable;
import static com.google.common.truth.Truth.assertThat;
import static java.util.Arrays.asList;
@ -24,11 +25,11 @@ import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.robolectric.TestDownloadManagerListener;
import com.google.android.exoplayer2.scheduler.Requirements;
import com.google.android.exoplayer2.testutil.DownloadBuilder;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.DummyMainThread.TestRunnable;
import com.google.android.exoplayer2.testutil.TestDownloadManagerListener;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.ConditionVariable;
@ -834,10 +835,10 @@ public class DownloadManagerTest {
private FakeDownloader(DownloadRequest request) {
this.request = request;
downloadStarted = TestUtil.createRobolectricConditionVariable();
removeStarted = TestUtil.createRobolectricConditionVariable();
finished = TestUtil.createRobolectricConditionVariable();
blocker = TestUtil.createRobolectricConditionVariable();
downloadStarted = createRobolectricConditionVariable();
removeStarted = createRobolectricConditionVariable();
finished = createRobolectricConditionVariable();
blocker = createRobolectricConditionVariable();
startCount = new AtomicInteger();
bytesDownloaded = new AtomicInteger();
}

View file

@ -15,7 +15,7 @@
*/
package com.google.android.exoplayer2.source;
import static com.google.android.exoplayer2.testutil.TestUtil.runMainLooperUntil;
import static com.google.android.exoplayer2.robolectric.RobolectricUtil.runMainLooperUntil;
import static com.google.common.truth.Truth.assertThat;
import android.net.Uri;

View file

@ -26,9 +26,9 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.robolectric.PlaybackOutput;
import com.google.android.exoplayer2.robolectric.ShadowMediaCodecConfig;
import com.google.android.exoplayer2.robolectric.TestPlayerRunHelper;
import com.google.android.exoplayer2.testutil.AutoAdvancingFakeClock;
import com.google.android.exoplayer2.testutil.DumpFileAsserts;
import com.google.android.exoplayer2.testutil.TestPlayerRunHelper;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import org.junit.Rule;
import org.junit.Test;

View file

@ -31,13 +31,13 @@ import com.google.android.exoplayer2.offline.DefaultDownloaderFactory;
import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadRequest;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.robolectric.TestDownloadManagerListener;
import com.google.android.exoplayer2.scheduler.Requirements;
import com.google.android.exoplayer2.testutil.CacheAsserts.RequestSet;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.DummyMainThread.TestRunnable;
import com.google.android.exoplayer2.testutil.FakeDataSet;
import com.google.android.exoplayer2.testutil.FakeDataSource;
import com.google.android.exoplayer2.testutil.TestDownloadManagerListener;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.upstream.DataSource.Factory;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;

View file

@ -34,11 +34,11 @@ import com.google.android.exoplayer2.offline.DownloadManager;
import com.google.android.exoplayer2.offline.DownloadRequest;
import com.google.android.exoplayer2.offline.DownloadService;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.robolectric.TestDownloadManagerListener;
import com.google.android.exoplayer2.scheduler.Scheduler;
import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.FakeDataSet;
import com.google.android.exoplayer2.testutil.FakeDataSource;
import com.google.android.exoplayer2.testutil.TestDownloadManagerListener;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;

View file

@ -0,0 +1,90 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.robolectric;
import android.os.Looper;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.ConditionVariable;
import com.google.android.exoplayer2.util.SystemClock;
import com.google.common.base.Supplier;
import java.util.concurrent.TimeoutException;
import org.robolectric.shadows.ShadowLooper;
/** Utility methods for Robolectric-based tests. */
public final class RobolectricUtil {
private RobolectricUtil() {}
/**
* The default timeout applied when calling {@link #runMainLooperUntil(Supplier)}. This timeout
* should be sufficient for any condition using a Robolectric test.
*/
public static final long DEFAULT_TIMEOUT_MS = 10_000;
/**
* Creates a {@link ConditionVariable} whose {@link ConditionVariable#block(long)} method times
* out according to wallclock time when used in Robolectric tests.
*/
public static ConditionVariable createRobolectricConditionVariable() {
return new ConditionVariable(
new SystemClock() {
@Override
public long elapsedRealtime() {
// elapsedRealtime() does not advance during Robolectric test execution, so use
// currentTimeMillis() instead. This is technically unsafe because this clock is not
// guaranteed to be monotonic, but in practice it will work provided the clock of the
// host machine does not change during test execution.
return Clock.DEFAULT.currentTimeMillis();
}
});
}
/**
* Runs tasks of the main Robolectric {@link Looper} until the {@code condition} returns {@code
* true}.
*
* <p>Must be called on the main test thread.
*
* @param condition The condition.
* @throws TimeoutException If the {@link #DEFAULT_TIMEOUT_MS} is exceeded.
*/
public static void runMainLooperUntil(Supplier<Boolean> condition) throws TimeoutException {
runMainLooperUntil(condition, DEFAULT_TIMEOUT_MS, Clock.DEFAULT);
}
/**
* Runs tasks of the main Robolectric {@link Looper} until the {@code condition} returns {@code
* true}.
*
* @param condition The condition.
* @param timeoutMs The timeout in milliseconds.
* @param clock The {@link Clock} to measure the timeout.
* @throws TimeoutException If the {@code timeoutMs timeout} is exceeded.
*/
public static void runMainLooperUntil(Supplier<Boolean> condition, long timeoutMs, Clock clock)
throws TimeoutException {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException();
}
long timeoutTimeMs = clock.currentTimeMillis() + timeoutMs;
while (!condition.get()) {
if (clock.currentTimeMillis() >= timeoutTimeMs) {
throw new TimeoutException();
}
ShadowLooper.runMainLooperOneTask();
}
}
}

View file

@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.testutil;
package com.google.android.exoplayer2.robolectric;
import static com.google.android.exoplayer2.robolectric.RobolectricUtil.createRobolectricConditionVariable;
import static com.google.common.truth.Truth.assertThat;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.junit.Assert.fail;
@ -46,8 +47,8 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen
public TestDownloadManagerListener(DownloadManager downloadManager) {
this.downloadManager = downloadManager;
downloadStates = new HashMap<>();
initializedCondition = TestUtil.createRobolectricConditionVariable();
idleCondition = TestUtil.createRobolectricConditionVariable();
initializedCondition = createRobolectricConditionVariable();
idleCondition = createRobolectricConditionVariable();
downloadManager.addListener(this);
}
@ -61,7 +62,7 @@ public final class TestDownloadManagerListener implements DownloadManager.Listen
idleCondition.close();
// If the manager is already idle the condition will be opened by the code immediately below.
// Else it will be opened by onIdle().
ConditionVariable checkedOnMainThread = TestUtil.createRobolectricConditionVariable();
ConditionVariable checkedOnMainThread = createRobolectricConditionVariable();
new Handler(downloadManager.getApplicationLooper())
.post(
() -> {

View file

@ -14,9 +14,9 @@
* limitations under the License.
*/
package com.google.android.exoplayer2.testutil;
package com.google.android.exoplayer2.robolectric;
import static com.google.android.exoplayer2.testutil.TestUtil.runMainLooperUntil;
import static com.google.android.exoplayer2.robolectric.RobolectricUtil.runMainLooperUntil;
import android.os.Handler;
import android.os.Looper;
@ -48,7 +48,7 @@ public class TestPlayerRunHelper {
*
* @param player The {@link Player}.
* @param expectedState The expected {@link Player.State}.
* @throws TimeoutException If the {@link TestUtil#DEFAULT_TIMEOUT_MS default timeout} is
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
* exceeded.
*/
public static void runUntilPlaybackState(Player player, @Player.State int expectedState)
@ -78,7 +78,7 @@ public class TestPlayerRunHelper {
*
* @param player The {@link Player}.
* @param expectedPlayWhenReady The expected value for {@link Player#getPlayWhenReady()}.
* @throws TimeoutException If the {@link TestUtil#DEFAULT_TIMEOUT_MS default timeout} is
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
* exceeded.
*/
public static void runUntilPlayWhenReady(Player player, boolean expectedPlayWhenReady)
@ -108,7 +108,7 @@ public class TestPlayerRunHelper {
*
* @param player The {@link Player}.
* @param expectedTimeline The expected {@link Timeline}.
* @throws TimeoutException If the {@link TestUtil#DEFAULT_TIMEOUT_MS default timeout} is
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
* exceeded.
*/
public static void runUntilTimelineChanged(Player player, Timeline expectedTimeline)
@ -137,7 +137,7 @@ public class TestPlayerRunHelper {
*
* @param player The {@link Player}.
* @return The new {@link Timeline}.
* @throws TimeoutException If the {@link TestUtil#DEFAULT_TIMEOUT_MS default timeout} is
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
* exceeded.
*/
public static Timeline runUntilTimelineChanged(Player player) throws TimeoutException {
@ -163,7 +163,7 @@ public class TestPlayerRunHelper {
*
* @param player The {@link Player}.
* @param expectedReason The expected {@link Player.DiscontinuityReason}.
* @throws TimeoutException If the {@link TestUtil#DEFAULT_TIMEOUT_MS default timeout} is
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
* exceeded.
*/
public static void runUntilPositionDiscontinuity(
@ -189,7 +189,7 @@ public class TestPlayerRunHelper {
*
* @param player The {@link Player}.
* @return The raised {@link ExoPlaybackException}.
* @throws TimeoutException If the {@link TestUtil#DEFAULT_TIMEOUT_MS default timeout} is
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
* exceeded.
*/
public static ExoPlaybackException runUntilError(Player player) throws TimeoutException {
@ -214,7 +214,7 @@ public class TestPlayerRunHelper {
*
* @param player The {@link Player}.
* @return The new offloadSchedulingEnabled state.
* @throws TimeoutException If the {@link TestUtil#DEFAULT_TIMEOUT_MS default timeout} is
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
* exceeded.
*/
public static boolean runUntilReceiveOffloadSchedulingEnabledNewState(Player player)
@ -241,7 +241,7 @@ public class TestPlayerRunHelper {
*
* @param player The {@link Player}.
* @param expectedSleepForOffload The expected sleep of offload state.
* @throws TimeoutException If the {@link TestUtil#DEFAULT_TIMEOUT_MS default timeout} is
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
* exceeded.
*/
public static void runUntilSleepingForOffload(Player player, boolean expectedSleepForOffload)
@ -266,7 +266,7 @@ public class TestPlayerRunHelper {
* callback has been called.
*
* @param player The {@link Player}.
* @throws TimeoutException If the {@link TestUtil#DEFAULT_TIMEOUT_MS default timeout} is
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
* exceeded.
*/
public static void runUntilRenderedFirstFrame(SimpleExoPlayer player) throws TimeoutException {
@ -291,7 +291,7 @@ public class TestPlayerRunHelper {
* @param player The {@link Player}.
* @param windowIndex The window.
* @param positionMs The position within the window, in milliseconds.
* @throws TimeoutException If the {@link TestUtil#DEFAULT_TIMEOUT_MS default timeout} is
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
* exceeded.
*/
public static void playUntilPosition(ExoPlayer player, int windowIndex, long positionMs)
@ -329,7 +329,7 @@ public class TestPlayerRunHelper {
*
* @param player The {@link Player}.
* @param windowIndex The window.
* @throws TimeoutException If the {@link TestUtil#DEFAULT_TIMEOUT_MS default timeout} is
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
* exceeded.
*/
public static void playUntilStartOfWindow(ExoPlayer player, int windowIndex)
@ -342,7 +342,7 @@ public class TestPlayerRunHelper {
* commands on the internal playback thread.
*
* @param player The {@link Player}.
* @throws TimeoutException If the {@link TestUtil#DEFAULT_TIMEOUT_MS default timeout} is
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
* exceeded.
*/
public static void runUntilPendingCommandsAreFullyHandled(ExoPlayer player)

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.testutil;
package com.google.android.exoplayer2.robolectric;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
@ -31,14 +31,13 @@ import java.util.concurrent.TimeoutException;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Unit test for {@link TestUtil}. */
/** Unit test for {@link RobolectricUtil}. */
@RunWith(AndroidJUnit4.class)
public class TestUtilTest {
public class RobolectricUtilTest {
@Test
public void createRobolectricConditionVariable_blockWithTimeout_timesOut()
throws InterruptedException {
ConditionVariable conditionVariable = TestUtil.createRobolectricConditionVariable();
ConditionVariable conditionVariable = RobolectricUtil.createRobolectricConditionVariable();
assertThat(conditionVariable.block(/* timeoutMs= */ 1)).isFalse();
assertThat(conditionVariable.isOpen()).isFalse();
}
@ -46,7 +45,7 @@ public class TestUtilTest {
@Test
public void createRobolectricConditionVariable_blockWithTimeout_blocksForAtLeastTimeout()
throws InterruptedException {
ConditionVariable conditionVariable = TestUtil.createRobolectricConditionVariable();
ConditionVariable conditionVariable = RobolectricUtil.createRobolectricConditionVariable();
long startTimeMs = System.currentTimeMillis();
assertThat(conditionVariable.block(/* timeoutMs= */ 500)).isFalse();
long endTimeMs = System.currentTimeMillis();
@ -57,7 +56,7 @@ public class TestUtilTest {
public void runMainLooperUntil_withConditionAlreadyTrue_returnsImmediately() throws Exception {
Clock mockClock = mock(Clock.class);
TestUtil.runMainLooperUntil(() -> true, /* timeoutMs= */ 0, mockClock);
RobolectricUtil.runMainLooperUntil(() -> true, /* timeoutMs= */ 0, mockClock);
verify(mockClock, atMost(1)).currentTimeMillis();
}
@ -69,7 +68,7 @@ public class TestUtilTest {
assertThrows(
TimeoutException.class,
() -> TestUtil.runMainLooperUntil(() -> false, /* timeoutMs= */ 42, mockClock));
() -> RobolectricUtil.runMainLooperUntil(() -> false, /* timeoutMs= */ 42, mockClock));
verify(mockClock, times(3)).currentTimeMillis();
}
@ -87,7 +86,7 @@ public class TestUtilTest {
.thenReturn(false)
.thenReturn(true);
TestUtil.runMainLooperUntil(mockCondition, /* timeoutMs= */ 5674, mock(Clock.class));
RobolectricUtil.runMainLooperUntil(mockCondition, /* timeoutMs= */ 5674, mock(Clock.class));
verify(mockCondition, times(5)).get();
}

View file

@ -25,7 +25,6 @@ import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.media.MediaCodec;
import android.net.Uri;
import android.os.Looper;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.database.DatabaseProvider;
import com.google.android.exoplayer2.database.DefaultDatabaseProvider;
@ -38,41 +37,21 @@ import com.google.android.exoplayer2.metadata.MetadataInputBuffer;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.ConditionVariable;
import com.google.android.exoplayer2.util.SystemClock;
import com.google.android.exoplayer2.util.Util;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Bytes;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.Random;
import java.util.concurrent.TimeoutException;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
* Utility methods for tests.
*/
public class TestUtil {
/**
* The default timeout applied when calling {@link #runMainLooperUntil(Supplier)}. This timeout
* should be sufficient for any condition using a Robolectric test.
*/
public static final long DEFAULT_TIMEOUT_MS = 10_000;
/** Reflectively loaded Robolectric ShadowLooper#runOneTask. */
private static @MonotonicNonNull Object shadowLooper;
private static @MonotonicNonNull Method runOneTaskMethod;
private TestUtil() {}
/**
@ -457,81 +436,4 @@ public class TestUtil {
return buffer;
}
/**
* Creates a {@link ConditionVariable} whose {@link ConditionVariable#block(long)} method times
* out according to wallclock time when used in Robolectric tests.
*/
public static ConditionVariable createRobolectricConditionVariable() {
return new ConditionVariable(
new SystemClock() {
@Override
public long elapsedRealtime() {
// elapsedRealtime() does not advance during Robolectric test execution, so use
// currentTimeMillis() instead. This is technically unsafe because this clock is not
// guaranteed to be monotonic, but in practice it will work provided the clock of the
// host machine does not change during test execution.
return Clock.DEFAULT.currentTimeMillis();
}
});
}
/**
* Runs tasks of the main Robolectric {@link Looper} until the {@code condition} returns {@code
* true}.
*
* <p>Must be called on the main test thread.
*
* @param condition The condition.
* @throws TimeoutException If the {@link #DEFAULT_TIMEOUT_MS} is exceeded.
*/
public static void runMainLooperUntil(Supplier<Boolean> condition) throws TimeoutException {
runMainLooperUntil(condition, DEFAULT_TIMEOUT_MS, Clock.DEFAULT);
}
/**
* Runs tasks of the main Robolectric {@link Looper} until the {@code condition} returns {@code
* true}.
*
* @param condition The condition.
* @param timeoutMs The timeout in milliseconds.
* @param clock The {@link Clock} to measure the timeout.
* @throws TimeoutException If the {@code timeoutMs timeout} is exceeded.
*/
public static void runMainLooperUntil(Supplier<Boolean> condition, long timeoutMs, Clock clock)
throws TimeoutException {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException();
}
maybeInitShadowLooperAndRunOneTaskMethod();
try {
long timeoutTimeMs = clock.currentTimeMillis() + timeoutMs;
while (!condition.get()) {
if (clock.currentTimeMillis() >= timeoutTimeMs) {
throw new TimeoutException();
}
runOneTaskMethod.invoke(shadowLooper);
}
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
} catch (InvocationTargetException e) {
throw new IllegalStateException(e.getCause());
}
}
@EnsuresNonNull({"shadowLooper", "runOneTaskMethod"})
private static void maybeInitShadowLooperAndRunOneTaskMethod() {
if (shadowLooper != null && runOneTaskMethod != null) {
return;
}
try {
Class<?> clazz = Class.forName("org.robolectric.Shadows");
Method shadowOfMethod =
Assertions.checkNotNull(clazz.getDeclaredMethod("shadowOf", Looper.class));
shadowLooper =
Assertions.checkNotNull(shadowOfMethod.invoke(new Object(), Looper.getMainLooper()));
runOneTaskMethod = shadowLooper.getClass().getDeclaredMethod("runOneTask");
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}