blob: d29d5efe0d320d66419b04ae9fd757b9b0e79f3b [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base;
import androidx.annotation.GuardedBy;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.chromium.base.TimeUtils.FakeClock;
/** Causes fake times to be used in TimeUtils. */
public class FakeTimeTestRule implements TestRule {
private final Object mLock = new Object();
// Milliseconds since booted, excluding deep sleep.
@GuardedBy("mLock")
private long mUptimeMillis;
// Nanoseconds since booted, including deep sleep.
@GuardedBy("mLock")
private long mElapsedRealtimeNanos;
// Per-thread CPU time.
@GuardedBy("mLock")
private ThreadLocal<Long> mThreadTimes;
// epoch time.
@GuardedBy("mLock")
private long mCurrentTimeMillis;
/** Resets to default time values. */
public void resetTimes() {
synchronized (mLock) {
mUptimeMillis = 10000;
mElapsedRealtimeNanos = 20000L * TimeUtils.NANOSECONDS_PER_MILLISECOND;
mCurrentTimeMillis = 1653000000000L; // May 19 2022 18:40:00 GMT-0400
mThreadTimes =
new ThreadLocal<Long>() {
@Override
protected Long initialValue() {
return 0L;
}
};
}
}
private final TimeUtils.FakeClock mFakeClock =
new FakeClock() {
@Override
public long uptimeMillis() {
synchronized (mLock) {
return mUptimeMillis;
}
}
@Override
public long elapsedRealtimeNanos() {
synchronized (mLock) {
return mElapsedRealtimeNanos;
}
}
@Override
public long currentThreadTimeMillis() {
synchronized (mLock) {
return mThreadTimes.get();
}
}
@Override
public long currentTimeMillis() {
synchronized (mLock) {
return mCurrentTimeMillis;
}
}
};
/** Advances uptime, elapsedRealtime, and the current thread's threadTime.. */
public void advanceMillis(long increment) {
assert increment > 0 : "Negative increment: " + increment;
synchronized (mLock) {
mCurrentTimeMillis += increment;
mUptimeMillis += increment;
mElapsedRealtimeNanos += increment * TimeUtils.NANOSECONDS_PER_MILLISECOND;
mThreadTimes.set(mThreadTimes.get() + increment);
}
}
/** Advances uptime and elapsedRealtime. */
public void sleepMillis(long duration) {
assert duration > 0 : "Negative duration: " + duration;
synchronized (mLock) {
mCurrentTimeMillis += duration;
mUptimeMillis += duration;
mElapsedRealtimeNanos += duration * TimeUtils.NANOSECONDS_PER_MILLISECOND;
}
}
/** Advances elapsedRealtime. */
public void deepSleepMillis(long duration) {
assert duration > 0 : "Negative duration: " + duration;
synchronized (mLock) {
mCurrentTimeMillis += duration;
mElapsedRealtimeNanos += duration * TimeUtils.NANOSECONDS_PER_MILLISECOND;
}
}
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
try {
resetTimes();
TimeUtils.sFakeClock = mFakeClock;
base.evaluate();
} finally {
TimeUtils.sFakeClock = null;
}
}
};
}
}