What 505 real channels showed that 20 demo ones could not
All checks were successful
Build TV app / build (push) Successful in 42s
All checks were successful
Build TV app / build (push) Successful in 42s
The favourite count sat at zero while the backend already had two: the rail read the favourites off the StateFlow inside the composable instead of collecting it, so it never recomposed — a regression from making favourites work for both source types. The list collects again. Real EPG titles broke the fixture shortener in both directions. "BL: VfB Stuttgart - Saison 25-26" became a fixture against a season, and "… FC Bayern München - VfB Stuttgart - 1. Halbzeit" grew a third team; a season, a matchday, a half or a year is now rejected, and a second dash ends the away side. The kick-off chip also gave way in the row: the line below already carries the time, and "Bayer Leverkusen - VfB Stuttgart" needed the width to fit at all. Merged provider EPGs list the same programme several times, minutes apart — the day plan showed The Big Bang Theory three times and two entries marked as running at once. XmltvParser.collapseDuplicates folds a repeat into the entry whose slot it falls inside. The login code expired before anyone could confirm it: Authentik hands out a minute, and the TV dropped back to step one without a word. It fetches a fresh code instead, up to eight times. Tested against the household's own Dispatcharr through api.tv.beckm4nn.net. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
74
tests/unit/DuplicateProgrammesTest.kt
Normal file
74
tests/unit/DuplicateProgrammesTest.kt
Normal file
@@ -0,0 +1,74 @@
|
||||
package dev.castarr.tv.data
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class DuplicateProgrammesTest {
|
||||
|
||||
private fun p(startMin: Int, stopMin: Int, title: String) =
|
||||
Programme(startMin * 60_000L, stopMin * 60_000L, title)
|
||||
|
||||
/** The real case: one channel, one episode, three EPG entries. */
|
||||
@Test
|
||||
fun `repeats a few minutes apart collapse into one`() {
|
||||
val collapsed = XmltvParser.collapseDuplicates(
|
||||
listOf(
|
||||
p(1090, 1140, "The Big Bang Theory"),
|
||||
p(1095, 1145, "The Big Bang Theory"),
|
||||
p(1120, 1170, "The Big Bang Theory"),
|
||||
)
|
||||
)
|
||||
assertEquals(1, collapsed.size)
|
||||
assertEquals(1090 * 60_000L, collapsed.first().start)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a genuine repeat later in the evening survives`() {
|
||||
val collapsed = XmltvParser.collapseDuplicates(
|
||||
listOf(
|
||||
p(1090, 1140, "Tagesschau"),
|
||||
p(1300, 1320, "Tagesschau"),
|
||||
)
|
||||
)
|
||||
assertEquals(2, collapsed.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `different programmes at the same time both stay`() {
|
||||
val collapsed = XmltvParser.collapseDuplicates(
|
||||
listOf(
|
||||
p(1090, 1140, "Sportschau"),
|
||||
p(1092, 1142, "Tagesschau"),
|
||||
)
|
||||
)
|
||||
assertEquals(2, collapsed.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a repeat inside the first slot collapses even beyond the tolerance`() {
|
||||
val collapsed = XmltvParser.collapseDuplicates(
|
||||
listOf(
|
||||
p(1000, 1200, "Fußball: Konferenz"),
|
||||
p(1100, 1300, "Fußball: Konferenz"),
|
||||
)
|
||||
)
|
||||
assertEquals(1, collapsed.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `an unsorted list is handled in time order`() {
|
||||
val collapsed = XmltvParser.collapseDuplicates(
|
||||
listOf(
|
||||
p(1095, 1145, "Two and a Half Men"),
|
||||
p(1090, 1140, "Two and a Half Men"),
|
||||
)
|
||||
)
|
||||
assertEquals(1, collapsed.size)
|
||||
assertEquals(1090 * 60_000L, collapsed.first().start)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `an empty list stays empty`() {
|
||||
assertEquals(emptyList<Programme>(), XmltvParser.collapseDuplicates(emptyList()))
|
||||
}
|
||||
}
|
||||
@@ -56,4 +56,49 @@ class FixtureTest {
|
||||
TeamFilters.fixture("3. Liga: Rot-Weiss Essen - Preußen Münster"),
|
||||
)
|
||||
}
|
||||
|
||||
// --- Titel aus echten EPG-Daten (Sky, DAZN, ran) ----------------------
|
||||
|
||||
@Test
|
||||
fun `a real Sky title yields both clubs`() {
|
||||
assertEquals(
|
||||
"Bayer Leverkusen - VfB Stuttgart",
|
||||
TeamFilters.fixture(
|
||||
"BL: Bayer Leverkusen - VfB Stuttgart, tipico Topspiel der Woche, 16. Spieltag"
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/** The one that gave "VfB Stuttgart - Saison 25-26" as a fixture. */
|
||||
@Test
|
||||
fun `a season is not an opponent`() {
|
||||
assertNull(TeamFilters.fixture("BL: VfB Stuttgart - Saison 25-26"))
|
||||
assertNull(TeamFilters.fixture("BL: Vereinsprofil VfB Stuttgart"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a trailing half is dropped, not treated as a third team`() {
|
||||
assertEquals(
|
||||
"Bundesliga Eröffnungsspiel FC Bayern München - VfB Stuttgart",
|
||||
TeamFilters.fixture(
|
||||
"ran Fußball: Bundesliga Eröffnungsspiel FC Bayern München - VfB Stuttgart - 1. Halbzeit"
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `youth teams keep their suffix`() {
|
||||
assertEquals(
|
||||
"VfB Stuttgart U19 - SC Freiburg U19",
|
||||
TeamFilters.fixture("Live DFB-Pokal Junioren: VfB Stuttgart U19 - SC Freiburg U19, 2. Runde"),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a live prefix falls away with the competition`() {
|
||||
assertEquals(
|
||||
"VfB Stuttgart - Hamburger SV",
|
||||
TeamFilters.fixture("LIVE: VfB Stuttgart - Hamburger SV"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user