1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <vector>
18
19 #include <gmock/gmock.h>
20
21 #include <application.h>
22 #include <nos/transport.h>
23
24 #include "crc16.h"
25
26 using ::testing::_;
27 using ::testing::Args;
28 using ::testing::DoAll;
29 using ::testing::Eq;
30 using ::testing::ElementsAreArray;
31 using ::testing::InSequence;
32 using ::testing::IsNull;
33 using ::testing::Return;
34 using ::testing::SetArrayArgument;
35 using ::testing::StrictMock;
36
37 namespace {
38
39 struct Device {
40 virtual int Read(uint32_t command, uint8_t* buf, uint32_t len) = 0;
41 virtual int Write(uint32_t command, const uint8_t* buf, uint32_t len) = 0;
42 virtual int WaitForInterrupt(int msecs) = 0;
43 virtual int Reset() = 0;
44 };
45
46 struct MockDevice : public Device {
47 MOCK_METHOD3(Read, int(uint32_t command, uint8_t* buf, uint32_t len));
48 MOCK_METHOD3(Write, int(uint32_t command, const uint8_t* buf, uint32_t len));
49 MOCK_METHOD1(WaitForInterrupt, int(int msecs));
50 MOCK_METHOD0(Reset, int());
51 };
52
53 // We want to closely examine the interactions with the device to make it a
54 // strict mock
55 using CtxType = StrictMock<MockDevice>;
56
57 // Forward calls onto the mock
read_datagram(void * ctx,uint32_t command,uint8_t * buf,uint32_t len)58 int read_datagram(void* ctx, uint32_t command, uint8_t* buf, uint32_t len) {
59 return reinterpret_cast<CtxType*>(ctx)->Read(command, buf, len);
60 }
write_datagram(void * ctx,uint32_t command,const uint8_t * buf,uint32_t len)61 int write_datagram(void* ctx, uint32_t command, const uint8_t* buf, uint32_t len) {
62 return reinterpret_cast<CtxType*>(ctx)->Write(command, buf, len);
63 }
wait_for_interrupt(void * ctx,int msecs)64 int wait_for_interrupt(void* ctx, int msecs) {
65 return reinterpret_cast<CtxType*>(ctx)->WaitForInterrupt(msecs);
66 }
reset(void * ctx)67 int reset(void* ctx) {
68 return reinterpret_cast<CtxType*>(ctx)->Reset();
69 }
close_device(void * ctx)70 void close_device(void* ctx) {
71 delete reinterpret_cast<CtxType*>(ctx);
72 }
73
74 // Implement the datagram API that calls a mock.
75 extern "C" {
nos_device_open(const char * device_name,struct nos_device * dev)76 int nos_device_open(const char* device_name, struct nos_device* dev) {
77 EXPECT_THAT(device_name, IsNull());
78 dev->ctx = new CtxType;
79 dev->ops.read = read_datagram;
80 dev->ops.write = write_datagram;
81 dev->ops.wait_for_interrupt = wait_for_interrupt;
82 dev->ops.reset = reset;
83 dev->ops.close = close_device;
84 return 0;
85 }
86 }
87
88 // Test fixture that sets up the mocked device.
89 struct TransportTest : public ::testing::Test {
SetUp__anonb1b6e5140111::TransportTest90 virtual void SetUp() override {
91 nos_device_open(nullptr, &dev_);
92 mock_dev_ = reinterpret_cast<CtxType*>(dev_.ctx);
93 }
TearDown__anonb1b6e5140111::TransportTest94 virtual void TearDown() override {
95 dev_.ops.close(dev_.ctx);
96 }
97
dev__anonb1b6e5140111::TransportTest98 nos_device* dev() { return &dev_; }
mock_dev__anonb1b6e5140111::TransportTest99 CtxType& mock_dev() { return *mock_dev_; }
100
101 private:
102 nos_device dev_;
103 CtxType* mock_dev_;
104 };
105
command_crc(uint32_t command,const uint8_t * args,uint16_t args_len,const transport_command_info * command_info)106 uint16_t command_crc(uint32_t command, const uint8_t* args, uint16_t args_len,
107 const transport_command_info* command_info) {
108 uint16_t crc = crc16(&args_len, sizeof(args_len));
109 crc = crc16_update(args, args_len, crc);
110 crc = crc16_update(&command, sizeof(command), crc);
111 crc = crc16_update(command_info, sizeof(*command_info), crc);
112 return htole16(crc);
113 }
114
115 } // namespace
116
117 /* Actions to return mock data */
118
119 #define READ_UNSET 0xdf
120
ACTION(ReadStatusV0_Idle)121 ACTION(ReadStatusV0_Idle) {
122 transport_status* status = (transport_status*)arg1;
123 memset(status, READ_UNSET, sizeof(*status));
124 status->status = APP_STATUS_IDLE;
125 status->reply_len = 0;
126 }
127
ACTION(ReadStatusV1_Idle)128 ACTION(ReadStatusV1_Idle) {
129 transport_status* status = (transport_status*)arg1;
130 memset(status, READ_UNSET, sizeof(*status));
131 status->status = APP_STATUS_IDLE;
132 status->reply_len = 0;
133 status->length = sizeof(transport_status);
134 status->version = TRANSPORT_V1;
135 status->flags = 0;
136 status->crc = STATUS_CRC_FOR_IDLE;
137 status->reply_crc = 0;
138 }
139
ACTION(ReadStatusV1_IdleWithBadCrc)140 ACTION(ReadStatusV1_IdleWithBadCrc) {
141 transport_status* status = (transport_status*)arg1;
142 memset(status, READ_UNSET, sizeof(*status));
143 status->status = APP_STATUS_IDLE;
144 status->reply_len = 0;
145 status->length = sizeof(transport_status);
146 status->version = TRANSPORT_V1;
147 status->flags = 0;
148 status->crc = STATUS_CRC_FOR_IDLE + 1; // <- wrong!
149 status->reply_crc = 0;
150 }
151
ACTION(ReadStatusV1_Working)152 ACTION(ReadStatusV1_Working) {
153 transport_status* status = (transport_status*)arg1;
154 memset(status, READ_UNSET, sizeof(*status));
155 status->status = APP_STATUS_IDLE;
156 status->reply_len = 0;
157 status->length = sizeof(transport_status);
158 status->version = TRANSPORT_V1;
159 status->flags = STATUS_FLAG_WORKING;
160 status->crc = STATUS_CRC_FOR_WORKING;
161 status->reply_crc = 0;
162 }
163
ACTION_P(ReadStatusV0_DoneWithData,reply_len)164 ACTION_P(ReadStatusV0_DoneWithData, reply_len) {
165 transport_status* status = (transport_status*)arg1;
166 memset(status, READ_UNSET, sizeof(*status));
167 status->status = APP_STATUS_DONE | APP_SUCCESS;
168 status->reply_len = reply_len;
169 }
170
ACTION_P2(ReadStatusV1_DoneWithData,reply,reply_len)171 ACTION_P2(ReadStatusV1_DoneWithData, reply, reply_len) {
172 transport_status* status = (transport_status*)arg1;
173 memset(status, READ_UNSET, sizeof(*status));
174 status->status = APP_STATUS_DONE | APP_SUCCESS;
175 status->reply_len = reply_len;
176 status->length = sizeof(transport_status);
177 status->version = TRANSPORT_V1;
178 status->flags = 0;
179 status->reply_crc = crc16(reply, reply_len);
180 status->crc = 0;
181 status->crc = crc16(status, status->length);
182 }
183
ACTION(ReadStatusV1_BadCrc)184 ACTION(ReadStatusV1_BadCrc) {
185 transport_status* status = (transport_status*)arg1;
186 memset(status, READ_UNSET, sizeof(*status));
187 status->status = APP_STATUS_DONE | APP_ERROR_CHECKSUM;
188 status->reply_len = 0;
189 status->length = sizeof(transport_status);
190 status->version = TRANSPORT_V1;
191 status->flags = 0;
192 status->crc = 0x92c0;
193 status->reply_crc = 0;
194 }
195
ACTION(ReadStatusV42_Working)196 ACTION(ReadStatusV42_Working) {
197 memset(arg1, 0xb3, STATUS_MAX_LENGTH);
198 transport_status* status = (transport_status*)arg1;
199 status->status = APP_STATUS_IDLE;
200 status->reply_len = 0;
201 status->length = STATUS_MAX_LENGTH;
202 status->version = 42;
203 status->flags = STATUS_FLAG_WORKING;
204 status->crc = 0xaec0;
205 status->reply_crc = 0;
206 }
207
ACTION_P3(ReadData,len,data,size)208 ACTION_P3(ReadData, len, data, size) {
209 memset(arg1, READ_UNSET, len);
210 memcpy(arg1, data, size);
211 }
212
213 /* Helper macros to expect datagram calls */
214
215 #define EXPECT_GET_STATUS_V0_IDLE(app_id) do { \
216 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
217 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
218 .WillOnce(DoAll(ReadStatusV0_Idle(), Return(0))); \
219 } while (0)
220
221 #define EXPECT_GET_STATUS_IDLE(app_id) do { \
222 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
223 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
224 .WillOnce(DoAll(ReadStatusV1_Idle(), Return(0))); \
225 } while (0)
226
227 #define EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id) do { \
228 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
229 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
230 .WillOnce(DoAll(ReadStatusV1_IdleWithBadCrc(), Return(0))); \
231 } while (0)
232
233 #define EXPECT_GET_STATUS_BAD_CRC(app_id) do { \
234 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
235 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
236 .WillOnce(DoAll(ReadStatusV1_BadCrc(), Return(0))); \
237 } while (0)
238
239 #define EXPECT_GET_STATUS_WORKING(app_id) do { \
240 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
241 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
242 .WillOnce(DoAll(ReadStatusV1_Working(), Return(0))); \
243 } while (0)
244
245 #define EXPECT_GET_STATUS_V0_DONE(app_id) do { \
246 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
247 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
248 .WillOnce(DoAll(ReadStatusV0_DoneWithData(0), Return(0))); \
249 } while (0)
250
251 #define EXPECT_GET_STATUS_V0_DONE_WITH_DATA(app_id, reply_len) do { \
252 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
253 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
254 .WillOnce(DoAll(ReadStatusV0_DoneWithData((reply_len)), Return(0))); \
255 } while (0)
256
257 #define EXPECT_GET_STATUS_DONE(app_id) do { \
258 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
259 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
260 .WillOnce(DoAll(ReadStatusV1_DoneWithData(nullptr, 0), Return(0))); \
261 } while (0)
262
263 #define EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, reply, reply_len) do { \
264 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
265 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
266 .WillOnce(DoAll(ReadStatusV1_DoneWithData((reply), (reply_len)), Return(0))); \
267 } while (0)
268
269 #define EXPECT_GET_STATUS_DONE_BAD_CRC(app_id, reply, reply_len) do { \
270 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
271 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
272 .WillOnce(DoAll(ReadStatusV1_DoneBadCrc((reply), (reply_len)), Return(0))); \
273 } while (0)
274
275 #define EXPECT_SEND_DATA(app_id, args, args_len) do { \
276 const uint32_t command = CMD_ID((app_id)) | CMD_IS_DATA | CMD_TRANSPORT | CMD_PARAM((args_len)); \
277 EXPECT_CALL(mock_dev(), Write(command, _, (args_len))) \
278 .With(Args<1,2>(ElementsAreArray((uint8_t*)(args), (args_len)))) \
279 .WillOnce(Return(0)); \
280 } while (0)
281
282 #define EXPECT_GO_COMMAND(app_id, param, args, args_len, reply_len) do { \
283 const uint32_t command = CMD_ID((app_id)) | CMD_PARAM((param)); \
284 transport_command_info command_info = {}; \
285 command_info.length = sizeof(command_info); \
286 command_info.version = htole16(TRANSPORT_V1); \
287 command_info.reply_len_hint = htole16((reply_len)); \
288 command_info.crc = command_crc(command, (args), (args_len), &command_info); \
289 EXPECT_CALL(mock_dev(), Write(command, _, command_info.length)) \
290 .With(Args<1,2>(ElementsAreArray((uint8_t*)&command_info, command_info.length))) \
291 .WillOnce(Return(0)); \
292 } while (0)
293
294 #define EXPECT_RECV_DATA(app_id, len, reply, reply_len) do { \
295 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_IS_DATA | CMD_TRANSPORT; \
296 EXPECT_CALL(mock_dev(), Read(command, _, (reply_len))) \
297 .WillOnce(DoAll(ReadData((len), (reply), (reply_len)), Return(0))); \
298 } while (0)
299
300 #define EXPECT_RECV_MORE_DATA(app_id, len, reply, reply_len) do { \
301 const uint32_t command = \
302 CMD_ID((app_id)) | CMD_IS_READ | CMD_IS_DATA | CMD_MORE_TO_COME | CMD_TRANSPORT; \
303 EXPECT_CALL(mock_dev(), Read(command, _, (reply_len))) \
304 .WillOnce(DoAll(ReadData((len), (reply), (reply_len)), Return(0))); \
305 } while (0)
306
307 #define EXPECT_CLEAR_STATUS(app_id) do { \
308 const uint32_t command = CMD_ID((app_id)) | CMD_TRANSPORT; \
309 EXPECT_CALL(mock_dev(), Write(command, _, 0)) \
310 .WillOnce(Return(0)); \
311 } while (0)
312
313 /* Protocol tests */
314
TEST_F(TransportTest,WorkingAppIsBusy)315 TEST_F(TransportTest, WorkingAppIsBusy) {
316 const uint8_t app_id = 213;
317 EXPECT_GET_STATUS_WORKING(app_id);
318
319 const uint16_t param = 2;
320 uint32_t reply_len = 0;
321 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, &reply_len);
322 EXPECT_THAT(res, Eq(APP_ERROR_BUSY));
323 }
324
TEST_F(TransportTest,WorkingIsForwardCompatible)325 TEST_F(TransportTest, WorkingIsForwardCompatible) {
326 const uint8_t app_id = 25;
327 const uint32_t command = CMD_ID(app_id) | CMD_IS_READ | CMD_TRANSPORT;
328 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH))
329 .WillOnce(DoAll(ReadStatusV42_Working(), Return(0)));
330
331 const uint16_t param = 2;
332 uint32_t reply_len = 0;
333 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, &reply_len);
334 EXPECT_THAT(res, Eq(APP_ERROR_BUSY));
335 }
336
TEST_F(TransportTest,SuccessIfStatusNotClear)337 TEST_F(TransportTest, SuccessIfStatusNotClear) {
338 const uint8_t app_id = 12;
339 const uint16_t param = 2;
340 const uint8_t args[] = {1, 2, 3};
341 const uint16_t args_len = 3;
342
343 InSequence please;
344 EXPECT_GET_STATUS_BAD_CRC(app_id);
345 // Try and reset
346 EXPECT_CLEAR_STATUS(app_id);
347 // Try again
348 EXPECT_GET_STATUS_IDLE(app_id);
349 EXPECT_SEND_DATA(app_id, args, args_len);
350 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
351 EXPECT_GET_STATUS_WORKING(app_id);
352 EXPECT_GET_STATUS_DONE(app_id);
353 EXPECT_CLEAR_STATUS(app_id);
354
355 uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
356 EXPECT_THAT(res, Eq(APP_SUCCESS));
357 }
358
TEST_F(TransportTest,StatusCrcError)359 TEST_F(TransportTest, StatusCrcError) {
360 const uint8_t app_id = 53;
361 const uint16_t param = 192;
362
363 InSequence please;
364 // Try 3 times
365 EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
366 EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
367 EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
368
369 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr);
370 EXPECT_THAT(res, Eq(APP_ERROR_IO));
371 }
372
TEST_F(TransportTest,FailToClearStatus)373 TEST_F(TransportTest, FailToClearStatus) {
374 const uint8_t app_id = 12;
375 const uint16_t param = 2;
376 const uint8_t args[] = {1, 2, 3};
377 const uint16_t args_len = 3;
378
379 InSequence please;
380 EXPECT_GET_STATUS_BAD_CRC(app_id);
381 // Try and reset
382 EXPECT_CLEAR_STATUS(app_id);
383 // No luck
384 EXPECT_GET_STATUS_BAD_CRC(app_id);
385
386 uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
387 EXPECT_THAT(res, Eq(APP_ERROR_IO));
388 }
389
TEST_F(TransportTest,FailToClearStatusAfterStatusCrcError)390 TEST_F(TransportTest, FailToClearStatusAfterStatusCrcError) {
391 const uint8_t app_id = 53;
392 const uint16_t param = 192;
393
394 InSequence please;
395 // Try 3 times
396 EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
397 EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
398 EXPECT_GET_STATUS_BAD_CRC(app_id);
399 // Try and reset
400 EXPECT_CLEAR_STATUS(app_id);
401 // No luck
402 EXPECT_GET_STATUS_BAD_CRC(app_id);
403
404 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr);
405 EXPECT_THAT(res, Eq(APP_ERROR_IO));
406 }
407
TEST_F(TransportTest,RequestCrcError)408 TEST_F(TransportTest, RequestCrcError) {
409 const uint8_t app_id = 58;
410 const uint16_t param = 93;
411 const uint8_t args[] = {4, 24, 183, 255, 219};
412 const uint16_t args_len = 5;
413
414 InSequence please;
415 // Should try 3 times
416 EXPECT_GET_STATUS_IDLE(app_id);
417 EXPECT_SEND_DATA(app_id, args, args_len);
418 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
419 EXPECT_GET_STATUS_BAD_CRC(app_id);
420 // 2 more
421 EXPECT_GET_STATUS_IDLE(app_id);
422 EXPECT_SEND_DATA(app_id, args, args_len);
423 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
424 EXPECT_GET_STATUS_BAD_CRC(app_id);
425 // last one
426 EXPECT_GET_STATUS_IDLE(app_id);
427 EXPECT_SEND_DATA(app_id, args, args_len);
428 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
429 EXPECT_GET_STATUS_BAD_CRC(app_id);
430 // Clean up
431 EXPECT_CLEAR_STATUS(app_id);
432
433 uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
434 EXPECT_THAT(res, Eq(APP_ERROR_IO));
435 }
436
TEST_F(TransportTest,SuccessAfterRequestCrcError)437 TEST_F(TransportTest, SuccessAfterRequestCrcError) {
438 const uint8_t app_id = 255;
439 const uint16_t param = 163;
440 const uint8_t args[] = {42, 89, 125, 0, 83, 92, 80};
441 const uint16_t args_len = 7;
442
443 InSequence please;
444 // First request is CRC error
445 EXPECT_GET_STATUS_IDLE(app_id);
446 EXPECT_SEND_DATA(app_id, args, args_len);
447 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
448 EXPECT_GET_STATUS_BAD_CRC(app_id);
449 // The retry succeeds
450 EXPECT_GET_STATUS_IDLE(app_id);
451 EXPECT_SEND_DATA(app_id, args, args_len);
452 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
453 EXPECT_GET_STATUS_WORKING(app_id);
454 EXPECT_GET_STATUS_DONE(app_id);
455 EXPECT_CLEAR_STATUS(app_id);
456
457 uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
458 EXPECT_THAT(res, Eq(APP_SUCCESS));
459 }
460
TEST_F(TransportTest,SuccessWithoutReply)461 TEST_F(TransportTest, SuccessWithoutReply) {
462 const uint8_t app_id = 12;
463 const uint16_t param = 2;
464 const uint8_t args[] = {1, 2, 3};
465 const uint16_t args_len = 3;
466
467 InSequence please;
468 EXPECT_GET_STATUS_IDLE(app_id);
469 EXPECT_SEND_DATA(app_id, args, args_len);
470 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
471 EXPECT_GET_STATUS_WORKING(app_id);
472 EXPECT_GET_STATUS_DONE(app_id);
473 EXPECT_CLEAR_STATUS(app_id);
474
475 uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
476 EXPECT_THAT(res, Eq(APP_SUCCESS));
477 }
478
TEST_F(TransportTest,DetectAppAbort)479 TEST_F(TransportTest, DetectAppAbort) {
480 const uint8_t app_id = 25;
481 const uint16_t param = 252;
482 const uint8_t args[] = {17, 27, 43, 193};
483 const uint16_t args_len = 4;
484
485 InSequence please;
486 EXPECT_GET_STATUS_IDLE(app_id);
487 EXPECT_SEND_DATA(app_id, args, args_len);
488 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
489 EXPECT_GET_STATUS_WORKING(app_id);
490 EXPECT_GET_STATUS_WORKING(app_id);
491 EXPECT_GET_STATUS_WORKING(app_id);
492 // It just stopped working
493 EXPECT_GET_STATUS_IDLE(app_id);
494 // It's probably already clear but just making sure
495 EXPECT_CLEAR_STATUS(app_id);
496
497 uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
498 EXPECT_THAT(res, Eq(APP_ERROR_INTERNAL));
499 }
500
TEST_F(TransportTest,SuccessWithReply)501 TEST_F(TransportTest, SuccessWithReply) {
502 const uint8_t app_id = 165;
503 const uint16_t param = 16;
504 const uint8_t data[] = {5, 6, 7, 8};
505 uint8_t reply[4];
506 uint32_t reply_len = 4;
507
508 InSequence please;
509 EXPECT_GET_STATUS_IDLE(app_id);
510 EXPECT_SEND_DATA(app_id, nullptr, 0);
511 EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
512 EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data, sizeof(data));
513 EXPECT_RECV_DATA(app_id, reply_len, data, sizeof(data));
514 EXPECT_CLEAR_STATUS(app_id);
515
516 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len);
517 EXPECT_THAT(res, Eq(APP_SUCCESS));
518 EXPECT_THAT(reply_len, Eq(4));
519 EXPECT_THAT(reply, ElementsAreArray(data, sizeof(data)));
520 }
521
TEST_F(TransportTest,SuccessWithReplyInMultipleDatagrams)522 TEST_F(TransportTest, SuccessWithReplyInMultipleDatagrams) {
523 const uint8_t app_id = 165;
524 const uint16_t param = 16;
525 std::vector<uint8_t> data(MAX_DEVICE_TRANSFER + 24, 0xea);
526 std::vector<uint8_t> reply(data.size());
527 uint32_t reply_len = reply.size();
528
529 InSequence please;
530 EXPECT_GET_STATUS_IDLE(app_id);
531 EXPECT_SEND_DATA(app_id, nullptr, 0);
532 EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
533 EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data.data(), data.size());
534 EXPECT_RECV_DATA(app_id, reply_len, data.data(), MAX_DEVICE_TRANSFER);
535 EXPECT_RECV_MORE_DATA(app_id, 24, data.data() + MAX_DEVICE_TRANSFER, 24);
536 EXPECT_CLEAR_STATUS(app_id);
537
538 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply.data(), &reply_len);
539 EXPECT_THAT(res, Eq(APP_SUCCESS));
540 EXPECT_THAT(reply_len, Eq(MAX_DEVICE_TRANSFER + 24));
541 EXPECT_THAT(reply, ElementsAreArray(data));
542 }
543
TEST_F(TransportTest,ReplyCrcError)544 TEST_F(TransportTest, ReplyCrcError) {
545 const uint8_t app_id = 5;
546 const uint16_t param = 0;
547 const uint8_t data[] = {1, 1, 2, 3, 5, 7};
548 const uint8_t wrong_data[] = {3, 1, 2, 3, 5, 7};
549 uint8_t reply[6];
550 uint32_t reply_len = 6;
551
552 InSequence please;
553 EXPECT_GET_STATUS_IDLE(app_id);
554 EXPECT_SEND_DATA(app_id, nullptr, 0);
555 EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
556 EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data, sizeof(data));
557 // Try 3 times to read data
558 EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
559 EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
560 EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
561
562 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len);
563 EXPECT_THAT(res, Eq(APP_ERROR_IO));
564 }
565
TEST_F(TransportTest,SuccessAfterReplyCrcError)566 TEST_F(TransportTest, SuccessAfterReplyCrcError) {
567 const uint8_t app_id = 5;
568 const uint16_t param = 0;
569 const uint8_t data[] = {2, 4, 9, 16};
570 const uint8_t wrong_data[] = {2, 4, 9, 48};
571 uint8_t reply[4];
572 uint32_t reply_len = 4;
573
574 InSequence please;
575 EXPECT_GET_STATUS_IDLE(app_id);
576 EXPECT_SEND_DATA(app_id, nullptr, 0);
577 EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
578 EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data, sizeof(data));
579 // Retry due to crc error
580 EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
581 EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
582 EXPECT_RECV_DATA(app_id, reply_len, data, sizeof(data));
583 EXPECT_CLEAR_STATUS(app_id);
584
585 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len);
586 EXPECT_THAT(res, Eq(APP_SUCCESS));
587 EXPECT_THAT(reply_len, Eq(4));
588 EXPECT_THAT(reply, ElementsAreArray(data, sizeof(data)));
589 }
590
TEST_F(TransportTest,V0SuccessWithoutReply)591 TEST_F(TransportTest, V0SuccessWithoutReply) {
592 const uint8_t app_id = 6;
593 const uint16_t param = 92;
594
595 InSequence please;
596 EXPECT_GET_STATUS_V0_IDLE(app_id);
597 EXPECT_SEND_DATA(app_id, nullptr, 0);
598 EXPECT_GO_COMMAND(app_id, param, nullptr, 0, 0);
599 EXPECT_GET_STATUS_V0_DONE(app_id);
600 EXPECT_CLEAR_STATUS(app_id);
601
602 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr);
603 EXPECT_THAT(res, Eq(APP_SUCCESS));
604 }
605
TEST_F(TransportTest,V0SuccessWithReply)606 TEST_F(TransportTest, V0SuccessWithReply) {
607 const uint8_t app_id = 0;
608 const uint16_t param = 18;
609 const uint8_t data[] = {15, 20, 25, 30, 35, 40};
610 uint8_t reply[6];
611 uint32_t reply_len = 6;
612
613 InSequence please;
614 EXPECT_GET_STATUS_V0_IDLE(app_id);
615 EXPECT_SEND_DATA(app_id, nullptr, 0);
616 EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
617 EXPECT_GET_STATUS_V0_DONE_WITH_DATA(app_id, sizeof(data));
618 EXPECT_RECV_DATA(app_id, reply_len, data, sizeof(data));
619 EXPECT_CLEAR_STATUS(app_id);
620
621 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len);
622 EXPECT_THAT(res, Eq(APP_SUCCESS));
623 EXPECT_THAT(reply_len, Eq(6));
624 EXPECT_THAT(reply, ElementsAreArray(data, sizeof(data)));
625 }
626
TEST_F(TransportTest,ErrorIfArgsLenButNotArgs)627 TEST_F(TransportTest, ErrorIfArgsLenButNotArgs) {
628 uint8_t reply[] = {1, 2, 3};
629 uint32_t reply_len = 0;
630 uint32_t status = nos_call_application(dev(), 1, 2, nullptr, 5, reply, &reply_len);
631 EXPECT_THAT(status, Eq(APP_ERROR_IO));
632 }
633
main(int argc,char ** argv)634 int main(int argc, char** argv) {
635 ::testing::InitGoogleTest(&argc, argv);
636 return RUN_ALL_TESTS();
637 }
638
639 #ifdef TEST_TIMEOUT
TEST_F(TransportTest,Timeout)640 TEST_F(TransportTest, Timeout) {
641 const uint8_t app_id = 49;
642 const uint16_t param = 64;
643
644 InSequence please;
645 EXPECT_GET_STATUS_IDLE(app_id);
646 EXPECT_SEND_DATA(app_id, nullptr, 0);
647 EXPECT_GO_COMMAND(app_id, param, nullptr, 0, 0);
648
649 // Keep saying we're working on it
650 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT;
651 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH))
652 .WillRepeatedly(DoAll(ReadStatusV1_Working(), Return(0)));
653
654 // We'll still try and clean up
655 EXPECT_CLEAR_STATUS(app_id);
656
657 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr);
658 EXPECT_THAT(res, Eq(APP_ERROR_TIMEOUT));
659 }
660 #endif
661