Reine Umbenennung, kein Verhalten geändert — bewusst als eigener Commit, damit „umbenannt" und „umgebaut" im Nachhinein auseinanderzuhalten sind. 712 mal `Flixy`, 778 mal `flixy`, 7 mal `FLIXY` in 169 Dateien, dazu 25 Pfade. Ziele: `Halo`, `HaloTV`, `HaloTopShelf`, `HaloUITests`, `HaloKit` mit `HaloDesign`, `HaloDownloads`, `HaloPlayback` und `HaloProbe`. `MediaKit`, `JellyfinKit` und `DownloadWidget` tragen den Namen nicht und bleiben. `docs/superpowers/specs/` ist ausgenommen. Datierte Entwurfsdokumente sind ein Protokoll getroffener Entscheidungen; sie nachträglich umzuschreiben hiesse, die Vergangenheit zu glätten. **Die Bundle-ID wandert mit** — ausdrücklich so gewollt, mit bekanntem Preis: `com.scarriffleservices.halo` ist für iOS eine *andere* App. Die installierte Fassung bleibt daneben stehen, mit Downloads und Anmeldung in ihrem Container; Halo startet leer. Einmal neu anmelden, heruntergeladene Folgen einmal neu laden. Dasselbe gilt für die App-Gruppe, die sich TV-App und Top Shelf teilen. **Eine Folge davon, die beim Entscheiden noch nicht auf dem Tisch lag:** der Aufräumschritt aus dem vorigen Commit kommt an die alten Plex-Token nicht mehr heran. Der Schlüsselbund grenzt nach Team und Bundle-Kennung ab — was unter `…flixy` liegt, ist für `…halo` unerreichbar. Erledigt wird es trotzdem, nur anders: **die alte App löschen.** iOS räumt deren Schlüsselbund-Einträge beim Deinstallieren mit ab. Der Code bleibt stehen, weil er für jeden Stand vor dieser Umbenennung richtig ist und im Jahr einen Startvergleich kostet. Die Projektdatei ist aus `project.yml` neu erzeugt, der alte Ordner gelöscht statt umbenannt — sie steht ohnehin nicht im Repo. Das Verzeichnis des Repos selbst heißt weiter `Flixy`; das umzubenennen ist ein Handgriff ausserhalb des Projekts. 368 Tests grün. iOS und tvOS bauen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
116 lines
4.5 KiB
Swift
116 lines
4.5 KiB
Swift
import Testing
|
|
import MediaKit
|
|
@testable import HaloPlayback
|
|
|
|
@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)
|
|
}
|
|
}
|