Fri, 16 Jan 2015 04:50:19 +0100
Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32
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 #ifdef NDEBUG
8 #undef NDEBUG
9 #endif
10 #define _XOPEN_SOURCE 500
11 #include "cubeb/cubeb.h"
12 #include <assert.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <math.h>
16 #include "common.h"
18 #if (defined(_WIN32) || defined(__WIN32__))
19 #define __func__ __FUNCTION__
20 #endif
22 #define ARRAY_LENGTH(_x) (sizeof(_x) / sizeof(_x[0]))
23 #define BEGIN_TEST fprintf(stderr, "START %s\n", __func__);
24 #define END_TEST fprintf(stderr, "END %s\n", __func__);
26 #define STREAM_LATENCY 100
27 #define STREAM_RATE 44100
28 #define STREAM_CHANNELS 1
29 #define STREAM_FORMAT CUBEB_SAMPLE_S16LE
31 static int dummy;
32 static uint64_t total_frames_written;
33 static int delay_callback;
35 static long
36 test_data_callback(cubeb_stream * stm, void * user_ptr, void * p, long nframes)
37 {
38 assert(stm && user_ptr == &dummy && p && nframes > 0);
39 memset(p, 0, nframes * sizeof(short));
40 total_frames_written += nframes;
41 if (delay_callback) {
42 delay(10);
43 }
44 return nframes;
45 }
47 void
48 test_state_callback(cubeb_stream * stm, void * user_ptr, cubeb_state state)
49 {
50 }
52 static void
53 test_init_destroy_context(void)
54 {
55 int r;
56 cubeb * ctx;
58 BEGIN_TEST
60 r = cubeb_init(&ctx, "test_sanity");
61 assert(r == 0 && ctx);
63 cubeb_destroy(ctx);
65 END_TEST
66 }
68 static void
69 test_init_destroy_multiple_contexts(void)
70 {
71 int i;
72 int r;
73 cubeb * ctx[4];
75 BEGIN_TEST
77 for (i = 0; i < 4; ++i) {
78 r = cubeb_init(&ctx[i], NULL);
79 assert(r == 0 && ctx[i]);
80 }
82 /* destroy in a different order */
83 cubeb_destroy(ctx[2]);
84 cubeb_destroy(ctx[0]);
85 cubeb_destroy(ctx[3]);
86 cubeb_destroy(ctx[1]);
88 END_TEST
89 }
91 static void
92 test_init_destroy_stream(void)
93 {
94 int r;
95 cubeb * ctx;
96 cubeb_stream * stream;
97 cubeb_stream_params params;
99 BEGIN_TEST
101 r = cubeb_init(&ctx, "test_sanity");
102 assert(r == 0 && ctx);
104 params.format = STREAM_FORMAT;
105 params.rate = STREAM_RATE;
106 params.channels = STREAM_CHANNELS;
108 r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
109 test_data_callback, test_state_callback, &dummy);
110 assert(r == 0 && stream);
112 cubeb_stream_destroy(stream);
113 cubeb_destroy(ctx);
115 END_TEST
116 }
118 static void
119 test_init_destroy_multiple_streams(void)
120 {
121 int i;
122 int r;
123 cubeb * ctx;
124 cubeb_stream * stream[16];
125 cubeb_stream_params params;
127 BEGIN_TEST
129 r = cubeb_init(&ctx, "test_sanity");
130 assert(r == 0 && ctx);
132 params.format = STREAM_FORMAT;
133 params.rate = STREAM_RATE;
134 params.channels = STREAM_CHANNELS;
136 for (i = 0; i < 16; ++i) {
137 r = cubeb_stream_init(ctx, &stream[i], "test", params, STREAM_LATENCY,
138 test_data_callback, test_state_callback, &dummy);
139 assert(r == 0);
140 assert(stream[i]);
141 }
143 for (i = 0; i < 16; ++i) {
144 cubeb_stream_destroy(stream[i]);
145 }
147 cubeb_destroy(ctx);
149 END_TEST
150 }
152 static void
153 test_init_start_stop_destroy_multiple_streams(int early, int delay_ms)
154 {
155 int i;
156 int r;
157 cubeb * ctx;
158 cubeb_stream * stream[16];
159 cubeb_stream_params params;
161 BEGIN_TEST
163 r = cubeb_init(&ctx, "test_sanity");
164 assert(r == 0 && ctx);
166 params.format = STREAM_FORMAT;
167 params.rate = STREAM_RATE;
168 params.channels = STREAM_CHANNELS;
170 for (i = 0; i < 16; ++i) {
171 r = cubeb_stream_init(ctx, &stream[i], "test", params, STREAM_LATENCY,
172 test_data_callback, test_state_callback, &dummy);
173 assert(r == 0);
174 assert(stream[i]);
175 if (early) {
176 r = cubeb_stream_start(stream[i]);
177 assert(r == 0);
178 }
179 }
182 if (!early) {
183 for (i = 0; i < 16; ++i) {
184 r = cubeb_stream_start(stream[i]);
185 assert(r == 0);
186 }
187 }
189 if (delay_ms) {
190 delay(delay_ms);
191 }
193 if (!early) {
194 for (i = 0; i < 16; ++i) {
195 r = cubeb_stream_stop(stream[i]);
196 assert(r == 0);
197 }
198 }
200 for (i = 0; i < 16; ++i) {
201 if (early) {
202 r = cubeb_stream_stop(stream[i]);
203 assert(r == 0);
204 }
205 cubeb_stream_destroy(stream[i]);
206 }
208 cubeb_destroy(ctx);
210 END_TEST
211 }
213 static void
214 test_init_destroy_multiple_contexts_and_streams(void)
215 {
216 int i, j;
217 int r;
218 cubeb * ctx[4];
219 cubeb_stream * stream[16];
220 cubeb_stream_params params;
222 BEGIN_TEST
224 params.format = STREAM_FORMAT;
225 params.rate = STREAM_RATE;
226 params.channels = STREAM_CHANNELS;
228 for (i = 0; i < 4; ++i) {
229 r = cubeb_init(&ctx[i], "test_sanity");
230 assert(r == 0 && ctx[i]);
232 for (j = 0; j < 4; ++j) {
233 r = cubeb_stream_init(ctx[i], &stream[i * 4 + j], "test", params, STREAM_LATENCY,
234 test_data_callback, test_state_callback, &dummy);
235 assert(r == 0);
236 assert(stream[i * 4 + j]);
237 }
238 }
240 for (i = 0; i < 4; ++i) {
241 for (j = 0; j < 4; ++j) {
242 cubeb_stream_destroy(stream[i * 4 + j]);
243 }
244 cubeb_destroy(ctx[i]);
245 }
247 END_TEST
248 }
250 static void
251 test_basic_stream_operations(void)
252 {
253 int r;
254 cubeb * ctx;
255 cubeb_stream * stream;
256 cubeb_stream_params params;
257 uint64_t position;
259 BEGIN_TEST
261 r = cubeb_init(&ctx, "test_sanity");
262 assert(r == 0 && ctx);
264 params.format = STREAM_FORMAT;
265 params.rate = STREAM_RATE;
266 params.channels = STREAM_CHANNELS;
268 r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
269 test_data_callback, test_state_callback, &dummy);
270 assert(r == 0 && stream);
272 /* position and volume before stream has started */
273 r = cubeb_stream_get_position(stream, &position);
274 assert(r == 0 && position == 0);
276 r = cubeb_stream_start(stream);
277 assert(r == 0);
279 /* position and volume after while stream running */
280 r = cubeb_stream_get_position(stream, &position);
281 assert(r == 0);
283 r = cubeb_stream_stop(stream);
284 assert(r == 0);
286 /* position and volume after stream has stopped */
287 r = cubeb_stream_get_position(stream, &position);
288 assert(r == 0);
290 cubeb_stream_destroy(stream);
291 cubeb_destroy(ctx);
293 END_TEST
294 }
296 static void
297 test_stream_position(void)
298 {
299 int i;
300 int r;
301 cubeb * ctx;
302 cubeb_stream * stream;
303 cubeb_stream_params params;
304 uint64_t position, last_position;
306 BEGIN_TEST
308 total_frames_written = 0;
310 r = cubeb_init(&ctx, "test_sanity");
311 assert(r == 0 && ctx);
313 params.format = STREAM_FORMAT;
314 params.rate = STREAM_RATE;
315 params.channels = STREAM_CHANNELS;
317 r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
318 test_data_callback, test_state_callback, &dummy);
319 assert(r == 0 && stream);
321 /* stream position should not advance before starting playback */
322 r = cubeb_stream_get_position(stream, &position);
323 assert(r == 0 && position == 0);
325 delay(500);
327 r = cubeb_stream_get_position(stream, &position);
328 assert(r == 0 && position == 0);
330 /* stream position should advance during playback */
331 r = cubeb_stream_start(stream);
332 assert(r == 0);
334 /* XXX let start happen */
335 delay(500);
337 /* stream should have prefilled */
338 assert(total_frames_written > 0);
340 r = cubeb_stream_get_position(stream, &position);
341 assert(r == 0);
342 last_position = position;
344 delay(500);
346 r = cubeb_stream_get_position(stream, &position);
347 assert(r == 0);
348 assert(position >= last_position);
349 last_position = position;
351 /* stream position should not exceed total frames written */
352 for (i = 0; i < 5; ++i) {
353 r = cubeb_stream_get_position(stream, &position);
354 assert(r == 0);
355 assert(position >= last_position);
356 assert(position <= total_frames_written);
357 last_position = position;
358 delay(500);
359 }
361 assert(last_position != 0);
363 /* stream position should not advance after stopping playback */
364 r = cubeb_stream_stop(stream);
365 assert(r == 0);
367 /* XXX allow stream to settle */
368 delay(500);
370 r = cubeb_stream_get_position(stream, &position);
371 assert(r == 0);
372 last_position = position;
374 delay(500);
376 r = cubeb_stream_get_position(stream, &position);
377 assert(r == 0);
378 assert(position == last_position);
380 cubeb_stream_destroy(stream);
381 cubeb_destroy(ctx);
383 END_TEST
384 }
386 static int do_drain;
387 static int got_drain;
389 static long
390 test_drain_data_callback(cubeb_stream * stm, void * user_ptr, void * p, long nframes)
391 {
392 assert(stm && user_ptr == &dummy && p && nframes > 0);
393 if (do_drain == 1) {
394 do_drain = 2;
395 return 0;
396 }
397 /* once drain has started, callback must never be called again */
398 assert(do_drain != 2);
399 memset(p, 0, nframes * sizeof(short));
400 total_frames_written += nframes;
401 return nframes;
402 }
404 void
405 test_drain_state_callback(cubeb_stream * stm, void * user_ptr, cubeb_state state)
406 {
407 if (state == CUBEB_STATE_DRAINED) {
408 assert(!got_drain);
409 got_drain = 1;
410 }
411 }
413 static void
414 test_drain(void)
415 {
416 int r;
417 cubeb * ctx;
418 cubeb_stream * stream;
419 cubeb_stream_params params;
420 uint64_t position;
422 BEGIN_TEST
424 total_frames_written = 0;
426 r = cubeb_init(&ctx, "test_sanity");
427 assert(r == 0 && ctx);
429 params.format = STREAM_FORMAT;
430 params.rate = STREAM_RATE;
431 params.channels = STREAM_CHANNELS;
433 r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
434 test_drain_data_callback, test_drain_state_callback, &dummy);
435 assert(r == 0 && stream);
437 r = cubeb_stream_start(stream);
438 assert(r == 0);
440 delay(500);
442 do_drain = 1;
444 for (;;) {
445 r = cubeb_stream_get_position(stream, &position);
446 assert(r == 0);
447 if (got_drain) {
448 break;
449 } else {
450 uint32_t i, skip = 0;
451 /* Latency passed to cubeb_stream_init is not really honored on OSX,
452 win32/winmm and android, skip this test. */
453 const char * backend_id = cubeb_get_backend_id(ctx);
454 const char * latency_not_honored_bakends[] = {
455 "audiounit",
456 "winmm",
457 "audiotrack",
458 "opensl"
459 };
461 for (i = 0; i < ARRAY_LENGTH(latency_not_honored_bakends); i++) {
462 if (!strcmp(backend_id, latency_not_honored_bakends[i])) {
463 skip = 1;
464 }
465 }
466 if (!skip) {
467 /* Position should roughly be equal to the number of written frames. We
468 * need to take the latency into account. */
469 int latency = (STREAM_LATENCY * STREAM_RATE) / 1000;
470 assert(position + latency <= total_frames_written);
471 }
472 }
473 delay(500);
474 }
476 r = cubeb_stream_get_position(stream, &position);
477 assert(r == 0);
478 assert(got_drain);
480 // Disabled due to failures in the ALSA backend.
481 //assert(position == total_frames_written);
483 cubeb_stream_destroy(stream);
484 cubeb_destroy(ctx);
486 END_TEST
487 }
489 int is_windows_7()
490 {
491 #if (defined(_WIN32) || defined(__WIN32__))
492 OSVERSIONINFOEX osvi;
493 DWORDLONG condition_mask = 0;
495 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
496 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
498 // NT 6.1 is Windows 7
499 osvi.dwMajorVersion = 6;
500 osvi.dwMinorVersion = 1;
502 VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, VER_EQUAL);
503 VER_SET_CONDITION(condition_mask, VER_MINORVERSION, VER_EQUAL);
505 return VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, condition_mask);
506 #else
507 return 0;
508 #endif
509 }
511 int
512 main(int argc, char * argv[])
513 {
514 test_init_destroy_context();
515 test_init_destroy_multiple_contexts();
516 test_init_destroy_stream();
517 test_init_destroy_multiple_streams();
518 test_basic_stream_operations();
519 test_stream_position();
521 /* Sometimes, when using WASAPI on windows 7 (vista and 8 are okay), and
522 * calling Activate a lot on an AudioClient, 0x800700b7 is returned. This is
523 * the HRESULT value for "Cannot create a file when that file already exists",
524 * and is not documented as a possible return value for this call. Hence, we
525 * try to limit the number of streams we create in this test. */
526 if (!is_windows_7()) {
527 test_init_destroy_multiple_contexts_and_streams();
529 delay_callback = 0;
530 test_init_start_stop_destroy_multiple_streams(0, 0);
531 test_init_start_stop_destroy_multiple_streams(1, 0);
532 test_init_start_stop_destroy_multiple_streams(0, 150);
533 test_init_start_stop_destroy_multiple_streams(1, 150);
534 delay_callback = 1;
535 test_init_start_stop_destroy_multiple_streams(0, 0);
536 test_init_start_stop_destroy_multiple_streams(1, 0);
537 test_init_start_stop_destroy_multiple_streams(0, 150);
538 test_init_start_stop_destroy_multiple_streams(1, 150);
539 }
540 delay_callback = 0;
541 test_drain();
542 /*
543 to implement:
544 test_eos_during_prefill();
545 test_stream_destroy_pending_drain();
546 */
547 printf("\n");
548 return 0;
549 }