1 /*
2  * Copyright © 2011 Mozilla Foundation
3  *
4  * This program is made available under an ISC-style license.  See the
5  * accompanying file LICENSE for details.
6  */
7 #include "gtest/gtest.h"
8 #if !defined(_XOPEN_SOURCE)
9 #define _XOPEN_SOURCE 600
10 #endif
11 #include "cubeb/cubeb.h"
12 #include <atomic>
13 #include <stdio.h>
14 #include <string.h>
15 #include <math.h>
16 #include "common.h"
17 
18 #define STREAM_RATE 44100
19 #define STREAM_LATENCY 100 * STREAM_RATE / 1000
20 #define STREAM_CHANNELS 1
21 #define STREAM_LAYOUT CUBEB_LAYOUT_MONO
22 #define STREAM_FORMAT CUBEB_SAMPLE_S16LE
23 
is_windows_7()24 int is_windows_7()
25 {
26 #ifdef __MINGW32__
27   fprintf(stderr, "Warning: this test was built with MinGW.\n"
28          "MinGW does not contain necessary version checking infrastructure. Claiming to be Windows 7, even if we're not.\n");
29   return 1;
30 #endif
31 #if (defined(_WIN32) || defined(__WIN32__)) && ( !defined(__MINGW32__))
32   OSVERSIONINFOEX osvi;
33   DWORDLONG condition_mask = 0;
34 
35   ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
36   osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
37 
38   // NT 6.1 is Windows 7
39   osvi.dwMajorVersion = 6;
40   osvi.dwMinorVersion = 1;
41 
42   VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, VER_EQUAL);
43   VER_SET_CONDITION(condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
44 
45   return VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, condition_mask);
46 #else
47   return 0;
48 #endif
49 }
50 
51 static int dummy;
52 static std::atomic<uint64_t> total_frames_written;
53 static int delay_callback;
54 
55 static long
test_data_callback(cubeb_stream * stm,void * user_ptr,const void *,void * outputbuffer,long nframes)56 test_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
57 {
58   EXPECT_TRUE(stm && user_ptr == &dummy && outputbuffer && nframes > 0);
59   assert(outputbuffer);
60   memset(outputbuffer, 0, nframes * sizeof(short));
61 
62   total_frames_written += nframes;
63   if (delay_callback) {
64     delay(10);
65   }
66   return nframes;
67 }
68 
69 void
test_state_callback(cubeb_stream *,void *,cubeb_state)70 test_state_callback(cubeb_stream * /*stm*/, void * /*user_ptr*/, cubeb_state /*state*/)
71 {
72 }
73 
TEST(cubeb,init_destroy_context)74 TEST(cubeb, init_destroy_context)
75 {
76   int r;
77   cubeb * ctx;
78   char const* backend_id;
79 
80   r = common_init(&ctx, "test_sanity");
81   ASSERT_EQ(r, CUBEB_OK);
82   ASSERT_NE(ctx, nullptr);
83 
84   backend_id = cubeb_get_backend_id(ctx);
85   ASSERT_TRUE(backend_id);
86 
87   fprintf(stderr, "Backend: %s\n", backend_id);
88 
89   cubeb_destroy(ctx);
90 }
91 
TEST(cubeb,init_destroy_multiple_contexts)92 TEST(cubeb, init_destroy_multiple_contexts)
93 {
94   size_t i;
95   int r;
96   cubeb * ctx[4];
97   int order[4] = {2, 0, 3, 1};
98   ASSERT_EQ(ARRAY_LENGTH(ctx), ARRAY_LENGTH(order));
99 
100   for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
101     r = common_init(&ctx[i], NULL);
102     ASSERT_EQ(r, CUBEB_OK);
103     ASSERT_NE(ctx[i], nullptr);
104   }
105 
106   /* destroy in a different order */
107   for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
108     cubeb_destroy(ctx[order[i]]);
109   }
110 }
111 
TEST(cubeb,context_variables)112 TEST(cubeb, context_variables)
113 {
114   int r;
115   cubeb * ctx;
116   uint32_t value;
117   cubeb_channel_layout layout;
118   cubeb_stream_params params;
119 
120   r = common_init(&ctx, "test_context_variables");
121   ASSERT_EQ(r, CUBEB_OK);
122   ASSERT_NE(ctx, nullptr);
123 
124   params.channels = STREAM_CHANNELS;
125   params.format = STREAM_FORMAT;
126   params.rate = STREAM_RATE;
127   params.layout = STREAM_LAYOUT;
128   params.prefs = CUBEB_STREAM_PREF_NONE;
129 
130   r = cubeb_get_min_latency(ctx, &params, &value);
131   ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
132   if (r == CUBEB_OK) {
133     ASSERT_TRUE(value > 0);
134   }
135 
136   r = cubeb_get_preferred_sample_rate(ctx, &value);
137   ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
138   if (r == CUBEB_OK) {
139     ASSERT_TRUE(value > 0);
140   }
141 
142   r = cubeb_get_preferred_channel_layout(ctx, &layout);
143   ASSERT_TRUE(r == CUBEB_ERROR_NOT_SUPPORTED ||
144               (r == CUBEB_OK && layout != CUBEB_LAYOUT_UNDEFINED) ||
145               (r == CUBEB_ERROR && layout == CUBEB_LAYOUT_UNDEFINED));
146 
147   cubeb_destroy(ctx);
148 }
149 
TEST(cubeb,init_destroy_stream)150 TEST(cubeb, init_destroy_stream)
151 {
152   int r;
153   cubeb * ctx;
154   cubeb_stream * stream;
155   cubeb_stream_params params;
156 
157   r = common_init(&ctx, "test_sanity");
158   ASSERT_EQ(r, CUBEB_OK);
159   ASSERT_NE(ctx, nullptr);
160 
161   params.format = STREAM_FORMAT;
162   params.rate = STREAM_RATE;
163   params.channels = STREAM_CHANNELS;
164   params.layout = STREAM_LAYOUT;
165   params.prefs = CUBEB_STREAM_PREF_NONE;
166 
167   r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
168                         test_data_callback, test_state_callback, &dummy);
169   ASSERT_EQ(r, CUBEB_OK);
170   ASSERT_NE(stream, nullptr);
171 
172   cubeb_stream_destroy(stream);
173   cubeb_destroy(ctx);
174 }
175 
TEST(cubeb,init_destroy_multiple_streams)176 TEST(cubeb, init_destroy_multiple_streams)
177 {
178   size_t i;
179   int r;
180   cubeb * ctx;
181   cubeb_stream * stream[8];
182   cubeb_stream_params params;
183 
184   r = common_init(&ctx, "test_sanity");
185   ASSERT_EQ(r, CUBEB_OK);
186   ASSERT_NE(ctx, nullptr);
187 
188   params.format = STREAM_FORMAT;
189   params.rate = STREAM_RATE;
190   params.channels = STREAM_CHANNELS;
191   params.layout = STREAM_LAYOUT;
192   params.prefs = CUBEB_STREAM_PREF_NONE;
193 
194   for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
195     r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
196                           test_data_callback, test_state_callback, &dummy);
197     ASSERT_EQ(r, CUBEB_OK);
198     ASSERT_NE(stream[i], nullptr);
199   }
200 
201   for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
202     cubeb_stream_destroy(stream[i]);
203   }
204 
205   cubeb_destroy(ctx);
206 }
207 
TEST(cubeb,configure_stream)208 TEST(cubeb, configure_stream)
209 {
210   int r;
211   cubeb * ctx;
212   cubeb_stream * stream;
213   cubeb_stream_params params;
214 
215   r = common_init(&ctx, "test_sanity");
216   ASSERT_EQ(r, CUBEB_OK);
217   ASSERT_NE(ctx, nullptr);
218 
219   params.format = STREAM_FORMAT;
220   params.rate = STREAM_RATE;
221   params.channels = 2; // panning
222   params.layout = CUBEB_LAYOUT_STEREO;
223   params.prefs = CUBEB_STREAM_PREF_NONE;
224 
225   r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
226                         test_data_callback, test_state_callback, &dummy);
227   ASSERT_EQ(r, CUBEB_OK);
228   ASSERT_NE(stream, nullptr);
229 
230   r = cubeb_stream_set_volume(stream, 1.0f);
231   ASSERT_TRUE(r == 0 || r == CUBEB_ERROR_NOT_SUPPORTED);
232 
233   r = cubeb_stream_set_panning(stream, 0.0f);
234   ASSERT_TRUE(r == 0 || r == CUBEB_ERROR_NOT_SUPPORTED);
235 
236   cubeb_stream_destroy(stream);
237   cubeb_destroy(ctx);
238 }
239 
TEST(cubeb,configure_stream_undefined_layout)240 TEST(cubeb, configure_stream_undefined_layout)
241 {
242   int r;
243   cubeb * ctx;
244   cubeb_stream * stream;
245   cubeb_stream_params params;
246 
247   r = common_init(&ctx, "test_sanity");
248   ASSERT_EQ(r, CUBEB_OK);
249   ASSERT_NE(ctx, nullptr);
250 
251   params.format = STREAM_FORMAT;
252   params.rate = STREAM_RATE;
253   params.channels = 2; // panning
254   params.layout = CUBEB_LAYOUT_UNDEFINED;
255   params.prefs = CUBEB_STREAM_PREF_NONE;
256 
257   r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
258                         test_data_callback, test_state_callback, &dummy);
259   ASSERT_EQ(r, CUBEB_OK);
260   ASSERT_NE(stream, nullptr);
261 
262   r = cubeb_stream_start(stream);
263   ASSERT_EQ(r, CUBEB_OK);
264 
265   delay(100);
266 
267   r = cubeb_stream_stop(stream);
268   ASSERT_EQ(r, CUBEB_OK);
269 
270   cubeb_stream_destroy(stream);
271   cubeb_destroy(ctx);
272 }
273 
274 static void
test_init_start_stop_destroy_multiple_streams(int early,int delay_ms)275 test_init_start_stop_destroy_multiple_streams(int early, int delay_ms)
276 {
277   size_t i;
278   int r;
279   cubeb * ctx;
280   cubeb_stream * stream[8];
281   cubeb_stream_params params;
282 
283   r = common_init(&ctx, "test_sanity");
284   ASSERT_EQ(r, CUBEB_OK);
285   ASSERT_NE(ctx, nullptr);
286 
287   params.format = STREAM_FORMAT;
288   params.rate = STREAM_RATE;
289   params.channels = STREAM_CHANNELS;
290   params.layout = STREAM_LAYOUT;
291   params.prefs = CUBEB_STREAM_PREF_NONE;
292 
293   for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
294     r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
295                           test_data_callback, test_state_callback, &dummy);
296     ASSERT_EQ(r, CUBEB_OK);
297     ASSERT_NE(stream[i], nullptr);
298     if (early) {
299       r = cubeb_stream_start(stream[i]);
300       ASSERT_EQ(r, CUBEB_OK);
301     }
302   }
303 
304   if (!early) {
305     for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
306       r = cubeb_stream_start(stream[i]);
307       ASSERT_EQ(r, CUBEB_OK);
308     }
309   }
310 
311   if (delay_ms) {
312     delay(delay_ms);
313   }
314 
315   if (!early) {
316     for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
317       r = cubeb_stream_stop(stream[i]);
318       ASSERT_EQ(r, CUBEB_OK);
319     }
320   }
321 
322   for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
323     if (early) {
324       r = cubeb_stream_stop(stream[i]);
325       ASSERT_EQ(r, CUBEB_OK);
326     }
327     cubeb_stream_destroy(stream[i]);
328   }
329 
330   cubeb_destroy(ctx);
331 }
332 
TEST(cubeb,init_start_stop_destroy_multiple_streams)333 TEST(cubeb, init_start_stop_destroy_multiple_streams)
334 {
335   /* Sometimes, when using WASAPI on windows 7 (vista and 8 are okay), and
336    * calling Activate a lot on an AudioClient, 0x800700b7 is returned. This is
337    * the HRESULT value for "Cannot create a file when that file already exists",
338    * and is not documented as a possible return value for this call. Hence, we
339    * try to limit the number of streams we create in this test. */
340   if (!is_windows_7()) {
341     delay_callback = 0;
342     test_init_start_stop_destroy_multiple_streams(0, 0);
343     test_init_start_stop_destroy_multiple_streams(1, 0);
344     test_init_start_stop_destroy_multiple_streams(0, 150);
345     test_init_start_stop_destroy_multiple_streams(1, 150);
346     delay_callback = 1;
347     test_init_start_stop_destroy_multiple_streams(0, 0);
348     test_init_start_stop_destroy_multiple_streams(1, 0);
349     test_init_start_stop_destroy_multiple_streams(0, 150);
350     test_init_start_stop_destroy_multiple_streams(1, 150);
351   }
352 }
353 
TEST(cubeb,init_destroy_multiple_contexts_and_streams)354 TEST(cubeb, init_destroy_multiple_contexts_and_streams)
355 {
356   size_t i, j;
357   int r;
358   cubeb * ctx[2];
359   cubeb_stream * stream[8];
360   cubeb_stream_params params;
361   size_t streams_per_ctx = ARRAY_LENGTH(stream) / ARRAY_LENGTH(ctx);
362   ASSERT_EQ(ARRAY_LENGTH(ctx) * streams_per_ctx, ARRAY_LENGTH(stream));
363 
364   /* Sometimes, when using WASAPI on windows 7 (vista and 8 are okay), and
365    * calling Activate a lot on an AudioClient, 0x800700b7 is returned. This is
366    * the HRESULT value for "Cannot create a file when that file already exists",
367    * and is not documented as a possible return value for this call. Hence, we
368    * try to limit the number of streams we create in this test. */
369   if (is_windows_7())
370     return;
371 
372   params.format = STREAM_FORMAT;
373   params.rate = STREAM_RATE;
374   params.channels = STREAM_CHANNELS;
375   params.layout = STREAM_LAYOUT;
376   params.prefs = CUBEB_STREAM_PREF_NONE;
377 
378   for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
379     r = common_init(&ctx[i], "test_sanity");
380     ASSERT_EQ(r, CUBEB_OK);
381     ASSERT_NE(ctx[i], nullptr);
382 
383     for (j = 0; j < streams_per_ctx; ++j) {
384       r = cubeb_stream_init(ctx[i], &stream[i * streams_per_ctx + j], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
385                             test_data_callback, test_state_callback, &dummy);
386       ASSERT_EQ(r, CUBEB_OK);
387       ASSERT_NE(stream[i * streams_per_ctx + j], nullptr);
388     }
389   }
390 
391   for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
392     for (j = 0; j < streams_per_ctx; ++j) {
393       cubeb_stream_destroy(stream[i * streams_per_ctx + j]);
394     }
395     cubeb_destroy(ctx[i]);
396   }
397 }
398 
TEST(cubeb,basic_stream_operations)399 TEST(cubeb, basic_stream_operations)
400 {
401   int r;
402   cubeb * ctx;
403   cubeb_stream * stream;
404   cubeb_stream_params params;
405   uint64_t position;
406 
407   r = common_init(&ctx, "test_sanity");
408   ASSERT_EQ(r, CUBEB_OK);
409   ASSERT_NE(ctx, nullptr);
410 
411   params.format = STREAM_FORMAT;
412   params.rate = STREAM_RATE;
413   params.channels = STREAM_CHANNELS;
414   params.layout = STREAM_LAYOUT;
415   params.prefs = CUBEB_STREAM_PREF_NONE;
416 
417   r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
418                         test_data_callback, test_state_callback, &dummy);
419   ASSERT_EQ(r, CUBEB_OK);
420   ASSERT_NE(stream, nullptr);
421 
422   /* position and volume before stream has started */
423   r = cubeb_stream_get_position(stream, &position);
424   ASSERT_EQ(r, CUBEB_OK);
425   ASSERT_EQ(position, 0u);
426 
427   r = cubeb_stream_start(stream);
428   ASSERT_EQ(r, CUBEB_OK);
429 
430   /* position and volume after while stream running */
431   r = cubeb_stream_get_position(stream, &position);
432   ASSERT_EQ(r, CUBEB_OK);
433 
434   r = cubeb_stream_stop(stream);
435   ASSERT_EQ(r, CUBEB_OK);
436 
437   /* position and volume after stream has stopped */
438   r = cubeb_stream_get_position(stream, &position);
439   ASSERT_EQ(r, CUBEB_OK);
440 
441   cubeb_stream_destroy(stream);
442   cubeb_destroy(ctx);
443 }
444 
TEST(cubeb,stream_position)445 TEST(cubeb, stream_position)
446 {
447   size_t i;
448   int r;
449   cubeb * ctx;
450   cubeb_stream * stream;
451   cubeb_stream_params params;
452   uint64_t position, last_position;
453 
454   total_frames_written = 0;
455 
456   r = common_init(&ctx, "test_sanity");
457   ASSERT_EQ(r, CUBEB_OK);
458   ASSERT_NE(ctx, nullptr);
459 
460   params.format = STREAM_FORMAT;
461   params.rate = STREAM_RATE;
462   params.channels = STREAM_CHANNELS;
463   params.layout = STREAM_LAYOUT;
464   params.prefs = CUBEB_STREAM_PREF_NONE;
465 
466   r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
467                         test_data_callback, test_state_callback, &dummy);
468   ASSERT_EQ(r, CUBEB_OK);
469   ASSERT_NE(stream, nullptr);
470 
471   /* stream position should not advance before starting playback */
472   r = cubeb_stream_get_position(stream, &position);
473   ASSERT_EQ(r, CUBEB_OK);
474   ASSERT_EQ(position, 0u);
475 
476   delay(500);
477 
478   r = cubeb_stream_get_position(stream, &position);
479   ASSERT_EQ(r, CUBEB_OK);
480   ASSERT_EQ(position, 0u);
481 
482   /* stream position should advance during playback */
483   r = cubeb_stream_start(stream);
484   ASSERT_EQ(r, CUBEB_OK);
485 
486   /* XXX let start happen */
487   delay(500);
488 
489   /* stream should have prefilled */
490   ASSERT_TRUE(total_frames_written.load() > 0);
491 
492   r = cubeb_stream_get_position(stream, &position);
493   ASSERT_EQ(r, CUBEB_OK);
494   last_position = position;
495 
496   delay(500);
497 
498   r = cubeb_stream_get_position(stream, &position);
499   ASSERT_EQ(r, CUBEB_OK);
500   ASSERT_GE(position, last_position);
501   last_position = position;
502 
503   /* stream position should not exceed total frames written */
504   for (i = 0; i < 5; ++i) {
505     r = cubeb_stream_get_position(stream, &position);
506     ASSERT_EQ(r, CUBEB_OK);
507     ASSERT_GE(position, last_position);
508     ASSERT_LE(position, total_frames_written.load());
509     last_position = position;
510     delay(500);
511   }
512 
513   /* test that the position is valid even when starting and
514    * stopping the stream.  */
515   for (i = 0; i < 5; ++i) {
516     r = cubeb_stream_stop(stream);
517     ASSERT_EQ(r, CUBEB_OK);
518     r = cubeb_stream_get_position(stream, &position);
519     ASSERT_EQ(r, CUBEB_OK);
520     ASSERT_TRUE(last_position < position);
521     last_position = position;
522     delay(500);
523     r = cubeb_stream_start(stream);
524     ASSERT_EQ(r, CUBEB_OK);
525     delay(500);
526   }
527 
528   ASSERT_NE(last_position, 0u);
529 
530   /* stream position should not advance after stopping playback */
531   r = cubeb_stream_stop(stream);
532   ASSERT_EQ(r, CUBEB_OK);
533 
534   /* XXX allow stream to settle */
535   delay(500);
536 
537   r = cubeb_stream_get_position(stream, &position);
538   ASSERT_EQ(r, CUBEB_OK);
539   last_position = position;
540 
541   delay(500);
542 
543   r = cubeb_stream_get_position(stream, &position);
544   ASSERT_EQ(r, CUBEB_OK);
545   ASSERT_EQ(position, last_position);
546 
547   cubeb_stream_destroy(stream);
548   cubeb_destroy(ctx);
549 }
550 
551 static std::atomic<int> do_drain;
552 static std::atomic<int> got_drain;
553 
554 static long
test_drain_data_callback(cubeb_stream * stm,void * user_ptr,const void *,void * outputbuffer,long nframes)555 test_drain_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
556 {
557   EXPECT_TRUE(stm && user_ptr == &dummy && outputbuffer && nframes > 0);
558   assert(outputbuffer);
559   if (do_drain == 1) {
560     do_drain = 2;
561     return 0;
562   }
563   /* once drain has started, callback must never be called again */
564   EXPECT_TRUE(do_drain != 2);
565   memset(outputbuffer, 0, nframes * sizeof(short));
566   total_frames_written += nframes;
567   return nframes;
568 }
569 
570 void
test_drain_state_callback(cubeb_stream *,void *,cubeb_state state)571 test_drain_state_callback(cubeb_stream * /*stm*/, void * /*user_ptr*/, cubeb_state state)
572 {
573   if (state == CUBEB_STATE_DRAINED) {
574     ASSERT_TRUE(!got_drain);
575     got_drain = 1;
576   }
577 }
578 
TEST(cubeb,drain)579 TEST(cubeb, drain)
580 {
581   int r;
582   cubeb * ctx;
583   cubeb_stream * stream;
584   cubeb_stream_params params;
585   uint64_t position;
586 
587   delay_callback = 0;
588   total_frames_written = 0;
589 
590   r = common_init(&ctx, "test_sanity");
591   ASSERT_EQ(r, CUBEB_OK);
592   ASSERT_NE(ctx, nullptr);
593 
594   params.format = STREAM_FORMAT;
595   params.rate = STREAM_RATE;
596   params.channels = STREAM_CHANNELS;
597   params.layout = STREAM_LAYOUT;
598   params.prefs = CUBEB_STREAM_PREF_NONE;
599 
600   r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
601                         test_drain_data_callback, test_drain_state_callback, &dummy);
602   ASSERT_EQ(r, CUBEB_OK);
603   ASSERT_NE(stream, nullptr);
604 
605   r = cubeb_stream_start(stream);
606   ASSERT_EQ(r, CUBEB_OK);
607 
608   delay(500);
609 
610   do_drain = 1;
611 
612   for (;;) {
613     r = cubeb_stream_get_position(stream, &position);
614     ASSERT_EQ(r, CUBEB_OK);
615     if (got_drain) {
616       break;
617     } else {
618       ASSERT_LE(position, total_frames_written.load());
619     }
620     delay(500);
621   }
622 
623   r = cubeb_stream_get_position(stream, &position);
624   ASSERT_EQ(r, CUBEB_OK);
625   ASSERT_TRUE(got_drain);
626 
627   // Really, we should be able to rely on position reaching our final written frame, but
628   // for now let's make sure it doesn't continue beyond that point.
629   //ASSERT_LE(position, total_frames_written.load());
630 
631   cubeb_stream_destroy(stream);
632   cubeb_destroy(ctx);
633 
634   got_drain = 0;
635   do_drain = 0;
636 }
637 
TEST(cubeb,DISABLED_eos_during_prefill)638 TEST(cubeb, DISABLED_eos_during_prefill)
639 {
640   // This test needs to be implemented.
641 }
642 
TEST(cubeb,DISABLED_stream_destroy_pending_drain)643 TEST(cubeb, DISABLED_stream_destroy_pending_drain)
644 {
645   // This test needs to be implemented.
646 }
647 
TEST(cubeb,stable_devid)648 TEST(cubeb, stable_devid)
649 {
650   /* Test that the devid field of cubeb_device_info is stable
651    * (ie. compares equal) over two invocations of
652    * cubeb_enumerate_devices(). */
653 
654   int r;
655   cubeb * ctx;
656   cubeb_device_collection first;
657   cubeb_device_collection second;
658   cubeb_device_type all_devices =
659     (cubeb_device_type) (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT);
660   size_t n;
661 
662   r = common_init(&ctx, "test_sanity");
663   ASSERT_EQ(r, CUBEB_OK);
664   ASSERT_NE(ctx, nullptr);
665 
666   r = cubeb_enumerate_devices(ctx, all_devices, &first);
667   if (r == CUBEB_ERROR_NOT_SUPPORTED)
668     return;
669 
670   ASSERT_EQ(r, CUBEB_OK);
671 
672   r = cubeb_enumerate_devices(ctx, all_devices, &second);
673   ASSERT_EQ(r, CUBEB_OK);
674 
675   ASSERT_EQ(first.count, second.count);
676   for (n = 0; n < first.count; n++) {
677     ASSERT_EQ(first.device[n].devid, second.device[n].devid);
678   }
679 
680   r = cubeb_device_collection_destroy(ctx, &first);
681   ASSERT_EQ(r, CUBEB_OK);
682   r = cubeb_device_collection_destroy(ctx, &second);
683   ASSERT_EQ(r, CUBEB_OK);
684   cubeb_destroy(ctx);
685 }
686