Alle Kacheln, Popovers und Fenster auf die Designschicht umgestellt: rund hundert gesetzte Schriftgrößen und 159 gesetzte Abstände und Radien sind jetzt Token. Übrig bleibt `spacing: 0` — das ist eine Aussage, keine geratene Zahl. Die Einstellungen bekommen eine Seitenleiste statt sieben Reitern, deren deutsche Beschriftungen zusammen 880 Punkte brauchten. Panel-Token stehen nicht mehr in Fenstern: dort gilt Systemsemantik, damit der Hellmodus nicht schwarz auf schwarz endet. Neu ist ein Schaufenster, das alle Bauteile nebeneinander zeigt — ohne das lässt sich eine Änderung an einem Token nur prüfen, indem man die App bedient, bis jede Kachel einmal zu sehen war. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
72 lines
3.4 KiB
Swift
72 lines
3.4 KiB
Swift
import Testing
|
|
@testable import AudioProvider
|
|
|
|
// Der Mixer war an einem Headset völlig still. Nachgemessen am Aggregate-Gerät
|
|
// über der „PRO X Wireless Gaming Headset":
|
|
//
|
|
// MacBook Pro-Lautsprecher Eingang 1 Puffer: 2 Kan. → nur der Tap
|
|
// PRO X Headset Eingang 2 Puffer: 1 Kan. | 2 Kan. → Mikrofon, dann Tap
|
|
//
|
|
// Ein Ausgabegerät, das selbst einen Eingang mitbringt — jedes Headset, jedes
|
|
// USB-Interface — schiebt seine eigenen Eingangspuffer vor die des Taps. Wer
|
|
// blind Puffer null nimmt, greift das Mikrofon ab statt des Programms: der
|
|
// Programmton wird nie ausgegeben, während der Tap das Original stumm hält.
|
|
// Die eingebauten Lautsprecher haben keinen Eingang, deshalb fiel es dort nie auf.
|
|
|
|
@Suite("Wo der Tap im Aggregate-Gerät liegt")
|
|
struct TapBufferLayoutTests {
|
|
|
|
@Test("Reines Ausgabegerät: der Tap liegt vorn")
|
|
func outputOnlyDeviceKeepsTapFirst() {
|
|
#expect(TapBufferLayout.offset(subDeviceInputBuffers: 0,
|
|
aggregateInputChannels: [2],
|
|
tapChannels: 2) == 0)
|
|
}
|
|
|
|
@Test("Headset: der eigene Mikrofonpuffer schiebt den Tap nach hinten")
|
|
func headsetMicrophoneShiftsTap() {
|
|
// Der gemessene Fall. Vorher wurde hier Puffer null genommen — das
|
|
// Mikrofon — und der Programmton blieb liegen.
|
|
#expect(TapBufferLayout.offset(subDeviceInputBuffers: 1,
|
|
aggregateInputChannels: [1, 2],
|
|
tapChannels: 2) == 1)
|
|
}
|
|
|
|
@Test("Mehrere Eingangspuffer des Geräts zählen alle")
|
|
func severalDeviceInputsCount() {
|
|
#expect(TapBufferLayout.offset(subDeviceInputBuffers: 2,
|
|
aggregateInputChannels: [1, 1, 2],
|
|
tapChannels: 2) == 2)
|
|
}
|
|
|
|
@Test("Passt die Kanalzahl an der erwarteten Stelle nicht, wird nicht geraten")
|
|
func mismatchedChannelsAreRefused() {
|
|
// Läge der Tap wider Erwarten woanders, wäre die Folge wieder Stille
|
|
// bei stummgeschaltetem Original. Dann lieber gar kein Tap: der Aufbau
|
|
// fliegt auf, das Programm bleibt hörbar.
|
|
#expect(TapBufferLayout.offset(subDeviceInputBuffers: 0,
|
|
aggregateInputChannels: [1, 2],
|
|
tapChannels: 2) == nil)
|
|
}
|
|
|
|
@Test("Fehlt der Puffer ganz, wird nicht geraten")
|
|
func missingBufferIsRefused() {
|
|
#expect(TapBufferLayout.offset(subDeviceInputBuffers: 2,
|
|
aggregateInputChannels: [1, 2],
|
|
tapChannels: 2) == nil)
|
|
#expect(TapBufferLayout.offset(subDeviceInputBuffers: 0,
|
|
aggregateInputChannels: [],
|
|
tapChannels: 2) == nil)
|
|
}
|
|
|
|
@Test("Unsinnige Angaben werden abgewiesen, nicht verrechnet")
|
|
func nonsenseIsRefused() {
|
|
#expect(TapBufferLayout.offset(subDeviceInputBuffers: -1,
|
|
aggregateInputChannels: [2],
|
|
tapChannels: 2) == nil)
|
|
#expect(TapBufferLayout.offset(subDeviceInputBuffers: 0,
|
|
aggregateInputChannels: [2],
|
|
tapChannels: 0) == nil)
|
|
}
|
|
}
|