import Testing import Foundation import MediaKit @testable import FlixyPlayback @Suite("Sprachkürzel") struct LanguageCodeTests { /// Der Fall, an dem die alte Prüfung lautlos scheiterte: `ger` ist die /// ISO-639-2/B-Form von Deutsch, und ffmpeg schreibt sie in viele Dateien. @Test("ger, deu und de-DE meinen alle Deutsch") func germanHasThreeSpellings() { #expect(LanguageCode.matches("ger", "de")) #expect(LanguageCode.matches("deu", "de")) #expect(LanguageCode.matches("de-DE", "de")) #expect(LanguageCode.matches("GER", "de")) } /// Schweizerdeutsch ist keine Schreibweise von Deutsch. Es gleichzusetzen /// wäre bequem und falsch — wer Hochdeutsch will, bekäme sonst /// Schweizerdeutsch untergeschoben. @Test("Schweizerdeutsch ist nicht Deutsch") func swissGermanStandsAlone() { #expect(!LanguageCode.matches("gsw", "de")) #expect(LanguageCode.matches("gsw", "gsw")) } @Test("Auch die anderen Doppelformen") func otherDoubleForms() { #expect(LanguageCode.matches("fre", "fra")) #expect(LanguageCode.matches("dut", "nl")) #expect(LanguageCode.matches("cze", "cs")) #expect(LanguageCode.matches("chi", "zh")) } @Test("Ohne Angabe kein Treffer") func missingIsNoMatch() { #expect(!LanguageCode.matches(nil, "de")) #expect(!LanguageCode.matches("de", nil)) } } @Suite("Wahl der Tonspur") struct AudioTrackChooserTests { private func spur( _ index: Int, _ sprache: String?, title: String? = nil, channels: Int = 2, isDefault: Bool = false ) -> MediaStream { MediaStream( id: String(index), kind: .audio, codec: "aac", language: sprache, languageCode: sprache, displayTitle: title, channels: channels, title: title, isDefault: isDefault ) } // MARK: - Der ZDF-Fall /// Wörtlich der Ärger, wegen dem es den Typ gibt: die als deutsch /// ausgezeichnete Spur ist die mit Audiodeskription, und die als englisch /// ausgezeichnete ist in Wahrheit die deutsche ohne. private var zdf: [MediaStream] { [ spur(0, "ger", title: "Deutsch (Audiodeskription)", isDefault: true), spur(1, "eng", title: "Deutsch"), ] } @Test("Audiodeskription wird gemieden, auch wenn sie Standard ist") func avoidsAudioDescription() { let gewählt = AudioTrackChooser.choose( from: zdf, wish: .init(language: "de") ) #expect(gewählt?.id == "1") } /// Ohne das Meiden gewinnt die ausgezeichnete Sprache — der Beweis, dass /// der Schalter tatsächlich das Ruder herumreißt und nicht nur dabeisteht. @Test("Ohne das Meiden gewinnt das Kürzel") func withoutAvoidanceTheCodeWins() { let gewählt = AudioTrackChooser.choose( from: zdf, wish: .init(language: "de", avoidsAudioDescription: false) ) #expect(gewählt?.id == "0") } /// Und wenn der Nutzer einmal gewählt hat, gilt das — auch gegen die /// Sprache. @Test("Die gemerkte Spur schlägt den Sprachwunsch") func rememberedBeatsLanguage() { let gemerkt = AudioTrackChooser.TrackSignature(zdf[1]) let gewählt = AudioTrackChooser.choose( from: zdf, wish: .init(language: "de", remembered: gemerkt, avoidsAudioDescription: false) ) #expect(gewählt?.id == "1") } // MARK: - Die Rangliste @Test("Fehlt die Wunschsprache, greift die Rangliste") func fallbackListApplies() { let spuren = [spur(0, "fre"), spur(1, "eng"), spur(2, "ita")] let gewählt = AudioTrackChooser.choose( from: spuren, wish: .init(language: "de", fallbacks: ["gsw", "en", "fr"]) ) #expect(gewählt?.id == "1") } @Test("Die Reihenfolge der Rangliste zählt") func fallbackOrderMatters() { let spuren = [spur(0, "fre"), spur(1, "eng")] let zuerstFranzösisch = AudioTrackChooser.choose( from: spuren, wish: .init(language: "de", fallbacks: ["fr", "en"]) ) #expect(zuerstFranzösisch?.id == "0") } @Test("Schweizerdeutsch lässt sich in die Rangliste stellen") func swissGermanInTheList() { let spuren = [spur(0, "eng"), spur(1, "gsw")] let gewählt = AudioTrackChooser.choose( from: spuren, wish: .init(language: "de", fallbacks: ["gsw", "en"]) ) #expect(gewählt?.id == "1") } // MARK: - Der Boden darunter @Test("Ohne jede Übereinstimmung kommt trotzdem eine Spur") func alwaysAnswers() { let spuren = [spur(0, "jpn"), spur(1, "kor", isDefault: true)] let gewählt = AudioTrackChooser.choose( from: spuren, wish: .init(language: "de", fallbacks: ["en"]) ) // Die Standardspur — nachvollziehbar statt beliebig. #expect(gewählt?.id == "1") } @Test("Gibt es die Sprache nur mit Audiodeskription, ist sie besser als nichts") func audioDescriptionBeatsSilence() { let spuren = [spur(0, "ger", title: "Deutsch (Audiodeskription)")] let gewählt = AudioTrackChooser.choose(from: spuren, wish: .init(language: "de")) #expect(gewählt?.id == "0") } @Test("Ohne Tonspuren gibt es nichts zu wählen") func noAudioNoChoice() { let nurBild = [MediaStream(id: "0", kind: .video)] #expect(AudioTrackChooser.choose(from: nurBild, wish: .init()) == nil) } @Test("Kommentarspuren werden gemieden") func avoidsCommentary() { let spuren = [ spur(0, "ger", title: "Audiokommentar des Regisseurs", isDefault: true), spur(1, "ger", title: "Deutsch"), ] let gewählt = AudioTrackChooser.choose(from: spuren, wish: .init(language: "de")) #expect(gewählt?.id == "1") } /// „AD" als eigenes Wort, nicht als Teil von „Broadcast". @Test("AD erkennt nur das freistehende Wort") func adOnlyAsWholeWord() { let spuren = [ spur(0, "ger", title: "Deutsch Broadcast Mix"), spur(1, "ger", title: "Deutsch AD"), ] let gewählt = AudioTrackChooser.choose(from: spuren, wish: .init(language: "de")) #expect(gewählt?.id == "0") } @Test("Mehr Kanäle gewinnen bei sonst gleichem Stand") func moreChannelsWin() { let spuren = [spur(0, "ger", title: "Deutsch", channels: 2), spur(1, "ger", title: "Deutsch", channels: 6)] let gewählt = AudioTrackChooser.choose(from: spuren, wish: .init(language: "de")) #expect(gewählt?.id == "1") } /// Zweimal dieselbe Datei muss zweimal dasselbe ergeben — sonst ist ein /// Fehler nicht wiederholbar. @Test("Gleichstand entscheidet die Nummer") func tiesAreStable() { let spuren = [spur(1, "ger", title: "Deutsch"), spur(0, "ger", title: "Deutsch")] #expect(AudioTrackChooser.choose(from: spuren, wish: .init(language: "de"))?.id == "0") } }