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

184 lines
8.2 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
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)
}
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)
}
}