Fix timestamps in Bazel/Blaze JUnit runner
RELNOTES: None.
PiperOrigin-RevId: 255058249
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/JUnit4Bazel.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/JUnit4Bazel.java
index 2443583..7ce14d6 100644
--- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/JUnit4Bazel.java
+++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/JUnit4Bazel.java
@@ -17,6 +17,7 @@
import com.google.testing.junit.runner.internal.SignalHandlers;
import com.google.testing.junit.runner.internal.SignalHandlersFactory;
import com.google.testing.junit.runner.junit4.CancellableRequestFactoryFactory;
+import com.google.testing.junit.runner.junit4.ClockFactory;
import com.google.testing.junit.runner.junit4.CurrentRunningTestFactory;
import com.google.testing.junit.runner.junit4.JUnit4ConfigFactory;
import com.google.testing.junit.runner.junit4.JUnit4InstanceModules;
@@ -37,7 +38,6 @@
import com.google.testing.junit.runner.junit4.StackTraceListenerFactory;
import com.google.testing.junit.runner.junit4.TestSuiteModelSupplierFactory;
import com.google.testing.junit.runner.junit4.TextListenerFactory;
-import com.google.testing.junit.runner.junit4.TickerFactory;
import com.google.testing.junit.runner.junit4.TopLevelSuiteFactory;
import com.google.testing.junit.runner.junit4.TopLevelSuiteNameFactory;
import com.google.testing.junit.runner.junit4.XmlListenerFactory;
@@ -153,7 +153,7 @@
this.builderSupplier =
TestSuiteModelBuilderFactory.create(
- TickerFactory.create(),
+ ClockFactory.create(),
shardingFiltersSupplier,
ShardingEnvironmentFactory.create(),
resultWriterSupplier);
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/TickerFactory.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/ClockFactory.java
similarity index 69%
rename from src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/TickerFactory.java
rename to src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/ClockFactory.java
index f3c6c9a..0ec085a 100644
--- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/TickerFactory.java
+++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/ClockFactory.java
@@ -15,22 +15,20 @@
package com.google.testing.junit.runner.junit4;
import com.google.testing.junit.runner.util.Factory;
-import com.google.testing.junit.runner.util.Ticker;
+import com.google.testing.junit.runner.util.TestClock;
-/**
- * A factory that supplies {@link Ticker}.
- */
-public enum TickerFactory implements Factory<Ticker> {
+/** A factory that supplies {@link TestClock}. */
+public enum ClockFactory implements Factory<TestClock> {
INSTANCE;
@Override
- public Ticker get() {
- Ticker ticker = JUnit4RunnerModule.ticker();
- assert ticker != null;
- return ticker;
+ public TestClock get() {
+ TestClock testClock = JUnit4RunnerModule.clock();
+ assert testClock != null;
+ return testClock;
}
- public static Factory<Ticker> create() {
+ public static Factory<TestClock> create() {
return INSTANCE;
}
}
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/JUnit4RunnerModule.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/JUnit4RunnerModule.java
index 1bf6443..73ff05f 100644
--- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/JUnit4RunnerModule.java
+++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/junit4/JUnit4RunnerModule.java
@@ -20,8 +20,8 @@
import com.google.testing.junit.runner.internal.junit4.JUnit4TestStackTraceListener;
import com.google.testing.junit.runner.internal.junit4.JUnit4TestXmlListener;
import com.google.testing.junit.runner.internal.junit4.SettableCurrentRunningTest;
+import com.google.testing.junit.runner.util.TestClock;
import com.google.testing.junit.runner.util.TestNameProvider;
-import com.google.testing.junit.runner.util.Ticker;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
@@ -34,8 +34,8 @@
* Utility class for real test runs. This is a legacy Dagger module.
*/
public final class JUnit4RunnerModule {
- static Ticker ticker() {
- return Ticker.systemTicker();
+ static TestClock clock() {
+ return TestClock.systemClock();
}
static SignalHandlers.HandlerInstaller signalHandlerInstaller() {
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestCaseNode.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestCaseNode.java
index 8662523..581dac6 100644
--- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestCaseNode.java
+++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestCaseNode.java
@@ -17,6 +17,7 @@
import static com.google.testing.junit.runner.util.TestPropertyExporter.INITIAL_INDEX_FOR_REPEATED_PROPERTY;
import com.google.testing.junit.runner.model.TestResult.Status;
+import com.google.testing.junit.runner.util.TestClock.TestInstant;
import com.google.testing.junit.runner.util.TestIntegration;
import com.google.testing.junit.runner.util.TestIntegrationsExporter;
import com.google.testing.junit.runner.util.TestPropertyExporter;
@@ -66,7 +67,7 @@
* Indicates that the test represented by this node is scheduled to start.
*/
void pending() {
- compareAndSetState(State.INITIAL, State.PENDING, -1);
+ compareAndSetState(State.INITIAL, State.PENDING, TestInstant.UNKNOWN);
}
/**
@@ -74,12 +75,12 @@
*
* @param now Time that the test started
*/
- public void started(long now) {
+ public void started(TestInstant now) {
compareAndSetState(INITIAL_STATES, State.STARTED, now);
}
@Override
- public void testInterrupted(long now) {
+ public void testInterrupted(TestInstant now) {
if (compareAndSetState(State.STARTED, State.INTERRUPTED, now)) {
globalFailures.add(new Exception("Test interrupted"));
return;
@@ -107,13 +108,12 @@
}
@Override
- public void testSkipped(long now) {
+ public void testSkipped(TestInstant now) {
compareAndSetState(State.STARTED, State.SKIPPED, now);
}
-
@Override
- public void testSuppressed(long now) {
+ public void testSuppressed(TestInstant now) {
compareAndSetState(INITIAL_STATES, State.SUPPRESSED, now);
}
@@ -122,18 +122,18 @@
*
* @param now Time that the test finished
*/
- public void finished(long now) {
+ public void finished(TestInstant now) {
compareAndSetState(State.STARTED, State.FINISHED, now);
}
@Override
- public void testFailure(Throwable throwable, long now) {
+ public void testFailure(Throwable throwable, TestInstant now) {
compareAndSetState(INITIAL_STATES, State.FINISHED, now);
globalFailures.add(throwable);
}
@Override
- public void dynamicTestFailure(Description test, Throwable throwable, long now) {
+ public void dynamicTestFailure(Description test, Throwable throwable, TestInstant now) {
compareAndSetState(INITIAL_STATES, State.FINISHED, now);
addThrowableToDynamicTestToFailures(test, throwable);
}
@@ -168,7 +168,7 @@
return previousRepetitionsNr;
}
- private boolean compareAndSetState(State fromState, State toState, long now) {
+ private boolean compareAndSetState(State fromState, State toState, TestInstant now) {
if (fromState == null) {
throw new NullPointerException();
}
@@ -176,14 +176,14 @@
}
// TODO(bazel-team): Use AtomicReference instead of a synchronized method.
- private synchronized boolean compareAndSetState(Set<State> fromStates, State toState, long now) {
+ private synchronized boolean compareAndSetState(
+ Set<State> fromStates, State toState, TestInstant now) {
if (fromStates == null || toState == null || state == null) {
throw new NullPointerException();
}
if (fromStates.isEmpty()) {
throw new IllegalArgumentException();
}
-
if (fromStates.contains(state) && toState != state) {
state = toState;
if (toState != State.PENDING) {
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestInterval.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestInterval.java
index 455fb04..e2ab8fc 100644
--- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestInterval.java
+++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestInterval.java
@@ -14,6 +14,7 @@
package com.google.testing.junit.runner.model;
+import com.google.testing.junit.runner.util.TestClock.TestInstant;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -25,11 +26,11 @@
* <p>This class is thread-safe and immutable.
*/
public final class TestInterval {
- private final long startInstant;
- private final long endInstant;
+ private final TestInstant startInstant;
+ private final TestInstant endInstant;
- public TestInterval(long startInstant, long endInstant) {
- if (startInstant > endInstant) {
+ public TestInterval(TestInstant startInstant, TestInstant endInstant) {
+ if (startInstant.monotonicTime().compareTo(endInstant.monotonicTime()) > 0) {
throw new IllegalArgumentException("Start must be before end");
}
this.startInstant = startInstant;
@@ -37,19 +38,19 @@
}
public long getStartMillis() {
- return startInstant;
+ return startInstant.wallTime().toEpochMilli();
}
public long getEndMillis() {
- return endInstant;
+ return endInstant.wallTime().toEpochMilli();
}
public long toDurationMillis() {
- return endInstant - startInstant;
+ return endInstant.monotonicTime().minus(startInstant.monotonicTime()).toMillis();
}
- public TestInterval withEndMillis(long millis) {
- return new TestInterval(startInstant, millis);
+ public TestInterval withEndMillis(TestInstant now) {
+ return new TestInterval(startInstant, now);
}
public String startInstantToString() {
@@ -62,6 +63,19 @@
String startInstantToString(TimeZone tz) {
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
format.setTimeZone(tz);
- return format.format(new Date(startInstant));
+ return format.format(Date.from(startInstant.wallTime()));
+ }
+
+ /** Returns a TestInterval that contains both TestIntervals passed as parameter. */
+ public static TestInterval around(TestInterval a, TestInterval b) {
+ TestInstant start =
+ a.startInstant.monotonicTime().compareTo(b.startInstant.monotonicTime()) < 0
+ ? a.startInstant
+ : b.startInstant;
+ TestInstant end =
+ a.endInstant.monotonicTime().compareTo(b.endInstant.monotonicTime()) > 0
+ ? a.endInstant
+ : b.endInstant;
+ return new TestInterval(start, end);
}
}
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestNode.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestNode.java
index 4ba8aad..a7e842c 100644
--- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestNode.java
+++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestNode.java
@@ -14,6 +14,7 @@
package com.google.testing.junit.runner.model;
+import com.google.testing.junit.runner.util.TestClock.TestInstant;
import java.util.List;
import javax.annotation.Nullable;
import org.junit.runner.Description;
@@ -52,31 +53,23 @@
*/
public abstract boolean isTestCase();
- /**
- * Indicates that the test represented by this node was skipped.
- */
- public abstract void testSkipped(long now);
+ /** Indicates that the test represented by this node was skipped. */
+ public abstract void testSkipped(TestInstant now);
/**
* Indicates that the test represented by this node was ignored or suppressed due to being
* annotated with {@code @Ignore} or {@code @Suppress}.
*/
- public abstract void testSuppressed(long now);
+ public abstract void testSuppressed(TestInstant now);
- /**
- * Indicates that the test represented by this node was interrupted.
- */
- public abstract void testInterrupted(long now);
+ /** Indicates that the test represented by this node was interrupted. */
+ public abstract void testInterrupted(TestInstant now);
- /**
- * Adds a failure to the test represented by this node.
- */
- public abstract void testFailure(Throwable throwable, long now);
+ /** Adds a failure to the test represented by this node. */
+ public abstract void testFailure(Throwable throwable, TestInstant now);
- /**
- * Indicates that a dynamically generated test case or suite failed.
- */
- public abstract void dynamicTestFailure(Description test, Throwable throwable, long now);
+ /** Indicates that a dynamically generated test case or suite failed. */
+ public abstract void dynamicTestFailure(Description test, Throwable throwable, TestInstant now);
/**
* Template-method that creates a {@link TestResult} object that represents the test outcome of
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestSuiteModel.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestSuiteModel.java
index d97f320..8dafb2d 100644
--- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestSuiteModel.java
+++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestSuiteModel.java
@@ -14,14 +14,13 @@
package com.google.testing.junit.runner.model;
-import static java.util.concurrent.TimeUnit.NANOSECONDS;
-
import com.google.testing.junit.junit4.runner.DynamicTestException;
import com.google.testing.junit.runner.sharding.ShardingEnvironment;
import com.google.testing.junit.runner.sharding.ShardingFilters;
+import com.google.testing.junit.runner.util.TestClock;
+import com.google.testing.junit.runner.util.TestClock.TestInstant;
import com.google.testing.junit.runner.util.TestIntegrationsRunnerIntegration;
import com.google.testing.junit.runner.util.TestPropertyRunnerIntegration;
-import com.google.testing.junit.runner.util.Ticker;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
@@ -48,7 +47,7 @@
private final TestSuiteNode rootNode;
private final Map<Description, TestCaseNode> testCaseMap;
private final Map<Description, TestNode> testsMap;
- private final Ticker ticker;
+ private final TestClock testClock;
private final AtomicBoolean wroteXml = new AtomicBoolean(false);
private final XmlResultWriter xmlResultWriter;
@Nullable private final Filter shardingFilter;
@@ -57,7 +56,7 @@
rootNode = builder.rootNode;
testsMap = builder.testsMap;
testCaseMap = filterTestCases(builder.testsMap);
- ticker = builder.ticker;
+ testClock = builder.testClock;
shardingFilter = builder.shardingFilter;
xmlResultWriter = builder.xmlResultWriter;
}
@@ -142,7 +141,7 @@
public void testStarted(Description description) {
TestCaseNode testCase = getTestCase(description);
if (testCase != null) {
- testCase.started(currentMillis());
+ testCase.started(now());
TestPropertyRunnerIntegration.setTestCaseForThread(testCase);
TestIntegrationsRunnerIntegration.setTestCaseForThread(testCase);
}
@@ -152,7 +151,7 @@
* Indicate that the entire test run was interrupted.
*/
public void testRunInterrupted() {
- rootNode.testInterrupted(currentMillis());
+ rootNode.testInterrupted(now());
}
/**
@@ -181,10 +180,9 @@
if (test != null) {
if (throwable instanceof DynamicTestException) {
DynamicTestException dynamicFailure = (DynamicTestException) throwable;
- test.dynamicTestFailure(
- dynamicFailure.getTest(), dynamicFailure.getCause(), currentMillis());
+ test.dynamicTestFailure(dynamicFailure.getTest(), dynamicFailure.getCause(), now());
} else {
- test.testFailure(throwable, currentMillis());
+ test.testFailure(throwable, now());
}
}
}
@@ -197,7 +195,7 @@
public void testSkipped(Description description) {
TestNode test = getTest(description);
if (test != null) {
- test.testSkipped(currentMillis());
+ test.testSkipped(now());
}
}
@@ -210,7 +208,7 @@
public void testSuppressed(Description description) {
TestNode test = getTest(description);
if (test != null) {
- test.testSuppressed(currentMillis());
+ test.testSuppressed(now());
}
}
@@ -220,7 +218,7 @@
public void testFinished(Description description) {
TestCaseNode testCase = getTestCase(description);
if (testCase != null) {
- testCase.finished(currentMillis());
+ testCase.finished(now());
}
/*
@@ -230,8 +228,8 @@
*/
}
- private long currentMillis() {
- return NANOSECONDS.toMillis(ticker.read());
+ private TestInstant now() {
+ return testClock.now();
}
/**
@@ -285,7 +283,7 @@
* A builder for creating a model of a test suite.
*/
public static class Builder {
- private final Ticker ticker;
+ private final TestClock testClock;
private final Map<Description, TestNode> testsMap = new ConcurrentHashMap<>();
private final ShardingEnvironment shardingEnvironment;
private final ShardingFilters shardingFilters;
@@ -295,9 +293,12 @@
private boolean buildWasCalled = false;
@Inject
- public Builder(Ticker ticker, ShardingFilters shardingFilters,
- ShardingEnvironment shardingEnvironment, XmlResultWriter xmlResultWriter) {
- this.ticker = ticker;
+ public Builder(
+ TestClock testClock,
+ ShardingFilters shardingFilters,
+ ShardingEnvironment shardingEnvironment,
+ XmlResultWriter xmlResultWriter) {
+ this.testClock = testClock;
this.shardingFilters = shardingFilters;
this.shardingEnvironment = shardingEnvironment;
this.xmlResultWriter = xmlResultWriter;
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestSuiteModelBuilderFactory.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestSuiteModelBuilderFactory.java
index 5f5bb30..3faffd2 100644
--- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestSuiteModelBuilderFactory.java
+++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestSuiteModelBuilderFactory.java
@@ -18,13 +18,13 @@
import com.google.testing.junit.runner.sharding.ShardingFilters;
import com.google.testing.junit.runner.util.Factory;
import com.google.testing.junit.runner.util.Supplier;
-import com.google.testing.junit.runner.util.Ticker;
+import com.google.testing.junit.runner.util.TestClock;
/**
* A factory that supplies a top level suite {@link TestSuiteModel.Builder}.
*/
public final class TestSuiteModelBuilderFactory implements Factory<TestSuiteModel.Builder> {
- private final Supplier<Ticker> tickerSupplier;
+ private final Supplier<TestClock> tickerSupplier;
private final Supplier<ShardingFilters> shardingFiltersSupplier;
@@ -33,7 +33,7 @@
private final Supplier<XmlResultWriter> xmlResultWriterSupplier;
public TestSuiteModelBuilderFactory(
- Supplier<Ticker> tickerSupplier,
+ Supplier<TestClock> tickerSupplier,
Supplier<ShardingFilters> shardingFiltersSupplier,
Supplier<ShardingEnvironment> shardingEnvironmentSupplier,
Supplier<XmlResultWriter> xmlResultWriterSupplier) {
@@ -57,7 +57,7 @@
}
public static Factory<TestSuiteModel.Builder> create(
- Supplier<Ticker> tickerSupplier,
+ Supplier<TestClock> tickerSupplier,
Supplier<ShardingFilters> shardingFiltersSupplier,
Supplier<ShardingEnvironment> shardingEnvironmentSupplier,
Supplier<XmlResultWriter> xmlResultWriterSupplier) {
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestSuiteNode.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestSuiteNode.java
index 73996b1..79a9e96 100644
--- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestSuiteNode.java
+++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/model/TestSuiteNode.java
@@ -15,6 +15,7 @@
package com.google.testing.junit.runner.model;
import com.google.testing.junit.runner.model.TestResult.Status;
+import com.google.testing.junit.runner.util.TestClock.TestInstant;
import com.google.testing.junit.runner.util.TestIntegration;
import java.util.ArrayList;
import java.util.Collections;
@@ -44,35 +45,35 @@
}
@Override
- public void testFailure(Throwable throwable, long now) {
+ public void testFailure(Throwable throwable, TestInstant now) {
for (TestNode child : getChildren()) {
child.testFailure(throwable, now);
}
}
@Override
- public void dynamicTestFailure(Description test, Throwable throwable, long now) {
+ public void dynamicTestFailure(Description test, Throwable throwable, TestInstant now) {
for (TestNode child : getChildren()) {
child.dynamicTestFailure(test, throwable, now);
}
}
@Override
- public void testInterrupted(long now) {
+ public void testInterrupted(TestInstant now) {
for (TestNode child : getChildren()) {
child.testInterrupted(now);
}
}
@Override
- public void testSkipped(long now) {
+ public void testSkipped(TestInstant now) {
for (TestNode child : getChildren()) {
child.testSkipped(now);
}
}
@Override
- public void testSuppressed(long now) {
+ public void testSuppressed(TestInstant now) {
for (TestNode child : getChildren()) {
child.testSuppressed(now);
}
@@ -101,12 +102,7 @@
TestInterval childRunTime = childResult.getRunTimeInterval();
if (childRunTime != null) {
- runTime =
- runTime == null
- ? childRunTime
- : new TestInterval(
- Math.min(runTime.getStartMillis(), childRunTime.getStartMillis()),
- Math.max(runTime.getEndMillis(), childRunTime.getEndMillis()));
+ runTime = runTime == null ? childRunTime : TestInterval.around(runTime, childRunTime);
}
}
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/util/FakeTestClock.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/util/FakeTestClock.java
new file mode 100644
index 0000000..5929ac5
--- /dev/null
+++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/util/FakeTestClock.java
@@ -0,0 +1,70 @@
+// Copyright 2010 The Bazel Authors. All Rights Reserved.
+//
+// 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.testing.junit.runner.util;
+
+import java.time.Duration;
+import java.time.Instant;
+
+/**
+ * A Ticker whose value can be advanced programmatically in test.
+ *
+ * <p>The ticker can be configured so that the time is incremented whenever {@link #now()} is
+ * called.
+ *
+ * <p>This class is thread-safe.
+ */
+public class FakeTestClock extends TestClock {
+
+ private final Instant wallTimeStart = Instant.EPOCH;
+ private Duration monotonic = Duration.ZERO;
+ private Duration autoIncrementStep = Duration.ZERO;
+
+ /** Advances the ticker value by {@code time} in {@code timeUnit}. */
+ public synchronized FakeTestClock advance(Duration duration) {
+ monotonic = monotonic.plus(duration);
+ return this;
+ }
+
+ /**
+ * Sets the increment applied to the ticker whenever it is queried.
+ *
+ * <p>The default behavior is to auto increment by zero. i.e: The ticker is left unchanged when
+ * queried.
+ */
+ public synchronized FakeTestClock setAutoIncrementStep(Duration autoIncrementStep) {
+ if (autoIncrementStep.toNanos() < 0) {
+ throw new IllegalArgumentException("May not auto-increment by a negative amount");
+ }
+ this.autoIncrementStep = autoIncrementStep;
+ return this;
+ }
+
+ @Override
+ Duration monotonicTime() {
+ return monotonic;
+ }
+
+ @Override
+ Instant wallTime() {
+ return wallTimeStart.plus(monotonic);
+ }
+
+ @Override
+ public synchronized TestInstant now() {
+ advance(autoIncrementStep);
+ return super.now();
+ }
+}
+
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/util/FakeTicker.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/util/FakeTicker.java
deleted file mode 100644
index 400a96c..0000000
--- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/util/FakeTicker.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2010 The Bazel Authors. All Rights Reserved.
-//
-// 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.testing.junit.runner.util;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * A Ticker whose value can be advanced programmatically in test.
- * <p> The ticker can be configured so that the time is incremented whenever {@link #read} is
- * called.
- *
- * <p> This class is thread-safe.
- */
-public class FakeTicker extends Ticker {
-
- private final AtomicLong nanos = new AtomicLong();
- private volatile long autoIncrementStepNanos;
-
- /** Advances the ticker value by {@code time} in {@code timeUnit}. */
- public FakeTicker advance(long time, TimeUnit timeUnit) {
- return advance(timeUnit.toNanos(time));
- }
-
- /** Advances the ticker value by {@code nanoseconds}. */
- public FakeTicker advance(long nanoseconds) {
- nanos.addAndGet(nanoseconds);
- return this;
- }
-
- /**
- * Sets the increment applied to the ticker whenever it is queried.
- *
- * <p>The default behavior is to auto increment by zero. i.e: The ticker is left unchanged when
- * queried.
- */
- public FakeTicker setAutoIncrementStep(long autoIncrementStep, TimeUnit timeUnit) {
- if (autoIncrementStep < 0) {
- throw new IllegalArgumentException("May not auto-increment by a negative amount");
- }
- this.autoIncrementStepNanos = timeUnit.toNanos(autoIncrementStep);
- return this;
- }
-
- @Override
- public long read() {
- return nanos.getAndAdd(autoIncrementStepNanos);
- }
-}
-
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/util/TestClock.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/util/TestClock.java
new file mode 100644
index 0000000..9bf3cad
--- /dev/null
+++ b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/util/TestClock.java
@@ -0,0 +1,109 @@
+// Copyright 2016 The Bazel Authors. All Rights Reserved.
+//
+// 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.testing.junit.runner.util;
+
+import java.time.Duration;
+import java.time.Instant;
+
+/**
+ * A time source used to obtain:
+ * <li>a monotonic timestamp with no relation to a wall time;
+ * <li>a timestamp that can be used to obtain wall time but is not guaranteed to be monotonic.
+ */
+public abstract class TestClock {
+ /** Constructor for use by subclasses. */
+ protected TestClock() {}
+
+ /**
+ * Returns an immutable value type that contains both a monotonic timestamp (used to measure
+ * relative time but unrelated to wall time) and an EPOCH relative timestamp.
+ */
+ public TestInstant now() {
+ return new TestInstant(wallTime(), monotonicTime());
+ }
+
+ /**
+ * Returns a monotonic timestamp that can only be used to compute relative time.
+ *
+ * <p><b>Warning:</b> the returned timestamp can only be used to measure elapsed time, not wall
+ * time.
+ */
+ abstract Duration monotonicTime();
+
+ /**
+ * A timestamp that may be used to obtain wall time, but is not guaranteed to be monotonic.
+ *
+ * <p><b>Warning:</b> the returned timestamp is not guaranteed to be monotonic, and it may appear
+ * to go back in time in certain cases (e.g. daylight saving time).
+ */
+ abstract Instant wallTime();
+
+ /**
+ * A time source that produces an epoch timestamp using {@link System#currentTimeMillis} and a
+ * monotonic timestamp using {@link System#nanoTime}.
+ */
+ public static TestClock systemClock() {
+ return SYSTEM_TEST_CLOCK;
+ }
+
+ private static final TestClock SYSTEM_TEST_CLOCK =
+ new TestClock() {
+ @Override
+ public Duration monotonicTime() {
+ return Duration.ofNanos(System.nanoTime());
+ }
+
+ @Override
+ public Instant wallTime() {
+ return Instant.ofEpochMilli(System.currentTimeMillis());
+ }
+ };
+
+ /**
+ * An immutable value type that contains both a monotonic timestamp (used to measure relative time
+ * but unrelated to wall time) and an EPOCH timestamp.
+ */
+ public static class TestInstant {
+ public static final TestInstant UNKNOWN = new TestInstant(Instant.EPOCH, Duration.ZERO);
+
+ private final Instant wallTime;
+ private final Duration monotonicTime;
+
+ public TestInstant(Instant wallTime, Duration monotonicTime) {
+ this.wallTime = wallTime;
+ this.monotonicTime = monotonicTime;
+ }
+
+ /**
+ * A timestamp that may be used to obtain wall time, but is not guaranteed to be monotonic.
+ *
+ * <p><b>Warning:</b> the returned timestamp is not guaranteed to be monotonic, and it may
+ * appear to go back in time in certain cases (e.g. daylight saving time).
+ */
+ public Instant wallTime() {
+ return wallTime;
+ }
+
+ /**
+ * Returns a monotonic timestamp that can only be used to compute relative time.
+ *
+ * <p><b>Warning:</b> the returned timestamp can only be used to measure elapsed time, not wall
+ * time.
+ */
+ public Duration monotonicTime() {
+ return monotonicTime;
+ }
+ }
+}
diff --git a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/util/Ticker.java b/src/java_tools/junitrunner/java/com/google/testing/junit/runner/util/Ticker.java
deleted file mode 100644
index 0b00a98..0000000
--- a/src/java_tools/junitrunner/java/com/google/testing/junit/runner/util/Ticker.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2016 The Bazel Authors. All Rights Reserved.
-//
-// 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.testing.junit.runner.util;
-
-/**
- * A time source; returns a time value representing the number of nanoseconds elapsed since some
- * fixed but arbitrary point in time.
- *
- * <p><b>Warning:</b> this interface can only be used to measure elapsed time, not wall time.
- */
-public abstract class Ticker {
- /**
- * Constructor for use by subclasses.
- */
- protected Ticker() {}
-
- /**
- * Returns the number of nanoseconds elapsed since this ticker's fixed point of reference.
- */
- public abstract long read();
-
- /**
- * A ticker that reads the current time using {@link System#nanoTime}.
- *
- * @since 10.0
- */
- public static Ticker systemTicker() {
- return SYSTEM_TICKER;
- }
-
- private static final Ticker SYSTEM_TICKER =
- new Ticker() {
- @Override
- public long read() {
- return System.nanoTime();
- }
- };
-}
diff --git a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/JUnit4BazelMock.java b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/JUnit4BazelMock.java
index 02c1874..bffc655 100644
--- a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/JUnit4BazelMock.java
+++ b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/JUnit4BazelMock.java
@@ -24,7 +24,7 @@
import com.google.testing.junit.runner.util.MemoizingSupplier;
import com.google.testing.junit.runner.util.SetFactory;
import com.google.testing.junit.runner.util.Supplier;
-import com.google.testing.junit.runner.util.Ticker;
+import com.google.testing.junit.runner.util.TestClock;
import java.io.PrintStream;
import java.util.Set;
import org.junit.internal.TextListener;
@@ -44,7 +44,7 @@
private Supplier<String> topLevelSuiteNameSupplier;
- private Supplier<Ticker> tickerSupplier;
+ private Supplier<TestClock> tickerSupplier;
private Supplier<ShardingEnvironment> shardingEnvironmentSupplier;
diff --git a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/JUnit4RunnerTest.java b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/JUnit4RunnerTest.java
index a061a92..c3377ec 100644
--- a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/JUnit4RunnerTest.java
+++ b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/JUnit4RunnerTest.java
@@ -40,10 +40,10 @@
import com.google.testing.junit.runner.sharding.api.ShardingFilterFactory;
import com.google.testing.junit.runner.sharding.testing.FakeShardingFilters;
import com.google.testing.junit.runner.util.CurrentRunningTest;
-import com.google.testing.junit.runner.util.FakeTicker;
+import com.google.testing.junit.runner.util.FakeTestClock;
import com.google.testing.junit.runner.util.GoogleTestSecurityManager;
+import com.google.testing.junit.runner.util.TestClock;
import com.google.testing.junit.runner.util.TestNameProvider;
-import com.google.testing.junit.runner.util.Ticker;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
@@ -571,8 +571,8 @@
return shardingEnvironment;
}
- Ticker ticker() {
- return new FakeTicker();
+ TestClock clock() {
+ return new FakeTestClock();
}
JUnit4Config config() {
diff --git a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/JUnit4TestModelBuilderTest.java b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/JUnit4TestModelBuilderTest.java
index c1a4efb..5fc4bf9 100644
--- a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/JUnit4TestModelBuilderTest.java
+++ b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/JUnit4TestModelBuilderTest.java
@@ -29,8 +29,8 @@
import com.google.testing.junit.runner.sharding.ShardingEnvironment;
import com.google.testing.junit.runner.sharding.ShardingFilters;
import com.google.testing.junit.runner.sharding.testing.StubShardingEnvironment;
-import com.google.testing.junit.runner.util.FakeTicker;
-import com.google.testing.junit.runner.util.Ticker;
+import com.google.testing.junit.runner.util.FakeTestClock;
+import com.google.testing.junit.runner.util.TestClock;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
@@ -46,15 +46,18 @@
*/
@RunWith(JUnit4.class)
public class JUnit4TestModelBuilderTest {
- private final Ticker fakeTicker = new FakeTicker();
+ private final TestClock fakeTestClock = new FakeTestClock();
private final ShardingEnvironment stubShardingEnvironment = new StubShardingEnvironment();
private final XmlResultWriter xmlResultWriter = new AntXmlResultWriter();
private JUnit4TestModelBuilder builder(Request request, String suiteName,
ShardingEnvironment shardingEnvironment, ShardingFilters shardingFilters,
XmlResultWriter xmlResultWriter) {
- return new JUnit4TestModelBuilder(request, suiteName, new TestSuiteModel.Builder(
- fakeTicker, shardingFilters, shardingEnvironment, xmlResultWriter));
+ return new JUnit4TestModelBuilder(
+ request,
+ suiteName,
+ new TestSuiteModel.Builder(
+ fakeTestClock, shardingFilters, shardingEnvironment, xmlResultWriter));
}
@Test
diff --git a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/TestModuleTickerFactory.java b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/TestModuleTickerFactory.java
index 9537666..5975b3c 100644
--- a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/TestModuleTickerFactory.java
+++ b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/junit4/TestModuleTickerFactory.java
@@ -15,12 +15,10 @@
package com.google.testing.junit.runner.junit4;
import com.google.testing.junit.runner.util.Factory;
-import com.google.testing.junit.runner.util.Ticker;
+import com.google.testing.junit.runner.util.TestClock;
-/**
- * A factory that supplies a {@link Ticker} for testing purposes.
- */
-public final class TestModuleTickerFactory implements Factory<Ticker> {
+/** A factory that supplies a {@link TestClock} for testing purposes. */
+public final class TestModuleTickerFactory implements Factory<TestClock> {
private final JUnit4RunnerTest.TestModule module;
public TestModuleTickerFactory(JUnit4RunnerTest.TestModule module) {
@@ -29,15 +27,15 @@
}
@Override
- public Ticker get() {
- Ticker ticker = module.ticker();
- if (ticker == null) {
+ public TestClock get() {
+ TestClock testClock = module.clock();
+ if (testClock == null) {
throw new NullPointerException();
}
- return ticker;
+ return testClock;
}
- public static Factory<Ticker> create(JUnit4RunnerTest.TestModule module) {
+ public static Factory<TestClock> create(JUnit4RunnerTest.TestModule module) {
return new TestModuleTickerFactory(module);
}
}
\ No newline at end of file
diff --git a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/AntXmlResultWriterTest.java b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/AntXmlResultWriterTest.java
index 3b208388..f4187d9 100644
--- a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/AntXmlResultWriterTest.java
+++ b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/AntXmlResultWriterTest.java
@@ -15,18 +15,31 @@
package com.google.testing.junit.runner.model;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.testing.junit.runner.model.TestInstantUtil.testInstant;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.testing.junit.runner.util.TestClock.TestInstant;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
@RunWith(JUnit4.class)
public class AntXmlResultWriterTest {
- private static final long NOW = 1;
+
private static TestSuiteNode root;
private static XmlWriter writer;
private static AntXmlResultWriter resultWriter;
@@ -69,9 +82,37 @@
assertThat(resultXml).doesNotContain("<testcase name='testCase2'");
}
+ @Test
+ public void testWallTimeAndMonotonicTimestamp() throws Exception {
+ TestSuiteNode parent = createTestSuite();
+ TestCaseNode test = createTestCase(parent);
+
+ // wall time may appear to go back in time in exceptional cases (e.g. daylight saving time)
+ test.started(new TestInstant(Instant.ofEpochMilli(1560786184600L), Duration.ZERO));
+ test.finished(new TestInstant(Instant.EPOCH, Duration.ofMillis(1L)));
+
+ resultWriter.writeTestSuites(writer, root.getResult());
+
+ String resultXml = stringWriter.toString();
+ assertThat(resultXml).contains("time=");
+ assertThat(resultXml).contains("timestamp=");
+
+ Document document = parseXml(resultXml);
+ Element testSuites = document.getDocumentElement();
+ Element testSuite = (Element) testSuites.getElementsByTagName("testsuite").item(0);
+ assertThat(testSuite.getTagName()).isEqualTo("testsuite");
+ assertThat(testSuite.getAttribute("name"))
+ .isEqualTo("com.google.testing.junit.runner.model.TestCaseNodeTest$TestSuite");
+ assertThat(testSuite.getAttribute("time")).isEqualTo("0.001");
+ assertThat(
+ Instant.from(
+ DateTimeFormatter.ISO_DATE_TIME.parse(testSuite.getAttribute("timestamp"))))
+ .isEqualTo(Instant.ofEpochMilli(1560786184600L));
+ }
+
private void runToCompletion(TestCaseNode test) {
- test.started(NOW);
- test.finished(NOW + 1);
+ test.started(testInstant(Instant.ofEpochMilli(1)));
+ test.finished(testInstant(Instant.ofEpochMilli(2)));
}
private TestCaseNode createTestCase(TestSuiteNode parent) {
@@ -88,4 +129,12 @@
root.addTestSuite(parent);
return parent;
}
+
+ private static Document parseXml(String testXml)
+ throws SAXException, ParserConfigurationException, IOException {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setIgnoringElementContentWhitespace(true);
+ DocumentBuilder documentBuilder = factory.newDocumentBuilder();
+ return documentBuilder.parse(new ByteArrayInputStream(testXml.getBytes(UTF_8)));
+ }
}
diff --git a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestCaseNodeTest.java b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestCaseNodeTest.java
index 852dd7b..9310eb7 100644
--- a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestCaseNodeTest.java
+++ b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestCaseNodeTest.java
@@ -15,7 +15,12 @@
package com.google.testing.junit.runner.model;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.testing.junit.runner.model.TestInstantUtil.advance;
+import static com.google.testing.junit.runner.model.TestInstantUtil.testInstant;
+import com.google.testing.junit.runner.util.TestClock.TestInstant;
+import java.time.Duration;
+import java.time.Instant;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.Description;
@@ -28,7 +33,7 @@
@RunWith(JUnit4.class)
public class TestCaseNodeTest {
- private static final long NOW = 1;
+ private static final TestInstant NOW = testInstant(Instant.ofEpochMilli(1));
private static Description suite;
private static Description testCase;
@@ -88,7 +93,7 @@
TestCaseNode testCaseNode = new TestCaseNode(testCase, new TestSuiteNode(suite));
testCaseNode.pending();
testCaseNode.started(NOW);
- testCaseNode.testInterrupted(NOW + 1);
+ testCaseNode.testInterrupted(advance(NOW, Duration.ofMillis(1)));
assertStatusAndTiming(testCaseNode, TestResult.Status.INTERRUPTED, NOW, 1);
}
@@ -97,7 +102,7 @@
TestCaseNode testCaseNode = new TestCaseNode(testCase, new TestSuiteNode(suite));
testCaseNode.pending();
testCaseNode.started(NOW);
- testCaseNode.testSkipped(NOW + 1);
+ testCaseNode.testSkipped(advance(NOW, Duration.ofMillis(1)));
assertStatusAndTiming(testCaseNode, TestResult.Status.SKIPPED, NOW, 1);
}
@@ -106,7 +111,7 @@
TestCaseNode testCaseNode = new TestCaseNode(testCase, new TestSuiteNode(suite));
testCaseNode.pending();
testCaseNode.started(NOW);
- testCaseNode.finished(NOW + 1);
+ testCaseNode.finished(advance(NOW, Duration.ofMillis(1)));
assertStatusAndTiming(testCaseNode, TestResult.Status.COMPLETED, NOW, 1);
}
@@ -115,8 +120,8 @@
TestCaseNode testCaseNode = new TestCaseNode(testCase, new TestSuiteNode(suite));
testCaseNode.pending();
testCaseNode.started(NOW);
- testCaseNode.testFailure(new Exception(), NOW + 1);
- testCaseNode.finished(NOW + 2);
+ testCaseNode.testFailure(new Exception(), advance(NOW, Duration.ofMillis(1)));
+ testCaseNode.finished(advance(NOW, Duration.ofMillis(2)));
assertStatusAndTiming(testCaseNode, TestResult.Status.COMPLETED, NOW, 2);
}
@@ -125,8 +130,8 @@
TestCaseNode testCaseNode = new TestCaseNode(testCase, new TestSuiteNode(suite));
testCaseNode.pending();
testCaseNode.started(NOW);
- testCaseNode.testFailure(new Exception(), NOW + 1);
- testCaseNode.testInterrupted(NOW + 2);
+ testCaseNode.testFailure(new Exception(), advance(NOW, Duration.ofMillis(1)));
+ testCaseNode.testInterrupted(advance(NOW, Duration.ofMillis(2)));
assertStatusAndTiming(testCaseNode, TestResult.Status.INTERRUPTED, NOW, 2);
}
@@ -139,11 +144,12 @@
}
private void assertStatusAndTiming(
- TestCaseNode testCase, TestResult.Status status, long start, long duration) {
+ TestCaseNode testCase, TestResult.Status status, TestInstant start, long duration) {
TestResult result = testCase.getResult();
assertThat(result.getStatus()).isEqualTo(status);
assertThat(result.getRunTimeInterval()).isNotNull();
- assertThat(result.getRunTimeInterval().getStartMillis()).isEqualTo(start);
+ assertThat(result.getRunTimeInterval().getStartMillis())
+ .isEqualTo(start.wallTime().toEpochMilli());
assertThat(result.getRunTimeInterval().toDurationMillis()).isEqualTo(duration);
}
diff --git a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestInstantUtil.java b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestInstantUtil.java
new file mode 100644
index 0000000..ac168fe
--- /dev/null
+++ b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestInstantUtil.java
@@ -0,0 +1,38 @@
+// Copyright 2015 The Bazel Authors. All Rights Reserved.
+//
+// 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.testing.junit.runner.model;
+
+import com.google.testing.junit.runner.util.TestClock.TestInstant;
+import java.time.Duration;
+import java.time.Instant;
+
+/** Utility class used for quick creation of TestInstant for testing. */
+public class TestInstantUtil {
+
+ // Added to the monotonic nano timestamp to assert it is not used as absolute time
+ private static final long INITIAL_RELATIVE_TIMESTAMP = 111111L;
+
+ /** Creates a TestInstant with a monotonic timestamp that is offset from the wall time. */
+ public static TestInstant testInstant(Instant wallTime) {
+ return new TestInstant(
+ wallTime, Duration.ofMillis(INITIAL_RELATIVE_TIMESTAMP + wallTime.toEpochMilli()));
+ }
+
+ /** Returns a TestInstant advanced in time by the specified duration. */
+ public static TestInstant advance(TestInstant instant, Duration duration) {
+ return new TestInstant(
+ instant.wallTime().plus(duration), instant.monotonicTime().plus(duration));
+ }
+}
diff --git a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestIntervalTest.java b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestIntervalTest.java
index 26573e8f..70b70f0 100644
--- a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestIntervalTest.java
+++ b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestIntervalTest.java
@@ -15,7 +15,11 @@
package com.google.testing.junit.runner.model;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.testing.junit.runner.model.TestInstantUtil.testInstant;
+import com.google.testing.junit.runner.util.TestClock.TestInstant;
+import java.time.Duration;
+import java.time.Instant;
import java.util.Date;
import java.util.TimeZone;
import org.junit.Rule;
@@ -30,11 +34,13 @@
@Test
public void testCreation() {
- TestInterval interval = new TestInterval(123456, 234567);
+ Instant start = Instant.ofEpochMilli(123456);
+ Instant end = Instant.ofEpochMilli(234567);
+ TestInterval interval = new TestInterval(testInstant(start), testInstant(end));
assertThat(interval.getStartMillis()).isEqualTo(123456);
assertThat(interval.getEndMillis()).isEqualTo(234567);
- interval = new TestInterval(123456, 123456);
+ interval = new TestInterval(testInstant(start), testInstant(start));
assertThat(interval.getStartMillis()).isEqualTo(123456);
assertThat(interval.getEndMillis()).isEqualTo(123456);
}
@@ -43,19 +49,43 @@
public void testCreationFailure() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Start must be before end");
- new TestInterval(35, 23);
+ new TestInterval(testInstant(Instant.ofEpochMilli(35)), testInstant(Instant.ofEpochMilli(23)));
}
@Test
public void testToDuration() {
- assertThat(new TestInterval(50, 150).toDurationMillis()).isEqualTo(100);
- assertThat(new TestInterval(100, 100).toDurationMillis()).isEqualTo(0);
+ assertThat(
+ new TestInterval(
+ testInstant(Instant.ofEpochMilli(50)), testInstant(Instant.ofEpochMilli(150)))
+ .toDurationMillis())
+ .isEqualTo(100);
+ assertThat(
+ new TestInterval(
+ testInstant(Instant.ofEpochMilli(100)), testInstant(Instant.ofEpochMilli(100)))
+ .toDurationMillis())
+ .isEqualTo(0);
+ }
+
+ @Test
+ public void testToDurationOnNonMonotonicWallTime() {
+ Instant start = Instant.ofEpochMilli(123456);
+ Instant end = Instant.ofEpochMilli(123456);
+ Duration monotonicStart = Duration.ofMillis(50);
+ Duration monotonicEnd = Duration.ofMillis(150);
+ TestInterval interval =
+ new TestInterval(
+ new TestInstant(start, monotonicStart), new TestInstant(end, monotonicEnd));
+ assertThat(interval.getStartMillis()).isEqualTo(123456);
+ assertThat(interval.getEndMillis()).isEqualTo(123456);
+ assertThat(interval.toDurationMillis()).isEqualTo(100);
}
@Test
public void testDateFormat() {
Date date = new Date(1471709734000L);
- TestInterval interval = new TestInterval(date.getTime(), date.getTime() + 100);
+ TestInterval interval =
+ new TestInterval(
+ testInstant(date.toInstant()), testInstant(date.toInstant().plusMillis(100)));
assertThat(interval.startInstantToString(TimeZone.getTimeZone("America/New_York")))
.isEqualTo("2016-08-20T12:15:34.000-04:00");
assertThat(interval.startInstantToString(TimeZone.getTimeZone("GMT")))
diff --git a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestSuiteNodeTest.java b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestSuiteNodeTest.java
index e6448c5..09ef917 100644
--- a/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestSuiteNodeTest.java
+++ b/src/java_tools/junitrunner/javatests/com/google/testing/junit/runner/model/TestSuiteNodeTest.java
@@ -20,6 +20,9 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
+import com.google.testing.junit.runner.util.TestClock.TestInstant;
+import java.time.Duration;
+import java.time.Instant;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.Description;
@@ -33,7 +36,7 @@
@RunWith(MockitoJUnitRunner.class)
public class TestSuiteNodeTest {
- private static final long NOW = 1;
+ private static final TestInstant NOW = new TestInstant(Instant.EPOCH, Duration.ZERO);
@Mock private TestCaseNode testCaseNode;
private TestSuiteNode testSuiteNode;