/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "crc16.h" using ::testing::_; using ::testing::Args; using ::testing::DoAll; using ::testing::Eq; using ::testing::ElementsAreArray; using ::testing::InSequence; using ::testing::IsNull; using ::testing::Return; using ::testing::SetArrayArgument; using ::testing::StrictMock; namespace { struct Device { virtual int Read(uint32_t command, uint8_t* buf, uint32_t len) = 0; virtual int Write(uint32_t command, const uint8_t* buf, uint32_t len) = 0; virtual int WaitForInterrupt(int msecs) = 0; virtual int Reset() = 0; }; struct MockDevice : public Device { MOCK_METHOD3(Read, int(uint32_t command, uint8_t* buf, uint32_t len)); MOCK_METHOD3(Write, int(uint32_t command, const uint8_t* buf, uint32_t len)); MOCK_METHOD1(WaitForInterrupt, int(int msecs)); MOCK_METHOD0(Reset, int()); }; // We want to closely examine the interactions with the device to make it a // strict mock using CtxType = StrictMock; // Forward calls onto the mock int read_datagram(void* ctx, uint32_t command, uint8_t* buf, uint32_t len) { return reinterpret_cast(ctx)->Read(command, buf, len); } int write_datagram(void* ctx, uint32_t command, const uint8_t* buf, uint32_t len) { return reinterpret_cast(ctx)->Write(command, buf, len); } int wait_for_interrupt(void* ctx, int msecs) { return reinterpret_cast(ctx)->WaitForInterrupt(msecs); } int reset(void* ctx) { return reinterpret_cast(ctx)->Reset(); } void close_device(void* ctx) { delete reinterpret_cast(ctx); } // Implement the datagram API that calls a mock. extern "C" { int nos_device_open(const char* device_name, struct nos_device* dev) { EXPECT_THAT(device_name, IsNull()); dev->ctx = new CtxType; dev->ops.read = read_datagram; dev->ops.write = write_datagram; dev->ops.wait_for_interrupt = wait_for_interrupt; dev->ops.reset = reset; dev->ops.close = close_device; return 0; } } // Test fixture that sets up the mocked device. struct TransportTest : public ::testing::Test { virtual void SetUp() override { nos_device_open(nullptr, &dev_); mock_dev_ = reinterpret_cast(dev_.ctx); } virtual void TearDown() override { dev_.ops.close(dev_.ctx); } nos_device* dev() { return &dev_; } CtxType& mock_dev() { return *mock_dev_; } private: nos_device dev_; CtxType* mock_dev_; }; uint16_t command_crc(uint32_t command, const uint8_t* args, uint16_t args_len, const transport_command_info* command_info) { uint16_t crc = crc16(&args_len, sizeof(args_len)); crc = crc16_update(args, args_len, crc); crc = crc16_update(&command, sizeof(command), crc); crc = crc16_update(command_info, sizeof(*command_info), crc); return htole16(crc); } } // namespace /* Actions to return mock data */ #define READ_UNSET 0xdf ACTION(ReadStatusV0_Idle) { transport_status* status = (transport_status*)arg1; memset(status, READ_UNSET, sizeof(*status)); status->status = APP_STATUS_IDLE; status->reply_len = 0; } ACTION(ReadStatusV1_Idle) { transport_status* status = (transport_status*)arg1; memset(status, READ_UNSET, sizeof(*status)); status->status = APP_STATUS_IDLE; status->reply_len = 0; status->length = sizeof(transport_status); status->version = TRANSPORT_V1; status->flags = 0; status->crc = STATUS_CRC_FOR_IDLE; status->reply_crc = 0; } ACTION(ReadStatusV1_IdleWithBadCrc) { transport_status* status = (transport_status*)arg1; memset(status, READ_UNSET, sizeof(*status)); status->status = APP_STATUS_IDLE; status->reply_len = 0; status->length = sizeof(transport_status); status->version = TRANSPORT_V1; status->flags = 0; status->crc = STATUS_CRC_FOR_IDLE + 1; // <- wrong! status->reply_crc = 0; } ACTION(ReadStatusV1_Working) { transport_status* status = (transport_status*)arg1; memset(status, READ_UNSET, sizeof(*status)); status->status = APP_STATUS_IDLE; status->reply_len = 0; status->length = sizeof(transport_status); status->version = TRANSPORT_V1; status->flags = STATUS_FLAG_WORKING; status->crc = STATUS_CRC_FOR_WORKING; status->reply_crc = 0; } ACTION_P(ReadStatusV0_DoneWithData, reply_len) { transport_status* status = (transport_status*)arg1; memset(status, READ_UNSET, sizeof(*status)); status->status = APP_STATUS_DONE | APP_SUCCESS; status->reply_len = reply_len; } ACTION_P2(ReadStatusV1_DoneWithData, reply, reply_len) { transport_status* status = (transport_status*)arg1; memset(status, READ_UNSET, sizeof(*status)); status->status = APP_STATUS_DONE | APP_SUCCESS; status->reply_len = reply_len; status->length = sizeof(transport_status); status->version = TRANSPORT_V1; status->flags = 0; status->reply_crc = crc16(reply, reply_len); status->crc = 0; status->crc = crc16(status, status->length); } ACTION(ReadStatusV1_BadCrc) { transport_status* status = (transport_status*)arg1; memset(status, READ_UNSET, sizeof(*status)); status->status = APP_STATUS_DONE | APP_ERROR_CHECKSUM; status->reply_len = 0; status->length = sizeof(transport_status); status->version = TRANSPORT_V1; status->flags = 0; status->crc = 0x92c0; status->reply_crc = 0; } ACTION(ReadStatusV42_Working) { memset(arg1, 0xb3, STATUS_MAX_LENGTH); transport_status* status = (transport_status*)arg1; status->status = APP_STATUS_IDLE; status->reply_len = 0; status->length = STATUS_MAX_LENGTH; status->version = 42; status->flags = STATUS_FLAG_WORKING; status->crc = 0xaec0; status->reply_crc = 0; } ACTION_P3(ReadData, len, data, size) { memset(arg1, READ_UNSET, len); memcpy(arg1, data, size); } /* Helper macros to expect datagram calls */ #define EXPECT_GET_STATUS_V0_IDLE(app_id) do { \ const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \ EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \ .WillOnce(DoAll(ReadStatusV0_Idle(), Return(0))); \ } while (0) #define EXPECT_GET_STATUS_IDLE(app_id) do { \ const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \ EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \ .WillOnce(DoAll(ReadStatusV1_Idle(), Return(0))); \ } while (0) #define EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id) do { \ const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \ EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \ .WillOnce(DoAll(ReadStatusV1_IdleWithBadCrc(), Return(0))); \ } while (0) #define EXPECT_GET_STATUS_BAD_CRC(app_id) do { \ const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \ EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \ .WillOnce(DoAll(ReadStatusV1_BadCrc(), Return(0))); \ } while (0) #define EXPECT_GET_STATUS_WORKING(app_id) do { \ const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \ EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \ .WillOnce(DoAll(ReadStatusV1_Working(), Return(0))); \ } while (0) #define EXPECT_GET_STATUS_V0_DONE(app_id) do { \ const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \ EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \ .WillOnce(DoAll(ReadStatusV0_DoneWithData(0), Return(0))); \ } while (0) #define EXPECT_GET_STATUS_V0_DONE_WITH_DATA(app_id, reply_len) do { \ const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \ EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \ .WillOnce(DoAll(ReadStatusV0_DoneWithData((reply_len)), Return(0))); \ } while (0) #define EXPECT_GET_STATUS_DONE(app_id) do { \ const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \ EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \ .WillOnce(DoAll(ReadStatusV1_DoneWithData(nullptr, 0), Return(0))); \ } while (0) #define EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, reply, reply_len) do { \ const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \ EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \ .WillOnce(DoAll(ReadStatusV1_DoneWithData((reply), (reply_len)), Return(0))); \ } while (0) #define EXPECT_GET_STATUS_DONE_BAD_CRC(app_id, reply, reply_len) do { \ const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \ EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \ .WillOnce(DoAll(ReadStatusV1_DoneBadCrc((reply), (reply_len)), Return(0))); \ } while (0) #define EXPECT_SEND_DATA(app_id, args, args_len) do { \ const uint32_t command = CMD_ID((app_id)) | CMD_IS_DATA | CMD_TRANSPORT | CMD_PARAM((args_len)); \ EXPECT_CALL(mock_dev(), Write(command, _, (args_len))) \ .With(Args<1,2>(ElementsAreArray((uint8_t*)(args), (args_len)))) \ .WillOnce(Return(0)); \ } while (0) #define EXPECT_GO_COMMAND(app_id, param, args, args_len, reply_len) do { \ const uint32_t command = CMD_ID((app_id)) | CMD_PARAM((param)); \ transport_command_info command_info = {}; \ command_info.length = sizeof(command_info); \ command_info.version = htole16(TRANSPORT_V1); \ command_info.reply_len_hint = htole16((reply_len)); \ command_info.crc = command_crc(command, (args), (args_len), &command_info); \ EXPECT_CALL(mock_dev(), Write(command, _, command_info.length)) \ .With(Args<1,2>(ElementsAreArray((uint8_t*)&command_info, command_info.length))) \ .WillOnce(Return(0)); \ } while (0) #define EXPECT_RECV_DATA(app_id, len, reply, reply_len) do { \ const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_IS_DATA | CMD_TRANSPORT; \ EXPECT_CALL(mock_dev(), Read(command, _, (reply_len))) \ .WillOnce(DoAll(ReadData((len), (reply), (reply_len)), Return(0))); \ } while (0) #define EXPECT_RECV_MORE_DATA(app_id, len, reply, reply_len) do { \ const uint32_t command = \ CMD_ID((app_id)) | CMD_IS_READ | CMD_IS_DATA | CMD_MORE_TO_COME | CMD_TRANSPORT; \ EXPECT_CALL(mock_dev(), Read(command, _, (reply_len))) \ .WillOnce(DoAll(ReadData((len), (reply), (reply_len)), Return(0))); \ } while (0) #define EXPECT_CLEAR_STATUS(app_id) do { \ const uint32_t command = CMD_ID((app_id)) | CMD_TRANSPORT; \ EXPECT_CALL(mock_dev(), Write(command, _, 0)) \ .WillOnce(Return(0)); \ } while (0) /* Protocol tests */ TEST_F(TransportTest, WorkingAppIsBusy) { const uint8_t app_id = 213; EXPECT_GET_STATUS_WORKING(app_id); const uint16_t param = 2; uint32_t reply_len = 0; uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, &reply_len); EXPECT_THAT(res, Eq(APP_ERROR_BUSY)); } TEST_F(TransportTest, WorkingIsForwardCompatible) { const uint8_t app_id = 25; const uint32_t command = CMD_ID(app_id) | CMD_IS_READ | CMD_TRANSPORT; EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) .WillOnce(DoAll(ReadStatusV42_Working(), Return(0))); const uint16_t param = 2; uint32_t reply_len = 0; uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, &reply_len); EXPECT_THAT(res, Eq(APP_ERROR_BUSY)); } TEST_F(TransportTest, SuccessIfStatusNotClear) { const uint8_t app_id = 12; const uint16_t param = 2; const uint8_t args[] = {1, 2, 3}; const uint16_t args_len = 3; InSequence please; EXPECT_GET_STATUS_BAD_CRC(app_id); // Try and reset EXPECT_CLEAR_STATUS(app_id); // Try again EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, args, args_len); EXPECT_GO_COMMAND(app_id, param, args, args_len, 0); EXPECT_GET_STATUS_WORKING(app_id); EXPECT_GET_STATUS_DONE(app_id); EXPECT_CLEAR_STATUS(app_id); uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr); EXPECT_THAT(res, Eq(APP_SUCCESS)); } TEST_F(TransportTest, StatusCrcError) { const uint8_t app_id = 53; const uint16_t param = 192; InSequence please; // Try 5 times EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id); EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id); EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id); EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id); EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id); uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr); EXPECT_THAT(res, Eq(APP_ERROR_IO)); } TEST_F(TransportTest, FailToClearStatus) { const uint8_t app_id = 12; const uint16_t param = 2; const uint8_t args[] = {1, 2, 3}; const uint16_t args_len = 3; InSequence please; EXPECT_GET_STATUS_BAD_CRC(app_id); // Try and reset EXPECT_CLEAR_STATUS(app_id); // No luck EXPECT_GET_STATUS_BAD_CRC(app_id); uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr); EXPECT_THAT(res, Eq(APP_ERROR_IO)); } TEST_F(TransportTest, FailToClearStatusAfterStatusCrcError) { const uint8_t app_id = 53; const uint16_t param = 192; InSequence please; // Try 5 times EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id); EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id); EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id); EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id); EXPECT_GET_STATUS_BAD_CRC(app_id); // Try and reset EXPECT_CLEAR_STATUS(app_id); // No luck EXPECT_GET_STATUS_BAD_CRC(app_id); uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr); EXPECT_THAT(res, Eq(APP_ERROR_IO)); } TEST_F(TransportTest, RequestCrcError) { const uint8_t app_id = 58; const uint16_t param = 93; const uint8_t args[] = {4, 24, 183, 255, 219}; const uint16_t args_len = 5; InSequence please; // Should try 5 times EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, args, args_len); EXPECT_GO_COMMAND(app_id, param, args, args_len, 0); EXPECT_GET_STATUS_BAD_CRC(app_id); // 4 more EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, args, args_len); EXPECT_GO_COMMAND(app_id, param, args, args_len, 0); EXPECT_GET_STATUS_BAD_CRC(app_id); // 3 more EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, args, args_len); EXPECT_GO_COMMAND(app_id, param, args, args_len, 0); EXPECT_GET_STATUS_BAD_CRC(app_id); // 2 more EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, args, args_len); EXPECT_GO_COMMAND(app_id, param, args, args_len, 0); EXPECT_GET_STATUS_BAD_CRC(app_id); // last one EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, args, args_len); EXPECT_GO_COMMAND(app_id, param, args, args_len, 0); EXPECT_GET_STATUS_BAD_CRC(app_id); // Clean up EXPECT_CLEAR_STATUS(app_id); uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr); EXPECT_THAT(res, Eq(APP_ERROR_IO)); } TEST_F(TransportTest, SuccessAfterRequestCrcError) { const uint8_t app_id = 255; const uint16_t param = 163; const uint8_t args[] = {42, 89, 125, 0, 83, 92, 80}; const uint16_t args_len = 7; InSequence please; // First request is CRC error EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, args, args_len); EXPECT_GO_COMMAND(app_id, param, args, args_len, 0); EXPECT_GET_STATUS_BAD_CRC(app_id); // The retry succeeds EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, args, args_len); EXPECT_GO_COMMAND(app_id, param, args, args_len, 0); EXPECT_GET_STATUS_WORKING(app_id); EXPECT_GET_STATUS_DONE(app_id); EXPECT_CLEAR_STATUS(app_id); uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr); EXPECT_THAT(res, Eq(APP_SUCCESS)); } TEST_F(TransportTest, SuccessWithoutReply) { const uint8_t app_id = 12; const uint16_t param = 2; const uint8_t args[] = {1, 2, 3}; const uint16_t args_len = 3; InSequence please; EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, args, args_len); EXPECT_GO_COMMAND(app_id, param, args, args_len, 0); EXPECT_GET_STATUS_WORKING(app_id); EXPECT_GET_STATUS_DONE(app_id); EXPECT_CLEAR_STATUS(app_id); uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr); EXPECT_THAT(res, Eq(APP_SUCCESS)); } TEST_F(TransportTest, DetectAppAbort) { const uint8_t app_id = 25; const uint16_t param = 252; const uint8_t args[] = {17, 27, 43, 193}; const uint16_t args_len = 4; InSequence please; EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, args, args_len); EXPECT_GO_COMMAND(app_id, param, args, args_len, 0); EXPECT_GET_STATUS_WORKING(app_id); EXPECT_GET_STATUS_WORKING(app_id); EXPECT_GET_STATUS_WORKING(app_id); // It just stopped working EXPECT_GET_STATUS_IDLE(app_id); // It's probably already clear but just making sure EXPECT_CLEAR_STATUS(app_id); uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr); EXPECT_THAT(res, Eq(APP_ERROR_INTERNAL)); } TEST_F(TransportTest, SuccessWithReply) { const uint8_t app_id = 165; const uint16_t param = 16; const uint8_t data[] = {5, 6, 7, 8}; uint8_t reply[4]; uint32_t reply_len = 4; InSequence please; EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, nullptr, 0); EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len); EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data, sizeof(data)); EXPECT_RECV_DATA(app_id, reply_len, data, sizeof(data)); EXPECT_CLEAR_STATUS(app_id); uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len); EXPECT_THAT(res, Eq(APP_SUCCESS)); EXPECT_THAT(reply_len, Eq(4)); EXPECT_THAT(reply, ElementsAreArray(data, sizeof(data))); } TEST_F(TransportTest, SuccessWithReplyInMultipleDatagrams) { const uint8_t app_id = 165; const uint16_t param = 16; std::vector data(MAX_DEVICE_TRANSFER + 24, 0xea); std::vector reply(data.size()); uint32_t reply_len = reply.size(); InSequence please; EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, nullptr, 0); EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len); EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data.data(), data.size()); EXPECT_RECV_DATA(app_id, reply_len, data.data(), MAX_DEVICE_TRANSFER); EXPECT_RECV_MORE_DATA(app_id, 24, data.data() + MAX_DEVICE_TRANSFER, 24); EXPECT_CLEAR_STATUS(app_id); uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply.data(), &reply_len); EXPECT_THAT(res, Eq(APP_SUCCESS)); EXPECT_THAT(reply_len, Eq(MAX_DEVICE_TRANSFER + 24)); EXPECT_THAT(reply, ElementsAreArray(data)); } TEST_F(TransportTest, ReplyCrcError) { const uint8_t app_id = 5; const uint16_t param = 0; const uint8_t data[] = {1, 1, 2, 3, 5, 7}; const uint8_t wrong_data[] = {3, 1, 2, 3, 5, 7}; uint8_t reply[6]; uint32_t reply_len = 6; InSequence please; EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, nullptr, 0); EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len); EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data, sizeof(data)); // Try 5 times to read data EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data)); EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data)); EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data)); EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data)); EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data)); uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len); EXPECT_THAT(res, Eq(APP_ERROR_IO)); } TEST_F(TransportTest, SuccessAfterReplyCrcError) { const uint8_t app_id = 5; const uint16_t param = 0; const uint8_t data[] = {2, 4, 9, 16}; const uint8_t wrong_data[] = {2, 4, 9, 48}; uint8_t reply[4]; uint32_t reply_len = 4; InSequence please; EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, nullptr, 0); EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len); EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data, sizeof(data)); // Retry due to crc error EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data)); EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data)); EXPECT_RECV_DATA(app_id, reply_len, data, sizeof(data)); EXPECT_CLEAR_STATUS(app_id); uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len); EXPECT_THAT(res, Eq(APP_SUCCESS)); EXPECT_THAT(reply_len, Eq(4)); EXPECT_THAT(reply, ElementsAreArray(data, sizeof(data))); } TEST_F(TransportTest, V0SuccessWithoutReply) { const uint8_t app_id = 6; const uint16_t param = 92; InSequence please; EXPECT_GET_STATUS_V0_IDLE(app_id); EXPECT_SEND_DATA(app_id, nullptr, 0); EXPECT_GO_COMMAND(app_id, param, nullptr, 0, 0); EXPECT_GET_STATUS_V0_DONE(app_id); EXPECT_CLEAR_STATUS(app_id); uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr); EXPECT_THAT(res, Eq(APP_SUCCESS)); } TEST_F(TransportTest, V0SuccessWithReply) { const uint8_t app_id = 0; const uint16_t param = 18; const uint8_t data[] = {15, 20, 25, 30, 35, 40}; uint8_t reply[6]; uint32_t reply_len = 6; InSequence please; EXPECT_GET_STATUS_V0_IDLE(app_id); EXPECT_SEND_DATA(app_id, nullptr, 0); EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len); EXPECT_GET_STATUS_V0_DONE_WITH_DATA(app_id, sizeof(data)); EXPECT_RECV_DATA(app_id, reply_len, data, sizeof(data)); EXPECT_CLEAR_STATUS(app_id); uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len); EXPECT_THAT(res, Eq(APP_SUCCESS)); EXPECT_THAT(reply_len, Eq(6)); EXPECT_THAT(reply, ElementsAreArray(data, sizeof(data))); } TEST_F(TransportTest, ErrorIfArgsLenButNotArgs) { uint8_t reply[] = {1, 2, 3}; uint32_t reply_len = 0; uint32_t status = nos_call_application(dev(), 1, 2, nullptr, 5, reply, &reply_len); EXPECT_THAT(status, Eq(APP_ERROR_IO)); } int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } #ifdef TEST_TIMEOUT TEST_F(TransportTest, Timeout) { const uint8_t app_id = 49; const uint16_t param = 64; InSequence please; EXPECT_GET_STATUS_IDLE(app_id); EXPECT_SEND_DATA(app_id, nullptr, 0); EXPECT_GO_COMMAND(app_id, param, nullptr, 0, 0); // Keep saying we're working on it const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) .WillRepeatedly(DoAll(ReadStatusV1_Working(), Return(0))); // We'll still try and clean up EXPECT_CLEAR_STATUS(app_id); uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr); EXPECT_THAT(res, Eq(APP_ERROR_TIMEOUT)); } #endif