Skip to content

Commit 3788a7d

Browse files
Merge pull request #149 from SpineEventEngine/time-provider
Time provider and publishing `testutil`
2 parents d595ef5 + fd2daea commit 3788a7d

File tree

6 files changed

+114
-21
lines changed

6 files changed

+114
-21
lines changed

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ allprojects {
1515
apply plugin: 'jacoco'
1616

1717
group = 'org.spine3'
18-
version = '0.4'
18+
version = '0.4.1-SNAPSHOT'
1919
}
2020

2121
project.ext {
@@ -162,7 +162,7 @@ void dependPublish(Project project) {
162162
}
163163

164164
// Artifacts to publish
165-
def publishingProjects = ["client", "server", "values"];
165+
def publishingProjects = ["client", "server", "values", "testutil"];
166166

167167
publishingProjects.each {
168168
project(":$it") { currentProject ->

client/src/main/java/org/spine3/protobuf/Timestamps.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@
2323
import com.google.protobuf.Timestamp;
2424
import com.google.protobuf.TimestampOrBuilder;
2525
import com.google.protobuf.util.TimeUtil;
26+
import org.spine3.Internal;
2627

2728
import javax.annotation.Nullable;
2829
import java.io.Serializable;
2930
import java.util.Comparator;
3031
import java.util.Date;
3132

3233
import static com.google.common.base.Preconditions.checkArgument;
34+
import static com.google.common.base.Preconditions.checkNotNull;
3335
import static com.google.protobuf.util.TimeUtil.*;
3436

3537
/**
@@ -97,6 +99,64 @@ private Timestamps() {
9799
*/
98100
public static final int HOURS_PER_DAY = 24;
99101

102+
private static final ThreadLocal<Provider> timeProvider = new ThreadLocal<Provider>() {
103+
@SuppressWarnings("RefusedBequest") // We want to provide our default value.
104+
@Override
105+
protected Provider initialValue() {
106+
return new SystemTimeProvider();
107+
}
108+
};
109+
110+
/**
111+
* Obtains current time.
112+
*
113+
* <p>This method should be used instead of {@link TimeUtil#getCurrentTime()} for testability
114+
* of time-related code.
115+
*
116+
* @return current time
117+
*/
118+
public static Timestamp getCurrentTime() {
119+
final Timestamp result = timeProvider.get()
120+
.getCurrentTime();
121+
return result;
122+
}
123+
124+
/**
125+
* The provider of the current time.
126+
*
127+
* <p>Implement this interface and pass the resulting class to
128+
*/
129+
@Internal
130+
public interface Provider {
131+
Timestamp getCurrentTime();
132+
}
133+
134+
/**
135+
* Sets provider of the current time.
136+
*
137+
* <p>The most common scenario for using this method is test cases of code that deals
138+
* with current time.
139+
*
140+
* @param provider the provider to set
141+
*/
142+
@Internal
143+
@VisibleForTesting
144+
public static void setProvider(Provider provider) {
145+
timeProvider.set(checkNotNull(provider));
146+
}
147+
148+
/**
149+
* Default implementation of current time provider based on {@link TimeUtil#getCurrentTime()}.
150+
*
151+
* <p>This is the only place, which should invoke obtaining current time from {@code TimeUtil} directly.
152+
*/
153+
private static class SystemTimeProvider implements Provider {
154+
@Override
155+
public Timestamp getCurrentTime() {
156+
return TimeUtil.getCurrentTime();
157+
}
158+
}
159+
100160
/**
101161
* Verifies if the passed {@code Timestamp} instance is valid.
102162
*

client/src/test/java/org/spine3/protobuf/TimestampsShould.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public void expose_some_private_constants_from_TimeUtil() {
7777

7878
@Test
7979
public void calculate_timestamp_of_moment_minute_ago_from_now() {
80-
final Timestamp currentTime = getCurrentTime();
80+
final Timestamp currentTime = Timestamps.getCurrentTime();
8181
final Timestamp expected = subtract(currentTime, MINUTE);
8282

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

8888
@Test
8989
public void compare_two_timestamps_return_negative_int_if_first_less_than_second_one() {
90-
final Timestamp time1 = getCurrentTime();
90+
final Timestamp time1 = Timestamps.getCurrentTime();
9191
final Timestamp time2 = add(time1, TEN_SECONDS);
9292

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

9898
@Test
9999
public void compare_two_timestamps_return_negative_int_if_first_is_null() {
100-
final Timestamp currentTime = getCurrentTime();
100+
final Timestamp currentTime = Timestamps.getCurrentTime();
101101

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

@@ -131,7 +131,7 @@ public void compare_two_timestamps_return_zero_if_pass_null() {
131131

132132
@Test
133133
public void compare_two_timestamps_return_positive_int_if_first_greater_than_second_one() {
134-
final Timestamp currentTime = getCurrentTime();
134+
final Timestamp currentTime = Timestamps.getCurrentTime();
135135
final Timestamp timeAfterCurrent = add(currentTime, TEN_SECONDS);
136136

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

142142
@Test
143143
public void compare_two_timestamps_return_positive_int_if_second_one_is_null() {
144-
final Timestamp currentTime = getCurrentTime();
144+
final Timestamp currentTime = Timestamps.getCurrentTime();
145145

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

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

151151
@Test
152152
public void return_true_if_timestamp_is_between_two_timestamps() {
153-
final Timestamp start = getCurrentTime();
153+
final Timestamp start = Timestamps.getCurrentTime();
154154
final Timestamp timeBetween = add(start, TEN_SECONDS);
155155
final Timestamp finish = add(timeBetween, TEN_SECONDS);
156156

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

162162
@Test
163163
public void return_false_if_timestamp_is_not_between_two_timestamps() {
164-
final Timestamp start = getCurrentTime();
164+
final Timestamp start = Timestamps.getCurrentTime();
165165
final Timestamp finish = add(start, TEN_SECONDS);
166166
final Timestamp timeNotBetween = add(finish, TEN_SECONDS);
167167

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

173173
@Test
174174
public void return_true_if_timestamp_is_after_another_one() {
175-
final Timestamp fromPoint = getCurrentTime();
175+
final Timestamp fromPoint = Timestamps.getCurrentTime();
176176
final Timestamp timeToCheck = add(fromPoint, TEN_SECONDS);
177177

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

183183
@Test
184184
public void return_false_if_timestamp_is_not_after_another_one() {
185-
final Timestamp fromPoint = getCurrentTime();
185+
final Timestamp fromPoint = Timestamps.getCurrentTime();
186186
final Timestamp timeToCheck = subtract(fromPoint, TEN_SECONDS);
187187

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

193193
@Test
194194
public void compare_two_timestamps_using_comparator_return_negative_int_if_first_less_than_second_one() {
195-
final Timestamp time1 = getCurrentTime();
195+
final Timestamp time1 = Timestamps.getCurrentTime();
196196
final Timestamp time2 = add(time1, TEN_SECONDS);
197197

198198
final int result = Timestamps.comparator()
@@ -222,7 +222,7 @@ public void compare_two_timestamps_using_comparator_return_zero_if_timestamps_ar
222222

223223
@Test
224224
public void compare_two_timestamps_using_comparator_return_positive_int_if_first_greater_than_second_one() {
225-
final Timestamp currentTime = getCurrentTime();
225+
final Timestamp currentTime = Timestamps.getCurrentTime();
226226
final Timestamp timeAfterCurrent = add(currentTime, TEN_SECONDS);
227227

228228
final int result = Timestamps.comparator()
@@ -234,7 +234,7 @@ public void compare_two_timestamps_using_comparator_return_positive_int_if_first
234234
@Test
235235
public void convert_timestamp_to_date_to_nearest_second() {
236236

237-
final Timestamp expectedTime = getCurrentTime();
237+
final Timestamp expectedTime = Timestamps.getCurrentTime();
238238

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

245245
@Test
246246
public void convert_timestamp_to_nanos() {
247-
final Timestamp expectedTime = getCurrentTime();
247+
final Timestamp expectedTime = Timestamps.getCurrentTime();
248248

249249
final long nanos = convertToNanos(expectedTime);
250250
final long expectedNanos = expectedTime.getSeconds() * NANOS_IN_SECOND + expectedTime.getNanos();

client/src/test/java/org/spine3/util/TestsShould.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@
2020

2121
package org.spine3.util;
2222

23+
import com.google.protobuf.Timestamp;
2324
import org.junit.Test;
25+
import org.spine3.protobuf.Durations;
26+
import org.spine3.protobuf.Timestamps;
2427
import org.spine3.test.Tests;
2528

29+
import static com.google.protobuf.util.TimeUtil.subtract;
2630
import static org.junit.Assert.*;
2731
import static org.spine3.test.Tests.hasPrivateUtilityConstructor;
2832

@@ -62,4 +66,13 @@ private ClassThrowingExceptionInConstructor() {
6266
throw new AssertionError("Private constructor must not be called.");
6367
}
6468
}
69+
70+
@Test
71+
public void have_frozen_time_provider() {
72+
final Timestamp fiveMinutesAgo = subtract(Timestamps.getCurrentTime(), Durations.ofMinutes(5));
73+
74+
Timestamps.setProvider(new Tests.FrozenMadHatterParty(fiveMinutesAgo));
75+
76+
assertEquals(fiveMinutesAgo, Timestamps.getCurrentTime());
77+
}
6578
}

testutil/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ dependencies {
33
// Depend on JUnit in the production part of the code, as this module exposes testing utilities
44
// that are based on JUnit.
55
compile group: 'junit', name: 'junit', version: '4.12'
6+
7+
compile project(path: ':client');
68
}
79

810
// required for usage of test classes from other modules

testutil/src/main/java/org/spine3/test/Tests.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,19 @@
2020

2121
package org.spine3.test;
2222

23+
import com.google.protobuf.Timestamp;
24+
import org.spine3.protobuf.Timestamps;
25+
2326
import java.lang.reflect.Constructor;
2427
import java.lang.reflect.Modifier;
2528

26-
import static java.lang.System.currentTimeMillis;
27-
2829
/**
2930
* Utilities for testing.
3031
*
3132
* @author Alexander Yevsyukov
3233
*/
3334
public class Tests {
3435

35-
private static final long MSEC_IN_SECOND = 1000L;
36-
3736
private Tests() {}
3837

3938
/**
@@ -79,12 +78,12 @@ public static boolean hasPrivateUtilityConstructor(Class<?> utilityClass) {
7978
}
8079

8180
/**
82-
* Returns the current time in seconds.
81+
* Returns the current time in seconds via {@link Timestamps#getCurrentTime()}.
8382
*
8483
* @return a seconds value
8584
*/
8685
public static long currentTimeSeconds() {
87-
final long secs = currentTimeMillis() / MSEC_IN_SECOND;
86+
final long secs = Timestamps.getCurrentTime().getSeconds();
8887
return secs;
8988
}
9089

@@ -97,4 +96,23 @@ public static <T> T nullRef() {
9796
//noinspection ConstantConditions
9897
return nullRef;
9998
}
99+
100+
/**
101+
* The provider of current time, which is always the same.
102+
*/
103+
public static class FrozenMadHatterParty implements Timestamps.Provider {
104+
private final Timestamp frozenTime;
105+
106+
public FrozenMadHatterParty(Timestamp frozenTime) {
107+
this.frozenTime = frozenTime;
108+
}
109+
110+
/**
111+
* @return the value passed to the constructor
112+
*/
113+
@Override
114+
public Timestamp getCurrentTime() {
115+
return frozenTime;
116+
}
117+
}
100118
}

0 commit comments

Comments
 (0)