001/*
002 * Copyright (C) 2008 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.testing;
018
019import static com.google.common.base.Preconditions.checkArgument;
020import static java.util.concurrent.TimeUnit.NANOSECONDS;
021
022import com.google.common.annotations.Beta;
023import com.google.common.annotations.GwtCompatible;
024import com.google.common.annotations.GwtIncompatible;
025import com.google.common.annotations.J2ktIncompatible;
026import com.google.common.base.Ticker;
027import com.google.errorprone.annotations.CanIgnoreReturnValue;
028import java.time.Duration;
029import java.util.concurrent.TimeUnit;
030import java.util.concurrent.atomic.AtomicLong;
031import org.jspecify.annotations.NullMarked;
032
033/**
034 * A Ticker whose value can be advanced programmatically in test.
035 *
036 * <p>The ticker can be configured so that the time is incremented whenever {@link #read} is called:
037 * see {@link #setAutoIncrementStep}.
038 *
039 * <p>This class is thread-safe.
040 *
041 * @author Jige Yu
042 * @since 10.0
043 */
044@NullMarked
045@GwtCompatible
046public class FakeTicker extends Ticker {
047
048  private final AtomicLong nanos = new AtomicLong();
049  private volatile long autoIncrementStepNanos;
050
051  /** Advances the ticker value by {@code time} in {@code timeUnit}. */
052  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
053  @CanIgnoreReturnValue
054  public FakeTicker advance(long time, TimeUnit timeUnit) {
055    return advance(timeUnit.toNanos(time));
056  }
057
058  /** Advances the ticker value by {@code nanoseconds}. */
059  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
060  @CanIgnoreReturnValue
061  public FakeTicker advance(long nanoseconds) {
062    nanos.addAndGet(nanoseconds);
063    return this;
064  }
065
066  /**
067   * Advances the ticker value by {@code duration}.
068   *
069   * @since 33.1.0 (but since 28.0 in the JRE <a
070   *     href="https://github.com/google/guava#guava-google-core-libraries-for-java">flavor</a>)
071   */
072  @GwtIncompatible
073  @J2ktIncompatible
074  @CanIgnoreReturnValue
075  @SuppressWarnings("Java7ApiChecker") // guava-android can rely on library desugaring now.
076  @IgnoreJRERequirement // TODO: b/288085449 - Remove this once we use library-desugaring scents.
077  @Beta // TODO: b/288085449 - Remove @Beta after we're sure that Java 8 APIs are safe for Android
078  public FakeTicker advance(Duration duration) {
079    return advance(duration.toNanos());
080  }
081
082  /**
083   * Sets the increment applied to the ticker whenever it is queried.
084   *
085   * <p>The default behavior is to auto increment by zero. i.e: The ticker is left unchanged when
086   * queried.
087   */
088  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
089  @CanIgnoreReturnValue
090  public FakeTicker setAutoIncrementStep(long autoIncrementStep, TimeUnit timeUnit) {
091    checkArgument(autoIncrementStep >= 0, "May not auto-increment by a negative amount");
092    this.autoIncrementStepNanos = timeUnit.toNanos(autoIncrementStep);
093    return this;
094  }
095
096  /**
097   * Sets the increment applied to the ticker whenever it is queried.
098   *
099   * <p>The default behavior is to auto increment by zero. i.e: The ticker is left unchanged when
100   * queried.
101   *
102   * @since 33.1.0 (but since 28.0 in the JRE <a
103   *     href="https://github.com/google/guava#guava-google-core-libraries-for-java">flavor</a>)
104   */
105  @GwtIncompatible
106  @J2ktIncompatible
107  @CanIgnoreReturnValue
108  @SuppressWarnings("Java7ApiChecker") // guava-android can rely on library desugaring now.
109  @IgnoreJRERequirement // TODO: b/288085449 - Remove this once we use library-desugaring scents.
110  @Beta // TODO: b/288085449 - Remove @Beta after we're sure that Java 8 APIs are safe for Android
111  public FakeTicker setAutoIncrementStep(Duration autoIncrementStep) {
112    return setAutoIncrementStep(autoIncrementStep.toNanos(), NANOSECONDS);
113  }
114
115  @Override
116  public long read() {
117    return nanos.getAndAdd(autoIncrementStepNanos);
118  }
119}