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