Skip to content

Commit

Permalink
Merge pull request #149 from SpineEventEngine/time-provider
Browse files Browse the repository at this point in the history
Time provider and publishing `testutil`
  • Loading branch information
alexander-yevsyukov committed Jun 7, 2016
2 parents d595ef5 + fd2daea commit 3788a7d
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 21 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ allprojects {
apply plugin: 'jacoco'

group = 'org.spine3'
version = '0.4'
version = '0.4.1-SNAPSHOT'
}

project.ext {
Expand Down Expand Up @@ -162,7 +162,7 @@ void dependPublish(Project project) {
}

// Artifacts to publish
def publishingProjects = ["client", "server", "values"];
def publishingProjects = ["client", "server", "values", "testutil"];

publishingProjects.each {
project(":$it") { currentProject ->
Expand Down
60 changes: 60 additions & 0 deletions client/src/main/java/org/spine3/protobuf/Timestamps.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
import com.google.protobuf.Timestamp;
import com.google.protobuf.TimestampOrBuilder;
import com.google.protobuf.util.TimeUtil;
import org.spine3.Internal;

import javax.annotation.Nullable;
import java.io.Serializable;
import java.util.Comparator;
import java.util.Date;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.protobuf.util.TimeUtil.*;

/**
Expand Down Expand Up @@ -97,6 +99,64 @@ private Timestamps() {
*/
public static final int HOURS_PER_DAY = 24;

private static final ThreadLocal<Provider> timeProvider = new ThreadLocal<Provider>() {
@SuppressWarnings("RefusedBequest") // We want to provide our default value.
@Override
protected Provider initialValue() {
return new SystemTimeProvider();
}
};

/**
* Obtains current time.
*
* <p>This method should be used instead of {@link TimeUtil#getCurrentTime()} for testability
* of time-related code.
*
* @return current time
*/
public static Timestamp getCurrentTime() {
final Timestamp result = timeProvider.get()
.getCurrentTime();
return result;
}

/**
* The provider of the current time.
*
* <p>Implement this interface and pass the resulting class to
*/
@Internal
public interface Provider {
Timestamp getCurrentTime();
}

/**
* Sets provider of the current time.
*
* <p>The most common scenario for using this method is test cases of code that deals
* with current time.
*
* @param provider the provider to set
*/
@Internal
@VisibleForTesting
public static void setProvider(Provider provider) {
timeProvider.set(checkNotNull(provider));
}

/**
* Default implementation of current time provider based on {@link TimeUtil#getCurrentTime()}.
*
* <p>This is the only place, which should invoke obtaining current time from {@code TimeUtil} directly.
*/
private static class SystemTimeProvider implements Provider {
@Override
public Timestamp getCurrentTime() {
return TimeUtil.getCurrentTime();
}
}

/**
* Verifies if the passed {@code Timestamp} instance is valid.
*
Expand Down
26 changes: 13 additions & 13 deletions client/src/test/java/org/spine3/protobuf/TimestampsShould.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public void expose_some_private_constants_from_TimeUtil() {

@Test
public void calculate_timestamp_of_moment_minute_ago_from_now() {
final Timestamp currentTime = getCurrentTime();
final Timestamp currentTime = Timestamps.getCurrentTime();
final Timestamp expected = subtract(currentTime, MINUTE);

final Timestamp actual = Timestamps.minutesAgo(1);
Expand All @@ -87,7 +87,7 @@ public void calculate_timestamp_of_moment_minute_ago_from_now() {

@Test
public void compare_two_timestamps_return_negative_int_if_first_less_than_second_one() {
final Timestamp time1 = getCurrentTime();
final Timestamp time1 = Timestamps.getCurrentTime();
final Timestamp time2 = add(time1, TEN_SECONDS);

final int result = Timestamps.compare(time1, time2);
Expand All @@ -97,7 +97,7 @@ public void compare_two_timestamps_return_negative_int_if_first_less_than_second

@Test
public void compare_two_timestamps_return_negative_int_if_first_is_null() {
final Timestamp currentTime = getCurrentTime();
final Timestamp currentTime = Timestamps.getCurrentTime();

final int result = Timestamps.compare(null, currentTime);

Expand Down Expand Up @@ -131,7 +131,7 @@ public void compare_two_timestamps_return_zero_if_pass_null() {

@Test
public void compare_two_timestamps_return_positive_int_if_first_greater_than_second_one() {
final Timestamp currentTime = getCurrentTime();
final Timestamp currentTime = Timestamps.getCurrentTime();
final Timestamp timeAfterCurrent = add(currentTime, TEN_SECONDS);

final int result = Timestamps.compare(timeAfterCurrent, currentTime);
Expand All @@ -141,7 +141,7 @@ public void compare_two_timestamps_return_positive_int_if_first_greater_than_sec

@Test
public void compare_two_timestamps_return_positive_int_if_second_one_is_null() {
final Timestamp currentTime = getCurrentTime();
final Timestamp currentTime = Timestamps.getCurrentTime();

final int result = Timestamps.compare(currentTime, null);

Expand All @@ -150,7 +150,7 @@ public void compare_two_timestamps_return_positive_int_if_second_one_is_null() {

@Test
public void return_true_if_timestamp_is_between_two_timestamps() {
final Timestamp start = getCurrentTime();
final Timestamp start = Timestamps.getCurrentTime();
final Timestamp timeBetween = add(start, TEN_SECONDS);
final Timestamp finish = add(timeBetween, TEN_SECONDS);

Expand All @@ -161,7 +161,7 @@ public void return_true_if_timestamp_is_between_two_timestamps() {

@Test
public void return_false_if_timestamp_is_not_between_two_timestamps() {
final Timestamp start = getCurrentTime();
final Timestamp start = Timestamps.getCurrentTime();
final Timestamp finish = add(start, TEN_SECONDS);
final Timestamp timeNotBetween = add(finish, TEN_SECONDS);

Expand All @@ -172,7 +172,7 @@ public void return_false_if_timestamp_is_not_between_two_timestamps() {

@Test
public void return_true_if_timestamp_is_after_another_one() {
final Timestamp fromPoint = getCurrentTime();
final Timestamp fromPoint = Timestamps.getCurrentTime();
final Timestamp timeToCheck = add(fromPoint, TEN_SECONDS);

final boolean isAfter = Timestamps.isLaterThan(timeToCheck, fromPoint);
Expand All @@ -182,7 +182,7 @@ public void return_true_if_timestamp_is_after_another_one() {

@Test
public void return_false_if_timestamp_is_not_after_another_one() {
final Timestamp fromPoint = getCurrentTime();
final Timestamp fromPoint = Timestamps.getCurrentTime();
final Timestamp timeToCheck = subtract(fromPoint, TEN_SECONDS);

final boolean isAfter = Timestamps.isLaterThan(timeToCheck, fromPoint);
Expand All @@ -192,7 +192,7 @@ public void return_false_if_timestamp_is_not_after_another_one() {

@Test
public void compare_two_timestamps_using_comparator_return_negative_int_if_first_less_than_second_one() {
final Timestamp time1 = getCurrentTime();
final Timestamp time1 = Timestamps.getCurrentTime();
final Timestamp time2 = add(time1, TEN_SECONDS);

final int result = Timestamps.comparator()
Expand Down Expand Up @@ -222,7 +222,7 @@ public void compare_two_timestamps_using_comparator_return_zero_if_timestamps_ar

@Test
public void compare_two_timestamps_using_comparator_return_positive_int_if_first_greater_than_second_one() {
final Timestamp currentTime = getCurrentTime();
final Timestamp currentTime = Timestamps.getCurrentTime();
final Timestamp timeAfterCurrent = add(currentTime, TEN_SECONDS);

final int result = Timestamps.comparator()
Expand All @@ -234,7 +234,7 @@ public void compare_two_timestamps_using_comparator_return_positive_int_if_first
@Test
public void convert_timestamp_to_date_to_nearest_second() {

final Timestamp expectedTime = getCurrentTime();
final Timestamp expectedTime = Timestamps.getCurrentTime();

final Date actualDate = convertToDate(expectedTime);
final long actualSeconds = actualDate.getTime() / MILLIS_PER_SECOND;
Expand All @@ -244,7 +244,7 @@ public void convert_timestamp_to_date_to_nearest_second() {

@Test
public void convert_timestamp_to_nanos() {
final Timestamp expectedTime = getCurrentTime();
final Timestamp expectedTime = Timestamps.getCurrentTime();

final long nanos = convertToNanos(expectedTime);
final long expectedNanos = expectedTime.getSeconds() * NANOS_IN_SECOND + expectedTime.getNanos();
Expand Down
13 changes: 13 additions & 0 deletions client/src/test/java/org/spine3/util/TestsShould.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@

package org.spine3.util;

import com.google.protobuf.Timestamp;
import org.junit.Test;
import org.spine3.protobuf.Durations;
import org.spine3.protobuf.Timestamps;
import org.spine3.test.Tests;

import static com.google.protobuf.util.TimeUtil.subtract;
import static org.junit.Assert.*;
import static org.spine3.test.Tests.hasPrivateUtilityConstructor;

Expand Down Expand Up @@ -62,4 +66,13 @@ private ClassThrowingExceptionInConstructor() {
throw new AssertionError("Private constructor must not be called.");
}
}

@Test
public void have_frozen_time_provider() {
final Timestamp fiveMinutesAgo = subtract(Timestamps.getCurrentTime(), Durations.ofMinutes(5));

Timestamps.setProvider(new Tests.FrozenMadHatterParty(fiveMinutesAgo));

assertEquals(fiveMinutesAgo, Timestamps.getCurrentTime());
}
}
2 changes: 2 additions & 0 deletions testutil/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ dependencies {
// Depend on JUnit in the production part of the code, as this module exposes testing utilities
// that are based on JUnit.
compile group: 'junit', name: 'junit', version: '4.12'

compile project(path: ':client');
}

// required for usage of test classes from other modules
Expand Down
30 changes: 24 additions & 6 deletions testutil/src/main/java/org/spine3/test/Tests.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,19 @@

package org.spine3.test;

import com.google.protobuf.Timestamp;
import org.spine3.protobuf.Timestamps;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

import static java.lang.System.currentTimeMillis;

/**
* Utilities for testing.
*
* @author Alexander Yevsyukov
*/
public class Tests {

private static final long MSEC_IN_SECOND = 1000L;

private Tests() {}

/**
Expand Down Expand Up @@ -79,12 +78,12 @@ public static boolean hasPrivateUtilityConstructor(Class<?> utilityClass) {
}

/**
* Returns the current time in seconds.
* Returns the current time in seconds via {@link Timestamps#getCurrentTime()}.
*
* @return a seconds value
*/
public static long currentTimeSeconds() {
final long secs = currentTimeMillis() / MSEC_IN_SECOND;
final long secs = Timestamps.getCurrentTime().getSeconds();
return secs;
}

Expand All @@ -97,4 +96,23 @@ public static <T> T nullRef() {
//noinspection ConstantConditions
return nullRef;
}

/**
* The provider of current time, which is always the same.
*/
public static class FrozenMadHatterParty implements Timestamps.Provider {
private final Timestamp frozenTime;

public FrozenMadHatterParty(Timestamp frozenTime) {
this.frozenTime = frozenTime;
}

/**
* @return the value passed to the constructor
*/
@Override
public Timestamp getCurrentTime() {
return frozenTime;
}
}
}

0 comments on commit 3788a7d

Please sign in to comment.