diff options
Diffstat (limited to 'media/libcubeb/tests/test_sanity.cpp')
-rw-r--r-- | media/libcubeb/tests/test_sanity.cpp | 676 |
1 files changed, 676 insertions, 0 deletions
diff --git a/media/libcubeb/tests/test_sanity.cpp b/media/libcubeb/tests/test_sanity.cpp new file mode 100644 index 000000000..77973ff15 --- /dev/null +++ b/media/libcubeb/tests/test_sanity.cpp @@ -0,0 +1,676 @@ +/* + * Copyright © 2011 Mozilla Foundation + * + * This program is made available under an ISC-style license. See the + * accompanying file LICENSE for details. + */ +#ifdef NDEBUG +#undef NDEBUG +#endif +#define _XOPEN_SOURCE 600 +#include "cubeb/cubeb.h" +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include "common.h" +#ifdef CUBEB_GECKO_BUILD +#include "TestHarness.h" +#endif + +#define BEGIN_TEST fprintf(stderr, "START %s\n", __func__) +#define END_TEST fprintf(stderr, "END %s\n", __func__) + +#define STREAM_RATE 44100 +#define STREAM_LATENCY 100 * STREAM_RATE / 1000 +#define STREAM_CHANNELS 1 +#if (defined(_WIN32) || defined(__WIN32__)) +#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE +#else +#define STREAM_FORMAT CUBEB_SAMPLE_S16LE +#endif + +template<typename T, size_t N> +constexpr size_t +ARRAY_LENGTH(T(&)[N]) +{ + return N; +} + +static int dummy; +static uint64_t total_frames_written; +static int delay_callback; + +static long +test_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inputbuffer*/, void * outputbuffer, long nframes) +{ + assert(stm && user_ptr == &dummy && outputbuffer && nframes > 0); +#if (defined(_WIN32) || defined(__WIN32__)) + memset(outputbuffer, 0, nframes * sizeof(float)); +#else + memset(outputbuffer, 0, nframes * sizeof(short)); +#endif + + total_frames_written += nframes; + if (delay_callback) { + delay(10); + } + return nframes; +} + +void +test_state_callback(cubeb_stream * /*stm*/, void * /*user_ptr*/, cubeb_state /*state*/) +{ +} + +static void +test_init_destroy_context(void) +{ + int r; + cubeb * ctx; + char const* backend_id; + + BEGIN_TEST; + + r = cubeb_init(&ctx, "test_sanity"); + assert(r == 0 && ctx); + + + backend_id = cubeb_get_backend_id(ctx); + assert(backend_id); + + fprintf(stderr, "Backend: %s\n", backend_id); + + cubeb_destroy(ctx); + + END_TEST; +} + +static void +test_init_destroy_multiple_contexts(void) +{ + size_t i; + int r; + cubeb * ctx[4]; + int order[4] = {2, 0, 3, 1}; + assert(ARRAY_LENGTH(ctx) == ARRAY_LENGTH(order)); + + BEGIN_TEST; + + for (i = 0; i < ARRAY_LENGTH(ctx); ++i) { + r = cubeb_init(&ctx[i], NULL); + assert(r == 0 && ctx[i]); + } + + /* destroy in a different order */ + for (i = 0; i < ARRAY_LENGTH(ctx); ++i) { + cubeb_destroy(ctx[order[i]]); + } + + END_TEST; +} + +static void +test_context_variables(void) +{ + int r; + cubeb * ctx; + uint32_t value; + cubeb_stream_params params; + + BEGIN_TEST; + + r = cubeb_init(&ctx, "test_context_variables"); + assert(r == 0 && ctx); + + params.channels = STREAM_CHANNELS; + params.format = STREAM_FORMAT; + params.rate = STREAM_RATE; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif + r = cubeb_get_min_latency(ctx, params, &value); + assert(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED); + if (r == CUBEB_OK) { + assert(value > 0); + } + + r = cubeb_get_preferred_sample_rate(ctx, &value); + assert(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED); + if (r == CUBEB_OK) { + assert(value > 0); + } + + cubeb_destroy(ctx); + + END_TEST; +} + +static void +test_init_destroy_stream(void) +{ + int r; + cubeb * ctx; + cubeb_stream * stream; + cubeb_stream_params params; + + BEGIN_TEST; + + r = cubeb_init(&ctx, "test_sanity"); + assert(r == 0 && ctx); + + params.format = STREAM_FORMAT; + params.rate = STREAM_RATE; + params.channels = STREAM_CHANNELS; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif + + r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, + test_data_callback, test_state_callback, &dummy); + assert(r == 0 && stream); + + cubeb_stream_destroy(stream); + cubeb_destroy(ctx); + + END_TEST; +} + +static void +test_init_destroy_multiple_streams(void) +{ + size_t i; + int r; + cubeb * ctx; + cubeb_stream * stream[8]; + cubeb_stream_params params; + + BEGIN_TEST; + + r = cubeb_init(&ctx, "test_sanity"); + assert(r == 0 && ctx); + + params.format = STREAM_FORMAT; + params.rate = STREAM_RATE; + params.channels = STREAM_CHANNELS; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif + + for (i = 0; i < ARRAY_LENGTH(stream); ++i) { + r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, + test_data_callback, test_state_callback, &dummy); + assert(r == 0); + assert(stream[i]); + } + + for (i = 0; i < ARRAY_LENGTH(stream); ++i) { + cubeb_stream_destroy(stream[i]); + } + + cubeb_destroy(ctx); + + END_TEST; +} + +static void +test_configure_stream(void) +{ + int r; + cubeb * ctx; + cubeb_stream * stream; + cubeb_stream_params params; + + BEGIN_TEST; + + r = cubeb_init(&ctx, "test_sanity"); + assert(r == 0 && ctx); + + params.format = STREAM_FORMAT; + params.rate = STREAM_RATE; + params.channels = 2; // panning +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif + + r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, + test_data_callback, test_state_callback, &dummy); + assert(r == 0 && stream); + + r = cubeb_stream_set_volume(stream, 1.0f); + assert(r == 0 || r == CUBEB_ERROR_NOT_SUPPORTED); + + r = cubeb_stream_set_panning(stream, 0.0f); + assert(r == 0 || r == CUBEB_ERROR_NOT_SUPPORTED); + + cubeb_stream_destroy(stream); + cubeb_destroy(ctx); + END_TEST; +} + +static void +test_init_start_stop_destroy_multiple_streams(int early, int delay_ms) +{ + size_t i; + int r; + cubeb * ctx; + cubeb_stream * stream[8]; + cubeb_stream_params params; + + BEGIN_TEST; + + r = cubeb_init(&ctx, "test_sanity"); + assert(r == 0 && ctx); + + params.format = STREAM_FORMAT; + params.rate = STREAM_RATE; + params.channels = STREAM_CHANNELS; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif + + for (i = 0; i < ARRAY_LENGTH(stream); ++i) { + r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, + test_data_callback, test_state_callback, &dummy); + assert(r == 0); + assert(stream[i]); + if (early) { + r = cubeb_stream_start(stream[i]); + assert(r == 0); + } + } + + + if (!early) { + for (i = 0; i < ARRAY_LENGTH(stream); ++i) { + r = cubeb_stream_start(stream[i]); + assert(r == 0); + } + } + + if (delay_ms) { + delay(delay_ms); + } + + if (!early) { + for (i = 0; i < ARRAY_LENGTH(stream); ++i) { + r = cubeb_stream_stop(stream[i]); + assert(r == 0); + } + } + + for (i = 0; i < ARRAY_LENGTH(stream); ++i) { + if (early) { + r = cubeb_stream_stop(stream[i]); + assert(r == 0); + } + cubeb_stream_destroy(stream[i]); + } + + cubeb_destroy(ctx); + + END_TEST; +} + +static void +test_init_destroy_multiple_contexts_and_streams(void) +{ + size_t i, j; + int r; + cubeb * ctx[2]; + cubeb_stream * stream[8]; + cubeb_stream_params params; + size_t streams_per_ctx = ARRAY_LENGTH(stream) / ARRAY_LENGTH(ctx); + assert(ARRAY_LENGTH(ctx) * streams_per_ctx == ARRAY_LENGTH(stream)); + + BEGIN_TEST; + + params.format = STREAM_FORMAT; + params.rate = STREAM_RATE; + params.channels = STREAM_CHANNELS; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif + + for (i = 0; i < ARRAY_LENGTH(ctx); ++i) { + r = cubeb_init(&ctx[i], "test_sanity"); + assert(r == 0 && ctx[i]); + + for (j = 0; j < streams_per_ctx; ++j) { + r = cubeb_stream_init(ctx[i], &stream[i * streams_per_ctx + j], "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, + test_data_callback, test_state_callback, &dummy); + assert(r == 0); + assert(stream[i * streams_per_ctx + j]); + } + } + + for (i = 0; i < ARRAY_LENGTH(ctx); ++i) { + for (j = 0; j < streams_per_ctx; ++j) { + cubeb_stream_destroy(stream[i * streams_per_ctx + j]); + } + cubeb_destroy(ctx[i]); + } + + END_TEST; +} + +static void +test_basic_stream_operations(void) +{ + int r; + cubeb * ctx; + cubeb_stream * stream; + cubeb_stream_params params; + uint64_t position; + + BEGIN_TEST; + + r = cubeb_init(&ctx, "test_sanity"); + assert(r == 0 && ctx); + + params.format = STREAM_FORMAT; + params.rate = STREAM_RATE; + params.channels = STREAM_CHANNELS; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif + + r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, + test_data_callback, test_state_callback, &dummy); + assert(r == 0 && stream); + + /* position and volume before stream has started */ + r = cubeb_stream_get_position(stream, &position); + assert(r == 0 && position == 0); + + r = cubeb_stream_start(stream); + assert(r == 0); + + /* position and volume after while stream running */ + r = cubeb_stream_get_position(stream, &position); + assert(r == 0); + + r = cubeb_stream_stop(stream); + assert(r == 0); + + /* position and volume after stream has stopped */ + r = cubeb_stream_get_position(stream, &position); + assert(r == 0); + + cubeb_stream_destroy(stream); + cubeb_destroy(ctx); + + END_TEST; +} + +static void +test_stream_position(void) +{ + size_t i; + int r; + cubeb * ctx; + cubeb_stream * stream; + cubeb_stream_params params; + uint64_t position, last_position; + + BEGIN_TEST; + + total_frames_written = 0; + + r = cubeb_init(&ctx, "test_sanity"); + assert(r == 0 && ctx); + + params.format = STREAM_FORMAT; + params.rate = STREAM_RATE; + params.channels = STREAM_CHANNELS; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif + + r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, + test_data_callback, test_state_callback, &dummy); + assert(r == 0 && stream); + + /* stream position should not advance before starting playback */ + r = cubeb_stream_get_position(stream, &position); + assert(r == 0 && position == 0); + + delay(500); + + r = cubeb_stream_get_position(stream, &position); + assert(r == 0 && position == 0); + + /* stream position should advance during playback */ + r = cubeb_stream_start(stream); + assert(r == 0); + + /* XXX let start happen */ + delay(500); + + /* stream should have prefilled */ + assert(total_frames_written > 0); + + r = cubeb_stream_get_position(stream, &position); + assert(r == 0); + last_position = position; + + delay(500); + + r = cubeb_stream_get_position(stream, &position); + assert(r == 0); + assert(position >= last_position); + last_position = position; + + /* stream position should not exceed total frames written */ + for (i = 0; i < 5; ++i) { + r = cubeb_stream_get_position(stream, &position); + assert(r == 0); + assert(position >= last_position); + assert(position <= total_frames_written); + last_position = position; + delay(500); + } + + /* test that the position is valid even when starting and + * stopping the stream. */ + for (i = 0; i < 5; ++i) { + r = cubeb_stream_stop(stream); + assert(r == 0); + r = cubeb_stream_get_position(stream, &position); + assert(r == 0); + assert(last_position < position); + last_position = position; + delay(500); + r = cubeb_stream_start(stream); + assert(r == 0); + delay(500); + } + + assert(last_position != 0); + + /* stream position should not advance after stopping playback */ + r = cubeb_stream_stop(stream); + assert(r == 0); + + /* XXX allow stream to settle */ + delay(500); + + r = cubeb_stream_get_position(stream, &position); + assert(r == 0); + last_position = position; + + delay(500); + + r = cubeb_stream_get_position(stream, &position); + assert(r == 0); + assert(position == last_position); + + cubeb_stream_destroy(stream); + cubeb_destroy(ctx); + + END_TEST; +} + +static int do_drain; +static int got_drain; + +static long +test_drain_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inputbuffer*/, void * outputbuffer, long nframes) +{ + assert(stm && user_ptr == &dummy && outputbuffer && nframes > 0); + if (do_drain == 1) { + do_drain = 2; + return 0; + } + /* once drain has started, callback must never be called again */ + assert(do_drain != 2); +#if (defined(_WIN32) || defined(__WIN32__)) + memset(outputbuffer, 0, nframes * sizeof(float)); +#else + memset(outputbuffer, 0, nframes * sizeof(short)); +#endif + total_frames_written += nframes; + return nframes; +} + +void +test_drain_state_callback(cubeb_stream * /*stm*/, void * /*user_ptr*/, cubeb_state state) +{ + if (state == CUBEB_STATE_DRAINED) { + assert(!got_drain); + got_drain = 1; + } +} + +static void +test_drain(void) +{ + int r; + cubeb * ctx; + cubeb_stream * stream; + cubeb_stream_params params; + uint64_t position; + + BEGIN_TEST; + + total_frames_written = 0; + + r = cubeb_init(&ctx, "test_sanity"); + assert(r == 0 && ctx); + + params.format = STREAM_FORMAT; + params.rate = STREAM_RATE; + params.channels = STREAM_CHANNELS; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif + + r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, + test_drain_data_callback, test_drain_state_callback, &dummy); + assert(r == 0 && stream); + + r = cubeb_stream_start(stream); + assert(r == 0); + + delay(500); + + do_drain = 1; + + for (;;) { + r = cubeb_stream_get_position(stream, &position); + assert(r == 0); + if (got_drain) { + break; + } else { + assert(position <= total_frames_written); + } + delay(500); + } + + r = cubeb_stream_get_position(stream, &position); + assert(r == 0); + assert(got_drain); + + // Really, we should be able to rely on position reaching our final written frame, but + // for now let's make sure it doesn't continue beyond that point. + //assert(position <= total_frames_written); + + cubeb_stream_destroy(stream); + cubeb_destroy(ctx); + + END_TEST; +} + +int is_windows_7() +{ +#ifdef __MINGW32__ + printf("Warning: this test was built with MinGW.\n" + "MinGW does not contain necessary version checking infrastructure. Claiming to be Windows 7, even if we're not.\n"); + return 1; +#endif +#if (defined(_WIN32) || defined(__WIN32__)) && ( !defined(__MINGW32__)) + OSVERSIONINFOEX osvi; + DWORDLONG condition_mask = 0; + + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + // NT 6.1 is Windows 7 + osvi.dwMajorVersion = 6; + osvi.dwMinorVersion = 1; + + VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, VER_EQUAL); + VER_SET_CONDITION(condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL); + + return VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, condition_mask); +#else + return 0; +#endif +} + +int +main(int /*argc*/, char * /*argv*/[]) +{ +#ifdef CUBEB_GECKO_BUILD + ScopedXPCOM xpcom("test_sanity"); +#endif + + test_init_destroy_context(); + test_init_destroy_multiple_contexts(); + test_context_variables(); + test_init_destroy_stream(); + test_init_destroy_multiple_streams(); + test_configure_stream(); + test_basic_stream_operations(); + test_stream_position(); + + /* Sometimes, when using WASAPI on windows 7 (vista and 8 are okay), and + * calling Activate a lot on an AudioClient, 0x800700b7 is returned. This is + * the HRESULT value for "Cannot create a file when that file already exists", + * and is not documented as a possible return value for this call. Hence, we + * try to limit the number of streams we create in this test. */ + if (!is_windows_7()) { + test_init_destroy_multiple_contexts_and_streams(); + + delay_callback = 0; + test_init_start_stop_destroy_multiple_streams(0, 0); + test_init_start_stop_destroy_multiple_streams(1, 0); + test_init_start_stop_destroy_multiple_streams(0, 150); + test_init_start_stop_destroy_multiple_streams(1, 150); + delay_callback = 1; + test_init_start_stop_destroy_multiple_streams(0, 0); + test_init_start_stop_destroy_multiple_streams(1, 0); + test_init_start_stop_destroy_multiple_streams(0, 150); + test_init_start_stop_destroy_multiple_streams(1, 150); + } + delay_callback = 0; + test_drain(); +/* + to implement: + test_eos_during_prefill(); + test_stream_destroy_pending_drain(); +*/ + printf("\n"); + + return 0; +} |