1 // Copyright 2019 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 #include <unistd.h>
8
9 extern "C" {
10 #include "audio_thread.h"
11 #include "cras_bt_log.h"
12 #include "cras_messages.h"
13 #include "cras_rclient.h"
14 #include "cras_rstream.h"
15 #include "cras_system_state.h"
16
17 // Access to data structures and static functions.
18 #include "cras_playback_rclient.c"
19 #include "cras_rclient_util.c"
20 }
21 static unsigned int cras_make_fd_nonblocking_called;
22 static unsigned int cras_observer_remove_called;
23 static unsigned int cras_server_metrics_stream_config_called;
24 static int stream_list_add_called;
25 static int stream_list_add_return;
26 static unsigned int stream_list_rm_called;
27 static struct cras_audio_shm dummy_shm;
28 static struct cras_rstream dummy_rstream;
29 static unsigned int cras_rstream_config_init_with_message_called;
30
ResetStubData()31 void ResetStubData() {
32 cras_make_fd_nonblocking_called = 0;
33 cras_observer_remove_called = 0;
34 cras_server_metrics_stream_config_called = 0;
35 stream_list_add_called = 0;
36 stream_list_add_return = 0;
37 stream_list_rm_called = 0;
38 cras_rstream_config_init_with_message_called = 0;
39 }
40
41 namespace {
42
TEST(RClientSuite,CreateSendMessage)43 TEST(RClientSuite, CreateSendMessage) {
44 struct cras_rclient* rclient;
45 int rc;
46 struct cras_client_connected msg;
47 int pipe_fds[2];
48
49 ResetStubData();
50
51 rc = pipe(pipe_fds);
52 ASSERT_EQ(0, rc);
53
54 rclient = cras_playback_rclient_create(pipe_fds[1], 800);
55 ASSERT_NE((void*)NULL, rclient);
56 EXPECT_EQ(800, rclient->id);
57
58 rc = read(pipe_fds[0], &msg, sizeof(msg));
59 EXPECT_EQ(sizeof(msg), rc);
60 EXPECT_EQ(CRAS_CLIENT_CONNECTED, msg.header.id);
61
62 rclient->ops->destroy(rclient);
63 EXPECT_EQ(1, cras_observer_remove_called);
64 close(pipe_fds[0]);
65 close(pipe_fds[1]);
66 }
67
68 class CPRMessageSuite : public testing::Test {
69 protected:
SetUp()70 virtual void SetUp() {
71 int rc;
72 struct cras_client_connected msg;
73
74 rc = pipe(pipe_fds_);
75 if (rc < 0)
76 return;
77
78 rclient_ = cras_playback_rclient_create(pipe_fds_[1], 1);
79 rc = read(pipe_fds_[0], &msg, sizeof(msg));
80 if (rc < 0)
81 return;
82
83 fmt = {
84 .format = SND_PCM_FORMAT_S16_LE,
85 .frame_rate = 48000,
86 .num_channels = 2,
87 };
88 cras_audio_format_set_default_channel_layout(&fmt);
89 ResetStubData();
90 }
91
TearDown()92 virtual void TearDown() {
93 rclient_->ops->destroy(rclient_);
94 close(pipe_fds_[0]);
95 close(pipe_fds_[1]);
96 }
97
98 struct cras_rclient* rclient_;
99 struct cras_audio_format fmt;
100 int pipe_fds_[2];
101 int fd_;
102 };
103
TEST_F(CPRMessageSuite,StreamConnectMessage)104 TEST_F(CPRMessageSuite, StreamConnectMessage) {
105 struct cras_client_stream_connected out_msg;
106 int rc;
107
108 struct cras_connect_message msg;
109 cras_stream_id_t stream_id = 0x10002;
110 cras_fill_connect_message(&msg, CRAS_STREAM_OUTPUT, stream_id,
111 CRAS_STREAM_TYPE_DEFAULT, CRAS_CLIENT_TYPE_UNKNOWN,
112 480, 240, /*flags=*/0, /*effects=*/0, fmt,
113 NO_DEVICE, /*client_shm_size=*/0);
114 ASSERT_EQ(stream_id, msg.stream_id);
115
116 fd_ = 100;
117 rclient_->ops->handle_message_from_client(rclient_, &msg.header, &fd_, 1);
118 EXPECT_EQ(1, cras_make_fd_nonblocking_called);
119 EXPECT_EQ(1, cras_rstream_config_init_with_message_called);
120 EXPECT_EQ(1, stream_list_add_called);
121 EXPECT_EQ(0, stream_list_rm_called);
122
123 rc = read(pipe_fds_[0], &out_msg, sizeof(out_msg));
124 EXPECT_EQ(sizeof(out_msg), rc);
125 EXPECT_EQ(stream_id, out_msg.stream_id);
126 }
127
TEST_F(CPRMessageSuite,StreamConnectMessageInvalidDirection)128 TEST_F(CPRMessageSuite, StreamConnectMessageInvalidDirection) {
129 struct cras_client_stream_connected out_msg;
130 int rc;
131
132 struct cras_connect_message msg;
133 cras_stream_id_t stream_id = 0x10002;
134
135 for (int i = 0; i < CRAS_NUM_DIRECTIONS; i++) {
136 const auto dir = static_cast<CRAS_STREAM_DIRECTION>(i);
137 if (dir == CRAS_STREAM_OUTPUT)
138 continue;
139 cras_fill_connect_message(&msg, dir, stream_id, CRAS_STREAM_TYPE_DEFAULT,
140 CRAS_CLIENT_TYPE_UNKNOWN, 480, 240, /*flags=*/0,
141 /*effects=*/0, fmt, NO_DEVICE,
142 /*client_shm_size=*/0);
143 ASSERT_EQ(stream_id, msg.stream_id);
144
145 fd_ = 100;
146 rc = rclient_->ops->handle_message_from_client(rclient_, &msg.header, &fd_,
147 1);
148 EXPECT_EQ(-EINVAL, rc);
149 EXPECT_EQ(0, cras_make_fd_nonblocking_called);
150 EXPECT_EQ(0, cras_rstream_config_init_with_message_called);
151 EXPECT_EQ(0, stream_list_add_called);
152 EXPECT_EQ(0, stream_list_rm_called);
153
154 rc = read(pipe_fds_[0], &out_msg, sizeof(out_msg));
155 EXPECT_EQ(sizeof(out_msg), rc);
156 EXPECT_EQ(-EINVAL, out_msg.err);
157 EXPECT_EQ(stream_id, out_msg.stream_id);
158 }
159 }
160
TEST_F(CPRMessageSuite,StreamConnectMessageInvalidClientId)161 TEST_F(CPRMessageSuite, StreamConnectMessageInvalidClientId) {
162 struct cras_client_stream_connected out_msg;
163 int rc;
164
165 struct cras_connect_message msg;
166 cras_stream_id_t stream_id = 0x20002; // stream_id with invalid client_id
167 cras_fill_connect_message(&msg, CRAS_STREAM_OUTPUT, stream_id,
168 CRAS_STREAM_TYPE_DEFAULT, CRAS_CLIENT_TYPE_UNKNOWN,
169 480, 240, /*flags=*/0, /*effects=*/0, fmt,
170 NO_DEVICE, /*client_shm_size=*/0);
171 ASSERT_EQ(stream_id, msg.stream_id);
172
173 fd_ = 100;
174 rc =
175 rclient_->ops->handle_message_from_client(rclient_, &msg.header, &fd_, 1);
176 EXPECT_EQ(-EINVAL, rc);
177 EXPECT_EQ(0, cras_make_fd_nonblocking_called);
178 EXPECT_EQ(0, cras_rstream_config_init_with_message_called);
179 EXPECT_EQ(0, stream_list_add_called);
180 EXPECT_EQ(0, stream_list_rm_called);
181
182 rc = read(pipe_fds_[0], &out_msg, sizeof(out_msg));
183 EXPECT_EQ(sizeof(out_msg), rc);
184 EXPECT_EQ(-EINVAL, out_msg.err);
185 EXPECT_EQ(stream_id, out_msg.stream_id);
186 }
187
188 /*
189 * TODO(yuhsaun): Remove this test when there are no client uses the old
190 * craslib. (CRAS_PROTO_VER = 3)
191 */
TEST_F(CPRMessageSuite,StreamConnectMessageOldProtocal)192 TEST_F(CPRMessageSuite, StreamConnectMessageOldProtocal) {
193 struct cras_client_stream_connected out_msg;
194 int rc;
195
196 struct cras_connect_message_old msg;
197 cras_stream_id_t stream_id = 0x10002;
198
199 msg.proto_version = 3;
200 msg.direction = CRAS_STREAM_OUTPUT;
201 msg.stream_id = stream_id;
202 msg.stream_type = CRAS_STREAM_TYPE_DEFAULT;
203 msg.buffer_frames = 480;
204 msg.cb_threshold = 240;
205 msg.flags = 0;
206 msg.effects = 0;
207 pack_cras_audio_format(&msg.format, &fmt);
208 msg.dev_idx = NO_DEVICE;
209 msg.header.id = CRAS_SERVER_CONNECT_STREAM;
210 msg.header.length = sizeof(struct cras_connect_message_old);
211
212 fd_ = 100;
213 rc =
214 rclient_->ops->handle_message_from_client(rclient_, &msg.header, &fd_, 1);
215 EXPECT_EQ(1, cras_make_fd_nonblocking_called);
216 EXPECT_EQ(1, cras_rstream_config_init_with_message_called);
217 EXPECT_EQ(1, stream_list_add_called);
218 EXPECT_EQ(0, stream_list_rm_called);
219
220 rc = read(pipe_fds_[0], &out_msg, sizeof(out_msg));
221 EXPECT_EQ(sizeof(out_msg), rc);
222 EXPECT_EQ(stream_id, out_msg.stream_id);
223 }
224
TEST_F(CPRMessageSuite,StreamDisconnectMessage)225 TEST_F(CPRMessageSuite, StreamDisconnectMessage) {
226 struct cras_disconnect_stream_message msg;
227 cras_stream_id_t stream_id = 0x10002;
228 cras_fill_disconnect_stream_message(&msg, stream_id);
229
230 rclient_->ops->handle_message_from_client(rclient_, &msg.header, NULL, 0);
231 EXPECT_EQ(0, stream_list_add_called);
232 EXPECT_EQ(1, stream_list_rm_called);
233 }
234
TEST_F(CPRMessageSuite,StreamDisconnectMessageInvalidClientId)235 TEST_F(CPRMessageSuite, StreamDisconnectMessageInvalidClientId) {
236 struct cras_disconnect_stream_message msg;
237 cras_stream_id_t stream_id = 0x20002; // stream_id with invalid client_id
238 cras_fill_disconnect_stream_message(&msg, stream_id);
239
240 rclient_->ops->handle_message_from_client(rclient_, &msg.header, NULL, 0);
241 EXPECT_EQ(0, stream_list_add_called);
242 EXPECT_EQ(0, stream_list_rm_called);
243 }
244 } // namespace
245
main(int argc,char ** argv)246 int main(int argc, char** argv) {
247 ::testing::InitGoogleTest(&argc, argv);
248 return RUN_ALL_TESTS();
249 }
250
251 /* stubs */
252 extern "C" {
253
cras_iodev_list_get_stream_list()254 struct stream_list* cras_iodev_list_get_stream_list() {
255 return NULL;
256 }
257
cras_make_fd_nonblocking(int fd)258 int cras_make_fd_nonblocking(int fd) {
259 cras_make_fd_nonblocking_called++;
260 return 0;
261 }
262
cras_observer_remove(struct cras_observer_client * client)263 void cras_observer_remove(struct cras_observer_client* client) {
264 cras_observer_remove_called++;
265 }
266
cras_rstream_get_effects(const struct cras_rstream * stream)267 unsigned int cras_rstream_get_effects(const struct cras_rstream* stream) {
268 return 0;
269 }
270
cras_server_metrics_stream_config(struct cras_rstream_config * config)271 int cras_server_metrics_stream_config(struct cras_rstream_config* config) {
272 cras_server_metrics_stream_config_called++;
273 return 0;
274 }
275
cras_send_with_fds(int sockfd,const void * buf,size_t len,int * fd,unsigned int num_fds)276 int cras_send_with_fds(int sockfd,
277 const void* buf,
278 size_t len,
279 int* fd,
280 unsigned int num_fds) {
281 return write(sockfd, buf, len);
282 }
283
cras_sys_state_shm_fd()284 key_t cras_sys_state_shm_fd() {
285 return 1;
286 }
287
cras_system_set_suspended(int suspended)288 void cras_system_set_suspended(int suspended) {}
289
stream_list_rm_all_client_streams(struct stream_list * list,struct cras_rclient * rclient)290 int stream_list_rm_all_client_streams(struct stream_list* list,
291 struct cras_rclient* rclient) {
292 return 0;
293 }
294
stream_list_rm(struct stream_list * list,cras_stream_id_t id)295 int stream_list_rm(struct stream_list* list, cras_stream_id_t id) {
296 stream_list_rm_called++;
297 return 0;
298 }
299
stream_list_add(struct stream_list * list,struct cras_rstream_config * config,struct cras_rstream ** stream)300 int stream_list_add(struct stream_list* list,
301 struct cras_rstream_config* config,
302 struct cras_rstream** stream) {
303 int ret;
304
305 *stream = &dummy_rstream;
306
307 stream_list_add_called++;
308 ret = stream_list_add_return;
309 if (ret)
310 stream_list_add_return = -EINVAL;
311
312 dummy_rstream.shm = &dummy_shm;
313 dummy_rstream.direction = config->direction;
314 dummy_rstream.stream_id = config->stream_id;
315
316 return ret;
317 }
318
cras_rstream_config_init_with_message(struct cras_rclient * client,const struct cras_connect_message * msg,int * aud_fd,int * client_shm_fd,const struct cras_audio_format * remote_fmt,struct cras_rstream_config * stream_config)319 void cras_rstream_config_init_with_message(
320 struct cras_rclient* client,
321 const struct cras_connect_message* msg,
322 int* aud_fd,
323 int* client_shm_fd,
324 const struct cras_audio_format* remote_fmt,
325 struct cras_rstream_config* stream_config) {
326 cras_rstream_config_init_with_message_called++;
327 }
328
cras_rstream_config_cleanup(struct cras_rstream_config * stream_config)329 void cras_rstream_config_cleanup(struct cras_rstream_config* stream_config) {}
330
331 } // extern "C"
332