1
- %!encoding: iso-8859-1
2
-
3
-
4
- % These are comments and will not be generated in any output
5
- % -------------------
6
-
7
- %This document is in text2tags format. You can generate html, plain text and
8
- %moinmoin formatted documentation by running txt2tags on this document. See the
9
- %txt2tags home page for more details. Please insert manual line breaks in this
10
- %document as it makes diffing for changes much easier. To do this in vim
11
- %automatically, select a section then issue (gq) command. Please dont
12
- %apply vim formatting to the whole document as it screws up some formatting
13
- %rather apply it selectively to paragraphs where needed.
14
-
15
- % To generate the text version of this document:
16
- % txt2tags -t txt --toc --enum-title -o CODING CODING.t2t
17
- % To generate the moinmoin version of this document
18
- % txt2tags -t moin --toc --enum-title -o CODING.moin CODING.t2t
19
-
20
- % End of comments
1
+ %!encoding: iso-8859-1
2
+
3
+
4
+ % These are comments and will not be generated in any output
5
+ % -------------------
6
+
7
+ %This document is in text2tags format. You can generate html, plain text and
8
+ %moinmoin formatted documentation by running txt2tags on this document. See the
9
+ %txt2tags home page for more details. Please insert manual line breaks in this
10
+ %document as it makes diffing for changes much easier. To do this in vim
11
+ %automatically, select a section then issue (gq) command. Please dont
12
+ %apply vim formatting to the whole document as it screws up some formatting
13
+ %rather apply it selectively to paragraphs where needed.
14
+
15
+ % To generate the text version of this document:
16
+ % txt2tags -t txt --toc --enum-title -o CODING CODING.t2t
17
+ % To generate the moinmoin version of this document
18
+ % txt2tags -t moin --toc --enum-title -o CODING.moin CODING.t2t
19
+
20
+ % End of comments
21
21
% -------------------
22
22
23
23
24
24
%-----------------------------------------------------------------
25
25
% Insert the following preamble on moinmoin generated output
26
26
%-----------------------------------------------------------------
27
27
28
- %/!\ ''' Note:''' Please do not edit this document directly.
28
+ %/!\ ** Note:** Please do not edit this document directly.
29
29
%
30
- %/!\ ''' Note:''' Please do not remove this notice.
30
+ %/!\ ** Note:** Please do not remove this notice.
31
31
%
32
32
%(!) This document was generated using text2tags from INSTALL.t2t in the QGIS sources. Make your
33
33
% edits to that file and use t2t to regenerate in moinmoin %format, then paste the procedure in below.
@@ -58,16 +58,16 @@ Examples:
58
58
```
59
59
60
60
=== Members ===
61
- Class member names begin with a lower case ''m'' and are formed using mixed case.
61
+ Class member names begin with a lower case //m// and are formed using mixed case.
62
62
```
63
63
mMapCanvas
64
64
mCurrentExtent
65
65
```
66
66
67
67
All class members should be private.
68
- ''' Public class members are STRONGLY discouraged'''
68
+ ** Public class members are STRONGLY discouraged**
69
69
=== Accessor Functions ===
70
- Class member values should be obtained through accesssor functions. The function should be named without a '' get'' prefix. Accessor functions for the two private members above would be:
70
+ Class member values should be obtained through accesssor functions. The function should be named without a // get// prefix. Accessor functions for the two private members above would be:
71
71
```
72
72
mapCanvas()
73
73
currentExtent()
@@ -82,7 +82,7 @@ Function names begin with a lowercase letter and are formed using mixed case. Th
82
82
83
83
== Qt Designer ==
84
84
=== Generated Classes ===
85
- QGIS classes that are generated from Qt Designer (ui) files should have a '' Base'' suffix. This identifies the class as a generated base class.
85
+ QGIS classes that are generated from Qt Designer (ui) files should have a // Base// suffix. This identifies the class as a generated base class.
86
86
```
87
87
Examples:
88
88
QgsPluginMangerBase
@@ -91,8 +91,8 @@ Examples:
91
91
=== Dialogs ===
92
92
All dialogs should implement the following:
93
93
* Tooltip help for all toolbar icons and other relevant widgets
94
- * WhatsThis help for ''' all''' widgets on the dialog
95
- * An optional (though highly recommended) context sensitive '' Help'' button that directs the user to the appropriate help page by launching their web browser
94
+ * WhatsThis help for ** all** widgets on the dialog
95
+ * An optional (though highly recommended) context sensitive // Help// button that directs the user to the appropriate help page by launching their web browser
96
96
== C++ Files ==
97
97
=== Names ===
98
98
C++ implementation and header files should be have a .cpp and .h extension respectively.
@@ -261,10 +261,10 @@ So, prefer this:
261
261
262
262
=== Book recommendations ===
263
263
264
- * [http://www.awprofessional.com/title/0321334876 Effective C++ ], Scott Meyers
265
- * [http://www.awprofessional.com/bookstore/product.asp?isbn=020163371X&rl=1 More Effective C++ ], Scott Meyers
266
- * [http://www.awprofessional.com/title/0201749629 Effective STL ], Scott Meyers
267
- * [http://www.awprofessional.com/title/0201634988 Design Patterns ], GoF
264
+ * [Effective C++ http://www.awprofessional.com/title/0321334876], Scott Meyers
265
+ * [More Effective C++ http://www.awprofessional.com/bookstore/product.asp?isbn=020163371X&rl=1], Scott Meyers
266
+ * [Effective STL http://www.awprofessional.com/title/0201749629], Scott Meyers
267
+ * [Design Patterns http://www.awprofessional.com/title/0201634988], GoF
268
268
269
269
270
270
@@ -298,10 +298,10 @@ To check out SVN stable trunk:
298
298
svn co https://svn.qgis.org/repos/qgis/trunk/qgis qgis_unstable
299
299
```
300
300
301
- /!\ ''' Note:''' If you are behind a proxy server, edit your ~/subversion/servers file to specify
301
+ /!\ ** Note:** If you are behind a proxy server, edit your ~/subversion/servers file to specify
302
302
your proxy settings first!
303
303
304
- /!\ ''' Note:''' In QGIS we keep our most stable code in trunk. Periodically we will tag a release
304
+ /!\ ** Note:** In QGIS we keep our most stable code in trunk. Periodically we will tag a release
305
305
off trunk, and then continue stabilisation and selective incorporation of new features into trunk.
306
306
307
307
See the INSTALL file in the source tree for specific instructions on building development versions.
@@ -409,7 +409,7 @@ deal with the patches that are sent to use easily.
409
409
=== Patch file naming ===
410
410
411
411
If the patch is a fix for a specific bug, please name the file with the bug number in it e.g.
412
- ''' bug777fix.diff''' , and attach it to the original bug report in trac (https://svn.qgis.org/trac).
412
+ ** bug777fix.diff** , and attach it to the original bug report in trac (https://svn.qgis.org/trac).
413
413
414
414
If the bug is an enhancement or new feature, its usually a good idea to create a ticket in
415
415
trac (https://svn.qgis.org/trac) first and then attach you
@@ -536,38 +536,38 @@ Unit testing is carried out using a combination of QTestLib (the Qt testing libr
536
536
CTest (a framework for compiling and running tests as part of the CMake build process).
537
537
Lets take an overview of the process before I delve into the details:
538
538
539
- * ''' There is some code you want to test''' , e.g. a class or function. Extreme programming
539
+ * ** There is some code you want to test** , e.g. a class or function. Extreme programming
540
540
advocates suggest that the code should not even be written yet when you start
541
541
building your tests, and then as you implement your code you can immediately validate
542
542
each new functional part you add with your test. In practive you will probably
543
543
need to write tests for pre-existing code in QGIS since we are starting with a testing
544
544
framework well after much application logic has already been implemented.
545
545
546
- * ''' You create a unit test.''' This happens under <QGIS Source Dir>/tests/src/core
546
+ * ** You create a unit test.** This happens under <QGIS Source Dir>/tests/src/core
547
547
in the case of the core lib. The test is basically a client that creates an instance
548
548
of a class and calls some methods on that class. It will check the return from each
549
549
method to make sure it matches the expected value. If any one of the calls fails,
550
550
the unit will fail.
551
551
552
- * ''' You include QtTestLib macros in your test class.''' This macro is processed by
552
+ * ** You include QtTestLib macros in your test class.** This macro is processed by
553
553
the Qt meta object compiler (moc) and expands your test class into a runnable application.
554
554
555
- * ''' You add a section to the CMakeLists.txt''' in your tests directory that will
555
+ * ** You add a section to the CMakeLists.txt** in your tests directory that will
556
556
build your test.
557
557
558
- * ''' You ensure you have ENABLE_TESTING enabled in ccmake / cmakesetup.''' This
558
+ * ** You ensure you have ENABLE_TESTING enabled in ccmake / cmakesetup.** This
559
559
will ensure your tests actually get compiled when you type make.
560
560
561
- * ''' You optionally add test data to <QGIS Source Dir>/tests/testdata''' if your
561
+ * ** You optionally add test data to <QGIS Source Dir>/tests/testdata** if your
562
562
test is data driven (e.g. needs to load a shapefile). These test data should be
563
563
as small as possible and wherever possible you should use the existing datasets
564
564
already there. Your tests should never modify this data in situ, but rather
565
565
may a temporary copy somewhere if needed.
566
566
567
- * ''' You compile your sources and install.''' Do this using normal make && (sudo)
567
+ * ** You compile your sources and install.** Do this using normal make && (sudo)
568
568
make install procedure.
569
569
570
- * ''' You run your tests.''' This is normally done simply by doing ''' make test'''
570
+ * ** You run your tests.** This is normally done simply by doing ** make test**
571
571
after the make install step, though I will explain other aproaches that offer more
572
572
fine grained control over running tests.
573
573
@@ -577,12 +577,493 @@ so all you need to do are the easy bits - writing unit tests!
577
577
578
578
== Creating a unit test ==
579
579
580
+ Creating a unit test is easy - typically you will do this by just creating a
581
+ single .cpp file (not .h file is used) and implement all your test methods as
582
+ public methods that return void. I'll use a simple test class for QgsRasterLayer
583
+ throughout the section that follows to illustrate. By convention we will name our
584
+ test with the same name as the class they are testing but prefixed with 'Test'.
585
+ So our test implementation goes in a file called testqgsrasterlayer.cpp and
586
+ the class itself will be TestQgsRasterLayer. First we add our standard copyright
587
+ banner:
588
+
589
+ ```
590
+ /***************************************************************************
591
+ testqgsvectorfilewriter.cpp
592
+ --------------------------------------
593
+ Date : Frida Nov 23 2007
594
+ Copyright : (C) 2007 by Tim Sutton
595
+ Email : tim@linfiniti.com
596
+ ***************************************************************************
597
+ * *
598
+ * This program is free software; you can redistribute it and/or modify *
599
+ * it under the terms of the GNU General Public License as published by *
600
+ * the Free Software Foundation; either version 2 of the License, or *
601
+ * (at your option) any later version. *
602
+ * *
603
+ ***************************************************************************/
604
+ ```
605
+
606
+ Next we use start our includes needed for the tests we plan to run. There is
607
+ one special include all tests should have:
608
+
609
+ ```
610
+ #include <QtTest>
611
+ ```
612
+
613
+ Beyond that you just continue implementing your class as per normal, pulling
614
+ in whatever headers you may need:
615
+
616
+ ```
617
+ //Qt includes...
618
+ #include <QObject>
619
+ #include <QString>
620
+ #include <QObject>
621
+ #include <QApplication>
622
+ #include <QFileInfo>
623
+ #include <QDir>
624
+
625
+ //qgis includes...
626
+ #include <qgsrasterlayer.h>
627
+ #include <qgsrasterbandstats.h>
628
+ #include <qgsapplication.h>
629
+ ```
630
+
631
+ Since we are combining both class declaration and implementation in a single
632
+ file the class declaration comes next. We start with our doxygen documentation.
633
+ Every test case should be properly documented. We use the doxygen **ingroup**
634
+ directive so that all the UnitTests appear as a module in the generated
635
+ Doxygen documentation. After that comes a short description of the unit test:
636
+
637
+ ```
638
+ /** \ingroup UnitTests
639
+ * This is a unit test for the QgsRasterLayer class.
640
+ */
641
+ ```
642
+
643
+ The class **must** inherit from QObject and include the Q_OBJECT macro.
644
+
645
+ ```
646
+ class TestQgsRasterLayer: public QObject
647
+ {
648
+ Q_OBJECT;
649
+ ```
650
+
651
+ All our test methods are implemented as **private slots**. The QtTest framework
652
+ will sequentially call each private slot method in the test class. There are
653
+ four 'special' methods which if implemented will be called at the start of
654
+ the unit test (**initTestCase**), at the end of the unit test (**cleanupTestCase**).
655
+ Before each test method is called, the **init()** method will be called and
656
+ after each test method is called the **cleanup()** method is called. These
657
+ methods are handy in that they allow you to allocate and cleanup resources
658
+ prior to running each test, and the test unit as a whole.
659
+
660
+
661
+ ```
662
+ private slots:
663
+ // will be called before the first testfunction is executed.
664
+ void initTestCase();
665
+ // will be called after the last testfunction was executed.
666
+ void cleanupTestCase(){};
667
+ // will be called before each testfunction is executed.
668
+ void init(){};
669
+ // will be called after every testfunction.
670
+ void cleanup();
671
+ ```
672
+
673
+ Then come your test methods, all of which should take **no parameters** and
674
+ should **return void**. The methods will be called in order of declaration.
675
+ I am implementing two methods here which illustrates to types of testing. In
676
+ the first case I want to generally test the various parts of the class are
677
+ working, I can use a **functional testing** approach. Once again, extreme
678
+ programmers would advocate writing these tests **before** implementing the
679
+ class. Then as you work your way through your class implementation you
680
+ iteratively run your unit tests. More and more test functions should complete
681
+ sucessfully as your class implementation work progresses, and when the whole
682
+ unit test passes, your new class is done and is now complete with a repeatable
683
+ way to validate it.
684
+
685
+ Typically your unit tests would only cover the **public** API of your
686
+ class, and normally you do not need to write tests for accessors and mutators.
687
+ If it should happen that an acccessor or mutator is not working as expected
688
+ you would normally implement a **regression** test to check for this (see
689
+ lower down).
690
+
691
+ ```
692
+ //
693
+ // Functional Testing
694
+ //
695
+
696
+ /** Check if a raster is valid. */
697
+ void isValid();
698
+
699
+ // more functional tests here ...
700
+ ```
701
+
702
+ Next we implement our **regression tests**. Regression tests should be
703
+ implemented to replicate the conditions of a particular bug. For example
704
+ I recently received a report by email that the cell count by rasters was
705
+ off by 1, throwing off all the statistics for the raster bands. I opened
706
+ a bug (ticket #832) and then created a regression test that replicated
707
+ the bug using a small test dataset (a 10x10 raster). Then I ran the test
708
+ and ran it, verifying that it did indeed fail (the cell count was 99
709
+ instead of 100). Then I went to fix the bug and reran the unit test and
710
+ the regression test passed. I committed the regression test along with
711
+ the bug fix. Now if anybody breakes this in the source code again in the
712
+ future, we can immediatly identify that the code has regressed. Better
713
+ yet before committing any changes in the future, running our tests will
714
+ ensure our changes dont have unexpected side effects - like breaking
715
+ existing functionality.
716
+
717
+ There is one more benifit to regression tests - they can save you time.
718
+ If you ever fixed a bug that involved making changes to the source,
719
+ and then running the application and performing a series of convoluted
720
+ steps to replicate the issue, it will be immediately apparent that
721
+ simply implementing your regression test **before** fixing the bug
722
+ will let you automate the testing for bug resolution in an efficient
723
+ manner.
724
+
725
+ To implement your regression test, you should follow the naming
726
+ convention of regression<TicketID> for your test functions. If no
727
+ trac ticket exists for the regression, you should create one first.
728
+ Using this approach allows the person running a failed regression
729
+ test easily go and find out more information.
730
+
731
+ ```
732
+ //
733
+ // Regression Testing
734
+ //
735
+
736
+ /** This is our second test case...to check if a raster
737
+ reports its dimensions properly. It is a regression test
738
+ for ticket #832 which was fixed with change r7650.
739
+ */
740
+ void regression832();
741
+
742
+ // more regression tests go here ...
743
+ ```
744
+
745
+ Finally in our test class declaration you can declare privately
746
+ any data members and helper methods your unit test may need. In our
747
+ case I will declare a QgsRasterLayer * which can be used by any
748
+ of our test methods. The raster layer will be created in the
749
+ initTestCase() function which is run before any other tests, and then
750
+ destroyed using cleanupTestCase() which is run after all tests. By
751
+ declaring helper methods (which may be called by various test
752
+ functions) privately, you can ensure that they wont be automatically
753
+ run by the QTest executeable that is created when we compile our test.
754
+
755
+ ```
756
+ private:
757
+ // Here we have any data structures that may need to
758
+ // be used in many test cases.
759
+ QgsRasterLayer * mpLayer;
760
+ };
761
+
762
+ ```
763
+
764
+ That ends our class declaration. The implementation is simply
765
+ inlined in the same file lower down. First our init and cleanup functions:
766
+
767
+ ```
768
+ void TestQgsRasterLayer::initTestCase()
769
+ {
770
+ // init QGIS's paths - true means that all path will be inited from prefix
771
+ QString qgisPath = QCoreApplication::applicationDirPath ();
772
+ QgsApplication::setPrefixPath(qgisPath, TRUE);
773
+ #ifdef Q_OS_LINUX
774
+ QgsApplication::setPkgDataPath(qgisPath + "/../share/qgis");
775
+ #endif
776
+ //create some objects that will be used in all tests...
777
+
778
+ std::cout << "Prefix PATH: " << QgsApplication::prefixPath().toLocal8Bit().data() << std::endl;
779
+ std::cout << "Plugin PATH: " << QgsApplication::pluginPath().toLocal8Bit().data() << std::endl;
780
+ std::cout << "PkgData PATH: " << QgsApplication::pkgDataPath().toLocal8Bit().data() << std::endl;
781
+ std::cout << "User DB PATH: " << QgsApplication::qgisUserDbFilePath().toLocal8Bit().data() << std::endl;
782
+
783
+ //create a raster layer that will be used in all tests...
784
+ QString myFileName (TEST_DATA_DIR); //defined in CmakeLists.txt
785
+ myFileName = myFileName + QDir::separator() + "tenbytenraster.asc";
786
+ QFileInfo myRasterFileInfo ( myFileName );
787
+ mpLayer = new QgsRasterLayer ( myRasterFileInfo.filePath(),
788
+ myRasterFileInfo.completeBaseName() );
789
+ }
790
+
791
+ void TestQgsRasterLayer::cleanupTestCase()
792
+ {
793
+ delete mpLayer;
794
+ }
795
+
796
+ ```
797
+
798
+ The above init function illustrates a couple of interesting things.
799
+
800
+ 1. I needed to manually set the QGIS application data path so that
801
+ resources such as srs.db can be found properly.
802
+ 2. Secondly, this is a data driven test so we needed to provide a
803
+ way to generically locate the 'tenbytenraster.asc file. This was
804
+ achieved by using the compiler define **TEST_DATA_PATH**. The
805
+ define is created in the CMakeLists.txt configuration file under
806
+ <QGIS Source Root>/tests/CMakeLists.txt and is available to all
807
+ QGIS unit tests. If you need test data for your test, commit it
808
+ under <QGIS Source Root>/tests/testdata. You should only commit
809
+ very small datasets here. If your test needs to modify the test
810
+ data, it should make a copy of if first.
811
+
812
+ Qt also provides some other interesting mechanisms for data driven
813
+ testing, so if you are interested to know more on the topic, consult
814
+ the Qt documentation.
815
+
816
+ Next lets look at our functional test. The isValid() test simply
817
+ checks the raster layer was correctly loaded in the initTestCase.
818
+ QVERIFY is a Qt macro that you can use to evaluate a test condition.
819
+ There are a few other use macros Qt provide for use in your tests
820
+ including:
821
+
822
+ ```
823
+ QCOMPARE ( actual, expected )
824
+ QEXPECT_FAIL ( dataIndex, comment, mode )
825
+ QFAIL ( message )
826
+ QFETCH ( type, name )
827
+ QSKIP ( description, mode )
828
+ QTEST ( actual, testElement )
829
+ QTEST_APPLESS_MAIN ( TestClass )
830
+ QTEST_MAIN ( TestClass )
831
+ QTEST_NOOP_MAIN ()
832
+ QVERIFY2 ( condition, message )
833
+ QVERIFY ( condition )
834
+ QWARN ( message )
835
+ ```
836
+
837
+ Some of these macros are useful only when using the Qt framework
838
+ for data driven testing (see the Qt docs for more detail).
839
+
840
+ ```
841
+ void TestQgsRasterLayer::isValid()
842
+ {
843
+ QVERIFY ( mpLayer->isValid() );
844
+ }
845
+ ```
846
+
847
+ Normally your functional tests would cover all the range of
848
+ functionality of your classes public API where feasible. With our
849
+ functional tests out the way, we can look at our regression test example.
850
+
851
+ Since the issue in bug #832 is a misreported cell count, writing
852
+ our test if simply a matter of using QVERIFY to check that the
853
+ cell count meets the expected value:
854
+
855
+ ```
856
+ void TestQgsRasterLayer::regression832()
857
+ {
858
+ QVERIFY ( mpLayer->getRasterXDim() == 10 );
859
+ QVERIFY ( mpLayer->getRasterYDim() == 10 );
860
+ // regression check for ticket #832
861
+ // note getRasterBandStats call is base 1
862
+ QVERIFY ( mpLayer->getRasterBandStats(1).elementCountInt == 100 );
863
+ }
864
+ ```
865
+
866
+ With all the unit test functions implemented, there one final thing we
867
+ need to add to our test class:
868
+
869
+ ```
870
+ QTEST_MAIN(TestQgsRasterLayer)
871
+ #include "moc_testqgsrasterlayer.cxx"
872
+ ```
873
+
874
+ The purpose of these two lines is to signal to Qt's moc that his is a
875
+ QtTest (it will generate a main method that in turn calls each test funtion.
876
+ The last line is the include for the MOC generated sources. You should
877
+ replace 'testqgsrasterlayer' with the name of your class in lower case.
878
+
580
879
== Adding your unit test to CMakeLists.txt ==
581
880
881
+ Adding your unit test to the build system is simply a matter of editing
882
+ the CMakeLists.txt in the test directory, cloning one of the existing
883
+ test blocks, and then search and replacing your test class name into it.
884
+ For example:
885
+
886
+ ```
887
+ #
888
+ # QgsRasterLayer test
889
+ #
890
+ SET(qgis_rasterlayertest_SRCS testqgsrasterlayer.cpp)
891
+ SET(qgis_rasterlayertest_MOC_CPPS testqgsrasterlayer.cpp)
892
+ QT4_WRAP_CPP(qgis_rasterlayertest_MOC_SRCS ${qgis_rasterlayertest_MOC_CPPS})
893
+ ADD_CUSTOM_TARGET(qgis_rasterlayertestmoc ALL DEPENDS ${qgis_rasterlayertest_MOC_SRCS})
894
+ ADD_EXECUTABLE(qgis_rasterlayertest ${qgis_rasterlayertest_SRCS})
895
+ ADD_DEPENDENCIES(qgis_rasterlayertest qgis_rasterlayertestmoc)
896
+ TARGET_LINK_LIBRARIES(qgis_rasterlayertest ${QT_LIBRARIES} qgis_core)
897
+ INSTALL(TARGETS qgis_rasterlayertest RUNTIME DESTINATION ${QGIS_BIN_DIR})
898
+ ADD_TEST(qgis_rasterlayertest ${QGIS_BIN_DIR}/qgis_rasterlayertest)
899
+ ```
900
+
901
+ I'll run through these lines briefly to explain what they do, but if
902
+ you are not interested, just clone the block, search and replace e.g.
903
+
904
+ ```
905
+ :'<,'>s/rasterlayer/mynewtest/g
906
+ ```
907
+
908
+ Lets look a little more in detail at the individual lines. First we
909
+ define the list of sources for our test. Since we have only one source file
910
+ (following the methodology I described above where class declaration and
911
+ definition are in the same file) its a simple statement:
912
+
913
+ ```
914
+ SET(qgis_rasterlayertest_SRCS testqgsrasterlayer.cpp)
915
+ ```
916
+
917
+ Since our test class needs to be run through the Qt meta object compiler (moc)
918
+ we need to provide a couple of lines to make that happen too:
919
+
920
+ ```
921
+ SET(qgis_rasterlayertest_MOC_CPPS testqgsrasterlayer.cpp)
922
+ QT4_WRAP_CPP(qgis_rasterlayertest_MOC_SRCS ${qgis_rasterlayertest_MOC_CPPS})
923
+ ADD_CUSTOM_TARGET(qgis_rasterlayertestmoc ALL DEPENDS ${qgis_rasterlayertest_MOC_SRCS})
924
+ ```
925
+
926
+ Next we tell cmake that it must make an executeable from the test class.
927
+ Remember in the previous section on the last line of the class implementation
928
+ I included the moc outputs directly into our test class, so that will
929
+ give it (among other things) a main method so the class can be
930
+ compiled as an executeable:
931
+
932
+ ```
933
+ ADD_EXECUTABLE(qgis_rasterlayertest ${qgis_rasterlayertest_SRCS})
934
+ ADD_DEPENDENCIES(qgis_rasterlayertest qgis_rasterlayertestmoc)
935
+ ```
936
+
937
+ Next we need to specify any library dependencies. At the moment classes
938
+ have been implemented with a catch-all QT_LIBRARIES dependency, but I will
939
+ be working to replace that with the specific Qt libraries that each class
940
+ needs only. Of course you also need to link to the relevant qgis
941
+ libraries as required by your unit test.
942
+
943
+ ```
944
+ TARGET_LINK_LIBRARIES(qgis_rasterlayertest ${QT_LIBRARIES} qgis_core)
945
+ ```
946
+
947
+ Next I tell cmake to the same place as the qgis binaries itself. This
948
+ is something I plan to remove in the future so that the tests can
949
+ run directly from inside the source tree.
950
+
951
+ ```
952
+ INSTALL(TARGETS qgis_rasterlayertest RUNTIME DESTINATION ${QGIS_BIN_DIR})
953
+ ```
954
+
955
+ Finally here is where the best magic happens - we register the class with
956
+ ctest. If you recall in the overview I gave in the beginning of this
957
+ section we are using both QtTest and CTest together. To recap, **QtTest** adds a
958
+ main method to your test unit and handles calling your test methods within
959
+ the class. It also provides some macros like QVERIFY that you can use as
960
+ to test for failure of the tests using conditions. The output from
961
+ a QtTest unit test is an executeable which you can run from the command line.
962
+ However when you have a suite of tests and you want to run each executeable
963
+ in turn, and better yet integrate running tests into the build process,
964
+ the **CTest** is what we use. The next line registers the unit test with
965
+ CMake / CTest.
966
+
967
+ ```
968
+ ADD_TEST(qgis_rasterlayertest ${QGIS_BIN_DIR}/qgis_rasterlayertest)
969
+ ```
970
+
971
+ The last thing I should add is that if your test requires optional
972
+ parts of the build process (e.g. Postgresql support, GSL libs, GRASS etc.),
973
+ you should take care to enclose you test block inside a IF () block
974
+ in the CMakeLists.txt file.
975
+
976
+
582
977
== Building your unit test ==
583
978
979
+ To build the unit test you need only to make sure that ENABLE_TESTS=true
980
+ in the cmake configuration. There are two ways to do this:
981
+
982
+ 1. Run ccmake .. (cmakesetup .. under windows) and interactively set
983
+ the ENABLE_TESTS flag to ON.
984
+ 1. Add a command line flag to cmake e.g. cmake -DENABLE_TESTS=true ..
985
+
986
+ Other than that, just build QGIS as per normal and the tests should build
987
+ too.
988
+
584
989
== Run your tests ==
585
990
991
+ The simplest way to run the tests is as part of your normal build process:
992
+
993
+ ```
994
+ make && make install && make test
995
+ ```
996
+
997
+ The make test command will invoke CTest which will run each test that
998
+ was registered using the ADD_TEST CMake directive described above. Typical
999
+ output from make test will look like this:
1000
+
1001
+ ```
1002
+ Running tests...
1003
+ Start processing tests
1004
+ Test project /Users/tim/dev/cpp/qgis/build
1005
+ 1/ 3 Testing qgis_applicationtest ***Exception: Other
1006
+ 2/ 3 Testing qgis_filewritertest *** Passed
1007
+ 3/ 3 Testing qgis_rasterlayertest *** Passed
1008
+
1009
+ 0% tests passed, 3 tests failed out of 3
1010
+
1011
+ The following tests FAILED:
1012
+ 1 - qgis_applicationtest (OTHER_FAULT)
1013
+ Errors while running CTest
1014
+ make: *** [test] Error 8
1015
+ ```
1016
+
1017
+ If a test fails, you can use the ctest command to examine more
1018
+ closely why it failed. User the -R option to specify a regex for
1019
+ which tests you want to run and -V to get verbose output:
1020
+
1021
+ ```
1022
+ [build] ctest -R appl -V
1023
+ Start processing tests
1024
+ Test project /Users/tim/dev/cpp/qgis/build
1025
+ Constructing a list of tests
1026
+ Done constructing a list of tests
1027
+ Changing directory into /Users/tim/dev/cpp/qgis/build/tests/src/core
1028
+ 1/ 3 Testing qgis_applicationtest
1029
+ Test command: /Users/tim/dev/cpp/qgis/build/tests/src/core/qgis_applicationtest
1030
+ ********* Start testing of TestQgsApplication *********
1031
+ Config: Using QTest library 4.3.0, Qt 4.3.0
1032
+ PASS : TestQgsApplication::initTestCase()
1033
+ Prefix PATH: /Users/tim/dev/cpp/qgis/build/tests/src/core/../
1034
+ Plugin PATH: /Users/tim/dev/cpp/qgis/build/tests/src/core/..//lib/qgis
1035
+ PkgData PATH: /Users/tim/dev/cpp/qgis/build/tests/src/core/..//share/qgis
1036
+ User DB PATH: /Users/tim/.qgis/qgis.db
1037
+ PASS : TestQgsApplication::getPaths()
1038
+ Prefix PATH: /Users/tim/dev/cpp/qgis/build/tests/src/core/../
1039
+ Plugin PATH: /Users/tim/dev/cpp/qgis/build/tests/src/core/..//lib/qgis
1040
+ PkgData PATH: /Users/tim/dev/cpp/qgis/build/tests/src/core/..//share/qgis
1041
+ User DB PATH: /Users/tim/.qgis/qgis.db
1042
+ QDEBUG : TestQgsApplication::checkTheme() Checking if a theme icon exists:
1043
+ QDEBUG : TestQgsApplication::checkTheme()
1044
+ /Users/tim/dev/cpp/qgis/build/tests/src/core/..//share/qgis/themes/default//mIconProjectionDisabled.png
1045
+ FAIL! : TestQgsApplication::checkTheme() '!myPixmap.isNull()' returned FALSE. ()
1046
+ Loc: [/Users/tim/dev/cpp/qgis/tests/src/core/testqgsapplication.cpp(59)]
1047
+ PASS : TestQgsApplication::cleanupTestCase()
1048
+ Totals: 3 passed, 1 failed, 0 skipped
1049
+ ********* Finished testing of TestQgsApplication *********
1050
+ -- Process completed
1051
+ ***Failed
1052
+
1053
+ 0% tests passed, 1 tests failed out of 1
1054
+
1055
+ The following tests FAILED:
1056
+ 1 - qgis_applicationtest (Failed)
1057
+ Errors while running CTest
1058
+
1059
+ ```
1060
+
1061
+ Well that concludes this section on writing unit tests in QGIS. We hope you
1062
+ will get into the habit of writing test to test new functionality and to
1063
+ check for regressions. Some aspects of the test system (in particular the
1064
+ CMakeLists.txt parts) are still being worked on so that the testing framework
1065
+ works in a truly platform way. I will update this document as things progress.
1066
+
586
1067
= Authors =
587
1068
588
1069
* Tim Sutton (author and editor)
0 commit comments