mkdb: logging more details for entrity expansion
[gtk-doc.git] / gtkdoc / check.py
blobde5eafd4eb4d34179eba9ebff01cbd0d0a948167
1 # -*- python; coding: utf-8 -*-
3 # gtk-doc - GTK DocBook documentation generator.
4 # Copyright (C) 2007 David Nečas
5 # 2007-2017 Stefan Sauer
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 """
23 The check tool runs various tests on built documentation and outputs test
24 results. Can be run druring make check, by adding this to the documentations
25 Makefile.am: TESTS = $(GTKDOC_CHECK).
26 """
28 # Support both Python 2 and 3
29 from __future__ import print_function
31 import os
32 import re
33 from glob import glob
36 class FileFormatError(Exception):
37 pass
40 def grep(regexp, lines, what):
41 pattern = re.compile(regexp)
42 for line in lines:
43 for match in re.finditer(pattern, line):
44 return match.group(1)
45 raise FileFormatError(what)
48 def check_empty(filename):
49 with open(filename) as f:
50 count = sum(1 for line in f if line.strip())
51 return count
54 def check_includes(filename):
55 # Check that each XML file in the xml directory is included in doc_main_file
56 with open(filename) as f:
57 lines = f.read().splitlines()
58 num_missing = 0
59 for include in glob('xml/*.xml'):
60 try:
61 next(line for line in lines if include in line)
62 except StopIteration:
63 num_missing += 1
64 print('%s:1:E: doesn\'t appear to include "%s"' % (filename, include))
66 return num_missing
69 def get_variable(env, lines, variable):
70 value = env.get(variable,
71 grep(r'^\s*' + variable + r'\s*=\s*(\S+)', lines, variable))
72 return value
75 def read_file(filename):
76 with open(filename) as f:
77 return f.read().splitlines()
80 def run_tests(workdir, doc_module, doc_main_file):
81 checks = 4
83 print('Running suite(s): gtk-doc-' + doc_module)
85 # Test #1
86 statusfilename = os.path.join(workdir, doc_module + '-undocumented.txt')
87 statusfile = read_file(statusfilename)
88 try:
89 undocumented = int(grep(r'^(\d+)\s+not\s+documented\.\s*$',
90 statusfile, 'number of undocumented symbols'))
91 incomplete = int(grep(r'^(\d+)\s+symbols?\s+incomplete\.\s*$',
92 statusfile, 'number of incomplete symbols'))
93 except FileFormatError as e:
94 print('Cannot find %s in %s' % (str(e), statusfilename))
95 return checks # consider all failed
97 total = undocumented + incomplete
98 if total:
99 print(doc_module + '-undocumented.txt:1:E: %d undocumented or incomplete symbols' % total)
101 # Test #2
102 undeclared = check_empty(os.path.join(workdir, doc_module + '-undeclared.txt'))
103 if undeclared:
104 print(doc_module + '-undeclared.txt:1:E: %d undeclared symbols\n' % undeclared)
106 # Test #3
107 unused = check_empty(os.path.join(workdir, doc_module + '-unused.txt'))
108 if unused:
109 print(doc_module + '-unused.txt:1:E: %d unused documentation entries\n' % unused)
111 # Test #4
112 missing_includes = check_includes(os.path.join(workdir, doc_main_file))
114 # Test Summary
115 failed = (total > 0) + (undeclared != 0) + (unused != 0) + (missing_includes != 0)
116 rate = 100.0 * (checks - failed) / checks
117 print("%.1f%%: Checks %d, Failures: %d" % (rate, checks, failed))
118 return failed
121 def run(options=None):
122 """Runs the tests.
124 Returns:
125 int: a system exit code.
128 # Get parameters from test env, if not there try to grab them from the makefile
129 # We like Makefile.am more but builddir does not necessarily contain one.
130 makefilename = 'Makefile.am'
131 if not os.path.exists(makefilename):
132 makefilename = 'Makefile'
133 makefile = read_file(makefilename)
135 # For historic reasons tests are launched in srcdir
136 workdir = os.environ.get('BUILDDIR', None)
137 if not workdir:
138 workdir = '.'
140 try:
141 doc_module = get_variable(os.environ, makefile, 'DOC_MODULE')
142 doc_main_file = get_variable(os.environ, makefile, 'DOC_MAIN_SGML_FILE')
143 except FileFormatError as e:
144 print('Cannot find %s in %s' % (str(e), makefilename))
145 return 1
147 doc_main_file = doc_main_file.replace('$(DOC_MODULE)', doc_module)
149 return run_tests(workdir, doc_module, doc_main_file)