Rename Shadow*SystemClock classes.
ShadowBaseSystemClock -> ShadowSystemClock
ShadowRealisticSystemClock -> ShadowPausedSystemClock
ShadowSystemClock -> ShadowLegacySystemClock
ShadowSystemClock now should (only) expose the public API.
Shadows explicitly enabled using @Config(shadows=...) will now have their shadowPicker honored.
PiperOrigin-RevId: 242194000
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowDateUtilsTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowDateUtilsTest.java
index 6f1ee65..50d8751 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowDateUtilsTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowDateUtilsTest.java
@@ -8,6 +8,7 @@
import static com.google.common.truth.TruthJUnit.assume;
import android.app.Application;
+import android.os.SystemClock;
import android.text.format.DateUtils;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -77,7 +78,7 @@
public void isToday_shouldReturnFalseForNotToday() {
assume().that(ShadowBaseLooper.useRealisticLooper()).isFalse();
long today = java.util.Calendar.getInstance().getTimeInMillis();
- ShadowSystemClock.setCurrentTimeMillis(today);
+ SystemClock.setCurrentTimeMillis(today);
assertThat(DateUtils.isToday(today)).isTrue();
assertThat(DateUtils.isToday(today + (86400 * 1000) /* 24 hours */)).isFalse();
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowSystemClockTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowLegacySystemClockTest.java
similarity index 88%
rename from robolectric/src/test/java/org/robolectric/shadows/ShadowSystemClockTest.java
rename to robolectric/src/test/java/org/robolectric/shadows/ShadowLegacySystemClockTest.java
index 3d6ec9e..acea84f 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowSystemClockTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowLegacySystemClockTest.java
@@ -3,28 +3,24 @@
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static android.os.Build.VERSION_CODES.P;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.robolectric.annotation.LooperMode.Mode.LEGACY;
import android.os.SystemClock;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.time.DateTimeException;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
+import org.robolectric.annotation.LooperMode;
import org.robolectric.internal.bytecode.RobolectricInternals;
@RunWith(AndroidJUnit4.class)
-public class ShadowSystemClockTest {
-
- @Before
- public void setUp() {
- assume().that(ShadowRealisticLooper.useRealisticLooper()).isFalse();
- }
+@LooperMode(LEGACY)
+public class ShadowLegacySystemClockTest {
@Test
public void shouldAllowForFakingOfTime() throws Exception {
@@ -43,11 +39,11 @@
@Test
public void testSetCurrentTime() {
Robolectric.getForegroundThreadScheduler().advanceTo(1000);
- assertThat(ShadowSystemClock.now()).isEqualTo(1000);
+ assertThat(ShadowLegacySystemClock.now()).isEqualTo(1000);
assertTrue(SystemClock.setCurrentTimeMillis(1034));
- assertThat(ShadowSystemClock.now()).isEqualTo(1034);
+ assertThat(ShadowLegacySystemClock.now()).isEqualTo(1034);
assertFalse(SystemClock.setCurrentTimeMillis(1000));
- assertThat(ShadowSystemClock.now()).isEqualTo(1034);
+ assertThat(ShadowLegacySystemClock.now()).isEqualTo(1034);
}
@Test
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowRealisticSystemClockTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowPausedSystemClockTest.java
similarity index 89%
rename from robolectric/src/test/java/org/robolectric/shadows/ShadowRealisticSystemClockTest.java
rename to robolectric/src/test/java/org/robolectric/shadows/ShadowPausedSystemClockTest.java
index df57eab..f27ae8c 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowRealisticSystemClockTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowPausedSystemClockTest.java
@@ -3,29 +3,24 @@
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static android.os.Build.VERSION_CODES.P;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.robolectric.annotation.LooperMode.Mode.PAUSED;
import android.os.SystemClock;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.time.DateTimeException;
import java.util.concurrent.TimeUnit;
-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;
import org.robolectric.internal.bytecode.RobolectricInternals;
@RunWith(AndroidJUnit4.class)
-public class ShadowRealisticSystemClockTest {
-
- @Before
- public void assertSimplifiedLooper() {
- assume().that(ShadowBaseLooper.useRealisticLooper()).isTrue();
- }
+@LooperMode(PAUSED)
+public class ShadowPausedSystemClockTest {
@Test
public void sleep() {
@@ -83,7 +78,7 @@
@Test
@Config(minSdk = P)
public void currentNetworkTimeMillis_networkTimeNotAvailable_shouldThrowDateTimeException() {
- ShadowRealisticSystemClock.setNetworkTimeAvailable(false);
+ ShadowSystemClock.setNetworkTimeAvailable(false);
try {
SystemClock.currentNetworkTimeMillis();
fail("Trying to get currentNetworkTimeMillis without network time should throw");
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowRealisticMessageQueueTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowRealisticMessageQueueTest.java
index 0fd0750..6c14899 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowRealisticMessageQueueTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowRealisticMessageQueueTest.java
@@ -9,6 +9,7 @@
import android.os.Message;
import android.os.MessageQueue;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import java.time.Duration;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.After;
@@ -75,7 +76,7 @@
msg.setTarget(new Handler());
shadowQueue.doEnqueueMessage(msg, TimeUnit.MINUTES.toMillis(10));
NextThread t = NextThread.startSync(shadowQueue);
- ShadowRealisticSystemClock.advanceBy(10, TimeUnit.MINUTES);
+ ShadowSystemClock.advanceBy(Duration.ofMinutes(10));
t.join();
}
diff --git a/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowMap.java b/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowMap.java
index 840a800..f62b74a 100644
--- a/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowMap.java
+++ b/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowMap.java
@@ -62,6 +62,8 @@
ShadowInfo shadowInfo = overriddenShadows.get(instrumentedClassName);
if (shadowInfo == null) {
shadowInfo = checkShadowPickers(instrumentedClassName, clazz);
+ } else if (shadowInfo.hasShadowPicker()) {
+ shadowInfo = pickShadow(instrumentedClassName, clazz, shadowInfo);
}
if (shadowInfo == null && clazz.getClassLoader() != null) {
@@ -94,10 +96,15 @@
return null;
}
- ClassLoader classLoader = clazz.getClassLoader();
+ return pickShadow(instrumentedClassName, clazz, shadowPickerClassName);
+ }
+
+ private ShadowInfo pickShadow(String instrumentedClassName, Class<?> clazz,
+ String shadowPickerClassName) {
+ ClassLoader sandboxClassLoader = clazz.getClassLoader();
try {
Class<? extends ShadowPicker<?>> shadowPickerClass =
- (Class<? extends ShadowPicker<?>>) classLoader.loadClass(shadowPickerClassName);
+ (Class<? extends ShadowPicker<?>>) sandboxClassLoader.loadClass(shadowPickerClassName);
ShadowPicker<?> shadowPicker = shadowPickerClass.getDeclaredConstructor().newInstance();
Class<?> selectedShadowClass = shadowPicker.pickShadowClass();
if (selectedShadowClass == null) {
@@ -119,6 +126,11 @@
}
}
+ private ShadowInfo pickShadow(
+ String instrumentedClassName, Class<?> clazz, ShadowInfo shadowInfo) {
+ return pickShadow(instrumentedClassName, clazz, shadowInfo.getShadowPickerClass().getName());
+ }
+
public static ShadowInfo obtainShadowInfo(Class<?> clazz) {
return obtainShadowInfo(clazz, false);
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBaseSystemClock.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBaseSystemClock.java
deleted file mode 100644
index 481d166..0000000
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBaseSystemClock.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.robolectric.shadows;
-
-abstract class ShadowBaseSystemClock {
-
- public static class Picker extends LooperShadowPicker<ShadowBaseSystemClock> {
-
- public Picker() {
- super(ShadowSystemClock.class, ShadowRealisticSystemClock.class);
- }
- }
-}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayEventReceiver.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayEventReceiver.java
index e4d8b3c..6c0d8b1 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayEventReceiver.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayEventReceiver.java
@@ -7,13 +7,12 @@
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.N_MR1;
import static android.os.Build.VERSION_CODES.O;
-import static android.os.Build.VERSION_CODES.P;
import static android.os.Build.VERSION_CODES.Q;
import android.os.MessageQueue;
import android.view.DisplayEventReceiver;
import java.lang.ref.WeakReference;
-import java.util.concurrent.TimeUnit;
+import java.time.Duration;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@@ -34,7 +33,7 @@
protected @RealObject DisplayEventReceiver receiver;
- private static final long VSYNC_DELAY_MS = 1;
+ private static final Duration VSYNC_DELAY = Duration.ofMillis(1);
@Implementation(minSdk = O)
protected static long nativeInit(
@@ -114,7 +113,7 @@
public void scheduleVsync() {
// simulate an immediate callback
DisplayEventReceiver receiver = receiverRef.get();
- ShadowRealisticSystemClock.advanceBy(VSYNC_DELAY_MS, TimeUnit.MILLISECONDS);
+ ShadowSystemClock.advanceBy(VSYNC_DELAY);
if (receiver != null) {
ShadowDisplayEventReceiver shadowReceiver = Shadow.extract(receiver);
shadowReceiver.onVsync();
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLegacySystemClock.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLegacySystemClock.java
new file mode 100644
index 0000000..5c3c76e
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLegacySystemClock.java
@@ -0,0 +1,131 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+import static android.os.Build.VERSION_CODES.P;
+
+import android.os.SystemClock;
+import java.time.DateTimeException;
+import org.robolectric.annotation.HiddenApi;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+/**
+ * A shadow SystemClock for {@link LooperMode.Mode.LEGACY}
+ *
+ * <p>In LEGACY LooperMode, Robolectric's concept of current time is base on the current time of the
+ * UI Scheduler for consistency with previous implementations. This is not ideal, since both
+ * schedulers (background and foreground), can see different values for the current time.
+ */
+@Implements(
+ value = SystemClock.class,
+ shadowPicker = ShadowSystemClock.Picker.class,
+ // turn off shadowOf generation
+ isInAndroidSdk = false)
+public class ShadowLegacySystemClock extends ShadowSystemClock {
+ private static long bootedAt = 0;
+ private static long nanoTime = 0;
+ private static final int MILLIS_PER_NANO = 1000000;
+
+ static long now() {
+ if (ShadowApplication.getInstance() == null) {
+ return 0;
+ }
+ return ShadowApplication.getInstance().getForegroundThreadScheduler().getCurrentTime();
+ }
+
+ @Implementation
+ protected static void sleep(long millis) {
+ if (ShadowApplication.getInstance() == null) {
+ return;
+ }
+
+ nanoTime = millis * MILLIS_PER_NANO;
+ ShadowApplication.getInstance().getForegroundThreadScheduler().advanceBy(millis);
+ }
+
+ @Implementation
+ protected static boolean setCurrentTimeMillis(long millis) {
+ if (ShadowApplication.getInstance() == null) {
+ return false;
+ }
+
+ if (now() > millis) {
+ return false;
+ }
+ nanoTime = millis * MILLIS_PER_NANO;
+ ShadowApplication.getInstance().getForegroundThreadScheduler().advanceTo(millis);
+ return true;
+ }
+
+ @Implementation
+ protected static long uptimeMillis() {
+ return now() - bootedAt;
+ }
+
+ @Implementation
+ protected static long elapsedRealtime() {
+ return uptimeMillis();
+ }
+
+ @Implementation(minSdk = JELLY_BEAN_MR1)
+ protected static long elapsedRealtimeNanos() {
+ return elapsedRealtime() * MILLIS_PER_NANO;
+ }
+
+ @Implementation
+ protected static long currentThreadTimeMillis() {
+ return uptimeMillis();
+ }
+
+ @HiddenApi
+ @Implementation
+ public static long currentThreadTimeMicro() {
+ return uptimeMillis() * 1000;
+ }
+
+ @HiddenApi
+ @Implementation
+ public static long currentTimeMicro() {
+ return now() * 1000;
+ }
+
+ /**
+ * Implements {@link System#currentTimeMillis} through ShadowWrangler.
+ *
+ * @return Current time in millis.
+ */
+ @SuppressWarnings("unused")
+ public static long currentTimeMillis() {
+ return nanoTime / MILLIS_PER_NANO;
+ }
+
+ /**
+ * Implements {@link System#nanoTime} through ShadowWrangler.
+ *
+ * @return Current time with nanos.
+ */
+ @SuppressWarnings("unused")
+ public static long nanoTime() {
+ return nanoTime;
+ }
+
+ public static void setNanoTime(long nanoTime) {
+ ShadowLegacySystemClock.nanoTime = nanoTime;
+ }
+
+ @Implementation(minSdk = P)
+ @HiddenApi
+ protected static long currentNetworkTimeMillis() {
+ if (networkTimeAvailable) {
+ return currentTimeMillis();
+ } else {
+ throw new DateTimeException("Network time not available");
+ }
+ }
+
+ @Resetter
+ public static void reset() {
+ ShadowSystemClock.reset();
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLooper.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLooper.java
index 556e58b..d1e8f3b 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLooper.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLooper.java
@@ -16,8 +16,10 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.RealObject;
import org.robolectric.annotation.Resetter;
+import org.robolectric.config.ConfigurationRegistry;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.Scheduler;
@@ -435,4 +437,11 @@
private static ShadowMessageQueue shadowOf(MessageQueue mq) {
return Shadow.extract(mq);
}
+
+ static void assertLooperMode(LooperMode.Mode expectedMode) {
+ LooperMode.Mode looperMode = ConfigurationRegistry.get(LooperMode.Mode.class);
+ if (looperMode != expectedMode) {
+ throw new IllegalStateException("this action is not supported in " + looperMode + " mode.");
+ }
+ }
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowRealisticSystemClock.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPausedSystemClock.java
similarity index 75%
rename from shadows/framework/src/main/java/org/robolectric/shadows/ShadowRealisticSystemClock.java
rename to shadows/framework/src/main/java/org/robolectric/shadows/ShadowPausedSystemClock.java
index 4947261..4c086e6 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowRealisticSystemClock.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPausedSystemClock.java
@@ -4,19 +4,16 @@
import static android.os.Build.VERSION_CODES.P;
import android.os.SystemClock;
-import androidx.test.annotation.Beta;
import java.time.DateTimeException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.TimeUnit;
import org.robolectric.annotation.HiddenApi;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
/**
- * A new version of a shadow SystemClock used when {@link ShadowBaseLooper#useRealisticLooper()} is
- * active.
+ * A shadow SystemClock used when {@link LooperMode.Mode.PAUSED} is active.
*
* <p>In this variant, there is just one global system time controlled by this class. The current
* time is fixed in place, and manually advanced by calling {@link
@@ -25,18 +22,16 @@
* <p>{@link SystemClock#uptimeMillis()} and {@link SystemClock#currentThreadTimeMillis()} are
* identical.
*
- * This is beta API, and will very likely be renamed in a future Robolectric release.
+ * <p>This class should not be referenced directly. Use ShadowSystemClock instead.
*/
@Implements(
value = SystemClock.class,
isInAndroidSdk = false,
- shadowPicker = ShadowBaseSystemClock.Picker.class)
-@Beta
-public class ShadowRealisticSystemClock extends ShadowBaseSystemClock {
+ shadowPicker = ShadowSystemClock.Picker.class)
+public class ShadowPausedSystemClock extends ShadowSystemClock {
private static final long INITIAL_TIME = 100;
private static final int MILLIS_PER_NANO = 1000000;;
private static long currentTimeMillis = INITIAL_TIME;
- private static boolean networkTimeAvailable = true;
private static List<Listener> listeners = new CopyOnWriteArrayList<>();
/**
@@ -102,13 +97,13 @@
@HiddenApi
@Implementation
- public static long currentThreadTimeMicro() {
+ protected static long currentThreadTimeMicro() {
return uptimeMillis() * 1000;
}
@HiddenApi
@Implementation
- public static long currentTimeMicro() {
+ protected static long currentTimeMicro() {
return currentThreadTimeMicro();
}
@@ -122,24 +117,9 @@
}
}
- /** Sets whether network time is available. */
- public static void setNetworkTimeAvailable(boolean available) {
- networkTimeAvailable = available;
- }
-
- /**
- * Convenience method for calling {@link setCurrentTimeMillis()} to a
- *
- */
- public static void advanceBy(long timeValue, TimeUnit timeUnit) {
- setCurrentTimeMillis(currentTimeMillis + timeUnit.toMillis(timeValue));
- }
-
@Resetter
public static void reset() {
currentTimeMillis = INITIAL_TIME;
- networkTimeAvailable = true;
+ ShadowSystemClock.reset();
}
-
-
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowRealisticLooper.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowRealisticLooper.java
index ac5aed7..578a42a 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowRealisticLooper.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowRealisticLooper.java
@@ -78,7 +78,7 @@
@Override
public void idleFor(long time, TimeUnit timeUnit) {
- ShadowRealisticSystemClock.advanceBy(time, timeUnit);
+ ShadowSystemClock.advanceBy(Duration.ofMillis(timeUnit.toMillis(time)));
idle();
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowRealisticMessageQueue.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowRealisticMessageQueue.java
index b38e407..fe495b3 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowRealisticMessageQueue.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowRealisticMessageQueue.java
@@ -49,7 +49,7 @@
private static NativeObjRegistry<ShadowRealisticMessageQueue> nativeQueueRegistry =
new NativeObjRegistry<ShadowRealisticMessageQueue>(ShadowRealisticMessageQueue.class);
private boolean isPolling = false;
- private ShadowRealisticSystemClock.Listener clockListener;
+ private ShadowPausedSystemClock.Listener clockListener;
// shadow constructor instead of nativeInit because nativeInit signature has changed across SDK
// versions
@@ -60,7 +60,7 @@
reflector(ReflectorMessageQueue.class, realQueue).setPtr(ptr);
clockListener =
newCurrentTimeMillis -> nativeWake(ptr);
- ShadowRealisticSystemClock.addListener(clockListener);
+ ShadowPausedSystemClock.addListener(clockListener);
}
@Implementation(maxSdk = JELLY_BEAN_MR1)
@@ -76,7 +76,7 @@
@Implementation(minSdk = KITKAT_WATCH)
protected static void nativeDestroy(long ptr) {
ShadowRealisticMessageQueue q = nativeQueueRegistry.unregister(ptr);
- ShadowRealisticSystemClock.removeListener(q.clockListener);
+ ShadowPausedSystemClock.removeListener(q.clockListener);
}
@Implementation(maxSdk = JELLY_BEAN_MR1)
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSystem.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSystem.java
index 50be796..2ff3d4f 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSystem.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSystem.java
@@ -15,7 +15,7 @@
if (ShadowBaseLooper.useRealisticLooper()) {
return TimeUnit.MILLISECONDS.toNanos(SystemClock.uptimeMillis());
} else {
- return ShadowSystemClock.nanoTime();
+ return ShadowLegacySystemClock.nanoTime();
}
}
@@ -29,7 +29,7 @@
if (ShadowBaseLooper.useRealisticLooper()) {
return SystemClock.uptimeMillis();
} else {
- return ShadowSystemClock.currentTimeMillis();
+ return ShadowLegacySystemClock.currentTimeMillis();
}
}
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSystemClock.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSystemClock.java
index 35534bb..a4153de 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSystemClock.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSystemClock.java
@@ -1,89 +1,23 @@
package org.robolectric.shadows;
-import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
-import static android.os.Build.VERSION_CODES.P;
+import static org.robolectric.shadows.ShadowLooper.assertLooperMode;
import android.os.SystemClock;
-import java.time.DateTimeException;
-import org.robolectric.annotation.HiddenApi;
-import org.robolectric.annotation.Implementation;
+import java.time.Duration;
import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.Resetter;
+import org.robolectric.annotation.LooperMode;
+import org.robolectric.annotation.LooperMode.Mode;
/**
- * Robolectric's concept of current time is base on the current time of the UI Scheduler for
- * consistency with previous implementations. This is not ideal, since both schedulers (background
- * and foreground), can see different values for the current time.
+ * The shadow API for {@link SystemClock}.
+ *
+ * The behavior of SystemClock in Robolectric will differ based on the current {@link
+ * LooperMode}. See {@link ShadowLegacySystemClock} and {@link ShadowPausedSystemClock} for more
+ * details.
*/
-@Implements(value = SystemClock.class, shadowPicker = ShadowBaseSystemClock.Picker.class)
-public class ShadowSystemClock extends ShadowBaseSystemClock {
- private static long bootedAt = 0;
- private static long nanoTime = 0;
- private static final int MILLIS_PER_NANO = 1000000;
- private static boolean networkTimeAvailable = true;
-
- static long now() {
- if (ShadowApplication.getInstance() == null) {
- return 0;
- }
- return ShadowApplication.getInstance().getForegroundThreadScheduler().getCurrentTime();
- }
-
- @Implementation
- protected static void sleep(long millis) {
- if (ShadowApplication.getInstance() == null) {
- return;
- }
-
- nanoTime = millis * MILLIS_PER_NANO;
- ShadowApplication.getInstance().getForegroundThreadScheduler().advanceBy(millis);
- }
-
- @Implementation
- protected static boolean setCurrentTimeMillis(long millis) {
- if (ShadowApplication.getInstance() == null) {
- return false;
- }
-
- if (now() > millis) {
- return false;
- }
- nanoTime = millis * MILLIS_PER_NANO;
- ShadowApplication.getInstance().getForegroundThreadScheduler().advanceTo(millis);
- return true;
- }
-
- @Implementation
- protected static long uptimeMillis() {
- return now() - bootedAt;
- }
-
- @Implementation
- protected static long elapsedRealtime() {
- return uptimeMillis();
- }
-
- @Implementation(minSdk = JELLY_BEAN_MR1)
- protected static long elapsedRealtimeNanos() {
- return elapsedRealtime() * MILLIS_PER_NANO;
- }
-
- @Implementation
- protected static long currentThreadTimeMillis() {
- return uptimeMillis();
- }
-
- @HiddenApi
- @Implementation
- public static long currentThreadTimeMicro() {
- return uptimeMillis() * 1000;
- }
-
- @HiddenApi
- @Implementation
- public static long currentTimeMicro() {
- return now() * 1000;
- }
+@Implements(value = SystemClock.class, shadowPicker = ShadowSystemClock.Picker.class)
+public abstract class ShadowSystemClock {
+ protected static boolean networkTimeAvailable = true;
/**
* Implements {@link System#currentTimeMillis} through ShadowWrangler.
@@ -92,31 +26,31 @@
*/
@SuppressWarnings("unused")
public static long currentTimeMillis() {
- return nanoTime / MILLIS_PER_NANO;
+ return ShadowLegacySystemClock.currentTimeMillis();
}
/**
- * Implements {@link System#nanoTime} through ShadowWrangler.
+ * Implements {@link System#nanoTime}.
*
* @return Current time with nanos.
+ * @deprecated Don't call this method directly; instead, use {@link System#nanoTime()}.
*/
@SuppressWarnings("unused")
+ @Deprecated
public static long nanoTime() {
- return nanoTime;
+ return ShadowSystem.nanoTime();
}
+ /**
+ * Sets the value for {@link System#nanoTime()}.
+ *
+ * May only be used for {@link LooperMode.Mode.LEGACY}. For {@link LooperMode.Mode.PAUSED},
+ * `nanoTime` is calculated based on {@link SystemClock#uptimeMillis()} and can't be set
+ * explicitly.
+ */
public static void setNanoTime(long nanoTime) {
- ShadowSystemClock.nanoTime = nanoTime;
- }
-
- @Implementation(minSdk = P)
- @HiddenApi
- protected static long currentNetworkTimeMillis() {
- if (networkTimeAvailable) {
- return currentTimeMillis();
- } else {
- throw new DateTimeException("Network time not available");
- }
+ assertLooperMode(Mode.LEGACY);
+ ShadowLegacySystemClock.setNanoTime(nanoTime);
}
/** Sets whether network time is available. */
@@ -124,8 +58,23 @@
networkTimeAvailable = available;
}
- @Resetter
+ /**
+ * A convenience method for advancing the clock via {@link SystemClock#setCurrentTimeMillis(long)}
+ *
+ * @param duration The interval by which to advance.
+ */
+ public static void advanceBy(Duration duration) {
+ SystemClock.setCurrentTimeMillis(SystemClock.uptimeMillis() + duration.toMillis());
+ }
+
public static void reset() {
networkTimeAvailable = true;
}
+
+ public static class Picker extends LooperShadowPicker<ShadowSystemClock> {
+
+ public Picker() {
+ super(ShadowLegacySystemClock.class, ShadowPausedSystemClock.class);
+ }
+ }
}