oficina / meet Public

Clone
git clone https://amadan.net/oficina/meet

Plain git — no account needed to clone.

Download

Download this file

1#if os(macOS)
2 import CoreAudio
3 import XCTest
4
5 @testable import CallsKit
6
7 /// The Mac's input channel choice (spec §15.20), apart from any device:
8 /// the remembered choice, the channel map it becomes, the channel count
9 /// read from a stream configuration, and the routes' picker state.
10 @MainActor final class InputChannelTests: XCTestCase {
11 private var store: UserDefaults!
12 private var storeName = ""
13
14 override func setUp() {
15 super.setUp()
16 storeName = "calls-channel-tests-\(UUID().uuidString)"
17 store = UserDefaults(suiteName: storeName)
18 }
19 override func tearDown() {
20 store.removePersistentDomain(forName: storeName)
21 super.tearDown()
22 }
23
24 func testInputOneIsWhereEveryDeviceStarts() {
25 XCTAssertEqual(InputChannelPreference.load(uid: "scarlett", channels: 18, store), 0)
26 }
27
28 func testTheChoiceIsRememberedPerDevice() {
29 InputChannelPreference.save(1, uid: "scarlett", store)
30 XCTAssertEqual(InputChannelPreference.load(uid: "scarlett", channels: 18, store), 1)
31 XCTAssertEqual(InputChannelPreference.load(uid: "motu", channels: 8, store), 0)
32 InputChannelPreference.save(0, uid: "scarlett", store)
33 XCTAssertEqual(InputChannelPreference.load(uid: "scarlett", channels: 18, store), 0)
34 }
35
36 func testAChoiceTheDeviceNoLongerHasFallsBackToInputOne() {
37 InputChannelPreference.save(9, uid: "scarlett", store)
38 XCTAssertEqual(InputChannelPreference.load(uid: "scarlett", channels: 18, store), 9)
39 XCTAssertEqual(InputChannelPreference.load(uid: "scarlett", channels: 2, store), 0)
40 }
41
42 func testTheMapCarriesTheChosenInputOnEveryClientChannel() {
43 // A mono client (the engine's mono connection): one entry.
44 XCTAssertEqual(InputChannelMap.map(channel: 1, clientChannels: 1, mappedBefore: false), [1])
45 // The hardware's width: every channel is input 2, so any mixdown or
46 // "first channel" of them is input 2.
47 XCTAssertEqual(
48 InputChannelMap.map(channel: 1, clientChannels: 4, mappedBefore: false), [1, 1, 1, 1])
49 // Input 1 on a unit never mapped: left alone, today's behaviour.
50 XCTAssertNil(InputChannelMap.map(channel: 0, clientChannels: 18, mappedBefore: false))
51 // Back to input 1 after a choice: straight through.
52 XCTAssertEqual(
53 InputChannelMap.map(channel: 0, clientChannels: 3, mappedBefore: true), [0, 1, 2])
54 // A unit that has not said its width still gets a map.
55 XCTAssertEqual(InputChannelMap.map(channel: 2, clientChannels: 0, mappedBefore: false), [2])
56 }
57
58 /// 0.3.8: the map is strictly opt-in. Only an input other than the
59 /// first, chosen on a microphone chosen by name that has more than
60 /// one input; anything else leaves the unit as the module wired it,
61 /// which is what 0.3.3 did for every microphone.
62 func testTheMapIsOnlyForAnInputChosenOnAChosenInterface() {
63 func channel(_ saved: Int, _ device: String, explicit: String?, inputs: Int) -> Int {
64 InputChannelMap.channel(saved: saved, device: device, explicit: explicit, inputs: inputs)
65 }
66 XCTAssertEqual(channel(1, "scarlett", explicit: "scarlett", inputs: 18), 1, "chosen: input 2")
67 XCTAssertEqual(channel(0, "scarlett", explicit: "scarlett", inputs: 18), 0)
68 XCTAssertEqual(channel(1, "scarlett", explicit: nil, inputs: 18), 0, "on the system default: never mapped")
69 XCTAssertEqual(channel(1, "scarlett", explicit: "usb", inputs: 18), 0, "another device chosen")
70 XCTAssertEqual(channel(1, "airpods", explicit: "airpods", inputs: 1), 0, "a one-input microphone")
71 XCTAssertEqual(channel(5, "solo", explicit: "solo", inputs: 2), 0, "a remembered input it no longer has")
72 XCTAssertEqual(channel(1, "solo", explicit: "solo", inputs: 2), 1)
73 XCTAssertNil(
74 InputChannelMap.map(channel: channel(1, "scarlett", explicit: nil, inputs: 18), clientChannels: 18, mappedBefore: false),
75 "so the unit is left alone")
76 }
77
78 func testChannelsAreCountedAcrossAStreamConfiguration() {
79 let list = AudioBufferList.allocate(maximumBuffers: 3)
80 defer { free(list.unsafeMutablePointer) }
81 list[0] = AudioBuffer(mNumberChannels: 8, mDataByteSize: 0, mData: nil)
82 list[1] = AudioBuffer(mNumberChannels: 8, mDataByteSize: 0, mData: nil)
83 list[2] = AudioBuffer(mNumberChannels: 2, mDataByteSize: 0, mData: nil)
84 XCTAssertEqual(InputChannelMap.count(list), 18)
85 }
86
87 func testChannelsAreLabelledByNumberAndName() {
88 let device = InputDeviceChannels(
89 uid: "scarlett", name: "Scarlett 18i8 USB", count: 3, names: ["Analogue 1", "", "3"])
90 XCTAssertEqual(device.label(0), "Input 1 (Analogue 1)")
91 XCTAssertEqual(device.label(1), "Input 2")
92 XCTAssertEqual(device.label(2), "Input 3", "a name that is only the number adds nothing")
93 XCTAssertEqual(device.describe(channel: 0), "Scarlett 18i8 USB, input 1")
94 XCTAssertEqual(
95 InputDeviceChannels(uid: "mbp", name: "MacBook Pro Microphone", count: 1).describe(channel: 0),
96 "MacBook Pro Microphone")
97 }
98
99 private func routes(channels: InputDeviceChannels?, rebuilt: @escaping () -> Void) -> AudioRoutes {
100 let routes = AudioRoutes(defaults: store)
101 routes.attach(
102 AudioEngineControl(
103 inventory: {
104 AudioInventory(
105 devices: [
106 SystemAudioDevice(uid: "mbp", name: "MacBook Pro Microphone", inputChannels: 1, outputChannels: 0),
107 SystemAudioDevice(uid: "scarlett", name: "Scarlett 18i8 USB", inputChannels: 4, outputChannels: 0),
108 ], defaultInput: "mbp", defaultOutput: nil)
109 },
110 channels: { id in id == "scarlett" ? channels : nil },
111 rebuildInput: rebuilt))
112 return routes
113 }
114
115 func testThePickerAppearsForAMultiChannelMicrophoneOnly() throws {
116 var rebuilds = 0
117 let scarlett = InputDeviceChannels(uid: "scarlett-uid", name: "Scarlett 18i8 USB", count: 4)
118 let routes = routes(channels: scarlett) { rebuilds += 1 }
119 XCTAssertEqual(routes.inputChannels, [], "on the default, which is not the interface here")
120 try routes.use(input: "scarlett")
121 XCTAssertEqual(routes.inputChannels, ["Input 1", "Input 2", "Input 3", "Input 4"])
122 XCTAssertEqual(routes.inputChannel, 0)
123 XCTAssertEqual(routes.inputSource, "Scarlett 18i8 USB, input 1")
124
125 routes.use(inputChannel: 1)
126 XCTAssertEqual(routes.inputChannel, 1)
127 XCTAssertEqual(routes.inputSource, "Scarlett 18i8 USB, input 2")
128 XCTAssertEqual(rebuilds, 1, "the module's input is rebuilt so the map applies now")
129 XCTAssertEqual(InputChannelPreference.load(uid: "scarlett-uid", channels: 4, store), 1)
130
131 routes.use(inputChannel: 1)
132 routes.use(inputChannel: 7)
133 XCTAssertEqual(rebuilds, 1, "the same choice, or one the device lacks, does nothing")
134
135 // Remembered: a new launch on the same defaults comes back on input 2.
136 let again = self.routes(channels: scarlett) {}
137 XCTAssertEqual(again.inputChannel, 1)
138 }
139
140 /// The interface as the system default, with an input remembered for
141 /// it: no picker, and input 1 as the module wires it.
142 func testOnTheSystemDefaultThereIsNoPickerAndNoMap() throws {
143 InputChannelPreference.save(1, uid: "scarlett-uid", store)
144 let scarlett = InputDeviceChannels(uid: "scarlett-uid", name: "Scarlett 18i8 USB", count: 4)
145 let routes = AudioRoutes(defaults: store)
146 routes.attach(
147 AudioEngineControl(
148 inventory: {
149 AudioInventory(
150 devices: [SystemAudioDevice(uid: "scarlett", name: "Scarlett 18i8 USB", inputChannels: 4, outputChannels: 0)],
151 defaultInput: "scarlett", defaultOutput: nil)
152 },
153 channels: { _ in scarlett }))
154 XCTAssertEqual(routes.currentInput, AudioRouteRules.defaultID)
155 XCTAssertEqual(routes.inputChannels, [])
156 XCTAssertEqual(routes.inputChannel, 0)
157 XCTAssertEqual(routes.inputSource, "Scarlett 18i8 USB, input 1")
158 }
159
160 func testAnOrdinaryMicrophoneHasNoPicker() throws {
161 let routes = routes(channels: InputDeviceChannels(uid: "usb", name: "USB Mic", count: 1)) {}
162 try routes.use(input: "scarlett")
163 XCTAssertEqual(routes.inputChannels, [])
164 XCTAssertEqual(routes.inputSource, "USB Mic")
165 }
166 }
167#endif
168