mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Merge pull request #3751 from elpaso/downloader_master
[bugfix][forwardport] File downloader for identify dialog hyperlinks
This commit is contained in:
commit
fb124baaf6
@ -24,5 +24,5 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
# Set OTB application path (installed in before_install.sh script)
|
||||
export OTB_APPLICATION_PATH=${HOME}/OTB-5.6.0-Linux64/lib/otb/applications
|
||||
|
||||
xvfb-run ctest -V -E "qgis_openstreetmaptest|qgis_wcsprovidertest|PyQgsWFSProviderGUI|qgis_ziplayertest|$(cat ${DIR}/blacklist.txt | paste -sd '|' -)" -S ./qgis-test-travis.ctest --output-on-failure
|
||||
xvfb-run ctest -V -E "qgis_filedownloader|qgis_openstreetmaptest|qgis_wcsprovidertest|PyQgsWFSProviderGUI|qgis_ziplayertest|$(cat ${DIR}/blacklist.txt | paste -sd '|' -)" -S ./qgis-test-travis.ctest --output-on-failure
|
||||
# xvfb-run ctest -V -E "qgis_openstreetmaptest|qgis_wcsprovidertest" -S ./qgis-test-travis.ctest --output-on-failure
|
||||
|
@ -77,6 +77,7 @@
|
||||
%Include qgsfieldvalidator.sip
|
||||
%Include qgsfiledropedit.sip
|
||||
%Include qgsfilewidget.sip
|
||||
%Include qgsfiledownloader.sip
|
||||
%Include qgsfilterlineedit.sip
|
||||
%Include qgsfocuswatcher.sip
|
||||
%Include qgsformannotationitem.sip
|
||||
|
68
python/gui/qgsfiledownloader.sip
Normal file
68
python/gui/qgsfiledownloader.sip
Normal file
@ -0,0 +1,68 @@
|
||||
/***************************************************************************
|
||||
qgsfiledownloader.sip
|
||||
--------------------------------------
|
||||
Date : November 2016
|
||||
Copyright : (C) 2016 by Alessandro Pasotti
|
||||
Email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program 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 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
/** \ingroup gui
|
||||
* QgsFileDownloader is a utility class for downloading files.
|
||||
*
|
||||
* To use this class, it is necessary to pass the URL and an output file name as
|
||||
* arguments to the constructor, the download will start immediately.
|
||||
* The download is asynchronous and depending on the guiNotificationsEnabled
|
||||
* parameter accepted by the constructor (default = true) the class will
|
||||
* show a progress dialog and report all errors in a QMessageBox::warning dialog.
|
||||
* If the guiNotificationsEnabled parameter is set to false, the class can still
|
||||
* be used through the signals and slots mechanism.
|
||||
* The object will destroy itself when the request completes, errors or is canceled.
|
||||
*
|
||||
* @note added in QGIS 2.18.1
|
||||
*/
|
||||
class QgsFileDownloader : public QObject
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgsfiledownloader.h>
|
||||
%End
|
||||
public:
|
||||
/**
|
||||
* QgsFileDownloader
|
||||
* @param url the download url
|
||||
* @param outputFileName file name where the downloaded content will be stored
|
||||
* @param guiNotificationsEnabled if false, the downloader will not display any progress bar or error message
|
||||
*/
|
||||
QgsFileDownloader(QUrl url, QString outputFileName, bool guiNotificationsEnabled = true);
|
||||
|
||||
signals:
|
||||
/** Emitted when the download has completed successfully */
|
||||
void downloadCompleted();
|
||||
/** Emitted always when the downloader exits */
|
||||
void downloadExited();
|
||||
/** Emitted when the download was canceled by the user */
|
||||
void downloadCanceled();
|
||||
/** Emitted when an error makes the download fail */
|
||||
void downloadError( QStringList errorMessages );
|
||||
/** Emitted when data ready to be processed */
|
||||
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* Called when a download is canceled by the user
|
||||
* this slot aborts the download and deletes the object
|
||||
* Never call this slot directly: this is meant to
|
||||
* be managed by the signal-slot system.
|
||||
*/
|
||||
void onDownloadCanceled();
|
||||
|
||||
private:
|
||||
~QgsFileDownloader();
|
||||
|
||||
};
|
@ -42,6 +42,7 @@
|
||||
#include "qgswebframe.h"
|
||||
#include "qgsstringutils.h"
|
||||
#include "qgstreewidgetitem.h"
|
||||
#include "qgsfiledownloader.h"
|
||||
|
||||
#include <QCloseEvent>
|
||||
#include <QLabel>
|
||||
@ -59,6 +60,11 @@
|
||||
#include <QMessageBox>
|
||||
#include <QComboBox>
|
||||
#include <QTextDocument>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QRegExp>
|
||||
|
||||
//graph
|
||||
#include <qwt_plot.h>
|
||||
@ -72,6 +78,7 @@ QgsIdentifyResultsWebView::QgsIdentifyResultsWebView( QWidget *parent ) : QgsWeb
|
||||
setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Minimum );
|
||||
page()->setNetworkAccessManager( QgsNetworkAccessManager::instance() );
|
||||
// page()->setLinkDelegationPolicy( QWebPage::DelegateAllLinks );
|
||||
page()->setForwardUnsupportedContent( true );
|
||||
page()->setLinkDelegationPolicy( QWebPage::DontDelegateLinks );
|
||||
settings()->setAttribute( QWebSettings::LocalContentCanAccessRemoteUrls, true );
|
||||
settings()->setAttribute( QWebSettings::JavascriptCanOpenWindows, true );
|
||||
@ -79,6 +86,51 @@ QgsIdentifyResultsWebView::QgsIdentifyResultsWebView( QWidget *parent ) : QgsWeb
|
||||
#ifdef QGISDEBUG
|
||||
settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
|
||||
#endif
|
||||
connect( page(), SIGNAL( downloadRequested( QNetworkRequest ) ), this, SLOT( downloadRequested( QNetworkRequest ) ) );
|
||||
connect( page(), SIGNAL( unsupportedContent( QNetworkReply* ) ), this, SLOT( unsupportedContent( QNetworkReply* ) ) );
|
||||
}
|
||||
|
||||
|
||||
void QgsIdentifyResultsWebView::downloadRequested( const QNetworkRequest &request )
|
||||
{
|
||||
handleDownload( request.url() );
|
||||
}
|
||||
|
||||
void QgsIdentifyResultsWebView::unsupportedContent( QNetworkReply * reply )
|
||||
{
|
||||
handleDownload( reply->url() );
|
||||
}
|
||||
|
||||
void QgsIdentifyResultsWebView::handleDownload( QUrl url )
|
||||
{
|
||||
if ( ! url.isValid() )
|
||||
{
|
||||
QMessageBox::warning( this, tr( "Invalid URL" ), tr( "The download URL is not valid: %1" ).arg( url.toString( ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString DOWNLOADER_LAST_DIR_KEY( "Qgis/fileDownloaderLastDir" );
|
||||
QSettings settings;
|
||||
// Try to get some information from the URL
|
||||
QFileInfo info( url.toString( ) );
|
||||
QString savePath = settings.value( DOWNLOADER_LAST_DIR_KEY ).toString( );
|
||||
QString fileName = info.fileName().replace( QRegExp( "[^A-z0-9\\-_\\.]" ), "_" );
|
||||
if ( ! savePath.isEmpty() && ! fileName.isEmpty( ) )
|
||||
{
|
||||
savePath = QDir::cleanPath( savePath + QDir::separator() + fileName );
|
||||
}
|
||||
QString targetFile = QFileDialog::getSaveFileName( this,
|
||||
tr( "Save as" ),
|
||||
savePath,
|
||||
info.suffix( ).isEmpty() ? QString( ) : "*." + info.suffix( )
|
||||
);
|
||||
if ( ! targetFile.isEmpty() )
|
||||
{
|
||||
settings.setValue( DOWNLOADER_LAST_DIR_KEY, QFileInfo( targetFile ).dir().absolutePath( ) );
|
||||
// Start the download
|
||||
new QgsFileDownloader( url, targetFile );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsIdentifyResultsWebView::print()
|
||||
|
@ -28,6 +28,9 @@
|
||||
|
||||
#include <QWidget>
|
||||
#include <QList>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
|
||||
class QCloseEvent;
|
||||
class QTreeWidgetItem;
|
||||
@ -57,9 +60,13 @@ class APP_EXPORT QgsIdentifyResultsWebView : public QgsWebView
|
||||
QSize sizeHint() const override;
|
||||
public slots:
|
||||
void print();
|
||||
void downloadRequested( const QNetworkRequest &request );
|
||||
void unsupportedContent( QNetworkReply *reply );
|
||||
protected:
|
||||
void contextMenuEvent( QContextMenuEvent* ) override;
|
||||
QgsWebView *createWindow( QWebPage::WebWindowType type ) override;
|
||||
private:
|
||||
void handleDownload( QUrl url );
|
||||
};
|
||||
|
||||
class APP_EXPORT QgsIdentifyResultsFeatureItem: public QTreeWidgetItem
|
||||
|
@ -317,6 +317,7 @@ SET(QGIS_GUI_SRCS
|
||||
qgsuserinputdockwidget.cpp
|
||||
qgsvariableeditorwidget.cpp
|
||||
qgsvertexmarker.cpp
|
||||
qgsfiledownloader.cpp
|
||||
)
|
||||
|
||||
IF (WITH_QTWEBKIT)
|
||||
@ -471,6 +472,7 @@ SET(QGIS_GUI_MOC_HDRS
|
||||
qgsunitselectionwidget.h
|
||||
qgsuserinputdockwidget.h
|
||||
qgsvariableeditorwidget.h
|
||||
qgsfiledownloader.h
|
||||
|
||||
raster/qgsmultibandcolorrendererwidget.h
|
||||
raster/qgspalettedrendererwidget.h
|
||||
@ -664,6 +666,7 @@ SET(QGIS_GUI_HDRS
|
||||
qgsuserinputdockwidget.h
|
||||
qgsvectorlayertools.h
|
||||
qgsvertexmarker.h
|
||||
qgsfiledownloader.h
|
||||
|
||||
attributetable/qgsfeaturemodel.h
|
||||
|
||||
|
203
src/gui/qgsfiledownloader.cpp
Normal file
203
src/gui/qgsfiledownloader.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
/***************************************************************************
|
||||
qgsfiledownloader.cpp
|
||||
--------------------------------------
|
||||
Date : November 2016
|
||||
Copyright : (C) 2016 by Alessandro Pasotti
|
||||
Email : apasotti at boundlessgeo dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program 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 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsfiledownloader.h"
|
||||
#include "qgsnetworkaccessmanager.h"
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QMessageBox>
|
||||
#ifndef QT_NO_OPENSSL
|
||||
#include <QSslError>
|
||||
#endif
|
||||
|
||||
QgsFileDownloader::QgsFileDownloader( QUrl url, QString outputFileName, bool enableGuiNotifications )
|
||||
: mUrl( url )
|
||||
, mReply( nullptr )
|
||||
, mProgressDialog( nullptr )
|
||||
, mDownloadCanceled( false )
|
||||
, mErrors()
|
||||
, mGuiNotificationsEnabled( enableGuiNotifications )
|
||||
{
|
||||
mFile.setFileName( outputFileName );
|
||||
startDownload();
|
||||
}
|
||||
|
||||
|
||||
QgsFileDownloader::~QgsFileDownloader()
|
||||
{
|
||||
if ( mReply )
|
||||
{
|
||||
mReply->abort();
|
||||
mReply->deleteLater();
|
||||
}
|
||||
if ( mProgressDialog )
|
||||
{
|
||||
mProgressDialog->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QgsFileDownloader::startDownload()
|
||||
{
|
||||
QgsNetworkAccessManager* nam = QgsNetworkAccessManager::instance();
|
||||
|
||||
QNetworkRequest request( mUrl );
|
||||
|
||||
mReply = nam->get( request );
|
||||
|
||||
connect( mReply, &QNetworkReply::readyRead, this, &QgsFileDownloader::onReadyRead );
|
||||
connect( mReply, static_cast < void ( QNetworkReply::* )( QNetworkReply::NetworkError ) > ( &QNetworkReply::error ), this, &QgsFileDownloader::onNetworkError );
|
||||
connect( mReply, &QNetworkReply::finished, this, &QgsFileDownloader::onFinished );
|
||||
connect( mReply, &QNetworkReply::downloadProgress, this, &QgsFileDownloader::onDownloadProgress );
|
||||
connect( nam, &QgsNetworkAccessManager::requestTimedOut, this, &QgsFileDownloader::onRequestTimedOut );
|
||||
#ifndef QT_NO_OPENSSL
|
||||
connect( nam, &QgsNetworkAccessManager::sslErrors, this, &QgsFileDownloader::onSslErrors );
|
||||
#endif
|
||||
if ( mGuiNotificationsEnabled )
|
||||
{
|
||||
mProgressDialog = new QProgressDialog();
|
||||
mProgressDialog->setWindowTitle( tr( "Download" ) );
|
||||
mProgressDialog->setLabelText( tr( "Downloading %1." ).arg( mFile.fileName() ) );
|
||||
mProgressDialog->show();
|
||||
connect( mProgressDialog, &QProgressDialog::canceled, this, &QgsFileDownloader::onDownloadCanceled );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsFileDownloader::onDownloadCanceled()
|
||||
{
|
||||
mDownloadCanceled = true;
|
||||
emit downloadCanceled();
|
||||
onFinished();
|
||||
}
|
||||
|
||||
void QgsFileDownloader::onRequestTimedOut()
|
||||
{
|
||||
error( tr( "Network request %1 timed out" ).arg( mUrl.toString() ) );
|
||||
}
|
||||
|
||||
#ifndef QT_NO_OPENSSL
|
||||
void QgsFileDownloader::onSslErrors( QNetworkReply *reply, const QList<QSslError> &errors )
|
||||
{
|
||||
Q_UNUSED( reply );
|
||||
QStringList errorMessages;
|
||||
errorMessages << "SSL Errors: ";
|
||||
for ( auto end = errors.size(), i = 0; i != end; ++i )
|
||||
{
|
||||
errorMessages << errors[i].errorString();
|
||||
}
|
||||
error( errorMessages );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void QgsFileDownloader::error( QStringList errorMessages )
|
||||
{
|
||||
for ( auto end = errorMessages.size(), i = 0; i != end; ++i )
|
||||
{
|
||||
mErrors.append( errorMessages[i] );
|
||||
}
|
||||
// Show error
|
||||
if ( mGuiNotificationsEnabled )
|
||||
{
|
||||
QMessageBox::warning( nullptr, tr( "Download failed" ), mErrors.join( "<br>" ) );
|
||||
}
|
||||
emit downloadError( mErrors );
|
||||
}
|
||||
|
||||
void QgsFileDownloader::error( QString errorMessage )
|
||||
{
|
||||
error( QStringList() << errorMessage );
|
||||
}
|
||||
|
||||
void QgsFileDownloader::onReadyRead()
|
||||
{
|
||||
Q_ASSERT( mReply );
|
||||
if ( ! mFile.isOpen() && ! mFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
|
||||
{
|
||||
error( tr( "Cannot open output file: %1" ).arg( mFile.fileName() ) );
|
||||
onFinished();
|
||||
}
|
||||
else
|
||||
{
|
||||
QByteArray data = mReply->readAll();
|
||||
mFile.write( data );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsFileDownloader::onFinished()
|
||||
{
|
||||
// when canceled
|
||||
if ( ! mErrors.isEmpty() || mDownloadCanceled )
|
||||
{
|
||||
mFile.close();
|
||||
mFile.remove();
|
||||
if ( mGuiNotificationsEnabled )
|
||||
mProgressDialog->hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
// download finished normally
|
||||
if ( mGuiNotificationsEnabled )
|
||||
mProgressDialog->hide();
|
||||
mFile.flush();
|
||||
mFile.close();
|
||||
|
||||
// get redirection url
|
||||
QVariant redirectionTarget = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
|
||||
if ( mReply->error() )
|
||||
{
|
||||
mFile.remove();
|
||||
error( tr( "Download failed: %1." ).arg( mReply->errorString() ) );
|
||||
}
|
||||
else if ( !redirectionTarget.isNull() )
|
||||
{
|
||||
QUrl newUrl = mUrl.resolved( redirectionTarget.toUrl() );
|
||||
mUrl = newUrl;
|
||||
mReply->deleteLater();
|
||||
mFile.open( QIODevice::WriteOnly );
|
||||
mFile.resize( 0 );
|
||||
mFile.close();
|
||||
startDownload();
|
||||
return;
|
||||
}
|
||||
// All done
|
||||
emit downloadCompleted();
|
||||
}
|
||||
emit downloadExited();
|
||||
this->deleteLater();
|
||||
}
|
||||
|
||||
void QgsFileDownloader::onNetworkError( QNetworkReply::NetworkError err )
|
||||
{
|
||||
Q_ASSERT( mReply );
|
||||
error( QString( "Network error %1: %2" ).arg( err ).arg( mReply->errorString() ) );
|
||||
}
|
||||
|
||||
void QgsFileDownloader::onDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
|
||||
{
|
||||
if ( mDownloadCanceled )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ( mGuiNotificationsEnabled )
|
||||
{
|
||||
mProgressDialog->setMaximum( bytesTotal );
|
||||
mProgressDialog->setValue( bytesReceived );
|
||||
}
|
||||
emit downloadProgress( bytesReceived, bytesTotal );
|
||||
}
|
||||
|
117
src/gui/qgsfiledownloader.h
Normal file
117
src/gui/qgsfiledownloader.h
Normal file
@ -0,0 +1,117 @@
|
||||
/***************************************************************************
|
||||
qgsfiledownloader.h
|
||||
--------------------------------------
|
||||
Date : November 2016
|
||||
Copyright : (C) 2016 by Alessandro Pasotti
|
||||
Email : apasotti at boundlessgeo dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program 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 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSFILEDOWNLOADER_H
|
||||
#define QGSFILEDOWNLOADER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QFile>
|
||||
#include <QNetworkReply>
|
||||
#include <QProgressDialog>
|
||||
#ifndef QT_NO_OPENSSL
|
||||
#include <QSslError>
|
||||
#endif
|
||||
|
||||
/** \ingroup gui
|
||||
* QgsFileDownloader is a utility class for downloading files.
|
||||
*
|
||||
* To use this class, it is necessary to pass the URL and an output file name as
|
||||
* arguments to the constructor, the download will start immediately.
|
||||
* The download is asynchronous and depending on the guiNotificationsEnabled
|
||||
* parameter accepted by the constructor (default = true) the class will
|
||||
* show a progress dialog and report all errors in a QMessageBox::warning dialog.
|
||||
* If the guiNotificationsEnabled parameter is set to false, the class can still
|
||||
* be used through the signals and slots mechanism.
|
||||
* The object will destroy itself when the request completes, errors or is canceled.
|
||||
*
|
||||
* @note added in QGIS 2.18.1
|
||||
*/
|
||||
class GUI_EXPORT QgsFileDownloader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
/**
|
||||
* QgsFileDownloader
|
||||
* @param url the download url
|
||||
* @param outputFileName file name where the downloaded content will be stored
|
||||
* @param guiNotificationsEnabled if false, the downloader will not display any progress bar or error message
|
||||
*/
|
||||
QgsFileDownloader( QUrl url, QString outputFileName, bool guiNotificationsEnabled = true );
|
||||
|
||||
signals:
|
||||
//! Emitted when the download has completed successfully
|
||||
void downloadCompleted();
|
||||
//! Emitted always when the downloader exits
|
||||
void downloadExited();
|
||||
//! Emitted when the download was canceled by the user
|
||||
void downloadCanceled();
|
||||
//! Emitted when an error makes the download fail
|
||||
void downloadError( QStringList errorMessages );
|
||||
//! Emitted when data ready to be processed
|
||||
void downloadProgress( qint64 bytesReceived, qint64 bytesTotal );
|
||||
|
||||
public slots:
|
||||
|
||||
/**
|
||||
* Called when a download is canceled by the user
|
||||
* this slot aborts the download and deletes
|
||||
* the object.
|
||||
* Never call this slot directly: this is meant to
|
||||
* be managed by the signal-slot system.
|
||||
*/
|
||||
void onDownloadCanceled();
|
||||
|
||||
private slots:
|
||||
//! Called when the network reply data are ready
|
||||
void onReadyRead();
|
||||
//! Called when the network reply has finished
|
||||
void onFinished();
|
||||
//! Called on Network Error
|
||||
void onNetworkError( QNetworkReply::NetworkError err );
|
||||
//! Called on data ready to be processed
|
||||
void onDownloadProgress( qint64 bytesReceived, qint64 bytesTotal );
|
||||
//! Called when a network request times out
|
||||
void onRequestTimedOut();
|
||||
//! Called to start the download
|
||||
void startDownload();
|
||||
#ifndef QT_NO_OPENSSL
|
||||
|
||||
/**
|
||||
* Called on SSL network Errors
|
||||
* @param reply
|
||||
* @param errors
|
||||
*/
|
||||
void onSslErrors( QNetworkReply *reply, const QList<QSslError> &errors );
|
||||
#endif
|
||||
private:
|
||||
~QgsFileDownloader();
|
||||
|
||||
/**
|
||||
* Abort current request and show an error if the instance has GUI
|
||||
* notifications enabled.
|
||||
*/
|
||||
void error( QStringList errorMessages );
|
||||
void error( QString errorMessage );
|
||||
QUrl mUrl;
|
||||
QNetworkReply* mReply;
|
||||
QFile mFile;
|
||||
QProgressDialog* mProgressDialog;
|
||||
bool mDownloadCanceled;
|
||||
QStringList mErrors;
|
||||
bool mGuiNotificationsEnabled;
|
||||
};
|
||||
|
||||
#endif // QGSFILEDOWNLOADER_H
|
@ -143,3 +143,5 @@ ADD_QGIS_TEST(sqlcomposerdialog testqgssqlcomposerdialog.cpp)
|
||||
ADD_QGIS_TEST(editorwidgetregistrytest testqgseditorwidgetregistry.cpp)
|
||||
ADD_QGIS_TEST(keyvaluewidgettest testqgskeyvaluewidget.cpp)
|
||||
ADD_QGIS_TEST(listwidgettest testqgslistwidget.cpp)
|
||||
ADD_QGIS_TEST(filedownloader testqgsfiledownloader.cpp)
|
||||
|
||||
|
254
tests/src/gui/testqgsfiledownloader.cpp
Normal file
254
tests/src/gui/testqgsfiledownloader.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
/***************************************************************************
|
||||
testqgsfilefiledownloader.cpp
|
||||
--------------------------------------
|
||||
Date : 11.8.2016
|
||||
Copyright : (C) 2016 Alessandro Pasotti
|
||||
Email : apasotti at boundlessgeo dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program 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 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include <QObject>
|
||||
#include <QTemporaryFile>
|
||||
#include <QUrl>
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
|
||||
#include <qgsapplication.h>
|
||||
#include <qgsfiledownloader.h>
|
||||
|
||||
class TestQgsFileDownloader: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TestQgsFileDownloader()
|
||||
: mTempFile( nullptr )
|
||||
, mErrorMessage()
|
||||
, mCanceled( false )
|
||||
, mProgress( false )
|
||||
, mError( false )
|
||||
, mCompleted( false )
|
||||
, mExited( false )
|
||||
, mFileDownloader( nullptr )
|
||||
{}
|
||||
|
||||
public slots:
|
||||
//! Called when the download has completed successfully
|
||||
void downloadCompleted()
|
||||
{
|
||||
mCompleted = true;
|
||||
}
|
||||
//! Called when the download exits
|
||||
void downloadExited()
|
||||
{
|
||||
mExited = true;
|
||||
}
|
||||
//! Called when the download was canceled by the user
|
||||
void downloadCanceled()
|
||||
{
|
||||
mCanceled = true;
|
||||
}
|
||||
//! Called when an error makes the download fail
|
||||
void downloadError( QStringList errorMessages )
|
||||
{
|
||||
mError = true;
|
||||
errorMessages.sort();
|
||||
mErrorMessage = errorMessages.join( ";" );
|
||||
}
|
||||
//! Called when data ready to be processed
|
||||
void downloadProgress( qint64 bytesReceived, qint64 bytesTotal )
|
||||
{
|
||||
Q_UNUSED( bytesReceived );
|
||||
Q_UNUSED( bytesTotal );
|
||||
mProgress = true;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase(); // will be called before the first testfunction is executed.
|
||||
void cleanupTestCase(); // will be called after the last testfunction was executed.
|
||||
void init(); // will be called before each testfunction is executed.
|
||||
void cleanup(); // will be called after every testfunction.
|
||||
|
||||
void testValidDownload();
|
||||
void testInValidDownload();
|
||||
void testCanceledDownload();
|
||||
void testInvalidFile();
|
||||
void testInvalidUrl();
|
||||
void testBlankUrl();
|
||||
#ifndef QT_NO_OPENSSL
|
||||
void testSslError_data();
|
||||
void testSslError();
|
||||
#endif
|
||||
|
||||
private:
|
||||
void makeCall( QUrl url , QString fileName, bool cancel = false );
|
||||
QTemporaryFile *mTempFile;
|
||||
QString mErrorMessage;
|
||||
bool mCanceled;
|
||||
bool mProgress;
|
||||
bool mError;
|
||||
bool mCompleted;
|
||||
bool mExited;
|
||||
QgsFileDownloader *mFileDownloader;
|
||||
};
|
||||
|
||||
void TestQgsFileDownloader::makeCall( QUrl url, QString fileName, bool cancel )
|
||||
{
|
||||
QEventLoop loop;
|
||||
|
||||
mFileDownloader = new QgsFileDownloader( url, fileName, false );
|
||||
connect( mFileDownloader, &QgsFileDownloader::downloadCompleted, this, &TestQgsFileDownloader::downloadCompleted );
|
||||
connect( mFileDownloader, &QgsFileDownloader::downloadCanceled, this, &TestQgsFileDownloader::downloadCanceled );
|
||||
connect( mFileDownloader, &QgsFileDownloader::downloadExited, this, &TestQgsFileDownloader::downloadExited );
|
||||
connect( mFileDownloader, &QgsFileDownloader::downloadError, this, &TestQgsFileDownloader::downloadError );
|
||||
connect( mFileDownloader, &QgsFileDownloader::downloadProgress, this, &TestQgsFileDownloader::downloadProgress );
|
||||
|
||||
connect( mFileDownloader, &QgsFileDownloader::downloadExited, &loop, &QEventLoop::quit );
|
||||
|
||||
if ( cancel )
|
||||
QTimer::singleShot( 1000, mFileDownloader, &QgsFileDownloader::onDownloadCanceled );
|
||||
|
||||
loop.exec();
|
||||
|
||||
}
|
||||
|
||||
void TestQgsFileDownloader::initTestCase()
|
||||
{
|
||||
QgsApplication::init();
|
||||
QgsApplication::initQgis();
|
||||
|
||||
}
|
||||
|
||||
void TestQgsFileDownloader::cleanupTestCase()
|
||||
{
|
||||
QgsApplication::exitQgis();
|
||||
}
|
||||
|
||||
void TestQgsFileDownloader::init()
|
||||
{
|
||||
mErrorMessage.clear();
|
||||
mCanceled = false;
|
||||
mProgress = false;
|
||||
mError = false;
|
||||
mCompleted = false;
|
||||
mExited = false;
|
||||
mTempFile = new QTemporaryFile( );
|
||||
Q_ASSERT( mTempFile->open() );
|
||||
mTempFile->close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TestQgsFileDownloader::cleanup()
|
||||
{
|
||||
delete mTempFile;
|
||||
}
|
||||
|
||||
void TestQgsFileDownloader::testValidDownload()
|
||||
{
|
||||
QVERIFY( ! mTempFile->fileName().isEmpty() );
|
||||
makeCall( QUrl( "http://www.qgis.org" ), mTempFile->fileName() );
|
||||
QVERIFY( mExited );
|
||||
QVERIFY( mCompleted );
|
||||
QVERIFY( mProgress );
|
||||
QVERIFY( !mError );
|
||||
QVERIFY( !mCanceled );
|
||||
QVERIFY( mTempFile->size() > 0 );
|
||||
}
|
||||
|
||||
void TestQgsFileDownloader::testInValidDownload()
|
||||
{
|
||||
QVERIFY( ! mTempFile->fileName().isEmpty() );
|
||||
makeCall( QUrl( "http://www.doesnotexistofthatimsure.qgis" ), mTempFile->fileName() );
|
||||
QVERIFY( mExited );
|
||||
QVERIFY( !mCompleted );
|
||||
QVERIFY( mError );
|
||||
QVERIFY( !mCanceled );
|
||||
QVERIFY( mTempFile->size() == 0 );
|
||||
QCOMPARE( mErrorMessage, QString( "Network error 3: Host www.doesnotexistofthatimsure.qgis not found" ) );
|
||||
}
|
||||
|
||||
void TestQgsFileDownloader::testCanceledDownload()
|
||||
{
|
||||
QVERIFY( ! mTempFile->fileName().isEmpty() );
|
||||
makeCall( QUrl( "https://github.com/qgis/QGIS/archive/master.zip" ), mTempFile->fileName(), true );
|
||||
QVERIFY( mExited );
|
||||
QVERIFY( !mCompleted );
|
||||
QVERIFY( !mError );
|
||||
QVERIFY( mProgress );
|
||||
QVERIFY( mCanceled );
|
||||
QVERIFY( mTempFile->size() == 0 );
|
||||
}
|
||||
|
||||
void TestQgsFileDownloader::testInvalidFile()
|
||||
{
|
||||
makeCall( QUrl( "https://github.com/qgis/QGIS/archive/master.zip" ), QString() );
|
||||
QVERIFY( mExited );
|
||||
QVERIFY( !mCompleted );
|
||||
QVERIFY( mError );
|
||||
QVERIFY( !mCanceled );
|
||||
QCOMPARE( mErrorMessage, QString( "Cannot open output file: " ) );
|
||||
}
|
||||
|
||||
void TestQgsFileDownloader::testInvalidUrl()
|
||||
{
|
||||
QVERIFY( ! mTempFile->fileName().isEmpty() );
|
||||
makeCall( QUrl( "xyz://www" ), mTempFile->fileName() );
|
||||
QVERIFY( mExited );
|
||||
QVERIFY( !mCompleted );
|
||||
QVERIFY( mError );
|
||||
QVERIFY( !mCanceled );
|
||||
QCOMPARE( mErrorMessage, QString( "Network error 301: Protocol \"xyz\" is unknown" ) );
|
||||
}
|
||||
|
||||
void TestQgsFileDownloader::testBlankUrl()
|
||||
{
|
||||
QVERIFY( ! mTempFile->fileName().isEmpty() );
|
||||
makeCall( QUrl( "" ), mTempFile->fileName() );
|
||||
QVERIFY( mExited );
|
||||
QVERIFY( !mCompleted );
|
||||
QVERIFY( mError );
|
||||
QVERIFY( !mCanceled );
|
||||
QCOMPARE( mErrorMessage, QString( "Network error 301: Protocol \"\" is unknown" ) );
|
||||
}
|
||||
|
||||
#ifndef QT_NO_OPENSSL
|
||||
void TestQgsFileDownloader::testSslError_data()
|
||||
{
|
||||
QTest::addColumn<QString>( "url" );
|
||||
QTest::addColumn<QString>( "result" );
|
||||
|
||||
QTest::newRow( "expired" ) << "https://expired.badssl.com/"
|
||||
<< "Network error 6: SSL handshake failed;SSL Errors: ;The certificate has expired";
|
||||
QTest::newRow( "self-signed" ) << "https://self-signed.badssl.com/"
|
||||
<< "Network error 6: SSL handshake failed;SSL Errors: ;The certificate is self-signed, and untrusted";
|
||||
QTest::newRow( "untrusted-root" ) << "https://untrusted-root.badssl.com/"
|
||||
<< "Network error 6: SSL handshake failed;No certificates could be verified;SSL Errors: ;The issuer certificate of a locally looked up certificate could not be found";
|
||||
}
|
||||
|
||||
void TestQgsFileDownloader::testSslError()
|
||||
{
|
||||
QFETCH( QString, url );
|
||||
QFETCH( QString, result );
|
||||
QVERIFY( ! mTempFile->fileName().isEmpty() );
|
||||
makeCall( QUrl( url ), mTempFile->fileName() );
|
||||
QCOMPARE( mErrorMessage, result );
|
||||
QVERIFY( !mCompleted );
|
||||
QVERIFY( mError );
|
||||
QVERIFY( !mCanceled );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
QTEST_MAIN( TestQgsFileDownloader )
|
||||
#include "testqgsfiledownloader.moc"
|
||||
|
||||
|
@ -123,6 +123,7 @@ ADD_PYTHON_TEST(PyQgsConsole test_console.py)
|
||||
ADD_PYTHON_TEST(PyQgsLayerDependencies test_layer_dependencies.py)
|
||||
ADD_PYTHON_TEST(PyQgsVersionCompare test_versioncompare.py)
|
||||
ADD_PYTHON_TEST(PyQgsDBManagerGpkg test_db_manager_gpkg.py)
|
||||
ADD_PYTHON_TEST(PyQgsFileDownloader test_qgsfiledownloader.py)
|
||||
|
||||
IF (NOT WIN32)
|
||||
ADD_PYTHON_TEST(PyQgsLogger test_qgslogger.py)
|
||||
|
147
tests/src/python/test_qgsfiledownloader.py
Normal file
147
tests/src/python/test_qgsfiledownloader.py
Normal file
@ -0,0 +1,147 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Test the QgsFileDownloader class
|
||||
|
||||
.. note:: This program 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
from future import standard_library
|
||||
import os
|
||||
import tempfile
|
||||
from functools import partial
|
||||
from qgis.PyQt.QtCore import QEventLoop, QUrl, QTimer
|
||||
from qgis.gui import (QgsFileDownloader,)
|
||||
from qgis.testing import start_app, unittest
|
||||
|
||||
standard_library.install_aliases()
|
||||
|
||||
__author__ = 'Alessandro Pasotti'
|
||||
__date__ = '08/11/2016'
|
||||
__copyright__ = 'Copyright 2016, The QGIS Project'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
|
||||
start_app()
|
||||
|
||||
|
||||
class TestQgsFileDownloader(unittest.TestCase):
|
||||
|
||||
"""
|
||||
This class tests the QgsFileDownloader class
|
||||
"""
|
||||
|
||||
def _make_download(self, url, destination, cancel=False):
|
||||
self.completed_was_called = False
|
||||
self.error_was_called = False
|
||||
self.canceled_was_called = False
|
||||
self.progress_was_called = False
|
||||
self.exited_was_called = False
|
||||
|
||||
loop = QEventLoop()
|
||||
|
||||
downloader = QgsFileDownloader(QUrl(url), destination, False)
|
||||
downloader.downloadCompleted.connect(partial(self._set_slot, 'completed'))
|
||||
downloader.downloadExited.connect(partial(self._set_slot, 'exited'))
|
||||
downloader.downloadCanceled.connect(partial(self._set_slot, 'canceled'))
|
||||
downloader.downloadError.connect(partial(self._set_slot, 'error'))
|
||||
downloader.downloadProgress.connect(partial(self._set_slot, 'progress'))
|
||||
|
||||
downloader.downloadExited.connect(loop.quit)
|
||||
|
||||
if cancel:
|
||||
downloader.downloadProgress.connect(downloader.onDownloadCanceled)
|
||||
|
||||
loop.exec_()
|
||||
|
||||
def test_validDownload(self):
|
||||
"""Tests a valid download"""
|
||||
destination = tempfile.mktemp()
|
||||
self._make_download('http://www.qgis.org', destination)
|
||||
self.assertTrue(self.exited_was_called)
|
||||
self.assertTrue(self.completed_was_called)
|
||||
self.assertTrue(self.progress_was_called)
|
||||
self.assertFalse(self.canceled_was_called)
|
||||
self.assertFalse(self.error_was_called)
|
||||
self.assertTrue(os.path.isfile(destination))
|
||||
self.assertGreater(os.path.getsize(destination), 0)
|
||||
|
||||
def test_inValidDownload(self):
|
||||
"""Tests an invalid download"""
|
||||
destination = tempfile.mktemp()
|
||||
self._make_download('http://www.doesnotexistofthatimsure.qgis', destination)
|
||||
self.assertTrue(self.exited_was_called)
|
||||
self.assertFalse(self.completed_was_called)
|
||||
self.assertTrue(self.progress_was_called)
|
||||
self.assertFalse(self.canceled_was_called)
|
||||
self.assertTrue(self.error_was_called)
|
||||
self.assertEqual(self.error_args[1], [u'Network error 3: Host www.doesnotexistofthatimsure.qgis not found'])
|
||||
self.assertFalse(os.path.isfile(destination))
|
||||
|
||||
def test_dowloadCanceled(self):
|
||||
"""Tests user canceled download"""
|
||||
destination = tempfile.mktemp()
|
||||
self._make_download('https://github.com/qgis/QGIS/archive/master.zip', destination, True)
|
||||
self.assertTrue(self.exited_was_called)
|
||||
self.assertFalse(self.completed_was_called)
|
||||
self.assertTrue(self.canceled_was_called)
|
||||
self.assertFalse(self.error_was_called)
|
||||
self.assertFalse(os.path.isfile(destination))
|
||||
|
||||
def test_InvalidUrl(self):
|
||||
destination = tempfile.mktemp()
|
||||
self._make_download('xyz://www', destination)
|
||||
self.assertTrue(self.exited_was_called)
|
||||
self.assertFalse(self.completed_was_called)
|
||||
self.assertFalse(self.canceled_was_called)
|
||||
self.assertTrue(self.error_was_called)
|
||||
self.assertFalse(os.path.isfile(destination))
|
||||
self.assertEqual(self.error_args[1], [u"Network error 301: Protocol \"xyz\" is unknown"])
|
||||
|
||||
def test_InvalidFile(self):
|
||||
self._make_download('https://github.com/qgis/QGIS/archive/master.zip', "")
|
||||
self.assertTrue(self.exited_was_called)
|
||||
self.assertFalse(self.completed_was_called)
|
||||
self.assertFalse(self.canceled_was_called)
|
||||
self.assertTrue(self.error_was_called)
|
||||
self.assertEqual(self.error_args[1], [u"Cannot open output file: "])
|
||||
|
||||
def test_BlankUrl(self):
|
||||
destination = tempfile.mktemp()
|
||||
self._make_download('', destination)
|
||||
self.assertTrue(self.exited_was_called)
|
||||
self.assertFalse(self.completed_was_called)
|
||||
self.assertFalse(self.canceled_was_called)
|
||||
self.assertTrue(self.error_was_called)
|
||||
self.assertFalse(os.path.isfile(destination))
|
||||
self.assertEqual(self.error_args[1], [u"Network error 301: Protocol \"\" is unknown"])
|
||||
|
||||
def ssl_compare(self, name, url, error):
|
||||
destination = tempfile.mktemp()
|
||||
self._make_download(url, destination)
|
||||
msg = "Failed in %s: %s" % (name, url)
|
||||
self.assertTrue(self.exited_was_called)
|
||||
self.assertFalse(self.completed_was_called, msg)
|
||||
self.assertFalse(self.canceled_was_called, msg)
|
||||
self.assertTrue(self.error_was_called, msg)
|
||||
self.assertFalse(os.path.isfile(destination), msg)
|
||||
result = sorted(self.error_args[1])
|
||||
result = ';'.join(result)
|
||||
self.assertTrue(result.startswith(error), msg + "expected:\n%s\nactual:\n%s\n" % (result, error))
|
||||
|
||||
def test_sslExpired(self):
|
||||
self.ssl_compare("expired", "https://expired.badssl.com/", "Network error 6: SSL handshake failed;SSL Errors: ;The certificate has expired")
|
||||
self.ssl_compare("self-signed", "https://self-signed.badssl.com/", "Network error 6: SSL handshake failed;SSL Errors: ;The certificate is self-signed, and untrusted")
|
||||
self.ssl_compare("untrusted-root", "https://untrusted-root.badssl.com/", "Network error 6: SSL handshake failed;No certificates could be verified;")
|
||||
|
||||
def _set_slot(self, *args, **kwargs):
|
||||
#print('_set_slot(%s) called' % args[0])
|
||||
setattr(self, args[0] + '_was_called', True)
|
||||
setattr(self, args[0] + '_args', args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
x
Reference in New Issue
Block a user