5"""Generates a coverage report for given tests.
7Requires that 'use_clang_coverage = true' is set in args.gn.
8Prefers that 'is_component_build = false' is set in args.gn.
12from collections
import namedtuple
21 os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
29TestSpec = namedtuple(
'TestSpec',
'binary, use_test_runner, opt_args')
34 TestSpec(
'pdfium_unittests',
False, []),
35 'pdfium_embeddertests':
36 TestSpec(
'pdfium_embeddertests',
False, []),
38 TestSpec(
'run_corpus_tests.py',
True, []),
39 'corpus_tests_javascript_disabled':
40 TestSpec(
'run_corpus_tests.py',
True, [
'--disable-javascript']),
41 'corpus_tests_xfa_disabled':
42 TestSpec(
'run_corpus_tests.py',
True, [
'--disable-xfa']),
43 'corpus_tests_render_oneshot':
44 TestSpec(
'run_corpus_tests.py',
True, [
'--render-oneshot']),
45 'corpus_tests_reverse_byte_order':
46 TestSpec(
'run_corpus_tests.py',
True, [
'--reverse-byte-order']),
48 TestSpec(
'run_javascript_tests.py',
True, []),
49 'javascript_tests_javascript_disabled':
50 TestSpec(
'run_javascript_tests.py',
True, [
'--disable-javascript']),
51 'javascript_tests_xfa_disabled':
52 TestSpec(
'run_javascript_tests.py',
True, [
'--disable-xfa']),
54 TestSpec(
'run_pixel_tests.py',
True, []),
55 'pixel_tests_javascript_disabled':
56 TestSpec(
'run_pixel_tests.py',
True, [
'--disable-javascript']),
57 'pixel_tests_xfa_disabled':
58 TestSpec(
'run_pixel_tests.py',
True, [
'--disable-xfa']),
59 'pixel_tests_render_oneshot':
60 TestSpec(
'run_pixel_tests.py',
True, [
'--render-oneshot']),
61 'pixel_tests_reverse_byte_order':
62 TestSpec(
'run_pixel_tests.py',
True, [
'--reverse-byte-order']),
69 """Initialize executor based on the current script environment
72 parser: argparse.ArgumentParser for handling improper inputs.
73 args: Dictionary of arguments passed into the calling script.
83 'llvm-build',
'Release+Asserts',
'bin')
85 parser.error(
"Cannot find LLVM bin directory , expected it to be in '%s'"
96 'No valid tests in set to be run. This is likely due to bad command '
99 if not common.GetBooleanGnArg(
'use_clang_coverage', self.
build_directory,
102 'use_clang_coverage does not appear to be set to true for build, but '
112 parser.error(
'%s is not empty, cowardly refusing to continue' %
118 """Dry run aware wrapper of subprocess.check_output()"""
120 print(
"Would have run '%s'" %
' '.join(args))
123 output = subprocess.check_output(args, env=env)
126 print(
"check_output(%s) returned '%s'" % (args, output))
129 def call(self, args, dry_run=False, env=None):
130 """Dry run aware wrapper of subprocess.call()"""
132 print(
"Would have run '%s'" %
' '.join(args))
135 output = subprocess.call(args, env=env)
138 print(
'call(%s) returned %s' % (args, output))
142 """Dry run aware wrapper of subprocess.call() that eats output from call"""
144 print(
"Would have run '%s'" %
' '.join(args))
147 with open(os.devnull,
'w')
as f:
148 output = subprocess.call(args, env=env, stdout=f)
151 print(
'call_silent(%s) returned %s' % (args, output))
155 """Determine which tests should be run."""
158 tests = args[
'tests']
if args[
'tests']
else COVERAGE_TESTS.keys()
160 build_targets =
set()
162 test_spec = COVERAGE_TESTS[name]
163 if test_spec.use_test_runner:
164 binary_path = os.path.join(testing_tools_directory, test_spec.binary)
165 build_targets.add(
'pdfium_diff')
166 build_targets.add(
'pdfium_test')
169 build_targets.add(name)
170 coverage_tests[name] =
TestSpec(binary_path, test_spec.use_test_runner,
173 build_targets =
list(build_targets)
175 return coverage_tests, build_targets
178 """Build all the binaries that are going to be needed for coverage
182 return self.
call(call_args, dry_run=self.
dry_run) == 0
185 """Generate the coverage data for a test
188 name: Name associated with the test to be run. This is used as a label
189 in the coverage data, so should be unique across all of the tests
191 spec: Tuple containing the TestSpec.
194 print(
"Generating coverage for test '%s', using data '%s'" % (name, spec))
195 if not os.path.exists(spec.binary):
196 print(
'Unable to generate coverage for %s, since it appears to not exist'
197 ' @ %s' % (name, spec.binary))
200 binary_args = [spec.binary]
202 binary_args.extend(spec.opt_args)
203 profile_pattern_string =
'%8m'
204 expected_profraw_file =
'%s.%s.profraw' % (name, profile_pattern_string)
206 expected_profraw_file)
209 'LLVM_PROFILE_FILE': expected_profraw_path,
213 if spec.use_test_runner:
219 print(
'Running %s appears to have failed, which might affect '
220 'results' % spec.binary)
225 """Merge raw coverage data sets into one one file for report generation."""
226 llvm_profdata_bin = os.path.join(self.
llvm_directory,
'llvm-profdata')
229 raw_data_pattern =
'*.profraw'
231 if fnmatch.fnmatch(file_name, raw_data_pattern):
235 [llvm_profdata_bin,
'merge',
'-o', self.
prof_data,
'-sparse=true'] +
239 """Generate HTML report by calling upstream coverage.py"""
240 coverage_bin = os.path.join(self.
source_directory,
'tools',
'code_coverage',
246 coverage_args += [
'-o', report_directory]
250 coverage_args += [
'-f',
'core']
251 coverage_args += [
'-f',
'fpdfsdk']
252 coverage_args += [
'-f',
'fxbarcode']
253 coverage_args += [
'-f',
'fxjs']
254 coverage_args += [
'-f',
'public']
255 coverage_args += [
'-f',
'samples']
256 coverage_args += [
'-f',
'xfa']
259 coverage_args += [
'-i',
'.*test.*']
262 coverage_args += [
'--no-component-view']
264 return self.
call([coverage_bin] + coverage_args) == 0
267 """Setup environment, execute the tests and generate coverage report"""
269 print(
'Unable to fetch profiling tools')
273 print(
'Failed to successfully build binaries')
278 print(
'Failed to successfully generate coverage data')
282 print(
'Failed to successfully merge raw coverage results')
286 print(
'Failed to successfully generate HTML report')
292 """Call coverage.py with no args to ensure profiling tools are present."""
299 parser = argparse.ArgumentParser()
300 parser.formatter_class = argparse.RawDescriptionHelpFormatter
301 parser.description =
'Generates a coverage report for given tests.'
305 '--source_directory',
306 help=
'Location of PDFium source directory, defaults to CWD',
308 build_default = os.path.join(
'out',
'Coverage')
313 'Location of PDFium build directory with coverage enabled, defaults to '
314 '%s under CWD' % build_default,
315 default=os.path.join(os.getcwd(), build_default))
316 output_default =
'coverage_report'
319 '--output_directory',
320 help=
'Location to write out coverage report to, defaults to %s under CWD '
322 default=os.path.join(os.getcwd(), output_default))
326 help=
'Output commands instead of executing them',
331 help=
'Output additional diagnostic information',
335 help=
'Tests to be run, defaults to all. Valid entries are %s' %
336 COVERAGE_TESTS.keys(),
339 args = vars(parser.parse_args())
349if __name__ ==
'__main__':
calculate_coverage_tests(self, args)
fetch_profiling_tools(self)
call_silent(self, args, dry_run=False, env=None)
__init__(self, parser, args)
call(self, args, dry_run=False, env=None)
check_output(self, args, dry_run=False, env=None)
generate_html_report(self)
generate_coverage(self, name, spec)
merge_raw_coverage_results(self)
QDebug print(QDebug debug, QSslError::SslError error)
QFuture< QSet< QChar > > set
file open(QIODevice::ReadOnly)