1 /***************************************************************************
2     testqgsfilefiledownloader.cpp
3      --------------------------------------
4     Date                 : 11.8.2016
5     Copyright            : (C) 2016 Alessandro Pasotti
6     Email                : apasotti at boundlessgeo dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 
17 #include "qgstest.h"
18 #include <QObject>
19 #include <QTemporaryFile>
20 #include <QTemporaryDir>
21 #include <QUrl>
22 #include <QEventLoop>
23 #include <QTimer>
24 
25 #include <qgsapplication.h>
26 #include <qgsfiledownloader.h>
27 
28 class TestQgsFileDownloader: public QObject
29 {
30     Q_OBJECT
31   public:
32     TestQgsFileDownloader() = default;
33 
34   public slots:
35     //! Called when the download has completed successfully
downloadCompleted()36     void downloadCompleted()
37     {
38       mCompleted = true;
39     }
40     //! Called when the download exits
downloadExited()41     void downloadExited()
42     {
43       mExited = true;
44     }
45     //! Called when the download was canceled by the user
downloadCanceled()46     void downloadCanceled()
47     {
48       mCanceled = true;
49     }
50     //! Called when an error makes the download fail
downloadError(QStringList errorMessages)51     void downloadError( QStringList errorMessages )
52     {
53       mError = true;
54       errorMessages.sort();
55       mErrorMessage = errorMessages.join( QLatin1Char( ';' ) );
56     }
57     //! Called when data ready to be processed
downloadProgress(qint64 bytesReceived,qint64 bytesTotal)58     void downloadProgress( qint64 bytesReceived, qint64 bytesTotal )
59     {
60       Q_UNUSED( bytesReceived );
61       Q_UNUSED( bytesTotal );
62       mProgress = true;
63     }
64 
65   private slots:
66     void initTestCase(); // will be called before the first testfunction is executed.
67     void cleanupTestCase(); // will be called after the last testfunction was executed.
68     void init(); // will be called before each testfunction is executed.
69     void cleanup(); // will be called after every testfunction.
70 
71     void testValidDownload();
72     void testInValidDownload();
73     void testInvalidFile();
74     void testCanceledDownload();
75     void testInvalidUrl();
76     void testBlankUrl();
77     void testLacksWritePermissionsError();
78 #ifndef QT_NO_SSL
79     void testSslError_data();
80     void testSslError();
81 #endif
82 
83   private:
84     void makeCall( QUrl url, QString fileName, bool cancel = false );
85     QTemporaryFile *mTempFile = nullptr;
86     QString mErrorMessage;
87     bool mCanceled =  false ;
88     bool mProgress =  false ;
89     bool mError =  false ;
90     bool mCompleted =  false ;
91     bool mExited =  false ;
92     QgsFileDownloader *mFileDownloader = nullptr;
93 };
94 
makeCall(QUrl url,QString fileName,bool cancel)95 void TestQgsFileDownloader::makeCall( QUrl url, QString fileName, bool cancel )
96 {
97   QEventLoop loop;
98 
99   mFileDownloader = new QgsFileDownloader( url, fileName );
100   connect( mFileDownloader, &QgsFileDownloader::downloadCompleted, this, &TestQgsFileDownloader::downloadCompleted );
101   connect( mFileDownloader, &QgsFileDownloader::downloadCanceled, this, &TestQgsFileDownloader::downloadCanceled );
102   connect( mFileDownloader, &QgsFileDownloader::downloadExited, this, &TestQgsFileDownloader::downloadExited );
103   connect( mFileDownloader, &QgsFileDownloader::downloadError, this, &TestQgsFileDownloader::downloadError );
104   connect( mFileDownloader, &QgsFileDownloader::downloadProgress, this, &TestQgsFileDownloader::downloadProgress );
105 
106   connect( mFileDownloader, &QgsFileDownloader::downloadExited, &loop, &QEventLoop::quit );
107 
108   if ( cancel )
109     QTimer::singleShot( 1000, mFileDownloader, &QgsFileDownloader::cancelDownload );
110 
111   loop.exec();
112 
113 }
114 
initTestCase()115 void TestQgsFileDownloader::initTestCase()
116 {
117   QgsApplication::init();
118   QgsApplication::initQgis();
119 
120 }
121 
cleanupTestCase()122 void TestQgsFileDownloader::cleanupTestCase()
123 {
124   QgsApplication::exitQgis();
125 }
126 
init()127 void TestQgsFileDownloader::init()
128 {
129   mErrorMessage.clear();
130   mCanceled = false;
131   mProgress = false;
132   mError = false;
133   mCompleted = false;
134   mExited = false;
135   mTempFile = new QTemporaryFile();
136   QVERIFY( mTempFile->open() );
137   mTempFile->close();
138 }
139 
140 
141 
cleanup()142 void TestQgsFileDownloader::cleanup()
143 {
144   delete mTempFile;
145 }
146 
testValidDownload()147 void TestQgsFileDownloader::testValidDownload()
148 {
149   QVERIFY( ! mTempFile->fileName().isEmpty() );
150   makeCall( QUrl( QStringLiteral( "http://www.qgis.org" ) ), mTempFile->fileName() );
151   QVERIFY( mExited );
152   QVERIFY( mCompleted );
153   QVERIFY( mProgress );
154   QVERIFY( !mError );
155   QVERIFY( !mCanceled );
156   QVERIFY( mTempFile->size() > 0 );
157 }
158 
testInValidDownload()159 void TestQgsFileDownloader::testInValidDownload()
160 {
161   QVERIFY( ! mTempFile->fileName().isEmpty() );
162   makeCall( QUrl( QStringLiteral( "http://www.doesnotexistofthatimsure.qgis" ) ), mTempFile->fileName() );
163   QVERIFY( mExited );
164   QVERIFY( !mCompleted );
165   QVERIFY( mError );
166   QVERIFY( !mCanceled );
167   QVERIFY( mTempFile->size() == 0 );
168   QCOMPARE( mErrorMessage, QString( "Download failed: Host www.doesnotexistofthatimsure.qgis not found" ) );
169 }
170 
testCanceledDownload()171 void TestQgsFileDownloader::testCanceledDownload()
172 {
173   QVERIFY( ! mTempFile->fileName().isEmpty() );
174   makeCall( QUrl( QStringLiteral( "https://github.com/qgis/QGIS/archive/master.zip" ) ), mTempFile->fileName(), true );
175   QVERIFY( mExited );
176   QVERIFY( !mCompleted );
177   QVERIFY( !mError );
178   QVERIFY( mProgress );
179   QVERIFY( mCanceled );
180   QVERIFY( !mTempFile->exists() );
181 }
182 
testInvalidFile()183 void TestQgsFileDownloader::testInvalidFile()
184 {
185   makeCall( QUrl( QStringLiteral( "https://github.com/qgis/QGIS/archive/master.zip" ) ), QString() );
186   QVERIFY( mExited );
187   QVERIFY( !mCompleted );
188   QVERIFY( mError );
189   QVERIFY( !mCanceled );
190   QCOMPARE( mErrorMessage, QString( "No output filename specified" ) );
191 }
192 
testInvalidUrl()193 void TestQgsFileDownloader::testInvalidUrl()
194 {
195   QVERIFY( ! mTempFile->fileName().isEmpty() );
196   makeCall( QUrl( QStringLiteral( "xyz://www" ) ), mTempFile->fileName() );
197   QVERIFY( mExited );
198   QVERIFY( !mCompleted );
199   QVERIFY( mError );
200   QVERIFY( !mCanceled );
201   QCOMPARE( mErrorMessage, QString( "Download failed: Protocol \"xyz\" is unknown" ) );
202 }
203 
testBlankUrl()204 void TestQgsFileDownloader::testBlankUrl()
205 {
206   QVERIFY( ! mTempFile->fileName().isEmpty() );
207   makeCall( QUrl( QString() ), mTempFile->fileName() );
208   QVERIFY( mExited );
209   QVERIFY( !mCompleted );
210   QVERIFY( mError );
211   QVERIFY( !mCanceled );
212   QCOMPARE( mErrorMessage, QString( "Download failed: Protocol \"\" is unknown" ) );
213 }
214 
215 #ifndef QT_NO_SSL
testSslError_data()216 void TestQgsFileDownloader::testSslError_data()
217 {
218   QTest::addColumn<QString>( "url" );
219   QTest::addColumn<QString>( "result" );
220 
221   QTest::newRow( "expired" ) << "https://expired.badssl.com/"
222                              << "SSL Errors: ;The certificate has expired";
223   QTest::newRow( "self-signed" ) << "https://self-signed.badssl.com/"
224                                  << "SSL Errors: ;The certificate is self-signed, and untrusted";
225   QTest::newRow( "untrusted-root" ) << "https://untrusted-root.badssl.com/"
226                                     << "No certificates could be verified;SSL Errors: ;The issuer certificate of a locally looked up certificate could not be found";
227 }
228 
testSslError()229 void TestQgsFileDownloader::testSslError()
230 {
231   QFETCH( QString, url );
232   QFETCH( QString, result );
233   QVERIFY( ! mTempFile->fileName().isEmpty() );
234   makeCall( QUrl( url ), mTempFile->fileName() );
235   QCOMPARE( mErrorMessage, result );
236   QVERIFY( !mCompleted );
237   QVERIFY( mError );
238   QVERIFY( !mCanceled );
239 }
240 
testLacksWritePermissionsError()241 void TestQgsFileDownloader::testLacksWritePermissionsError()
242 {
243   const QTemporaryDir dir;
244   QFile tmpDir( dir.path( ) );
245   tmpDir.setPermissions( tmpDir.permissions() & ~( QFile::Permission::WriteGroup |  QFile::Permission::WriteUser | QFile::Permission::WriteOther | QFile::Permission::WriteOwner ) );
246   QVERIFY( ! tmpDir.isWritable() );
247   const QString fileName( dir.path() + '/' + QStringLiteral( "tmp.bin" ) );
248   makeCall( QUrl( QStringLiteral( "http://www.qgis.org" ) ), fileName );
249   QVERIFY( mExited );
250   QVERIFY( !mCompleted );
251   QVERIFY( mError );
252   QVERIFY( !mCanceled );
253   QVERIFY( ! QFileInfo::exists( fileName ) );
254 }
255 
256 
257 #endif
258 
259 
260 QGSTEST_MAIN( TestQgsFileDownloader )
261 #include "testqgsfiledownloader.moc"
262 
263 
264