import Testing import MediaKit @testable import FlixyPlayback @Suite("Was im Spurenmenü steht") struct TrackMenuTests { private func spur( _ id: String, kind: MediaStream.Kind = .audio, sprache: String? = "Deutsch", code: String? = "deu", name: String? = nil, codec: String? = "AC3" ) -> MediaStream { MediaStream( id: id, kind: kind, codec: codec, language: sprache, languageCode: code, displayTitle: name, channels: nil, isSelected: false, isForced: false, title: nil, isDefault: false, isHearingImpaired: false, isOriginal: false ) } // MARK: - Wann es einen Neuaufbau kostet /// **Der Fall, der bisher nirgends stand.** Rechnet der Server um, liegt /// im Stream genau eine Tonspur — jede andere Wahl verlangt eine neue /// Bestellung und damit Sekunden mit schwarzem Bild. @Test("Beim Umrechnen kostet jede Wahl einen Neuaufbau") func transcodeAlwaysRebuilds() { let einträge = TrackMenu.audio( from: [spur("1"), spur("2")], selected: nil, isDirectPlay: false, playerTrackCount: 1 ) #expect(einträge.allSatisfy { $0.rebuilds }) } @Test("Unverändert gespielt und gleiche Anzahl: kein Neuaufbau") func directPlayMatchingCountIsCheap() { let einträge = TrackMenu.audio( from: [spur("1"), spur("2")], selected: nil, isDirectPlay: true, playerTrackCount: 2 ) #expect(einträge.allSatisfy { !$0.rebuilds }) } /// **Die Regel aus `PlayerModel`, zum ersten Mal prüfbar.** Die Zuordnung /// geht über die Reihenfolge; stimmt die Anzahl nicht, ist die Annahme /// falsch, und es wäre die falsche Sprache statt einer Unterbrechung. /// Auslösen liess sich das bisher nur an einem echten Server. @Test("Abweichende Anzahl heißt Neuaufbau, auch unverändert gespielt") func directPlayMismatchRebuilds() { let einträge = TrackMenu.audio( from: [spur("1"), spur("2"), spur("3")], selected: nil, isDirectPlay: true, playerTrackCount: 2 ) #expect(einträge.allSatisfy { $0.rebuilds }) } // MARK: - Untertitel @Test("„Aus“ steht vorn und kostet nie einen Neuaufbau") func offComesFirstAndIsFree() { let einträge = TrackMenu.subtitles( from: [spur("1", kind: .subtitle)], selected: nil, isDirectPlay: false, offLabel: "Aus" ) #expect(einträge.first?.id == nil) #expect(einträge.first?.title == "Aus") #expect(einträge.first?.isSelected == true) #expect(einträge.first?.rebuilds == false) } /// Nur was der Player wirklich hat, lässt sich ohne Neuaufbau schalten. @Test("Eine Untertitelspur, die der Player nicht hat, kostet einen Neuaufbau") func unmappableSubtitleRebuilds() { let deutsch = spur("1", kind: .subtitle, sprache: "Deutsch", code: "deu") let englisch = spur("2", kind: .subtitle, sprache: "English", code: "eng") let einträge = TrackMenu.subtitles( from: [deutsch, englisch], selected: nil, isDirectPlay: true, playerTrackLanguages: ["de"], offLabel: "Aus" ) // „deu" und „de" sind dieselbe Sprache — das entscheidet LanguageCode. #expect(einträge[1].rebuilds == false) #expect(einträge[2].rebuilds == true) } // MARK: - Beschriftung @Test("Der Name des Servers gewinnt, sonst Sprache und Codec") func fallsBackToLanguageAndCodec() { let mitNamen = spur("1", name: "Deutsch (AC3 5.1)") let ohneNamen = spur("2", sprache: "English", codec: "eac3") let einträge = TrackMenu.audio( from: [mitNamen, ohneNamen], selected: mitNamen, isDirectPlay: true, playerTrackCount: 2 ) #expect(einträge[0].title == "Deutsch (AC3 5.1)") #expect(einträge[1].title == "English · EAC3") #expect(einträge[0].isSelected) #expect(!einträge[1].isSelected) } /// Ohne alles bleibt ein Strich — besser als eine leere Zeile, bei der /// niemand weiss, ob sie anklickbar ist. @Test("Ohne Sprache und Codec bleibt ein Strich") func namelessStreamStillHasALabel() { let leer = spur("7", sprache: nil, code: nil, codec: nil) let einträge = TrackMenu.audio( from: [leer], selected: nil, isDirectPlay: true, playerTrackCount: 1 ) #expect(einträge[0].title == "—") #expect(einträge[0].id == 7) } }