Commit d778af3b authored by Christian Kandeler's avatar Christian Kandeler
Browse files

SSH: Finish Implementing SFTP test.

parent 3d90463d
......@@ -160,12 +160,16 @@ SshConnection::~SshConnection()
QSharedPointer<SshRemoteProcess> SshConnection::createRemoteProcess(const QByteArray &command)
{
// TODO: Is this conditonal return value really a good idea?
// Get rid of this IF we can prove that no harm is done by returning
// a non-working (but non-null) process pointer.
return state() == Connected
? d->createRemoteProcess(command) : QSharedPointer<SshRemoteProcess>();
}
QSharedPointer<SftpChannel> SshConnection::createSftpChannel()
{
// TODO: See above
return state() == Connected
? d->createSftpChannel() : QSharedPointer<SftpChannel>();
}
......
#include <coreplugin/ssh/sftpchannel.h>
#include <coreplugin/ssh/sshconnection.h>
#include <coreplugin/ssh/sshremoteprocess.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QList>
#include <QtCore/QObject>
#include <QtCore/QPair>
#include <QtCore/QTimer>
using namespace Core;
class Test : public QObject {
Q_OBJECT
public:
Test()
{
m_timeoutTimer.setSingleShot(true);
m_connection = SshConnection::create();
if (m_connection->state() != SshConnection::Unconnected) {
qDebug("Error: Newly created SSH connection has state %d.",
m_connection->state());
}
if (m_connection->createRemoteProcess(""))
qDebug("Error: Unconnected SSH connection creates remote process.");
if (m_connection->createSftpChannel())
qDebug("Error: Unconnected SSH connection creates SFTP channel.");
SshConnectionParameters noHost;
noHost.host = QLatin1String("hgdfxgfhgxfhxgfchxgcf");
noHost.port = 12345;
noHost.timeout = 10;
SshConnectionParameters noUser;
noUser.host = QLatin1String("localhost");
noUser.port = 22;
noUser.timeout = 30;
noUser.authType = SshConnectionParameters::AuthByPwd;
noUser.uname = QLatin1String("dumdidumpuffpuff");
noUser.uname = QLatin1String("whatever");
SshConnectionParameters wrongPwd;
wrongPwd.host = QLatin1String("localhost");
wrongPwd.port = 22;
wrongPwd.timeout = 30;
wrongPwd.authType = SshConnectionParameters::AuthByPwd;
wrongPwd.uname = QLatin1String("root");
noUser.uname = QLatin1String("thiscantpossiblybeapasswordcanit");
SshConnectionParameters invalidKeyFile;
invalidKeyFile.host = QLatin1String("localhost");
invalidKeyFile.port = 22;
invalidKeyFile.timeout = 30;
invalidKeyFile.authType = SshConnectionParameters::AuthByKey;
invalidKeyFile.uname = QLatin1String("root");
invalidKeyFile.privateKeyFile
= QLatin1String("somefilenamethatwedontexpecttocontainavalidkey");
// TODO: Create a valid key file and check for authentication error.
m_testSet << TestItem("Behavior with non-existing host",
noHost, ErrorList() << SshSocketError);
m_testSet << TestItem("Behavior with non-existing user", noUser,
ErrorList() << SshSocketError << SshTimeoutError
<< SshAuthenticationError);
m_testSet << TestItem("Behavior with wrong password", wrongPwd,
ErrorList() << SshSocketError << SshTimeoutError
<< SshAuthenticationError);
m_testSet << TestItem("Behavior with invalid key file", invalidKeyFile,
ErrorList() << SshSocketError << SshTimeoutError
<< SshKeyFileError);
runNextTest();
}
~Test();
private slots:
void handleConnected()
{
qDebug("Error: Received unexpected connected() signal.");
qApp->quit();
}
void handleDisconnected()
{
qDebug("Error: Received unexpected disconnected() signal.");
qApp->quit();
}
void handleDataAvailable(const QString &msg)
{
qDebug("Error: Received unexpected dataAvailable() signal. "
"Message was: '%s'.", qPrintable(msg));
qApp->quit();
}
void handleError(SshError error)
{
if (m_testSet.isEmpty()) {
qDebug("Error: Received error %d, but no test was running.", error);
qApp->quit();
}
const TestItem testItem = m_testSet.takeFirst();
if (testItem.allowedErrors.contains(error)) {
qDebug("Received error %d, as expected.", error);
if (m_testSet.isEmpty()) {
qDebug("All tests finished successfully.");
qApp->quit();
} else {
runNextTest();
}
} else {
qDebug("Received unexpected error %d.", error);
qApp->quit();
}
}
void handleTimeout()
{
if (m_testSet.isEmpty()) {
qDebug("Error: timeout, but no test was running.");
qApp->quit();
}
const TestItem testItem = m_testSet.takeFirst();
qDebug("Error: The following test timed out: %s", testItem.description);
}
private:
void runNextTest()
{
if (m_connection)
disconnect(m_connection.data(), 0, this, 0);
m_connection = SshConnection::create();
connect(m_connection.data(), SIGNAL(connected()), this,
SLOT(handleConnected()));
connect(m_connection.data(), SIGNAL(disconnected()), this,
SLOT(handleDisconnected()));
connect(m_connection.data(), SIGNAL(dataAvailable(QString)), this,
SLOT(handleDataAvailable(QString)));
connect(m_connection.data(), SIGNAL(error(SshError)), this,
SLOT(handleError(SshError)));
const TestItem &nextItem = m_testSet.first();
m_timeoutTimer.stop();
m_timeoutTimer.setInterval(qMax(10000, nextItem.params.timeout * 1000));
qDebug("Testing: %s", nextItem.description);
m_connection->connectToHost(m_testSet.first().params);
}
SshConnection::Ptr m_connection;
typedef QList<SshError> ErrorList;
struct TestItem {
TestItem(const char *d, const SshConnectionParameters &p,
const ErrorList &e) : description(d), params(p), allowedErrors(e) {}
const char *description;
SshConnectionParameters params;
ErrorList allowedErrors;
};
QList<TestItem> m_testSet;
QTimer m_timeoutTimer;
};
Test::~Test() {}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Test t;
return a.exec();
}
#include "main.moc"
......@@ -80,7 +80,7 @@ void SftpTest::handleDisconnected()
void SftpTest::handleError()
{
std::cerr << "Encountered SSH error "
std::cerr << "Encountered SSH error: "
<< qPrintable(m_connection->errorString()) << "." << std::endl;
qApp->quit();
}
......@@ -103,7 +103,7 @@ void SftpTest::handleChannelInitialized()
const FilePtr file(new QFile(QDir::tempPath() + QLatin1Char('/')
+ fileName));
bool success = true;
if (!file->open(QIODevice::ReadWrite | QIODevice::Truncate))
if (!file->open(QIODevice::WriteOnly | QIODevice::Truncate))
success = false;
if (success) {
int content[1024/sizeof(int)];
......@@ -153,16 +153,12 @@ void SftpTest::handleChannelClosed()
std::cerr << "Unexpected state " << m_state << " in function "
<< Q_FUNC_INFO << "." << std::endl;
} else {
std::cout << "SFTP channel closed. Now disconnecting." << std::endl;
std::cout << "SFTP channel closed. Now disconnecting..." << std::endl;
}
m_state = Disconnecting;
m_connection->disconnectFromHost();
}
// compare the original to the downloaded files
// remove local files
// remove remote files
// then the same for a big N GB file
void SftpTest::handleJobFinished(Core::SftpJobId job, const QString &error)
{
switch (m_state) {
......@@ -170,7 +166,7 @@ void SftpTest::handleJobFinished(Core::SftpJobId job, const QString &error)
if (!handleJobFinished(job, m_smallFilesUploadJobs, error, "uploading"))
return;
if (m_smallFilesUploadJobs.isEmpty()) {
std::cout << "Uploading finished, now downloading for comparison."
std::cout << "Uploading finished, now downloading for comparison..."
<< std::endl;
foreach (const FilePtr &file, m_localSmallFiles) {
const QString localFilePath = file->fileName();
......@@ -195,7 +191,7 @@ void SftpTest::handleJobFinished(Core::SftpJobId job, const QString &error)
if (!handleJobFinished(job, m_smallFilesDownloadJobs, error, "downloading"))
return;
if (m_smallFilesDownloadJobs.isEmpty()) {
std::cout << "Downloading finished, now comparing." << std::endl;
std::cout << "Downloading finished, now comparing..." << std::endl;
foreach (const FilePtr &ptr, m_localSmallFiles) {
if (!ptr->open(QIODevice::ReadOnly)) {
std::cerr << "Error opening local file "
......@@ -215,7 +211,7 @@ void SftpTest::handleJobFinished(Core::SftpJobId job, const QString &error)
return;
}
std::cout << "Comparisons successful, now removing files."
std::cout << "Comparisons successful, now removing files..."
<< std::endl;
QList<QString> remoteFilePaths;
foreach (const FilePtr &ptr, m_localSmallFiles) {
......@@ -245,22 +241,135 @@ void SftpTest::handleJobFinished(Core::SftpJobId job, const QString &error)
if (!handleJobFinished(job, m_smallFilesRemovalJobs, error, "removing"))
return;
if (m_smallFilesRemovalJobs.isEmpty()) {
// TODO: create and upload big file
std::cout << "Files successfully removed. "
<< "Now closing the SFTP channel." << std::endl;
m_channel->closeChannel();
m_state = ChannelClosing;
std::cout << "Small files successfully removed. "
<< "Now creating big file..." << std::endl;
const QLatin1String bigFileName("sftpbigfile");
m_localBigFile = FilePtr(new QFile(QDir::tempPath()
+ QLatin1Char('/') + bigFileName));
bool success = m_localBigFile->open(QIODevice::WriteOnly);
const int blockSize = 8192;
const int blockCount = m_parameters.bigFileSize*1024*1024/blockSize;
for (int block = 0; block < blockCount; ++block) {
int content[blockSize/sizeof(int)];
for (size_t j = 0; j < sizeof content / sizeof content[0]; ++j)
content[j] = qrand();
m_localBigFile->write(reinterpret_cast<char *>(content),
sizeof content);
}
m_localBigFile->close();
success = success && m_localBigFile->error() == QFile::NoError;
if (!success) {
std::cerr << "Error trying to create big file '"
<< qPrintable(m_localBigFile->fileName()) << "'."
<< std::endl;
earlyDisconnectFromHost();
return;
}
std::cout << "Big file created. Now uploading ..." << std::endl;
m_bigJobTimer.start();
m_bigFileUploadJob
= m_channel->uploadFile(m_localBigFile->fileName(),
remoteFilePath(bigFileName), SftpOverwriteExisting);
if (m_bigFileUploadJob == SftpInvalidJob) {
std::cerr << "Error uploading file '" << bigFileName.latin1()
<< "'." << std::endl;
earlyDisconnectFromHost();
return;
}
m_state = UploadingBig;
}
break;
case UploadingBig: {
if (!handleBigJobFinished(job, m_bigFileUploadJob, error, "uploading"))
return;
const qint64 msecs = m_bigJobTimer.elapsed();
std::cout << "Successfully uploaded big file. Took " << (msecs/1000)
<< " seconds for " << m_parameters.bigFileSize << " MB."
<< std::endl;
const QString localFilePath = m_localBigFile->fileName();
const QString downloadedFilePath = cmpFileName(localFilePath);
const QString remoteFp
= remoteFilePath(QFileInfo(localFilePath).fileName());
std::cout << "Now downloading big file for comparison..." << std::endl;
m_bigJobTimer.start();
m_bigFileDownloadJob = m_channel->downloadFile(remoteFp,
downloadedFilePath, SftpOverwriteExisting);
if (m_bigFileDownloadJob == SftpInvalidJob) {
std::cerr << "Error downloading remote file '"
<< qPrintable(remoteFp) << "' to local file '"
<< qPrintable(downloadedFilePath) << "'." << std::endl;
earlyDisconnectFromHost();
return;
}
m_state = DownloadingBig;
break;
}
case DownloadingBig: {
if (!handleBigJobFinished(job, m_bigFileDownloadJob, error, "downloading"))
return;
const qint64 msecs = m_bigJobTimer.elapsed();
std::cout << "Successfully downloaded big file. Took " << (msecs/1000)
<< " seconds for " << m_parameters.bigFileSize << " MB."
<< std::endl;
std::cout << "Now comparing big files..." << std::endl;
QFile downloadedFile(cmpFileName(m_localBigFile->fileName()));
if (!downloadedFile.open(QIODevice::ReadOnly)) {
std::cerr << "Error opening downloaded file '"
<< qPrintable(downloadedFile.fileName()) << "': "
<< qPrintable(downloadedFile.errorString()) << "." << std::endl;
earlyDisconnectFromHost();
return;
}
if (!m_localBigFile->open(QIODevice::ReadOnly)) {
std::cerr << "Error opening big file '"
<< qPrintable(m_localBigFile->fileName()) << "': "
<< qPrintable(m_localBigFile->errorString()) << "."
<< std::endl;
earlyDisconnectFromHost();
return;
}
if (!compareFiles(m_localBigFile.data(), &downloadedFile))
return;
std::cout << "Comparison successful. Now removing big files..."
<< std::endl;
if (!m_localBigFile->remove()) {
std::cerr << "Error: Could not remove file '"
<< qPrintable(m_localBigFile->fileName()) << "'." << std::endl;
earlyDisconnectFromHost();
return;
}
if (!downloadedFile.remove()) {
std::cerr << "Error: Could not remove file '"
<< qPrintable(downloadedFile.fileName()) << "'." << std::endl;
earlyDisconnectFromHost();
return;
}
const QString remoteFp
= remoteFilePath(QFileInfo(m_localBigFile->fileName()).fileName());
m_bigFileRemovalJob = m_channel->removeFile(remoteFp);
m_state = RemovingBig;
break;
}
case RemovingBig: {
if (!handleBigJobFinished(job, m_bigFileRemovalJob, error, "removing"))
return;
const QString remoteFp
= remoteFilePath(QFileInfo(m_localBigFile->fileName()).fileName());
std::cout << "Big files successfully removed. "
<< "Now closing the SFTP channel..." << std::endl;
m_state = ChannelClosing;
m_channel->closeChannel();
break;
}
case Disconnecting:
break;
case UploadingBig:
case DownloadingBig:
case RemovingBig:
default:
std::cerr << "Unexpected state " << m_state << " in function "
<< Q_FUNC_INFO << "." << std::endl;
earlyDisconnectFromHost();
if (!m_error) {
std::cerr << "Unexpected state " << m_state << " in function "
<< Q_FUNC_INFO << "." << std::endl;
earlyDisconnectFromHost();
}
}
}
......@@ -324,13 +433,34 @@ bool SftpTest::handleJobFinished(SftpJobId job, JobMap &jobMap,
return true;
}
bool SftpTest::handleBigJobFinished(SftpJobId job, SftpJobId expectedJob,
const QString &error, const char *activity)
{
if (job != expectedJob) {
std::cerr << "Error " << activity << " file '"
<< qPrintable(m_localBigFile->fileName())
<< "': Expected job id " << expectedJob
<< ", got job id " << job << '.' << std::endl;
earlyDisconnectFromHost();
return false;
}
if (!error.isEmpty()) {
std::cerr << "Error " << activity << " file '"
<< qPrintable(m_localBigFile->fileName()) << "': "
<< qPrintable(error) << std::endl;
earlyDisconnectFromHost();
return false;
}
return true;
}
bool SftpTest::compareFiles(QFile *orig, QFile *copy)
{
bool success = orig->size() == copy->size();
qint64 bytesLeft = orig->size();
orig->seek(0);
while (success && bytesLeft > 0) {
const qint64 bytesToRead = qMin(bytesLeft, Q_INT64_C(64*1024));
const qint64 bytesToRead = qMin(bytesLeft, Q_INT64_C(1024*1024));
const QByteArray origBlock = orig->read(bytesToRead);
const QByteArray copyBlock = copy->read(bytesToRead);
if (origBlock.size() != bytesToRead || origBlock != copyBlock)
......
......@@ -6,6 +6,7 @@
#include <coreplugin/ssh/sftpchannel.h>
#include <coreplugin/ssh/sshconnection.h>
#include <QtCore/QElapsedTimer>
#include <QtCore/QHash>
#include <QtCore/QList>
#include <QtCore/QObject>
......@@ -45,6 +46,8 @@ private:
void earlyDisconnectFromHost();
bool handleJobFinished(Core::SftpJobId job, JobMap &jobMap,
const QString &error, const char *activity);
bool handleBigJobFinished(Core::SftpJobId job, Core::SftpJobId expectedJob,
const QString &error, const char *activity);
bool compareFiles(QFile *orig, QFile *copy);
const Parameters m_parameters;
......@@ -60,6 +63,7 @@ private:
Core::SftpJobId m_bigFileUploadJob;
Core::SftpJobId m_bigFileDownloadJob;
Core::SftpJobId m_bigFileRemovalJob;
QElapsedTimer m_bigJobTimer;
};
......
Supports Markdown
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