Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/gtest/TestAudioDeviceEnumerator.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "gtest/gtest.h"
7
#include "mozilla/UniquePtr.h"
8
#include "mozilla/Attributes.h"
9
#include "nsTArray.h"
10
#define ENABLE_SET_CUBEB_BACKEND 1
11
#include "CubebUtils.h"
12
#include "MediaEngineWebRTC.h"
13
14
using namespace mozilla;
15
16
const bool DEBUG_PRINTS = false;
17
18
// Keep those and the struct definition in sync with cubeb.h and
19
// cubeb-internal.h
20
void
21
cubeb_mock_destroy(cubeb* context);
22
static int
23
cubeb_mock_enumerate_devices(cubeb* context,
24
                             cubeb_device_type type,
25
                             cubeb_device_collection* out);
26
27
static int
28
cubeb_mock_device_collection_destroy(cubeb* context,
29
                                     cubeb_device_collection* collection);
30
31
static int
32
cubeb_mock_register_device_collection_changed(
33
  cubeb* context,
34
  cubeb_device_type devtype,
35
  cubeb_device_collection_changed_callback callback,
36
  void* user_ptr);
37
38
struct cubeb_ops
39
{
40
  int (*init)(cubeb** context, char const* context_name);
41
  char const* (*get_backend_id)(cubeb* context);
42
  int (*get_max_channel_count)(cubeb* context, uint32_t* max_channels);
43
  int (*get_min_latency)(cubeb* context,
44
                         cubeb_stream_params params,
45
                         uint32_t* latency_ms);
46
  int (*get_preferred_sample_rate)(cubeb* context, uint32_t* rate);
47
  int (*enumerate_devices)(cubeb* context,
48
                           cubeb_device_type type,
49
                           cubeb_device_collection* collection);
50
  int (*device_collection_destroy)(cubeb* context,
51
                                   cubeb_device_collection* collection);
52
  void (*destroy)(cubeb* context);
53
  int (*stream_init)(cubeb* context,
54
                     cubeb_stream** stream,
55
                     char const* stream_name,
56
                     cubeb_devid input_device,
57
                     cubeb_stream_params* input_stream_params,
58
                     cubeb_devid output_device,
59
                     cubeb_stream_params* output_stream_params,
60
                     unsigned int latency,
61
                     cubeb_data_callback data_callback,
62
                     cubeb_state_callback state_callback,
63
                     void* user_ptr);
64
  void (*stream_destroy)(cubeb_stream* stream);
65
  int (*stream_start)(cubeb_stream* stream);
66
  int (*stream_stop)(cubeb_stream* stream);
67
  int (*stream_reset_default_device)(cubeb_stream* stream);
68
  int (*stream_get_position)(cubeb_stream* stream, uint64_t* position);
69
  int (*stream_get_latency)(cubeb_stream* stream, uint32_t* latency);
70
  int (*stream_set_volume)(cubeb_stream* stream, float volumes);
71
  int (*stream_set_panning)(cubeb_stream* stream, float panning);
72
  int (*stream_get_current_device)(cubeb_stream* stream,
73
                                   cubeb_device** const device);
74
  int (*stream_device_destroy)(cubeb_stream* stream, cubeb_device* device);
75
  int (*stream_register_device_changed_callback)(
76
    cubeb_stream* stream,
77
    cubeb_device_changed_callback device_changed_callback);
78
  int (*register_device_collection_changed)(
79
    cubeb* context,
80
    cubeb_device_type devtype,
81
    cubeb_device_collection_changed_callback callback,
82
    void* user_ptr);
83
};
84
85
// Mock cubeb impl, only supports device enumeration for now.
86
cubeb_ops const mock_ops = {
87
  /*.init =*/NULL,
88
  /*.get_backend_id =*/NULL,
89
  /*.get_max_channel_count =*/NULL,
90
  /*.get_min_latency =*/NULL,
91
  /*.get_preferred_sample_rate =*/NULL,
92
  /*.enumerate_devices =*/cubeb_mock_enumerate_devices,
93
  /*.device_collection_destroy =*/cubeb_mock_device_collection_destroy,
94
  /*.destroy =*/cubeb_mock_destroy,
95
  /*.stream_init =*/NULL,
96
  /*.stream_destroy =*/NULL,
97
  /*.stream_start =*/NULL,
98
  /*.stream_stop =*/NULL,
99
  /*.stream_reset_default_device =*/NULL,
100
  /*.stream_get_position =*/NULL,
101
  /*.stream_get_latency =*/NULL,
102
  /*.stream_set_volume =*/NULL,
103
  /*.stream_set_panning =*/NULL,
104
  /*.stream_get_current_device =*/NULL,
105
  /*.stream_device_destroy =*/NULL,
106
  /*.stream_register_device_changed_callback =*/NULL,
107
  /*.register_device_collection_changed =*/
108
  cubeb_mock_register_device_collection_changed
109
};
110
111
// This class has two facets: it is both a fake cubeb backend that is intended
112
// to be used for testing, and passed to Gecko code that expects a normal
113
// backend, but is also controllable by the test code to decide what the backend
114
// should do, depending on what is being tested.
115
class MockCubeb
116
{
117
public:
118
  MockCubeb()
119
    : ops(&mock_ops)
120
    , mDeviceCollectionChangeCallback(nullptr)
121
    , mDeviceCollectionChangeType(CUBEB_DEVICE_TYPE_UNKNOWN)
122
    , mDeviceCollectionChangeUserPtr(nullptr)
123
    , mSupportsDeviceCollectionChangedCallback(true)
124
0
  {
125
0
  }
126
  // Cubeb backend implementation
127
  // This allows passing this class as a cubeb* instance.
128
0
  cubeb* AsCubebContext() { return reinterpret_cast<cubeb*>(this); }
129
  // Fill in the collection parameter with all devices of aType.
130
  int EnumerateDevices(cubeb_device_type aType,
131
                       cubeb_device_collection* collection)
132
0
  {
133
#ifdef ANDROID
134
    EXPECT_TRUE(false) << "This is not to be called on Android.";
135
#endif
136
    size_t count = 0;
137
0
    if (aType & CUBEB_DEVICE_TYPE_INPUT) {
138
0
      count += mInputDevices.Length();
139
0
    }
140
0
    if (aType & CUBEB_DEVICE_TYPE_OUTPUT) {
141
0
      count += mOutputDevices.Length();
142
0
    }
143
0
    collection->device = new cubeb_device_info[count];
144
0
    collection->count = count;
145
0
146
0
    uint32_t collection_index = 0;
147
0
    if (aType & CUBEB_DEVICE_TYPE_INPUT) {
148
0
      for (auto& device : mInputDevices) {
149
0
        collection->device[collection_index] = device;
150
0
        collection_index++;
151
0
      }
152
0
    }
153
0
    if (aType & CUBEB_DEVICE_TYPE_OUTPUT) {
154
0
      for (auto& device : mOutputDevices) {
155
0
        collection->device[collection_index] = device;
156
0
        collection_index++;
157
0
      }
158
0
    }
159
0
160
0
    return CUBEB_OK;
161
0
  }
162
163
  // For a given device type, add a callback, called with a user pointer, when
164
  // the device collection for this backend changes (i.e. a device has been
165
  // removed or added).
166
  int RegisterDeviceCollectionChangeCallback(
167
    cubeb_device_type aDevType,
168
    cubeb_device_collection_changed_callback aCallback,
169
    void* aUserPtr)
170
0
  {
171
0
    if (!mSupportsDeviceCollectionChangedCallback) {
172
0
      return CUBEB_ERROR;
173
0
    }
174
0
175
0
    mDeviceCollectionChangeType = aDevType;
176
0
    mDeviceCollectionChangeCallback = aCallback;
177
0
    mDeviceCollectionChangeUserPtr = aUserPtr;
178
0
179
0
    return CUBEB_OK;
180
0
  }
181
182
  // Control API
183
184
  // Add an input or output device to this backend. This calls the device
185
  // collection invalidation callback if needed.
186
  void AddDevice(cubeb_device_info aDevice)
187
0
  {
188
0
    bool needToCall = false;
189
0
190
0
    if (aDevice.type == CUBEB_DEVICE_TYPE_INPUT) {
191
0
      mInputDevices.AppendElement(aDevice);
192
0
    } else if (aDevice.type == CUBEB_DEVICE_TYPE_OUTPUT) {
193
0
      mOutputDevices.AppendElement(aDevice);
194
0
    } else {
195
0
      MOZ_CRASH("bad device type when adding a device in mock cubeb backend");
196
0
    }
197
0
198
0
    bool isInput = aDevice.type & CUBEB_DEVICE_TYPE_INPUT;
199
0
200
0
    needToCall |=
201
0
      isInput && mDeviceCollectionChangeType & CUBEB_DEVICE_TYPE_INPUT;
202
0
    needToCall |=
203
0
      !isInput && mDeviceCollectionChangeType & CUBEB_DEVICE_TYPE_OUTPUT;
204
0
205
0
    if (needToCall && mDeviceCollectionChangeCallback) {
206
0
      mDeviceCollectionChangeCallback(AsCubebContext(),
207
0
                                      mDeviceCollectionChangeUserPtr);
208
0
    }
209
0
  }
210
  // Remove a specific input or output device to this backend, returns true if
211
  // a device was removed. This calls the device collection invalidation
212
  // callback if needed.
213
  bool RemoveDevice(cubeb_devid aId)
214
0
  {
215
0
    bool foundInput = false;
216
0
    bool foundOutput = false;
217
0
    mInputDevices.RemoveElementsBy(
218
0
      [aId, &foundInput](cubeb_device_info& aDeviceInfo) {
219
0
        bool foundThisTime = aDeviceInfo.devid == aId;
220
0
        foundInput |= foundThisTime;
221
0
        return foundThisTime;
222
0
      });
223
0
    mOutputDevices.RemoveElementsBy(
224
0
      [aId, &foundOutput](cubeb_device_info& aDeviceInfo) {
225
0
        bool foundThisTime = aDeviceInfo.devid == aId;
226
0
        foundOutput |= foundThisTime;
227
0
        return foundThisTime;
228
0
      });
229
0
230
0
    bool needToCall = false;
231
0
232
0
    needToCall |=
233
0
      foundInput && mDeviceCollectionChangeType & CUBEB_DEVICE_TYPE_INPUT;
234
0
    needToCall |=
235
0
      foundOutput && mDeviceCollectionChangeType & CUBEB_DEVICE_TYPE_OUTPUT;
236
0
237
0
    if (needToCall && mDeviceCollectionChangeCallback) {
238
0
      mDeviceCollectionChangeCallback(AsCubebContext(),
239
0
                                      mDeviceCollectionChangeUserPtr);
240
0
    }
241
0
242
0
    // If the device removed was a default device, set another device as the
243
0
    // default, if there are still devices available.
244
0
    bool foundDefault = false;
245
0
    for (uint32_t i = 0; i < mInputDevices.Length(); i++) {
246
0
      foundDefault |= mInputDevices[i].preferred != CUBEB_DEVICE_PREF_NONE;
247
0
    }
248
0
249
0
    if (!foundDefault) {
250
0
      if (!mInputDevices.IsEmpty()) {
251
0
        mInputDevices[mInputDevices.Length() - 1].preferred =
252
0
          CUBEB_DEVICE_PREF_ALL;
253
0
      }
254
0
    }
255
0
256
0
    foundDefault = false;
257
0
    for (uint32_t i = 0; i < mOutputDevices.Length(); i++) {
258
0
      foundDefault |= mOutputDevices[i].preferred != CUBEB_DEVICE_PREF_NONE;
259
0
    }
260
0
261
0
    if (!foundDefault) {
262
0
      if (!mOutputDevices.IsEmpty()) {
263
0
        mOutputDevices[mOutputDevices.Length() - 1].preferred =
264
0
          CUBEB_DEVICE_PREF_ALL;
265
0
      }
266
0
    }
267
0
268
0
    return foundInput | foundOutput;
269
0
  }
270
  // Remove all input or output devices from this backend, without calling the
271
  // callback. This is meant to clean up in between tests.
272
  void ClearDevices(cubeb_device_type aType)
273
0
  {
274
0
    mInputDevices.Clear();
275
0
    mOutputDevices.Clear();
276
0
  }
277
278
  // This allows simulating a backend that does not support setting a device
279
  // collection invalidation callback, to be able to test the fallback path.
280
  void SetSupportDeviceChangeCallback(bool aSupports)
281
0
  {
282
0
    mSupportsDeviceCollectionChangedCallback = aSupports;
283
0
  }
284
285
private:
286
  // This needs to have the exact same memory layout as a real cubeb backend.
287
  // It's very important for this `ops` member to be the very first member of
288
  // the class, and to not have any virtual members (to avoid having a vtable).
289
  const cubeb_ops* ops;
290
  // The callback to call when the device list has been changed.
291
  cubeb_device_collection_changed_callback mDeviceCollectionChangeCallback;
292
  // For which device type to call the callback.
293
  cubeb_device_type mDeviceCollectionChangeType;
294
  // The pointer to pass in the callback.
295
  void* mDeviceCollectionChangeUserPtr;
296
  // Whether or not this backed supports device collection change notification
297
  // via a system callback. If not, Gecko is expected to re-query the list every
298
  // time.
299
  bool mSupportsDeviceCollectionChangedCallback;
300
  // Our input and output devices.
301
  nsTArray<cubeb_device_info> mInputDevices;
302
  nsTArray<cubeb_device_info> mOutputDevices;
303
};
304
305
void
306
cubeb_mock_destroy(cubeb* context)
307
0
{
308
0
  delete reinterpret_cast<MockCubeb*>(context);
309
0
}
310
311
static int
312
cubeb_mock_enumerate_devices(cubeb* context,
313
                             cubeb_device_type type,
314
                             cubeb_device_collection* out)
315
0
{
316
0
  MockCubeb* mock = reinterpret_cast<MockCubeb*>(context);
317
0
  return mock->EnumerateDevices(type, out);
318
0
}
319
320
int
321
cubeb_mock_device_collection_destroy(cubeb* context,
322
                                     cubeb_device_collection* collection)
323
0
{
324
0
  delete[] collection->device;
325
0
  return CUBEB_OK;
326
0
}
327
328
int
329
cubeb_mock_register_device_collection_changed(
330
  cubeb* context,
331
  cubeb_device_type devtype,
332
  cubeb_device_collection_changed_callback callback,
333
  void* user_ptr)
334
0
{
335
0
  MockCubeb* mock = reinterpret_cast<MockCubeb*>(context);
336
0
  return mock->RegisterDeviceCollectionChangeCallback(
337
0
    devtype, callback, user_ptr);
338
0
  return CUBEB_OK;
339
0
}
340
341
void
342
PrintDevice(cubeb_device_info aInfo)
343
0
{
344
0
  printf("id: %zu\n"
345
0
         "device_id: %s\n"
346
0
         "friendly_name: %s\n"
347
0
         "group_id: %s\n"
348
0
         "vendor_name: %s\n"
349
0
         "type: %d\n"
350
0
         "state: %d\n"
351
0
         "preferred: %d\n"
352
0
         "format: %d\n"
353
0
         "default_format: %d\n"
354
0
         "max_channels: %d\n"
355
0
         "default_rate: %d\n"
356
0
         "max_rate: %d\n"
357
0
         "min_rate: %d\n"
358
0
         "latency_lo: %d\n"
359
0
         "latency_hi: %d\n",
360
0
         reinterpret_cast<uintptr_t>(aInfo.devid),
361
0
         aInfo.device_id,
362
0
         aInfo.friendly_name,
363
0
         aInfo.group_id,
364
0
         aInfo.vendor_name,
365
0
         aInfo.type,
366
0
         aInfo.state,
367
0
         aInfo.preferred,
368
0
         aInfo.format,
369
0
         aInfo.default_format,
370
0
         aInfo.max_channels,
371
0
         aInfo.default_rate,
372
0
         aInfo.max_rate,
373
0
         aInfo.min_rate,
374
0
         aInfo.latency_lo,
375
0
         aInfo.latency_hi);
376
0
}
377
378
void
379
PrintDevice(AudioDeviceInfo* aInfo)
380
0
{
381
0
  cubeb_devid id;
382
0
  nsString name;
383
0
  nsString groupid;
384
0
  nsString vendor;
385
0
  uint16_t type;
386
0
  uint16_t state;
387
0
  uint16_t preferred;
388
0
  uint16_t supportedFormat;
389
0
  uint16_t defaultFormat;
390
0
  uint32_t maxChannels;
391
0
  uint32_t defaultRate;
392
0
  uint32_t maxRate;
393
0
  uint32_t minRate;
394
0
  uint32_t maxLatency;
395
0
  uint32_t minLatency;
396
0
397
0
  id = aInfo->DeviceID();
398
0
  aInfo->GetName(name);
399
0
  aInfo->GetGroupId(groupid);
400
0
  aInfo->GetVendor(vendor);
401
0
  aInfo->GetType(&type);
402
0
  aInfo->GetState(&state);
403
0
  aInfo->GetPreferred(&preferred);
404
0
  aInfo->GetSupportedFormat(&supportedFormat);
405
0
  aInfo->GetDefaultFormat(&defaultFormat);
406
0
  aInfo->GetMaxChannels(&maxChannels);
407
0
  aInfo->GetDefaultRate(&defaultRate);
408
0
  aInfo->GetMaxRate(&maxRate);
409
0
  aInfo->GetMinRate(&minRate);
410
0
  aInfo->GetMinLatency(&minLatency);
411
0
  aInfo->GetMaxLatency(&maxLatency);
412
0
413
0
  printf("device id: %zu\n"
414
0
         "friendly_name: %s\n"
415
0
         "group_id: %s\n"
416
0
         "vendor_name: %s\n"
417
0
         "type: %d\n"
418
0
         "state: %d\n"
419
0
         "preferred: %d\n"
420
0
         "format: %d\n"
421
0
         "default_format: %d\n"
422
0
         "max_channels: %d\n"
423
0
         "default_rate: %d\n"
424
0
         "max_rate: %d\n"
425
0
         "min_rate: %d\n"
426
0
         "latency_lo: %d\n"
427
0
         "latency_hi: %d\n",
428
0
         reinterpret_cast<uintptr_t>(id),
429
0
         NS_LossyConvertUTF16toASCII(name).get(),
430
0
         NS_LossyConvertUTF16toASCII(groupid).get(),
431
0
         NS_LossyConvertUTF16toASCII(vendor).get(),
432
0
         type,
433
0
         state,
434
0
         preferred,
435
0
         supportedFormat,
436
0
         defaultFormat,
437
0
         maxChannels,
438
0
         defaultRate,
439
0
         maxRate,
440
0
         minRate,
441
0
         minLatency,
442
0
         maxLatency);
443
0
}
444
445
cubeb_device_info
446
InputDeviceTemplate(cubeb_devid aId)
447
0
{
448
0
  // A fake input device
449
0
  cubeb_device_info device;
450
0
  device.devid = aId;
451
0
  device.device_id = "nice name";
452
0
  device.friendly_name = "an even nicer name";
453
0
  device.group_id = "the physical device";
454
0
  device.vendor_name = "mozilla";
455
0
  device.type = CUBEB_DEVICE_TYPE_INPUT;
456
0
  device.state = CUBEB_DEVICE_STATE_ENABLED;
457
0
  device.preferred = CUBEB_DEVICE_PREF_NONE;
458
0
  device.format = CUBEB_DEVICE_FMT_F32NE;
459
0
  device.default_format = CUBEB_DEVICE_FMT_F32NE;
460
0
  device.max_channels = 2;
461
0
  device.default_rate = 44100;
462
0
  device.max_rate = 44100;
463
0
  device.min_rate = 16000;
464
0
  device.latency_lo = 256;
465
0
  device.latency_hi = 1024;
466
0
467
0
  return device;
468
0
}
469
470
enum DeviceOperation
471
{
472
  ADD,
473
  REMOVE
474
};
475
476
void
477
TestEnumeration(MockCubeb* aMock,
478
                uint32_t aExpectedDeviceCount,
479
                DeviceOperation aOperation)
480
0
{
481
0
  CubebDeviceEnumerator enumerator;
482
0
483
0
  nsTArray<RefPtr<AudioDeviceInfo>> inputDevices;
484
0
485
0
  enumerator.EnumerateAudioInputDevices(inputDevices);
486
0
487
0
  EXPECT_EQ(inputDevices.Length(), aExpectedDeviceCount)
488
0
    << "Device count is correct when enumerating";
489
0
490
0
  if (DEBUG_PRINTS) {
491
0
    for (uint32_t i = 0; i < inputDevices.Length(); i++) {
492
0
      printf("=== Before removal\n");
493
0
      PrintDevice(inputDevices[i]);
494
0
    }
495
0
  }
496
0
497
0
  if (aOperation == DeviceOperation::REMOVE) {
498
0
    aMock->RemoveDevice(reinterpret_cast<cubeb_devid>(1));
499
0
  } else {
500
0
    aMock->AddDevice(InputDeviceTemplate(reinterpret_cast<cubeb_devid>(123)));
501
0
  }
502
0
503
0
  enumerator.EnumerateAudioInputDevices(inputDevices);
504
0
505
0
  uint32_t newExpectedDeviceCount = aOperation == DeviceOperation::REMOVE
506
0
                                      ? aExpectedDeviceCount - 1
507
0
                                      : aExpectedDeviceCount + 1;
508
0
509
0
  EXPECT_EQ(inputDevices.Length(), newExpectedDeviceCount)
510
0
    << "Device count is correct when enumerating after operation";
511
0
512
0
  if (DEBUG_PRINTS) {
513
0
    for (uint32_t i = 0; i < inputDevices.Length(); i++) {
514
0
      printf("=== After removal\n");
515
0
      PrintDevice(inputDevices[i]);
516
0
    }
517
0
  }
518
0
}
519
520
#ifndef ANDROID
521
TEST(CubebDeviceEnumerator, EnumerateSimple)
522
0
{
523
0
  // It looks like we're leaking this object, but in fact it will be freed by
524
0
  // gecko sometime later: `cubeb_destroy` is called when layout statics are
525
0
  // shutdown and we cast back to a MockCubeb* and call the dtor.
526
0
  MockCubeb* mock = new MockCubeb();
527
0
  mozilla::CubebUtils::ForceSetCubebContext(mock->AsCubebContext());
528
0
529
0
  // We want to test whether CubebDeviceEnumerator works with and without a
530
0
  // backend that can notify of a device collection change via callback.
531
0
  // Additionally, we're testing that both adding and removing a device
532
0
  // invalidates the list correctly.
533
0
  bool supportsDeviceChangeCallback[2] = { true, false };
534
0
  DeviceOperation operations[2] = { DeviceOperation::ADD,
535
0
                                    DeviceOperation::REMOVE };
536
0
537
0
  for (DeviceOperation op : operations) {
538
0
    for (bool supports : supportsDeviceChangeCallback) {
539
0
      mock->ClearDevices(CUBEB_DEVICE_TYPE_INPUT);
540
0
      // Add a few input devices (almost all the same but it does not really
541
0
      // matter as long as they have distinct IDs and only one is the default
542
0
      // devices)
543
0
      uint32_t device_count = 4;
544
0
      for (uintptr_t i = 0; i < device_count; i++) {
545
0
        cubeb_device_info device =
546
0
          InputDeviceTemplate(reinterpret_cast<void*>(i + 1));
547
0
        // Make it so that the last device is the default input device.
548
0
        if (i == device_count - 1) {
549
0
          device.preferred = CUBEB_DEVICE_PREF_ALL;
550
0
        }
551
0
        mock->AddDevice(device);
552
0
      }
553
0
554
0
      mock->SetSupportDeviceChangeCallback(supports);
555
0
      TestEnumeration(mock, device_count, op);
556
0
    }
557
0
  }
558
0
}
559
#else // building for Android, which has no device enumeration support
560
TEST(CubebDeviceEnumerator, EnumerateAndroid)
561
{
562
  MockCubeb* mock = new MockCubeb();
563
  mozilla::CubebUtils::ForceSetCubebContext(mock->AsCubebContext());
564
565
  CubebDeviceEnumerator enumerator;
566
567
  nsTArray<RefPtr<AudioDeviceInfo>> inputDevices;
568
  enumerator.EnumerateAudioInputDevices(inputDevices);
569
  EXPECT_EQ(inputDevices.Length(), 1u) <<  "Android always exposes a single input device.";
570
  EXPECT_EQ(inputDevices[0]->MaxChannels(), 1u) << "With a single channel.";
571
  EXPECT_EQ(inputDevices[0]->DeviceID(), nullptr) << "It's always the default device.";
572
  EXPECT_TRUE(inputDevices[0]->Preferred()) << "it's always the prefered device.";
573
}
574
#endif
575
576
TEST(CubebDeviceEnumerator, ForceNullCubebContext)
577
0
{
578
0
  mozilla::CubebUtils::ForceSetCubebContext(nullptr);
579
0
  CubebDeviceEnumerator enumerator;
580
0
  nsTArray<RefPtr<AudioDeviceInfo>> inputDevices;
581
0
  enumerator.EnumerateAudioInputDevices(inputDevices);
582
0
  EXPECT_EQ(inputDevices.Length(), 0u) << "Enumeration must fail device list must be empty.";
583
0
}
584