Eingedeutschte Fachbegriffe in den Kommentaren — „Wachhund" für Watchdog, „Motor" für Engine. Das liest sich albern und ist unklarer als das Original; niemand sagt Wachhund, wenn er einen Watchdog meint. Acht Stellen berichtigt. Gewöhnliche Sprache bleibt deutsch, der Stil der Kommentare ändert sich nicht. Dazu die Plex-Leiter messbar gemacht: `--leiter` baut dieselbe Anfrage zweimal, mit und ohne `autoAdjustQuality=1`, und zählt die Stufen. Die Zeile steht seit dem Umbau im Code und war nie geprüft — genau die Art ungeprüfter Annahme über Serververhalten, die bei Jellyfin vier Runden gekostet hat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
90 lines
3.4 KiB
Swift
90 lines
3.4 KiB
Swift
import Testing
|
|
import Foundation
|
|
@testable import FlixyPlayback
|
|
|
|
@Suite("Rückler-Buchführung")
|
|
struct StallLedgerTests {
|
|
/// Der Fall, wegen dem es den Typ gibt.
|
|
///
|
|
/// Zehn Rückler à dreißig Sekunden lösten beim alten Watchdog nichts aus,
|
|
/// weil er sich bei jedem Erholen zurücksetzte. Hier muss sichtbar
|
|
/// werden, dass die Wiedergabe unbrauchbar ist.
|
|
@Test("Viele kurze Rückler bleiben sichtbar")
|
|
func manyShortStallsAreVisible() {
|
|
var ledger = StallLedger(window: .seconds(180))
|
|
|
|
// Alle 20 s ein Rückler von 5 s, drei Minuten lang.
|
|
for step in 1...9 {
|
|
ledger.record(.init(endedAt: .seconds(step * 20), length: .seconds(5)))
|
|
}
|
|
|
|
let now = Duration.seconds(180)
|
|
#expect(ledger.count(at: now) == 9)
|
|
// 45 s Warten in 180 s Fenster.
|
|
#expect(ledger.share(at: now) > 0.2)
|
|
#expect(ledger.share(at: now) < 0.3)
|
|
}
|
|
|
|
@Test("Was aus dem Fenster fällt, zählt nicht mehr")
|
|
func oldEpisodesFallOut() {
|
|
var ledger = StallLedger(window: .seconds(180))
|
|
ledger.record(.init(endedAt: .seconds(10), length: .seconds(30)))
|
|
|
|
#expect(ledger.count(at: .seconds(60)) == 1)
|
|
// Weit hinter dem Fenster: vergessen.
|
|
#expect(ledger.count(at: .seconds(300)) == 0)
|
|
#expect(ledger.share(at: .seconds(300)) == 0)
|
|
}
|
|
|
|
/// Am Anfang ist das Fenster noch nicht voll — dann gegen die tatsächlich
|
|
/// vergangene Zeit rechnen, sonst sieht ein früher Rückler harmlos aus.
|
|
@Test("Früh im Film zählt die vergangene Zeit, nicht das ganze Fenster")
|
|
func earlyStallsAreNotDiluted() {
|
|
var ledger = StallLedger(window: .seconds(180))
|
|
ledger.record(.init(endedAt: .seconds(20), length: .seconds(5)))
|
|
|
|
let share = ledger.share(at: .seconds(20))
|
|
// 5 s von 20 s, nicht 5 s von 180 s.
|
|
#expect(share > 0.2)
|
|
#expect(share < 0.3)
|
|
}
|
|
|
|
@Test("Der laufende Rückler zählt mit")
|
|
func ongoingStallCounts() {
|
|
var ledger = StallLedger(window: .seconds(180))
|
|
ledger.record(.init(endedAt: .seconds(60), length: .seconds(10)))
|
|
|
|
let without = ledger.share(at: .seconds(180))
|
|
let with = ledger.share(at: .seconds(180), ongoing: .seconds(20))
|
|
#expect(with > without)
|
|
}
|
|
|
|
/// Ein einzelner langer Rückler ist eine andere Aussage als viele kurze.
|
|
@Test("Der längste Einzelfall lässt sich getrennt ablesen")
|
|
func longestIsSeparate() {
|
|
var ledger = StallLedger(window: .seconds(180))
|
|
ledger.record(.init(endedAt: .seconds(30), length: .seconds(2)))
|
|
ledger.record(.init(endedAt: .seconds(60), length: .seconds(18)))
|
|
ledger.record(.init(endedAt: .seconds(90), length: .seconds(3)))
|
|
|
|
#expect(ledger.longest(at: .seconds(120)) == .seconds(18))
|
|
#expect(ledger.count(at: .seconds(120)) == 3)
|
|
}
|
|
|
|
@Test("Eine ruhige Wiedergabe meldet nichts")
|
|
func quietPlaybackIsQuiet() {
|
|
let ledger = StallLedger()
|
|
#expect(ledger.count(at: .seconds(600)) == 0)
|
|
#expect(ledger.share(at: .seconds(600)) == 0)
|
|
#expect(ledger.longest(at: .seconds(600)) == .zero)
|
|
}
|
|
|
|
@Test("Der Anteil übersteigt nie eins")
|
|
func shareIsBounded() {
|
|
var ledger = StallLedger(window: .seconds(60))
|
|
// Absurd: mehr Wartezeit als Fenster.
|
|
ledger.record(.init(endedAt: .seconds(30), length: .seconds(200)))
|
|
#expect(ledger.share(at: .seconds(60)) == 1)
|
|
}
|
|
}
|