/// @brief Tests for correct reponse to an Open command on a file, followed by Terminate bool MavlinkFtpTest::_open_terminate_test(void) { MavlinkFTP::PayloadHeader payload; mavlink_file_transfer_protocol_t ftp_msg; MavlinkFTP::PayloadHeader *reply; for (size_t i=0; i<sizeof(_rgReadTestCases)/sizeof(_rgReadTestCases[0]); i++) { struct stat st; const ReadTestCase *test = &_rgReadTestCases[i]; payload.opcode = MavlinkFTP::kCmdOpenFileRO; payload.offset = 0; bool success = _send_receive_msg(&payload, // FTP payload header strlen(test->file)+1, // size in bytes of data (uint8_t*)test->file, // Data to start into FTP message payload &ftp_msg, // Response from server &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("stat failed", stat(test->file, &st), 0); ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck); ut_compare("Incorrect payload size", reply->size, sizeof(uint32_t)); ut_compare("File size incorrect", *((uint32_t*)&reply->data[0]), (uint32_t)st.st_size); payload.opcode = MavlinkFTP::kCmdTerminateSession; payload.session = reply->session; payload.size = 0; success = _send_receive_msg(&payload, // FTP payload header 0, // size in bytes of data nullptr, // Data to start into FTP message payload &ftp_msg, // Response from server &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck); ut_compare("Incorrect payload size", reply->size, 0); } return true; }
/// @brief Tests for correct reponse to a List command on a valid directory, but with an offset that /// is beyond the last directory entry. bool MavlinkFtpTest::_list_eof_test(void) { MavlinkFTP::PayloadHeader payload; mavlink_file_transfer_protocol_t ftp_msg; MavlinkFTP::PayloadHeader *reply; const char *dir = "/"; payload.opcode = MavlinkFTP::kCmdListDirectory; payload.offset = 4; // offset past top level dirs bool success = _send_receive_msg(&payload, // FTP payload header strlen(dir)+1, // size in bytes of data (uint8_t*)dir, // Data to start into FTP message payload &ftp_msg, // Response from server &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak); ut_compare("Incorrect payload size", reply->size, 1); ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrEOF); return true; }
/// @brief Tests for correct reponse to an Open command on a file which does not exist. bool MavlinkFtpTest::_open_badfile_test(void) { MavlinkFTP::PayloadHeader payload; mavlink_file_transfer_protocol_t ftp_msg; MavlinkFTP::PayloadHeader *reply; const char *dir = "/foo"; // non-existent file payload.opcode = MavlinkFTP::kCmdOpenFileRO; payload.offset = 0; bool success = _send_receive_msg(&payload, // FTP payload header strlen(dir)+1, // size in bytes of data (uint8_t*)dir, // Data to start into FTP message payload &ftp_msg, // Response from server &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak); ut_compare("Incorrect payload size", reply->size, 2); ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrFailErrno); return true; }
bool MavlinkFtpTest::_removedirectory_test(void) { MavlinkFTP::PayloadHeader payload; mavlink_file_transfer_protocol_t ftp_msg; MavlinkFTP::PayloadHeader *reply; int fd; struct _testCase { const char *dir; bool success; bool deleteFile; }; static const struct _testCase rgTestCases[] = { { "/bogus", false, false }, { "/etc/unit_test_data/mavlink_tests/empty_dir", false, false }, { _unittest_microsd_dir, false, false }, { _unittest_microsd_file, false, false }, { _unittest_microsd_dir, true, true }, }; ut_compare("mkdir failed", ::mkdir(_unittest_microsd_dir, S_IRWXU | S_IRWXG | S_IRWXO), 0); ut_assert("open failed", (fd = ::open(_unittest_microsd_file, O_CREAT | O_EXCL)) != -1); ::close(fd); for (size_t i=0; i<sizeof(rgTestCases)/sizeof(rgTestCases[0]); i++) { const struct _testCase *test = &rgTestCases[i]; if (test->deleteFile) { ut_compare("unlink failed", ::unlink(_unittest_microsd_file), 0); } payload.opcode = MavlinkFTP::kCmdRemoveDirectory; payload.offset = 0; bool success = _send_receive_msg(&payload, // FTP payload header strlen(test->dir)+1, // size in bytes of data (uint8_t*)test->dir, // Data to start into FTP message payload &ftp_msg, // Response from server &reply); // Payload inside FTP message response if (!success) { return false; } if (test->success) { ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck); ut_compare("Incorrect payload size", reply->size, 0); } else { ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak); ut_compare("Incorrect payload size", reply->size, 2); ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrFailErrno); } } return true; }
bool MavlinkFtpTest::_removefile_test() { MavlinkFTP::PayloadHeader payload; const MavlinkFTP::PayloadHeader *reply; int fd; struct _testCase { const char *file; bool success; }; static const struct _testCase rgTestCases[] = { { "/bogus", false }, #ifdef __PX4_NUTTX // file can actually be deleted on linux { _rgDownloadTestCases[0].file, false }, #endif /* __PX4_NUTTX */ { _unittest_microsd_dir, false }, { _unittest_microsd_file, true }, { _unittest_microsd_file, false }, }; ut_compare("mkdir failed", ::mkdir(_unittest_microsd_dir, S_IRWXU | S_IRWXG | S_IRWXO), 0); ut_assert("open failed", (fd = ::open(_unittest_microsd_file, O_CREAT | O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO)) != -1); ::close(fd); for (size_t i = 0; i < sizeof(rgTestCases) / sizeof(rgTestCases[0]); i++) { const struct _testCase *test = &rgTestCases[i]; payload.opcode = MavlinkFTP::kCmdRemoveFile; payload.offset = 0; bool success = _send_receive_msg(&payload, // FTP payload header strlen(test->file) + 1, // size in bytes of data (uint8_t *)test->file, // Data to start into FTP message payload &reply); // Payload inside FTP message response if (!success) { return false; } if (test->success) { ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck); ut_compare("Incorrect payload size", reply->size, 0); } else { ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak); ut_compare("Incorrect payload size", reply->size, 2); ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrFailErrno); } } return true; }
/// @brief Tests for correct reponse to a Read command on an invalid session. bool MavlinkFtpTest::_read_badsession_test(void) { MavlinkFTP::PayloadHeader payload; mavlink_file_transfer_protocol_t ftp_msg; MavlinkFTP::PayloadHeader *reply; const char *file = _rgReadTestCases[0].file; payload.opcode = MavlinkFTP::kCmdOpenFileRO; payload.offset = 0; bool success = _send_receive_msg(&payload, // FTP payload header strlen(file)+1, // size in bytes of data (uint8_t*)file, // Data to start into FTP message payload &ftp_msg, // Response from server &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck); payload.opcode = MavlinkFTP::kCmdReadFile; payload.session = reply->session + 1; // Invalid session payload.offset = 0; success = _send_receive_msg(&payload, // FTP payload header 0, // size in bytes of data nullptr, // Data to start into FTP message payload &ftp_msg, // Response from server &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak); ut_compare("Incorrect payload size", reply->size, 1); ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrInvalidSession); return true; }
/// @brief Tests for correct reponse to a Terminate command on an invalid session. bool MavlinkFtpTest::_terminate_badsession_test() { MavlinkFTP::PayloadHeader payload; const MavlinkFTP::PayloadHeader *reply; const char *file = _rgDownloadTestCases[0].file; payload.opcode = MavlinkFTP::kCmdOpenFileRO; payload.offset = 0; bool success = _send_receive_msg(&payload, // FTP payload header strlen(file) + 1, // size in bytes of data (uint8_t *)file, // Data to start into FTP message payload &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck); payload.opcode = MavlinkFTP::kCmdTerminateSession; payload.session = reply->session + 1; payload.size = 0; success = _send_receive_msg(&payload, // FTP payload header 0, // size in bytes of data nullptr, // Data to start into FTP message payload &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak); ut_compare("Incorrect payload size", reply->size, 1); ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrInvalidSession); return true; }
bool MavlinkFtpTest::_createdirectory_test() { MavlinkFTP::PayloadHeader payload; const MavlinkFTP::PayloadHeader *reply; struct _testCase { const char *dir; bool success; bool fail_exists; }; static const struct _testCase rgTestCases[] = { { "/etc/bogus", false, false }, { _unittest_microsd_dir, true, false }, { _unittest_microsd_dir, false, true }, { "/fs/microsd/bogus/bogus", false, false }, }; for (size_t i = 0; i < sizeof(rgTestCases) / sizeof(rgTestCases[0]); i++) { const struct _testCase *test = &rgTestCases[i]; payload.opcode = MavlinkFTP::kCmdCreateDirectory; payload.offset = 0; bool success = _send_receive_msg(&payload, // FTP payload header strlen(test->dir) + 1, // size in bytes of data (uint8_t *)test->dir, // Data to start into FTP message payload &reply); // Payload inside FTP message response if (!success) { return false; } if (test->success) { ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck); ut_compare("Incorrect payload size", reply->size, 0); } else if (test->fail_exists) { ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak); ut_compare("Incorrect payload size", reply->size, 1); ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrFailFileExists); } else { ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak); ut_compare("Incorrect payload size", reply->size, 2); ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrFailErrno); } } return true; }
/// @brief Tests for correct behavior of an Ack response. bool MavlinkFtpTest::_ack_test() { MavlinkFTP::PayloadHeader payload; const MavlinkFTP::PayloadHeader *reply; payload.opcode = MavlinkFTP::kCmdNone; bool success = _send_receive_msg(&payload, // FTP payload header 0, // size in bytes of data nullptr, // Data to start into FTP message payload &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck); ut_compare("Incorrect payload size", reply->size, 0); return true; }
/// @brief Tests for correct response to an invalid opcpde. bool MavlinkFtpTest::_bad_opcode_test() { MavlinkFTP::PayloadHeader payload; const MavlinkFTP::PayloadHeader *reply; payload.opcode = 0xFF; // bogus opcode bool success = _send_receive_msg(&payload, // FTP payload header 0, // size in bytes of data nullptr, // Data to start into FTP message payload &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak); ut_compare("Incorrect payload size", reply->size, 1); ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrUnknownCommand); return true; }
/// @brief Tests for correct reponse to a Read command on an open session. bool MavlinkFtpTest::_read_test(void) { MavlinkFTP::PayloadHeader payload; mavlink_file_transfer_protocol_t ftp_msg; MavlinkFTP::PayloadHeader *reply; for (size_t i=0; i<sizeof(_rgReadTestCases)/sizeof(_rgReadTestCases[0]); i++) { struct stat st; const ReadTestCase *test = &_rgReadTestCases[i]; // Read in the file so we can compare it to what we get back ut_compare("stat failed", stat(test->file, &st), 0); uint8_t *bytes = new uint8_t[st.st_size]; ut_assert("new failed", bytes != nullptr); int fd = ::open(test->file, O_RDONLY); ut_assert("open failed", fd != -1); int bytes_read = ::read(fd, bytes, st.st_size); ut_compare("read failed", bytes_read, st.st_size); ::close(fd); // Test case data files are created for specific boundary conditions ut_compare("Test case data files are out of date", test->length, st.st_size); payload.opcode = MavlinkFTP::kCmdOpenFileRO; payload.offset = 0; bool success = _send_receive_msg(&payload, // FTP payload header strlen(test->file)+1, // size in bytes of data (uint8_t*)test->file, // Data to start into FTP message payload &ftp_msg, // Response from server &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck); payload.opcode = MavlinkFTP::kCmdReadFile; payload.session = reply->session; payload.offset = 0; success = _send_receive_msg(&payload, // FTP payload header 0, // size in bytes of data nullptr, // Data to start into FTP message payload &ftp_msg, // Response from server &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck); ut_compare("Offset incorrect", reply->offset, 0); if (test->length <= MAVLINK_MSG_FILE_TRANSFER_PROTOCOL_FIELD_PAYLOAD_LEN - sizeof(MavlinkFTP::PayloadHeader)) { ut_compare("Payload size incorrect", reply->size, (uint32_t)st.st_size); ut_compare("File contents differ", memcmp(reply->data, bytes, st.st_size), 0); } payload.opcode = MavlinkFTP::kCmdTerminateSession; payload.session = reply->session; payload.size = 0; success = _send_receive_msg(&payload, // FTP payload header 0, // size in bytes of data nullptr, // Data to start into FTP message payload &ftp_msg, // Response from server &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck); ut_compare("Incorrect payload size", reply->size, 0); } return true; }
bool MavlinkFtpTest::_list_test(void) { MavlinkFTP::PayloadHeader payload; mavlink_file_transfer_protocol_t ftp_msg; MavlinkFTP::PayloadHeader *reply; char response1[] = "Dempty_dir|Ftest_238.data\t238|Ftest_239.data\t239|Ftest_240.data\t240"; char response2[] = "Ddev|Detc|Dfs|Dobj"; struct _testCase { const char *dir; ///< Directory to run List command on char *response; ///< Expected response entries from List command int response_count; ///< Number of directories that should be returned bool success; ///< true: List command should succeed, false: List command should fail }; struct _testCase rgTestCases[] = { { "/bogus", nullptr, 0, false }, { "/etc/unit_test_data/mavlink_tests", response1, 4, true }, { "/", response2, 4, true }, }; for (size_t i=0; i<sizeof(rgTestCases)/sizeof(rgTestCases[0]); i++) { const struct _testCase *test = &rgTestCases[i]; payload.opcode = MavlinkFTP::kCmdListDirectory; payload.offset = 0; bool success = _send_receive_msg(&payload, // FTP payload header strlen(test->dir)+1, // size in bytes of data (uint8_t*)test->dir, // Data to start into FTP message payload &ftp_msg, // Response from server &reply); // Payload inside FTP message response if (!success) { return false; } if (test->success) { ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck); ut_compare("Incorrect payload size", reply->size, strlen(test->response) + 1); // The return order of directories from the List command is not repeatable. So we can't do a direct comparison // to a hardcoded return result string. // Convert null terminators to seperator char so we can use strok to parse returned data for (uint8_t j=0; j<reply->size-1; j++) { if (reply->data[j] == 0) { reply->data[j] = '|'; } } // Loop over returned directory entries trying to find then in the response list char *dir; int response_count = 0; dir = strtok((char *)&reply->data[0], "|"); while (dir != nullptr) { ut_assert("Returned directory not found in expected response", strstr(test->response, dir)); response_count++; dir = strtok(nullptr, "|"); } ut_compare("Incorrect number of directory entires returned", test->response_count, response_count); } else { ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak); ut_compare("Incorrect payload size", reply->size, 2); ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrFailErrno); } } return true; }
/// @brief Tests for correct reponse to a Read command on an open session. bool MavlinkFtpTest::_burst_test() { MavlinkFTP::PayloadHeader payload; const MavlinkFTP::PayloadHeader *reply; BurstInfo burst_info; for (size_t i = 0; i < sizeof(_rgDownloadTestCases) / sizeof(_rgDownloadTestCases[0]); i++) { struct stat st; const DownloadTestCase *test = &_rgDownloadTestCases[i]; // Read in the file so we can compare it to what we get back ut_compare("stat failed", stat(test->file, &st), 0); uint8_t *bytes = new uint8_t[st.st_size]; ut_assert("new failed", bytes != nullptr); int fd = ::open(test->file, O_RDONLY); ut_assert("open failed", fd != -1); int bytes_read = ::read(fd, bytes, st.st_size); ut_compare("read failed", bytes_read, st.st_size); ::close(fd); // Test case data files are created for specific boundary conditions ut_compare("Test case data files are out of date", test->length, st.st_size); payload.opcode = MavlinkFTP::kCmdOpenFileRO; payload.offset = 0; bool success = _send_receive_msg(&payload, // FTP payload header strlen(test->file) + 1, // size in bytes of data (uint8_t *)test->file, // Data to start into FTP message payload &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck); // Setup for burst response handler burst_info.burst_state = burst_state_first_ack; burst_info.single_packet_file = test->singlePacketRead; burst_info.file_size = st.st_size; burst_info.file_bytes = bytes; burst_info.ftp_test_class = this; _ftp_server->set_unittest_worker(MavlinkFtpTest::receive_message_handler_burst, &burst_info); // Send the burst command, message response will be handled by _receive_message_handler_stream payload.opcode = MavlinkFTP::kCmdBurstReadFile; payload.session = reply->session; payload.offset = 0; mavlink_message_t msg; _setup_ftp_msg(&payload, 0, nullptr, &msg); _ftp_server->handle_message(&msg); // First packet is sent using stream mechanism, so we need to force it out ourselves hrt_abstime t = 0; _ftp_server->send(t); ut_compare("Incorrect sequence of messages", burst_info.burst_state, burst_state_complete); // Put back generic message handler _ftp_server->set_unittest_worker(MavlinkFtpTest::receive_message_handler_generic, this); // Terminate session payload.opcode = MavlinkFTP::kCmdTerminateSession; payload.session = reply->session; payload.size = 0; success = _send_receive_msg(&payload, // FTP payload header 0, // size in bytes of data nullptr, // Data to start into FTP message payload &reply); // Payload inside FTP message response if (!success) { return false; } ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck); ut_compare("Incorrect payload size", reply->size, 0); } return true; }