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 <stdio.h>
6 #include <gtest/gtest.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 pipe_called;
23 static int sendmsg_called;
24 static int write_called;
25 static void *mmap_return_value;
26 static int samples_ready_called;
27 static int samples_ready_frames_value;
28 static uint8_t *samples_ready_samples_value;
29
30 static int pthread_create_returned_value;
31
32 namespace {
33
InitStaticVariables()34 void InitStaticVariables() {
35 pthread_create_called = 0;
36 pthread_join_called = 0;
37 pthread_cond_timedwait_called = 0;
38 pthread_cond_timedwait_retval = 0;
39 close_called = 0;
40 pipe_called = 0;
41 sendmsg_called = 0;
42 write_called = 0;
43 pthread_create_returned_value = 0;
44 mmap_return_value = NULL;
45 samples_ready_called = 0;
46 samples_ready_frames_value = 0;
47 }
48
49 class CrasClientTestSuite : public testing::Test {
50 protected:
51
InitShm(struct cras_audio_shm * shm)52 void InitShm(struct cras_audio_shm* shm) {
53 shm->area = static_cast<cras_audio_shm_area*>(
54 calloc(1, sizeof(*shm->area)));
55 cras_shm_set_frame_bytes(shm, 4);
56 cras_shm_set_used_size(shm, shm_writable_frames_ * 4);
57 memcpy(&shm->area->config, &shm->config, sizeof(shm->config));
58 }
59
FreeShm(struct cras_audio_shm * shm)60 void FreeShm(struct cras_audio_shm* shm) {
61 if (shm->area) {
62 free(shm->area);
63 shm->area = NULL;
64 }
65 }
66
SetUp()67 virtual void SetUp() {
68 shm_writable_frames_ = 100;
69 InitStaticVariables();
70
71 memset(&client_, 0, sizeof(client_));
72 client_.server_fd_state = CRAS_SOCKET_STATE_CONNECTED;
73 memset(&stream_, 0, sizeof(stream_));
74 stream_.id = FIRST_STREAM_ID;
75
76 struct cras_stream_params* config =
77 static_cast<cras_stream_params*>(calloc(1, sizeof(*config)));
78 config->buffer_frames = 1024;
79 config->cb_threshold = 512;
80 stream_.config = config;
81 }
82
TearDown()83 virtual void TearDown() {
84 if (stream_.config) {
85 free(stream_.config);
86 stream_.config = NULL;
87 }
88 }
89
90 void StreamConnected(CRAS_STREAM_DIRECTION direction);
91
92 void StreamConnectedFail(CRAS_STREAM_DIRECTION direction);
93
94 struct client_stream stream_;
95 struct cras_client client_;
96 int shm_writable_frames_;
97 };
98
set_audio_format(struct cras_audio_format * format,snd_pcm_format_t pcm_format,size_t frame_rate,size_t num_channels)99 void set_audio_format(struct cras_audio_format* format,
100 snd_pcm_format_t pcm_format,
101 size_t frame_rate,
102 size_t num_channels) {
103 format->format = pcm_format;
104 format->frame_rate = frame_rate;
105 format->num_channels = num_channels;
106 for (size_t i = 0; i < CRAS_CH_MAX; ++i)
107 format->channel_layout[i] = i < num_channels ? i : -1;
108 }
109
capture_samples_ready(cras_client * client,cras_stream_id_t stream_id,uint8_t * samples,size_t frames,const timespec * sample_ts,void * arg)110 int capture_samples_ready(cras_client* client,
111 cras_stream_id_t stream_id,
112 uint8_t* samples,
113 size_t frames,
114 const timespec* sample_ts,
115 void* arg) {
116 samples_ready_called++;
117 samples_ready_samples_value = samples;
118 samples_ready_frames_value = frames;
119 return frames;
120 }
121
TEST_F(CrasClientTestSuite,HandleCaptureDataReady)122 TEST_F(CrasClientTestSuite, HandleCaptureDataReady) {
123 struct cras_audio_shm *shm = &stream_.capture_shm;
124
125 stream_.direction = CRAS_STREAM_INPUT;
126
127 shm_writable_frames_ = 480;
128 InitShm(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->area->write_buf_idx = 0;
135 shm->area->read_buf_idx = 0;
136 shm->area->write_offset[0] = 480 * 4;
137 shm->area->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->area->read_buf_idx);
147 EXPECT_EQ(0, shm->area->write_offset[0]);
148 EXPECT_EQ(0, shm->area->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->area->read_buf_idx = 0;
153 shm->area->write_offset[0] = 0;
154 shm->area->read_offset[0] = 0;
155 handle_capture_data_ready(&stream_, 480);
156 EXPECT_EQ(1, samples_ready_called);
157 EXPECT_EQ(0, shm->area->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->area->read_buf_idx = 0;
162 shm->area->write_offset[0] = 123;
163 shm->area->read_offset[0] = 0;
164 handle_capture_data_ready(&stream_, 480);
165 EXPECT_EQ(1, samples_ready_called);
166 EXPECT_EQ(0, shm->area->read_buf_idx);
167 FreeShm(shm);
168 }
169
StreamConnected(CRAS_STREAM_DIRECTION direction)170 void CrasClientTestSuite::StreamConnected(CRAS_STREAM_DIRECTION direction) {
171 struct cras_client_stream_connected msg;
172 int shm_fds[2] = {0, 1};
173 int shm_max_size = 600;
174 size_t format_bytes;
175 size_t effects = 123;
176 struct cras_audio_shm_area area;
177
178 stream_.direction = direction;
179 set_audio_format(&stream_.config->format, SND_PCM_FORMAT_S16_LE, 48000, 4);
180
181 struct cras_audio_format server_format;
182 set_audio_format(&server_format, SND_PCM_FORMAT_S16_LE, 44100, 2);
183
184 // Initialize shm area
185 format_bytes = cras_get_format_bytes(&server_format);
186 memset(&area, 0, sizeof(area));
187 area.config.frame_bytes = format_bytes;
188 area.config.used_size = shm_writable_frames_ * format_bytes;
189
190 mmap_return_value = &area;
191
192 cras_fill_client_stream_connected(
193 &msg,
194 0,
195 stream_.id,
196 &server_format,
197 shm_max_size,
198 effects);
199
200 stream_connected(&stream_, &msg, shm_fds, 2);
201
202 EXPECT_EQ(CRAS_THREAD_RUNNING, stream_.thread.state);
203
204 if (direction == CRAS_STREAM_OUTPUT) {
205 EXPECT_EQ(NULL, stream_.capture_shm.area);
206 EXPECT_EQ(&area, stream_.play_shm.area);
207 } else {
208 EXPECT_EQ(NULL, stream_.play_shm.area);
209 EXPECT_EQ(&area, stream_.capture_shm.area);
210 }
211 }
212
TEST_F(CrasClientTestSuite,InputStreamConnected)213 TEST_F(CrasClientTestSuite, InputStreamConnected) {
214 StreamConnected(CRAS_STREAM_INPUT);
215 }
216
TEST_F(CrasClientTestSuite,OutputStreamConnected)217 TEST_F(CrasClientTestSuite, OutputStreamConnected) {
218 StreamConnected(CRAS_STREAM_OUTPUT);
219 }
220
StreamConnectedFail(CRAS_STREAM_DIRECTION direction)221 void CrasClientTestSuite::StreamConnectedFail(
222 CRAS_STREAM_DIRECTION direction) {
223
224 struct cras_client_stream_connected msg;
225 int shm_fds[2] = {0, 1};
226 int shm_max_size = 600;
227 size_t format_bytes;
228 size_t effects = 123;
229 struct cras_audio_shm_area area;
230 int rc;
231
232 stream_.direction = direction;
233 set_audio_format(&stream_.config->format, SND_PCM_FORMAT_S16_LE, 48000, 4);
234
235 struct cras_audio_format server_format;
236 set_audio_format(&server_format, SND_PCM_FORMAT_S16_LE, 44100, 2);
237
238 // Thread setup
239 rc = pipe(stream_.wake_fds);
240 ASSERT_EQ(0, rc);
241 stream_.thread.state = CRAS_THREAD_WARMUP;
242
243 // Initialize shm area
244 format_bytes = cras_get_format_bytes(&server_format);
245 memset(&area, 0, sizeof(area));
246 area.config.frame_bytes = format_bytes;
247 area.config.used_size = shm_writable_frames_ * format_bytes;
248
249 mmap_return_value = &area;
250
251 // Put an error in the message.
252 cras_fill_client_stream_connected(
253 &msg,
254 1,
255 stream_.id,
256 &server_format,
257 shm_max_size,
258 effects);
259
260 stream_connected(&stream_, &msg, shm_fds, 2);
261
262 EXPECT_EQ(CRAS_THREAD_STOP, stream_.thread.state);
263 EXPECT_EQ(4, close_called); // close the pipefds and shm_fds
264 }
265
TEST_F(CrasClientTestSuite,InputStreamConnectedFail)266 TEST_F(CrasClientTestSuite, InputStreamConnectedFail) {
267 StreamConnectedFail(CRAS_STREAM_INPUT);
268 }
269
TEST_F(CrasClientTestSuite,OutputStreamConnectedFail)270 TEST_F(CrasClientTestSuite, OutputStreamConnectedFail) {
271 StreamConnectedFail(CRAS_STREAM_OUTPUT);
272 }
273
TEST_F(CrasClientTestSuite,AddAndRemoveStream)274 TEST_F(CrasClientTestSuite, AddAndRemoveStream) {
275 cras_stream_id_t stream_id;
276
277 // Dynamically allocat the stream so that it can be freed later.
278 struct client_stream* stream_ptr = (struct client_stream *)
279 malloc(sizeof(*stream_ptr));
280 memcpy(stream_ptr, &stream_, sizeof(client_stream));
281 stream_ptr->config = (struct cras_stream_params *)
282 malloc(sizeof(*(stream_ptr->config)));
283 memcpy(stream_ptr->config, stream_.config, sizeof(*(stream_.config)));
284
285 pthread_cond_timedwait_retval = ETIMEDOUT;
286 EXPECT_EQ(-ETIMEDOUT, client_thread_add_stream(
287 &client_, stream_ptr, &stream_id, NO_DEVICE));
288 EXPECT_EQ(pthread_cond_timedwait_called, 1);
289 EXPECT_EQ(pthread_join_called, 0);
290
291 InitStaticVariables();
292 EXPECT_EQ(0, client_thread_add_stream(
293 &client_, stream_ptr, &stream_id, NO_DEVICE));
294 EXPECT_EQ(&client_, stream_ptr->client);
295 EXPECT_EQ(stream_id, stream_ptr->id);
296 EXPECT_EQ(pthread_create_called, 1);
297 EXPECT_EQ(pipe_called, 1);
298 EXPECT_EQ(1, sendmsg_called); // send connect message to server
299 EXPECT_EQ(stream_ptr, stream_from_id(&client_, stream_id));
300
301 stream_ptr->thread.state = CRAS_THREAD_RUNNING;
302
303 EXPECT_EQ(0, client_thread_rm_stream(&client_, stream_id));
304
305 // One for the disconnect message to server,
306 // the other is to wake_up the audio thread
307 EXPECT_EQ(2, write_called);
308 EXPECT_EQ(1, pthread_join_called);
309
310 EXPECT_EQ(NULL, stream_from_id(&client_, stream_id));
311 }
312
313 } // namepsace
314
main(int argc,char ** argv)315 int main(int argc, char **argv) {
316 ::testing::InitGoogleTest(&argc, argv);
317 return RUN_ALL_TESTS();
318 }
319
320 /* stubs */
321 extern "C" {
322
write(int fd,const void * buf,size_t count)323 ssize_t write(int fd, const void *buf, size_t count) {
324 ++write_called;
325 return count;
326 }
327
sendmsg(int sockfd,const struct msghdr * msg,int flags)328 ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) {
329 ++sendmsg_called;
330 return msg->msg_iov->iov_len;
331 }
332
pipe(int pipefd[2])333 int pipe(int pipefd[2]) {
334 pipefd[0] = 1;
335 pipefd[1] = 2;
336 ++pipe_called;
337 return 0;
338 }
339
close(int fd)340 int close(int fd) {
341 ++close_called;
342 return 0;
343 }
344
pthread_create(pthread_t * thread,const pthread_attr_t * attr,void * (* start_routine)(void *),void * arg)345 int pthread_create(pthread_t *thread,
346 const pthread_attr_t *attr,
347 void *(*start_routine)(void *),
348 void *arg) {
349 ++pthread_create_called;
350 return pthread_create_returned_value;
351 }
352
pthread_join(pthread_t thread,void ** retval)353 int pthread_join(pthread_t thread, void **retval) {
354 ++pthread_join_called;
355 return 0;
356 }
357
pthread_cond_timedwait(pthread_cond_t * __restrict cond,pthread_mutex_t * __restrict mutex,const struct timespec * __restrict timeout)358 int pthread_cond_timedwait(pthread_cond_t *__restrict cond,
359 pthread_mutex_t *__restrict mutex,
360 const struct timespec *__restrict timeout) {
361 ++pthread_cond_timedwait_called;
362 return pthread_cond_timedwait_retval;
363 }
364
clock_gettime(clockid_t clk_id,struct timespec * tp)365 int clock_gettime(clockid_t clk_id, struct timespec *tp) {
366 tp->tv_sec = 0;
367 tp->tv_nsec = 0;
368 return 0;
369 }
370
mmap(void * addr,size_t length,int prot,int flags,int fd,off_t offset)371 void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
372 {
373 return mmap_return_value;
374 }
375
cras_audio_format_create(snd_pcm_format_t format,size_t frame_rate,size_t num_channels)376 struct cras_audio_format *cras_audio_format_create(snd_pcm_format_t format,
377 size_t frame_rate,
378 size_t num_channels)
379 {
380 return reinterpret_cast<struct cras_audio_format*>(0x123);
381 }
382
cras_audio_format_destroy(struct cras_audio_format * fmt)383 void cras_audio_format_destroy(struct cras_audio_format *fmt)
384 {
385 }
386
387 }
388