Skip to content

Commit

Permalink
Add RandomLocalTime
Browse files Browse the repository at this point in the history
  • Loading branch information
mkutz committed Sep 11, 2024
1 parent c81e719 commit f8cec7d
Show file tree
Hide file tree
Showing 5 changed files with 388 additions and 20 deletions.
40 changes: 20 additions & 20 deletions modules/random/src/main/java/org/stubit/random/RandomLocalDate.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ public class RandomLocalDate {
private RandomLocalDate() {}

/**
* @param afterInclusive the minimum value (inclusive)
* @param beforeInclusive the maximum value (inclusive)
* @return a random {@link LocalDate} between {@code afterInclusive} and {@code beforeInclusive}
* @param after the minimum value (inclusive)
* @param before the maximum value (inclusive)
* @return a random {@link LocalDate} between {@code after} and {@code before}
*/
public static LocalDate aLocalDateBetween(LocalDate afterInclusive, LocalDate beforeInclusive) {
return aLocalDateInRange().after(afterInclusive).before(beforeInclusive).build();
public static LocalDate aLocalDateBetween(LocalDate after, LocalDate before) {
return aLocalDateInRange().after(after).before(before).build();
}

/**
Expand Down Expand Up @@ -57,9 +57,9 @@ public static class LocalDateInRangeBuilder {
private LocalDate after;
private LocalDate before;

private LocalDateInRangeBuilder(LocalDate min, LocalDate max) {
this.after = min;
this.before = max;
private LocalDateInRangeBuilder(LocalDate after, LocalDate before) {
this.after = after;
this.before = before;
}

/**
Expand All @@ -81,32 +81,32 @@ public LocalDateInRangeBuilder future() {
}

/**
* @param afterIncluding the minimum value (inclusive)
* @param after the minimum value (inclusive)
* @return this
* @throws IllegalArgumentException if {@code afterIncluding} is after {@link #before}
* @throws IllegalArgumentException if {@code after} is after {@link #before}
*/
public LocalDateInRangeBuilder after(LocalDate afterIncluding) {
if (afterIncluding.isAfter(before)) {
public LocalDateInRangeBuilder after(LocalDate after) {
if (after.isAfter(before)) {
throw new IllegalArgumentException(
"Can't set after to %s, as it must not be greater than before (%s)"
.formatted(afterIncluding, before));
.formatted(after, before));
}
this.after = afterIncluding;
this.after = after;
return this;
}

/**
* @param beforeIncluding the maximum value (inclusive)
* @param before the maximum value (inclusive)
* @return this
* @throws IllegalArgumentException if {@code beforeIncluding} is before {@link #after}
* @throws IllegalArgumentException if {@code before} is before {@link #after}
*/
public LocalDateInRangeBuilder before(LocalDate beforeIncluding) {
if (beforeIncluding.isBefore(after)) {
public LocalDateInRangeBuilder before(LocalDate before) {
if (before.isBefore(after)) {
throw new IllegalArgumentException(
"Can't set before to %s, as it must not be less than after (%s)"
.formatted(beforeIncluding, after));
.formatted(before, after));
}
this.before = beforeIncluding;
this.before = before;
return this;
}

Expand Down
167 changes: 167 additions & 0 deletions modules/random/src/main/java/org/stubit/random/RandomLocalTime.java
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;
}
}
}
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");
}
}
Loading

0 comments on commit f8cec7d

Please sign in to comment.