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