tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_sanity.cpp (18890B)


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