-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
388 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
167 changes: 167 additions & 0 deletions
167
modules/random/src/main/java/org/stubit/random/RandomLocalTime.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package org.stubit.random; | ||
|
||
import java.security.SecureRandom; | ||
import java.time.LocalTime; | ||
import org.jspecify.annotations.NullMarked; | ||
|
||
@NullMarked | ||
public class RandomLocalTime { | ||
|
||
private RandomLocalTime() {} | ||
|
||
/** | ||
* @param after the minimum value (inclusive) | ||
* @param before the maximum value (inclusive) | ||
* @return a random {@link LocalTime} between {@code after} and {@code before} | ||
*/ | ||
public static LocalTime aLocalDateBetween(LocalTime after, LocalTime before) { | ||
return aLocalTimeInRange().after(after).before(before).build(); | ||
} | ||
|
||
/** | ||
* @return a random {@link LocalTime} between {@link LocalTime#MIN} and now | ||
*/ | ||
public static LocalTime aPastLocalTime() { | ||
return aLocalTimeInRange().before(LocalTime.now()).build(); | ||
} | ||
|
||
/** | ||
* @return a random {@link LocalTime} between now and {@link LocalTime#MAX} | ||
*/ | ||
public static LocalTime aFutureLocalTime() { | ||
return aLocalTimeInRange().after(LocalTime.now()).build(); | ||
} | ||
|
||
/** | ||
* @return a {@link RandomLocalTime.LocalTimeInRangeBuilder} to configure the random {@link | ||
* LocalTime} | ||
*/ | ||
public static LocalTimeInRangeBuilder aLocalTimeInRange() { | ||
return new LocalTimeInRangeBuilder(LocalTime.MIN, LocalTime.MAX); | ||
} | ||
|
||
/** | ||
* @return a {@link LocalTimeBuilder} to configure the random {@link LocalTime} | ||
*/ | ||
public static LocalTimeBuilder aLocalTime() { | ||
return new LocalTimeBuilder(); | ||
} | ||
|
||
/** Builds a random {@link LocalTime} within a specified range. */ | ||
public static class LocalTimeInRangeBuilder { | ||
|
||
private final SecureRandom secureRandom = new SecureRandom(); | ||
private LocalTime after; | ||
private LocalTime before; | ||
|
||
private LocalTimeInRangeBuilder(LocalTime after, LocalTime before) { | ||
this.after = after; | ||
this.before = before; | ||
} | ||
|
||
/** | ||
* Sets {@link #after} to {@link LocalTime#MIN} and {@link #before} to now. | ||
* | ||
* @return this | ||
*/ | ||
public LocalTimeInRangeBuilder past() { | ||
return after(LocalTime.MIN).before(LocalTime.now().minusSeconds(1)); | ||
} | ||
|
||
/** | ||
* Sets {@link #after} to now and {@link #before} to {@link LocalTime#MAX}. | ||
* | ||
* @return this | ||
*/ | ||
public LocalTimeInRangeBuilder future() { | ||
return after(LocalTime.now().plusSeconds(1)).before(LocalTime.MAX); | ||
} | ||
|
||
/** | ||
* @param after the minimum value (inclusive). | ||
* @return this | ||
*/ | ||
public LocalTimeInRangeBuilder after(LocalTime after) { | ||
if (after.isAfter(before)) { | ||
throw new IllegalArgumentException( | ||
"Can't set after to %s, as it must not be greater than before (%s)" | ||
.formatted(after, before)); | ||
} | ||
this.after = after; | ||
return this; | ||
} | ||
|
||
/** | ||
* @param before the minimum value (inclusive) | ||
* @return this builder | ||
*/ | ||
public LocalTimeInRangeBuilder before(LocalTime before) { | ||
if (before.isBefore(after)) { | ||
throw new IllegalArgumentException( | ||
"Can't set before to %s, as it must not be less than after (%s)" | ||
.formatted(before, after)); | ||
} | ||
this.before = before; | ||
return this; | ||
} | ||
|
||
/** | ||
* @return a random {@link LocalTime} between {@link #after} and {@link #before} | ||
*/ | ||
public LocalTime build() { | ||
long minDayNano = after.toNanoOfDay(); | ||
long maxDayNano = before.toNanoOfDay() + 1; | ||
long randomDayNano = minDayNano + secureRandom.nextLong(0, maxDayNano - minDayNano); | ||
return LocalTime.ofNanoOfDay(randomDayNano); | ||
} | ||
} | ||
|
||
/** Builds a random {@link LocalTime} with specified field values (e.g. hour, minute). */ | ||
public static class LocalTimeBuilder { | ||
|
||
private LocalTime localTime = RandomLocalTime.aLocalTimeInRange().build(); | ||
|
||
/** | ||
* @param hour the hour for the random {@link LocalTime} | ||
* @return this | ||
*/ | ||
public LocalTimeBuilder hour(int hour) { | ||
localTime = localTime.withHour(hour); | ||
return this; | ||
} | ||
|
||
/** | ||
* @param minute the minute for the random {@link LocalTime} | ||
* @return this | ||
*/ | ||
public LocalTimeBuilder minute(int minute) { | ||
localTime = localTime.withMinute(minute); | ||
return this; | ||
} | ||
|
||
/** | ||
* @param second the second for the random {@link LocalTime} | ||
* @return this | ||
*/ | ||
public LocalTimeBuilder second(int second) { | ||
localTime = localTime.withSecond(second); | ||
return this; | ||
} | ||
|
||
/** | ||
* @param nano the nanosecond for the random {@link LocalTime} | ||
* @return this | ||
*/ | ||
public LocalTimeBuilder nano(int nano) { | ||
localTime = localTime.withNano(nano); | ||
return this; | ||
} | ||
|
||
/** | ||
* @return the random {@link LocalTime} | ||
*/ | ||
public LocalTime build() { | ||
return localTime; | ||
} | ||
} | ||
} |
108 changes: 108 additions & 0 deletions
108
modules/random/src/test/java/org/stubit/random/RandomLocalTimeBuilderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package org.stubit.random; | ||
|
||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; | ||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat; | ||
|
||
import java.time.DateTimeException; | ||
import java.time.LocalTime; | ||
import org.junit.jupiter.api.Test; | ||
|
||
class RandomLocalTimeBuilderTest { | ||
|
||
@Test | ||
void aLocalTime() { | ||
assertThat(RandomLocalTime.aLocalTime().build()).isBetween(LocalTime.MIN, LocalTime.MAX); | ||
} | ||
|
||
@Test | ||
void hour() { | ||
int expectedHour = 23; | ||
LocalTime radomLocalTime = RandomLocalTime.aLocalTime().hour(expectedHour).build(); | ||
assertThat(radomLocalTime.getHour()).isEqualTo(expectedHour); | ||
} | ||
|
||
@Test | ||
void hour_negative() { | ||
var localTimeBuilder = RandomLocalTime.aLocalTime(); | ||
assertThatExceptionOfType(DateTimeException.class) | ||
.isThrownBy(() -> localTimeBuilder.hour(-1)) | ||
.withMessage("Invalid value for HourOfDay (valid values 0 - 23): -1"); | ||
} | ||
|
||
@Test | ||
void hour_too_big() { | ||
var localTimeBuilder = RandomLocalTime.aLocalTime(); | ||
assertThatExceptionOfType(DateTimeException.class) | ||
.isThrownBy(() -> localTimeBuilder.hour(24)) | ||
.withMessage("Invalid value for HourOfDay (valid values 0 - 23): 24"); | ||
} | ||
|
||
@Test | ||
void minute() { | ||
int expectedMinute = 59; | ||
LocalTime radomLocalTime = RandomLocalTime.aLocalTime().minute(expectedMinute).build(); | ||
assertThat(radomLocalTime.getMinute()).isEqualTo(expectedMinute); | ||
} | ||
|
||
@Test | ||
void minute_negative() { | ||
var localTimeBuilder = RandomLocalTime.aLocalTime(); | ||
assertThatExceptionOfType(DateTimeException.class) | ||
.isThrownBy(() -> localTimeBuilder.minute(-1)) | ||
.withMessage("Invalid value for MinuteOfHour (valid values 0 - 59): -1"); | ||
} | ||
|
||
@Test | ||
void minute_too_big() { | ||
var localTimeBuilder = RandomLocalTime.aLocalTime(); | ||
assertThatExceptionOfType(DateTimeException.class) | ||
.isThrownBy(() -> localTimeBuilder.minute(60)) | ||
.withMessage("Invalid value for MinuteOfHour (valid values 0 - 59): 60"); | ||
} | ||
|
||
@Test | ||
void second() { | ||
int expectedSecond = 59; | ||
LocalTime radomLocalTime = RandomLocalTime.aLocalTime().second(expectedSecond).build(); | ||
assertThat(radomLocalTime.getSecond()).isEqualTo(expectedSecond); | ||
} | ||
|
||
@Test | ||
void second_negative() { | ||
var localTimeBuilder = RandomLocalTime.aLocalTime(); | ||
assertThatExceptionOfType(DateTimeException.class) | ||
.isThrownBy(() -> localTimeBuilder.second(-1)) | ||
.withMessage("Invalid value for SecondOfMinute (valid values 0 - 59): -1"); | ||
} | ||
|
||
@Test | ||
void second_too_big() { | ||
var localTimeBuilder = RandomLocalTime.aLocalTime(); | ||
assertThatExceptionOfType(DateTimeException.class) | ||
.isThrownBy(() -> localTimeBuilder.second(60)) | ||
.withMessage("Invalid value for SecondOfMinute (valid values 0 - 59): 60"); | ||
} | ||
|
||
@Test | ||
void nano() { | ||
int expectedNano = 999999999; | ||
LocalTime radomLocalTime = RandomLocalTime.aLocalTime().nano(expectedNano).build(); | ||
assertThat(radomLocalTime.getNano()).isEqualTo(expectedNano); | ||
} | ||
|
||
@Test | ||
void nano_negative() { | ||
var localTimeBuilder = RandomLocalTime.aLocalTime(); | ||
assertThatExceptionOfType(DateTimeException.class) | ||
.isThrownBy(() -> localTimeBuilder.nano(-1)) | ||
.withMessage("Invalid value for NanoOfSecond (valid values 0 - 999999999): -1"); | ||
} | ||
|
||
@Test | ||
void nano_too_big() { | ||
var localTimeBuilder = RandomLocalTime.aLocalTime(); | ||
assertThatExceptionOfType(DateTimeException.class) | ||
.isThrownBy(() -> localTimeBuilder.nano(1000000000)) | ||
.withMessage("Invalid value for NanoOfSecond (valid values 0 - 999999999): 1000000000"); | ||
} | ||
} |
Oops, something went wrong.