From f8cec7db6e5afe17914c7d52a157eab778a03ffc Mon Sep 17 00:00:00 2001 From: Michael Kutz Date: Wed, 11 Sep 2024 18:07:58 +0200 Subject: [PATCH] Add RandomLocalTime --- .../org/stubit/random/RandomLocalDate.java | 40 ++--- .../org/stubit/random/RandomLocalTime.java | 167 ++++++++++++++++++ .../random/RandomLocalTimeBuilderTest.java | 108 +++++++++++ .../RandomLocalTimeInRangeBuilderTest.java | 68 +++++++ .../stubit/random/RandomLocalTimeTest.java | 25 +++ 5 files changed, 388 insertions(+), 20 deletions(-) create mode 100644 modules/random/src/main/java/org/stubit/random/RandomLocalTime.java create mode 100644 modules/random/src/test/java/org/stubit/random/RandomLocalTimeBuilderTest.java create mode 100644 modules/random/src/test/java/org/stubit/random/RandomLocalTimeInRangeBuilderTest.java create mode 100644 modules/random/src/test/java/org/stubit/random/RandomLocalTimeTest.java diff --git a/modules/random/src/main/java/org/stubit/random/RandomLocalDate.java b/modules/random/src/main/java/org/stubit/random/RandomLocalDate.java index 1c900c7..2d41d2a 100644 --- a/modules/random/src/main/java/org/stubit/random/RandomLocalDate.java +++ b/modules/random/src/main/java/org/stubit/random/RandomLocalDate.java @@ -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(); } /** @@ -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; } /** @@ -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; } diff --git a/modules/random/src/main/java/org/stubit/random/RandomLocalTime.java b/modules/random/src/main/java/org/stubit/random/RandomLocalTime.java new file mode 100644 index 0000000..5caa009 --- /dev/null +++ b/modules/random/src/main/java/org/stubit/random/RandomLocalTime.java @@ -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; + } + } +} diff --git a/modules/random/src/test/java/org/stubit/random/RandomLocalTimeBuilderTest.java b/modules/random/src/test/java/org/stubit/random/RandomLocalTimeBuilderTest.java new file mode 100644 index 0000000..d3b05ed --- /dev/null +++ b/modules/random/src/test/java/org/stubit/random/RandomLocalTimeBuilderTest.java @@ -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"); + } +} diff --git a/modules/random/src/test/java/org/stubit/random/RandomLocalTimeInRangeBuilderTest.java b/modules/random/src/test/java/org/stubit/random/RandomLocalTimeInRangeBuilderTest.java new file mode 100644 index 0000000..cc98b5a --- /dev/null +++ b/modules/random/src/test/java/org/stubit/random/RandomLocalTimeInRangeBuilderTest.java @@ -0,0 +1,68 @@ +package org.stubit.random; + +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.time.LocalTime; +import org.junit.jupiter.api.Test; + +class RandomLocalTimeInRangeBuilderTest { + + @Test + void aLocalTimeInRange() { + assertThat(RandomLocalTime.aLocalTimeInRange().build()).isBetween(LocalTime.MIN, LocalTime.MAX); + } + + @Test + void after() { + LocalTime min = LocalTime.of(12, 23, 45); + assertThat(RandomLocalTime.aLocalTimeInRange().after(min).build()).isAfterOrEqualTo(min); + } + + @Test + void after_before_VALUE() { + LocalTime min = LocalTime.MAX; + assertThat(RandomLocalTime.aLocalTimeInRange().after(min).build()).isEqualTo(min); + } + + @Test + void after_greater_than_before() { + LocalTime max = LocalTime.of(12, 23, 45); + LocalTime min = max.plusNanos(1); + assertThatIllegalArgumentException() + .isThrownBy(() -> RandomLocalTime.aLocalTimeInRange().before(max).after(min)) + .withMessage("Can't set after to %s, as it must not be greater than before (%s)", min, max); + } + + @Test + void before() { + LocalTime max = LocalTime.of(12, 23, 45); + assertThat(RandomLocalTime.aLocalTimeInRange().before(max).build()).isBeforeOrEqualTo(max); + } + + @Test + void before_after_VALUE() { + LocalTime max = LocalTime.MIN; + assertThat(RandomLocalTime.aLocalTimeInRange().before(max).build()).isEqualTo(max); + } + + @Test + void before_less_than_after() { + LocalTime after = LocalTime.of(12, 23, 45); + LocalTime before = after.minusNanos(1); + assertThatIllegalArgumentException() + .isThrownBy(() -> RandomLocalTime.aLocalTimeInRange().after(after).before(before)) + .withMessage( + "Can't set before to %s, as it must not be less than after (%s)", before, after); + } + + @Test + void future() { + assertThat(RandomLocalTime.aLocalTimeInRange().future().build()).isAfter(LocalTime.now()); + } + + @Test + void past() { + assertThat(RandomLocalTime.aLocalTimeInRange().past().build()).isBefore(LocalTime.now()); + } +} diff --git a/modules/random/src/test/java/org/stubit/random/RandomLocalTimeTest.java b/modules/random/src/test/java/org/stubit/random/RandomLocalTimeTest.java new file mode 100644 index 0000000..5bb3d02 --- /dev/null +++ b/modules/random/src/test/java/org/stubit/random/RandomLocalTimeTest.java @@ -0,0 +1,25 @@ +package org.stubit.random; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.LocalTime; +import org.junit.jupiter.api.Test; + +class RandomLocalTimeTest { + + @Test + void aLocalDateBetween_min_equal_to_max() { + LocalTime min = LocalTime.now(); + assertThat(RandomLocalTime.aLocalDateBetween(min, min)).isEqualTo(min); + } + + @Test + void aFutureLocalTime() { + assertThat(RandomLocalTime.aFutureLocalTime()).isAfter(LocalTime.now()); + } + + @Test + void aPastLocalTime() { + assertThat(RandomLocalTime.aPastLocalTime()).isBefore(LocalTime.now()); + } +}