Files
Flaro/Packages/FlixyKit/Tests/FlixyDesignTests/MiniPlayerPlacementTests.swift
T
ScarriffleandClaude Opus 5 12bb49615b Player: die Qualitätsleiter des Servers, Bitrate im Blick, Doppeltipp
Die vier erfundenen Stufen weichen der Leiter, die der Server selbst
meldet: 64, 96, 208, 320, 720, 1500, 2000, 3000, 4000, 8000, 10000,
12000, 20000 kbps mit den Auflösungen, die Plex dafür vorsieht. Eine
eigene Leiter hieße, Stufen anzubieten, die der Transcoder nicht kennt —
er rundet dann still auf die nächste, und die Anzeige log.

Angeboten wird nur, was zur Quelle passt. Eine 720p-Datei auf 1080p
hochzurechnen bringt kein Bild dazu, und die Stufen sähen alle gleich aus.

Oben im Blatt steht, was tatsächlich über die Leitung kommt — gemessene
Bitrate, Auflösung, verworfene Bilder. Der Unterschied zur gewählten
Stufe ist keine Haarspalterei: die eine sagt, was erlaubt ist, die andere,
was ankommt. Ruckelt es trotz hoher Stufe, steht die Antwort dort.

„Nur Ton" gibt es, aber ehrlich beschriftet: Plex kennt für Videos keinen
reinen Tonstrom, also wird die unterste Sprosse geholt und das Bild
ausgeblendet.

Dazu Doppeltipp links und rechts für zehn Sekunden zurück und vor. Die
Reihenfolge der Gesten ist entscheidend — das einfache Tippen verschluckt
sonst das doppelte.

Die Kachel hängt nicht mehr in vier Ecken fest, sondern klebt links oder
rechts und ist in der Höhe frei. Vier Ecken sind schnell gebaut, aber im
Weg: steht sie über einem Knopf, bleiben drei Orte, die ebenso stören.
Größer wird sie durch Zusammen- und Auseinanderziehen; der Griff ist weg,
er saß zwangsläufig auf dem Bild und stritt sich mit dem Verschieben.

Und die Begriffe: „Motor" für engine sagt kein deutscher Entwickler.
Fachwörter, die im Deutschen englisch bleiben, bleiben jetzt englisch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 21:50:39 +02:00

102 lines
3.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Foundation
import Testing
@testable import FlixyDesign
/// Wo die schwebende Videokachel landet.
@Suite("Kachel-Platzierung")
struct MiniPlayerPlacementTests {
/// Ein iPhone im Hochformat, Kachel 180 × 101.
private let placement = MiniPlayerPlacement(
bounds: CGSize(width: 390, height: 844),
tile: CGSize(width: 180, height: 101),
inset: 12
)
@Test("Die nähere Seite gewinnt")
func picksTheNearestSide() {
#expect(placement.nearestSide(toCenter: CGPoint(x: 50, y: 400)) == .leading)
#expect(placement.nearestSide(toCenter: CGPoint(x: 340, y: 400)) == .trailing)
}
@Test("Die Höhe bleibt frei, nicht auf Ecken festgelegt")
func keepsTheVerticalPosition() {
// Der Kern des Umbaus: vier Ecken sind schnell gebaut, aber im Weg.
let middle = placement.center(side: .trailing, verticalFraction: 0.5)
let top = placement.center(side: .trailing, verticalFraction: 0)
let bottom = placement.center(side: .trailing, verticalFraction: 1)
#expect(top.y < middle.y)
#expect(middle.y < bottom.y)
#expect(top.x == middle.x && middle.x == bottom.x)
}
@Test("Anteil und Mittelpunkt rechnen ineinander um")
func roundTripsTheFraction() {
for fraction in [0.0, 0.25, 0.5, 0.9, 1.0] {
let center = placement.center(side: .leading, verticalFraction: fraction)
let back = placement.verticalFraction(forCenter: center)
#expect(abs(back - fraction) < 0.0001)
}
}
@Test("Der Anteil bleibt zwischen null und eins")
func clampsTheFraction() {
// Sonst wandert die Kachel beim Drehen aus dem Bild: der Anteil wird
// gespeichert, und ein Wert über eins landete unterhalb des Randes.
#expect(placement.verticalFraction(forCenter: CGPoint(x: 0, y: -9_000)) == 0)
#expect(placement.verticalFraction(forCenter: CGPoint(x: 0, y: 9_000)) == 1)
#expect(placement.center(side: .leading, verticalFraction: 5).y <= 844 - 12)
}
@Test("An jeder Seite bleibt der Sicherheitsabstand")
func keepsTheInset() {
for side in MiniPlayerPlacement.Side.allCases {
for fraction in [0.0, 0.5, 1.0] {
let center = placement.center(side: side, verticalFraction: fraction)
#expect(center.x - 90 >= 12)
#expect(center.x + 90 <= 390 - 12)
#expect(center.y - 50.5 >= 12)
#expect(center.y + 50.5 <= 844 - 12)
}
}
}
@Test("Beim Ziehen rutscht nichts über den Rand")
func clampsWhileDragging() {
// Ohne diese Grenze zieht man die Kachel hinter den Bildschirmrand,
// und dort ist sie mit dem Finger nicht mehr einzuholen.
let farOut = placement.clamped(center: CGPoint(x: -500, y: -500))
#expect(farOut.x == 102.0)
#expect(farOut.y == 62.5)
let alsoFarOut = placement.clamped(center: CGPoint(x: 9_000, y: 9_000))
#expect(alsoFarOut.x == 288.0)
#expect(alsoFarOut.y == 781.5)
}
@Test("Ein Punkt in der Mitte bleibt, wo er ist")
func leavesValidPointsAlone() {
let inside = CGPoint(x: 195, y: 400)
#expect(placement.clamped(center: inside) == inside)
}
@Test("Ein zu enger Bereich schiebt die Kachel nicht hinaus")
func survivesTinyBounds() {
// Kommt beim Drehen und in der Übergangsphase vor: der Bereich ist
// kurzzeitig schmaler als die Kachel. Ein falsch herum geklammerter
// Wert schöbe sie dann dauerhaft aus dem Bild.
let narrow = MiniPlayerPlacement(
bounds: CGSize(width: 100, height: 80),
tile: CGSize(width: 180, height: 101),
inset: 12
)
#expect(narrow.clamped(center: CGPoint(x: 0, y: 0)) == CGPoint(x: 102, y: 62.5))
#expect(narrow.center(side: .trailing, verticalFraction: 1) == CGPoint(x: 102, y: 62.5))
}
}