Files
vyatsu-schedule/app/src/test/java/agzam/vyatsu/TeacherScheduleParserTest.kt
T

184 lines
8.2 KiB
Kotlin
Raw Normal View History

package agzam.vyatsu
import agzam.vyatsu.data.remote.TeacherScheduleParser
import agzam.vyatsu.domain.model.LessonType
import java.time.LocalDate
import java.time.LocalTime
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
class TeacherScheduleParserTest {
private fun fixture(): String =
checkNotNull(javaClass.getResourceAsStream("/prepod.html"))
.readBytes().toString(Charsets.UTF_8)
2026-10-08 01:42:26 +03:00
private fun fixture517(): String =
checkNotNull(javaClass.getResourceAsStream("/prepod517.html"))
.readBytes().toString(Charsets.UTF_8)
private fun fixture974(): String =
checkNotNull(javaClass.getResourceAsStream("/prepod974.html"))
.readBytes().toString(Charsets.UTF_8)
@Test
fun teachersHeaderParsedInOrderAndDeduped() {
val parsed = TeacherScheduleParser.parse(fixture())
assertEquals(listOf("Лелекова Е.В.", "Иванов И.И.", "Петрова А.С."), parsed.teachers)
}
@Test
fun daysWithCorrectDates() {
val parsed = TeacherScheduleParser.parse(fixture())
assertEquals(2, parsed.days.size)
assertEquals(LocalDate.of(2026, 9, 14), parsed.days[0].date)
assertEquals(LocalDate.of(2026, 9, 15), parsed.days[1].date)
}
@Test
fun intervalTimesParsed() {
val parsed = TeacherScheduleParser.parse(fixture())
val day0 = parsed.days[0]
assertEquals(2, day0.intervals.size)
assertEquals(LocalTime.of(8, 20), day0.intervals[0].start)
assertEquals(LocalTime.of(9, 50), day0.intervals[0].end)
assertEquals(LocalTime.of(10, 0), day0.intervals[1].start)
assertEquals(LocalTime.of(11, 30), day0.intervals[1].end)
}
@Test
fun multiGroupCellSplitsIntoPerGroupLessons() {
val parsed = TeacherScheduleParser.parse(fixture())
val cell = parsed.days[0].intervals[0].lessons.filter { it.teacher == "Лелекова Е.В." }
assertEquals(2, cell.size)
val lesson0 = cell[0]
val lesson1 = cell[1]
assertEquals("Бб-1101-04-00", lesson0.group)
assertNull(lesson0.subgroup)
assertEquals("01 подгруппа", lesson1.subgroup)
assertEquals("4-302", lesson0.room)
assertEquals("Биоразнообразие", lesson0.subject)
assertEquals(LessonType.LECTURE, lesson0.type)
assertEquals("Лелекова Е.В.", lesson0.teacher)
}
@Test
fun emptyCellContributesNoLesson() {
val parsed = TeacherScheduleParser.parse(fixture())
val threeSlots = (0..6).map { slot ->
parsed.days[0].intervals.firstOrNull { it.start.hour == 8 && it.start.minute == 20 } ?: return@map true
}
val petrovLessons = parsed.days[0].intervals[0].lessons.filter { it.teacher == "Петрова А.С." }
assertEquals(0, petrovLessons.size)
assertTrue(threeSlots.isEmpty() || threeSlots.size == 7)
}
@Test
fun noRoomCellKeepsRawAndNullRoom() {
val parsed = TeacherScheduleParser.parse(fixture())
val cell = parsed.days[0].intervals[1].lessons.filter { it.teacher == "Лелекова Е.В." }
assertEquals(2, cell.size)
assertNull(cell[0].room)
assertTrue(cell[0].raw.contains("Бб-1101-04-00"))
assertNull(cell[0].subject)
}
@Test
fun exactTotalLessonCount() {
val parsed = TeacherScheduleParser.parse(fixture())
val total = parsed.days.sumOf { day -> day.intervals.sumOf { it.lessons.size } }
assertEquals(11, total)
}
2026-10-08 01:42:26 +03:00
private fun spacerColumnFixture(): String = """
<table>
<tr>
<td>День</td><td>Интервал</td><td></td>
<td><span>Марков Р.В.</span></td><td><span>Мельцов В.Ю.</span></td>
</tr>
<tr>
<td>четверг 08.10.26</td><td>08:20-09:50</td><td></td>
<td>2-206 Математика Практическое занятие УТб-2301-02-00</td>
<td>1-334 Организация параллельных вычислительных систем Лекция ИВТб-4301-05-00</td>
</tr>
<tr>
<td>10:00-11:30</td><td></td>
<td>2-206 Математика Практическое занятие УТб-2301-02-00</td>
<td>1-334 Организация параллельных вычислительных систем Лекция ИВТб-4301-05-00</td>
</tr>
</table>
""".trimIndent()
@Test
fun emptyHeaderSpacerDoesNotShiftTeacherColumns() {
val parsed = TeacherScheduleParser.parse(spacerColumnFixture())
assertEquals(listOf("Марков Р.В.", "Мельцов В.Ю."), parsed.teachers)
assertEquals(1, parsed.days.size)
assertEquals(LocalDate.of(2026, 10, 8), parsed.days[0].date)
assertEquals(2, parsed.days[0].intervals.size)
for (slot in parsed.days[0].intervals) {
val markov = slot.lessons.filter { it.teacher == "Марков Р.В." }
val meltsov = slot.lessons.filter { it.teacher == "Мельцов В.Ю." }
assertEquals(1, markov.size)
assertEquals(1, meltsov.size)
assertEquals("Математика", markov[0].subject)
assertEquals("Организация параллельных вычислительных систем", meltsov[0].subject)
}
}
@Test
fun realPage517TeacherColumnsAligned() {
val html = checkNotNull(javaClass.getResourceAsStream("/prepod517.html"))
.readBytes().toString(Charsets.UTF_8)
val parsed = TeacherScheduleParser.parse(html)
assertTrue(parsed.teachers.contains("Мельцов В.Ю."))
val day = parsed.days.first { it.date == LocalDate.of(2026, 10, 8) }
val all = day.intervals.flatMap { it.lessons }
val markov = all.filter { it.teacher == "Марков Р.В." }
val meltsov = all.filter { it.teacher == "Мельцов В.Ю." }
assertTrue("Мельцов должен иметь пары 08.10", meltsov.isNotEmpty())
assertTrue(
"У Мельцова не должно быть математики Маркова",
meltsov.none { it.subject?.contains("Математика") == true }
)
assertTrue(
"У Маркова должна остаться математика",
markov.any { it.subject?.contains("Математика") == true }
)
assertTrue(
"У Мельцова параллельные вычислительные системы",
meltsov.any { it.subject?.contains("Организация параллельных") == true }
)
}
@Test
fun realPage517MeltsovLabsSitAt1000And1145NotAt0820() {
val parsed = TeacherScheduleParser.parse(fixture517())
val day = parsed.days.first { it.date == LocalDate.of(2026, 10, 6) }
val labsAt = { time: java.time.LocalTime ->
day.intervals.firstOrNull { it.start == time }
?.lessons
?.filter { it.teacher == "Мельцов В.Ю." && it.type == LessonType.LABORATORY }
.orEmpty()
}
assertEquals(
"В 08:20 у Мельцова на странице 517 лабораторных нет",
0, labsAt(java.time.LocalTime.of(8, 20)).size
)
assertTrue(labsAt(java.time.LocalTime.of(10, 0)).isNotEmpty())
assertTrue(labsAt(java.time.LocalTime.of(11, 45)).isNotEmpty())
}
@Test
fun realPage974MeltsovHasPracticeAt0820() {
val parsed = TeacherScheduleParser.parse(fixture974())
val day = parsed.days.first { it.date == LocalDate.of(2026, 10, 6) }
val slot = day.intervals.first { it.start == java.time.LocalTime.of(8, 20) }
val mel = slot.lessons.filter { it.teacher == "Мельцов В.Ю." }
assertTrue(mel.isNotEmpty())
assertTrue("08:20 на странице 974 — практическое занятие", mel.all { it.type == LessonType.PRACTICE })
assertEquals("ИВТб-2305-06-00", mel.first().group)
}
}