diff --git a/core/src/main/java/com/tngtech/valueprovider/AbstractValueProvider.java b/core/src/main/java/com/tngtech/valueprovider/AbstractValueProvider.java index c87bf94..1120661 100644 --- a/core/src/main/java/com/tngtech/valueprovider/AbstractValueProvider.java +++ b/core/src/main/java/com/tngtech/valueprovider/AbstractValueProvider.java @@ -10,7 +10,6 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; -import java.time.Month; import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; @@ -585,18 +584,57 @@ public LocalDate fixedLocalDate() { } /** - * Draws a {@link LocalDate} in [1st of January of {@code begin} ; 31st of December of {@code end}]. + + * Draws a {@link LocalDate} in [1st of January of {@code startYear} ; 31st of December of {@code endYear}]. + * + * @param startYear the earliest year to draw from. + * @param endYear the latest year to draw from. + * @return the drawn {@link LocalDate}. + * @throws IllegalArgumentException if {@code endYear} < {@code startYear}. + */ + public LocalDate localDateBetweenYears(int startYear, int endYear) { + LocalDate start = LocalDate.ofYearDay(startYear, 1); + LocalDate end = LocalDate.of(endYear, 12, 31); + return localDateBetween(start, end); + } + + /** + * Draws a {@link LocalDate} in [{@code start} ; {@code end}]. + * + * @param start the earliest date to draw from. + * @param end the latest date to draw from. + * @return the drawn {@link LocalDate}. + * @throws IllegalArgumentException if {@code start} > {@code end}. + */ + public LocalDate localDateBetween(LocalDate start, LocalDate end) { + checkArgument(beforeOrEqual(start, end), "start %s must be before or equal end %s", start, end); + return start.plusDays(longNumber(0, DAYS.between(start, end))); + } + + private boolean beforeOrEqual(LocalDate start, LocalDate end) { + return start.isBefore(end) || start.isEqual(end); + } + + /** + * Draws a {@link LocalDate} in [{@code today-pastDuration} ; {@code today}]. * - * @param begin minimum year to draw from. - * @param end maximum year to draw from. + * @param pastDuration the maximum duration in the past to consider drawing from. * @return the drawn {@link LocalDate}. - * @throws IllegalArgumentException if {@code end} < {@code begin}. */ - public LocalDate localDateBetweenYears(int begin, int end) { - checkArgument(begin <= end, "begin %s must be before end %s", begin, end); - LocalDate lower = LocalDate.of(begin, Month.JANUARY, 1); - LocalDate upper = LocalDate.of(end, Month.DECEMBER, 31); - return lower.plusDays(longNumber(0, DAYS.between(lower, upper))); + public LocalDate localDateInPast(Duration pastDuration) { + LocalDate start = fixedLocalDate().minusDays(pastDuration.toDays()); + return localDateBetween(start, fixedLocalDate()); + } + + /** + * Draws a {@link LocalDate} in [{@code today} ; {@code today+futureDuration}]. + * + * @param futureDuration the maximum duration in the future to consider drawing from. + * @return the drawn {@link LocalDate}. + */ + public LocalDate localDateInFuture(Duration futureDuration) { + LocalDate end = fixedLocalDate().plusDays(futureDuration.toDays()); + return localDateBetween(fixedLocalDate(), end); } /** @@ -609,6 +647,58 @@ public LocalTime localTime() { return LocalTime.ofSecondOfDay(longNumber(0, secondsPerDay - 1)); } + /** + * Draws a {@link LocalDateTime} in [{@code start} ; {@code end}]. + * + * @param start the earliest date time to draw from. + * @param end the latest date time to draw from. + * @return the drawn {@link LocalDateTime}. + * @throws IllegalArgumentException if {@code start} > {@code end}. + */ + public LocalDateTime localDateTimeBetween(LocalDateTime start, LocalDateTime end) { + checkArgument(beforeOrEqual(start, end), "start %s must be before or equal end %s", start, end); + return start.plusSeconds(longNumber(0, SECONDS.between(start, end))); + } + + private boolean beforeOrEqual(LocalDateTime start, LocalDateTime end) { + return start.isBefore(end) || start.isEqual(end); + } + + /** + * Draws a {@link LocalDateTime} in [{@code start} T00:00:00 ; {@code end} T23:59:59]. + * + * @param start the earliest date to draw from. + * @param end the latest date to draw from. + * @return the drawn {@link LocalDateTime}. + * @throws IllegalArgumentException if {@code start} > {@code end}. + */ + public LocalDateTime localDateTimeBetween(LocalDate start, LocalDate end) { + return localDateTimeBetween(start.atStartOfDay(), end.atTime(23, 59, 59)); + } + + /** + * Generates a {@link Duration} in [0 ; {@code max}]. + * + * @param max the upper bound of the generated {@link Duration}. + * @return the generated {@link Duration}. + */ + public Duration duration(Duration max) { + return Duration.ofMillis(longNumber(0, max.toMillis())); + } + + /** + * Generates a {@link Duration} in [{@code min} ; {@code max}]. + * + * @param min the lower bound of the generated {@link Duration}. + * @param max the upper bound of the generated {@link Duration}. + * @return the generated {@link Duration}. + * @throws IllegalArgumentException if {@code min} > {@code max}. + */ + public Duration duration(Duration min, Duration max) { + checkArgument(min.compareTo(max) <= 0, "min %s must be less than or equal max %s", min, max); + return Duration.ofMillis(longNumber(min.toMillis(), max.toMillis())); + } + /** * Draws one element from the elements of an Enum class. *

diff --git a/core/src/test/java/com/tngtech/valueprovider/ValueProviderTest.java b/core/src/test/java/com/tngtech/valueprovider/ValueProviderTest.java index 831c7d7..c352b0a 100644 --- a/core/src/test/java/com/tngtech/valueprovider/ValueProviderTest.java +++ b/core/src/test/java/com/tngtech/valueprovider/ValueProviderTest.java @@ -7,7 +7,13 @@ import java.net.InetAddress; import java.net.URL; import java.net.UnknownHostException; +import java.time.Duration; +import java.time.LocalDate; +import java.time.Duration; +import java.time.Duration; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; @@ -21,6 +27,7 @@ import java.util.stream.Stream; import lombok.Data; + import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; @@ -94,7 +101,16 @@ private static Collection allMethodInvocations() { String address = random.lowercaseString(stringLength); int min = random.intNumber(100, 1000); int max = random.intNumber(1001, 2000); + LocalDate start = random.localDateBetweenYears(min, max); + LocalDate end = random.localDateBetweenYears( + random.intNumber(3000, 4000), + random.intNumber(4001, 10000) + ); + Duration duration = Duration.ofDays(Long.valueOf(min)); + LocalTime time = random.localTime(); int numLines = random.intNumber(2, 10); + Duration minDuration = Duration.ofHours(1); + Duration maxDuration = Duration.ofDays(1); return newArrayList( invoke("booleanValue"), @@ -123,7 +139,14 @@ private static Collection allMethodInvocations() { invoke("fixedLocalDate"), invoke("localDateBetweenYears", min, max), invoke("localTime"), + invoke("duration", maxDuration), + invoke("duration", minDuration, maxDuration), invoke("fixedLocalDateTime"), + invoke("localDateBetween", start, end), + invoke("localDateInPast", duration), + invoke("localDateInFuture", duration), + invoke("localDateTimeBetween", LocalDateTime.of(start, time), LocalDateTime.of(end, time)), + invoke("localDateTimeBetween", start, end), invoke("fixedDecoratedStrings", numLines), invoke("fixedDecoratedStrings", numLines, random.randomString(stringLength)) ); @@ -538,6 +561,74 @@ void numericString_should_reject_too_small_length_wrt_min() { assertThat(thrown.getMessage()).contains("" + tooSmallLength, "" + min); } + @Test + void localDateInBetween_should_generate_value_in_between() { + LocalDate start = LocalDate.of(2000, 1, 1); + LocalDate end = LocalDate.of(2040, 12, 31); + assertThat(withRandomValues().localDateBetween(start, end)).isAfterOrEqualTo(start) + .isBeforeOrEqualTo(end); + } + + @Test + void localDateInBetween_should_reject_temporal_inconsistency() { + LocalDate start = LocalDate.of(2000, 1, 1); + LocalDate end = LocalDate.of(2040, 12, 31); + Throwable thrown = assertThrows(IllegalArgumentException.class, + () -> withRandomValues().localDateBetween(end, start)); + assertThat(thrown.getMessage()).contains("must be before or equal end "); + } + + @Test + void localDateInPast_should_return_value_in_allowed_range() { + LocalDate today = LocalDate.now(); + Duration duration = Duration.ofDays(300); + LocalDate pastDate = today.minusDays(duration.toDays()); + assertThat(withRandomValues().localDateInPast(duration)).isAfterOrEqualTo(pastDate) + .isBeforeOrEqualTo(today); + } + + @Test + void localDateInFuture_should_return_value_in_allowed_range() { + LocalDate today = LocalDate.now(); + Duration duration = Duration.ofDays(300); + LocalDate futureDate = today.plusDays(duration.toDays()); + assertThat(withRandomValues().localDateInFuture(duration)).isAfterOrEqualTo(today) + .isBeforeOrEqualTo(futureDate); + } + + @Test + void localDateTimeInBetween_should_generate_value_in_between() { + LocalDate start = LocalDate.of(2000, 1, 1); + LocalDate end = LocalDate.of(2040, 12, 31); + assertThat(withRandomValues().localDateTimeBetween(start, end)) + .isAfterOrEqualTo(LocalDateTime.of(start, LocalTime.MIN)) + .isBeforeOrEqualTo(LocalDateTime.of(end, LocalTime.MAX)); + } + + @Test + void localDateTimeInBetween_should_reject_temporal_inconsistency() { + LocalDate start = LocalDate.of(2000, 1, 1); + LocalDate end = LocalDate.of(2040, 12, 31); + Throwable thrown = assertThrows(IllegalArgumentException.class, + () -> withRandomValues().localDateTimeBetween(end, start)); + assertThat(thrown.getMessage()).contains("must be before or equal end "); + } + + @Test + void duration_should_be_smaller_than_max_duration() { + ValueProvider random = withRandomValues(); + assertThat(random.duration(Duration.ofDays(1))) + .isLessThanOrEqualTo(Duration.ofDays(1)); + } + + @Test + void duration_should_be_bigger_than_min_and_smaller_than_max_duration() { + ValueProvider random = withRandomValues(); + assertThat(random.duration(Duration.ofMinutes(1), Duration.ofDays(1))) + .isGreaterThanOrEqualTo(Duration.ofMinutes(1)) + .isLessThanOrEqualTo(Duration.ofDays(1)); + } + @Test void oneOf_should_return_single_value() { ValueProvider random = withRandomValues();