1 /***************************************************************************
2   testqgsruntimeprofiler.cpp
3   --------------------------------------
4 Date                 : June 2020
5 Copyright            : (C) 2020 by Nyall Dawson
6 Email                : nyall dot dawson at gmail 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 #include "qgstest.h"
16 
17 #include "qgsapplication.h"
18 #include "qgsruntimeprofiler.h"
19 
20 #include <QSignalSpy>
21 
22 class TestQgsRuntimeProfiler: public QObject
23 {
24     Q_OBJECT
25   private slots:
26     void initTestCase();
27     void cleanupTestCase();
28     void testGroups();
29     void threading();
30 
31 };
32 
33 
initTestCase()34 void TestQgsRuntimeProfiler::initTestCase()
35 {
36   //
37   // Runs once before any tests are run
38   //
39   // init QGIS's paths - true means that all path will be inited from prefix
40   QgsApplication::init();
41   QgsApplication::initQgis();
42 }
43 
cleanupTestCase()44 void TestQgsRuntimeProfiler::cleanupTestCase()
45 {
46   QgsApplication::exitQgis();
47 }
48 
testGroups()49 void TestQgsRuntimeProfiler::testGroups()
50 {
51   QgsRuntimeProfiler profiler;
52 
53   QVERIFY( profiler.groups().isEmpty() );
54   QVERIFY( !profiler.groupIsActive( QStringLiteral( "xxx" ) ) );
55 
56   QSignalSpy spy( &profiler, &QgsRuntimeProfiler::groupAdded );
57   profiler.start( QStringLiteral( "task 1" ), QStringLiteral( "group 1" ) );
58 
59   QCOMPARE( profiler.groups().count(), 1 );
60   QVERIFY( profiler.groups().contains( QStringLiteral( "group 1" ) ) );
61   QCOMPARE( spy.count(), 1 );
62   QCOMPARE( spy.at( 0 ).at( 0 ).toString(), QStringLiteral( "group 1" ) );
63   QVERIFY( profiler.groupIsActive( QStringLiteral( "group 1" ) ) );
64 
65   profiler.start( QStringLiteral( "task 2" ), QStringLiteral( "group 2" ) );
66 
67   QCOMPARE( profiler.groups().count(), 2 );
68   QVERIFY( profiler.groups().contains( QStringLiteral( "group 1" ) ) );
69   QVERIFY( profiler.groups().contains( QStringLiteral( "group 2" ) ) );
70   QCOMPARE( spy.count(), 2 );
71   QCOMPARE( spy.at( 1 ).at( 0 ).toString(), QStringLiteral( "group 2" ) );
72   QVERIFY( profiler.groupIsActive( QStringLiteral( "group 2" ) ) );
73   QVERIFY( profiler.groupIsActive( QStringLiteral( "group 1" ) ) );
74 
75   // sub task
76   profiler.start( QStringLiteral( "task 1a" ), QStringLiteral( "group 1" ) );
77   QCOMPARE( profiler.groups().count(), 2 );
78   QCOMPARE( spy.count(), 2 );
79 
80   profiler.end( QStringLiteral( "group 1" ) );
81   QVERIFY( profiler.groupIsActive( QStringLiteral( "group 1" ) ) );
82   profiler.end( QStringLiteral( "group 2" ) );
83   QVERIFY( !profiler.groupIsActive( QStringLiteral( "group 2" ) ) );
84   profiler.end( QStringLiteral( "group 1" ) );
85   QVERIFY( !profiler.groupIsActive( QStringLiteral( "group 1" ) ) );
86 
87   QCOMPARE( profiler.childGroups( QString(), QStringLiteral( "group 1" ) ), QStringList() << QStringLiteral( "task 1" ) );
88   QCOMPARE( profiler.childGroups( QStringLiteral( "task 1" ), QStringLiteral( "group 1" ) ), QStringList() << QStringLiteral( "task 1a" ) );
89   QCOMPARE( profiler.childGroups( QString(), QStringLiteral( "group 2" ) ), QStringList() << QStringLiteral( "task 2" ) );
90 }
91 
92 
93 
94 class ProfileInThread : public QThread
95 {
96 
97   public :
ProfileInThread(QgsRuntimeProfiler * mainProfiler)98     ProfileInThread( QgsRuntimeProfiler *mainProfiler )
99       : mMainProfiler( mainProfiler )
100     {}
101 
run()102     void run() override
103     {
104       QgsScopedRuntimeProfile profile( QStringLiteral( "in thread" ), QStringLiteral( "bg" ) );
105       QVERIFY( mMainProfiler != QgsApplication::profiler() );
106     }
107 
108   private:
109     QgsRuntimeProfiler *mMainProfiler = nullptr;
110 
111 
112 };
113 
threading()114 void TestQgsRuntimeProfiler::threading()
115 {
116   // test that profiling which occurs in a background thread is bubbled up to the main thread runtime profiler
117   QgsApplication::profiler()->clear();
118   QCOMPARE( QgsApplication::profiler()->rowCount(), 0 );
119 
120   QThread *thread = new ProfileInThread( QgsApplication::profiler() );
121   {
122     QgsScopedRuntimeProfile profile( QStringLiteral( "launch thread" ), QStringLiteral( "main" ) );
123 
124     QSignalSpy  spy( QgsApplication::profiler(), &QgsRuntimeProfiler::groupAdded );
125     thread->start();
126     thread->exit();
127 
128     spy.wait();
129     QCOMPARE( spy.count(), 1 );
130     QCOMPARE( spy.at( 0 ).at( 0 ).toString(), QStringLiteral( "bg" ) );
131   }
132 
133   QCOMPARE( QgsApplication::profiler()->rowCount(), 2 );
134   int row1 = QgsApplication::profiler()->data( QgsApplication::profiler()->index( 0, 0 ) ).toString() == QLatin1String( "launch thread" ) ? 0 : 1;
135   QCOMPARE( QgsApplication::profiler()->data( QgsApplication::profiler()->index( row1, 0 ) ).toString(), QStringLiteral( "launch thread" ) );
136   QCOMPARE( QgsApplication::profiler()->data( QgsApplication::profiler()->index( row1, 0 ), QgsRuntimeProfilerNode::Group ).toString(), QStringLiteral( "main" ) );
137   QCOMPARE( QgsApplication::profiler()->data( QgsApplication::profiler()->index( row1 == 0 ? 1 : 0, 0 ) ).toString(), QStringLiteral( "in thread" ) );
138   QCOMPARE( QgsApplication::profiler()->data( QgsApplication::profiler()->index( row1 == 0 ? 1 : 0, 0 ), QgsRuntimeProfilerNode::Group ).toString(), QStringLiteral( "bg" ) );
139 }
140 
141 
142 QGSTEST_MAIN( TestQgsRuntimeProfiler )
143 #include "testqgsruntimeprofiler.moc"
144