Commit bc71d49d authored by Lorenz Meier's avatar Lorenz Meier

Merge pull request #1557 from DonLakeFlyer/FileManager

File Manager fixes and unit test
parents 886b40b5 f534aa7e
...@@ -236,6 +236,7 @@ HEADERS += \ ...@@ -236,6 +236,7 @@ HEADERS += \
src/comm/LinkManager.h \ src/comm/LinkManager.h \
src/comm/MAVLinkProtocol.h \ src/comm/MAVLinkProtocol.h \
src/comm/MockLink.h \ src/comm/MockLink.h \
src/comm/MockLinkFileServer.h \
src/comm/MockLinkMissionItemHandler.h \ src/comm/MockLinkMissionItemHandler.h \
src/comm/ProtocolInterface.h \ src/comm/ProtocolInterface.h \
src/comm/QGCFlightGearLink.h \ src/comm/QGCFlightGearLink.h \
...@@ -374,6 +375,7 @@ SOURCES += \ ...@@ -374,6 +375,7 @@ SOURCES += \
src/comm/LinkManager.cc \ src/comm/LinkManager.cc \
src/comm/MAVLinkProtocol.cc \ src/comm/MAVLinkProtocol.cc \
src/comm/MockLink.cc \ src/comm/MockLink.cc \
src/comm/MockLinkFileServer.cc \
src/comm/MockLinkMissionItemHandler.cc \ src/comm/MockLinkMissionItemHandler.cc \
src/comm/QGCFlightGearLink.cc \ src/comm/QGCFlightGearLink.cc \
src/comm/QGCJSBSimLink.cc \ src/comm/QGCJSBSimLink.cc \
...@@ -518,8 +520,6 @@ INCLUDEPATH += \ ...@@ -518,8 +520,6 @@ INCLUDEPATH += \
HEADERS += \ HEADERS += \
src/qgcunittest/FlightGearTest.h \ src/qgcunittest/FlightGearTest.h \
src/qgcunittest/MockMavlinkFileServer.h \
src/qgcunittest/MockMavlinkInterface.h \
src/qgcunittest/MultiSignalSpy.h \ src/qgcunittest/MultiSignalSpy.h \
src/qgcunittest/TCPLinkTest.h \ src/qgcunittest/TCPLinkTest.h \
src/qgcunittest/TCPLoopBackServer.h \ src/qgcunittest/TCPLoopBackServer.h \
...@@ -534,10 +534,10 @@ HEADERS += \ ...@@ -534,10 +534,10 @@ HEADERS += \
src/qgcunittest/PX4RCCalibrationTest.h \ src/qgcunittest/PX4RCCalibrationTest.h \
src/qgcunittest/UnitTest.h \ src/qgcunittest/UnitTest.h \
src/VehicleSetup/SetupViewTest.h \ src/VehicleSetup/SetupViewTest.h \
src/qgcunittest/FileManagerTest.h \
SOURCES += \ SOURCES += \
src/qgcunittest/FlightGearTest.cc \ src/qgcunittest/FlightGearTest.cc \
src/qgcunittest/MockMavlinkFileServer.cc \
src/qgcunittest/MultiSignalSpy.cc \ src/qgcunittest/MultiSignalSpy.cc \
src/qgcunittest/TCPLinkTest.cc \ src/qgcunittest/TCPLinkTest.cc \
src/qgcunittest/TCPLoopBackServer.cc \ src/qgcunittest/TCPLoopBackServer.cc \
...@@ -552,6 +552,7 @@ SOURCES += \ ...@@ -552,6 +552,7 @@ SOURCES += \
src/qgcunittest/PX4RCCalibrationTest.cc \ src/qgcunittest/PX4RCCalibrationTest.cc \
src/qgcunittest/UnitTest.cc \ src/qgcunittest/UnitTest.cc \
src/VehicleSetup/SetupViewTest.cc \ src/VehicleSetup/SetupViewTest.cc \
src/qgcunittest/FileManagerTest.cc \
} # DebugBuild|WindowsDebugAndRelease } # DebugBuild|WindowsDebugAndRelease
} # AndroidBuild } # AndroidBuild
......
...@@ -75,7 +75,8 @@ MockLink::MockLink(MockConfiguration* config) : ...@@ -75,7 +75,8 @@ MockLink::MockLink(MockConfiguration* config) :
_mavlinkStarted(false), _mavlinkStarted(false),
_mavBaseMode(MAV_MODE_FLAG_MANUAL_INPUT_ENABLED | MAV_MODE_FLAG_CUSTOM_MODE_ENABLED), _mavBaseMode(MAV_MODE_FLAG_MANUAL_INPUT_ENABLED | MAV_MODE_FLAG_CUSTOM_MODE_ENABLED),
_mavState(MAV_STATE_STANDBY), _mavState(MAV_STATE_STANDBY),
_autopilotType(MAV_AUTOPILOT_PX4) _autopilotType(MAV_AUTOPILOT_PX4),
_fileServer(NULL)
{ {
_config = config; _config = config;
union px4_custom_mode px4_cm; union px4_custom_mode px4_cm;
...@@ -84,6 +85,9 @@ MockLink::MockLink(MockConfiguration* config) : ...@@ -84,6 +85,9 @@ MockLink::MockLink(MockConfiguration* config) :
px4_cm.main_mode = PX4_CUSTOM_MAIN_MODE_MANUAL; px4_cm.main_mode = PX4_CUSTOM_MAIN_MODE_MANUAL;
_mavCustomMode = px4_cm.data; _mavCustomMode = px4_cm.data;
_fileServer = new MockLinkFileServer(_vehicleSystemId, _vehicleComponentId, this);
Q_CHECK_PTR(_fileServer);
_missionItemHandler = new MockLinkMissionItemHandler(_vehicleSystemId, this); _missionItemHandler = new MockLinkMissionItemHandler(_vehicleSystemId, this);
Q_CHECK_PTR(_missionItemHandler); Q_CHECK_PTR(_missionItemHandler);
...@@ -218,7 +222,6 @@ void MockLink::_loadParams(void) ...@@ -218,7 +222,6 @@ void MockLink::_loadParams(void)
void MockLink::_sendHeartBeat(void) void MockLink::_sendHeartBeat(void)
{ {
mavlink_message_t msg; mavlink_message_t msg;
uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
mavlink_msg_heartbeat_pack(_vehicleSystemId, mavlink_msg_heartbeat_pack(_vehicleSystemId,
_vehicleComponentId, _vehicleComponentId,
...@@ -228,7 +231,14 @@ void MockLink::_sendHeartBeat(void) ...@@ -228,7 +231,14 @@ void MockLink::_sendHeartBeat(void)
_mavBaseMode, // MAV_MODE _mavBaseMode, // MAV_MODE
_mavCustomMode, // custom mode _mavCustomMode, // custom mode
_mavState); // MAV_STATE _mavState); // MAV_STATE
respondWithMavlinkMessage(msg);
}
void MockLink::respondWithMavlinkMessage(const mavlink_message_t& msg)
{
uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
int cBuffer = mavlink_msg_to_send_buffer(buffer, &msg); int cBuffer = mavlink_msg_to_send_buffer(buffer, &msg);
QByteArray bytes((char *)buffer, cBuffer); QByteArray bytes((char *)buffer, cBuffer);
emit bytesReceived(this, bytes); emit bytesReceived(this, bytes);
...@@ -332,6 +342,10 @@ void MockLink::_handleIncomingMavlinkBytes(const uint8_t* bytes, int cBytes) ...@@ -332,6 +342,10 @@ void MockLink::_handleIncomingMavlinkBytes(const uint8_t* bytes, int cBytes)
_handleMissionCount(msg); _handleMissionCount(msg);
break; break;
#endif #endif
case MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL:
_handleFTP(msg);
break;
default: default:
qDebug() << "MockLink: Unhandled mavlink message, id:" << msg.msgid; qDebug() << "MockLink: Unhandled mavlink message, id:" << msg.msgid;
...@@ -340,15 +354,6 @@ void MockLink::_handleIncomingMavlinkBytes(const uint8_t* bytes, int cBytes) ...@@ -340,15 +354,6 @@ void MockLink::_handleIncomingMavlinkBytes(const uint8_t* bytes, int cBytes)
} }
} }
void MockLink::_emitMavlinkMessage(const mavlink_message_t& msg)
{
uint8_t outputBuffer[MAVLINK_MAX_PACKET_LEN];
int cBuffer = mavlink_msg_to_send_buffer(outputBuffer, &msg);
QByteArray bytes((char *)outputBuffer, cBuffer);
emit bytesReceived(this, bytes);
}
void MockLink::_handleHeartBeat(const mavlink_message_t& msg) void MockLink::_handleHeartBeat(const mavlink_message_t& msg)
{ {
Q_UNUSED(msg); Q_UNUSED(msg);
...@@ -494,7 +499,7 @@ void MockLink::_handleParamRequestList(const mavlink_message_t& msg) ...@@ -494,7 +499,7 @@ void MockLink::_handleParamRequestList(const mavlink_message_t& msg)
paramType, // MAV_PARAM_TYPE paramType, // MAV_PARAM_TYPE
cParameters, // Total number of parameters cParameters, // Total number of parameters
paramIndex++); // Index of this parameter paramIndex++); // Index of this parameter
_emitMavlinkMessage(responseMsg); respondWithMavlinkMessage(responseMsg);
// Only first parameter the first time through // Only first parameter the first time through
break; break;
...@@ -533,7 +538,7 @@ void MockLink::_handleParamRequestList(const mavlink_message_t& msg) ...@@ -533,7 +538,7 @@ void MockLink::_handleParamRequestList(const mavlink_message_t& msg)
paramType, // MAV_PARAM_TYPE paramType, // MAV_PARAM_TYPE
cParameters, // Total number of parameters cParameters, // Total number of parameters
paramIndex++); // Index of this parameter paramIndex++); // Index of this parameter
_emitMavlinkMessage(responseMsg); respondWithMavlinkMessage(responseMsg);
} }
} }
} }
...@@ -571,7 +576,7 @@ void MockLink::_handleParamSet(const mavlink_message_t& msg) ...@@ -571,7 +576,7 @@ void MockLink::_handleParamSet(const mavlink_message_t& msg)
request.param_type, // Send same type back request.param_type, // Send same type back
_mapParamName2Value[componentId].count(), // Total number of parameters _mapParamName2Value[componentId].count(), // Total number of parameters
_mapParamName2Value[componentId].keys().indexOf(paramId)); // Index of this parameter _mapParamName2Value[componentId].keys().indexOf(paramId)); // Index of this parameter
_emitMavlinkMessage(responseMsg); respondWithMavlinkMessage(responseMsg);
} }
void MockLink::_handleParamRequestRead(const mavlink_message_t& msg) void MockLink::_handleParamRequestRead(const mavlink_message_t& msg)
...@@ -613,7 +618,7 @@ void MockLink::_handleParamRequestRead(const mavlink_message_t& msg) ...@@ -613,7 +618,7 @@ void MockLink::_handleParamRequestRead(const mavlink_message_t& msg)
_mapParamName2MavParamType[paramId], // Parameter type _mapParamName2MavParamType[paramId], // Parameter type
_mapParamName2Value[componentId].count(), // Total number of parameters _mapParamName2Value[componentId].count(), // Total number of parameters
_mapParamName2Value[componentId].keys().indexOf(paramId)); // Index of this parameter _mapParamName2Value[componentId].keys().indexOf(paramId)); // Index of this parameter
_emitMavlinkMessage(responseMsg); respondWithMavlinkMessage(responseMsg);
} }
void MockLink::_handleMissionRequestList(const mavlink_message_t& msg) void MockLink::_handleMissionRequestList(const mavlink_message_t& msg)
...@@ -632,7 +637,7 @@ void MockLink::_handleMissionRequestList(const mavlink_message_t& msg) ...@@ -632,7 +637,7 @@ void MockLink::_handleMissionRequestList(const mavlink_message_t& msg)
msg.sysid, // Target is original sender msg.sysid, // Target is original sender
msg.compid, // Target is original sender msg.compid, // Target is original sender
_missionItems.count()); // Number of mission items _missionItems.count()); // Number of mission items
_emitMavlinkMessage(responseMsg); respondWithMavlinkMessage(responseMsg);
} }
void MockLink::_handleMissionRequest(const mavlink_message_t& msg) void MockLink::_handleMissionRequest(const mavlink_message_t& msg)
...@@ -660,7 +665,7 @@ void MockLink::_handleMissionRequest(const mavlink_message_t& msg) ...@@ -660,7 +665,7 @@ void MockLink::_handleMissionRequest(const mavlink_message_t& msg)
item.autocontinue, item.autocontinue,
item.param1, item.param2, item.param3, item.param4, item.param1, item.param2, item.param3, item.param4,
item.x, item.y, item.z); item.x, item.y, item.z);
_emitMavlinkMessage(responseMsg); respondWithMavlinkMessage(responseMsg);
} }
void MockLink::_handleMissionItem(const mavlink_message_t& msg) void MockLink::_handleMissionItem(const mavlink_message_t& msg)
...@@ -711,5 +716,11 @@ void MockLink::emitRemoteControlChannelRawChanged(int channel, uint16_t raw) ...@@ -711,5 +716,11 @@ void MockLink::emitRemoteControlChannelRawChanged(int channel, uint16_t raw)
chanRaw[16], // channel raw value chanRaw[16], // channel raw value
chanRaw[17], // channel raw value chanRaw[17], // channel raw value
0); // rss 0); // rss
_emitMavlinkMessage(responseMsg); respondWithMavlinkMessage(responseMsg);
}
void MockLink::_handleFTP(const mavlink_message_t& msg)
{
Q_ASSERT(_fileServer);
_fileServer->handleFTPMessage(msg);
} }
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <QLoggingCategory> #include <QLoggingCategory>
#include "MockLinkMissionItemHandler.h" #include "MockLinkMissionItemHandler.h"
#include "MockLinkFileServer.h"
#include "LinkManager.h" #include "LinkManager.h"
#include "QGCMAVLink.h" #include "QGCMAVLink.h"
...@@ -71,6 +72,11 @@ public: ...@@ -71,6 +72,11 @@ public:
MAV_AUTOPILOT getAutopilotType(void) { return _autopilotType; } MAV_AUTOPILOT getAutopilotType(void) { return _autopilotType; }
void setAutopilotType(MAV_AUTOPILOT autopilot) { _autopilotType = autopilot; } void setAutopilotType(MAV_AUTOPILOT autopilot) { _autopilotType = autopilot; }
void emitRemoteControlChannelRawChanged(int channel, uint16_t raw); void emitRemoteControlChannelRawChanged(int channel, uint16_t raw);
/// Sends the specified mavlink message to QGC
void respondWithMavlinkMessage(const mavlink_message_t& msg);
MockLinkFileServer* getFileServer(void) { return _fileServer; }
// These are left unimplemented in order to cause linker errors which indicate incorrect usage of // These are left unimplemented in order to cause linker errors which indicate incorrect usage of
// connect/disconnect on link directly. All connect/disconnect calls should be made through LinkManager. // connect/disconnect on link directly. All connect/disconnect calls should be made through LinkManager.
...@@ -109,7 +115,6 @@ private: ...@@ -109,7 +115,6 @@ private:
void _handleIncomingNSHBytes(const char* bytes, int cBytes); void _handleIncomingNSHBytes(const char* bytes, int cBytes);
void _handleIncomingMavlinkBytes(const uint8_t* bytes, int cBytes); void _handleIncomingMavlinkBytes(const uint8_t* bytes, int cBytes);
void _loadParams(void); void _loadParams(void);
void _emitMavlinkMessage(const mavlink_message_t& msg);
void _handleHeartBeat(const mavlink_message_t& msg); void _handleHeartBeat(const mavlink_message_t& msg);
void _handleSetMode(const mavlink_message_t& msg); void _handleSetMode(const mavlink_message_t& msg);
void _handleParamRequestList(const mavlink_message_t& msg); void _handleParamRequestList(const mavlink_message_t& msg);
...@@ -118,6 +123,7 @@ private: ...@@ -118,6 +123,7 @@ private:
void _handleMissionRequestList(const mavlink_message_t& msg); void _handleMissionRequestList(const mavlink_message_t& msg);
void _handleMissionRequest(const mavlink_message_t& msg); void _handleMissionRequest(const mavlink_message_t& msg);
void _handleMissionItem(const mavlink_message_t& msg); void _handleMissionItem(const mavlink_message_t& msg);
void _handleFTP(const mavlink_message_t& msg);
float _floatUnionForParam(int componentId, const QString& paramName); float _floatUnionForParam(int componentId, const QString& paramName);
void _setParamFloatUnionIntoMap(int componentId, const QString& paramName, float paramFloat); void _setParamFloatUnionIntoMap(int componentId, const QString& paramName, float paramFloat);
...@@ -144,6 +150,8 @@ private: ...@@ -144,6 +150,8 @@ private:
MockConfiguration* _config; MockConfiguration* _config;
MAV_AUTOPILOT _autopilotType; MAV_AUTOPILOT _autopilotType;
MockLinkFileServer* _fileServer;
}; };
#endif #endif
...@@ -21,18 +21,19 @@ ...@@ -21,18 +21,19 @@
======================================================================*/ ======================================================================*/
#include "MockMavlinkFileServer.h" #include "MockLinkFileServer.h"
#include "MockLink.h"
const MockMavlinkFileServer::ErrorMode_t MockMavlinkFileServer::rgFailureModes[] = {
MockMavlinkFileServer::errModeNoResponse, const MockLinkFileServer::ErrorMode_t MockLinkFileServer::rgFailureModes[] = {
MockMavlinkFileServer::errModeNakResponse, MockLinkFileServer::errModeNoResponse,
MockMavlinkFileServer::errModeNoSecondResponse, MockLinkFileServer::errModeNakResponse,
MockMavlinkFileServer::errModeNakSecondResponse, MockLinkFileServer::errModeNoSecondResponse,
MockMavlinkFileServer::errModeBadSequence, MockLinkFileServer::errModeNakSecondResponse,
MockLinkFileServer::errModeBadSequence,
}; };
const size_t MockMavlinkFileServer::cFailureModes = sizeof(MockMavlinkFileServer::rgFailureModes) / sizeof(MockMavlinkFileServer::rgFailureModes[0]); const size_t MockLinkFileServer::cFailureModes = sizeof(MockLinkFileServer::rgFailureModes) / sizeof(MockLinkFileServer::rgFailureModes[0]);
const MockMavlinkFileServer::FileTestCase MockMavlinkFileServer::rgFileTestCases[MockMavlinkFileServer::cFileTestCases] = { const MockLinkFileServer::FileTestCase MockLinkFileServer::rgFileTestCases[MockLinkFileServer::cFileTestCases] = {
// File fits one Read Ack packet, partially filling data // File fits one Read Ack packet, partially filling data
{ "partial.qgc", sizeof(((FileManager::Request*)0)->data) - 1, 1, false}, { "partial.qgc", sizeof(((FileManager::Request*)0)->data) - 1, 1, false},
// File fits one Read Ack packet, exactly filling all data // File fits one Read Ack packet, exactly filling all data
...@@ -42,19 +43,20 @@ const MockMavlinkFileServer::FileTestCase MockMavlinkFileServer::rgFileTestCases ...@@ -42,19 +43,20 @@ const MockMavlinkFileServer::FileTestCase MockMavlinkFileServer::rgFileTestCases
}; };
// We only support a single fixed session // We only support a single fixed session
const uint8_t MockMavlinkFileServer::_sessionId = 1; const uint8_t MockLinkFileServer::_sessionId = 1;
MockMavlinkFileServer::MockMavlinkFileServer(uint8_t systemIdQGC, uint8_t systemIdServer) : MockLinkFileServer::MockLinkFileServer(uint8_t systemIdServer, uint8_t componentIdServer, MockLink* mockLink) :
_errMode(errModeNone), _errMode(errModeNone),
_systemIdServer(systemIdServer), _systemIdServer(systemIdServer),
_systemIdQGC(systemIdQGC) _componentIdServer(componentIdServer),
_mockLink(mockLink)
{ {
} }
/// @brief Handles List command requests. Only supports root folder paths. /// @brief Handles List command requests. Only supports root folder paths.
/// File list returned is set using the setFileList method. /// File list returned is set using the setFileList method.
void MockMavlinkFileServer::_listCommand(FileManager::Request* request, uint16_t seqNumber) void MockLinkFileServer::_listCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber)
{ {
// FIXME: Does not support directories that span multiple packets // FIXME: Does not support directories that span multiple packets
...@@ -65,17 +67,18 @@ void MockMavlinkFileServer::_listCommand(FileManager::Request* request, uint16_t ...@@ -65,17 +67,18 @@ void MockMavlinkFileServer::_listCommand(FileManager::Request* request, uint16_t
// We only support root path // We only support root path
path = (char *)&request->data[0]; path = (char *)&request->data[0];
if (!path.isEmpty() && path != "/") { if (!path.isEmpty() && path != "/") {
_sendNak(FileManager::kErrFail, outgoingSeqNumber, FileManager::kCmdListDirectory); _sendNak(senderSystemId, senderComponentId, FileManager::kErrFail, outgoingSeqNumber, FileManager::kCmdListDirectory);
return; return;
} }
// Offset requested is past the end of the list // Offset requested is past the end of the list
if (request->hdr.offset > (uint32_t)_fileList.size()) { if (request->hdr.offset > (uint32_t)_fileList.size()) {
_sendNak(FileManager::kErrEOF, outgoingSeqNumber, FileManager::kCmdListDirectory); _sendNak(senderSystemId, senderComponentId, FileManager::kErrEOF, outgoingSeqNumber, FileManager::kCmdListDirectory);
return; return;
} }
ackResponse.hdr.opcode = FileManager::kRspAck; ackResponse.hdr.opcode = FileManager::kRspAck;
ackResponse.hdr.req_opcode = FileManager::kCmdListDirectory;
ackResponse.hdr.session = 0; ackResponse.hdr.session = 0;
ackResponse.hdr.offset = request->hdr.offset; ackResponse.hdr.offset = request->hdr.offset;
ackResponse.hdr.size = 0; ackResponse.hdr.size = 0;
...@@ -92,22 +95,22 @@ void MockMavlinkFileServer::_listCommand(FileManager::Request* request, uint16_t ...@@ -92,22 +95,22 @@ void MockMavlinkFileServer::_listCommand(FileManager::Request* request, uint16_t
bufPtr += cchFilename + 1; bufPtr += cchFilename + 1;
} }
_emitResponse(&ackResponse, outgoingSeqNumber); _sendResponse(senderSystemId, senderComponentId, &ackResponse, outgoingSeqNumber);
} else if (_errMode == errModeNakSecondResponse) { } else if (_errMode == errModeNakSecondResponse) {
// Nak error all subsequent requests // Nak error all subsequent requests
_sendNak(FileManager::kErrFail, outgoingSeqNumber, FileManager::kCmdListDirectory); _sendNak(senderSystemId, senderComponentId, FileManager::kErrFail, outgoingSeqNumber, FileManager::kCmdListDirectory);
return; return;
} else if (_errMode == errModeNoSecondResponse) { } else if (_errMode == errModeNoSecondResponse) {
// No response for all subsequent requests // No response for all subsequent requests
return; return;
} else { } else {
// FIXME: Does not support directories that span multiple packets // FIXME: Does not support directories that span multiple packets
_sendNak(FileManager::kErrEOF, outgoingSeqNumber, FileManager::kCmdListDirectory); _sendNak(senderSystemId, senderComponentId, FileManager::kErrEOF, outgoingSeqNumber, FileManager::kCmdListDirectory);
} }
} }
/// @brief Handles Open command requests. /// @brief Handles Open command requests.
void MockMavlinkFileServer::_openCommand(FileManager::Request* request, uint16_t seqNumber) void MockLinkFileServer::_openCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber)
{ {
FileManager::Request response; FileManager::Request response;
QString path; QString path;
...@@ -129,7 +132,7 @@ void MockMavlinkFileServer::_openCommand(FileManager::Request* request, uint16_t ...@@ -129,7 +132,7 @@ void MockMavlinkFileServer::_openCommand(FileManager::Request* request, uint16_t
} }
} }
if (!found) { if (!found) {
_sendNak(FileManager::kErrFail, outgoingSeqNumber, FileManager::kCmdOpenFileRO); _sendNak(senderSystemId, senderComponentId, FileManager::kErrFail, outgoingSeqNumber, FileManager::kCmdOpenFileRO);
return; return;
} }
...@@ -141,16 +144,16 @@ void MockMavlinkFileServer::_openCommand(FileManager::Request* request, uint16_t ...@@ -141,16 +144,16 @@ void MockMavlinkFileServer::_openCommand(FileManager::Request* request, uint16_t
response.hdr.size = sizeof(uint32_t); response.hdr.size = sizeof(uint32_t);
response.openFileLength = _readFileLength; response.openFileLength = _readFileLength;
_emitResponse(&response, outgoingSeqNumber); _sendResponse(senderSystemId, senderComponentId, &response, outgoingSeqNumber);
} }
void MockMavlinkFileServer::_readCommand(FileManager::Request* request, uint16_t seqNumber) void MockLinkFileServer::_readCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber)
{ {
FileManager::Request response; FileManager::Request response;
uint16_t outgoingSeqNumber = _nextSeqNumber(seqNumber); uint16_t outgoingSeqNumber = _nextSeqNumber(seqNumber);
if (request->hdr.session != _sessionId) { if (request->hdr.session != _sessionId) {
_sendNak(FileManager::kErrFail, outgoingSeqNumber, FileManager::kCmdReadFile); _sendNak(senderSystemId, senderComponentId, FileManager::kErrFail, outgoingSeqNumber, FileManager::kCmdReadFile);
return; return;
} }
...@@ -161,7 +164,7 @@ void MockMavlinkFileServer::_readCommand(FileManager::Request* request, uint16_t ...@@ -161,7 +164,7 @@ void MockMavlinkFileServer::_readCommand(FileManager::Request* request, uint16_t
// If we get here it means the client is requesting additional data past the first request // If we get here it means the client is requesting additional data past the first request
if (_errMode == errModeNakSecondResponse) { if (_errMode == errModeNakSecondResponse) {
// Nak error all subsequent requests // Nak error all subsequent requests
_sendNak(FileManager::kErrFail, outgoingSeqNumber, FileManager::kCmdReadFile); _sendNak(senderSystemId, senderComponentId, FileManager::kErrFail, outgoingSeqNumber, FileManager::kCmdReadFile);
return; return;
} else if (_errMode == errModeNoSecondResponse) { } else if (_errMode == errModeNoSecondResponse) {
// No rsponse for all subsequent requests // No rsponse for all subsequent requests
...@@ -170,7 +173,7 @@ void MockMavlinkFileServer::_readCommand(FileManager::Request* request, uint16_t ...@@ -170,7 +173,7 @@ void MockMavlinkFileServer::_readCommand(FileManager::Request* request, uint16_t
} }
if (readOffset >= _readFileLength) { if (readOffset >= _readFileLength) {
_sendNak(FileManager::kErrEOF, outgoingSeqNumber, FileManager::kCmdReadFile); _sendNak(senderSystemId, senderComponentId, FileManager::kErrEOF, outgoingSeqNumber, FileManager::kCmdReadFile);
return; return;
} }
...@@ -188,16 +191,16 @@ void MockMavlinkFileServer::_readCommand(FileManager::Request* request, uint16_t ...@@ -188,16 +191,16 @@ void MockMavlinkFileServer::_readCommand(FileManager::Request* request, uint16_t
response.hdr.opcode = FileManager::kRspAck; response.hdr.opcode = FileManager::kRspAck;
response.hdr.req_opcode = FileManager::kCmdReadFile; response.hdr.req_opcode = FileManager::kCmdReadFile;
_emitResponse(&response, outgoingSeqNumber); _sendResponse(senderSystemId, senderComponentId, &response, outgoingSeqNumber);
} }
void MockMavlinkFileServer::_streamCommand(FileManager::Request* request, uint16_t seqNumber) void MockLinkFileServer::_streamCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber)
{ {
uint16_t outgoingSeqNumber = _nextSeqNumber(seqNumber); uint16_t outgoingSeqNumber = _nextSeqNumber(seqNumber);
FileManager::Request response; FileManager::Request response;
if (request->hdr.session != _sessionId) { if (request->hdr.session != _sessionId) {
_sendNak(FileManager::kErrFail, outgoingSeqNumber, FileManager::kCmdBurstReadFile); _sendNak(senderSystemId, senderComponentId, FileManager::kErrFail, outgoingSeqNumber, FileManager::kCmdBurstReadFile);
return; return;
} }
...@@ -212,7 +215,7 @@ void MockMavlinkFileServer::_streamCommand(FileManager::Request* request, uint16 ...@@ -212,7 +215,7 @@ void MockMavlinkFileServer::_streamCommand(FileManager::Request* request, uint16
// If we get here it means the client is requesting additional data past the first request // If we get here it means the client is requesting additional data past the first request
if (_errMode == errModeNakSecondResponse) { if (_errMode == errModeNakSecondResponse) {
// Nak error all subsequent requests // Nak error all subsequent requests
_sendNak(FileManager::kErrFail, outgoingSeqNumber, FileManager::kCmdBurstReadFile); _sendNak(senderSystemId, senderComponentId, FileManager::kErrFail, outgoingSeqNumber, FileManager::kCmdBurstReadFile);
return; return;
} else if (_errMode == errModeNoSecondResponse) { } else if (_errMode == errModeNoSecondResponse) {
// No response for all subsequent requests // No response for all subsequent requests
...@@ -234,55 +237,67 @@ void MockMavlinkFileServer::_streamCommand(FileManager::Request* request, uint16 ...@@ -234,55 +237,67 @@ void MockMavlinkFileServer::_streamCommand(FileManager::Request* request, uint16
response.hdr.opcode = FileManager::kRspAck; response.hdr.opcode = FileManager::kRspAck;
response.hdr.req_opcode = FileManager::kCmdBurstReadFile; response.hdr.req_opcode = FileManager::kCmdBurstReadFile;
_emitResponse(&response, outgoingSeqNumber); _sendResponse(senderSystemId, senderComponentId, &response, outgoingSeqNumber);
outgoingSeqNumber = _nextSeqNumber(outgoingSeqNumber); outgoingSeqNumber = _nextSeqNumber(outgoingSeqNumber);
ackOffset += cDataAck; ackOffset += cDataAck;
} }
_sendNak(FileManager::kErrEOF, outgoingSeqNumber, FileManager::kCmdBurstReadFile); _sendNak(senderSystemId, senderComponentId, FileManager::kErrEOF, outgoingSeqNumber, FileManager::kCmdBurstReadFile);
} }
/// @brief Handles Terminate commands void MockLinkFileServer::_terminateCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber)
void MockMavlinkFileServer::_terminateCommand(FileManager::Request* request, uint16_t seqNumber)
{ {
uint16_t outgoingSeqNumber = _nextSeqNumber(seqNumber); uint16_t outgoingSeqNumber = _nextSeqNumber(seqNumber);
if (request->hdr.session != _sessionId) { if (request->hdr.session != _sessionId) {
_sendNak(FileManager::kErrInvalidSession, outgoingSeqNumber, FileManager::kCmdTerminateSession); _sendNak(senderSystemId, senderComponentId, FileManager::kErrInvalidSession, outgoingSeqNumber, FileManager::kCmdTerminateSession);
return; return;
} }
_sendAck(outgoingSeqNumber, FileManager::kCmdTerminateSession); _sendAck(senderSystemId, senderComponentId, outgoingSeqNumber, FileManager::kCmdTerminateSession);
// Let our test harness know that we got a terminate command. This is used to validate the a Terminate is correctly
// sent after an Open.
emit terminateCommandReceived(); emit terminateCommandReceived();
} }
/// @brief Handles messages sent to the FTP server. void MockLinkFileServer::_resetCommand(uint8_t senderSystemId, uint8_t senderComponentId, uint16_t seqNumber)
void MockMavlinkFileServer::sendMessage(mavlink_message_t message)
{ {
FileManager::Request ackResponse; uint16_t outgoingSeqNumber = _nextSeqNumber(seqNumber);
_sendAck(senderSystemId, senderComponentId, outgoingSeqNumber, FileManager::kCmdResetSessions);
emit resetCommandReceived();
}
Q_ASSERT(message.msgid == MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL); void MockLinkFileServer::handleFTPMessage(const mavlink_message_t& message)
{
if (message.msgid != MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL) {
return;
}
mavlink_file_transfer_protocol_t requestFileTransferProtocol; FileManager::Request ackResponse;
mavlink_msg_file_transfer_protocol_decode(&message, &requestFileTransferProtocol);
FileManager::Request* request = (FileManager::Request*)&requestFileTransferProtocol.payload[0];
Q_ASSERT(requestFileTransferProtocol.target_system == _systemIdServer); mavlink_file_transfer_protocol_t requestFTP;
mavlink_msg_file_transfer_protocol_decode(&message, &requestFTP);
if (requestFTP.target_system != _systemIdServer) {
return;
}
FileManager::Request* request = (FileManager::Request*)&requestFTP.payload[0];
uint16_t incomingSeqNumber = request->hdr.seqNumber; uint16_t incomingSeqNumber = request->hdr.seqNumber;
uint16_t outgoingSeqNumber = _nextSeqNumber(incomingSeqNumber); uint16_t outgoingSeqNumber = _nextSeqNumber(incomingSeqNumber);
if (_errMode == errModeNoResponse) { if (request->hdr.opcode != FileManager::kCmdResetSessions && request->hdr.opcode != FileManager::kCmdTerminateSession) {
// Don't respond to any requests, this shold cause the client to eventually timeout waiting for the ack if (_errMode == errModeNoResponse) {
return; // Don't respond to any requests, this shold cause the client to eventually timeout waiting for the ack
} else if (_errMode == errModeNakResponse) { return;
// Nak all requests, the actual error send back doesn't really matter as long as it's an error } else if (_errMode == errModeNakResponse) {
_sendNak(FileManager::kErrFail, outgoingSeqNumber, (FileManager::Opcode)request->hdr.opcode); // Nak all requests, the actual error send back doesn't really matter as long as it's an error
return; _sendNak(message.sysid, message.compid, FileManager::kErrFail, outgoingSeqNumber, (FileManager::Opcode)request->hdr.opcode);
return;
}
} }
switch (request->hdr.opcode) { switch (request->hdr.opcode) {
...@@ -290,47 +305,47 @@ void MockMavlinkFileServer::sendMessage(mavlink_message_t message) ...@@ -290,47 +305,47 @@ void MockMavlinkFileServer::sendMessage(mavlink_message_t message)
// ignored, ack not sent back, for testing only // ignored, ack not sent back, for testing only
break; break;
case FileManager::kCmdResetSessions:
// terminates all sessions
// Fall through to send back Ack
case FileManager::kCmdNone: case FileManager::kCmdNone:
// ignored, always acked // ignored, always acked
ackResponse.hdr.opcode = FileManager::kRspAck; ackResponse.hdr.opcode = FileManager::kRspAck;
ackResponse.hdr.session = 0; ackResponse.hdr.session = 0;
ackResponse.hdr.size = 0; ackResponse.hdr.size = 0;
_emitResponse(&ackResponse, outgoingSeqNumber); _sendResponse(message.sysid, message.compid, &ackResponse, outgoingSeqNumber);
break; break;
case FileManager::kCmdListDirectory: case FileManager::kCmdListDirectory:
_listCommand(request, incomingSeqNumber); _listCommand(message.sysid, message.compid, request, incomingSeqNumber);
break; break;
case FileManager::kCmdOpenFileRO: case FileManager::kCmdOpenFileRO:
_openCommand(request, incomingSeqNumber); _openCommand(message.sysid, message.compid, request, incomingSeqNumber);
break; break;
case FileManager::kCmdReadFile: case FileManager::kCmdReadFile:
_readCommand(request, incomingSeqNumber); _readCommand(message.sysid, message.compid, request, incomingSeqNumber);
break; break;
case FileManager::kCmdBurstReadFile: case FileManager::kCmdBurstReadFile:
_streamCommand(request, incomingSeqNumber); _streamCommand(message.sysid, message.compid, request, incomingSeqNumber);
break; break;
case FileManager::kCmdTerminateSession: case FileManager::kCmdTerminateSession:
_terminateCommand(request, incomingSeqNumber); _terminateCommand(message.sysid, message.compid, request, incomingSeqNumber);
break; break;
case FileManager::kCmdResetSessions:
_resetCommand(message.sysid, message.compid, incomingSeqNumber);
break;
default: default:
// nack for all NYI opcodes // nack for all NYI opcodes
_sendNak(FileManager::kErrUnknownCommand, outgoingSeqNumber, (FileManager::Opcode)request->hdr.opcode); _sendNak(message.sysid, message.compid, FileManager::kErrUnknownCommand, outgoingSeqNumber, (FileManager::Opcode)request->hdr.opcode);
break; break;
} }
} }
/// @brief Sends an Ack /// @brief Sends an Ack
void MockMavlinkFileServer::_sendAck(uint16_t seqNumber, FileManager::Opcode reqOpcode) void MockLinkFileServer::_sendAck(uint8_t targetSystemId, uint8_t targetComponentId, uint16_t seqNumber, FileManager::Opcode reqOpcode)
{ {
FileManager::Request ackResponse; FileManager::Request ackResponse;
...@@ -339,11 +354,11 @@ void MockMavlinkFileServer::_sendAck(uint16_t seqNumber, FileManager::Opcode req ...@@ -339,11 +354,11 @@ void MockMavlinkFileServer::_sendAck(uint16_t seqNumber, FileManager::Opcode req
ackResponse.hdr.session = 0; ackResponse.hdr.session = 0;
ackResponse.hdr.size = 0; ackResponse.hdr.size = 0;
_emitResponse(&ackResponse, seqNumber); _sendResponse(targetSystemId, targetComponentId, &ackResponse, seqNumber);
} }
/// @brief Sends a Nak with the specified error code. /// @brief Sends a Nak with the specified error code.
void MockMavlinkFileServer::_sendNak(FileManager::ErrorCode error, uint16_t seqNumber, FileManager::Opcode reqOpcode) void MockLinkFileServer::_sendNak(uint8_t targetSystemId, uint8_t targetComponentId, FileManager::ErrorCode error, uint16_t seqNumber, FileManager::Opcode reqOpcode)
{ {
FileManager::Request nakResponse; FileManager::Request nakResponse;
...@@ -353,11 +368,11 @@ void MockMavlinkFileServer::_sendNak(FileManager::ErrorCode error, uint16_t seqN ...@@ -353,11 +368,11 @@ void MockMavlinkFileServer::_sendNak(FileManager::ErrorCode error, uint16_t seqN
nakResponse.hdr.size = 1; nakResponse.hdr.size = 1;
nakResponse.data[0] = error; nakResponse.data[0] = error;
_emitResponse(&nakResponse, seqNumber); _sendResponse(targetSystemId, targetComponentId, &nakResponse, seqNumber);
} }
/// @brief Emits a Request through the messageReceived signal. /// @brief Emits a Request through the messageReceived signal.
void MockMavlinkFileServer::_emitResponse(FileManager::Request* request, uint16_t seqNumber) void MockLinkFileServer::_sendResponse(uint8_t targetSystemId, uint8_t targetComponentId, FileManager::Request* request, uint16_t seqNumber)
{ {
mavlink_message_t mavlinkMessage; mavlink_message_t mavlinkMessage;
...@@ -367,16 +382,16 @@ void MockMavlinkFileServer::_emitResponse(FileManager::Request* request, uint16_ ...@@ -367,16 +382,16 @@ void MockMavlinkFileServer::_emitResponse(FileManager::Request* request, uint16_
0, // Component ID 0, // Component ID
&mavlinkMessage, // Mavlink Message to pack into &mavlinkMessage, // Mavlink Message to pack into
0, // Target network 0, // Target network
_systemIdQGC, // QGC Target System ID targetSystemId,
0, // Target component targetComponentId,
(uint8_t*)request); // Payload (uint8_t*)request); // Payload
emit messageReceived(NULL, mavlinkMessage); _mockLink->respondWithMavlinkMessage(mavlinkMessage);
} }
/// @brief Generates the next sequence number given an incoming sequence number. Handles generating /// @brief Generates the next sequence number given an incoming sequence number. Handles generating
/// bad sequence numbers when errModeBadSequence is set. /// bad sequence numbers when errModeBadSequence is set.
uint16_t MockMavlinkFileServer::_nextSeqNumber(uint16_t seqNumber) uint16_t MockLinkFileServer::_nextSeqNumber(uint16_t seqNumber)
{ {
uint16_t outgoingSeqNumber = seqNumber + 1; uint16_t outgoingSeqNumber = seqNumber + 1;
......
...@@ -21,29 +21,25 @@ ...@@ -21,29 +21,25 @@
======================================================================*/ ======================================================================*/
#ifndef MOCKMAVLINKFILESERVER_H
#define MOCKMAVLINKFILESERVER_H
#include "MockMavlinkInterface.h"
#include "FileManager.h"
/// @file /// @file
/// @brief Mock implementation of Mavlink FTP server. Used as mavlink plugin to MockUAS.
/// Only root directory access is supported.
///
/// @author Don Gagne <don@thegagnes.com> /// @author Don Gagne <don@thegagnes.com>
#ifndef MockLinkFileServer_H
#define MockLinkFileServer_H
#include "FileManager.h"
#include <QStringList> #include <QStringList>
class MockMavlinkFileServer : public MockMavlinkInterface class MockLink;
/// Mock implementation of Mavlink FTP server.
class MockLinkFileServer : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
/// @brief Constructor for MockMavlinkFileServer MockLinkFileServer(uint8_t systemIdServer, uint8_t componentIdServer, MockLink* mockLink);
/// @param System ID for QGroundControl App
/// @pqram System ID for this Server
MockMavlinkFileServer(uint8_t systemIdQGC, uint8_t systemIdServer);
/// @brief Sets the list of files returned by the List command. Prepend names with F or D /// @brief Sets the list of files returned by the List command. Prepend names with F or D
/// to indicate (F)ile or (D)irectory. /// to indicate (F)ile or (D)irectory.
...@@ -70,8 +66,8 @@ public: ...@@ -70,8 +66,8 @@ public:
/// @brief The number of ErrorModes in the rgFailureModes array. /// @brief The number of ErrorModes in the rgFailureModes array.
static const size_t cFailureModes; static const size_t cFailureModes;
// From MockMavlinkInterface /// Called to handle an FTP message
virtual void sendMessage(mavlink_message_t message); void handleFTPMessage(const mavlink_message_t& message);
/// @brief Used to represent a single test case for download testing. /// @brief Used to represent a single test case for download testing.
struct FileTestCase { struct FileTestCase {
...@@ -88,18 +84,22 @@ public: ...@@ -88,18 +84,22 @@ public:
static const FileTestCase rgFileTestCases[cFileTestCases]; static const FileTestCase rgFileTestCases[cFileTestCases];
signals: signals:
/// @brief You can connect to this signal to be notified when the server receives a Terminate command. /// You can connect to this signal to be notified when the server receives a Terminate command.
void terminateCommandReceived(void); void terminateCommandReceived(void);
/// You can connect to this signal to be notified when the server receives a Reset command.
void resetCommandReceived(void);
private: private:
void _sendAck(uint16_t seqNumber, FileManager::Opcode reqOpcode); void _sendAck(uint8_t targetSystemId, uint8_t targetComponentId, uint16_t seqNumber, FileManager::Opcode reqOpcode);
void _sendNak(FileManager::ErrorCode error, uint16_t seqNumber, FileManager::Opcode reqOpcode); void _sendNak(uint8_t targetSystemId, uint8_t targetComponentId, FileManager::ErrorCode error, uint16_t seqNumber, FileManager::Opcode reqOpcode);
void _emitResponse(FileManager::Request* request, uint16_t seqNumber); void _sendResponse(uint8_t targetSystemId, uint8_t targetComponentId, FileManager::Request* request, uint16_t seqNumber);
void _listCommand(FileManager::Request* request, uint16_t seqNumber); void _listCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber);
void _openCommand(FileManager::Request* request, uint16_t seqNumber); void _openCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber);
void _readCommand(FileManager::Request* request, uint16_t seqNumber); void _readCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber);
void _streamCommand(FileManager::Request* request, uint16_t seqNumber); void _streamCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber);
void _terminateCommand(FileManager::Request* request, uint16_t seqNumber); void _terminateCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber);
void _resetCommand(uint8_t senderSystemId, uint8_t senderComponentId, uint16_t seqNumber);
uint16_t _nextSeqNumber(uint16_t seqNumber); uint16_t _nextSeqNumber(uint16_t seqNumber);
QStringList _fileList; ///< List of files returned by List command QStringList _fileList; ///< List of files returned by List command
...@@ -108,7 +108,8 @@ private: ...@@ -108,7 +108,8 @@ private:
uint8_t _readFileLength; ///< Length of active file being read uint8_t _readFileLength; ///< Length of active file being read
ErrorMode_t _errMode; ///< Currently set error mode, as specified by setErrorMode ErrorMode_t _errMode; ///< Currently set error mode, as specified by setErrorMode
const uint8_t _systemIdServer; ///< System ID for server const uint8_t _systemIdServer; ///< System ID for server
const uint8_t _systemIdQGC; ///< QGC System ID const uint8_t _componentIdServer; ///< Component ID for server
MockLink* _mockLink; ///< MockLink to communicate through
}; };
#endif #endif
...@@ -21,37 +21,21 @@ ...@@ -21,37 +21,21 @@
======================================================================*/ ======================================================================*/
#include "FileManagerTest.h"
/// @file /// @file
/// @brief FileManager unit test. Note: All code here assumes all work between
/// the unit test, mack mavlink file server and file manager is happening on
/// the same thread.
///
/// @author Don Gagne <don@thegagnes.com> /// @author Don Gagne <don@thegagnes.com>
#include "FileManagerTest.h"
#include "UASManager.h"
UT_REGISTER_TEST(FileManagerTest) UT_REGISTER_TEST(FileManagerTest)
FileManagerTest::FileManagerTest(void) : FileManagerTest::FileManagerTest(void) :
_mockFileServer(_systemIdQGC, _systemIdServer), _mockLink(NULL),
_fileServer(NULL),
_fileManager(NULL), _fileManager(NULL),
_multiSpy(NULL) _multiSpy(NULL)
{ {
}
// Called once before all test cases are run
void FileManagerTest::initTestCase(void)
{
_mockUAS = new MockUAS();
Q_CHECK_PTR(_mockUAS);
_mockUAS->setMockSystemId(_systemIdServer);
_mockUAS->setMockMavlinkPlugin(&_mockFileServer);
}
void FileManagerTest::cleanupTestCase(void)
{
delete _mockUAS;
} }
// Called before every test case // Called before every test case
...@@ -59,26 +43,37 @@ void FileManagerTest::init(void) ...@@ -59,26 +43,37 @@ void FileManagerTest::init(void)
{ {
UnitTest::init(); UnitTest::init();
Q_ASSERT(_multiSpy == NULL); _mockLink = new MockLink();
Q_CHECK_PTR(_mockLink);
LinkManager::instance()->_addLink(_mockLink);
LinkManager::instance()->connectLink(_mockLink);
_fileServer = _mockLink->getFileServer();
QVERIFY(_fileServer != NULL);
_fileManager = new FileManager(NULL, _mockUAS, _systemIdQGC); // Wait or the UAS to show up
Q_CHECK_PTR(_fileManager); UASManagerInterface* uasManager = UASManager::instance();
QSignalSpy spyUasCreate(uasManager, SIGNAL(UASCreated(UASInterface*)));
if (!uasManager->getActiveUAS()) {
QCOMPARE(spyUasCreate.wait(10000), true);
}
UASInterface* uas = uasManager->getActiveUAS();
QVERIFY(uas != NULL);
_fileManager = uas->getFileManager();
QVERIFY(_fileManager != NULL);
Q_ASSERT(_multiSpy == NULL);
// Reset any internal state back to normal // Reset any internal state back to normal
_mockFileServer.setErrorMode(MockMavlinkFileServer::errModeNone); _fileServer->setErrorMode(MockLinkFileServer::errModeNone);
_fileListReceived.clear(); _fileListReceived.clear();
connect(&_mockFileServer, &MockMavlinkFileServer::messageReceived, _fileManager, &FileManager::receiveMessage);
connect(_fileManager, &FileManager::listEntry, this, &FileManagerTest::listEntry); connect(_fileManager, &FileManager::listEntry, this, &FileManagerTest::listEntry);
_rgSignals[listEntrySignalIndex] = SIGNAL(listEntry(const QString&)); _rgSignals[listEntrySignalIndex] = SIGNAL(listEntry(const QString&));
_rgSignals[listCompleteSignalIndex] = SIGNAL(listComplete(void)); _rgSignals[commandCompleteSignalIndex] = SIGNAL(commandComplete(void));
_rgSignals[commandErrorSignalIndex] = SIGNAL(commandError(const QString&));
_rgSignals[downloadFileLengthSignalIndex] = SIGNAL(downloadFileLength(unsigned int));
_rgSignals[downloadFileCompleteSignalIndex] = SIGNAL(downloadFileComplete(void));
_rgSignals[errorMessageSignalIndex] = SIGNAL(errorMessage(const QString&));
_multiSpy = new MultiSignalSpy(); _multiSpy = new MultiSignalSpy();
Q_CHECK_PTR(_multiSpy); Q_CHECK_PTR(_multiSpy);
...@@ -91,10 +86,15 @@ void FileManagerTest::cleanup(void) ...@@ -91,10 +86,15 @@ void FileManagerTest::cleanup(void)
Q_ASSERT(_multiSpy); Q_ASSERT(_multiSpy);
Q_ASSERT(_fileManager); Q_ASSERT(_fileManager);
delete _fileManager; // Disconnecting the link will prompt for log file save
setExpectedFileDialog(getSaveFileName, QStringList());
LinkManager::instance()->disconnectLink(_mockLink);
_fileServer = NULL;
_mockLink = NULL;
_fileManager = NULL;
delete _multiSpy; delete _multiSpy;
_fileManager = NULL;
_multiSpy = NULL; _multiSpy = NULL;
UnitTest::cleanup(); UnitTest::cleanup();
...@@ -108,7 +108,6 @@ void FileManagerTest::listEntry(const QString& entry) ...@@ -108,7 +108,6 @@ void FileManagerTest::listEntry(const QString& entry)
} }
#if 0
void FileManagerTest::_ackTest(void) void FileManagerTest::_ackTest(void)
{ {
Q_ASSERT(_fileManager); Q_ASSERT(_fileManager);
...@@ -122,16 +121,17 @@ void FileManagerTest::_ackTest(void) ...@@ -122,16 +121,17 @@ void FileManagerTest::_ackTest(void)
QVERIFY(_multiSpy->checkNoSignals()); QVERIFY(_multiSpy->checkNoSignals());
// Setup for no response from ack. This should cause a timeout error // Setup for no response from ack. This should cause a timeout error
_mockFileServer.setErrorMode(MockMavlinkFileServer::errModeNoResponse); _fileServer->setErrorMode(MockLinkFileServer::errModeNoResponse);
QVERIFY(_fileManager->_sendCmdTestAck()); QVERIFY(_fileManager->_sendCmdTestAck());
QTest::qWait(_ackTimerTimeoutMsecs); // Let the file manager timeout _multiSpy->waitForSignalByIndex(commandErrorSignalIndex, _ackTimerTimeoutMsecs);
QCOMPARE(_multiSpy->checkOnlySignalByMask(errorMessageSignalMask), true); QCOMPARE(_multiSpy->checkOnlySignalByMask(commandErrorSignalMask), true);
_multiSpy->clearAllSignals(); _multiSpy->clearAllSignals();
// Setup for a bad sequence number in the ack. This should cause an error; // Setup for a bad sequence number in the ack. This should cause an error;
_mockFileServer.setErrorMode(MockMavlinkFileServer::errModeBadSequence); _fileServer->setErrorMode(MockLinkFileServer::errModeBadSequence);
QVERIFY(_fileManager->_sendCmdTestAck()); QVERIFY(_fileManager->_sendCmdTestAck());
QCOMPARE(_multiSpy->checkOnlySignalByMask(errorMessageSignalMask), true); _multiSpy->waitForSignalByIndex(commandErrorSignalIndex, _ackTimerTimeoutMsecs);
QCOMPARE(_multiSpy->checkOnlySignalByMask(commandErrorSignalMask), true);
_multiSpy->clearAllSignals(); _multiSpy->clearAllSignals();
} }
...@@ -144,19 +144,7 @@ void FileManagerTest::_noAckTest(void) ...@@ -144,19 +144,7 @@ void FileManagerTest::_noAckTest(void)
// This should not get the ack back and timeout. // This should not get the ack back and timeout.
QVERIFY(_fileManager->_sendCmdTestNoAck()); QVERIFY(_fileManager->_sendCmdTestNoAck());
QTest::qWait(_ackTimerTimeoutMsecs); // Let the file manager timeout QTest::qWait(_ackTimerTimeoutMsecs); // Let the file manager timeout
QCOMPARE(_multiSpy->checkOnlySignalByMask(errorMessageSignalMask), true); QCOMPARE(_multiSpy->checkOnlySignalByMask(commandErrorSignalMask), true);
}
void FileManagerTest::_resetTest(void)
{
Q_ASSERT(_fileManager);
Q_ASSERT(_multiSpy);
Q_ASSERT(_multiSpy->checkNoSignals() == true);
// Send a reset command
// We should not get any signals back from this
QVERIFY(_fileManager->_sendCmdReset());
QVERIFY(_multiSpy->checkNoSignals());
} }
void FileManagerTest::_listTest(void) void FileManagerTest::_listTest(void)
...@@ -167,37 +155,49 @@ void FileManagerTest::_listTest(void) ...@@ -167,37 +155,49 @@ void FileManagerTest::_listTest(void)
// FileManager::listDirectory signalling as follows: // FileManager::listDirectory signalling as follows:
// Emits a listEntry signal for each list entry // Emits a listEntry signal for each list entry
// Emits an errorMessage signal if: // Emits an commandError signal if:
// It gets a Nak back // It gets a Nak back
// Sequence number is incorrrect on any response // Sequence number is incorrrect on any response
// CRC is incorrect on any responses // CRC is incorrect on any responses
// List entry is formatted incorrectly // List entry is formatted incorrectly
// It is possible to get a number of good listEntry signals, followed by an errorMessage signal // It is possible to get a number of good listEntry signals, followed by an commandError signal
// Emits listComplete after it receives the final list entry // Emits commandComplete after it receives the final list entry
// If an errorMessage signal is signalled no listComplete is signalled // If an commandError signal is signalled no listComplete is signalled
// Send a bogus path // Send a bogus path
// We should get a single resetStatusMessages signal // We should get a single commandError signal
// We should get a single errorMessage signal
_fileManager->listDirectory("/bogus"); _fileManager->listDirectory("/bogus");
QCOMPARE(_multiSpy->checkOnlySignalByMask(errorMessageSignalMask), true); _multiSpy->waitForSignalByIndex(commandErrorSignalIndex, _ackTimerTimeoutMsecs);
QCOMPARE(_multiSpy->checkOnlySignalByMask(commandErrorSignalMask), true);
_multiSpy->clearAllSignals(); _multiSpy->clearAllSignals();
// Setup the mock file server with a valid directory list // Setup the mock file server with a valid directory list
QStringList fileList; QStringList fileList;
fileList << "Ddir" << "Ffoo" << "Fbar"; fileList << "Ddir" << "Ffoo" << "Fbar";
_mockFileServer.setFileList(fileList); _fileServer->setFileList(fileList);
// Send a list command at the root of the directory tree which should succeed
_fileManager->listDirectory("/");
QTest::qWait(_ackTimerTimeoutMsecs); // Let the file manager timeout
QCOMPARE(_multiSpy->checkSignalByMask(commandCompleteSignalMask), true);
QCOMPARE(_multiSpy->checkNoSignalByMask(commandErrorSignalMask), true);
QCOMPARE(_multiSpy->getSpyByIndex(listEntrySignalIndex)->count(), fileList.count());
QVERIFY(_fileListReceived == fileList);
// Set everything back to initial state
_fileListReceived.clear();
_multiSpy->clearAllSignals();
// Run through the various server side failure modes // Run through the various server side failure modes
for (size_t i=0; i<MockMavlinkFileServer::cFailureModes; i++) { for (size_t i=0; i<MockLinkFileServer::cFailureModes; i++) {
MockMavlinkFileServer::ErrorMode_t errMode = MockMavlinkFileServer::rgFailureModes[i]; MockLinkFileServer::ErrorMode_t errMode = MockLinkFileServer::rgFailureModes[i];
qDebug() << "Testing failure mode:" << errMode; qDebug() << "Testing failure mode:" << errMode;
_mockFileServer.setErrorMode(errMode); _fileServer->setErrorMode(errMode);
_fileManager->listDirectory("/"); _fileManager->listDirectory("/");
QTest::qWait(_ackTimerTimeoutMsecs); // Let the file manager timeout QTest::qWait(_ackTimerTimeoutMsecs); // Let the file manager timeout
if (errMode == MockMavlinkFileServer::errModeNoSecondResponse || errMode == MockMavlinkFileServer::errModeNakSecondResponse) { if (errMode == MockLinkFileServer::errModeNoSecondResponse || errMode == MockLinkFileServer::errModeNakSecondResponse) {
// For simulated server errors on subsequent Acks, the first Ack will go through. This means we should have gotten some // For simulated server errors on subsequent Acks, the first Ack will go through. This means we should have gotten some
// partial results. In the case of the directory list test set, all entries fit into the first ack, so we should have // partial results. In the case of the directory list test set, all entries fit into the first ack, so we should have
// gotten back all of them. // gotten back all of them.
...@@ -205,262 +205,193 @@ void FileManagerTest::_listTest(void) ...@@ -205,262 +205,193 @@ void FileManagerTest::_listTest(void)
_multiSpy->clearSignalByIndex(listEntrySignalIndex); _multiSpy->clearSignalByIndex(listEntrySignalIndex);
// And then it should have errored out because the next list Request would have failed. // And then it should have errored out because the next list Request would have failed.
QCOMPARE(_multiSpy->checkOnlySignalByMask(errorMessageSignalMask), true); QCOMPARE(_multiSpy->checkOnlySignalByMask(commandErrorSignalMask), true);
} else { } else {
// For the simulated errors which failed the intial response we should not have gotten any results back at all. // For the simulated errors which failed the intial response we should not have gotten any results back at all.
// Just an error. // Just an error.
QCOMPARE(_multiSpy->checkOnlySignalByMask(errorMessageSignalMask), true); QCOMPARE(_multiSpy->checkOnlySignalByMask(commandErrorSignalMask), true);
} }
// Set everything back to initial state // Set everything back to initial state
_fileListReceived.clear(); _fileListReceived.clear();
_multiSpy->clearAllSignals(); _multiSpy->clearAllSignals();
_mockFileServer.setErrorMode(MockMavlinkFileServer::errModeNone); _fileServer->setErrorMode(MockLinkFileServer::errModeNone);
} }
// Send a list command at the root of the directory tree which should succeed
_fileManager->listDirectory("/");
QCOMPARE(_multiSpy->checkSignalByMask(listCompleteSignalMask), true);
QCOMPARE(_multiSpy->checkNoSignalByMask(errorMessageSignalMask), true);
QCOMPARE(_multiSpy->getSpyByIndex(listEntrySignalIndex)->count(), fileList.count());
QVERIFY(_fileListReceived == fileList);
} }
#if 0
// Trying to write test code for read and burst mode download as well as implement support in MockLineFileServer reached a point
// of diminishing returns where the test code and mock server were generating more bugs in themselves than finding problems.
void FileManagerTest::_readDownloadTest(void) void FileManagerTest::_readDownloadTest(void)
{ {
Q_ASSERT(_fileManager); Q_ASSERT(_fileManager);
Q_ASSERT(_multiSpy); Q_ASSERT(_multiSpy);
Q_ASSERT(_multiSpy->checkNoSignals() == true); Q_ASSERT(_multiSpy->checkNoSignals() == true);
// FileManager::downloadPath works as follows: // We setup a spy on the Reset command signal of the mock file server so that we can determine that a
// Sends an Open Command to the server // Reset command was correctly sent after the Open/Read commands complete.
// Expects an Ack Response back from the server with the correct sequence numner QSignalSpy resetSpy(_fileServer, SIGNAL(resetCommandReceived()));
// Emits an errorMessage signal if it gets a Nak back
// Emits an downloadFileLength signal with the file length if it gets back a good Ack
// Sends subsequent Read commands to the server until it gets the full file contents back
// Emits a downloadFileProgress for each read command ack it gets back
// Sends Terminate command to server when download is complete to close Open command
// Mock file server will signal terminateCommandReceived when it gets a Terminate command
// Sends downloadFileComplete signal to indicate the download is complete
// Emits an errorMessage signal if sequence number is incorrrect on any response
// Emits an errorMessage signal if CRC is incorrect on any responses
// Expected signals if the Open command fails for any reason
quint16 signalMaskOpenFailure = errorMessageSignalMask;
// Expected signals if the Read command fails for any reason
quint16 signalMaskReadFailure = downloadFileLengthSignalMask | errorMessageSignalMask;
// Expected signals if the downloadPath command succeeds
quint16 signalMaskDownloadSuccess = downloadFileLengthSignalMask | downloadFileCompleteSignalMask;
// Send a bogus path // Send a bogus path
// We should get a single resetStatusMessages signal
// We should get a single errorMessage signal
_fileManager->downloadPath("bogus", QDir::temp()); _fileManager->downloadPath("bogus", QDir::temp());
QCOMPARE(_multiSpy->checkOnlySignalByMask(signalMaskOpenFailure), true); _multiSpy->waitForSignalByIndex(commandErrorSignalIndex, _ackTimerTimeoutMsecs);
QCOMPARE(_multiSpy->checkOnlySignalByMask(commandErrorSignalMask), true);
_multiSpy->clearAllSignals(); _multiSpy->clearAllSignals();
QCOMPARE(resetSpy.count(), 0);
// Clean previous downloads // Clean previous downloads
for (size_t i=0; i<MockMavlinkFileServer::cFileTestCases; i++) { for (size_t i=0; i<MockLinkFileServer::cFileTestCases; i++) {
QString filePath = QDir::temp().absoluteFilePath(MockMavlinkFileServer::rgFileTestCases[i].filename); QString filePath = QDir::temp().absoluteFilePath(MockLinkFileServer::rgFileTestCases[i].filename);
if (QFile::exists(filePath)) { if (QFile::exists(filePath)) {
Q_ASSERT(QFile::remove(filePath)); Q_ASSERT(QFile::remove(filePath));
} }
} }
// We setup a spy on the Terminate command signal of the mock file server so that we can determine that a
// Terminate command was correctly sent after the Open/Read commands complete.
QSignalSpy terminateSpy(&_mockFileServer, SIGNAL(terminateCommandReceived()));
// Run through the set of file test cases // Run through the set of file test cases
for (size_t i=0; i<MockMavlinkFileServer::cFileTestCases; i++) { for (size_t i=0; i<MockLinkFileServer::cFileTestCases; i++) {
const MockMavlinkFileServer::FileTestCase* testCase = &MockMavlinkFileServer::rgFileTestCases[i]; const MockLinkFileServer::FileTestCase* testCase = &MockLinkFileServer::rgFileTestCases[i];
// Run through the various failure modes for this test case // Run through the various failure modes for this test case
for (size_t j=0; j<MockMavlinkFileServer::cFailureModes; j++) { for (size_t j=0; j<MockLinkFileServer::cFailureModes; j++) {
qDebug() << "Testing successful download";
MockMavlinkFileServer::ErrorMode_t errMode = MockMavlinkFileServer::rgFailureModes[j]; // Run what should be a successful file download test case. No servers errors are being simulated.
_fileServer->setErrorMode(MockLinkFileServer::errModeNone);
_fileManager->downloadPath(testCase->filename, QDir::temp());
QTest::qWait(_ackTimerTimeoutMsecs); // Let the file manager timeout
// This should be a succesful download
QCOMPARE(_multiSpy->checkOnlySignalByMask(commandCompleteSignalMask), true);
_multiSpy->clearAllSignals();
// We should get a single Reset command to close the session
QCOMPARE(resetSpy.count(), 1);
resetSpy.clear();
// Validate file contents
QString filePath = QDir::temp().absoluteFilePath(MockLinkFileServer::rgFileTestCases[i].filename);
_validateFileContents(filePath, MockLinkFileServer::rgFileTestCases[i].length);
MockLinkFileServer::ErrorMode_t errMode = MockLinkFileServer::rgFailureModes[j];
qDebug() << "Testing failure mode:" << errMode; qDebug() << "Testing failure mode:" << errMode;
_mockFileServer.setErrorMode(errMode); _fileServer->setErrorMode(errMode);
_fileManager->downloadPath(testCase->filename, QDir::temp()); _fileManager->downloadPath(testCase->filename, QDir::temp());
QTest::qWait(_ackTimerTimeoutMsecs); // Let the file manager timeout QTest::qWait(_ackTimerTimeoutMsecs); // Let the file manager timeout
if (errMode == MockMavlinkFileServer::errModeNoSecondResponse || errMode == MockMavlinkFileServer::errModeNakSecondResponse) { if (errMode == MockLinkFileServer::errModeNakResponse) {
// For simulated server errors on subsequent Acks, the first Ack will go through. We must handle things differently depending // This will Nak the Open call which will fail the download, but not cause a Reset
// on whether the downloaded file requires multiple packets to complete the download. QCOMPARE(_multiSpy->checkOnlySignalByMask(commandErrorSignalMask), true);
if (testCase->fMultiPacketResponse) { QCOMPARE(resetSpy.count(), 0);
// The downloaded file requires multiple Acks to complete. Hence first Read should have succeeded and sent one downloadFileComplete. } else {
// Second Read should have failed. if (testCase->packetCount == 1 && (errMode == MockLinkFileServer::errModeNoSecondResponse || errMode == MockLinkFileServer::errModeNakSecondResponse)) {
QCOMPARE(_multiSpy->checkOnlySignalByMask(signalMaskReadFailure), true);
// Open command succeeded, so we should get a Terminate for the open
QCOMPARE(terminateSpy.count(), 1);
} else {
// The downloaded file fits within a single Ack response, hence there is no second Read issued. // The downloaded file fits within a single Ack response, hence there is no second Read issued.
// This should result in a successful download. // This should result in a successful download, followed by a Reset
QCOMPARE(_multiSpy->checkOnlySignalByMask(signalMaskDownloadSuccess), true); QCOMPARE(_multiSpy->checkOnlySignalByMask(commandCompleteSignalMask), true);
QCOMPARE(resetSpy.count(), 1);
// We should get a single Terminate command to close the Open session
QCOMPARE(terminateSpy.count(), 1);
// Validate file contents // Validate file contents
QString filePath = QDir::temp().absoluteFilePath(testCase->filename); QString filePath = QDir::temp().absoluteFilePath(testCase->filename);
_validateFileContents(filePath, testCase->length); _validateFileContents(filePath, testCase->length);
} else {
// Download should have failed, followed by a aReset
QCOMPARE(_multiSpy->checkOnlySignalByMask(commandErrorSignalMask), true);
QCOMPARE(resetSpy.count(), 1);
} }
} else {
// For all the other simulated server errors the Open command should have failed. Since the Open failed
// there is no session to terminate, hence no Terminate in this case.
QCOMPARE(_multiSpy->checkOnlySignalByMask(signalMaskOpenFailure), true);
QCOMPARE(terminateSpy.count(), 0);
} }
// Cleanup for next iteration // Cleanup for next iteration
_multiSpy->clearAllSignals(); _multiSpy->clearAllSignals();
terminateSpy.clear(); resetSpy.clear();
_mockFileServer.setErrorMode(MockMavlinkFileServer::errModeNone); _fileServer->setErrorMode(MockLinkFileServer::errModeNone);
} }
// Run what should be a successful file download test case. No servers errors are being simulated.
_mockFileServer.setErrorMode(MockMavlinkFileServer::errModeNone);
_fileManager->downloadPath(testCase->filename, QDir::temp());
// This should be a succesful download
QCOMPARE(_multiSpy->checkOnlySignalByMask(signalMaskDownloadSuccess), true);
// Make sure the file length coming back through the openFileLength signal is correct
QVERIFY(_multiSpy->getSpyByIndex(downloadFileLengthSignalIndex)->takeFirst().at(0).toInt() == testCase->length);
_multiSpy->clearAllSignals();
// We should get a single Terminate command to close the session
QCOMPARE(terminateSpy.count(), 1);
terminateSpy.clear();
// Validate file contents
QString filePath = QDir::temp().absoluteFilePath(MockMavlinkFileServer::rgFileTestCases[i].filename);
_validateFileContents(filePath, MockMavlinkFileServer::rgFileTestCases[i].length);
} }
} }
#endif
void FileManagerTest::_streamDownloadTest(void) void FileManagerTest::_streamDownloadTest(void)
{ {
Q_ASSERT(_fileManager); Q_ASSERT(_fileManager);
Q_ASSERT(_multiSpy); Q_ASSERT(_multiSpy);
Q_ASSERT(_multiSpy->checkNoSignals() == true); Q_ASSERT(_multiSpy->checkNoSignals() == true);
// FileManager::streamPath works as follows: // We setup a spy on the Reset command signal of the mock file server so that we can determine that a
// Sends an Open Command to the server // Reset command was correctly sent after the Open/Read commands complete.
// Expects an Ack Response back from the server with the correct sequence numner QSignalSpy resetSpy(_fileServer, SIGNAL(resetCommandReceived()));
// Emits an errorMessage signal if it gets a Nak back
// Emits an downloadFileLength signal with the file length if it gets back a good Ack // Send a bogus path
// Sends a single Stream command to the server _fileManager->streamPath("bogus", QDir::temp());
// Expects continuous Ack responses back with file contents _multiSpy->waitForSignalByIndex(commandErrorSignalIndex, _ackTimerTimeoutMsecs);
// Emits a downloadFileProgress for each ack it gets back QCOMPARE(_multiSpy->checkOnlySignalByMask(commandErrorSignalMask), true);
// Sends Terminate command to server when download is complete to close Open command _multiSpy->clearAllSignals();
// Mock file server will signal terminateCommandReceived when it gets a Terminate command QCOMPARE(resetSpy.count(), 0);
// Sends downloadFileComplete signal to indicate the download is complete
// Emits an errorMessage signal if sequence number is incorrrect on any response // Clean previous downloads
// Emits an errorMessage signal if CRC is incorrect on any responses for (size_t i=0; i<MockLinkFileServer::cFileTestCases; i++) {
QString filePath = QDir::temp().absoluteFilePath(MockLinkFileServer::rgFileTestCases[i].filename);
// Expected signals if the Open command fails for any reason if (QFile::exists(filePath)) {
quint16 signalMaskOpenFailure = errorMessageSignalMask; Q_ASSERT(QFile::remove(filePath));
}
// Expected signals if the Read command fails for any reason }
quint16 signalMaskReadFailure = downloadFileLengthSignalMask | errorMessageSignalMask;
// Run through the set of file test cases
// Expected signals if the downloadPath command succeeds for (size_t i=0; i<MockLinkFileServer::cFileTestCases; i++) {
quint16 signalMaskDownloadSuccess = downloadFileLengthSignalMask | downloadFileCompleteSignalMask; const MockLinkFileServer::FileTestCase* testCase = &MockLinkFileServer::rgFileTestCases[i];
// Send a bogus path // Run through the various failure modes for this test case
// We should get a single resetStatusMessages signal for (size_t j=0; j<MockLinkFileServer::cFailureModes; j++) {
// We should get a single errorMessage signal qDebug() << "Testing successful download";
_fileManager->streamPath("bogus", QDir::temp());
QCOMPARE(_multiSpy->checkOnlySignalByMask(signalMaskOpenFailure), true);
_multiSpy->clearAllSignals();
// Clean previous downloads
for (size_t i=0; i<MockMavlinkFileServer::cFileTestCases; i++) {
QString filePath = QDir::temp().absoluteFilePath(MockMavlinkFileServer::rgFileTestCases[i].filename);
if (QFile::exists(filePath)) {
Q_ASSERT(QFile::remove(filePath));
}
}
// We setup a spy on the Terminate command signal of the mock file server so that we can determine that a
// Terminate command was correctly sent after the Open/Read commands complete.
QSignalSpy terminateSpy(&_mockFileServer, SIGNAL(terminateCommandReceived()));
// Run through the set of file test cases
for (size_t i=0; i<MockMavlinkFileServer::cFileTestCases; i++) {
const MockMavlinkFileServer::FileTestCase* testCase = &MockMavlinkFileServer::rgFileTestCases[i];
// Run through the various failure modes for this test case
for (size_t j=0; j<MockMavlinkFileServer::cFailureModes; j++) {
MockMavlinkFileServer::ErrorMode_t errMode = MockMavlinkFileServer::rgFailureModes[j];
qDebug() << "Testing failure mode:" << errMode;
_mockFileServer.setErrorMode(errMode);
_fileManager->streamPath(testCase->filename, QDir::temp());
QTest::qWait(_ackTimerTimeoutMsecs); // Let the file manager timeout
if (errMode == MockMavlinkFileServer::errModeNoSecondResponse || errMode == MockMavlinkFileServer::errModeNakSecondResponse) {
// For simulated server errors on subsequent Acks, the first Ack will go through. We must handle things differently depending
// on whether the downloaded file requires multiple packets to complete the download.
if (testCase->packetCount != 1) {
// The downloaded file requires multiple Acks to complete. Second Ack should have failed.
QCOMPARE(_multiSpy->checkOnlySignalByMask(signalMaskReadFailure), true);
// Open command succeeded, so we should get a Terminate for the open
QCOMPARE(terminateSpy.count(), 1);
} else {
// The downloaded file fits within a single Ack response, hence there is no second Read issued.
// This should result in a successful download.
QCOMPARE(_multiSpy->checkOnlySignalByMask(signalMaskDownloadSuccess), true);
// We should get a single Terminate command to close the Open session
QCOMPARE(terminateSpy.count(), 1);
// Validate file contents
QString filePath = QDir::temp().absoluteFilePath(testCase->filename);
_validateFileContents(filePath, testCase->length);
}
} else {
// For all the other simulated server errors the Open command should have failed. Since the Open failed
// there is no session to terminate, hence no Terminate in this case.
QCOMPARE(_multiSpy->checkOnlySignalByMask(signalMaskOpenFailure), true);
QCOMPARE(terminateSpy.count(), 0);
}
// Cleanup for next iteration // Run what should be a successful file download test case. No servers errors are being simulated.
_multiSpy->clearAllSignals(); _fileServer->setErrorMode(MockLinkFileServer::errModeNone);
terminateSpy.clear(); _fileManager->streamPath(testCase->filename, QDir::temp());
_mockFileServer.setErrorMode(MockMavlinkFileServer::errModeNone); QTest::qWait(_ackTimerTimeoutMsecs); // Let the file manager timeout
}
// This should be a succesful download
// Run what should be a successful file download test case. No servers errors are being simulated. QCOMPARE(_multiSpy->checkOnlySignalByMask(commandCompleteSignalMask), true);
_mockFileServer.setErrorMode(MockMavlinkFileServer::errModeNone); _multiSpy->clearAllSignals();
_fileManager->streamPath(testCase->filename, QDir::temp());
// We should get a single Reset command to close the session
// This should be a succesful download QCOMPARE(resetSpy.count(), 1);
QCOMPARE(_multiSpy->checkOnlySignalByMask(signalMaskDownloadSuccess), true); resetSpy.clear();
// Make sure the file length coming back through the openFileLength signal is correct // Validate file contents
QVERIFY(_multiSpy->getSpyByIndex(downloadFileLengthSignalIndex)->takeFirst().at(0).toInt() == testCase->length); QString filePath = QDir::temp().absoluteFilePath(MockLinkFileServer::rgFileTestCases[i].filename);
_validateFileContents(filePath, MockLinkFileServer::rgFileTestCases[i].length);
_multiSpy->clearAllSignals(); MockLinkFileServer::ErrorMode_t errMode = MockLinkFileServer::rgFailureModes[j];
// We should get a single Terminate command to close the session qDebug() << "Testing failure mode:" << errMode;
QCOMPARE(terminateSpy.count(), 1); _fileServer->setErrorMode(errMode);
terminateSpy.clear();
_fileManager->downloadPath(testCase->filename, QDir::temp());
// Validate file contents QTest::qWait(_ackTimerTimeoutMsecs); // Let the file manager timeout
QString filePath = QDir::temp().absoluteFilePath(MockMavlinkFileServer::rgFileTestCases[i].filename);
_validateFileContents(filePath, MockMavlinkFileServer::rgFileTestCases[i].length); if (errMode == MockLinkFileServer::errModeNakResponse) {
} // This will Nak the Open call which will fail the download, but not cause a Reset
QCOMPARE(_multiSpy->checkOnlySignalByMask(commandErrorSignalMask), true);
QCOMPARE(resetSpy.count(), 0);
} else {
if (testCase->packetCount == 1 && (errMode == MockLinkFileServer::errModeNoSecondResponse || errMode == MockLinkFileServer::errModeNakSecondResponse)) {
// The downloaded file fits within a single Ack response, hence there is no second Read issued.
// This should result in a successful download, followed by a Reset
QCOMPARE(_multiSpy->checkOnlySignalByMask(commandCompleteSignalMask), true);
QCOMPARE(resetSpy.count(), 1);
// Validate file contents
QString filePath = QDir::temp().absoluteFilePath(testCase->filename);
_validateFileContents(filePath, testCase->length);
} else {
// Download should have failed, followed by a aReset
QCOMPARE(_multiSpy->checkOnlySignalByMask(commandErrorSignalMask), true);
QCOMPARE(resetSpy.count(), 1);
}
}
// Cleanup for next iteration
_multiSpy->clearAllSignals();
resetSpy.clear();
_fileServer->setErrorMode(MockLinkFileServer::errModeNone);
}
}
} }
void FileManagerTest::_validateFileContents(const QString& filePath, uint8_t length) void FileManagerTest::_validateFileContents(const QString& filePath, uint8_t length)
...@@ -481,3 +412,4 @@ void FileManagerTest::_validateFileContents(const QString& filePath, uint8_t len ...@@ -481,3 +412,4 @@ void FileManagerTest::_validateFileContents(const QString& filePath, uint8_t len
QCOMPARE((uint8_t)bytes[i], (uint8_t)(i & 0xFF)); QCOMPARE((uint8_t)bytes[i], (uint8_t)(i & 0xFF));
} }
} }
#endif
...@@ -28,9 +28,8 @@ ...@@ -28,9 +28,8 @@
#include <QtTest/QtTest> #include <QtTest/QtTest>
#include "UnitTest.h" #include "UnitTest.h"
#include "MockUAS.h"
#include "MockMavlinkFileServer.h"
#include "FileManager.h" #include "FileManager.h"
#include "MockLink.h"
#include "MultiSignalSpy.h" #include "MultiSignalSpy.h"
/// @file /// @file
...@@ -47,18 +46,13 @@ public: ...@@ -47,18 +46,13 @@ public:
private slots: private slots:
// Test case initialization // Test case initialization
void initTestCase(void);
void cleanupTestCase(void);
void init(void); void init(void);
void cleanup(void); void cleanup(void);
// Test cases // Test cases
void _ackTest(void); void _ackTest(void);
void _noAckTest(void); void _noAckTest(void);
void _resetTest(void);
void _listTest(void); void _listTest(void);
void _readDownloadTest(void);
void _streamDownloadTest(void);
// Connected to FileManager listEntry signal // Connected to FileManager listEntry signal
void listEntry(const QString& entry); void listEntry(const QString& entry);
...@@ -76,16 +70,15 @@ private: ...@@ -76,16 +70,15 @@ private:
enum { enum {
listEntrySignalMask = 1 << listEntrySignalIndex, listEntrySignalMask = 1 << listEntrySignalIndex,
commandCompleteSignalMask = 1 << commandCompleteSignalIndex, commandCompleteSignalMask = 1 << commandCompleteSignalIndex,
commandErrorSignalMask = 1 << errorMessageSignalIndex, commandErrorSignalMask = 1 << commandErrorSignalIndex,
}; };
static const uint8_t _systemIdQGC = 255; static const uint8_t _systemIdQGC = 255;
static const uint8_t _systemIdServer = 128; static const uint8_t _systemIdServer = 128;
MockUAS* _mockUAS; MockLink* _mockLink;
MockMavlinkFileServer _mockFileServer; MockLinkFileServer* _fileServer;
FileManager* _fileManager;
FileManager* _fileManager;
MultiSignalSpy* _multiSpy; MultiSignalSpy* _multiSpy;
static const size_t _cSignals = maxSignalIndex; static const size_t _cSignals = maxSignalIndex;
......
//
// MockMavlinkInterface.cc
// QGroundControl
//
// Created by Donald Gagne on 6/19/14.
// Copyright (c) 2014 Donald Gagne. All rights reserved.
//
#include "MockMavlinkInterface.h"
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include <QObject>
#include "QGCMAVLink.h"
#include "LinkInterface.h"
#ifndef MOCKMAVLINKINTERFACE_H
#define MOCKMAVLINKINTERFACE_H
class MockMavlinkInterface : public QObject
{
Q_OBJECT
public:
virtual void sendMessage(mavlink_message_t message) = 0;
signals:
// link argument will always be NULL
void messageReceived(LinkInterface* link, mavlink_message_t message);
};
#endif
...@@ -32,13 +32,13 @@ ...@@ -32,13 +32,13 @@
QGC_LOGGING_CATEGORY(FileManagerLog, "FileManagerLog") QGC_LOGGING_CATEGORY(FileManagerLog, "FileManagerLog")
FileManager::FileManager(QObject* parent, UASInterface* uas, uint8_t unitTestSystemIdQGC) : FileManager::FileManager(QObject* parent, UASInterface* uas) :
QObject(parent), QObject(parent),
_currentOperation(kCOIdle), _currentOperation(kCOIdle),
_mav(uas), _mav(uas),
_lastOutgoingSeqNumber(0), _lastOutgoingSeqNumber(0),
_activeSession(0), _activeSession(0),
_systemIdQGC(unitTestSystemIdQGC) _systemIdQGC(0)
{ {
connect(&_ackTimer, &QTimer::timeout, this, &FileManager::_ackTimeout); connect(&_ackTimer, &QTimer::timeout, this, &FileManager::_ackTimeout);
...@@ -104,8 +104,8 @@ void FileManager::_closeDownloadSession(bool success) ...@@ -104,8 +104,8 @@ void FileManager::_closeDownloadSession(bool success)
emit commandComplete(); emit commandComplete();
} }
// If !success error is emitted elsewhere _readFileAccumulator.clear();
// Close the open session // Close the open session
_sendResetCommand(); _sendResetCommand();
} }
...@@ -153,24 +153,19 @@ void FileManager::_downloadAckResponse(Request* readAck, bool readFile) ...@@ -153,24 +153,19 @@ void FileManager::_downloadAckResponse(Request* readAck, bool readFile)
emit commandProgress(100 * ((float)_readFileAccumulator.length() / (float)_downloadFileSize)); emit commandProgress(100 * ((float)_readFileAccumulator.length() / (float)_downloadFileSize));
} }
if (readAck->hdr.size == sizeof(readAck->data)) { if (readFile || readAck->hdr.burstComplete) {
if (readFile || readAck->hdr.burstComplete) { // Possibly still more data to read, send next read request
// Possibly still more data to read, send next read request
Request request; Request request;
request.hdr.session = _activeSession; request.hdr.session = _activeSession;
request.hdr.opcode = readFile ? kCmdReadFile : kCmdBurstReadFile; request.hdr.opcode = readFile ? kCmdReadFile : kCmdBurstReadFile;
request.hdr.offset = _downloadOffset; request.hdr.offset = _downloadOffset;
request.hdr.size = 0; request.hdr.size = 0;
_sendRequest(&request); _sendRequest(&request);
} else { } else if (!readFile) {
// Streaming, so next ack should come automatically // Streaming, so next ack should come automatically
_setupAckTimeout(); _setupAckTimeout();
}
} else if (readFile) {
// We only receieved a partial buffer back. These means we are at EOF
_closeDownloadSession(true /* success */);
} }
} }
...@@ -340,7 +335,30 @@ void FileManager::receiveMessage(LinkInterface* link, mavlink_message_t message) ...@@ -340,7 +335,30 @@ void FileManager::receiveMessage(LinkInterface* link, mavlink_message_t message)
// Make sure we have a good sequence number // Make sure we have a good sequence number
uint16_t expectedSeqNumber = _lastOutgoingSeqNumber + 1; uint16_t expectedSeqNumber = _lastOutgoingSeqNumber + 1;
if (incomingSeqNumber != expectedSeqNumber) { if (incomingSeqNumber != expectedSeqNumber) {
_currentOperation = kCOIdle; switch (_currentOperation) {
case kCOBurst:
case kCORead:
_closeDownloadSession(false /* failure */);
break;
case kCOWrite:
_closeUploadSession(false /* failure */);
break;
case kCOOpenRead:
case kCOOpenBurst:
case kCOCreate:
// We could have an open session hanging around
_currentOperation = kCOIdle;
_sendResetCommand();
break;
default:
// Don't need to do anything special
_currentOperation = kCOIdle;
break;
}
_emitErrorMessage(tr("Bad sequence number on received message: expected(%1) received(%2)").arg(expectedSeqNumber).arg(incomingSeqNumber)); _emitErrorMessage(tr("Bad sequence number on received message: expected(%1) received(%2)").arg(expectedSeqNumber).arg(incomingSeqNumber));
return; return;
} }
...@@ -457,12 +475,22 @@ void FileManager::_sendListCommand(void) ...@@ -457,12 +475,22 @@ void FileManager::_sendListCommand(void)
void FileManager::downloadPath(const QString& from, const QDir& downloadDir) void FileManager::downloadPath(const QString& from, const QDir& downloadDir)
{ {
if (_currentOperation != kCOIdle) {
_emitErrorMessage(tr("Command not sent. Waiting for previous command to complete."));
return;
}
qCDebug(FileManagerLog) << "downloadPath from:" << from << "to:" << downloadDir; qCDebug(FileManagerLog) << "downloadPath from:" << from << "to:" << downloadDir;
_downloadWorker(from, downloadDir, true /* read file */); _downloadWorker(from, downloadDir, true /* read file */);
} }
void FileManager::streamPath(const QString& from, const QDir& downloadDir) void FileManager::streamPath(const QString& from, const QDir& downloadDir)
{ {
if (_currentOperation != kCOIdle) {
_emitErrorMessage(tr("Command not sent. Waiting for previous command to complete."));
return;
}
qCDebug(FileManagerLog) << "streamPath from:" << from << "to:" << downloadDir; qCDebug(FileManagerLog) << "streamPath from:" << from << "to:" << downloadDir;
_downloadWorker(from, downloadDir, false /* stream file */); _downloadWorker(from, downloadDir, false /* stream file */);
} }
...@@ -677,7 +705,6 @@ void FileManager::_emitListEntry(const QString& entry) ...@@ -677,7 +705,6 @@ void FileManager::_emitListEntry(const QString& entry)
/// @brief Sends the specified Request out to the UAS. /// @brief Sends the specified Request out to the UAS.
void FileManager::_sendRequest(Request* request) void FileManager::_sendRequest(Request* request)
{ {
qCDebug(FileManagerLog) << "_sendRequest opcode:" << request->hdr.opcode;
mavlink_message_t message; mavlink_message_t message;
...@@ -687,6 +714,8 @@ void FileManager::_sendRequest(Request* request) ...@@ -687,6 +714,8 @@ void FileManager::_sendRequest(Request* request)
request->hdr.seqNumber = _lastOutgoingSeqNumber; request->hdr.seqNumber = _lastOutgoingSeqNumber;
qCDebug(FileManagerLog) << "_sendRequest opcode:" << request->hdr.opcode << "seqNumber:" << request->hdr.seqNumber;
if (_systemIdQGC == 0) { if (_systemIdQGC == 0) {
_systemIdQGC = MAVLinkProtocol::instance()->getSystemId(); _systemIdQGC = MAVLinkProtocol::instance()->getSystemId();
} }
......
...@@ -37,12 +37,11 @@ class FileManager : public QObject ...@@ -37,12 +37,11 @@ class FileManager : public QObject
Q_OBJECT Q_OBJECT
public: public:
FileManager(QObject* parent, UASInterface* uas, uint8_t unitTestSystemIdQGC = 0); FileManager(QObject* parent, UASInterface* uas);
/// These methods are only used for testing purposes. /// These methods are only used for testing purposes.
bool _sendCmdTestAck(void) { return _sendOpcodeOnlyCmd(kCmdNone, kCOAck); }; bool _sendCmdTestAck(void) { return _sendOpcodeOnlyCmd(kCmdNone, kCOAck); };
bool _sendCmdTestNoAck(void) { return _sendOpcodeOnlyCmd(kCmdTestNoAck, kCOAck); }; bool _sendCmdTestNoAck(void) { return _sendOpcodeOnlyCmd(kCmdTestNoAck, kCOAck); };
bool _sendCmdReset(void) { return _sendOpcodeOnlyCmd(kCmdResetSessions, kCOAck); };
/// Timeout in msecs to wait for an Ack time come back. This is public so we can write unit tests which wait long enough /// Timeout in msecs to wait for an Ack time come back. This is public so we can write unit tests which wait long enough
/// for the FileManager to timeout. /// for the FileManager to timeout.
...@@ -226,9 +225,9 @@ private: ...@@ -226,9 +225,9 @@ private:
uint8_t _systemIdQGC; ///< System ID for QGC uint8_t _systemIdQGC; ///< System ID for QGC
uint8_t _systemIdServer; ///< System ID for server uint8_t _systemIdServer; ///< System ID for server
// We give MockMavlinkFileServer friend access so that it can use the data structures and opcodes // We give MockLinkFileServer friend access so that it can use the data structures and opcodes
// to build a mock mavlink file server for testing. // to build a mock mavlink file server for testing.
friend class MockMavlinkFileServer; friend class MockLinkFileServer;
}; };
#endif // FileManager_H #endif // FileManager_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment