| /* |
| * 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 <vector> |
| |
| #include <gmock/gmock.h> |
| |
| #include <application.h> |
| #include <nos/transport.h> |
| |
| #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<MockDevice>; |
| |
| // Forward calls onto the mock |
| int read_datagram(void* ctx, uint32_t command, uint8_t* buf, uint32_t len) { |
| return reinterpret_cast<CtxType*>(ctx)->Read(command, buf, len); |
| } |
| int write_datagram(void* ctx, uint32_t command, const uint8_t* buf, uint32_t len) { |
| return reinterpret_cast<CtxType*>(ctx)->Write(command, buf, len); |
| } |
| int wait_for_interrupt(void* ctx, int msecs) { |
| return reinterpret_cast<CtxType*>(ctx)->WaitForInterrupt(msecs); |
| } |
| int reset(void* ctx) { |
| return reinterpret_cast<CtxType*>(ctx)->Reset(); |
| } |
| void close_device(void* ctx) { |
| delete reinterpret_cast<CtxType*>(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<CtxType*>(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 = 0xf781; |
| 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<uint8_t> data(MAX_DEVICE_TRANSFER + 24, 0xea); |
| std::vector<uint8_t> 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 |