test_logging.cpp (5203B)
1 /* 2 * Copyright © 2016 Mozilla Foundation 3 * 4 * This program is made available under an ISC-style license. See the 5 * accompanying file LICENSE for details. 6 */ 7 8 /* cubeb_logging test */ 9 #include "gtest/gtest.h" 10 #if !defined(_XOPEN_SOURCE) 11 #define _XOPEN_SOURCE 600 12 #endif 13 #include "cubeb/cubeb.h" 14 #include "cubeb_log.h" 15 #include <atomic> 16 #include <math.h> 17 #include <memory> 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <thread> 21 22 #include "common.h" 23 24 #define PRINT_LOGS_TO_STDERR 0 25 26 std::atomic<uint32_t> log_statements_received = {0}; 27 std::atomic<uint32_t> data_callback_call_count = {0}; 28 29 static void 30 test_logging_callback(char const * fmt, ...) 31 { 32 log_statements_received++; 33 #if PRINT_LOGS_TO_STDERR == 1 34 char buf[1024]; 35 va_list argslist; 36 va_start(argslist, fmt); 37 vsnprintf(buf, 1024, fmt, argslist); 38 fprintf(stderr, "%s\n", buf); 39 va_end(argslist); 40 #endif // PRINT_LOGS_TO_STDERR 41 } 42 43 static long 44 data_cb_load(cubeb_stream * stream, void * user, const void * inputbuffer, 45 void * outputbuffer, long nframes) 46 { 47 data_callback_call_count++; 48 return nframes; 49 } 50 51 static void 52 state_cb(cubeb_stream * stream, void * /*user*/, cubeb_state state) 53 { 54 if (stream == NULL) 55 return; 56 57 switch (state) { 58 case CUBEB_STATE_STARTED: 59 fprintf(stderr, "stream started\n"); 60 break; 61 case CUBEB_STATE_STOPPED: 62 fprintf(stderr, "stream stopped\n"); 63 break; 64 case CUBEB_STATE_DRAINED: 65 fprintf(stderr, "stream drained\n"); 66 break; 67 default: 68 fprintf(stderr, "unknown stream state %d\n", state); 69 } 70 71 return; 72 } 73 74 // Waits for at least one audio callback to have occured. 75 void 76 wait_for_audio_callback() 77 { 78 uint32_t audio_callback_index = 79 data_callback_call_count.load(std::memory_order_acquire); 80 while (audio_callback_index == 81 data_callback_call_count.load(std::memory_order_acquire)) { 82 delay(100); 83 } 84 } 85 86 TEST(cubeb, logging) 87 { 88 cubeb * ctx; 89 cubeb_stream * stream; 90 cubeb_stream_params output_params; 91 int r; 92 uint32_t latency_frames = 0; 93 94 cubeb_set_log_callback(CUBEB_LOG_NORMAL, test_logging_callback); 95 96 r = common_init(&ctx, "Cubeb logging test"); 97 ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library"; 98 99 std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit( 100 ctx, cubeb_destroy); 101 102 output_params.format = CUBEB_SAMPLE_FLOAT32LE; 103 output_params.rate = 48000; 104 output_params.channels = 2; 105 output_params.layout = CUBEB_LAYOUT_STEREO; 106 output_params.prefs = CUBEB_STREAM_PREF_NONE; 107 108 r = cubeb_get_min_latency(ctx, &output_params, &latency_frames); 109 if (r != CUBEB_OK) { 110 // not fatal 111 latency_frames = 1024; 112 } 113 114 r = cubeb_stream_init(ctx, &stream, "Cubeb logging", NULL, NULL, NULL, 115 &output_params, latency_frames, data_cb_load, state_cb, 116 NULL); 117 ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream"; 118 119 std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)> 120 cleanup_stream_at_exit(stream, cubeb_stream_destroy); 121 122 ASSERT_NE(log_statements_received.load(std::memory_order_acquire), 0u); 123 124 cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr); 125 log_statements_received.store(0, std::memory_order_release); 126 127 // This is synchronous and we'll receive log messages on all backends that we 128 // test 129 cubeb_stream_start(stream); 130 131 ASSERT_EQ(log_statements_received.load(std::memory_order_acquire), 0u); 132 133 cubeb_set_log_callback(CUBEB_LOG_VERBOSE, test_logging_callback); 134 135 wait_for_audio_callback(); 136 137 ASSERT_NE(log_statements_received.load(std::memory_order_acquire), 0u); 138 139 bool log_callback_set = true; 140 uint32_t iterations = 100; 141 while (iterations--) { 142 wait_for_audio_callback(); 143 144 if (!log_callback_set) { 145 ASSERT_EQ(log_statements_received.load(std::memory_order_acquire), 0u); 146 // Set a logging callback, start logging 147 cubeb_set_log_callback(CUBEB_LOG_VERBOSE, test_logging_callback); 148 log_callback_set = true; 149 } else { 150 // Disable the logging callback, stop logging. 151 ASSERT_NE(log_statements_received.load(std::memory_order_acquire), 0u); 152 cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr); 153 log_statements_received.store(0, std::memory_order_release); 154 // Disabling logging should flush any log message -- wait a bit and check 155 // that this is true. 156 ASSERT_EQ(log_statements_received.load(std::memory_order_acquire), 0u); 157 log_callback_set = false; 158 } 159 } 160 161 cubeb_stream_stop(stream); 162 } 163 164 TEST(cubeb, logging_stress) 165 { 166 cubeb_set_log_callback(CUBEB_LOG_NORMAL, test_logging_callback); 167 168 std::atomic<bool> thread_done = {false}; 169 170 auto t = std::thread([&thread_done]() { 171 uint32_t count = 0; 172 do { 173 while (rand() % 10) { 174 ALOG("Log message #%d!", count++); 175 } 176 } while (count < 1e4); 177 thread_done.store(true); 178 }); 179 180 bool enabled = true; 181 while (!thread_done.load()) { 182 if (enabled) { 183 cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr); 184 enabled = false; 185 } else { 186 cubeb_set_log_callback(CUBEB_LOG_NORMAL, test_logging_callback); 187 enabled = true; 188 } 189 } 190 191 cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr); 192 193 t.join(); 194 195 ASSERT_TRUE(true); 196 }