test_callback_ret.cpp (8196B)
1 /* 2 * Copyright � 2017 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 /* libcubeb api/function test. Test that different return values from user 9 specified callbacks are handled correctly. */ 10 #include "gtest/gtest.h" 11 #if !defined(_XOPEN_SOURCE) 12 #define _XOPEN_SOURCE 600 13 #endif 14 #include "cubeb/cubeb.h" 15 #include <atomic> 16 #include <memory> 17 #include <string> 18 19 // #define ENABLE_NORMAL_LOG 20 // #define ENABLE_VERBOSE_LOG 21 #include "common.h" 22 23 const uint32_t SAMPLE_FREQUENCY = 48000; 24 const cubeb_sample_format SAMPLE_FORMAT = CUBEB_SAMPLE_S16NE; 25 26 enum test_direction { INPUT_ONLY, OUTPUT_ONLY, DUPLEX }; 27 28 // Structure which is used by data callbacks to track the total callbacks 29 // executed vs the number of callbacks expected. 30 struct user_state_callback_ret { 31 std::atomic<int> cb_count{0}; 32 std::atomic<int> expected_cb_count{0}; 33 std::atomic<int> error_state{0}; 34 }; 35 36 // Data callback that always returns 0 37 long 38 data_cb_ret_zero(cubeb_stream * stream, void * user, const void * inputbuffer, 39 void * outputbuffer, long nframes) 40 { 41 user_state_callback_ret * u = (user_state_callback_ret *)user; 42 // If this is the first time the callback has been called set our expected 43 // callback count 44 if (u->cb_count == 0) { 45 u->expected_cb_count = 1; 46 } 47 u->cb_count++; 48 if (nframes < 1) { 49 // This shouldn't happen 50 EXPECT_TRUE(false) << "nframes should not be 0 in data callback!"; 51 } 52 return 0; 53 } 54 55 // Data callback that always returns nframes - 1 56 long 57 data_cb_ret_nframes_minus_one(cubeb_stream * stream, void * user, 58 const void * inputbuffer, void * outputbuffer, 59 long nframes) 60 { 61 user_state_callback_ret * u = (user_state_callback_ret *)user; 62 // If this is the first time the callback has been called set our expected 63 // callback count 64 if (u->cb_count == 0) { 65 u->expected_cb_count = 1; 66 } 67 u->cb_count++; 68 if (nframes < 1) { 69 // This shouldn't happen 70 EXPECT_TRUE(false) << "nframes should not be 0 in data callback!"; 71 } 72 if (outputbuffer != NULL) { 73 // If we have an output buffer insert silence 74 short * ob = (short *)outputbuffer; 75 for (long i = 0; i < nframes - 1; i++) { 76 ob[i] = 0; 77 } 78 } 79 return nframes - 1; 80 } 81 82 // Data callback that always returns nframes 83 long 84 data_cb_ret_nframes(cubeb_stream * stream, void * user, 85 const void * inputbuffer, void * outputbuffer, long nframes) 86 { 87 user_state_callback_ret * u = (user_state_callback_ret *)user; 88 u->cb_count++; 89 // Every callback returns nframes, so every callback is expected 90 u->expected_cb_count++; 91 if (nframes < 1) { 92 // This shouldn't happen 93 EXPECT_TRUE(false) << "nframes should not be 0 in data callback!"; 94 } 95 if (outputbuffer != NULL) { 96 // If we have an output buffer insert silence 97 short * ob = (short *)outputbuffer; 98 for (long i = 0; i < nframes; i++) { 99 ob[i] = 0; 100 } 101 } 102 return nframes; 103 } 104 105 // Data callback that always returns CUBEB_ERROR 106 long 107 data_cb_ret_error(cubeb_stream * stream, void * user, const void * inputbuffer, 108 void * outputbuffer, long nframes) 109 { 110 user_state_callback_ret * u = (user_state_callback_ret *)user; 111 // If this is the first time the callback has been called set our expected 112 // callback count 113 if (u->cb_count == 0) { 114 u->expected_cb_count = 1; 115 } 116 u->cb_count++; 117 if (nframes < 1) { 118 // This shouldn't happen 119 EXPECT_TRUE(false) << "nframes should not be 0 in data callback!"; 120 } 121 return CUBEB_ERROR; 122 } 123 124 void 125 state_cb_ret(cubeb_stream * stream, void * user, cubeb_state state) 126 { 127 if (stream == NULL) 128 return; 129 user_state_callback_ret * u = (user_state_callback_ret *)user; 130 131 switch (state) { 132 case CUBEB_STATE_STARTED: 133 fprintf(stderr, "stream started\n"); 134 break; 135 case CUBEB_STATE_STOPPED: 136 fprintf(stderr, "stream stopped\n"); 137 break; 138 case CUBEB_STATE_DRAINED: 139 fprintf(stderr, "stream drained\n"); 140 break; 141 case CUBEB_STATE_ERROR: 142 fprintf(stderr, "stream error\n"); 143 u->error_state.fetch_add(1); 144 break; 145 default: 146 fprintf(stderr, "unknown stream state %d\n", state); 147 } 148 } 149 150 void 151 run_test_callback(test_direction direction, cubeb_data_callback data_cb, 152 const std::string & test_desc) 153 { 154 cubeb * ctx; 155 cubeb_stream * stream; 156 cubeb_stream_params input_params; 157 cubeb_stream_params output_params; 158 int r; 159 user_state_callback_ret user_state; 160 uint32_t latency_frames = 0; 161 162 r = common_init(&ctx, "Cubeb callback return value example"); 163 ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library"; 164 165 std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit( 166 ctx, cubeb_destroy); 167 168 if ((direction == INPUT_ONLY || direction == DUPLEX) && 169 !can_run_audio_input_test(ctx)) { 170 /* This test needs an available input device, skip it if this host does not 171 * have one or if the backend doesn't implement input. */ 172 return; 173 } 174 175 // Setup all params, but only pass them later as required by direction 176 input_params.format = SAMPLE_FORMAT; 177 input_params.rate = SAMPLE_FREQUENCY; 178 input_params.channels = 1; 179 input_params.layout = CUBEB_LAYOUT_MONO; 180 input_params.prefs = CUBEB_STREAM_PREF_NONE; 181 output_params = input_params; 182 183 r = cubeb_get_min_latency(ctx, &input_params, &latency_frames); 184 if (r != CUBEB_OK) { 185 // not fatal 186 latency_frames = 1024; 187 } 188 189 switch (direction) { 190 case INPUT_ONLY: 191 r = cubeb_stream_init(ctx, &stream, "Cubeb callback ret input", NULL, 192 &input_params, NULL, NULL, latency_frames, data_cb, 193 state_cb_ret, &user_state); 194 break; 195 case OUTPUT_ONLY: 196 r = cubeb_stream_init(ctx, &stream, "Cubeb callback ret output", NULL, NULL, 197 NULL, &output_params, latency_frames, data_cb, 198 state_cb_ret, &user_state); 199 break; 200 case DUPLEX: 201 r = cubeb_stream_init(ctx, &stream, "Cubeb callback ret duplex", NULL, 202 &input_params, NULL, &output_params, latency_frames, 203 data_cb, state_cb_ret, &user_state); 204 break; 205 default: 206 ASSERT_TRUE(false) << "Unrecognized test direction!"; 207 } 208 EXPECT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream"; 209 210 std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)> 211 cleanup_stream_at_exit(stream, cubeb_stream_destroy); 212 213 cubeb_stream_start(stream); 214 delay(100); 215 cubeb_stream_stop(stream); 216 217 ASSERT_EQ(user_state.expected_cb_count, user_state.cb_count) 218 << "Callback called unexpected number of times for " << test_desc << "!"; 219 // TODO: On some test configurations, the data_callback is never called. 220 if (data_cb == data_cb_ret_error && user_state.cb_count != 0) { 221 ASSERT_EQ(user_state.error_state, 1) << "Callback expected error state"; 222 } 223 } 224 225 TEST(cubeb, test_input_callback) 226 { 227 run_test_callback(INPUT_ONLY, data_cb_ret_zero, "input only, return 0"); 228 run_test_callback(INPUT_ONLY, data_cb_ret_nframes_minus_one, 229 "input only, return nframes - 1"); 230 run_test_callback(INPUT_ONLY, data_cb_ret_nframes, 231 "input only, return nframes"); 232 run_test_callback(INPUT_ONLY, data_cb_ret_error, 233 "input only, return CUBEB_ERROR"); 234 } 235 236 TEST(cubeb, test_output_callback) 237 { 238 run_test_callback(OUTPUT_ONLY, data_cb_ret_zero, "output only, return 0"); 239 run_test_callback(OUTPUT_ONLY, data_cb_ret_nframes_minus_one, 240 "output only, return nframes - 1"); 241 run_test_callback(OUTPUT_ONLY, data_cb_ret_nframes, 242 "output only, return nframes"); 243 run_test_callback(OUTPUT_ONLY, data_cb_ret_error, 244 "output only, return CUBEB_ERROR"); 245 } 246 247 TEST(cubeb, test_duplex_callback) 248 { 249 run_test_callback(DUPLEX, data_cb_ret_zero, "duplex, return 0"); 250 run_test_callback(DUPLEX, data_cb_ret_nframes_minus_one, 251 "duplex, return nframes - 1"); 252 run_test_callback(DUPLEX, data_cb_ret_nframes, "duplex, return nframes"); 253 run_test_callback(DUPLEX, data_cb_ret_error, "duplex, return CUBEB_ERROR"); 254 }