Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial test framework for running python tests as part of the CTest …
…suite.
- Loading branch information
Showing
6 changed files
with
355 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# Add a python test from a python file | ||
# One cannot simply do: | ||
# SET(ENV{PYTHONPATH} ${LIBRARY_OUTPUT_PATH}) | ||
# SET(my_test "from test_mymodule import *\;test_mymodule()") | ||
# ADD_TEST(PYTHON-TEST-MYMODULE python -c ${my_test}) | ||
# Since cmake is only transmitting the ADD_TEST line to ctest thus you are loosing | ||
# the env var. The only way to store the env var is to physically write in the cmake script | ||
# whatever PYTHONPATH you want and then add the test as 'cmake -P python_test.cmake' | ||
# | ||
# Usage: | ||
# SET_SOURCE_FILES_PROPERTIES(test.py PROPERTIES PYTHONPATH | ||
# "${LIBRARY_OUTPUT_PATH}:${VTK_DIR}") | ||
# ADD_PYTHON_TEST(PYTHON-TEST test.py) | ||
# | ||
# Copyright (c) 2006-2010 Mathieu Malaterre <mathieu.malaterre@gmail.com> | ||
# | ||
# Redistribution and use is allowed according to the terms of the New | ||
# BSD license. | ||
# For details see the accompanying COPYING-CMAKE-SCRIPTS | ||
# | ||
|
||
# Need python interpreter: | ||
FIND_PACKAGE(PythonInterp REQUIRED) | ||
MARK_AS_ADVANCED(PYTHON_EXECUTABLE) | ||
|
||
MACRO(ADD_PYTHON_TEST TESTNAME FILENAME) | ||
GET_SOURCE_FILE_PROPERTY(loc ${FILENAME} LOCATION) | ||
GET_SOURCE_FILE_PROPERTY(pyenv ${FILENAME} PYTHONPATH) | ||
IF(CMAKE_CONFIGURATION_TYPES) | ||
# I cannot use CMAKE_CFG_INTDIR since it expand to "$(OutDir)" | ||
IF(pyenv) | ||
SET(pyenv "${pyenv};${LIBRARY_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}") | ||
ELSE(pyenv) | ||
SET(pyenv ${LIBRARY_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}) | ||
#SET(pyenv ${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}) | ||
#SET(pyenv ${LIBRARY_OUTPUT_PATH}/${CMAKE_CONFIG_TYPE}) | ||
#SET(pyenv ${LIBRARY_OUTPUT_PATH}/\${CMAKE_CONFIG_TYPE}) | ||
ENDIF(pyenv) | ||
ELSE(CMAKE_CONFIGURATION_TYPES) | ||
IF(pyenv) | ||
SET(pyenv ${pyenv}:${LIBRARY_OUTPUT_PATH}) | ||
ELSE(pyenv) | ||
SET(pyenv ${LIBRARY_OUTPUT_PATH}) | ||
ENDIF(pyenv) | ||
ENDIF(CMAKE_CONFIGURATION_TYPES) | ||
STRING(REGEX REPLACE ";" " " wo_semicolumn "${ARGN}") | ||
FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME}.cmake | ||
" | ||
SET(ENV{PYTHONPATH} ${pyenv}:\$ENV{PYTHONPATH}) | ||
SET(ENV{LD_LIBRARY_PATH} ${pyenv}:\$ENV{LD_LIBRARY_PATH}) | ||
MESSAGE(\"${pyenv}\") | ||
EXECUTE_PROCESS( | ||
COMMAND ${PYTHON_EXECUTABLE} ${loc} ${wo_semicolumn} | ||
#WORKING_DIRECTORY @LIBRARY_OUTPUT_PATH@ | ||
RESULT_VARIABLE import_res | ||
OUTPUT_VARIABLE import_output | ||
ERROR_VARIABLE import_output | ||
) | ||
# Pass the output back to ctest | ||
IF(import_output) | ||
MESSAGE("\${import_output}") | ||
ENDIF(import_output) | ||
IF(import_res) | ||
MESSAGE(SEND_ERROR "\${import_res}") | ||
ENDIF(import_res) | ||
" | ||
) | ||
ADD_TEST(${TESTNAME} ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME}.cmake) | ||
ENDMACRO(ADD_PYTHON_TEST) | ||
|
||
# Byte compile recursively a directory (DIRNAME) | ||
MACRO(ADD_PYTHON_COMPILEALL_TEST DIRNAME) | ||
# First get the path: | ||
GET_FILENAME_COMPONENT(temp_path "${PYTHON_LIBRARIES}" PATH) | ||
# Find the python script: | ||
GET_FILENAME_COMPONENT(PYTHON_COMPILE_ALL_PY "${temp_path}/../compileall.py" ABSOLUTE) | ||
# add test, use DIRNAME to create uniq name for the test: | ||
ADD_TEST(COMPILE_ALL-${DIRNAME} ${PYTHON_EXECUTABLE} "${PYTHON_COMPILE_ALL_PY}" -q ${DIRNAME}) | ||
ENDMACRO(ADD_PYTHON_COMPILEALL_TEST) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
INCLUDE(UsePythonTest) | ||
ADD_PYTHON_TEST(QGisApp test_qgisapp.py) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
""" | ||
InaSAFE Disaster risk assessment tool developed by AusAid - | ||
**QGIS plugin implementation.** | ||
Contact : ole.moller.nielsen@gmail.com | ||
.. note:: This program is free software; you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation; either version 2 of the License, or | ||
(at your option) any later version. | ||
.. note:: This source code was copied from the 'postgis viewer' application | ||
with original authors: | ||
Copyright (c) 2010 by Ivan Mincik, ivan.mincik@gista.sk | ||
Copyright (c) 2011 German Carrillo, geotux_tuxman@linuxmail.org | ||
""" | ||
|
||
__author__ = 'tim@linfiniti.com' | ||
__version__ = '0.5.0' | ||
__revision__ = '$Format:%H$' | ||
__date__ = '10/01/2011' | ||
__copyright__ = ('Copyright (c) 2010 by Ivan Mincik, ivan.mincik@gista.sk and ' | ||
'Copyright (c) 2011 German Carrillo, ' | ||
'geotux_tuxman@linuxmail.org') | ||
|
||
|
||
from PyQt4.QtCore import QObject | ||
from qgis.core import QgsMapLayerRegistry | ||
|
||
|
||
class QgisInterface(QObject): | ||
"""Class to expose qgis objects and functionalities to plugins. | ||
This class is here for enabling us to run unit tests only, | ||
so most methods are simply stubs. | ||
""" | ||
|
||
def __init__(self, canvas): | ||
"""Constructor""" | ||
QObject.__init__(self) | ||
self.canvas = canvas | ||
|
||
def zoomFull(self): | ||
"""Zoom to the map full extent""" | ||
pass | ||
|
||
def zoomToPrevious(self): | ||
"""Zoom to previous view extent""" | ||
pass | ||
|
||
def zoomToNext(self): | ||
"""Zoom to next view extent""" | ||
pass | ||
|
||
def zoomToActiveLayer(self): | ||
"""Zoom to extent of active layer""" | ||
pass | ||
|
||
def addVectorLayer(self, vectorLayerPath, baseName, providerKey): | ||
"""Add a vector layer""" | ||
pass | ||
|
||
def addRasterLayer(self, rasterLayerPath, baseName): | ||
"""Add a raster layer given a raster layer file name""" | ||
pass | ||
|
||
def activeLayer(self): | ||
"""Get pointer to the active layer (layer selected in the legend)""" | ||
myLayers = QgsMapLayerRegistry.instance().mapLayers() | ||
for myItem in myLayers: | ||
return myLayers[myItem] | ||
|
||
def addToolBarIcon(self, qAction): | ||
"""Add an icon to the plugins toolbar""" | ||
pass | ||
|
||
def removeToolBarIcon(self, qAction): | ||
"""Remove an action (icon) from the plugin toolbar""" | ||
pass | ||
|
||
def addToolBar(self, name): | ||
"""Add toolbar with specified name""" | ||
pass | ||
|
||
def mapCanvas(self): | ||
"""Return a pointer to the map canvas""" | ||
return self.canvas | ||
|
||
def mainWindow(self): | ||
"""Return a pointer to the main window | ||
In case of QGIS it returns an instance of QgisApp | ||
""" | ||
pass | ||
|
||
def addDockWidget(self, area, dockwidget): | ||
""" Add a dock widget to the main window """ | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import unittest | ||
from utilities import (getQgisTestApp, | ||
setCanvasCrs, | ||
GEOCRS, | ||
GOOGLECRS | ||
) | ||
QGISAPP, CANVAS, IFACE, PARENT = getQgisTestApp() | ||
|
||
class TestQGisApp(unittest.TestCase): | ||
|
||
def testValidThemeName(self): | ||
"""That can set the app to use a valid theme""" | ||
QGISAPP.setThemeName('gis') | ||
myExpectedResult = 'gis' | ||
myResult = QGISAPP.themeName() | ||
myMessage = ('Expected:\n%s\nGot:\n%s\n' % | ||
(myExpectedResult, myResult)) | ||
|
||
mySettings = QGISAPP.showSettings() | ||
print mySettings | ||
|
||
assert myExpectedResult == myResult, myMessage | ||
|
||
def testInvalidThemeName(self): | ||
"""That setting the app to use an invalid theme will fallback to 'default'""" | ||
QGISAPP.setThemeName('fooobar') | ||
myExpectedResult = 'default' | ||
myResult = QGISAPP.themeName() | ||
myMessage = ('Expected:\n%s\nGot:\n%s\n' % | ||
(myExpectedResult, myResult)) | ||
assert myExpectedResult == myResult, myMessage | ||
|
||
|
||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import os | ||
import sys | ||
from PyQt4 import QtGui, QtCore | ||
from qgis.core import (QgsApplication, | ||
QgsVectorLayer, | ||
QgsRasterLayer, | ||
QgsRectangle, | ||
QgsCoordinateReferenceSystem) | ||
from qgis.gui import QgsMapCanvas | ||
from qgis_interface import QgisInterface | ||
import hashlib | ||
|
||
QGISAPP = None # Static vainasafele used to hold hand to running QGis app | ||
CANVAS = None | ||
PARENT = None | ||
IFACE = None | ||
GEOCRS = 4326 # constant for EPSG:GEOCRS Geographic CRS id | ||
GOOGLECRS = 900913 # constant for EPSG:GOOGLECRS Google Mercator id | ||
|
||
|
||
def assertHashesForFile(theHashes, theFilename): | ||
"""Assert that a files has matches one of a list of expected hashes""" | ||
myHash = hashForFile(theFilename) | ||
myMessage = ('Unexpected hash' | ||
'\nGot: %s' | ||
'\nExpected: %s' | ||
'\nPlease check graphics %s visually ' | ||
'and add to list of expected hashes ' | ||
'if it is OK on this platform.' | ||
% (myHash, theHashes, theFilename)) | ||
assert myHash in theHashes, myMessage | ||
|
||
|
||
def assertHashForFile(theHash, theFilename): | ||
"""Assert that a files has matches its expected hash""" | ||
myHash = hashForFile(theFilename) | ||
myMessage = ('Unexpected hash' | ||
'\nGot: %s' | ||
'\nExpected: %s' % (myHash, theHash)) | ||
assert myHash == theHash, myMessage | ||
|
||
|
||
def hashForFile(theFilename): | ||
"""Return an md5 checksum for a file""" | ||
myPath = theFilename | ||
myData = file(myPath).read() | ||
myHash = hashlib.md5() | ||
myHash.update(myData) | ||
myHash = myHash.hexdigest() | ||
return myHash | ||
|
||
|
||
def getQgisTestApp(): | ||
""" Start one QGis application to test agaist | ||
Input | ||
NIL | ||
Output | ||
handle to qgis app | ||
If QGis is already running the handle to that app will be returned | ||
""" | ||
|
||
global QGISAPP # pylint: disable=W0603 | ||
|
||
if QGISAPP is None: | ||
myGuiFlag = True # All test will run qgis in gui mode | ||
QGISAPP = QgsApplication(sys.argv, myGuiFlag) | ||
if 'QGISPATH' in os.environ: | ||
myPath = os.environ['QGISPATH'] | ||
myUseDefaultPathFlag = True | ||
QGISAPP.setPrefixPath(myPath, myUseDefaultPathFlag) | ||
|
||
QGISAPP.initQgis() | ||
s = QGISAPP.showSettings() | ||
print s | ||
|
||
global PARENT # pylint: disable=W0603 | ||
if PARENT is None: | ||
PARENT = QtGui.QWidget() | ||
|
||
global CANVAS # pylint: disable=W0603 | ||
if CANVAS is None: | ||
CANVAS = QgsMapCanvas(PARENT) | ||
CANVAS.resize(QtCore.QSize(400, 400)) | ||
|
||
global IFACE # pylint: disable=W0603 | ||
if IFACE is None: | ||
# QgisInterface is a stub implementation of the QGIS plugin interface | ||
IFACE = QgisInterface(CANVAS) | ||
|
||
return QGISAPP, CANVAS, IFACE, PARENT | ||
|
||
|
||
def unitTestDataPath(theSubdir=None): | ||
"""Return the absolute path to the InaSAFE unit test data dir. | ||
.. note:: This is not the same thing as the SVN inasafe_data dir. Rather | ||
this is a new dataset where the test datasets are all tiny for fast | ||
testing and the datasets live in the same repo as the code. | ||
Args: | ||
* theSubdir: (Optional) Additional subdir to add to the path - typically | ||
'hazard' or 'exposure'. | ||
""" | ||
myPath = __file__ | ||
if theSubdir is not None: | ||
myPath = os.path.abspath(os.path.join(myPath, | ||
'unit_test_data', | ||
theSubdir)) | ||
else: | ||
myPath = os.path.abspath(os.path.join(myPath, 'unit_test_data')) | ||
return myPath | ||
|
||
def setCanvasCrs(theEpsgId, theOtfpFlag=False): | ||
"""Helper to set the crs for the CANVAS before a test is run. | ||
Args: | ||
* theEpsgId - Valid EPSG identifier (int) | ||
* theOtfpFlag - whether on the fly projections should be enabled | ||
on the CANVAS. Default to False. | ||
""" | ||
# Enable on-the-fly reprojection | ||
CANVAS.mapRenderer().setProjectionsEnabled(theOtfpFlag) | ||
|
||
# Create CRS Instance | ||
myCrs = QgsCoordinateReferenceSystem() | ||
myCrs.createFromId(theEpsgId, QgsCoordinateReferenceSystem.EpsgCrsId) | ||
|
||
# Reproject all layers to WGS84 geographic CRS | ||
CANVAS.mapRenderer().setDestinationCrs(myCrs) | ||
|