Rename Shadow*Looper classes (PR#4868)
ShadowLooper -> ShadowLegacyLooper ShadowRealisticLooper -> ShadowPausedLooper ShadowBaseLooper -> ShadowLooper And all public methods from ShadowLegacyLooper get pushed up to ShadowLooper Pull Request: https://github.com/robolectric/robolectric/pull/4868 Copybara: OK Also adjust Google3 tests using custom looper shadows where necessary. Convert exoplayer to paused looper to eliminate reliance on custom shadows PiperOrigin-RevId: 243839311
This commit is contained in:
parent
10f3b8db6e
commit
af5131e393
@ -20,8 +20,9 @@ project.ext {
|
||||
compileSdkVersion = 28
|
||||
dexmakerVersion = '2.21.0'
|
||||
mockitoVersion = '2.25.0'
|
||||
robolectricVersion = '4.2'
|
||||
robolectricVersion = '4.3-alpha-2'
|
||||
autoValueVersion = '1.6'
|
||||
autoServiceVersion = '1.0-rc4'
|
||||
checkerframeworkVersion = '2.5.0'
|
||||
androidXTestVersion = '1.1.0'
|
||||
modulePrefix = ':'
|
||||
|
@ -50,7 +50,6 @@ import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||
import com.google.android.exoplayer2.testutil.FakeTrackSelection;
|
||||
import com.google.android.exoplayer2.testutil.FakeTrackSelector;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
@ -68,11 +67,11 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
|
||||
/** Unit test for {@link ExoPlayer}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
public final class ExoPlayerTest {
|
||||
|
||||
/**
|
||||
|
@ -48,7 +48,6 @@ import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner.Builder;
|
||||
import com.google.android.exoplayer2.testutil.FakeMediaSource;
|
||||
import com.google.android.exoplayer2.testutil.FakeRenderer;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.android.exoplayer2.video.VideoRendererEventListener;
|
||||
@ -58,11 +57,12 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
import org.robolectric.annotation.LooperMode.Mode;
|
||||
|
||||
/** Integration test for {@link AnalyticsCollector}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(Mode.PAUSED)
|
||||
public final class AnalyticsCollectorTest {
|
||||
|
||||
private static final int EVENT_PLAYER_STATE_CHANGED = 0;
|
||||
|
@ -24,7 +24,6 @@ import android.util.Pair;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import java.util.HashMap;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@ -32,11 +31,11 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
|
||||
/** Tests {@link OfflineLicenseHelper}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
public class OfflineLicenseHelperTest {
|
||||
|
||||
private OfflineLicenseHelper<?> offlineLicenseHelper;
|
||||
|
@ -16,6 +16,7 @@
|
||||
package com.google.android.exoplayer2.offline;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.robolectric.shadows.ShadowBaseLooper.shadowMainLooper;
|
||||
|
||||
import android.net.Uri;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@ -34,7 +35,6 @@ import com.google.android.exoplayer2.testutil.FakeMediaSource;
|
||||
import com.google.android.exoplayer2.testutil.FakeRenderer;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.ParametersBuilder;
|
||||
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
|
||||
@ -51,12 +51,11 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowLooper;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
|
||||
/** Unit tests for {@link DownloadHelper}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
public class DownloadHelperTest {
|
||||
|
||||
private static final String TEST_DOWNLOAD_TYPE = "downloadType";
|
||||
@ -426,7 +425,7 @@ public class DownloadHelperTest {
|
||||
}
|
||||
});
|
||||
while (!preparedCondition.block(0)) {
|
||||
ShadowLooper.runMainLooperToNextTask();
|
||||
shadowMainLooper().idleFor(shadowMainLooper().getNextScheduledTaskTime());
|
||||
}
|
||||
if (prepareException.get() != null) {
|
||||
throw prepareException.get();
|
||||
|
@ -24,7 +24,6 @@ import com.google.android.exoplayer2.offline.Download.State;
|
||||
import com.google.android.exoplayer2.scheduler.Requirements;
|
||||
import com.google.android.exoplayer2.testutil.DummyMainThread;
|
||||
import com.google.android.exoplayer2.testutil.DummyMainThread.TestRunnable;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.testutil.TestDownloadManagerListener;
|
||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheUtil.CachingCounters;
|
||||
@ -41,12 +40,13 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
import org.robolectric.annotation.LooperMode.Mode;
|
||||
import org.robolectric.shadows.ShadowLog;
|
||||
|
||||
/** Tests {@link DownloadManager}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(Mode.PAUSED)
|
||||
public class DownloadManagerTest {
|
||||
|
||||
/** Used to check if condition becomes true in this time interval. */
|
||||
|
@ -35,7 +35,6 @@ import com.google.android.exoplayer2.testutil.FakeMediaSource;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.TransferListener;
|
||||
@ -43,11 +42,12 @@ import java.io.IOException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
import org.robolectric.annotation.LooperMode.Mode;
|
||||
|
||||
/** Unit tests for {@link ClippingMediaSource}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(Mode.PAUSED)
|
||||
public final class ClippingMediaSourceTest {
|
||||
|
||||
private static final long TEST_PERIOD_DURATION_US = 1000000;
|
||||
|
@ -34,7 +34,6 @@ import com.google.android.exoplayer2.testutil.FakeShuffleOrder;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@ -44,11 +43,11 @@ import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
|
||||
/** Unit tests for {@link ConcatenatingMediaSource}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
public final class ConcatenatingMediaSourceTest {
|
||||
|
||||
private ConcatenatingMediaSource mediaSource;
|
||||
|
@ -23,17 +23,16 @@ import com.google.android.exoplayer2.testutil.FakeMediaSource;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
||||
import java.io.IOException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
|
||||
/** Unit tests for {@link LoopingMediaSource}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
public class LoopingMediaSourceTest {
|
||||
|
||||
private FakeTimeline multiWindowTimeline;
|
||||
|
@ -26,15 +26,14 @@ import com.google.android.exoplayer2.testutil.FakeMediaSource;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||
import com.google.android.exoplayer2.testutil.MediaSourceTestRunner;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import java.io.IOException;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
|
||||
/** Unit tests for {@link MergingMediaSource}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
public class MergingMediaSourceTest {
|
||||
|
||||
@Test
|
||||
|
@ -35,7 +35,6 @@ import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegm
|
||||
import com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement;
|
||||
import com.google.android.exoplayer2.testutil.MediaPeriodAsserts;
|
||||
import com.google.android.exoplayer2.testutil.MediaPeriodAsserts.FilterableManifestMediaPeriodFactory;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
|
||||
import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
|
||||
@ -45,11 +44,11 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
|
||||
/** Unit tests for {@link DashMediaPeriod}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
public final class DashMediaPeriodTest {
|
||||
|
||||
@Test
|
||||
|
@ -36,7 +36,6 @@ import com.google.android.exoplayer2.scheduler.Requirements;
|
||||
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.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.testutil.TestDownloadManagerListener;
|
||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import com.google.android.exoplayer2.upstream.DataSource.Factory;
|
||||
@ -52,12 +51,12 @@ import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
import org.robolectric.shadows.ShadowLog;
|
||||
|
||||
/** Tests {@link DownloadManager}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
public class DownloadManagerDashTest {
|
||||
|
||||
private static final int ASSERT_TRUE_TIMEOUT = 1000;
|
||||
|
@ -40,7 +40,6 @@ 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.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.testutil.TestDownloadManagerListener;
|
||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
@ -58,11 +57,11 @@ import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
|
||||
/** Unit tests for {@link DownloadService}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
public class DownloadServiceDashTest {
|
||||
|
||||
private SimpleCache cache;
|
||||
|
@ -32,7 +32,6 @@ import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker;
|
||||
import com.google.android.exoplayer2.testutil.MediaPeriodAsserts;
|
||||
import com.google.android.exoplayer2.testutil.MediaPeriodAsserts.FilterableManifestMediaPeriodFactory;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
|
||||
@ -43,11 +42,11 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
|
||||
/** Unit test for {@link HlsMediaPeriod}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
public final class HlsMediaPeriodTest {
|
||||
|
||||
@Test
|
||||
|
@ -28,7 +28,6 @@ import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispat
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest;
|
||||
import com.google.android.exoplayer2.testutil.MediaPeriodAsserts;
|
||||
import com.google.android.exoplayer2.testutil.MediaPeriodAsserts.FilterableManifestMediaPeriodFactory;
|
||||
import com.google.android.exoplayer2.testutil.RobolectricUtil;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
|
||||
import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
|
||||
@ -36,11 +35,11 @@ import com.google.android.exoplayer2.upstream.TransferListener;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
|
||||
/** Unit tests for {@link SsMediaPeriod}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
public class SsMediaPeriodTest {
|
||||
|
||||
@Test
|
||||
|
@ -26,11 +26,11 @@ import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.LooperMode;
|
||||
|
||||
/** Unit test for {@link FakeClock}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(shadows = {RobolectricUtil.CustomLooper.class, RobolectricUtil.CustomMessageQueue.class})
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
public final class FakeClockTest {
|
||||
|
||||
private static final long TIMEOUT_MS = 10000;
|
||||
|
@ -42,4 +42,5 @@ dependencies {
|
||||
api project(modulePrefix + 'testutils')
|
||||
implementation project(modulePrefix + 'library-core')
|
||||
implementation 'androidx.annotation:annotation:1.0.1'
|
||||
annotationProcessor 'com.google.auto.service:auto-service:' + autoServiceVersion
|
||||
}
|
||||
|
@ -1,231 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.testutil;
|
||||
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
import static org.robolectric.util.ReflectionHelpers.callInstanceMethod;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.MessageQueue;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
import org.robolectric.shadows.ShadowLooper;
|
||||
import org.robolectric.shadows.ShadowMessageQueue;
|
||||
|
||||
/** Collection of shadow classes used to run tests with Robolectric which require Loopers. */
|
||||
public final class RobolectricUtil {
|
||||
|
||||
private static final AtomicLong sequenceNumberGenerator = new AtomicLong(0);
|
||||
private static final int ANY_MESSAGE = Integer.MIN_VALUE;
|
||||
|
||||
private RobolectricUtil() {}
|
||||
|
||||
/**
|
||||
* A custom implementation of Robolectric's ShadowLooper which runs all scheduled messages in the
|
||||
* loop method of the looper. Also ensures to correctly emulate the message order of the real
|
||||
* message loop and to avoid blocking caused by Robolectric's default implementation.
|
||||
*
|
||||
* <p>Only works in conjunction with {@link CustomMessageQueue}. Note that the test's {@code
|
||||
* SystemClock} is not advanced automatically.
|
||||
*/
|
||||
@Implements(Looper.class)
|
||||
public static final class CustomLooper extends ShadowLooper {
|
||||
|
||||
private final PriorityBlockingQueue<PendingMessage> pendingMessages;
|
||||
private final CopyOnWriteArraySet<RemovedMessage> removedMessages;
|
||||
|
||||
public CustomLooper() {
|
||||
pendingMessages = new PriorityBlockingQueue<>();
|
||||
removedMessages = new CopyOnWriteArraySet<>();
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public static void loop() {
|
||||
Looper looper = Looper.myLooper();
|
||||
if (shadowOf(looper) instanceof CustomLooper) {
|
||||
((CustomLooper) shadowOf(looper)).doLoop();
|
||||
}
|
||||
}
|
||||
|
||||
@Implementation
|
||||
@Override
|
||||
public void quitUnchecked() {
|
||||
super.quitUnchecked();
|
||||
// Insert message at the front of the queue to quit loop as soon as possible.
|
||||
addPendingMessage(/* message= */ null, /* when= */ Long.MIN_VALUE);
|
||||
}
|
||||
|
||||
private void addPendingMessage(@Nullable Message message, long when) {
|
||||
pendingMessages.put(new PendingMessage(message, when));
|
||||
}
|
||||
|
||||
private void removeMessages(Handler handler, int what, Object object) {
|
||||
RemovedMessage newRemovedMessage = new RemovedMessage(handler, what, object);
|
||||
removedMessages.add(newRemovedMessage);
|
||||
for (RemovedMessage removedMessage : removedMessages) {
|
||||
if (removedMessage != newRemovedMessage
|
||||
&& removedMessage.handler == handler
|
||||
&& removedMessage.what == what
|
||||
&& removedMessage.object == object) {
|
||||
removedMessages.remove(removedMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doLoop() {
|
||||
boolean wasInterrupted = false;
|
||||
while (true) {
|
||||
try {
|
||||
PendingMessage pendingMessage = pendingMessages.take();
|
||||
if (pendingMessage.message == null) {
|
||||
// Null message is signal to end message loop.
|
||||
return;
|
||||
}
|
||||
// Call through to real {@code Message.markInUse()} and {@code Message.recycle()} to
|
||||
// ensure message recycling works. This is also done in Robolectric's own implementation
|
||||
// of the message queue.
|
||||
callInstanceMethod(pendingMessage.message, "markInUse");
|
||||
Handler target = pendingMessage.message.getTarget();
|
||||
if (target != null) {
|
||||
boolean isRemoved = false;
|
||||
for (RemovedMessage removedMessage : removedMessages) {
|
||||
if (removedMessage.handler == target
|
||||
&& (removedMessage.what == ANY_MESSAGE
|
||||
|| removedMessage.what == pendingMessage.message.what)
|
||||
&& (removedMessage.object == null
|
||||
|| removedMessage.object == pendingMessage.message.obj)
|
||||
&& pendingMessage.sequenceNumber < removedMessage.sequenceNumber) {
|
||||
isRemoved = true;
|
||||
}
|
||||
}
|
||||
if (!isRemoved) {
|
||||
try {
|
||||
if (wasInterrupted) {
|
||||
wasInterrupted = false;
|
||||
// Restore the interrupt status flag, so long-running messages will exit early.
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
target.dispatchMessage(pendingMessage.message);
|
||||
} catch (Throwable t) {
|
||||
// Interrupt the main thread to terminate the test. Robolectric's HandlerThread will
|
||||
// print the rethrown error to standard output.
|
||||
Looper.getMainLooper().getThread().interrupt();
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Util.SDK_INT >= 21) {
|
||||
callInstanceMethod(pendingMessage.message, "recycleUnchecked");
|
||||
} else {
|
||||
callInstanceMethod(pendingMessage.message, "recycle");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
wasInterrupted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom implementation of Robolectric's ShadowMessageQueue which is needed to let {@link
|
||||
* CustomLooper} work as intended.
|
||||
*/
|
||||
@Implements(MessageQueue.class)
|
||||
public static final class CustomMessageQueue extends ShadowMessageQueue {
|
||||
|
||||
private final Thread looperThread;
|
||||
|
||||
public CustomMessageQueue() {
|
||||
looperThread = Thread.currentThread();
|
||||
}
|
||||
|
||||
@Implementation
|
||||
@Override
|
||||
public boolean enqueueMessage(Message msg, long when) {
|
||||
Looper looper = ShadowLooper.getLooperForThread(looperThread);
|
||||
if (shadowOf(looper) instanceof CustomLooper
|
||||
&& shadowOf(looper) != shadowOf(Looper.getMainLooper())) {
|
||||
((CustomLooper) shadowOf(looper)).addPendingMessage(msg, when);
|
||||
} else {
|
||||
super.enqueueMessage(msg, when);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public void removeMessages(Handler handler, int what, Object object) {
|
||||
Looper looper = ShadowLooper.getLooperForThread(looperThread);
|
||||
if (shadowOf(looper) instanceof CustomLooper
|
||||
&& shadowOf(looper) != shadowOf(Looper.getMainLooper())) {
|
||||
((CustomLooper) shadowOf(looper)).removeMessages(handler, what, object);
|
||||
}
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public void removeCallbacksAndMessages(Handler handler, Object object) {
|
||||
Looper looper = ShadowLooper.getLooperForThread(looperThread);
|
||||
if (shadowOf(looper) instanceof CustomLooper
|
||||
&& shadowOf(looper) != shadowOf(Looper.getMainLooper())) {
|
||||
((CustomLooper) shadowOf(looper)).removeMessages(handler, ANY_MESSAGE, object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PendingMessage implements Comparable<PendingMessage> {
|
||||
|
||||
public final @Nullable Message message;
|
||||
public final long when;
|
||||
public final long sequenceNumber;
|
||||
|
||||
public PendingMessage(@Nullable Message message, long when) {
|
||||
this.message = message;
|
||||
this.when = when;
|
||||
sequenceNumber = sequenceNumberGenerator.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NonNull PendingMessage other) {
|
||||
int res = Util.compareLong(this.when, other.when);
|
||||
if (res == 0 && this != other) {
|
||||
res = Util.compareLong(this.sequenceNumber, other.sequenceNumber);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RemovedMessage {
|
||||
|
||||
public final Handler handler;
|
||||
public final int what;
|
||||
public final Object object;
|
||||
public final long sequenceNumber;
|
||||
|
||||
public RemovedMessage(Handler handler, int what, Object object) {
|
||||
this.handler = handler;
|
||||
this.what = what;
|
||||
this.object = object;
|
||||
this.sequenceNumber = sequenceNumberGenerator.get();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user