Skip to content

Clock improvements and test re-org #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions src/main/java/org/dataloader/DataLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.dataloader.stats.StatisticsCollector;

import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -404,19 +405,21 @@ public DataLoader(BatchLoader<K, V> batchLoadFunction, DataLoaderOptions options
this((Object) batchLoadFunction, options);
}

@VisibleForTesting
DataLoader(Object batchLoadFunction, DataLoaderOptions options) {
this(batchLoadFunction, options, Clock.systemUTC());
}

@VisibleForTesting
DataLoader(Object batchLoadFunction, DataLoaderOptions options, Clock clock) {
DataLoaderOptions loaderOptions = options == null ? new DataLoaderOptions() : options;
this.futureCache = determineCacheMap(loaderOptions);
// order of keys matter in data loader
this.stats = nonNull(loaderOptions.getStatisticsCollector());

this.helper = new DataLoaderHelper<>(this, batchLoadFunction, loaderOptions, this.futureCache, this.stats, clock());
this.helper = new DataLoaderHelper<>(this, batchLoadFunction, loaderOptions, this.futureCache, this.stats, clock);
}

@VisibleForTesting
Clock clock() {
return Clock.systemUTC();
}

@SuppressWarnings("unchecked")
private CacheMap<Object, CompletableFuture<V>> determineCacheMap(DataLoaderOptions loaderOptions) {
Expand All @@ -433,6 +436,15 @@ public Instant getLastDispatchTime() {
return helper.getLastDispatchTime();
}

/**
* This returns the {@link Duration} since the data loader was dispatched. When the data loader is created this is zero.
*
* @return the time duration since the last dispatch
*/
public Duration getTimeSinceDispatch() {
return Duration.between(helper.getLastDispatchTime(), helper.now());
}


/**
* Requests to load the data with the specified key asynchronously, and returns a future of the resulting value.
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/dataloader/DataLoaderHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ Object getCallContext() {
this.lastDispatchTime.set(now());
}

private Instant now() {
return Instant.now(clock);
Instant now() {
return clock.instant();
}

public Instant getLastDispatchTime() {
Expand Down
15 changes: 15 additions & 0 deletions src/test/java/org/dataloader/ClockDataLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.dataloader;

import java.time.Clock;

public class ClockDataLoader<K, V> extends DataLoader<K, V> {

ClockDataLoader(Object batchLoadFunction, Clock clock) {
this(batchLoadFunction, null, clock);
}

ClockDataLoader(Object batchLoadFunction, DataLoaderOptions options, Clock clock) {
super(batchLoadFunction, options, clock);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
import static org.awaitility.Awaitility.await;
import static org.dataloader.DataLoaderFactory.newDataLoader;
import static org.dataloader.DataLoaderOptions.newOptions;
import static org.dataloader.TestKit.futureError;
import static org.dataloader.TestKit.listFrom;
import static org.dataloader.fixtures.TestKit.futureError;
import static org.dataloader.fixtures.TestKit.listFrom;
import static org.dataloader.impl.CompletableFutureKit.cause;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
Expand Down
4 changes: 3 additions & 1 deletion src/test/java/org/dataloader/DataLoaderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.dataloader;

import org.dataloader.fixtures.JsonObject;
import org.dataloader.fixtures.TestKit;
import org.dataloader.fixtures.User;
import org.dataloader.fixtures.UserManager;
import org.dataloader.impl.CompletableFutureKit;
Expand All @@ -37,7 +39,7 @@
import static org.awaitility.Awaitility.await;
import static org.dataloader.DataLoaderFactory.newDataLoader;
import static org.dataloader.DataLoaderOptions.newOptions;
import static org.dataloader.TestKit.listFrom;
import static org.dataloader.fixtures.TestKit.listFrom;
import static org.dataloader.impl.CompletableFutureKit.cause;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
Expand Down
57 changes: 17 additions & 40 deletions src/test/java/org/dataloader/DataLoaderTimeTest.java
Original file line number Diff line number Diff line change
@@ -1,68 +1,45 @@
package org.dataloader;

import org.dataloader.fixtures.TestingClock;
import org.junit.Test;

import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;

import static org.dataloader.fixtures.TestKit.keysAsValues;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;

@SuppressWarnings("UnusedReturnValue")
public class DataLoaderTimeTest {

private <T> BatchLoader<T, T> keysAsValues() {
return CompletableFuture::completedFuture;
}

AtomicReference<Clock> clockRef = new AtomicReference<>();

@Test
public void should_set_and_instant_if_dispatched() {
Clock clock = zeroEpoch();
clockRef.set(clock);

Instant startInstant = now();

@SuppressWarnings("deprecation")
DataLoader<Integer, Integer> dataLoader = new DataLoader<Integer, Integer>(keysAsValues()) {
@Override
Clock clock() {
return clockRef.get();
}
};
TestingClock clock = new TestingClock();
DataLoader<Integer, Integer> dataLoader = new ClockDataLoader<>(keysAsValues(), clock);
Instant then = clock.instant();

long sinceMS = msSince(dataLoader.getLastDispatchTime());
long sinceMS = dataLoader.getTimeSinceDispatch().toMillis();
assertThat(sinceMS, equalTo(0L));
assertThat(startInstant, equalTo(dataLoader.getLastDispatchTime()));
assertThat(then, equalTo(dataLoader.getLastDispatchTime()));

jump(clock, 1000);
dataLoader.dispatch();
then = clock.instant();
clock.jump(1000);

sinceMS = msSince(dataLoader.getLastDispatchTime());
sinceMS = dataLoader.getTimeSinceDispatch().toMillis();
assertThat(sinceMS, equalTo(1000L));
}
assertThat(then, equalTo(dataLoader.getLastDispatchTime()));

private long msSince(Instant lastDispatchTime) {
return Duration.between(lastDispatchTime, now()).toMillis();
}
// dispatch and hence reset the time of last dispatch
then = clock.instant();
dataLoader.dispatch();

private Instant now() {
return Instant.now(clockRef.get());
}
sinceMS = dataLoader.getTimeSinceDispatch().toMillis();
assertThat(sinceMS, equalTo(0L));
assertThat(then, equalTo(dataLoader.getLastDispatchTime()));

private Clock jump(Clock clock, int millis) {
clock = Clock.offset(clock, Duration.ofMillis(millis));
clockRef.set(clock);
return clock;
}

private Clock zeroEpoch() {
return Clock.fixed(Instant.ofEpochMilli(0), ZoneId.systemDefault());
}

}
23 changes: 0 additions & 23 deletions src/test/java/org/dataloader/TestKit.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package org.dataloader;
package org.dataloader.fixtures;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Stream;

class JsonObject {
public class JsonObject {

private final Map<String, Object> values;

JsonObject() {
public JsonObject() {
values = new LinkedHashMap<>();
}

Expand All @@ -19,8 +19,12 @@ public JsonObject put(String key, Object value) {

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

JsonObject that = (JsonObject) o;

Expand Down
61 changes: 61 additions & 0 deletions src/test/java/org/dataloader/fixtures/TestKit.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.dataloader.fixtures;

import org.dataloader.BatchLoader;
import org.dataloader.DataLoader;
import org.dataloader.DataLoaderFactory;
import org.dataloader.DataLoaderOptions;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import static org.dataloader.impl.CompletableFutureKit.failedFuture;

public class TestKit {

public static <T> BatchLoader<T, T> keysAsValues() {
return CompletableFuture::completedFuture;
}

public static <K, V> BatchLoader<K, V> keysAsValues(List<List<K>> loadCalls) {
return keys -> {
List<K> ks = new ArrayList<>(keys);
loadCalls.add(ks);
@SuppressWarnings("unchecked")
List<V> values = keys.stream()
.map(k -> (V) k)
.collect(Collectors.toList());
return CompletableFuture.completedFuture(values);
};
}

public static <K, V> DataLoader<K, V> idLoader(List<List<K>> loadCalls) {
return idLoader(null, loadCalls);
}

public static <K, V> DataLoader<K, V> idLoader(DataLoaderOptions options, List<List<K>> loadCalls) {
return DataLoaderFactory.newDataLoader(keysAsValues(loadCalls), options);
}

public static Collection<Integer> listFrom(int i, int max) {
List<Integer> ints = new ArrayList<>();
for (int j = i; j < max; j++) {
ints.add(j);
}
return ints;
}

public static <V> CompletableFuture<V> futureError() {
return failedFuture(new IllegalStateException("Error"));
}

public static void snooze(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
38 changes: 38 additions & 0 deletions src/test/java/org/dataloader/fixtures/TestingClock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.dataloader.fixtures;

import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;

/**
* A mutable (but time fixed) clock that can jump forward or back in time
*/
public class TestingClock extends Clock {

private Clock clock;

public TestingClock() {
clock = Clock.fixed(Instant.ofEpochMilli(0), ZoneId.systemDefault());
}

public Clock jump(int millisDelta) {
clock = Clock.offset(clock, Duration.ofMillis(millisDelta));
return clock;
}

@Override
public ZoneId getZone() {
return clock.getZone();
}

@Override
public Clock withZone(ZoneId zone) {
return clock.withZone(zone);
}

@Override
public Instant instant() {
return clock.instant();
}
}