1 /***************************************************************************
2     testqgsqueryresultwidget.cpp
3      ----------------------
4     Date                 : Jan 2021
5     Copyright            : (C) 2021 Alessandro Pasotti
6     Email                : elpaso at itopen dot it
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 "qgsqueryresultwidget.h"
20 #include "qgsqueryresultmodel.h"
21 #include "qgsproviderregistry.h"
22 #include "qgsprovidermetadata.h"
23 #include "qgsabstractdatabaseproviderconnection.h"
24 #include <QApplication>
25 #include <QAction>
26 #include <QDialog>
27 #include <QVBoxLayout>
28 
29 class TestQgsQueryResultWidget: public QObject
30 {
31     Q_OBJECT
32   private slots:
33     void initTestCase(); // will be called before the first testfunction is executed.
34     void cleanupTestCase(); // will be called after the last testfunction was executed.
35     void init(); // will be called before each testfunction is executed.
36     void cleanup(); // will be called after every testfunction.
37 
38   private slots:
39     void testWidget();
40     void testWidgetCrash();
41     void testWidgetInvalid();
42     void testCodeEditorApis();
43 
44   private:
45 
46     QgsAbstractDatabaseProviderConnection *makeConn();
47 
48     std::unique_ptr<QgsAbstractDatabaseProviderConnection> mConn;
49 
50 };
51 
makeConn()52 QgsAbstractDatabaseProviderConnection *TestQgsQueryResultWidget::makeConn()
53 {
54   return static_cast<QgsAbstractDatabaseProviderConnection *>( QgsProviderRegistry::instance( )->providerMetadata( QStringLiteral( "postgres" ) )->createConnection( qgetenv( "QGIS_PGTEST_DB" ), QVariantMap() ) );
55 }
56 
initTestCase()57 void TestQgsQueryResultWidget::initTestCase()
58 {
59   QgsApplication::initQgis();
60   mConn.reset( makeConn() );
61   // Prepare data for fetching test
62   mConn->execSql( QStringLiteral( "DROP TABLE IF EXISTS qgis_test.random_big_data" ) );
63   mConn->execSql( QStringLiteral( "SELECT * INTO qgis_test.random_big_data FROM generate_series(1,100000) AS id, md5(random()::text) AS descr" ) );
64 }
65 
cleanupTestCase()66 void TestQgsQueryResultWidget::cleanupTestCase()
67 {
68   mConn.reset( makeConn() );
69   mConn->execSql( QStringLiteral( "DROP TABLE IF EXISTS qgis_test.random_big_data" ) );
70 }
71 
init()72 void TestQgsQueryResultWidget::init()
73 {
74 }
75 
cleanup()76 void TestQgsQueryResultWidget::cleanup()
77 {
78 }
79 
80 // Test do not crash when deleting the result while the model fetcher is running
testWidgetCrash()81 void TestQgsQueryResultWidget::testWidgetCrash()
82 {
83   // Make a copy
84   mConn.reset( makeConn() );
85   auto res = new QgsAbstractDatabaseProviderConnection::QueryResult( mConn->execSql( QStringLiteral( "SELECT * FROM qgis_test.random_big_data" ) ) );
86   auto model = new QgsQueryResultModel( *res );
87   bool exited { false };
88   QTimer::singleShot( 1, model, [ & ] { delete res; } );
89   QTimer::singleShot( 2, model, [ & ] { exited = true; } );
90   while ( ! exited )
91   {
92     model->fetchMore( QModelIndex( ) );
93     QgsApplication::processEvents();
94   }
95   const auto rowCount { model->rowCount( model->index( -1, -1 ) ) };
96   QVERIFY( rowCount > 0 && rowCount < 100000 );
97   delete model;
98 
99   // Test widget closed while fetching
100   auto d = std::make_unique<QDialog>( );
101   QVBoxLayout *l = new QVBoxLayout();
102   QgsQueryResultWidget *w = new QgsQueryResultWidget( d.get(), makeConn() );
103   w->setQuery( QStringLiteral( "SELECT * FROM qgis_test.random_big_data" ) );
104   l->addWidget( w );
105   d->setLayout( l );
106   w->executeQuery();
107   exited = false;
108   QTimer::singleShot( 1, d.get(), [ & ] { exited = true; } );
109   while ( ! exited )
110     QgsApplication::processEvents();
111 }
112 
113 
testWidget()114 void TestQgsQueryResultWidget::testWidget()
115 {
116   auto d = std::make_unique<QDialog>( );
117   QVBoxLayout *l = new QVBoxLayout();
118   QgsQueryResultWidget *w = new QgsQueryResultWidget( d.get(), makeConn() );
119   w->setQuery( QStringLiteral( "SELECT * FROM qgis_test.random_big_data" ) );
120   l->addWidget( w );
121   d->setLayout( l );
122   // Uncomment for interactive testing:
123   //d->exec();
124   w->executeQuery();
125   bool exited = false;
126   connect( w, &QgsQueryResultWidget::firstResultBatchFetched, d.get(), [ & ] { exited = true; } );
127   while ( ! exited )
128     QgsApplication::processEvents();
129   const auto rowCount { w->mModel->rowCount( w->mModel->index( -1, -1 ) ) };
130   QVERIFY( rowCount > 0 && rowCount < 100000 );
131 }
132 
testWidgetInvalid()133 void TestQgsQueryResultWidget::testWidgetInvalid()
134 {
135   const QgsQueryResultWidget w( nullptr, nullptr );
136   Q_UNUSED( w )
137 }
138 
testCodeEditorApis()139 void TestQgsQueryResultWidget::testCodeEditorApis()
140 {
141   auto w = std::make_unique<QgsQueryResultWidget>( nullptr, makeConn() );
142   bool exited = false;
143   connect( w->mApiFetcher.get(), &QgsConnectionsApiFetcher::fetchingFinished, w.get(), [ & ] { exited = true; } );
144   while ( ! exited )
145     QgsApplication::processEvents();
146   QVERIFY( w->mSqlEditor->extraKeywords().contains( QStringLiteral( "qgis_test" ) ) );
147   QVERIFY( w->mSqlEditor->extraKeywords().contains( QStringLiteral( "random_big_data" ) ) );
148   QVERIFY( w->mSqlEditor->extraKeywords().contains( QStringLiteral( "descr" ) ) );
149 
150   // Test feedback interrupt
151   w = std::make_unique<QgsQueryResultWidget>( nullptr, makeConn() );
152   QTimer::singleShot( 0, w.get(), [ & ]
153   {
154     QTest::mousePress( w->mStopButton, Qt::MouseButton::LeftButton );
155   } );
156   connect( w->mApiFetcher.get(), &QgsConnectionsApiFetcher::fetchingFinished, w.get(), [ & ] { exited = true; } );
157   while ( ! exited )
158     QgsApplication::processEvents();
159 
160 }
161 
162 
163 QGSTEST_MAIN( TestQgsQueryResultWidget )
164 #include "testqgsqueryresultwidget.moc"
165