QGIS/tests/code_layout/test_qgssipcoverage.py
2024-11-29 15:38:02 +01:00

147 lines
5.1 KiB
Python

"""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"
import os
from doxygen_parser import DoxygenParser
# Import all the things!
from qgis.analysis import * # NOQA
from qgis.core import * # NOQA
from qgis.gui import * # NOQA
from qgis.testing import unittest
from termcolor import colored
from utilities import printImportant
try:
from qgis.server import * # NOQA
except:
pass
class TestQgsSipCoverage(unittest.TestCase):
def testCoverage(self):
print("CTEST_FULL_OUTPUT")
prefixPath = os.environ["QGIS_PREFIX_PATH"]
docPath = os.path.join(prefixPath, "..", "doc", "api", "xml")
parser = DoxygenParser(docPath)
# first look for objects without any bindings
objects = {m[0] for m in parser.bindable_members}
missing_objects = []
bound_objects = {}
for o in objects:
try:
if "::" in o:
bound_objects[o] = getattr(
globals()[o.split("::")[0]], o.split("::")[1]
)
else:
bound_objects[o] = globals()[o]
except:
missing_objects.append(o)
missing_objects.sort()
# next check for individual members
parser.bindable_members.sort()
missing_members = []
for m in parser.bindable_members:
if m[0] in bound_objects:
obj = bound_objects[m[0]]
if "::" in m[0] and m[0].split("::")[1] == m[1]:
# skip constructors of nested classes
continue
# try two different methods of checking for member existence
try:
if hasattr(obj, m[1]):
continue
except:
pass
try:
if m[1] in dir(obj):
continue
except:
printImportant(
f"SIP coverage test: something strange happened in {m[0]}.{m[1]}, obj={obj}"
)
missing_members.append(f"{m[0]}.{m[1]}")
missing_members.sort()
if missing_objects:
print("---------------------------------")
print(colored("Missing classes:", "yellow"))
print(
" "
+ "\n ".join(
[colored(obj, "yellow", attrs=["bold"]) for obj in missing_objects]
)
)
if missing_members:
print("---------------------------------")
print(colored("Missing members:", "yellow"))
print(
" "
+ "\n ".join(
[colored(mem, "yellow", attrs=["bold"]) for mem in missing_members]
)
)
# print summaries
missing_class_count = len(missing_objects)
present_count = len(objects) - missing_class_count
coverage = 100.0 * present_count / len(objects)
print("---------------------------------")
printImportant(f"{len(objects)} total bindable classes")
printImportant(f"{present_count} total have bindings")
printImportant(f"Binding coverage by classes {coverage}%")
printImportant("---------------------------------")
printImportant(f"{missing_class_count} classes missing bindings")
print("---------------------------------")
missing_member_count = len(missing_members)
present_count = len(parser.bindable_members) - missing_member_count
coverage = 100.0 * present_count / len(parser.bindable_members)
print("---------------------------------")
printImportant(f"{len(parser.bindable_members)} total bindable members")
printImportant(f"{present_count} total have bindings")
printImportant(f"Binding coverage by members {coverage}%")
printImportant("---------------------------------")
printImportant(f"{missing_member_count} members missing bindings")
self.assertEqual(
missing_class_count,
0,
"""\n\nFAIL: 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""",
)
self.assertEqual(
missing_member_count,
0,
"""\n\nFAIL: new unbound members have been introduced, please add SIP bindings for these members
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()