Skip to content
Permalink
Browse files

Add compliance test to doxygen test

Tests for "non-compliant" doxygen strings, e.g. use of
- "getter for..." instead of "returns the..."
- "setter for..." instead of "sets the..."

Will be expanded in future to also test for:
- "return ..." instead of "returns ..."
- "set ..." instead of "sets ..."
  • Loading branch information
nyalldawson committed May 22, 2018
1 parent 45aca2f commit 8b1bbfe8ae175d2a0a7f196c79f98edb552062cd
Showing with 38 additions and 2 deletions.
  1. +30 −2 tests/code_layout/doxygen_parser.py
  2. +8 −0 tests/code_layout/test_qgsdoccoverage.py
@@ -52,6 +52,7 @@ def __init__(self, path, acceptable_missing={}, acceptable_missing_added_note=[]
self.documentable_members = 0
self.documented_members = 0
self.undocumented_members = {}
self.noncompliant_members = {}
self.bindable_members = []
self.groups = {}
self.classes_missing_group = []
@@ -140,7 +141,7 @@ def parseFile(self, f):
if event == 'end' and elem.tag == 'compounddef':
if self.elemIsPublicClass(elem):
# store documentation status
members, documented, undocumented, bindable, has_brief_description, found_version_added = self.parseClassElem(elem)
members, documented, undocumented, noncompliant, bindable, has_brief_description, found_version_added = self.parseClassElem(elem)
documentable_members += members
documented_members += documented
class_name = elem.find('compoundname').text
@@ -168,6 +169,9 @@ def parseFile(self, f):
self.undocumented_members[class_name]['members'] = members
self.undocumented_members[class_name]['missing_members'] = unacceptable_undocumented

if len(noncompliant) > 0:
self.noncompliant_members[class_name] = noncompliant

# store bindable members
if self.classElemIsBindable(elem):
for m in bindable:
@@ -224,6 +228,7 @@ def parseClassElem(self, e):
documentable_members = 0
documented_members = 0
undocumented_members = set()
noncompliant_members = []
bindable_members = []
# loop through all members
for m in e.getiterator('memberdef'):
@@ -238,6 +243,9 @@ def parseClassElem(self, e):
documentable_members += 1
if self.memberIsDocumented(m):
documented_members += 1
error = self.memberDocIsNonCompliant(m)
if error:
noncompliant_members.append({m.find('name').text: error})
else:
undocumented_members.add(signature)
# test for brief description
@@ -259,7 +267,7 @@ def parseClassElem(self, e):
if found_version_added:
break

return documentable_members, documented_members, undocumented_members, bindable_members, has_brief_description, found_version_added
return documentable_members, documented_members, undocumented_members, noncompliant_members, bindable_members, has_brief_description, found_version_added

def memberSignature(self, elem):
""" Returns the signature for a member
@@ -537,3 +545,23 @@ def memberIsDocumented(self, member_elem):
if doc is not None and list(doc):
return True
return False

def memberDocIsNonCompliant(self, member_elem):
""" Tests whether an member's documentation is non-compliant
:param member_elem: XML element for a class member
"""
for doc_type in ['briefdescription']:
doc = member_elem.find(doc_type)
if doc is not None:
for para in doc.getiterator('para'):
if not para.text:
continue
if para.text.strip().lower().startswith('getter'):
return 'Use "Returns the..." instead of "getter"'
elif para.text.strip().lower().startswith('setter'):
return 'Use "Sets the..." instead of "setter"'
#elif para.text.strip().lower().startswith('return '):
# return 'Use "Returns the..." instead of "return ..."'
#elif para.text.strip().lower().startswith('set '):
# return 'Use "Sets the..." instead of "set ..."'
return False
@@ -60,6 +60,13 @@ def testCoverage(self):
for mem in props['missing_members']:
print((colored(' "' + mem + '"', 'yellow', attrs=['bold'])))

if parser.noncompliant_members:
for cls, props in list(parser.noncompliant_members.items()):
print(('\n\nClass {}, non-compliant members found\n'.format(colored(cls, 'yellow'))))
for p in props:
for mem, error in p.items():
print((colored(' ' + mem + ': ' + error, 'yellow', attrs=['bold'])))

# self.assertEquals(len(parser.undocumented_string), 0, 'FAIL: new undocumented members have been introduced, please add documentation for these members')

if parser.classes_missing_group:
@@ -88,6 +95,7 @@ def testCoverage(self):
self.assertTrue(not parser.classes_missing_group, 'Classes without \\group tag found')
self.assertTrue(not parser.classes_missing_version_added, 'Classes without \\since version tag found')
self.assertTrue(not parser.classes_missing_brief, 'Classes without \\brief description found')
self.assertTrue(not parser.noncompliant_members, 'Non compliant members found')


if __name__ == '__main__':

0 comments on commit 8b1bbfe

Please sign in to comment.
You can’t perform that action at this time.