Skip to content
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
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;
}
}
}