diff --git a/app/src/androidTest/java/agzam/vyatsu/data/remote/pdf/PdfDumpTest.kt b/app/src/androidTest/java/agzam/vyatsu/data/remote/pdf/PdfDumpTest.kt new file mode 100644 index 0000000..8245d77 --- /dev/null +++ b/app/src/androidTest/java/agzam/vyatsu/data/remote/pdf/PdfDumpTest.kt @@ -0,0 +1,21 @@ +package agzam.vyatsu.data.remote.pdf + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class PdfDumpTest { + + @Test + fun dumpParsedSchedule() { + val instrumentation = InstrumentationRegistry.getInstrumentation() + val bytes = instrumentation.context.assets.open("schedule.pdf").use { it.readBytes() } + + val days = PdfScheduleParser.parse(bytes) + + val target = instrumentation.targetContext.getExternalFilesDir(null)!!.resolve("parsed.txt") + target.writeText(days.joinToString(separator = "\n")) + } +} diff --git a/app/src/androidTest/java/agzam/vyatsu/data/remote/pdf/PdfScheduleParserTest.kt b/app/src/androidTest/java/agzam/vyatsu/data/remote/pdf/PdfScheduleParserTest.kt new file mode 100644 index 0000000..ea84b92 --- /dev/null +++ b/app/src/androidTest/java/agzam/vyatsu/data/remote/pdf/PdfScheduleParserTest.kt @@ -0,0 +1,164 @@ +package agzam.vyatsu.data.remote.pdf + +import agzam.vyatsu.domain.model.Day +import agzam.vyatsu.domain.model.Lesson +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import java.time.LocalDate +import java.time.LocalTime +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class PdfScheduleParserTest { + + companion object { + private val days: List by lazy { + val bytes = InstrumentationRegistry.getInstrumentation().context.assets + .open("schedule.pdf") + .use { it.readBytes() } + PdfScheduleParser.parse(bytes) + } + } + + private fun day(date: String): Day = days.first { it.date == LocalDate.parse(date) } + + private fun lessons(date: String, start: String): List = + day(date).intervals + .first { it.start == LocalTime.parse(start) } + .lessons + .filter { it.raw.isNotBlank() } + + private fun allLessons(date: String): List = + day(date).intervals.flatMap { it.lessons }.filter { it.raw.isNotBlank() } + + private fun Lesson.canonical(): String = + listOf(subject, type.name, room, teacher, subgroup).joinToString("|") + + @Test + fun parsesWholeTwoWeekPeriod() { + val expectedDates = (0..13L).map { LocalDate.of(2026, 9, 14).plusDays(it) } + + assertEquals(expectedDates, days.map { it.date }) + } + + @Test + fun everyDayHasSevenStandardIntervals() { + val expectedTimes = listOf( + LocalTime.of(8, 20) to LocalTime.of(9, 50), + LocalTime.of(10, 0) to LocalTime.of(11, 30), + LocalTime.of(11, 45) to LocalTime.of(13, 15), + LocalTime.of(14, 0) to LocalTime.of(15, 30), + LocalTime.of(15, 45) to LocalTime.of(17, 15), + LocalTime.of(17, 20) to LocalTime.of(18, 50), + LocalTime.of(18, 55) to LocalTime.of(20, 25) + ) + + for (day in days) { + assertEquals(day.date.toString(), expectedTimes, day.intervals.map { it.start to it.end }) + } + } + + @Test + fun emptyDaysHaveNoLessons() { + val emptyDays = listOf("2026-09-14", "2026-09-19", "2026-09-20", "2026-09-26", "2026-09-27") + + for (date in emptyDays) { + assertEquals(date, emptyList(), allLessons(date)) + } + } + + @Test + fun parsesTwoLecturesInOneSlot() { + assertEquals( + listOf( + "Объектно-ориентированный анализ и проектирование|LECTURE|2-202|Преподаватель -.П.|null", + "Разработка Web-приложений|LECTURE|1-236|Преподаватель -.П.|null" + ), + lessons("2026-09-15", "10:00").map { it.canonical() } + ) + } + + @Test + fun parsesThreeLecturesInOneSlot() { + assertEquals( + listOf( + "Инструментальные средства проектирования компонентов вычислительной техники|LECTURE|2-201|Семеновых В.И.|null", + "Предметно-ориентированные автоматизированные информационные системы|LECTURE|1-313|Прозорова Ю.А.|null", + "Сценарное и функциональное программирование|LECTURE|1-311|Преподаватель -.П.|null" + ), + lessons("2026-09-15", "11:45").map { it.canonical() } + ) + } + + @Test + fun parsesLaboratoryWork() { + assertEquals( + listOf( + "Инструментальные средства проектирования компонентов вычислительной техники|LABORATORY|2-201|Семеновых В.И.|null", + "Предметно-ориентированные автоматизированные информационные системы|LABORATORY|1-236|Прозорова Ю.А.|null", + "Сценарное и функциональное программирование|LABORATORY|1-114|Преподаватель -.П.|null" + ), + lessons("2026-09-15", "14:00").map { it.canonical() } + ) + } + + @Test + fun parsesWednesdayLectures() { + assertEquals( + listOf("Введение в архитектуру ЭВМ|LECTURE|1-242|Страбыкин Д.А.|null"), + lessons("2026-09-16", "08:20").map { it.canonical() } + ) + assertEquals( + listOf("Теория компиляторов и формальные грамматики|LECTURE|1-242|Мельцов В.Ю.|null"), + lessons("2026-09-16", "10:00").map { it.canonical() } + ) + } + + @Test + fun parsesPhysicalEducationSubgroups() { + val parsed = lessons("2026-09-17", "11:45").filter { it.room != null } + + assertEquals( + listOf( + "Элективные дисциплины (модули) по физической культуре и спорту|PRACTICE|9-115|Трушкова Е.Л.|01 подгруппа", + "Элективные дисциплины (модули) по физической культуре и спорту|PRACTICE|9-301|Салопин О.М.|01 подгруппа", + "Элективные дисциплины (модули) по физической культуре и спорту|PRACTICE|9-301|Салопин О.М.|02 подгруппа" + ), + parsed.map { it.canonical() } + ) + } + + @Test + fun keepsRawTextWhenRoomHasTrailingPunctuation() { + val unparsed = lessons("2026-09-17", "11:45").filter { it.room == null } + + assertEquals(2, unparsed.size) + assertTrue(unparsed.any { it.raw.contains("9-209.") }) + assertTrue(unparsed.any { it.raw.contains("9-208__") }) + } + + @Test + fun parsesFridaySubgroupLaboratories() { + assertEquals( + listOf( + "Введение в архитектуру ЭВМ|LABORATORY|1-117|Страбыкин Д.А.|01 подгруппа", + "Введение в архитектуру ЭВМ|LABORATORY|1-117_|Страбыкин Д.А.|02 подгруппа" + ), + lessons("2026-09-18", "08:20").map { it.canonical() } + ) + } + + @Test + fun parsesSecondWeekMondayLaboratories() { + assertEquals( + listOf( + "Теория компиляторов и формальные грамматики|LABORATORY|2-308|Караваева О.В.|01 подгруппа", + "Теория компиляторов и формальные грамматики|LABORATORY|2-308_|Караваева О.В.|02 подгруппа" + ), + lessons("2026-09-21", "10:00").map { it.canonical() } + ) + } +}