2015-10-15 18:31:17 +11:00
# -*- coding: utf-8 -*-
""" QGIS Unit tests for SIP binding coverage.
. . 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 .
"""
__author__ = ' Nyall Dawson '
__date__ = ' 15/10/2015 '
__copyright__ = ' Copyright 2015, The QGIS Project '
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = ' $Format: % H$ '
import os
2016-01-06 13:44:29 +01:00
from qgis . testing import unittest
2015-10-15 18:31:17 +11:00
2016-01-06 13:44:29 +01:00
from utilities import printImportant , DoxygenParser
2015-10-15 18:31:17 +11:00
2015-12-11 00:06:51 +01:00
# Import all the things!
2016-03-21 10:51:03 +01:00
from qgis . analysis import * # NOQA
from qgis . core import * # NOQA
from qgis . gui import * # NOQA
from qgis . networkanalysis import * # NOQA
2015-10-15 18:31:17 +11:00
try :
2016-03-21 10:51:03 +01:00
from qgis . server import * # NOQA
2015-10-15 18:31:17 +11:00
except :
pass
# BINDING THRESHOLD
#
# The minimum number of unbound functions in QGIS api
#
# DON'T RAISE THIS THRESHOLD!!!
# (changes which lower this threshold are welcomed though!)
2016-01-04 22:51:18 +11:00
ACCEPTABLE_MISSING_CLASSES = 0
ACCEPTABLE_MISSING_MEMBERS = 0
2015-10-15 18:31:17 +11:00
2016-01-06 13:44:29 +01:00
class TestQgsSipCoverage ( unittest . TestCase ) :
2015-10-15 18:31:17 +11:00
def testCoverage ( self ) :
print ' CTEST_FULL_OUTPUT '
prefixPath = os . environ [ ' QGIS_PREFIX_PATH ' ]
docPath = os . path . join ( prefixPath , ' .. ' , ' doc ' , ' api ' , ' xml ' )
parser = DoxygenParser ( docPath )
2015-12-11 00:06:51 +01:00
# first look for objects without any bindings
2015-10-15 18:31:17 +11:00
objects = set ( [ m [ 0 ] for m in parser . bindable_members ] )
missing_objects = [ ]
bound_objects = { }
for o in objects :
try :
2015-11-09 21:29:50 +11:00
if ' :: ' in o :
bound_objects [ o ] = getattr ( globals ( ) [ o . split ( ' :: ' ) [ 0 ] ] , o . split ( ' :: ' ) [ 1 ] )
else :
bound_objects [ o ] = globals ( ) [ o ]
2015-10-15 18:31:17 +11:00
except :
missing_objects . append ( o )
missing_objects . sort ( )
2015-12-11 00:06:51 +01:00
# next check for individual members
2015-10-15 18:31:17 +11:00
parser . bindable_members . sort ( )
missing_members = [ ]
for m in parser . bindable_members :
if m [ 0 ] in bound_objects :
obj = bound_objects [ m [ 0 ] ]
2015-12-14 17:43:57 +01:00
if " :: " in m [ 0 ] and m [ 0 ] . split ( " :: " ) [ 1 ] == m [ 1 ] :
# skip constructors of nested classes
continue
2015-11-09 21:29:50 +11:00
2015-12-11 00:06:51 +01:00
# try two different methods of checking for member existence
2015-11-09 21:29:50 +11:00
try :
if hasattr ( obj , m [ 1 ] ) :
continue
except :
pass
try :
if m [ 1 ] in dir ( obj ) :
continue
except :
printImportant ( " SIP coverage test: something strange happened in {} . {} , obj= {} " . format ( m [ 0 ] , m [ 1 ] , obj ) )
missing_members . append ( ' {} . {} ' . format ( m [ 0 ] , m [ 1 ] ) )
2015-10-15 18:31:17 +11:00
missing_members . sort ( )
2015-11-09 21:28:23 +11:00
print " --------------------------------- "
print ' Missing classes: \n {} ' . format ( ' \n ' . join ( missing_objects ) )
print " --------------------------------- "
print ' Missing members: \n {} ' . format ( ' \n ' . join ( missing_members ) )
2015-12-11 00:06:51 +01:00
# print summaries
2015-11-09 21:28:23 +11:00
missing_class_count = len ( missing_objects )
present_count = len ( objects ) - missing_class_count
coverage = 100.0 * present_count / len ( objects )
print " --------------------------------- "
printImportant ( " {} total bindable classes " . format ( len ( objects ) ) )
printImportant ( " {} total have bindings " . format ( present_count ) )
printImportant ( " Binding coverage by classes {} % " . format ( coverage ) )
printImportant ( " --------------------------------- " )
printImportant ( " {} classes missing bindings, out of {} allowed " . format ( missing_class_count , ACCEPTABLE_MISSING_CLASSES ) )
print " --------------------------------- "
missing_member_count = len ( missing_members )
present_count = len ( parser . bindable_members ) - missing_member_count
2015-10-15 18:31:17 +11:00
coverage = 100.0 * present_count / len ( parser . bindable_members )
print " --------------------------------- "
printImportant ( " {} total bindable members " . format ( len ( parser . bindable_members ) ) )
printImportant ( " {} total have bindings " . format ( present_count ) )
printImportant ( " Binding coverage by members {} % " . format ( coverage ) )
printImportant ( " --------------------------------- " )
2015-11-09 21:28:23 +11:00
printImportant ( " {} members missing bindings, out of {} allowed " . format ( missing_member_count , ACCEPTABLE_MISSING_MEMBERS ) )
assert missing_class_count < = ACCEPTABLE_MISSING_CLASSES , """ \n \n FAIL: new unbound classes have been introduced, please add SIP bindings for these classes
If these classes are not suitable for the Python bindings , please add the Doxygen tag
" @note not available in Python bindings " to the CLASS Doxygen comments """
2015-10-15 18:31:17 +11:00
2015-11-09 21:28:23 +11:00
assert missing_member_count < = ACCEPTABLE_MISSING_MEMBERS , """ \n \n FAIL: new unbound members have been introduced, please add SIP bindings for these members
2015-10-15 18:31:17 +11:00
If these members are not suitable for the Python bindings , please add the Doxygen tag
" @note not available in Python bindings " to the MEMBER Doxygen comments """
if __name__ == ' __main__ ' :
unittest . main ( )