1 /***************************************************************************
2     testqgsexternalstoragefilewidget.cpp
3      --------------------------------------
4     Date                 : August 2021
5     Copyright            : (C) 2021 Julien Cabieces
6     Email                : julien dot cabieces at oslandia 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 
19 #include "qgsexternalstoragefilewidget.h"
20 #include "qgsmimedatautils.h"
21 #include "qgsdataitem.h"
22 #include "qgsbrowsermodel.h"
23 #include "qgslayeritem.h"
24 #include "qgsdirectoryitem.h"
25 #include "qgsexternalstorage.h"
26 #include "qgsexternalstorageregistry.h"
27 #include "qgsmessagebar.h"
28 #include "qgsexpressioncontextutils.h"
29 #include <memory>
30 
31 #include <QLabel>
32 #include <QToolButton>
33 #include <QProgressBar>
34 
35 class TestQgsExternalStorageFileWidget: public QObject
36 {
37     Q_OBJECT
38   private slots:
39     void initTestCase(); // will be called before the first testfunction is executed.
40     void cleanupTestCase(); // will be called after the last testfunction was executed.
41     void init(); // will be called before each testfunction is executed.
42     void cleanup(); // will be called after every testfunction.
43     void testLayout_data();
44     void testLayout();
45     void testStoring();
46     void testStoring_data();
47     void testStoringSeveralFiles_data();
48     void testStoringSeveralFiles();
49     void testStoringSeveralFilesError_data();
50     void testStoringSeveralFilesError();
51     void testStoringSeveralFilesCancel_data();
52     void testStoringSeveralFilesCancel();
53     void testStoringDirectory_data();
54     void testStoringDirectory();
55     void testStoringChangeFeature();
56     void testStoringBadExpression_data();
57     void testStoringBadExpression();
58     void testDragAndDrop();
59 };
60 
61 class QgsTestExternalStorageStoredContent : public QgsExternalStorageStoredContent
62 {
63     Q_OBJECT
64 
65   public:
66 
QgsTestExternalStorageStoredContent(const QString & filePath,const QString & url)67     QgsTestExternalStorageStoredContent( const QString &filePath, const QString &url )
68       : QgsExternalStorageStoredContent(),
69         mUrl( filePath.endsWith( QLatin1String( "mydir" ) ) ? url + "mydir/" : url )
70     {}
71 
store()72     void store() override
73     {
74       mStatus = Qgis::ContentStatus::Running;
75     }
76 
cancel()77     void cancel() override
78     {
79       mStatus = Qgis::ContentStatus::Canceled;
80       emit canceled();
81     };
82 
error()83     void error()
84     {
85       mStatus = Qgis::ContentStatus::Failed;
86       mErrorString = QStringLiteral( "error" );
87       emit errorOccurred( mErrorString );
88     }
89 
setProgress(double progress)90     void setProgress( double progress )
91     {
92       emit progressChanged( progress );
93     }
94 
finish()95     void finish()
96     {
97       mStatus = Qgis::ContentStatus::Finished;
98       emit stored();
99     }
100 
url() const101     QString url() const override
102     {
103       return mUrl;
104     }
105 
106   private:
107 
108     QString mUrl;
109 
110 };
111 
112 class QgsTestExternalStorage : public QgsExternalStorage
113 {
114   public:
115 
type() const116     QString type() const override { return QStringLiteral( "test" ); }
117 
displayName() const118     QString displayName() const override { return QStringLiteral( "Test" ); }
119 
120     static QPointer<QgsTestExternalStorageStoredContent> sCurrentStoredContent;
121 
122   protected:
123 
doStore(const QString & filePath,const QString & url,const QString & authcfg=QString ()) const124     QgsExternalStorageStoredContent *doStore( const QString &filePath, const QString &url, const QString &authcfg = QString() ) const override
125     {
126       Q_UNUSED( authcfg );
127       sCurrentStoredContent = new QgsTestExternalStorageStoredContent( filePath, url );
128       return sCurrentStoredContent;
129     }
130 
doFetch(const QString & url,const QString & authcfg=QString ()) const131     QgsExternalStorageFetchedContent *doFetch( const QString &url, const QString &authcfg = QString() ) const override
132     {
133       Q_UNUSED( url );
134       Q_UNUSED( authcfg );
135       return nullptr;
136     }
137 };
138 
139 QPointer<QgsTestExternalStorageStoredContent> QgsTestExternalStorage::sCurrentStoredContent;
140 
initTestCase()141 void TestQgsExternalStorageFileWidget::initTestCase()
142 {
143   QgsApplication::externalStorageRegistry()->registerExternalStorage( new QgsTestExternalStorage() );
144 }
145 
cleanupTestCase()146 void TestQgsExternalStorageFileWidget::cleanupTestCase()
147 {
148 }
149 
init()150 void TestQgsExternalStorageFileWidget::init()
151 {
152 }
153 
cleanup()154 void TestQgsExternalStorageFileWidget::cleanup()
155 {
156 }
157 
testLayout_data()158 void TestQgsExternalStorageFileWidget::testLayout_data()
159 {
160   QTest::addColumn<QString>( "storageType" );
161 
162   QTest::newRow( "without external storage" ) << QString();
163   QTest::newRow( "with external storage" ) << QStringLiteral( "test" );
164 }
165 
testLayout()166 void TestQgsExternalStorageFileWidget::testLayout()
167 {
168   // test correct buttons are displayed according to different mode and interactions
169 
170   QFETCH( QString, storageType );
171 
172   QgsExternalStorageFileWidget w;
173   w.setStorageType( storageType );
174   w.show();
175 
176   QIcon editIcon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) );
177   QIcon saveIcon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveEdits.svg" ) );
178 
179   // with link, read-only
180   w.setReadOnly( true );
181   w.setUseLink( true );
182 
183   QVERIFY( w.mLinkLabel->isVisible() );
184   QVERIFY( !w.mLinkEditButton->isVisible() );
185   QVERIFY( !w.mLineEdit->isVisible() );
186   QVERIFY( w.mFileWidgetButton->isVisible() );
187   QVERIFY( !w.mFileWidgetButton->isEnabled() );
188   QVERIFY( !w.mProgressLabel->isVisible() );
189   QVERIFY( !w.mProgressBar->isVisible() );
190   QVERIFY( !w.mCancelButton->isVisible() );
191 
192   // with link, edit mode
193   w.setReadOnly( false );
194 
195   QVERIFY( w.mLinkLabel->isVisible() );
196   QVERIFY( w.mLinkEditButton->isVisible() );
197   QCOMPARE( w.mLinkEditButton->icon(), editIcon );
198   QVERIFY( !w.mLineEdit->isVisible() );
199   QVERIFY( w.mFileWidgetButton->isVisible() );
200   QVERIFY( w.mFileWidgetButton->isEnabled() );
201   QVERIFY( !w.mProgressLabel->isVisible() );
202   QVERIFY( !w.mProgressBar->isVisible() );
203   QVERIFY( !w.mCancelButton->isVisible() );
204 
205   // with link, edit mode, we edit the link
206   w.editLink();
207 
208   QVERIFY( !w.mLinkLabel->isVisible() );
209   QVERIFY( w.mLinkEditButton->isVisible() );
210   QCOMPARE( w.mLinkEditButton->icon(), saveIcon );
211   QVERIFY( w.mLineEdit->isVisible() );
212   QVERIFY( w.mLineEdit->isEnabled() );
213   QVERIFY( w.mFileWidgetButton->isVisible() );
214   QVERIFY( w.mFileWidgetButton->isEnabled() );
215   QVERIFY( !w.mProgressLabel->isVisible() );
216   QVERIFY( !w.mProgressBar->isVisible() );
217   QVERIFY( !w.mCancelButton->isVisible() );
218 
219   // with link, edit mode, we finish editing the link
220   w.editLink();
221 
222   QVERIFY( w.mLinkLabel->isVisible() );
223   QVERIFY( w.mLinkEditButton->isVisible() );
224   QCOMPARE( w.mLinkEditButton->icon(), editIcon );
225   QVERIFY( !w.mLineEdit->isVisible() );
226   QVERIFY( w.mFileWidgetButton->isVisible() );
227   QVERIFY( w.mFileWidgetButton->isEnabled() );
228   QVERIFY( !w.mProgressLabel->isVisible() );
229   QVERIFY( !w.mProgressBar->isVisible() );
230   QVERIFY( !w.mCancelButton->isVisible() );
231 
232   // without link, read-only
233   w.setUseLink( false );
234   w.setReadOnly( true );
235 
236   QVERIFY( !w.mLinkLabel->isVisible() );
237   QVERIFY( !w.mLinkEditButton->isVisible() );
238   QVERIFY( w.mLineEdit->isVisible() );
239   QVERIFY( !w.mLineEdit->isEnabled() );
240   QVERIFY( w.mFileWidgetButton->isVisible() );
241   QVERIFY( !w.mFileWidgetButton->isEnabled() );
242   QVERIFY( !w.mProgressLabel->isVisible() );
243   QVERIFY( !w.mProgressBar->isVisible() );
244   QVERIFY( !w.mCancelButton->isVisible() );
245 
246   // without link, edit mode
247   w.setReadOnly( false );
248 
249   QVERIFY( !w.mLinkLabel->isVisible() );
250   QVERIFY( !w.mLinkEditButton->isVisible() );
251   QVERIFY( w.mLineEdit->isVisible() );
252   QVERIFY( w.mLineEdit->isEnabled() );
253   QVERIFY( w.mFileWidgetButton->isVisible() );
254   QVERIFY( w.mFileWidgetButton->isEnabled() );
255   QVERIFY( !w.mProgressLabel->isVisible() );
256   QVERIFY( !w.mProgressBar->isVisible() );
257   QVERIFY( !w.mCancelButton->isVisible() );
258 }
259 
testStoring_data()260 void TestQgsExternalStorageFileWidget::testStoring_data()
261 {
262   QTest::addColumn<bool>( "useLink" );
263 
264   QTest::newRow( "use link" ) << true;
265   QTest::newRow( "don't use link" ) << false;
266 }
267 
testStoring()268 void TestQgsExternalStorageFileWidget::testStoring()
269 {
270   // test widget when an external storage is used
271 
272   QFETCH( bool, useLink );
273 
274   QgsExternalStorageFileWidget w;
275   w.show();
276 
277   QIcon editIcon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) );
278 
279   w.setStorageType( "test" );
280   w.setStorageUrlExpression( "'http://test.url.com/test/' || file_name(@selected_file_path)" );
281 
282   // start edit mode
283   w.setUseLink( useLink );
284   w.setReadOnly( false );
285 
286   const QStringList fileNames = QStringList() << "myfile1.txt" << "myfile2.txt" ;
287   for ( QString fileName : fileNames )
288   {
289     QVERIFY( useLink == w.mLinkLabel->isVisible() );
290     QVERIFY( useLink == w.mLinkEditButton->isVisible() );
291     if ( useLink ) QCOMPARE( w.mLinkEditButton->icon(), editIcon );
292     QVERIFY( useLink != w.mLineEdit->isVisible() );
293     QVERIFY( w.mFileWidgetButton->isVisible() );
294     QVERIFY( w.mFileWidgetButton->isEnabled() );
295     QVERIFY( !w.mProgressLabel->isVisible() );
296     QVERIFY( !w.mProgressBar->isVisible() );
297     QVERIFY( !w.mCancelButton->isVisible() );
298 
299     QString currentLabel = useLink ? w.mLinkLabel->text() : w.mLineEdit->text();
300 
301     w.setSelectedFileNames( QStringList() << fileName );
302 
303     QVERIFY( QgsTestExternalStorage::sCurrentStoredContent );
304 
305     QVERIFY( !w.mLinkLabel->isVisible() );
306     QVERIFY( !w.mLinkEditButton->isVisible() );
307     QVERIFY( !w.mLineEdit->isVisible() );
308     QVERIFY( !w.mFileWidgetButton->isVisible() );
309     QVERIFY( w.mProgressLabel->isVisible() );
310     QVERIFY( w.mProgressBar->isVisible() );
311     QCOMPARE( w.mProgressBar->value(), 0 );
312     QVERIFY( w.mCancelButton->isVisible() );
313 
314     // link is not yet updated
315     if ( useLink )
316       QCOMPARE( currentLabel, w.mLinkLabel->text() );
317     else
318       QCOMPARE( currentLabel, w.mLineEdit->text() );
319 
320     QgsTestExternalStorage::sCurrentStoredContent->setProgress( 50 );
321     QVERIFY( w.mProgressBar->isVisible() );
322     QCOMPARE( w.mProgressBar->value(), 50 );
323 
324     QgsTestExternalStorage::sCurrentStoredContent->finish();
325 
326     QVERIFY( useLink == w.mLinkLabel->isVisible() );
327     QVERIFY( useLink == w.mLinkEditButton->isVisible() );
328     if ( useLink ) QCOMPARE( w.mLinkEditButton->icon(), editIcon );
329     QVERIFY( useLink != w.mLineEdit->isVisible() );
330     QVERIFY( w.mFileWidgetButton->isVisible() );
331     QVERIFY( w.mFileWidgetButton->isEnabled() );
332     QVERIFY( !w.mProgressLabel->isVisible() );
333     QVERIFY( !w.mProgressBar->isVisible() );
334     QVERIFY( !w.mCancelButton->isVisible() );
335     if ( useLink )
336       QCOMPARE( w.mLinkLabel->text(), QStringLiteral( "<a href=\"http://test.url.com/test/%1\">%1</a>" ).arg( fileName ) );
337     else
338       QCOMPARE( w.mLineEdit->text(), QStringLiteral( "http://test.url.com/test/%1" ).arg( fileName ) );
339   }
340 }
341 
342 
testStoringSeveralFiles_data()343 void TestQgsExternalStorageFileWidget::testStoringSeveralFiles_data()
344 {
345   QTest::addColumn<bool>( "useLink" );
346 
347   QTest::newRow( "use link" ) << true;
348   QTest::newRow( "don't use link" ) << false;
349 }
350 
testStoringSeveralFiles()351 void TestQgsExternalStorageFileWidget::testStoringSeveralFiles()
352 {
353   // test widget when storing several files with an external storage
354   QEventLoop loop;
355   QFETCH( bool, useLink );
356 
357   QgsExternalStorageFileWidget w;
358   w.show();
359 
360   QIcon editIcon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) );
361 
362   w.setStorageType( "test" );
363   w.setStorageUrlExpression( "'http://test.url.com/test/' || file_name(@selected_file_path)" );
364   w.setStorageMode( QgsFileWidget::GetMultipleFiles );
365 
366   // start edit mode
367   w.setUseLink( useLink );
368   w.setReadOnly( false );
369 
370   QVERIFY( useLink == w.mLinkLabel->isVisible() );
371   QVERIFY( useLink == w.mLinkEditButton->isVisible() );
372   if ( useLink ) QCOMPARE( w.mLinkEditButton->icon(), editIcon );
373   QVERIFY( useLink != w.mLineEdit->isVisible() );
374   QVERIFY( w.mFileWidgetButton->isVisible() );
375   QVERIFY( w.mFileWidgetButton->isEnabled() );
376   QVERIFY( !w.mProgressLabel->isVisible() );
377   QVERIFY( !w.mProgressBar->isVisible() );
378   QVERIFY( !w.mCancelButton->isVisible() );
379 
380   w.setSelectedFileNames( QStringList() << QStringLiteral( "myfile1" ) << QStringLiteral( "myfile2" ) );
381 
382   QPointer<QgsTestExternalStorageStoredContent> content1 = QgsTestExternalStorage::sCurrentStoredContent;
383   QVERIFY( content1 );
384 
385   QVERIFY( !w.mLinkLabel->isVisible() );
386   QVERIFY( !w.mLinkEditButton->isVisible() );
387   QVERIFY( !w.mLineEdit->isVisible() );
388   QVERIFY( !w.mFileWidgetButton->isVisible() );
389   QVERIFY( w.mProgressLabel->isVisible() );
390   QVERIFY( w.mProgressBar->isVisible() );
391   QVERIFY( w.mCancelButton->isVisible() );
392   QVERIFY( w.mLinkLabel->text().isEmpty() );
393 
394   QgsTestExternalStorage::sCurrentStoredContent->finish();
395 
396   // second file is being stored
397   QVERIFY( content1 );
398   QVERIFY( QgsTestExternalStorage::sCurrentStoredContent );
399   QVERIFY( content1 != QgsTestExternalStorage::sCurrentStoredContent );
400 
401   QVERIFY( !w.mLinkLabel->isVisible() );
402   QVERIFY( !w.mLinkEditButton->isVisible() );
403   QVERIFY( !w.mLineEdit->isVisible() );
404   QVERIFY( !w.mFileWidgetButton->isVisible() );
405   QVERIFY( w.mProgressLabel->isVisible() );
406   QVERIFY( w.mProgressBar->isVisible() );
407   QVERIFY( w.mCancelButton->isVisible() );
408   QVERIFY( w.mLinkLabel->text().isEmpty() );
409 
410   // wait for first file content to be destroyed
411   connect( content1, &QObject::destroyed, &loop, &QEventLoop::quit );
412   loop.exec();
413   QVERIFY( !content1 );
414   QVERIFY( QgsTestExternalStorage::sCurrentStoredContent );
415 
416   // end second store
417   QgsTestExternalStorage::sCurrentStoredContent->finish();
418 
419   QVERIFY( useLink == w.mLinkLabel->isVisible() );
420   QVERIFY( useLink == w.mLinkEditButton->isVisible() );
421   if ( useLink ) QCOMPARE( w.mLinkEditButton->icon(), editIcon );
422   QVERIFY( useLink != w.mLineEdit->isVisible() );
423   QVERIFY( w.mFileWidgetButton->isVisible() );
424   QVERIFY( w.mFileWidgetButton->isEnabled() );
425   QVERIFY( !w.mProgressLabel->isVisible() );
426   QVERIFY( !w.mProgressBar->isVisible() );
427   QVERIFY( !w.mCancelButton->isVisible() );
428   if ( useLink )
429     QCOMPARE( w.mLinkLabel->text(), QStringLiteral( "<a>\"http://test.url.com/test/myfile1\" \"http://test.url.com/test/myfile2\"</a>" ) );
430   else
431     QCOMPARE( w.mLineEdit->text(), QStringLiteral( "\"http://test.url.com/test/myfile1\" \"http://test.url.com/test/myfile2\"" ) );
432 
433   // wait for second file content to be destroyed
434   connect( QgsTestExternalStorage::sCurrentStoredContent, &QObject::destroyed, &loop, &QEventLoop::quit );
435   loop.exec();
436   QVERIFY( !QgsTestExternalStorage::sCurrentStoredContent );
437 }
438 
testStoringSeveralFilesError_data()439 void TestQgsExternalStorageFileWidget::testStoringSeveralFilesError_data()
440 {
441   QTest::addColumn<bool>( "useLink" );
442 
443   QTest::newRow( "use link" ) << true;
444   QTest::newRow( "don't use link" ) << false;
445 }
446 
testStoringSeveralFilesError()447 void TestQgsExternalStorageFileWidget::testStoringSeveralFilesError()
448 {
449   // test widget when storing several files with an external storage and an error occurred
450   QEventLoop loop;
451   QFETCH( bool, useLink );
452 
453   QgsExternalStorageFileWidget w;
454   QgsMessageBar messageBar;
455   w.show();
456 
457   QIcon editIcon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) );
458 
459   w.setStorageType( "test" );
460   w.setStorageUrlExpression( "'http://test.url.com/test/' || file_name(@selected_file_path)" );
461   w.setMessageBar( &messageBar );
462 
463   // start edit mode
464   w.setUseLink( useLink );
465   w.setReadOnly( false );
466 
467   QVERIFY( useLink == w.mLinkLabel->isVisible() );
468   QVERIFY( useLink == w.mLinkEditButton->isVisible() );
469   if ( useLink ) QCOMPARE( w.mLinkEditButton->icon(), editIcon );
470   QVERIFY( useLink != w.mLineEdit->isVisible() );
471   QVERIFY( w.mFileWidgetButton->isVisible() );
472   QVERIFY( w.mFileWidgetButton->isEnabled() );
473   QVERIFY( !w.mProgressLabel->isVisible() );
474   QVERIFY( !w.mProgressBar->isVisible() );
475   QVERIFY( !w.mCancelButton->isVisible() );
476 
477   w.setSelectedFileNames( QStringList() << QStringLiteral( "myfile1" ) << QStringLiteral( "error.txt" ) );
478 
479   QPointer<QgsTestExternalStorageStoredContent> content1 = QgsTestExternalStorage::sCurrentStoredContent;
480   QVERIFY( content1 );
481 
482   QVERIFY( !w.mLinkLabel->isVisible() );
483   QVERIFY( !w.mLinkEditButton->isVisible() );
484   QVERIFY( !w.mLineEdit->isVisible() );
485   QVERIFY( !w.mFileWidgetButton->isVisible() );
486   QVERIFY( w.mProgressLabel->isVisible() );
487   QVERIFY( w.mProgressBar->isVisible() );
488   QVERIFY( w.mCancelButton->isVisible() );
489   QVERIFY( w.mLinkLabel->text().isEmpty() );
490   QVERIFY( !messageBar.currentItem() );
491 
492   QgsTestExternalStorage::sCurrentStoredContent->finish();
493 
494   // second file is being stored
495   QVERIFY( content1 );
496   QVERIFY( QgsTestExternalStorage::sCurrentStoredContent );
497   QVERIFY( content1 != QgsTestExternalStorage::sCurrentStoredContent );
498 
499   QVERIFY( !w.mLinkLabel->isVisible() );
500   QVERIFY( !w.mLinkEditButton->isVisible() );
501   QVERIFY( !w.mLineEdit->isVisible() );
502   QVERIFY( !w.mFileWidgetButton->isVisible() );
503   QVERIFY( w.mProgressLabel->isVisible() );
504   QVERIFY( w.mProgressBar->isVisible() );
505   QVERIFY( w.mCancelButton->isVisible() );
506   QVERIFY( w.mLinkLabel->text().isEmpty() );
507 
508   // wait for first file content to be destroyed
509   connect( content1, &QObject::destroyed, &loop, &QEventLoop::quit );
510   loop.exec();
511   QVERIFY( !content1 );
512   QVERIFY( QgsTestExternalStorage::sCurrentStoredContent );
513 
514   // error while storing second file
515   QgsTestExternalStorage::sCurrentStoredContent->error();
516 
517   QVERIFY( useLink == w.mLinkLabel->isVisible() );
518   QVERIFY( useLink == w.mLinkEditButton->isVisible() );
519   if ( useLink ) QCOMPARE( w.mLinkEditButton->icon(), editIcon );
520   QVERIFY( useLink != w.mLineEdit->isVisible() );
521   QVERIFY( w.mFileWidgetButton->isVisible() );
522   QVERIFY( w.mFileWidgetButton->isEnabled() );
523   QVERIFY( !w.mProgressLabel->isVisible() );
524   QVERIFY( !w.mProgressBar->isVisible() );
525   QVERIFY( !w.mCancelButton->isVisible() );
526   if ( useLink )
527     QVERIFY( w.mLinkLabel->text().isEmpty() );
528   else
529     QVERIFY( w.mLineEdit->text().isEmpty() );
530   QVERIFY( messageBar.currentItem() );
531 
532   // wait for second file content to be destroyed
533   connect( QgsTestExternalStorage::sCurrentStoredContent, &QObject::destroyed, &loop, &QEventLoop::quit );
534   loop.exec();
535   QVERIFY( !QgsTestExternalStorage::sCurrentStoredContent );
536 }
537 
538 
testStoringSeveralFilesCancel_data()539 void TestQgsExternalStorageFileWidget::testStoringSeveralFilesCancel_data()
540 {
541   QTest::addColumn<bool>( "useLink" );
542 
543   QTest::newRow( "use link" ) << true;
544   QTest::newRow( "don't use link" ) << false;
545 }
546 
testStoringSeveralFilesCancel()547 void TestQgsExternalStorageFileWidget::testStoringSeveralFilesCancel()
548 {
549   // test widget when storing several files with an external storage and user cancel operation
550   QEventLoop loop;
551   QFETCH( bool, useLink );
552 
553   QgsExternalStorageFileWidget w;
554   w.show();
555 
556   QIcon editIcon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) );
557 
558   w.setStorageType( "test" );
559   w.setStorageUrlExpression( "'http://test.url.com/test/' || file_name(@selected_file_path)" );
560 
561   // start edit mode
562   w.setUseLink( useLink );
563   w.setReadOnly( false );
564 
565   QVERIFY( useLink == w.mLinkLabel->isVisible() );
566   QVERIFY( useLink == w.mLinkEditButton->isVisible() );
567   if ( useLink ) QCOMPARE( w.mLinkEditButton->icon(), editIcon );
568   QVERIFY( useLink != w.mLineEdit->isVisible() );
569   QVERIFY( w.mFileWidgetButton->isVisible() );
570   QVERIFY( w.mFileWidgetButton->isEnabled() );
571   QVERIFY( !w.mProgressLabel->isVisible() );
572   QVERIFY( !w.mProgressBar->isVisible() );
573   QVERIFY( !w.mCancelButton->isVisible() );
574 
575   w.setSelectedFileNames( QStringList() << QStringLiteral( "myfile1" ) << QStringLiteral( "error.txt" ) );
576 
577   QPointer<QgsTestExternalStorageStoredContent> content1 = QgsTestExternalStorage::sCurrentStoredContent;
578   QVERIFY( content1 );
579 
580   QVERIFY( !w.mLinkLabel->isVisible() );
581   QVERIFY( !w.mLinkEditButton->isVisible() );
582   QVERIFY( !w.mLineEdit->isVisible() );
583   QVERIFY( !w.mFileWidgetButton->isVisible() );
584   QVERIFY( w.mProgressLabel->isVisible() );
585   QVERIFY( w.mProgressBar->isVisible() );
586   QVERIFY( w.mCancelButton->isVisible() );
587   QVERIFY( w.mLinkLabel->text().isEmpty() );
588 
589   QgsTestExternalStorage::sCurrentStoredContent->finish();
590 
591   // second file is being stored
592   QVERIFY( content1 );
593   QVERIFY( QgsTestExternalStorage::sCurrentStoredContent );
594   QVERIFY( content1 != QgsTestExternalStorage::sCurrentStoredContent );
595 
596   QVERIFY( !w.mLinkLabel->isVisible() );
597   QVERIFY( !w.mLinkEditButton->isVisible() );
598   QVERIFY( !w.mLineEdit->isVisible() );
599   QVERIFY( !w.mFileWidgetButton->isVisible() );
600   QVERIFY( w.mProgressLabel->isVisible() );
601   QVERIFY( w.mProgressBar->isVisible() );
602   QVERIFY( w.mCancelButton->isVisible() );
603   QVERIFY( w.mLinkLabel->text().isEmpty() );
604 
605   // wait for first file content to be destroyed
606   connect( content1, &QObject::destroyed, &loop, &QEventLoop::quit );
607   loop.exec();
608   QVERIFY( !content1 );
609   QVERIFY( QgsTestExternalStorage::sCurrentStoredContent );
610 
611   // cancel while storing second file
612   QgsTestExternalStorage::sCurrentStoredContent->cancel();
613 
614   QVERIFY( useLink == w.mLinkLabel->isVisible() );
615   QVERIFY( useLink == w.mLinkEditButton->isVisible() );
616   if ( useLink ) QCOMPARE( w.mLinkEditButton->icon(), editIcon );
617   QVERIFY( useLink != w.mLineEdit->isVisible() );
618   QVERIFY( w.mFileWidgetButton->isVisible() );
619   QVERIFY( w.mFileWidgetButton->isEnabled() );
620   QVERIFY( !w.mProgressLabel->isVisible() );
621   QVERIFY( !w.mProgressBar->isVisible() );
622   QVERIFY( !w.mCancelButton->isVisible() );
623   if ( useLink )
624     QVERIFY( w.mLinkLabel->text().isEmpty() );
625   else
626     QVERIFY( w.mLineEdit->text().isEmpty() );
627 
628   // wait for second file content to be destroyed
629   connect( QgsTestExternalStorage::sCurrentStoredContent, &QObject::destroyed, &loop, &QEventLoop::quit );
630   loop.exec();
631   QVERIFY( !QgsTestExternalStorage::sCurrentStoredContent );
632 }
633 
634 
testStoringChangeFeature()635 void TestQgsExternalStorageFileWidget::testStoringChangeFeature()
636 {
637   // test widget with external storage to store files with different features
638 
639   QgsExternalStorageFileWidget w;
640   w.show();
641 
642   QgsFields fields;
643   fields.append( QgsField( QStringLiteral( "myfield" ), QVariant::String ) );
644 
645   QgsFeature f1( fields );
646   f1.setAttribute( QStringLiteral( "myfield" ), QStringLiteral( "val1" ) );
647 
648   w.setStorageType( "test" );
649   w.setStorageUrlExpression( "'http://test.url.com/' || attribute( @current_feature, 'myfield' )" );
650 
651   QgsExpressionContext expressionContext;
652   expressionContext.appendScope( QgsExpressionContextUtils::formScope( f1 ) );
653   w.setExpressionContext( expressionContext );
654 
655   w.setUseLink( false );
656   w.setReadOnly( false );
657 
658   w.setSelectedFileNames( QStringList() << QStringLiteral( "blank" ) );
659 
660   QgsTestExternalStorage::sCurrentStoredContent->finish();
661 
662   QCOMPARE( w.mLineEdit->text(), QStringLiteral( "http://test.url.com/val1" ) );
663 
664   QgsFeature f2( fields );
665   f2.setAttribute( QStringLiteral( "myfield" ), QStringLiteral( "val2" ) );
666 
667   QgsExpressionContext expressionContext2;
668   expressionContext2.appendScope( QgsExpressionContextUtils::formScope( f2 ) );
669   w.setExpressionContext( expressionContext2 );
670 
671   w.setSelectedFileNames( QStringList() << QStringLiteral( "blank" ) );
672 
673   QgsTestExternalStorage::sCurrentStoredContent->finish();
674 
675   QCOMPARE( w.mLineEdit->text(), QStringLiteral( "http://test.url.com/val2" ) );
676 }
677 
testStoringBadExpression_data()678 void TestQgsExternalStorageFileWidget::testStoringBadExpression_data()
679 {
680   QTest::addColumn<bool>( "useLink" );
681 
682   QTest::newRow( "use link" ) << true;
683   QTest::newRow( "don't use link" ) << false;
684 }
685 
testStoringBadExpression()686 void TestQgsExternalStorageFileWidget::testStoringBadExpression()
687 {
688   // test widget when an external storage is used and the given expression if incorrect
689 
690   QFETCH( bool, useLink );
691 
692   QgsExternalStorageFileWidget w;
693   w.show();
694 
695   QIcon editIcon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) );
696 
697   w.setStorageType( "test" );
698   w.setStorageUrlExpression( "'http://test.url.com/test/' || file_name(@not_existing_variable)" );
699 
700   // start edit mode
701   w.setUseLink( useLink );
702   w.setReadOnly( false );
703 
704   QVERIFY( useLink == w.mLinkLabel->isVisible() );
705   QVERIFY( useLink == w.mLinkEditButton->isVisible() );
706   if ( useLink ) QCOMPARE( w.mLinkEditButton->icon(), editIcon );
707   QVERIFY( useLink != w.mLineEdit->isVisible() );
708   QVERIFY( w.mFileWidgetButton->isVisible() );
709   QVERIFY( w.mFileWidgetButton->isEnabled() );
710   QVERIFY( !w.mProgressLabel->isVisible() );
711   QVERIFY( !w.mProgressBar->isVisible() );
712   QVERIFY( !w.mCancelButton->isVisible() );
713 
714   w.setSelectedFileNames( QStringList() << QStringLiteral( "myfile" ) );
715 
716   QVERIFY( !QgsTestExternalStorage::sCurrentStoredContent );
717 
718   QVERIFY( useLink == w.mLinkLabel->isVisible() );
719   QVERIFY( useLink == w.mLinkEditButton->isVisible() );
720   if ( useLink ) QCOMPARE( w.mLinkEditButton->icon(), editIcon );
721   QVERIFY( useLink != w.mLineEdit->isVisible() );
722   QVERIFY( w.mFileWidgetButton->isVisible() );
723   QVERIFY( w.mFileWidgetButton->isEnabled() );
724   QVERIFY( !w.mProgressLabel->isVisible() );
725   QVERIFY( !w.mProgressBar->isVisible() );
726   QVERIFY( !w.mCancelButton->isVisible() );
727 
728   // link is not updated
729   QVERIFY( w.mLinkLabel->text().isEmpty() );
730 }
731 
testStoringDirectory_data()732 void TestQgsExternalStorageFileWidget::testStoringDirectory_data()
733 {
734   QTest::addColumn<bool>( "useLink" );
735 
736   QTest::newRow( "use link" ) << true;
737   QTest::newRow( "don't use link" ) << false;
738 }
739 
testStoringDirectory()740 void TestQgsExternalStorageFileWidget::testStoringDirectory()
741 {
742   // test widget when storing a directory with an external storage
743   QEventLoop loop;
744   QFETCH( bool, useLink );
745 
746   QgsExternalStorageFileWidget w;
747   w.show();
748 
749   QIcon editIcon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) );
750 
751   w.setStorageType( "test" );
752   w.setStorageUrlExpression( "'http://test.url.com/test/'" );
753   w.setStorageMode( QgsFileWidget::GetDirectory );
754 
755   // start edit mode
756   w.setUseLink( useLink );
757   w.setReadOnly( false );
758 
759   QVERIFY( useLink == w.mLinkLabel->isVisible() );
760   QVERIFY( useLink == w.mLinkEditButton->isVisible() );
761   if ( useLink ) QCOMPARE( w.mLinkEditButton->icon(), editIcon );
762   QVERIFY( useLink != w.mLineEdit->isVisible() );
763   QVERIFY( w.mFileWidgetButton->isVisible() );
764   QVERIFY( w.mFileWidgetButton->isEnabled() );
765   QVERIFY( !w.mProgressLabel->isVisible() );
766   QVERIFY( !w.mProgressBar->isVisible() );
767   QVERIFY( !w.mCancelButton->isVisible() );
768 
769   w.setSelectedFileNames( QStringList() << "/tmp/mydir" );
770 
771   QVERIFY( QgsTestExternalStorage::sCurrentStoredContent );
772 
773   QVERIFY( !w.mLinkLabel->isVisible() );
774   QVERIFY( !w.mLinkEditButton->isVisible() );
775   QVERIFY( !w.mLineEdit->isVisible() );
776   QVERIFY( !w.mFileWidgetButton->isVisible() );
777   QVERIFY( w.mProgressLabel->isVisible() );
778   QVERIFY( w.mProgressBar->isVisible() );
779   QVERIFY( w.mCancelButton->isVisible() );
780   QVERIFY( w.mLinkLabel->text().isEmpty() );
781 
782   QgsTestExternalStorage::sCurrentStoredContent->finish();
783 
784   QVERIFY( useLink == w.mLinkLabel->isVisible() );
785   QVERIFY( useLink == w.mLinkEditButton->isVisible() );
786   if ( useLink ) QCOMPARE( w.mLinkEditButton->icon(), editIcon );
787   QVERIFY( useLink != w.mLineEdit->isVisible() );
788   QVERIFY( w.mFileWidgetButton->isVisible() );
789   QVERIFY( w.mFileWidgetButton->isEnabled() );
790   QVERIFY( !w.mProgressLabel->isVisible() );
791   QVERIFY( !w.mProgressBar->isVisible() );
792   QVERIFY( !w.mCancelButton->isVisible() );
793   if ( useLink )
794     QCOMPARE( w.mLinkLabel->text(), QStringLiteral( "<a href=\"http://test.url.com/test/mydir/\"></a>" ) );
795   else
796     QCOMPARE( w.mLineEdit->text(), QStringLiteral( "http://test.url.com/test/mydir/" ) );
797 
798   // wait for file content to be destroyed
799   connect( QgsTestExternalStorage::sCurrentStoredContent, &QObject::destroyed, &loop, &QEventLoop::quit );
800   loop.exec();
801   QVERIFY( !QgsTestExternalStorage::sCurrentStoredContent );
802 }
803 
testDragAndDrop()804 void TestQgsExternalStorageFileWidget::testDragAndDrop()
805 {
806   // test widget when drag and droping urls with an external storage
807   QEventLoop loop;
808 
809   QgsExternalStorageFileWidget w;
810   w.show();
811 
812   std::unique_ptr<QMimeData> mime( new QMimeData() );
813   mime->setUrls( QList<QUrl>() << QUrl::fromLocalFile( TEST_DATA_DIR + QStringLiteral( "/bug5598.shp" ) ) );
814 
815   QIcon editIcon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) );
816 
817   QVERIFY( !w.acceptDrops() );
818 
819   w.setReadOnly( true );
820   w.setStorageType( "test" );
821   w.setStorageUrlExpression( "'http://test.url.com/test/' || file_name(@selected_file_path)" );
822   w.setStorageMode( QgsFileWidget::GetFile );
823   QVERIFY( !w.acceptDrops() );
824 
825   // start edit mode
826   w.setUseLink( true );
827   w.setReadOnly( false );
828 
829   QVERIFY( w.mLinkLabel->isVisible() );
830   QVERIFY( w.mLinkEditButton->isVisible() );
831   QCOMPARE( w.mLinkEditButton->icon(), editIcon );
832   QVERIFY( !w.mLineEdit->isVisible() );
833   QVERIFY( w.mFileWidgetButton->isVisible() );
834   QVERIFY( w.mFileWidgetButton->isEnabled() );
835   QVERIFY( !w.mProgressLabel->isVisible() );
836   QVERIFY( !w.mProgressBar->isVisible() );
837   QVERIFY( !w.mCancelButton->isVisible() );
838   QVERIFY( w.acceptDrops() );
839 
840   std::unique_ptr<QDragEnterEvent> dragEvent( new QDragEnterEvent( QPoint( 1, 1 ), Qt::CopyAction, mime.get(), Qt::LeftButton,  Qt::NoModifier ) );
841   w.dragEnterEvent( dragEvent.get() );
842   QVERIFY( dragEvent->isAccepted() );
843 
844   std::unique_ptr<QDropEvent> dropEvent( new QDropEvent( QPoint( 1, 1 ), Qt::CopyAction, mime.get(), Qt::LeftButton,  Qt::NoModifier ) );
845   w.dropEvent( dropEvent.get() );
846   QVERIFY( dropEvent->isAccepted() );
847 
848   QVERIFY( QgsTestExternalStorage::sCurrentStoredContent );
849 
850   QVERIFY( !w.mLinkLabel->isVisible() );
851   QVERIFY( !w.mLinkEditButton->isVisible() );
852   QVERIFY( !w.mLineEdit->isVisible() );
853   QVERIFY( !w.mFileWidgetButton->isVisible() );
854   QVERIFY( w.mProgressLabel->isVisible() );
855   QVERIFY( w.mProgressBar->isVisible() );
856   QVERIFY( w.mCancelButton->isVisible() );
857   QVERIFY( w.mLinkLabel->text().isEmpty() );
858 
859   QgsTestExternalStorage::sCurrentStoredContent->finish();
860 
861   QVERIFY( w.mLinkLabel->isVisible() );
862   QVERIFY( w.mLinkEditButton->isVisible() );
863   QCOMPARE( w.mLinkEditButton->icon(), editIcon );
864   QVERIFY( !w.mLineEdit->isVisible() );
865   QVERIFY( w.mFileWidgetButton->isVisible() );
866   QVERIFY( w.mFileWidgetButton->isEnabled() );
867   QVERIFY( !w.mProgressLabel->isVisible() );
868   QVERIFY( !w.mProgressBar->isVisible() );
869   QVERIFY( !w.mCancelButton->isVisible() );
870   QCOMPARE( w.mLinkLabel->text(), QStringLiteral( "<a href=\"http://test.url.com/test/bug5598.shp\">bug5598.shp</a>" ) );
871 
872   // wait for file content to be destroyed
873   connect( QgsTestExternalStorage::sCurrentStoredContent, &QObject::destroyed, &loop, &QEventLoop::quit );
874   loop.exec();
875   QVERIFY( !QgsTestExternalStorage::sCurrentStoredContent );
876 }
877 
878 
879 QGSTEST_MAIN( TestQgsExternalStorageFileWidget )
880 #include "testqgsexternalstoragefilewidget.moc"
881