Menüleiste springt nicht mehr, Mixer fasst Hilfsprozesse zusammen
Die Breite folgte der Stellenzahl des aktuellen Werts. Aus „14 KB/s" wird „431 KB/s", und mit der zusätzlichen Stelle wanderte die halbe Leiste. Jetzt wieder feste Breiten, bemessen am breitesten Wert, der vorkommen kann. Der frühere Fehler war nicht die feste Breite, sondern der falsche Bezugswert: gezeichnet wird „63°", bemessen wurde „100 °C" — fünfzehn Punkte für zwei Zeichen, die nie erscheinen. Nachgemessen: zwei Aufnahmen im Abstand von sechs Sekunden mit geänderten Werten ergeben identische Breiten. Der Mixer fasst Hilfsprozesse mit ihrem Programm zusammen. „Google Chrome" und „Google Chrome Helper" standen als zwei Zeilen nebeneinander, mit getrennten Reglern für dasselbe Programm — zieht man den einen, hört man den anderen. Chrome hat je Tab einen Helfer; fünfzehn Regler für einen Browser sind keine Auskunft. Abgegriffen wird weiterhin je Prozess, geregelt aber je Programm. Der Schlüssel der Taps muss deshalb den Prozess mitführen: mehrere Helfer teilen sich eine Kennung und überschrieben sich sonst gegenseitig. Der Pegel einer Zeile ist der lauteste ihrer Prozesse. Die Zeilen sind einzeilig statt zweizeilig: Symbol, Stummschalter, Regler, Wert. Der Name ist weg — bei einem erkennbaren Programmsymbol ist er redundant, und die gewonnene Zeile macht die Liste halb so hoch. Dafür ist das Symbol größer, und wer den Namen braucht, hält den Zeiger darauf. Popover 420 statt 330 Punkte breit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import Testing
|
||||
import CoreAudio
|
||||
@testable import AudioProvider
|
||||
|
||||
// Chrome gibt seinen Ton über einen Hilfsprozess aus. In der Liste standen
|
||||
// deshalb „Google Chrome" und „Google Chrome Helper" nebeneinander — zwei
|
||||
// Regler für dasselbe Programm, und wer den einen zieht, hört den anderen.
|
||||
|
||||
private func process(_ bundleID: String, name: String, id: UInt32,
|
||||
playing: Bool = false) -> AudioProcess {
|
||||
AudioProcess(objectID: AudioObjectID(id), pid: pid_t(id), bundleID: bundleID,
|
||||
name: name, isPlaying: playing)
|
||||
}
|
||||
|
||||
@Suite("Programme zusammenfassen")
|
||||
struct ProcessGroupTests {
|
||||
|
||||
@Test("Hilfsprozess und Programm werden eine Zeile")
|
||||
func helperJoinsItsApp() {
|
||||
let groups = AudioProcessGroup.group([
|
||||
process("com.google.Chrome", name: "Google Chrome", id: 1),
|
||||
process("com.google.Chrome.helper", name: "Google Chrome Helper", id: 2),
|
||||
])
|
||||
#expect(groups.count == 1)
|
||||
#expect(groups.first?.bundleID == "com.google.Chrome")
|
||||
#expect(groups.first?.processes.count == 2)
|
||||
}
|
||||
|
||||
@Test("Der Name kommt vom Programm, nicht vom Helfer")
|
||||
func nameComesFromTheApp() {
|
||||
let groups = AudioProcessGroup.group([
|
||||
process("com.google.Chrome.helper", name: "Google Chrome Helper", id: 2),
|
||||
process("com.google.Chrome", name: "Google Chrome", id: 1),
|
||||
])
|
||||
#expect(groups.first?.name == "Google Chrome")
|
||||
}
|
||||
|
||||
@Test("Ohne das Programm selbst trägt der Helfer den Namen")
|
||||
func helperAloneKeepsItsName() {
|
||||
// Chrome kann Ton ausgeben, ohne dass der Hauptprozess selbst als
|
||||
// Tonquelle auftaucht. Dann ist der Helfer alles, was da ist.
|
||||
let groups = AudioProcessGroup.group([
|
||||
process("com.google.Chrome.helper", name: "Google Chrome Helper", id: 2),
|
||||
])
|
||||
#expect(groups.count == 1)
|
||||
#expect(groups.first?.bundleID == "com.google.Chrome")
|
||||
}
|
||||
|
||||
@Test("Verschiedene Programme bleiben getrennt")
|
||||
func differentAppsStayApart() {
|
||||
let groups = AudioProcessGroup.group([
|
||||
process("com.spotify.client", name: "Spotify", id: 1),
|
||||
process("com.google.Chrome", name: "Google Chrome", id: 2),
|
||||
])
|
||||
#expect(groups.count == 2)
|
||||
}
|
||||
|
||||
@Test("Spielt einer, spielt die Gruppe")
|
||||
func groupPlaysIfAnyPlays() {
|
||||
// Der Ton kommt aus dem Helfer; der Hauptprozess schweigt. Die Zeile
|
||||
// muss trotzdem als spielend gelten, sonst verschwindet sie.
|
||||
let groups = AudioProcessGroup.group([
|
||||
process("com.google.Chrome", name: "Google Chrome", id: 1, playing: false),
|
||||
process("com.google.Chrome.helper", name: "Helper", id: 2, playing: true),
|
||||
])
|
||||
#expect(groups.first?.isPlaying == true)
|
||||
}
|
||||
|
||||
@Test("Mehrere Helfer landen alle in derselben Zeile")
|
||||
func manyHelpersOneRow() {
|
||||
// Chrome hat je Tab einen. Fünfzehn Regler für einen Browser wären
|
||||
// keine Auskunft, sondern eine Zumutung.
|
||||
let groups = AudioProcessGroup.group((1...5).map {
|
||||
process("com.google.Chrome.helper", name: "Helper", id: UInt32($0))
|
||||
})
|
||||
#expect(groups.count == 1)
|
||||
#expect(groups.first?.processes.count == 5)
|
||||
}
|
||||
|
||||
@Test("Die Reihenfolge bleibt stabil")
|
||||
func orderIsStable() {
|
||||
// Sonst springen die Zeilen bei jeder Messung umher, weil Core Audio
|
||||
// keine Reihenfolge zusagt.
|
||||
let input = [process("b.app", name: "B", id: 2), process("a.app", name: "A", id: 1)]
|
||||
#expect(AudioProcessGroup.group(input).map(\.name) == ["A", "B"])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user