forked from agzam/vyatsu-schedule
184 lines
8.2 KiB
Kotlin
184 lines
8.2 KiB
Kotlin
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)
|
||
}
|
||
} |