@@ -33,6 +33,8 @@ def __init__(self):
33
33
self .output_formats = None
34
34
self .coverage_capture = True
35
35
self .coverage_report = True
36
+ self .coverage_per_instance = False
37
+ self .instances = {}
36
38
37
39
@staticmethod
38
40
def factory (tool , jobs = None ):
@@ -156,9 +158,12 @@ def generate(self, outdir):
156
158
coverage_completed = self .capture_data (outdir ) if self .coverage_capture else True
157
159
if not coverage_completed or not self .coverage_report :
158
160
return coverage_completed , {}
161
+ build_dirs = None
162
+ if not self .coverage_capture and self .coverage_report and self .coverage_per_instance :
163
+ build_dirs = [instance .build_dir for instance in self .instances .values ()]
159
164
reports = {}
160
165
with open (os .path .join (outdir , "coverage.log" ), "a" ) as coveragelog :
161
- ret , reports = self ._generate (outdir , coveragelog )
166
+ ret , reports = self ._generate (outdir , coveragelog , build_dirs )
162
167
if ret == 0 :
163
168
report_log = {
164
169
"html" : "HTML report generated: {}" .format (
@@ -269,7 +274,8 @@ def run_lcov(self, args, coveragelog):
269
274
] + parallel + args
270
275
return self .run_command (cmd , coveragelog )
271
276
272
- def _generate (self , outdir , coveragelog ):
277
+
278
+ def _generate (self , outdir , coveragelog , build_dirs = None ):
273
279
coveragefile = os .path .join (outdir , "coverage.info" )
274
280
ztestfile = os .path .join (outdir , "ztest.info" )
275
281
@@ -317,6 +323,10 @@ def __init__(self):
317
323
self .ignore_branch_patterns = []
318
324
self .output_formats = "html"
319
325
self .version = self .get_version ()
326
+ # Different ifdef-ed implementations of the same function should not be
327
+ # in conflict treated by GCOVR as separate objects for coverage statistics.
328
+ self .options = ["-v" , "--merge-mode-functions=separate" ]
329
+
320
330
321
331
def get_version (self ):
322
332
try :
@@ -355,37 +365,29 @@ def _interleave_list(prefix, list):
355
365
def _flatten_list (list ):
356
366
return [a for b in list for a in b ]
357
367
358
- def _generate (self , outdir , coveragelog ):
359
- coverage_file = os .path .join (outdir , "coverage.json" )
360
- coverage_summary = os .path .join (outdir , "coverage_summary.json" )
361
- ztest_file = os .path .join (outdir , "ztest.json" )
362
-
368
+ def collect_coverage (self , outdir , coverage_file , ztest_file , coveragelog ):
363
369
excludes = Gcovr ._interleave_list ("-e" , self .ignores )
364
370
if len (self .ignore_branch_patterns ) > 0 :
365
371
# Last pattern overrides previous values, so merge all patterns together
366
372
merged_regex = "|" .join ([f"({ p } )" for p in self .ignore_branch_patterns ])
367
373
excludes += ["--exclude-branches-by-pattern" , merged_regex ]
368
374
369
- # Different ifdef-ed implementations of the same function should not be
370
- # in conflict treated by GCOVR as separate objects for coverage statistics.
371
- mode_options = ["--merge-mode-functions=separate" ]
372
-
373
375
# We want to remove tests/* and tests/ztest/test/* but save tests/ztest
374
- cmd = ["gcovr" , "-v" , "- r" , self .base_dir ,
376
+ cmd = ["gcovr" , "-r" , self .base_dir ,
375
377
"--gcov-ignore-parse-errors=negative_hits.warn_once_per_file" ,
376
378
"--gcov-executable" , self .gcov_tool ,
377
379
"-e" , "tests/*" ]
378
- cmd += excludes + mode_options + ["--json" , "-o" , coverage_file , outdir ]
380
+ cmd += excludes + self . options + ["--json" , "-o" , coverage_file , outdir ]
379
381
cmd_str = " " .join (cmd )
380
382
logger .debug (f"Running: { cmd_str } " )
381
383
coveragelog .write (f"Running: { cmd_str } \n " )
382
384
coveragelog .flush ()
383
385
ret = subprocess .call (cmd , stdout = coveragelog , stderr = coveragelog )
384
386
if ret :
385
387
logger .error (f"GCOVR failed with { ret } " )
386
- return ret , {}
388
+ return ret , []
387
389
388
- cmd = ["gcovr" , "-v" , "- r" , self .base_dir ] + mode_options
390
+ cmd = ["gcovr" , "-r" , self .base_dir ] + self . options
389
391
cmd += ["--gcov-executable" , self .gcov_tool ,
390
392
"-f" , "tests/ztest" , "-e" , "tests/ztest/test/*" ,
391
393
"--json" , "-o" , ztest_file , outdir ]
@@ -396,12 +398,40 @@ def _generate(self, outdir, coveragelog):
396
398
ret = subprocess .call (cmd , stdout = coveragelog , stderr = coveragelog )
397
399
if ret :
398
400
logger .error (f"GCOVR ztest stage failed with { ret } " )
399
- return ret , {}
401
+ return ret , []
402
+
403
+ return ret , [file_ for file_ in [coverage_file , ztest_file ]
404
+ if os .path .exists (file_ ) and os .path .getsize (file_ ) > 0 ]
405
+
400
406
401
- if os .path .exists (ztest_file ) and os .path .getsize (ztest_file ) > 0 :
402
- files = [coverage_file , ztest_file ]
407
+ def _generate (self , outdir , coveragelog , build_dirs = None ):
408
+ coverage_file = os .path .join (outdir , "coverage.json" )
409
+ coverage_summary = os .path .join (outdir , "coverage_summary.json" )
410
+ ztest_file = os .path .join (outdir , "ztest.json" )
411
+
412
+ ret = 0
413
+ cmd_ = []
414
+ files = []
415
+ if build_dirs :
416
+ for dir_ in build_dirs :
417
+ files_ = [fname for fname in
418
+ [os .path .join (dir_ , "coverage.json" ),
419
+ os .path .join (dir_ , "ztest.json" )]
420
+ if os .path .exists (fname )]
421
+ if not files_ :
422
+ logger .debug (f"Coverage merge no files in: { dir_ } " )
423
+ continue
424
+ files += files_
425
+ logger .debug (f"Coverage merge { len (files )} reports in { outdir } " )
426
+ ztest_file = None
427
+ cmd_ = ["--json-pretty" , "--json" , coverage_file ]
403
428
else :
404
- files = [coverage_file ]
429
+ ret , files = self .collect_coverage (outdir , coverage_file , ztest_file , coveragelog )
430
+ logger .debug (f"Coverage collected { len (files )} reports from: { outdir } " )
431
+
432
+ if not files :
433
+ logger .warning (f"No coverage files to compose report for { outdir } " )
434
+ return ret , {}
405
435
406
436
subdir = os .path .join (outdir , "coverage" )
407
437
os .makedirs (subdir , exist_ok = True )
@@ -422,7 +452,8 @@ def _generate(self, outdir, coveragelog):
422
452
[report_options [r ] for r in self .output_formats .split (',' )]
423
453
)
424
454
425
- cmd = ["gcovr" , "-v" , "-r" , self .base_dir ] + mode_options + gcovr_options + tracefiles
455
+ cmd = ["gcovr" , "-r" , self .base_dir ] + self .options + gcovr_options + tracefiles
456
+ cmd += cmd_
426
457
cmd += ["--json-summary-pretty" , "--json-summary" , coverage_summary ]
427
458
cmd_str = " " .join (cmd )
428
459
logger .debug (f"Running: { cmd_str } " )
@@ -468,14 +499,17 @@ def choose_gcov_tool(options, is_system_gcov):
468
499
return gcov_tool
469
500
470
501
471
- def run_coverage_tool (options , outdir , is_system_gcov , coverage_capture , coverage_report ):
502
+ def run_coverage_tool (options , outdir , is_system_gcov , instances ,
503
+ coverage_capture , coverage_report ):
472
504
coverage_tool = CoverageTool .factory (options .coverage_tool , jobs = options .jobs )
473
505
if not coverage_tool :
474
506
return False , {}
475
507
476
508
coverage_tool .gcov_tool = str (choose_gcov_tool (options , is_system_gcov ))
477
509
logger .debug (f"Using gcov tool: { coverage_tool .gcov_tool } " )
478
510
511
+ coverage_tool .instances = instances
512
+ coverage_tool .coverage_per_instance = options .coverage_per_instance
479
513
coverage_tool .coverage_capture = coverage_capture
480
514
coverage_tool .coverage_report = coverage_report
481
515
coverage_tool .base_dir = os .path .abspath (options .coverage_basedir )
@@ -509,6 +543,7 @@ def run_coverage(options, testplan):
509
543
break
510
544
511
545
return run_coverage_tool (options , options .outdir , is_system_gcov ,
546
+ instances = testplan .instances ,
512
547
coverage_capture = False ,
513
548
coverage_report = True )
514
549
@@ -518,5 +553,6 @@ def run_coverage_instance(options, instance):
518
553
"""
519
554
is_system_gcov = has_system_gcov (instance .platform )
520
555
return run_coverage_tool (options , instance .build_dir , is_system_gcov ,
556
+ instances = {instance .name : instance },
521
557
coverage_capture = True ,
522
558
coverage_report = options .coverage_per_instance )
0 commit comments