diff --git a/app/src/test/java/de/jeanlucmakiola/calendula/ui/week/PageNumberingTest.kt b/app/src/test/java/de/jeanlucmakiola/calendula/ui/week/PageNumberingTest.kt new file mode 100644 index 0000000..5e5e9bf --- /dev/null +++ b/app/src/test/java/de/jeanlucmakiola/calendula/ui/week/PageNumberingTest.kt @@ -0,0 +1,125 @@ +package de.jeanlucmakiola.calendula.ui.week + +import com.google.common.truth.Truth.assertThat +import kotlinx.datetime.DateTimeUnit +import kotlinx.datetime.DayOfWeek +import kotlinx.datetime.LocalDate +import kotlinx.datetime.daysUntil +import kotlinx.datetime.minus +import kotlinx.datetime.plus +import org.junit.jupiter.api.Test + +/** + * Pins each grid's page numbering to the formulas it shipped with: a pager + * restores its page by number, so a change here moves the user to another date. + */ +class PageNumberingTest { + + private val dates: List = buildList { + var d = LocalDate(1500, 1, 1) + while (d < LocalDate(2500, 1, 1)) { + add(d) + d = d.plus(37, DateTimeUnit.DAY) + } + var e = LocalDate(1999, 12, 1) + while (e < LocalDate(2000, 2, 1)) { + add(e) + e = e.plus(1, DateTimeUnit.DAY) + } + } + + private fun pages(count: Int): List = + listOf(-7, -1, 0, 1, count / 2 - 3, count / 2 - 1, count / 2, count / 2 + 1, count - 2, count - 1, count, count + 5) + + (0 until count step 997) + + @Test + fun `week pages keep their numbering for every first day`() { + DayOfWeek.entries.forEach { firstDay -> + val grid = WeekGrid(firstDay) + assertThat(grid.dayCount).isEqualTo(7) + assertThat(grid.pageCount).isEqualTo(LEGACY_WEEK_COUNT) + pages(LEGACY_WEEK_COUNT).forEach { page -> + assertThat(grid.startForPage(page)).isEqualTo(legacyWeekStart(page, firstDay)) + } + dates.forEach { date -> + assertThat(grid.pageFor(date)).isEqualTo(legacyWeekPage(date, firstDay)) + } + } + } + + @Test + fun `multi-day pages keep their numbering for every count and phase`() { + (1..7).forEach { count -> + (-count..count * 2).forEach { phase -> + val grid = DaysGrid(count, phase) + assertThat(grid.pageCount).isEqualTo(LEGACY_DAYS_COUNT) + pages(LEGACY_DAYS_COUNT).forEach { page -> + assertThat(grid.startForPage(page)).isEqualTo(legacyDaysStart(page, count, phase)) + } + dates.forEach { date -> + assertThat(grid.pageFor(date)).isEqualTo(legacyDaysPage(date, count, phase)) + } + } + dates.filter { it.year in 1950..2050 }.forEach { date -> + val grid = DaysGrid.startingAt(date, count) + assertThat(grid.startForPage(grid.pageFor(date))).isEqualTo(date) + } + } + } + + @Test + fun `day pages keep their numbering`() { + val grid = DayGrid + assertThat(grid.dayCount).isEqualTo(1) + assertThat(grid.pageCount).isEqualTo(LEGACY_DAY_COUNT) + pages(LEGACY_DAY_COUNT).forEach { page -> + assertThat(grid.startForPage(page)).isEqualTo(legacyDayStart(page)) + } + dates.forEach { date -> + assertThat(grid.pageFor(date)).isEqualTo(legacyDayPage(date)) + } + } + + @Test + fun `grids are equal exactly when they page the same way`() { + assertThat(WeekGrid(DayOfWeek.MONDAY)).isEqualTo(WeekGrid(DayOfWeek.MONDAY)) + assertThat(WeekGrid(DayOfWeek.MONDAY)).isNotEqualTo(WeekGrid(DayOfWeek.SUNDAY)) + assertThat(DaysGrid(3, 1)).isEqualTo(DaysGrid(3, 1)) + assertThat(DaysGrid(3, 1)).isNotEqualTo(DaysGrid(3, 2)) + assertThat(DaysGrid(3, 1)).isNotEqualTo(DaysGrid(4, 1)) + assertThat(DayGrid).isEqualTo(DayGrid) + assertThat(DayGrid.hashCode()).isEqualTo(DayGrid.hashCode()) + } + + private companion object { + const val LEGACY_WEEK_COUNT = 20_000 + const val LEGACY_DAYS_COUNT = 40_000 + const val LEGACY_DAY_COUNT = 140_000 + val WEEK_EPOCH = LocalDate(2000, 1, 3) + val DAYS_EPOCH = LocalDate(2000, 1, 1) + + fun LocalDate.weekStart(firstDay: DayOfWeek): LocalDate = + minus(((dayOfWeek.ordinal - firstDay.ordinal) + 7) % 7, DateTimeUnit.DAY) + + fun legacyWeekStart(page: Int, firstDay: DayOfWeek): LocalDate = + WEEK_EPOCH.weekStart(firstDay).plus((page - LEGACY_WEEK_COUNT / 2) * 7, DateTimeUnit.DAY) + + fun legacyWeekPage(date: LocalDate, firstDay: DayOfWeek): Int { + val days = WEEK_EPOCH.weekStart(firstDay).daysUntil(date.weekStart(firstDay)) + return (LEGACY_WEEK_COUNT / 2 + days / 7).coerceIn(0, LEGACY_WEEK_COUNT - 1) + } + + fun legacyDaysStart(page: Int, count: Int, phase: Int): LocalDate = + DAYS_EPOCH.plus(phase + (page - LEGACY_DAYS_COUNT / 2) * count, DateTimeUnit.DAY) + + fun legacyDaysPage(date: LocalDate, count: Int, phase: Int): Int = + (LEGACY_DAYS_COUNT / 2 + Math.floorDiv(DAYS_EPOCH.daysUntil(date) - phase, count)) + .coerceIn(0, LEGACY_DAYS_COUNT - 1) + + fun legacyDayStart(page: Int): LocalDate = + DAYS_EPOCH.plus(page - LEGACY_DAY_COUNT / 2, DateTimeUnit.DAY) + + fun legacyDayPage(date: LocalDate): Int = + (LEGACY_DAY_COUNT / 2 + DAYS_EPOCH.daysUntil(date)).coerceIn(0, LEGACY_DAY_COUNT - 1) + } +}