1 // Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <gtest/gtest.h>
6 #include <stdio.h>
7
8 extern "C" {
9 #include "cras_messages.h"
10
11 // Include C file to test static functions.
12 #include "cras_client.c"
13 }
14
15 static const cras_stream_id_t FIRST_STREAM_ID = 1;
16
17 static int pthread_create_called;
18 static int pthread_join_called;
19 static int pthread_cond_timedwait_called;
20 static int pthread_cond_timedwait_retval;
21 static int close_called;
22 static int sendmsg_called;
23 static void* mmap_return_value;
24 static int samples_ready_called;
25 static int samples_ready_frames_value;
26 static uint8_t* samples_ready_samples_value;
27
28 static int pthread_create_returned_value;
29
30 namespace {
31
InitStaticVariables()32 void InitStaticVariables() {
33 pthread_create_called = 0;
34 pthread_join_called = 0;
35 pthread_cond_timedwait_called = 0;
36 pthread_cond_timedwait_retval = 0;
37 close_called = 0;
38 sendmsg_called = 0;
39 pthread_create_returned_value = 0;
40 mmap_return_value = NULL;
41 samples_ready_called = 0;
42 samples_ready_frames_value = 0;
43 }
44
45 class CrasClientTestSuite : public testing::Test {
46 protected:
InitShm()47 struct cras_audio_shm* InitShm() {
48 struct cras_audio_shm* shm =
49 static_cast<struct cras_audio_shm*>(calloc(1, sizeof(*shm)));
50 shm->header =
51 static_cast<cras_audio_shm_header*>(calloc(1, sizeof(*shm->header)));
52 cras_shm_set_frame_bytes(shm, 4);
53 uint32_t used_size = shm_writable_frames_ * 4;
54 cras_shm_set_used_size(shm, used_size);
55 shm->samples_info.length = used_size * 2;
56 memcpy(&shm->header->config, &shm->config, sizeof(shm->config));
57 return shm;
58 }
59
SetUp()60 virtual void SetUp() {
61 shm_writable_frames_ = 100;
62 InitStaticVariables();
63
64 memset(&client_, 0, sizeof(client_));
65 client_.server_fd_state = CRAS_SOCKET_STATE_CONNECTED;
66 memset(&stream_, 0, sizeof(stream_));
67 stream_.id = FIRST_STREAM_ID;
68
69 struct cras_stream_params* config =
70 static_cast<cras_stream_params*>(calloc(1, sizeof(*config)));
71 config->buffer_frames = 1024;
72 config->cb_threshold = 512;
73 stream_.config = config;
74 }
75
TearDown()76 virtual void TearDown() {
77 if (stream_.config) {
78 free(stream_.config);
79 stream_.config = NULL;
80 }
81
82 if (stream_.shm) {
83 free(stream_.shm->header);
84 }
85 free(stream_.shm);
86 stream_.shm = NULL;
87 }
88
89 void StreamConnected(CRAS_STREAM_DIRECTION direction);
90
91 void StreamConnectedFail(CRAS_STREAM_DIRECTION direction);
92
93 struct client_stream stream_;
94 struct cras_client client_;
95 int shm_writable_frames_;
96 };
97
set_audio_format(struct cras_audio_format * format,snd_pcm_format_t pcm_format,size_t frame_rate,size_t num_channels)98 void set_audio_format(struct cras_audio_format* format,
99 snd_pcm_format_t pcm_format,
100 size_t frame_rate,
101 size_t num_channels) {
102 format->format = pcm_format;
103 format->frame_rate = frame_rate;
104 format->num_channels = num_channels;
105 for (size_t i = 0; i < CRAS_CH_MAX; ++i)
106 format->channel_layout[i] = i < num_channels ? i : -1;
107 }
108
capture_samples_ready(cras_client * client,cras_stream_id_t stream_id,uint8_t * samples,size_t frames,const timespec * sample_ts,void * arg)109 int capture_samples_ready(cras_client* client,
110 cras_stream_id_t stream_id,
111 uint8_t* samples,
112 size_t frames,
113 const timespec* sample_ts,
114 void* arg) {
115 samples_ready_called++;
116 samples_ready_samples_value = samples;
117 samples_ready_frames_value = frames;
118 return frames;
119 }
120
TEST_F(CrasClientTestSuite,HandleCaptureDataReady)121 TEST_F(CrasClientTestSuite, HandleCaptureDataReady) {
122 struct cras_audio_shm* shm;
123
124 stream_.direction = CRAS_STREAM_INPUT;
125
126 shm_writable_frames_ = 480;
127 shm = InitShm();
128 stream_.shm = shm;
129 stream_.config->buffer_frames = 480;
130 stream_.config->cb_threshold = 480;
131 stream_.config->aud_cb = capture_samples_ready;
132 stream_.config->unified_cb = 0;
133
134 shm->header->write_buf_idx = 0;
135 shm->header->read_buf_idx = 0;
136 shm->header->write_offset[0] = 480 * 4;
137 shm->header->read_offset[0] = 0;
138
139 /* Normal scenario: read buffer has full of data written,
140 * handle_capture_data_ready() should consume all 480 frames and move
141 * read_buf_idx to the next buffer. */
142 handle_capture_data_ready(&stream_, 480);
143 EXPECT_EQ(1, samples_ready_called);
144 EXPECT_EQ(480, samples_ready_frames_value);
145 EXPECT_EQ(cras_shm_buff_for_idx(shm, 0), samples_ready_samples_value);
146 EXPECT_EQ(1, shm->header->read_buf_idx);
147 EXPECT_EQ(0, shm->header->write_offset[0]);
148 EXPECT_EQ(0, shm->header->read_offset[0]);
149
150 /* At the beginning of overrun: handle_capture_data_ready() should not
151 * proceed to call audio_cb because there's no data captured. */
152 shm->header->read_buf_idx = 0;
153 shm->header->write_offset[0] = 0;
154 shm->header->read_offset[0] = 0;
155 handle_capture_data_ready(&stream_, 480);
156 EXPECT_EQ(1, samples_ready_called);
157 EXPECT_EQ(0, shm->header->read_buf_idx);
158
159 /* In the middle of overrun: partially written buffer should trigger
160 * audio_cb, feed the full-sized read buffer to client. */
161 shm->header->read_buf_idx = 0;
162 shm->header->write_offset[0] = 123;
163 shm->header->read_offset[0] = 0;
164 handle_capture_data_ready(&stream_, 480);
165 EXPECT_EQ(1, samples_ready_called);
166 EXPECT_EQ(0, shm->header->read_buf_idx);
167 }
168
StreamConnected(CRAS_STREAM_DIRECTION direction)169 void CrasClientTestSuite::StreamConnected(CRAS_STREAM_DIRECTION direction) {
170 struct cras_client_stream_connected msg;
171 int shm_fds[2] = {0, 1};
172 int shm_max_size = 600;
173 size_t format_bytes;
174 size_t effects = 123;
175 struct cras_audio_shm_header* header;
176
177 stream_.direction = direction;
178 set_audio_format(&stream_.config->format, SND_PCM_FORMAT_S16_LE, 48000, 4);
179
180 struct cras_audio_format server_format;
181 set_audio_format(&server_format, SND_PCM_FORMAT_S16_LE, 44100, 2);
182
183 // Initialize shm area
184 format_bytes = cras_get_format_bytes(&server_format);
185 header = (struct cras_audio_shm_header*)calloc(1, sizeof(*header));
186 header->config.frame_bytes = format_bytes;
187 header->config.used_size = shm_writable_frames_ * format_bytes;
188
189 mmap_return_value = header;
190
191 cras_fill_client_stream_connected(&msg, 0, stream_.id, &server_format,
192 shm_max_size, effects);
193
194 stream_connected(&stream_, &msg, shm_fds, 2);
195
196 EXPECT_EQ(CRAS_THREAD_RUNNING, stream_.thread.state);
197 EXPECT_EQ(header, stream_.shm->header);
198 }
199
TEST_F(CrasClientTestSuite,InputStreamConnected)200 TEST_F(CrasClientTestSuite, InputStreamConnected) {
201 StreamConnected(CRAS_STREAM_INPUT);
202 }
203
TEST_F(CrasClientTestSuite,OutputStreamConnected)204 TEST_F(CrasClientTestSuite, OutputStreamConnected) {
205 StreamConnected(CRAS_STREAM_OUTPUT);
206 }
207
StreamConnectedFail(CRAS_STREAM_DIRECTION direction)208 void CrasClientTestSuite::StreamConnectedFail(CRAS_STREAM_DIRECTION direction) {
209 struct cras_client_stream_connected msg;
210 int shm_fds[2] = {0, 1};
211 int shm_max_size = 600;
212 size_t format_bytes;
213 size_t effects = 123;
214 struct cras_audio_shm_header header;
215 int rc;
216
217 stream_.direction = direction;
218 set_audio_format(&stream_.config->format, SND_PCM_FORMAT_S16_LE, 48000, 4);
219
220 struct cras_audio_format server_format;
221 set_audio_format(&server_format, SND_PCM_FORMAT_S16_LE, 44100, 2);
222
223 // Thread setup
224 rc = pipe(stream_.wake_fds);
225 ASSERT_EQ(0, rc);
226 stream_.thread.state = CRAS_THREAD_WARMUP;
227
228 // Initialize shm area
229 format_bytes = cras_get_format_bytes(&server_format);
230 memset(&header, 0, sizeof(header));
231 header.config.frame_bytes = format_bytes;
232 header.config.used_size = shm_writable_frames_ * format_bytes;
233
234 mmap_return_value = &header;
235
236 // Put an error in the message.
237 cras_fill_client_stream_connected(&msg, 1, stream_.id, &server_format,
238 shm_max_size, effects);
239
240 stream_connected(&stream_, &msg, shm_fds, 2);
241
242 EXPECT_EQ(CRAS_THREAD_STOP, stream_.thread.state);
243 EXPECT_EQ(4, close_called); // close the pipefds and shm_fds
244 }
245
TEST_F(CrasClientTestSuite,InputStreamConnectedFail)246 TEST_F(CrasClientTestSuite, InputStreamConnectedFail) {
247 StreamConnectedFail(CRAS_STREAM_INPUT);
248 }
249
TEST_F(CrasClientTestSuite,OutputStreamConnectedFail)250 TEST_F(CrasClientTestSuite, OutputStreamConnectedFail) {
251 StreamConnectedFail(CRAS_STREAM_OUTPUT);
252 }
253
TEST_F(CrasClientTestSuite,AddAndRemoveStream)254 TEST_F(CrasClientTestSuite, AddAndRemoveStream) {
255 cras_stream_id_t stream_id;
256 struct cras_disconnect_stream_message msg;
257 int serv_fds[2];
258 int rc;
259
260 // Dynamically allocate the stream so that it can be freed later.
261 struct client_stream* stream_ptr =
262 (struct client_stream*)malloc(sizeof(*stream_ptr));
263 memcpy(stream_ptr, &stream_, sizeof(client_stream));
264
265 stream_ptr->config =
266 (struct cras_stream_params*)malloc(sizeof(*(stream_ptr->config)));
267 memcpy(stream_ptr->config, stream_.config, sizeof(*(stream_.config)));
268
269 stream_ptr->wake_fds[0] = -1;
270 stream_ptr->wake_fds[1] = -1;
271
272 pthread_cond_timedwait_retval = ETIMEDOUT;
273 EXPECT_EQ(-ETIMEDOUT, client_thread_add_stream(&client_, stream_ptr,
274 &stream_id, NO_DEVICE));
275 EXPECT_EQ(pthread_cond_timedwait_called, 1);
276 EXPECT_EQ(pthread_join_called, 0);
277
278 InitStaticVariables();
279 EXPECT_EQ(
280 0, client_thread_add_stream(&client_, stream_ptr, &stream_id, NO_DEVICE));
281 EXPECT_EQ(&client_, stream_ptr->client);
282 EXPECT_EQ(stream_id, stream_ptr->id);
283 EXPECT_EQ(pthread_create_called, 1);
284 EXPECT_NE(-1, stream_ptr->wake_fds[0]);
285 EXPECT_NE(-1, stream_ptr->wake_fds[1]);
286 EXPECT_EQ(1, sendmsg_called); // send connect message to server
287 EXPECT_EQ(stream_ptr, stream_from_id(&client_, stream_id));
288
289 stream_ptr->thread.state = CRAS_THREAD_RUNNING;
290
291 rc = pipe(serv_fds);
292 EXPECT_EQ(0, rc);
293 client_.server_fd = serv_fds[1];
294 client_.server_fd_state = CRAS_SOCKET_STATE_CONNECTED;
295 EXPECT_EQ(0, client_thread_rm_stream(&client_, stream_id));
296
297 rc = read(serv_fds[0], &msg, sizeof(msg));
298 EXPECT_EQ(sizeof(msg), rc);
299 EXPECT_EQ(stream_id, msg.stream_id);
300 EXPECT_EQ(1, pthread_join_called);
301
302 EXPECT_EQ(NULL, stream_from_id(&client_, stream_id));
303 }
304
TEST_F(CrasClientTestSuite,SetOutputStreamVolume)305 TEST_F(CrasClientTestSuite, SetOutputStreamVolume) {
306 cras_stream_id_t stream_id;
307
308 client_thread_add_stream(&client_, &stream_, &stream_id, NO_DEVICE);
309 EXPECT_EQ(&stream_, stream_from_id(&client_, stream_id));
310
311 /* Set volume before stream connected. */
312 client_thread_set_stream_volume(&client_, stream_id, 0.3f);
313 StreamConnected(CRAS_STREAM_OUTPUT);
314 EXPECT_EQ(0.3f, cras_shm_get_volume_scaler(stream_.shm));
315
316 /* Set volume after stream connected. */
317 client_thread_set_stream_volume(&client_, stream_id, 0.6f);
318 EXPECT_EQ(0.6f, cras_shm_get_volume_scaler(stream_.shm));
319 }
320
TEST_F(CrasClientTestSuite,SetInputStreamVolume)321 TEST_F(CrasClientTestSuite, SetInputStreamVolume) {
322 cras_stream_id_t stream_id;
323
324 client_thread_add_stream(&client_, &stream_, &stream_id, NO_DEVICE);
325 EXPECT_EQ(&stream_, stream_from_id(&client_, stream_id));
326
327 /* Set volume before stream connected. */
328 client_thread_set_stream_volume(&client_, stream_id, 0.3f);
329 StreamConnected(CRAS_STREAM_INPUT);
330 EXPECT_EQ(0.3f, cras_shm_get_volume_scaler(stream_.shm));
331
332 /* Set volume after stream connected. */
333 client_thread_set_stream_volume(&client_, stream_id, 0.6f);
334 EXPECT_EQ(0.6f, cras_shm_get_volume_scaler(stream_.shm));
335 }
336
TEST(CrasClientTest,InitStreamVolume)337 TEST(CrasClientTest, InitStreamVolume) {
338 cras_stream_id_t stream_id;
339 struct cras_stream_params config;
340 struct add_stream_command_message cmd_msg;
341 int rc;
342 struct cras_client client;
343
344 memset(&client, 0, sizeof(client));
345 memset(&config, 0, sizeof(config));
346 client.server_fd_state = CRAS_SOCKET_STATE_CONNECTED;
347
348 config.aud_cb = reinterpret_cast<cras_playback_cb_t>(0x123);
349 config.err_cb = reinterpret_cast<cras_error_cb_t>(0x456);
350 client.thread.state = CRAS_THREAD_RUNNING;
351 rc = pipe(client.command_reply_fds);
352 EXPECT_EQ(0, rc);
353 rc = pipe(client.command_fds);
354 EXPECT_EQ(0, rc);
355
356 rc = write(client.command_reply_fds[1], &rc, sizeof(rc));
357 cras_client_add_stream(&client, &stream_id, &config);
358
359 rc = read(client.command_fds[0], &cmd_msg, sizeof(cmd_msg));
360 EXPECT_EQ(sizeof(cmd_msg), rc);
361 EXPECT_NE((void*)NULL, cmd_msg.stream);
362
363 EXPECT_EQ(1.0f, cmd_msg.stream->volume_scaler);
364
365 if (cmd_msg.stream->config)
366 free(cmd_msg.stream->config);
367 free(cmd_msg.stream);
368 }
369
370 } // namespace
371
main(int argc,char ** argv)372 int main(int argc, char** argv) {
373 ::testing::InitGoogleTest(&argc, argv);
374 return RUN_ALL_TESTS();
375 }
376
377 /* stubs */
378 extern "C" {
379
sendmsg(int sockfd,const struct msghdr * msg,int flags)380 ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags) {
381 ++sendmsg_called;
382 return msg->msg_iov->iov_len;
383 }
384
close(int fd)385 int close(int fd) {
386 ++close_called;
387 return 0;
388 }
389
pthread_create(pthread_t * thread,const pthread_attr_t * attr,void * (* start_routine)(void *),void * arg)390 int pthread_create(pthread_t* thread,
391 const pthread_attr_t* attr,
392 void* (*start_routine)(void*),
393 void* arg) {
394 ++pthread_create_called;
395 return pthread_create_returned_value;
396 }
397
pthread_join(pthread_t thread,void ** retval)398 int pthread_join(pthread_t thread, void** retval) {
399 ++pthread_join_called;
400 return 0;
401 }
402
pthread_cond_timedwait(pthread_cond_t * __restrict cond,pthread_mutex_t * __restrict mutex,const struct timespec * __restrict timeout)403 int pthread_cond_timedwait(pthread_cond_t* __restrict cond,
404 pthread_mutex_t* __restrict mutex,
405 const struct timespec* __restrict timeout) {
406 ++pthread_cond_timedwait_called;
407 return pthread_cond_timedwait_retval;
408 }
409
clock_gettime(clockid_t clk_id,struct timespec * tp)410 int clock_gettime(clockid_t clk_id, struct timespec* tp) {
411 tp->tv_sec = 0;
412 tp->tv_nsec = 0;
413 return 0;
414 }
415
mmap(void * addr,size_t length,int prot,int flags,int fd,off_t offset)416 void* mmap(void* addr,
417 size_t length,
418 int prot,
419 int flags,
420 int fd,
421 off_t offset) {
422 return mmap_return_value;
423 }
424
cras_audio_format_create(snd_pcm_format_t format,size_t frame_rate,size_t num_channels)425 struct cras_audio_format* cras_audio_format_create(snd_pcm_format_t format,
426 size_t frame_rate,
427 size_t num_channels) {
428 return reinterpret_cast<struct cras_audio_format*>(0x123);
429 }
430
cras_audio_format_destroy(struct cras_audio_format * fmt)431 void cras_audio_format_destroy(struct cras_audio_format* fmt) {}
432 }
433