1 // Copyright 2022 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base; 6 7 import androidx.annotation.GuardedBy; 8 9 import org.junit.rules.TestRule; 10 import org.junit.runner.Description; 11 import org.junit.runners.model.Statement; 12 13 import org.chromium.base.TimeUtils.FakeClock; 14 15 /** Causes fake times to be used in TimeUtils. */ 16 public class FakeTimeTestRule implements TestRule { 17 private final Object mLock = new Object(); 18 19 // Milliseconds since booted, excluding deep sleep. 20 @GuardedBy("mLock") 21 private long mUptimeMillis; 22 23 // Nanoseconds since booted, including deep sleep. 24 @GuardedBy("mLock") 25 private long mElapsedRealtimeNanos; 26 27 // Per-thread CPU time. 28 @GuardedBy("mLock") 29 private ThreadLocal<Long> mThreadTimes; 30 31 // epoch time. 32 @GuardedBy("mLock") 33 private long mCurrentTimeMillis; 34 35 /** Resets to default time values. */ resetTimes()36 public void resetTimes() { 37 synchronized (mLock) { 38 mUptimeMillis = 10000; 39 mElapsedRealtimeNanos = 20000L * TimeUtils.NANOSECONDS_PER_MILLISECOND; 40 mCurrentTimeMillis = 1653000000000L; // May 19 2022 18:40:00 GMT-0400 41 42 mThreadTimes = 43 new ThreadLocal<Long>() { 44 @Override 45 protected Long initialValue() { 46 return 0L; 47 } 48 }; 49 } 50 } 51 52 private final TimeUtils.FakeClock mFakeClock = 53 new FakeClock() { 54 @Override 55 public long uptimeMillis() { 56 synchronized (mLock) { 57 return mUptimeMillis; 58 } 59 } 60 61 @Override 62 public long elapsedRealtimeNanos() { 63 synchronized (mLock) { 64 return mElapsedRealtimeNanos; 65 } 66 } 67 68 @Override 69 public long currentThreadTimeMillis() { 70 synchronized (mLock) { 71 return mThreadTimes.get(); 72 } 73 } 74 75 @Override 76 public long currentTimeMillis() { 77 synchronized (mLock) { 78 return mCurrentTimeMillis; 79 } 80 } 81 }; 82 83 /** Advances uptime, elapsedRealtime, and the current thread's threadTime.. */ advanceMillis(long increment)84 public void advanceMillis(long increment) { 85 assert increment > 0 : "Negative increment: " + increment; 86 synchronized (mLock) { 87 mCurrentTimeMillis += increment; 88 mUptimeMillis += increment; 89 mElapsedRealtimeNanos += increment * TimeUtils.NANOSECONDS_PER_MILLISECOND; 90 mThreadTimes.set(mThreadTimes.get() + increment); 91 } 92 } 93 94 /** Advances uptime and elapsedRealtime. */ sleepMillis(long duration)95 public void sleepMillis(long duration) { 96 assert duration > 0 : "Negative duration: " + duration; 97 synchronized (mLock) { 98 mCurrentTimeMillis += duration; 99 mUptimeMillis += duration; 100 mElapsedRealtimeNanos += duration * TimeUtils.NANOSECONDS_PER_MILLISECOND; 101 } 102 } 103 104 /** Advances elapsedRealtime. */ deepSleepMillis(long duration)105 public void deepSleepMillis(long duration) { 106 assert duration > 0 : "Negative duration: " + duration; 107 synchronized (mLock) { 108 mCurrentTimeMillis += duration; 109 mElapsedRealtimeNanos += duration * TimeUtils.NANOSECONDS_PER_MILLISECOND; 110 } 111 } 112 113 @Override apply(Statement base, Description description)114 public Statement apply(Statement base, Description description) { 115 return new Statement() { 116 @Override 117 public void evaluate() throws Throwable { 118 try { 119 resetTimes(); 120 TimeUtils.sFakeClock = mFakeClock; 121 base.evaluate(); 122 } finally { 123 TimeUtils.sFakeClock = null; 124 } 125 } 126 }; 127 } 128 } 129