-
-
Notifications
You must be signed in to change notification settings - Fork 22
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
7 changed files
with
454 additions
and
145 deletions.
There are no files selected for viewing
46 changes: 46 additions & 0 deletions
46
jollyday-tests/src/test/java/de/focus_shift/jollyday/tests/CalendarCheckerApi.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,46 @@ | ||
package de.focus_shift.jollyday.tests; | ||
|
||
import de.focus_shift.jollyday.core.HolidayCalendar; | ||
import de.focus_shift.jollyday.core.HolidayType; | ||
|
||
import java.time.DayOfWeek; | ||
import java.time.Month; | ||
import java.time.Year; | ||
|
||
public interface CalendarCheckerApi { | ||
|
||
interface Holiday { | ||
Properties hasFixedHoliday(final String propertyKey, final Month month, final int day); | ||
Properties hasFixedHoliday(final String propertyKey, final Month month, final int day, final HolidayType type); | ||
|
||
Properties hasChristianHoliday(final String propertyKey); | ||
Properties hasChristianHoliday(final String propertyKey, final HolidayType type); | ||
|
||
Properties hasIslamicHoliday(final String propertyKey); | ||
Properties hasIslamicHoliday(final String propertyKey, final HolidayType type); | ||
} | ||
|
||
interface Properties extends Subdivision, Between, Shift, Check { | ||
} | ||
|
||
interface Subdivision extends Check { | ||
Properties inSubdivision(final String... subdivisions); | ||
} | ||
|
||
interface Between extends Check { | ||
Properties between(Year from, Year to); | ||
} | ||
|
||
interface Shift extends Check { | ||
Properties canBeenShiftedFrom(DayOfWeek from, DayOfWeek to); | ||
} | ||
|
||
interface Check { | ||
Holiday and(); | ||
void check(); | ||
} | ||
|
||
static CalendarCheckerFluent assertFor(final HolidayCalendar calendar) { | ||
return new CalendarCheckerFluent(calendar); | ||
} | ||
} |
322 changes: 322 additions & 0 deletions
322
jollyday-tests/src/test/java/de/focus_shift/jollyday/tests/CalendarCheckerFluent.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,322 @@ | ||
package de.focus_shift.jollyday.tests; | ||
|
||
import de.focus_shift.jollyday.core.Holiday; | ||
import de.focus_shift.jollyday.core.HolidayCalendar; | ||
import de.focus_shift.jollyday.core.HolidayManager; | ||
import de.focus_shift.jollyday.core.HolidayType; | ||
import net.jqwik.api.Arbitraries; | ||
import net.jqwik.time.api.arbitraries.YearArbitrary; | ||
import org.junit.jupiter.api.Assertions; | ||
|
||
import java.time.DayOfWeek; | ||
import java.time.LocalDate; | ||
import java.time.Month; | ||
import java.time.Year; | ||
import java.time.temporal.TemporalAdjusters; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
|
||
import static de.focus_shift.jollyday.core.ManagerParameters.create; | ||
import static de.focus_shift.jollyday.tests.CalendarCheckerFluent.Category.BY_DAY; | ||
import static de.focus_shift.jollyday.tests.CalendarCheckerFluent.Category.BY_KEY; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
public class CalendarCheckerFluent implements CalendarCheckerApi.Holiday, CalendarCheckerApi.Between, CalendarCheckerApi.Properties { | ||
|
||
enum Category { | ||
BY_DAY, | ||
BY_KEY | ||
} | ||
|
||
private final HolidayCalendar calendar; | ||
private String propertyKey; | ||
private Month month; | ||
private int day; | ||
private HolidayType type; | ||
private Category category; | ||
private String[] subdivisions = new String[]{}; | ||
private List<YearRange> validRanges = new ArrayList<>(); | ||
private List<WeekDayFromTo> validShifts = new ArrayList<>(); | ||
|
||
private final List<HolidayCalendarCheck> checks = new ArrayList<>(); | ||
|
||
public CalendarCheckerFluent(HolidayCalendar calendar) { | ||
this.calendar = calendar; | ||
} | ||
|
||
|
||
@Override | ||
public CalendarCheckerApi.Properties hasChristianHoliday(final String propertyKey) { | ||
return hasChristianHoliday(propertyKey, HolidayType.PUBLIC_HOLIDAY); | ||
} | ||
|
||
@Override | ||
public CalendarCheckerApi.Properties hasChristianHoliday(final String propertyKey, final HolidayType type) { | ||
Objects.requireNonNull(propertyKey, "propertyKey is required"); | ||
Objects.requireNonNull(type, "holiday type is required"); | ||
|
||
this.category = BY_KEY; | ||
this.propertyKey = "christian." + propertyKey; | ||
this.type = type; | ||
|
||
return this; | ||
} | ||
|
||
@Override | ||
public CalendarCheckerApi.Properties hasIslamicHoliday(final String propertyKey) { | ||
return hasIslamicHoliday(propertyKey, HolidayType.PUBLIC_HOLIDAY); | ||
} | ||
|
||
@Override | ||
public CalendarCheckerApi.Properties hasIslamicHoliday(final String propertyKey, final HolidayType type) { | ||
Objects.requireNonNull(propertyKey, "propertyKey is required"); | ||
Objects.requireNonNull(type, "holiday type is required"); | ||
|
||
this.category = BY_KEY; | ||
this.propertyKey = "islamic." + propertyKey; | ||
this.type = type; | ||
|
||
return this; | ||
} | ||
|
||
@Override | ||
public CalendarCheckerApi.Properties hasFixedHoliday(final String propertyKey, final Month month, final int day) { | ||
return hasFixedHoliday(propertyKey, month, day, HolidayType.PUBLIC_HOLIDAY); | ||
} | ||
|
||
@Override | ||
public CalendarCheckerApi.Properties hasFixedHoliday(final String propertyKey, final Month month, final int day, final HolidayType type) { | ||
|
||
Objects.requireNonNull(propertyKey, "propertyKey is required"); | ||
Objects.requireNonNull(month, "month is required"); | ||
if (day >= 32 || day <= 0) { | ||
throw new IllegalArgumentException("day must be between 1 and 31"); | ||
} | ||
Objects.requireNonNull(type, "holiday type is required"); | ||
|
||
this.category = BY_DAY; | ||
this.propertyKey = propertyKey; | ||
this.month = month; | ||
this.day = day; | ||
this.type = type; | ||
|
||
return this; | ||
} | ||
|
||
@Override | ||
public CalendarCheckerApi.Properties between(final Year from, Year to) { | ||
this.validRanges.add(new YearRange(from, to)); | ||
return this; | ||
} | ||
|
||
@Override | ||
public CalendarCheckerApi.Properties inSubdivision(String... subdivisions) { | ||
this.subdivisions = subdivisions; | ||
return this; | ||
} | ||
|
||
@Override | ||
public CalendarCheckerApi.Properties canBeenShiftedFrom(DayOfWeek from, DayOfWeek to) { | ||
this.validShifts.add(new WeekDayFromTo(from, to)); | ||
return this; | ||
} | ||
|
||
|
||
@Override | ||
public CalendarCheckerApi.Holiday and() { | ||
checks.add(new HolidayCalendarCheck(this.calendar, this.propertyKey, this.month, this.day, this.type, this.validRanges, this.validShifts, this.subdivisions, this.category)); | ||
|
||
clearProperties(); | ||
|
||
return this; | ||
} | ||
|
||
private void clearProperties() { | ||
this.propertyKey = null; | ||
this.month = null; | ||
this.day = 0; | ||
this.type = null; | ||
this.category = null; | ||
this.subdivisions = new String[]{}; | ||
this.validRanges = new ArrayList<>(); | ||
this.validShifts = new ArrayList<>(); | ||
} | ||
|
||
@Override | ||
public void check() { | ||
checks.add(new HolidayCalendarCheck(calendar, propertyKey, month, day, type, validRanges, validShifts, subdivisions, category)); | ||
|
||
clearProperties(); | ||
|
||
for (HolidayCalendarCheck check : checks) { | ||
switch (check.category) { | ||
case BY_DAY: | ||
checkByDate(check); | ||
break; | ||
case BY_KEY: | ||
checkByKey(check); | ||
break; | ||
default: | ||
throw new IllegalStateException("Unexpected value: " + check.category); | ||
} | ||
} | ||
|
||
this.checks.clear(); | ||
} | ||
|
||
private void checkByDate(HolidayCalendarCheck check) { | ||
final HolidayManager holidayManager = HolidayManager.getInstance(create(check.calendar)); | ||
|
||
for (final YearRange validRange : check.getValidRanges()) { | ||
createYearArbitrary() | ||
.between(validRange.getFrom().getValue(), validRange.getTo().getValue()) | ||
.forEachValue(year -> { | ||
final Set<Holiday> holidays = holidayManager.getHolidays(year, check.getSubdivisions()); | ||
|
||
LocalDate date = LocalDate.of(year.getValue(), check.getMonth(), check.getDay()); | ||
if (!check.validShifts.isEmpty()) { | ||
for (WeekDayFromTo shift : check.validShifts) { | ||
if (date.getDayOfWeek().equals(shift.getFrom())) { | ||
date = date.with(TemporalAdjusters.nextOrSame(shift.getTo())); | ||
} | ||
} | ||
} | ||
|
||
assertThat(holidays) | ||
.isNotEmpty() | ||
.contains(new Holiday(date, check.getPropertiesKey(), check.getHolidayType())); | ||
} | ||
); | ||
} | ||
} | ||
|
||
private void checkByKey(HolidayCalendarCheck check) { | ||
final HolidayManager holidayManager = HolidayManager.getInstance(create(check.calendar)); | ||
|
||
for (final YearRange validRange : check.getValidRanges()) { | ||
createYearArbitrary() | ||
.between(validRange.getFrom().getValue(), validRange.getTo().getValue()) | ||
.forEachValue(year -> { | ||
final Set<Holiday> holidays = holidayManager.getHolidays(year, check.getSubdivisions()); | ||
assertThat(holidays) | ||
.isNotEmpty() | ||
.filteredOn(holiday -> holiday.getPropertiesKey().equals(check.getPropertiesKey())) | ||
.extracting(Holiday::getType) | ||
.contains(check.getHolidayType()); | ||
} | ||
); | ||
} | ||
} | ||
|
||
private static YearArbitrary createYearArbitrary() { | ||
return (YearArbitrary) Arbitraries.defaultFor(Year.class); | ||
} | ||
|
||
private static final class HolidayCalendarCheck { | ||
|
||
private final HolidayCalendar calendar; | ||
private final List<YearRange> validRanges; | ||
private final List<WeekDayFromTo> validShifts; | ||
private final Month month; | ||
private final int day; | ||
private final String propertiesKey; | ||
private final HolidayType holidayType; | ||
private final String[] subdivisions; | ||
private final Category category; | ||
|
||
HolidayCalendarCheck(HolidayCalendar calendar, | ||
String propertiesKey, Month month, int day, HolidayType holidayType, | ||
List<YearRange> validRanges, List<WeekDayFromTo> validShifts, String[] subdivisions, Category category | ||
) { | ||
this.calendar = calendar; | ||
this.propertiesKey = propertiesKey; | ||
this.month = month; | ||
this.day = day; | ||
this.holidayType = holidayType; | ||
this.validRanges = validRanges.isEmpty() ? List.of(new YearRange(Year.of(1900), Year.of(2500))) : Collections.unmodifiableList(validRanges); | ||
this.validShifts = Collections.unmodifiableList(validShifts); | ||
this.subdivisions = subdivisions; | ||
this.category = category; | ||
} | ||
|
||
public HolidayCalendar getCalendar() { | ||
return calendar; | ||
} | ||
|
||
public List<YearRange> getValidRanges() { | ||
return validRanges; | ||
} | ||
|
||
public List<WeekDayFromTo> getValidShifts() { | ||
return validShifts; | ||
} | ||
|
||
public Month getMonth() { | ||
return month; | ||
} | ||
|
||
public int getDay() { | ||
return day; | ||
} | ||
|
||
public String getPropertiesKey() { | ||
return propertiesKey; | ||
} | ||
|
||
public HolidayType getHolidayType() { | ||
return holidayType; | ||
} | ||
|
||
public String[] getSubdivisions() { | ||
return subdivisions; | ||
} | ||
|
||
public Category getCategory() { | ||
return category; | ||
} | ||
} | ||
|
||
private static class YearRange { | ||
|
||
private final Year from; | ||
private final Year to; | ||
|
||
YearRange(final Year from, final Year to) { | ||
if (from != null && to != null) { | ||
Assertions.assertFalse(from.isAfter(to), "To must be greater than or equal to the from year."); | ||
} | ||
this.from = from; | ||
this.to = to; | ||
} | ||
|
||
public Year getFrom() { | ||
return from; | ||
} | ||
|
||
public Year getTo() { | ||
return to; | ||
} | ||
} | ||
|
||
private static class WeekDayFromTo { | ||
|
||
private final DayOfWeek from; | ||
private final DayOfWeek to; | ||
|
||
public WeekDayFromTo(final DayOfWeek from, final DayOfWeek to) { | ||
this.from = from; | ||
this.to = to; | ||
} | ||
|
||
public DayOfWeek getFrom() { | ||
return from; | ||
} | ||
|
||
public DayOfWeek getTo() { | ||
return to; | ||
} | ||
} | ||
} |
Oops, something went wrong.