OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / tests / manual / ssh / sftp / sftptest.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "sftptest.h"
34
35 #include <QtCore/QCoreApplication>
36 #include <QtCore/QDateTime>
37 #include <QtCore/QDir>
38 #include <QtCore/QFile>
39 #include <QtCore/QFileInfo>
40
41 #include <iostream>
42
43 using namespace Utils;
44
45 SftpTest::SftpTest(const Parameters &params)
46     : m_parameters(params), m_state(Inactive), m_error(false),
47       m_bigFileUploadJob(SftpInvalidJob),
48       m_bigFileDownloadJob(SftpInvalidJob),
49       m_bigFileRemovalJob(SftpInvalidJob)
50 {
51 }
52
53 SftpTest::~SftpTest()
54 {
55     removeFiles(true);
56 }
57
58 void SftpTest::run()
59 {
60     m_connection = SshConnection::create();
61     connect(m_connection.data(), SIGNAL(connected()), this,
62         SLOT(handleConnected()));
63     connect(m_connection.data(), SIGNAL(error(Utils::SshError)), this,
64         SLOT(handleError()));
65     connect(m_connection.data(), SIGNAL(disconnected()), this,
66         SLOT(handleDisconnected()));
67     std::cout << "Connecting to host '"
68         << qPrintable(m_parameters.sshParams.host) << "'..." << std::endl;
69     m_state = Connecting;
70     m_connection->connectToHost(m_parameters.sshParams);
71 }
72
73 void SftpTest::handleConnected()
74 {
75     if (m_state != Connecting) {
76         std::cerr << "Unexpected state " << m_state << " in function "
77             << Q_FUNC_INFO << "." << std::endl;
78         earlyDisconnectFromHost();
79     } else {
80         std::cout << "Connected. Initializing SFTP channel..." << std::endl;
81         m_channel = m_connection->createSftpChannel();
82         connect(m_channel.data(), SIGNAL(initialized()), this,
83            SLOT(handleChannelInitialized()));
84         connect(m_channel.data(), SIGNAL(initializationFailed(QString)), this,
85             SLOT(handleChannelInitializationFailure(QString)));
86         connect(m_channel.data(), SIGNAL(finished(Utils::SftpJobId, QString)),
87             this, SLOT(handleJobFinished(Utils::SftpJobId, QString)));
88         connect(m_channel.data(), SIGNAL(closed()), this,
89             SLOT(handleChannelClosed()));
90         m_state = InitializingChannel;
91         m_channel->initialize();
92     }
93 }
94
95 void SftpTest::handleDisconnected()
96 {
97     if (m_state != Disconnecting) {
98         std::cerr << "Unexpected state " << m_state << " in function "
99             << Q_FUNC_INFO << std::endl;
100         m_error = true;
101     } else {
102         std::cout << "Connection closed." << std::endl;
103     }
104     std::cout << "Test finished. ";
105     if (m_error)
106         std::cout << "There were errors.";
107     else
108         std::cout << "No errors encountered.";
109     std::cout << std::endl;
110     qApp->quit();
111 }
112
113 void SftpTest::handleError()
114 {
115     std::cerr << "Encountered SSH error: "
116         << qPrintable(m_connection->errorString()) << "." << std::endl;
117     qApp->quit();
118 }
119
120 void SftpTest::handleChannelInitialized()
121 {
122     if (m_state != InitializingChannel) {
123         std::cerr << "Unexpected state " << m_state << "in function "
124             << Q_FUNC_INFO << "." << std::endl;
125         earlyDisconnectFromHost();
126         return;
127     }
128
129     std::cout << "Creating " << m_parameters.smallFileCount
130         << " files of 1 KB each ..." << std::endl;
131     qsrand(QDateTime::currentDateTime().toTime_t());
132     for (int i = 0; i < m_parameters.smallFileCount; ++i) {
133         const QString fileName
134             = QLatin1String("sftptestfile") + QString::number(i + 1);
135         const FilePtr file(new QFile(QDir::tempPath() + QLatin1Char('/')
136             + fileName));
137         bool success = true;
138         if (!file->open(QIODevice::WriteOnly | QIODevice::Truncate))
139             success = false;
140         if (success) {
141             int content[1024/sizeof(int)];
142             for (size_t j = 0; j < sizeof content / sizeof content[0]; ++j)
143                 content[j] = qrand();
144             file->write(reinterpret_cast<char *>(content), sizeof content);
145             file->close();
146         }
147         success = success && file->error() == QFile::NoError;
148         if (!success) {
149             std::cerr << "Error creating local file "
150                 << qPrintable(file->fileName()) << "." << std::endl;
151             earlyDisconnectFromHost();
152             return;
153         }
154         m_localSmallFiles << file;
155     }
156     std::cout << "Files created. Now uploading..." << std::endl;
157     foreach (const FilePtr &file, m_localSmallFiles) {
158         const QString localFilePath = file->fileName();
159         const QString remoteFp
160             = remoteFilePath(QFileInfo(localFilePath).fileName());
161         const SftpJobId uploadJob = m_channel->uploadFile(file->fileName(),
162             remoteFp, SftpOverwriteExisting);
163         if (uploadJob == SftpInvalidJob) {
164             std::cerr << "Error uploading local file "
165                 << qPrintable(localFilePath) << " to remote file "
166                 << qPrintable(remoteFp) << "." << std::endl;
167             earlyDisconnectFromHost();
168             return;
169         }
170         m_smallFilesUploadJobs.insert(uploadJob, remoteFp);
171     }
172     m_state = UploadingSmall;
173 }
174
175 void SftpTest::handleChannelInitializationFailure(const QString &reason)
176 {
177     std::cerr << "Could not initialize SFTP channel: " << qPrintable(reason)
178         << "." << std::endl;
179     earlyDisconnectFromHost();
180 }
181
182 void SftpTest::handleChannelClosed()
183 {
184     if (m_state != ChannelClosing) {
185         std::cerr << "Unexpected state " << m_state << " in function "
186             << Q_FUNC_INFO << "." << std::endl;
187     } else {
188         std::cout << "SFTP channel closed. Now disconnecting..." << std::endl;
189     }
190     m_state = Disconnecting;
191     m_connection->disconnectFromHost();
192 }
193
194 void SftpTest::handleJobFinished(Utils::SftpJobId job, const QString &error)
195 {
196     switch (m_state) {
197     case UploadingSmall:
198         if (!handleJobFinished(job, m_smallFilesUploadJobs, error, "uploading"))
199             return;
200         if (m_smallFilesUploadJobs.isEmpty()) {
201             std::cout << "Uploading finished, now downloading for comparison..."
202                 << std::endl;
203             foreach (const FilePtr &file, m_localSmallFiles) {
204                 const QString localFilePath = file->fileName();
205                 const QString remoteFp
206                     = remoteFilePath(QFileInfo(localFilePath).fileName());
207                 const QString downloadFilePath = cmpFileName(localFilePath);
208                 const SftpJobId downloadJob = m_channel->downloadFile(remoteFp,
209                     downloadFilePath, SftpOverwriteExisting);
210                 if (downloadJob == SftpInvalidJob) {
211                     std::cerr << "Error downloading remote file "
212                         << qPrintable(remoteFp) << " to local file "
213                         << qPrintable(downloadFilePath) << "." << std::endl;
214                     earlyDisconnectFromHost();
215                     return;
216                 }
217                 m_smallFilesDownloadJobs.insert(downloadJob, remoteFp);
218             }
219             m_state = DownloadingSmall;
220         }
221         break;
222     case DownloadingSmall:
223         if (!handleJobFinished(job, m_smallFilesDownloadJobs, error, "downloading"))
224             return;
225         if (m_smallFilesDownloadJobs.isEmpty()) {
226             std::cout << "Downloading finished, now comparing..." << std::endl;
227             foreach (const FilePtr &ptr, m_localSmallFiles) {
228                 if (!ptr->open(QIODevice::ReadOnly)) {
229                     std::cerr << "Error opening local file "
230                         << qPrintable(ptr->fileName()) << "." << std::endl;
231                     earlyDisconnectFromHost();
232                     return;
233                 }
234                 const QString downloadedFilePath = cmpFileName(ptr->fileName());
235                 QFile downloadedFile(downloadedFilePath);
236                 if (!downloadedFile.open(QIODevice::ReadOnly)) {
237                     std::cerr << "Error opening downloaded file "
238                         << qPrintable(downloadedFilePath) << "." << std::endl;
239                     earlyDisconnectFromHost();
240                     return;
241                 }
242                 if (!compareFiles(ptr.data(), &downloadedFile))
243                     return;
244             }
245
246             std::cout << "Comparisons successful, now removing files..."
247                 << std::endl;
248             QList<QString> remoteFilePaths;
249             foreach (const FilePtr &ptr, m_localSmallFiles) {
250                 const QString downloadedFilePath = cmpFileName(ptr->fileName());
251                 remoteFilePaths
252                     << remoteFilePath(QFileInfo(ptr->fileName()).fileName());
253                 if (!ptr->remove()) {
254                     std::cerr << "Error: Failed to remove local file '"
255                         << qPrintable(ptr->fileName()) << "'." << std::endl;
256                     earlyDisconnectFromHost();
257                 }
258                 if (!QFile::remove(downloadedFilePath)) {
259                     std::cerr << "Error: Failed to remove downloaded file '"
260                         << qPrintable(downloadedFilePath) << "'." << std::endl;
261                     earlyDisconnectFromHost();
262                 }
263             }
264             m_localSmallFiles.clear();
265             foreach (const QString &remoteFp, remoteFilePaths) {
266                 m_smallFilesRemovalJobs.insert(m_channel->removeFile(remoteFp),
267                     remoteFp);
268             }
269             m_state = RemovingSmall;
270         }
271         break;
272     case RemovingSmall:
273         if (!handleJobFinished(job, m_smallFilesRemovalJobs, error, "removing"))
274             return;
275         if (m_smallFilesRemovalJobs.isEmpty()) {
276             std::cout << "Small files successfully removed. "
277                 << "Now creating big file..." << std::endl;
278             const QLatin1String bigFileName("sftpbigfile");
279             m_localBigFile = FilePtr(new QFile(QDir::tempPath()
280                 + QLatin1Char('/') + bigFileName));
281             bool success = m_localBigFile->open(QIODevice::WriteOnly);
282             const int blockSize = 8192;
283             const int blockCount = m_parameters.bigFileSize*1024*1024/blockSize;
284             for (int block = 0; block < blockCount; ++block) {
285                 int content[blockSize/sizeof(int)];
286                 for (size_t j = 0; j < sizeof content / sizeof content[0]; ++j)
287                     content[j] = qrand();
288                 m_localBigFile->write(reinterpret_cast<char *>(content),
289                     sizeof content);
290             }
291             m_localBigFile->close();
292             success = success && m_localBigFile->error() == QFile::NoError;
293             if (!success) {
294                 std::cerr << "Error trying to create big file '"
295                     << qPrintable(m_localBigFile->fileName()) << "'."
296                     << std::endl;
297                 earlyDisconnectFromHost();
298                 return;
299             }
300
301             std::cout << "Big file created. Now uploading ..." << std::endl;
302             m_bigJobTimer.start();
303             m_bigFileUploadJob
304                 = m_channel->uploadFile(m_localBigFile->fileName(),
305                       remoteFilePath(bigFileName), SftpOverwriteExisting);
306             if (m_bigFileUploadJob == SftpInvalidJob) {
307                 std::cerr << "Error uploading file '" << bigFileName.latin1()
308                     << "'." << std::endl;
309                 earlyDisconnectFromHost();
310                 return;
311             }
312             m_state = UploadingBig;
313         }
314         break;
315     case UploadingBig: {
316         if (!handleBigJobFinished(job, m_bigFileUploadJob, error, "uploading"))
317             return;
318         const qint64 msecs = m_bigJobTimer.elapsed();
319         std::cout << "Successfully uploaded big file. Took " << (msecs/1000)
320             << " seconds for " << m_parameters.bigFileSize << " MB."
321             << std::endl;
322         const QString localFilePath = m_localBigFile->fileName();
323         const QString downloadedFilePath = cmpFileName(localFilePath);
324         const QString remoteFp
325             = remoteFilePath(QFileInfo(localFilePath).fileName());
326         std::cout << "Now downloading big file for comparison..." << std::endl;
327         m_bigJobTimer.start();
328         m_bigFileDownloadJob = m_channel->downloadFile(remoteFp,
329             downloadedFilePath, SftpOverwriteExisting);
330         if (m_bigFileDownloadJob == SftpInvalidJob) {
331             std::cerr << "Error downloading remote file '"
332                 << qPrintable(remoteFp) << "' to local file '"
333                 << qPrintable(downloadedFilePath) << "'." << std::endl;
334             earlyDisconnectFromHost();
335             return;
336         }
337         m_state = DownloadingBig;
338         break;
339     }
340     case DownloadingBig: {
341         if (!handleBigJobFinished(job, m_bigFileDownloadJob, error, "downloading"))
342             return;
343         const qint64 msecs = m_bigJobTimer.elapsed();
344         std::cout << "Successfully downloaded big file. Took " << (msecs/1000)
345             << " seconds for " << m_parameters.bigFileSize << " MB."
346             << std::endl;
347         std::cout << "Now comparing big files..." << std::endl;
348         QFile downloadedFile(cmpFileName(m_localBigFile->fileName()));
349         if (!downloadedFile.open(QIODevice::ReadOnly)) {
350             std::cerr << "Error opening downloaded file '"
351                 << qPrintable(downloadedFile.fileName()) << "': "
352                 << qPrintable(downloadedFile.errorString()) << "." << std::endl;
353             earlyDisconnectFromHost();
354             return;
355         }
356         if (!m_localBigFile->open(QIODevice::ReadOnly)) {
357             std::cerr << "Error opening big file '"
358                 << qPrintable(m_localBigFile->fileName()) << "': "
359                 << qPrintable(m_localBigFile->errorString()) << "."
360                 << std::endl;
361             earlyDisconnectFromHost();
362             return;
363         }
364         if (!compareFiles(m_localBigFile.data(), &downloadedFile))
365             return;
366         std::cout << "Comparison successful. Now removing big files..."
367             << std::endl;
368         if (!m_localBigFile->remove()) {
369             std::cerr << "Error: Could not remove file '"
370                 << qPrintable(m_localBigFile->fileName()) << "'." << std::endl;
371             earlyDisconnectFromHost();
372             return;
373         }
374         if (!downloadedFile.remove()) {
375             std::cerr << "Error: Could not remove file '"
376                 << qPrintable(downloadedFile.fileName()) << "'." << std::endl;
377             earlyDisconnectFromHost();
378             return;
379         }
380         const QString remoteFp
381             = remoteFilePath(QFileInfo(m_localBigFile->fileName()).fileName());
382         m_bigFileRemovalJob = m_channel->removeFile(remoteFp);
383         m_state = RemovingBig;
384         break;
385     }
386     case RemovingBig: {
387         if (!handleBigJobFinished(job, m_bigFileRemovalJob, error, "removing"))
388             return;
389         const QString remoteFp
390             = remoteFilePath(QFileInfo(m_localBigFile->fileName()).fileName());
391         std::cout << "Big files successfully removed. "
392             << "Now closing the SFTP channel..." << std::endl;
393         m_state = ChannelClosing;
394         m_channel->closeChannel();
395         break;
396     }
397     case Disconnecting:
398         break;
399     default:
400         if (!m_error) {
401             std::cerr << "Unexpected state " << m_state << " in function "
402                 << Q_FUNC_INFO << "." << std::endl;
403             earlyDisconnectFromHost();
404         }
405     }
406 }
407
408 void SftpTest::removeFile(const FilePtr &file, bool remoteToo)
409 {
410     if (!file)
411         return;
412     const QString localFilePath = file->fileName();
413     file->remove();
414     QFile::remove(cmpFileName(localFilePath));
415     if (remoteToo && m_channel
416             && m_channel->state() == SftpChannel::Initialized)
417         m_channel->removeFile(remoteFilePath(QFileInfo(localFilePath).fileName()));
418 }
419
420 QString SftpTest::cmpFileName(const QString &fileName) const
421 {
422     return fileName + QLatin1String(".cmp");
423 }
424
425 QString SftpTest::remoteFilePath(const QString &localFileName) const
426 {
427     return QLatin1String("/tmp/") + localFileName + QLatin1String(".upload");
428 }
429
430 void SftpTest::earlyDisconnectFromHost()
431 {
432     m_error = true;
433     removeFiles(true);
434     if (m_channel)
435         disconnect(m_channel.data(), 0, this, 0);
436     m_state = Disconnecting;
437     removeFiles(true);
438     m_connection->disconnectFromHost();
439 }
440
441 void SftpTest::removeFiles(bool remoteToo)
442 {
443     foreach (const FilePtr &file, m_localSmallFiles)
444         removeFile(file, remoteToo);
445     removeFile(m_localBigFile, remoteToo);
446 }
447
448 bool SftpTest::handleJobFinished(SftpJobId job, JobMap &jobMap,
449     const QString &error, const char *activity)
450 {
451     JobMap::Iterator it = jobMap.find(job);
452     if (it == jobMap.end()) {
453         std::cerr << "Error: Unknown job " << job << "finished."
454             << std::endl;
455         earlyDisconnectFromHost();
456         return false;
457     }
458     if (!error.isEmpty()) {
459         std::cerr << "Error " << activity << " file " << qPrintable(it.value())
460             << ": " << qPrintable(error) << "." << std::endl;
461         earlyDisconnectFromHost();
462         return false;
463     }
464     jobMap.erase(it);
465     return true;
466 }
467
468 bool SftpTest::handleBigJobFinished(SftpJobId job, SftpJobId expectedJob,
469     const QString &error, const char *activity)
470 {
471     if (job != expectedJob) {
472         std::cerr << "Error " << activity << " file '"
473            << qPrintable(m_localBigFile->fileName())
474            << "': Expected job id " << expectedJob
475            << ", got job id " << job << '.' << std::endl;
476         earlyDisconnectFromHost();
477         return false;
478     }
479     if (!error.isEmpty()) {
480         std::cerr << "Error " << activity << " file '"
481             << qPrintable(m_localBigFile->fileName()) << "': "
482             << qPrintable(error) << std::endl;
483         earlyDisconnectFromHost();
484         return false;
485     }
486     return true;
487 }
488
489 bool SftpTest::compareFiles(QFile *orig, QFile *copy)
490 {
491     bool success = orig->size() == copy->size();
492     qint64 bytesLeft = orig->size();
493     orig->seek(0);
494     while (success && bytesLeft > 0) {
495         const qint64 bytesToRead = qMin(bytesLeft, Q_INT64_C(1024*1024));
496         const QByteArray origBlock = orig->read(bytesToRead);
497         const QByteArray copyBlock = copy->read(bytesToRead);
498         if (origBlock.size() != bytesToRead || origBlock != copyBlock)
499             success = false;
500         bytesLeft -= bytesToRead;
501     }
502     orig->close();
503     success = success && orig->error() == QFile::NoError
504         && copy->error() == QFile::NoError;
505     if (!success) {
506         std::cerr << "Error: Original file '" << qPrintable(orig->fileName())
507             << "'' differs from downloaded file '"
508             << qPrintable(copy->fileName()) << "'." << std::endl;
509         earlyDisconnectFromHost();
510     }
511     return success;
512 }