ceedling 1.1.7 → 1.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/GIT_COMMIT_SHA +1 -1
- data/README.md +1 -1
- data/docs/Changelog.md +48 -0
- data/docs/KnownIssues.md +3 -0
- data/docs/mkdocs/plugins/gcov/gcovr.md +9 -4
- data/docs/mkdocs/reference/gcov-plugin.md +9 -4
- data/lib/ceedling/c_extractor/c_extractor_declarations.rb +11 -10
- data/lib/ceedling/c_extractor/c_extractor_preprocessing.rb +67 -2
- data/lib/ceedling/file_path_utils.rb +4 -0
- data/lib/ceedling/generators/generator_test_results_backtrace.rb +5 -3
- data/lib/ceedling/generators/generator_test_runner.rb +5 -1
- data/lib/ceedling/includes/includes.rb +8 -1
- data/lib/ceedling/partials/partializer.rb +69 -24
- data/lib/ceedling/preprocess/preprocessinator_line_marker_includes_extractor.rb +6 -1
- data/lib/ceedling/preprocess/preprocessinator_reconstructor.rb +18 -5
- data/lib/version.rb +1 -1
- data/plugins/gcov/lib/console_reportinator.rb +7 -0
- data/plugins/gcov/lib/gcov.rb +29 -10
- data/plugins/gcov/lib/gcovr_reportinator.rb +62 -48
- data/plugins/gcov/lib/reportgenerator_reportinator.rb +10 -6
- data/site-local/plugins/gcov/gcovr.html +9 -4
- data/site-local/reference/gcov-plugin.html +9 -4
- data/site-local/sitemap.xml.gz +0 -0
- data/spec/system/gcov_deployment_spec.rb +1 -0
- data/spec/system/support/gcov_common_test_cases.rb +18 -0
- data/spec/units/c_extractor/c_extractor_declarations_spec.rb +16 -0
- data/spec/units/c_extractor/c_extractor_integration_spec.rb +71 -0
- data/spec/units/c_extractor/c_extractor_preprocessing_spec.rb +122 -0
- data/spec/units/generators/generator_partials_spec.rb +98 -0
- data/spec/units/generators/generator_test_results_backtrace_spec.rb +26 -0
- data/spec/units/generators/generator_test_runner_spec.rb +23 -0
- data/spec/units/includes/includes_spec.rb +21 -3
- data/spec/units/partials/partializer_spec.rb +62 -0
- data/spec/units/preprocess/preprocessinator_line_marker_includes_extractor_spec.rb +64 -0
- data/spec/units/preprocess/preprocessinator_reconstructor_spec.rb +74 -0
- data/vendor/unity/auto/generate_test_runner.rb +3 -1
- data/vendor/unity/auto/unity_test_summary.rb +2 -2
- data/vendor/unity/docs/UnityChangeLog.md +7 -0
- data/vendor/unity/docs/UnityConfigurationGuide.md +4 -0
- data/vendor/unity/src/unity_internals.h +21 -7
- data/vendor/unity/test/Makefile +9 -2
- data/vendor/unity/test/rakefile_helper.rb +1 -0
- data/vendor/unity/test/tests/test_generate_test_runner.rb +49 -0
- data/vendor/unity/test/tests/test_unity_test_summary.rb +58 -0
- metadata +5 -2
|
@@ -66,9 +66,12 @@ class GcovrReportinator < GcovReportinator
|
|
|
66
66
|
)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
# --decisions was introduced in gcovr 5.1, not 6.0 -- confirmed directly against
|
|
70
|
+
# gcovr's own changelog and a real gcovr 5.1/5.0 install (5.1 accepts --decisions
|
|
71
|
+
# and proceeds; 5.0 rejects it outright as unrecognized).
|
|
72
|
+
if gcovr_opts[:decisions] && !min_version?( @gcovr_version, 5, 1 )
|
|
70
73
|
raise CeedlingException.new(
|
|
71
|
-
":gcov ↳ :gcovr ↳ :decisions ➡️ requires gcovr
|
|
74
|
+
":gcov ↳ :gcovr ↳ :decisions ➡️ requires gcovr 5.1 or higher " \
|
|
72
75
|
"(found #{@gcovr_version.major}.#{@gcovr_version.minor})"
|
|
73
76
|
)
|
|
74
77
|
end
|
|
@@ -116,9 +119,18 @@ class GcovrReportinator < GcovReportinator
|
|
|
116
119
|
args += "--gcov-filter \"#{gcovr_opts[:gcov_filter]}\" " unless gcovr_opts[:gcov_filter].nil?
|
|
117
120
|
args += "--gcov-exclude \"#{gcovr_opts[:gcov_exclude]}\" " unless gcovr_opts[:gcov_exclude].nil?
|
|
118
121
|
args += "--exclude-directories \"#{gcovr_opts[:exclude_directories]}\" " unless gcovr_opts[:exclude_directories].nil?
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
# gcovr 7.0 deprecated --branches/--sort-uncovered/--sort-percentage in favor of
|
|
123
|
+
# these renamed equivalents (still functional, but warn, below 7.0); use whichever
|
|
124
|
+
# form the detected gcovr_version actually expects.
|
|
125
|
+
if gcovr_opts[:branches]
|
|
126
|
+
args += (min_version?(gcovr_version, 7, 0) ? "--txt-metric branch " : "--branches ")
|
|
127
|
+
end
|
|
128
|
+
if gcovr_opts[:sort_uncovered]
|
|
129
|
+
args += (min_version?(gcovr_version, 7, 0) ? "--sort uncovered-number " : "--sort-uncovered ")
|
|
130
|
+
end
|
|
131
|
+
if gcovr_opts[:sort_percentage]
|
|
132
|
+
args += (min_version?(gcovr_version, 7, 0) ? "--sort uncovered-percent " : "--sort-percentage ")
|
|
133
|
+
end
|
|
122
134
|
args += "--print-summary " if gcovr_opts[:print_summary]
|
|
123
135
|
args += "--gcov-executable \"#{gcovr_opts[:gcov_executable]}\" " unless gcovr_opts[:gcov_executable].nil?
|
|
124
136
|
args += "--exclude-unreachable-branches " if gcovr_opts[:exclude_unreachable_branches]
|
|
@@ -152,18 +164,24 @@ class GcovrReportinator < GcovReportinator
|
|
|
152
164
|
next if gcovr_opts[opt].nil?
|
|
153
165
|
|
|
154
166
|
value = gcovr_opts[opt]
|
|
155
|
-
|
|
167
|
+
|
|
156
168
|
# Value sanity checks for :fail_under_* settings
|
|
157
169
|
if opt.to_s =~ /fail_/
|
|
158
170
|
if not value.is_a? Integer
|
|
159
171
|
raise CeedlingException.new(":gcov ↳ :gcovr ↳ :#{opt} ➡️ '#{value}' must be an integer")
|
|
160
|
-
elsif (value <
|
|
161
|
-
raise CeedlingException.new(":gcov ↳ :gcovr ↳ :#{opt} ➡️ '#{value}' must be an integer percentage
|
|
172
|
+
elsif (value < 1) || (value > 100)
|
|
173
|
+
raise CeedlingException.new(":gcov ↳ :gcovr ↳ :#{opt} ➡️ '#{value}' must be an integer percentage 1 – 100")
|
|
162
174
|
end
|
|
163
175
|
end
|
|
164
|
-
|
|
165
|
-
#
|
|
166
|
-
|
|
176
|
+
|
|
177
|
+
# :source_encoding/:object_directory are quoted (unlike the already-validated,
|
|
178
|
+
# always-integer :fail_under_* values) so a value containing a space doesn't
|
|
179
|
+
# break the constructed command line.
|
|
180
|
+
if [:source_encoding, :object_directory].include?(opt)
|
|
181
|
+
args += "--#{opt.to_s.gsub('_','-')} \"#{value}\" " unless value.nil?
|
|
182
|
+
else
|
|
183
|
+
args += "--#{opt.to_s.gsub('_','-')} #{value} " unless value.nil?
|
|
184
|
+
end
|
|
167
185
|
end
|
|
168
186
|
|
|
169
187
|
return args
|
|
@@ -409,7 +427,12 @@ class GcovrReportinator < GcovReportinator
|
|
|
409
427
|
_opts[:report_exclude] = excludes unless excludes.empty?
|
|
410
428
|
end
|
|
411
429
|
|
|
412
|
-
|
|
430
|
+
# No :mcdc-specific gcovr flag to set here -- GCC's own condition/MC-DC data flows
|
|
431
|
+
# into gcovr's reports unconditionally, in every format, regardless of --decisions
|
|
432
|
+
# (confirmed directly: gcovr's Condition-labeled HTML/JSON content is byte-identical
|
|
433
|
+
# with and without --decisions; that flag only adds its own separate, additive
|
|
434
|
+
# Decision data alongside it). :mcdc's own gcovr-version floor is still enforced in
|
|
435
|
+
# initialize -- there's just nothing more for gcovr's own CLI args to do here.
|
|
413
436
|
|
|
414
437
|
return _opts
|
|
415
438
|
end
|
|
@@ -417,22 +440,29 @@ class GcovrReportinator < GcovReportinator
|
|
|
417
440
|
|
|
418
441
|
# Build a combined Python regex for gcovr's `--exclude` flag covering all
|
|
419
442
|
# non-production file categories: test files, support files, and generated/framework files.
|
|
443
|
+
# Path/prefix values are config-driven, so they're Regexp-escaped -- interpolated raw, a
|
|
444
|
+
# metacharacter (e.g. a literal '.') in a project's own path would silently over- or
|
|
445
|
+
# under-match.
|
|
420
446
|
def build_report_exclusions
|
|
421
447
|
data = build_exclusion_data
|
|
422
448
|
patterns = []
|
|
423
449
|
|
|
450
|
+
test_prefix = Regexp.escape(data[:test_prefix].to_s)
|
|
451
|
+
build_root = Regexp.escape(data[:build_root].to_s)
|
|
452
|
+
src_extension = Regexp.escape(data[:src_extension].to_s)
|
|
453
|
+
|
|
424
454
|
data[:test_paths].each do |path|
|
|
425
455
|
# Test files (e.g. test_foo.c)
|
|
426
|
-
patterns << ".*#{path}.*/#{
|
|
456
|
+
patterns << ".*#{Regexp.escape(path)}.*/#{test_prefix}.+\\#{src_extension}$"
|
|
427
457
|
end
|
|
428
458
|
|
|
429
459
|
# Support files (e.g. helpers, stubs, fixtures) — never production source
|
|
430
460
|
data[:support_paths].each do |path|
|
|
431
|
-
patterns << ".*#{path}/.+\\#{
|
|
461
|
+
patterns << ".*#{Regexp.escape(path)}/.+\\#{src_extension}$"
|
|
432
462
|
end
|
|
433
463
|
|
|
434
464
|
# Any generated files for tests or vendored framework C source files below the root of the build directory
|
|
435
|
-
patterns << ".*#{
|
|
465
|
+
patterns << ".*#{build_root}/.+\\#{EXTENSION_CORE_SOURCE}$"
|
|
436
466
|
|
|
437
467
|
return patterns
|
|
438
468
|
end
|
|
@@ -535,59 +565,43 @@ class GcovrReportinator < GcovReportinator
|
|
|
535
565
|
|
|
536
566
|
# Output to console a human-friendly message on certain coverage failure exit codes.
|
|
537
567
|
# Prefers the actual gcovr error text from stderr; falls back to descriptive strings.
|
|
538
|
-
#
|
|
568
|
+
# gcovr's composite exit code bit-ORs every violated :fail_under_* threshold together --
|
|
569
|
+
# every violated bit is collected and reported, not just whichever is checked first.
|
|
539
570
|
def gcovr_exec_exception?(opts, exitcode, boom, shell_result=nil)
|
|
571
|
+
violations = []
|
|
540
572
|
|
|
541
573
|
# Special handling of exit code 2 with --fail-under-line
|
|
542
574
|
if ((exitcode & 2) == 2) and !opts[:fail_under_line].nil?
|
|
543
575
|
fallback = "Line coverage is less than the configured minimum of #{opts[:fail_under_line]}%"
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
raise CeedlingException.new(msg)
|
|
547
|
-
else
|
|
548
|
-
@loginator.log( msg, Verbosity::COMPLAIN )
|
|
549
|
-
# Clear bit in exit code
|
|
550
|
-
exitcode &= ~2
|
|
551
|
-
end
|
|
576
|
+
violations << "Gcovr ⏩️ #{extract_gcovr_error_message( shell_result ) || fallback}"
|
|
577
|
+
exitcode &= ~2
|
|
552
578
|
end
|
|
553
579
|
|
|
554
580
|
# Special handling of exit code 4 with --fail-under-branch
|
|
555
581
|
if ((exitcode & 4) == 4) and !opts[:fail_under_branch].nil?
|
|
556
582
|
fallback = "Branch coverage is less than the configured minimum of #{opts[:fail_under_branch]}%"
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
raise CeedlingException.new(msg)
|
|
560
|
-
else
|
|
561
|
-
@loginator.log( msg, Verbosity::COMPLAIN )
|
|
562
|
-
# Clear bit in exit code
|
|
563
|
-
exitcode &= ~4
|
|
564
|
-
end
|
|
583
|
+
violations << "Gcovr ⏩️ #{extract_gcovr_error_message( shell_result ) || fallback}"
|
|
584
|
+
exitcode &= ~4
|
|
565
585
|
end
|
|
566
586
|
|
|
567
587
|
# Special handling of exit code 8 with --fail-under-decision
|
|
568
588
|
if ((exitcode & 8) == 8) and !opts[:fail_under_decision].nil?
|
|
569
589
|
fallback = "Decision coverage is less than the configured minimum of #{opts[:fail_under_decision]}%"
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
raise CeedlingException.new(msg)
|
|
573
|
-
else
|
|
574
|
-
@loginator.log( msg, Verbosity::COMPLAIN )
|
|
575
|
-
# Clear bit in exit code
|
|
576
|
-
exitcode &= ~8
|
|
577
|
-
end
|
|
590
|
+
violations << "Gcovr ⏩️ #{extract_gcovr_error_message( shell_result ) || fallback}"
|
|
591
|
+
exitcode &= ~8
|
|
578
592
|
end
|
|
579
593
|
|
|
580
594
|
# Special handling of exit code 16 with --fail-under-function
|
|
581
595
|
if ((exitcode & 16) == 16) and !opts[:fail_under_function].nil?
|
|
582
596
|
fallback = "Function coverage is less than the configured minimum of #{opts[:fail_under_function]}%"
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
597
|
+
violations << "Gcovr ⏩️ #{extract_gcovr_error_message( shell_result ) || fallback}"
|
|
598
|
+
exitcode &= ~16
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
if boom
|
|
602
|
+
raise CeedlingException.new( violations.join("\n") ) unless violations.empty?
|
|
603
|
+
else
|
|
604
|
+
violations.each { |msg| @loginator.log( msg, Verbosity::COMPLAIN ) }
|
|
591
605
|
end
|
|
592
606
|
|
|
593
607
|
# A non-zero exit code is a problem
|
|
@@ -128,7 +128,9 @@ class ReportGeneratorReportinator < GcovReportinator
|
|
|
128
128
|
|
|
129
129
|
# Get the ReportGenerator options from the project options.
|
|
130
130
|
def collect_reportgenerator_opts(opts)
|
|
131
|
-
|
|
131
|
+
# dup prevents repeated calls from accumulating mutations on the shared opts hash --
|
|
132
|
+
# mirrors GcovrReportinator#collect_gcovr_opts' identical guard and rationale.
|
|
133
|
+
_opts = opts[REPORT_GENERATOR_SETTING_PREFIX.to_sym].dup
|
|
132
134
|
|
|
133
135
|
# Auto-generate -filefilters: exclusion patterns for test paths and the build root.
|
|
134
136
|
# These exclude test files and all generated/framework files from coverage reports.
|
|
@@ -275,17 +277,19 @@ class ReportGeneratorReportinator < GcovReportinator
|
|
|
275
277
|
# Build filename-fragment patterns for the .gcno scan exclusion regex.
|
|
276
278
|
# Excludes test files, generated mocks, and test runners from gcov processing.
|
|
277
279
|
# The '.*' suffix handles source extensions embedded in .gcno filenames (e.g. test_foo.c.gcno).
|
|
280
|
+
# test_prefix/mock_prefix are config-driven, so they're Regexp-escaped -- unescaped, a
|
|
281
|
+
# metacharacter in either (e.g. a literal '.') would silently over- or under-match.
|
|
278
282
|
def build_gcno_exclusions
|
|
279
283
|
data = build_exclusion_data
|
|
280
284
|
# Use [^\/\\]* (no path separators) rather than .* to match filenames only.
|
|
281
285
|
# Ceedling names build output dirs after test files (e.g. build/gcov/out/test_foo/),
|
|
282
286
|
# so .* would greedily match test_foo/bar.c and exclude ALL production sources.
|
|
283
287
|
[
|
|
284
|
-
"#{data[:test_prefix]}[^\\/\\\\]*", # test_foo.gcno
|
|
285
|
-
"#{data[:mock_prefix]}[^\\/\\\\]*", # MockBar.gcno
|
|
286
|
-
"[^\\/\\\\]*_runner[^\\/\\\\]*",
|
|
287
|
-
File.basename(UNITY_C_FILE, '.*'),
|
|
288
|
-
File.basename(CMOCK_C_FILE, '.*')
|
|
288
|
+
"#{Regexp.escape(data[:test_prefix].to_s)}[^\\/\\\\]*", # test_foo.gcno
|
|
289
|
+
"#{Regexp.escape(data[:mock_prefix].to_s)}[^\\/\\\\]*", # MockBar.gcno
|
|
290
|
+
"[^\\/\\\\]*_runner[^\\/\\\\]*", # test_foo_runner.gcno
|
|
291
|
+
Regexp.escape(File.basename(UNITY_C_FILE, '.*')), # unity.gcno
|
|
292
|
+
Regexp.escape(File.basename(CMOCK_C_FILE, '.*')) # cmock.gcno
|
|
289
293
|
]
|
|
290
294
|
end
|
|
291
295
|
|
|
@@ -5367,7 +5367,7 @@ it's never silently non-functional.</p>
|
|
|
5367
5367
|
<p>Report the decision coverage. For HTML, JSON, and the summary report.
|
|
5368
5368
|
(<code>gcovr --decisions</code>)</p>
|
|
5369
5369
|
<p>Implied automatically whenever <code>:fail_under_decision</code> is set.</p>
|
|
5370
|
-
<p><strong>Requires:</strong> <code>gcovr</code>
|
|
5370
|
+
<p><strong>Requires:</strong> <code>gcovr</code> 5.1 or higher</p>
|
|
5371
5371
|
<hr />
|
|
5372
5372
|
<h3 id="fail_under_function"><code>:fail_under_function</code></h3>
|
|
5373
5373
|
<p>Exit with a status of 16 if the total function coverage is less than this minimum
|
|
@@ -5387,15 +5387,20 @@ logs a warning without breaking the build.</p>
|
|
|
5387
5387
|
<hr />
|
|
5388
5388
|
<h3 id="branches"><code>:branches</code></h3>
|
|
5389
5389
|
<p>Report the branch coverage instead of the line coverage. Applies to the text
|
|
5390
|
-
report only.
|
|
5390
|
+
report only. Uses <code>gcovr --txt-metric branch</code> on <code>gcovr</code> 7.0+ (<code>--branches</code> is
|
|
5391
|
+
deprecated as of 7.0, though still functional); <code>gcovr --branches</code> below it.</p>
|
|
5391
5392
|
<hr />
|
|
5392
5393
|
<h3 id="sort_uncovered"><code>:sort_uncovered</code></h3>
|
|
5393
5394
|
<p>Sort entries by increasing number of uncovered lines. Applies to text and HTML
|
|
5394
|
-
reports.
|
|
5395
|
+
reports. Uses <code>gcovr --sort uncovered-number</code> on <code>gcovr</code> 7.0+
|
|
5396
|
+
(<code>--sort-uncovered</code> is deprecated as of 7.0, though still functional);
|
|
5397
|
+
<code>gcovr --sort-uncovered</code> below it.</p>
|
|
5395
5398
|
<hr />
|
|
5396
5399
|
<h3 id="sort_percentage"><code>:sort_percentage</code></h3>
|
|
5397
5400
|
<p>Sort entries by increasing percentage of uncovered lines. Applies to text and
|
|
5398
|
-
HTML reports.
|
|
5401
|
+
HTML reports. Uses <code>gcovr --sort uncovered-percent</code> on <code>gcovr</code> 7.0+
|
|
5402
|
+
(<code>--sort-percentage</code> is deprecated as of 7.0, though still functional);
|
|
5403
|
+
<code>gcovr --sort-percentage</code> below it.</p>
|
|
5399
5404
|
<hr />
|
|
5400
5405
|
<h3 id="print_summary"><code>:print_summary</code></h3>
|
|
5401
5406
|
<p>Print a small report to stdout with line and branch percentage coverage. This
|
|
@@ -5668,7 +5668,7 @@ present — setting this option implies <code>:decisions</code> (below) automati
|
|
|
5668
5668
|
<p>Report the decision coverage. For HTML, JSON, and the summary report.
|
|
5669
5669
|
(<code>gcovr --decisions</code>)</p>
|
|
5670
5670
|
<p>Implied automatically whenever <code>:fail_under_decision</code> is set.</p>
|
|
5671
|
-
<p><strong>Requires:</strong> <code>gcovr</code>
|
|
5671
|
+
<p><strong>Requires:</strong> <code>gcovr</code> 5.1 or higher</p>
|
|
5672
5672
|
<hr />
|
|
5673
5673
|
<h3 id="fail_under_function"><code>:fail_under_function</code></h3>
|
|
5674
5674
|
<p>Exit with status 16 if total function coverage is below this percentage.
|
|
@@ -5686,15 +5686,20 @@ instead of only logging a warning.</p>
|
|
|
5686
5686
|
<hr />
|
|
5687
5687
|
<h3 id="branches"><code>:branches</code></h3>
|
|
5688
5688
|
<p>Report branch coverage instead of line coverage. Applies to text reports only.
|
|
5689
|
-
|
|
5689
|
+
Uses <code>gcovr --txt-metric branch</code> on <code>gcovr</code> 7.0+ (<code>--branches</code> is deprecated as
|
|
5690
|
+
of 7.0, though still functional); <code>gcovr --branches</code> below it.</p>
|
|
5690
5691
|
<hr />
|
|
5691
5692
|
<h3 id="sort_uncovered"><code>:sort_uncovered</code></h3>
|
|
5692
5693
|
<p>Sort report entries by increasing number of uncovered lines. Applies to text
|
|
5693
|
-
and HTML reports.
|
|
5694
|
+
and HTML reports. Uses <code>gcovr --sort uncovered-number</code> on <code>gcovr</code> 7.0+
|
|
5695
|
+
(<code>--sort-uncovered</code> is deprecated as of 7.0, though still functional);
|
|
5696
|
+
<code>gcovr --sort-uncovered</code> below it.</p>
|
|
5694
5697
|
<hr />
|
|
5695
5698
|
<h3 id="sort_percentage"><code>:sort_percentage</code></h3>
|
|
5696
5699
|
<p>Sort report entries by increasing percentage of uncovered lines. Applies to
|
|
5697
|
-
text and HTML reports.
|
|
5700
|
+
text and HTML reports. Uses <code>gcovr --sort uncovered-percent</code> on <code>gcovr</code> 7.0+
|
|
5701
|
+
(<code>--sort-percentage</code> is deprecated as of 7.0, though still functional);
|
|
5702
|
+
<code>gcovr --sort-percentage</code> below it.</p>
|
|
5698
5703
|
<hr />
|
|
5699
5704
|
<h3 id="print_summary"><code>:print_summary</code></h3>
|
|
5700
5705
|
<p>Print a brief line- and branch-coverage summary to stdout in addition to any
|
data/site-local/sitemap.xml.gz
CHANGED
|
Binary file
|
|
@@ -37,6 +37,7 @@ ceedling_system_tests do
|
|
|
37
37
|
test_case :project_with_gcov_success
|
|
38
38
|
test_case :project_with_gcov_fail
|
|
39
39
|
test_case :project_with_gcov_fail_under_decision
|
|
40
|
+
test_case :project_with_gcov_fail_under_line_rejects_zero
|
|
40
41
|
test_case :gcov_console_report_with_system_header
|
|
41
42
|
test_case :gcov_console_report_with_partial
|
|
42
43
|
# TODO: Restore these tests when the :abort_on_uncovered option is restored in the Gcov plugin
|
|
@@ -98,6 +98,24 @@ module GcovCommonTestCases
|
|
|
98
98
|
end
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
# :fail_under_* documents a 1-100 range; 0 was previously silently accepted as a
|
|
102
|
+
# threshold that could never actually fail (0% coverage is always >= 0).
|
|
103
|
+
def project_with_gcov_fail_under_line_rejects_zero
|
|
104
|
+
@c.with_context do
|
|
105
|
+
Dir.chdir @proj_name do
|
|
106
|
+
prep_project_yml_for_coverage
|
|
107
|
+
@c.merge_project_yml_for_test( { :gcov => { :gcovr => { :fail_under_line => 0 } } } )
|
|
108
|
+
FileUtils.cp test_asset_path("example_file.h"), 'src/'
|
|
109
|
+
FileUtils.cp test_asset_path("example_file.c"), 'src/'
|
|
110
|
+
FileUtils.cp test_asset_path("test_example_file_success.c"), 'test/'
|
|
111
|
+
|
|
112
|
+
output = @c.ceedling_build_exec("gcov:all")
|
|
113
|
+
expect(@c.last_exit_status).to_not eq(0)
|
|
114
|
+
expect(output).to match(/:fail_under_line.*must be an integer percentage/)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
101
119
|
# TODO: Restore this test when the :abort_on_uncovered option is restored in the Gcov plugin
|
|
102
120
|
# def project_with_gcov_fail_because_of_uncovered_files
|
|
103
121
|
# @c.with_context do
|
|
@@ -52,6 +52,22 @@ describe CExtractorDeclarations do
|
|
|
52
52
|
expect(rest).to eq("")
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
# #1266 follow-on: same unanchored-substring risk class as #1262/#1266. The type-side
|
|
56
|
+
# identifier "counter_t" itself contains "count" as a substring; a plain rindex(name)
|
|
57
|
+
# would still find the real trailing occurrence here (rindex searches right-to-left),
|
|
58
|
+
# but this pins down that the \b-anchored match extract_type now uses doesn't regress
|
|
59
|
+
# this ordinary case, where the type name happens to share a substring with the
|
|
60
|
+
# declared name.
|
|
61
|
+
it "extracts a variable whose type name contains the variable's own name as a substring" do
|
|
62
|
+
content = "counter_t count;"
|
|
63
|
+
success, variable, pos, rest = extract_variable.call(content)
|
|
64
|
+
|
|
65
|
+
expect(success).to be true
|
|
66
|
+
check_single(variable, name: 'count', type: 'counter_t', text: 'counter_t count;')
|
|
67
|
+
expect(pos).to eq(content.length)
|
|
68
|
+
expect(rest).to eq("")
|
|
69
|
+
end
|
|
70
|
+
|
|
55
71
|
it "extracts simple char variable" do
|
|
56
72
|
content = "char c;"
|
|
57
73
|
success, variable, pos, rest = extract_variable.call(content)
|
|
@@ -565,6 +565,59 @@ describe CExtractor do
|
|
|
565
565
|
expect( contents.variable_declarations.length ).to eq 0
|
|
566
566
|
end
|
|
567
567
|
|
|
568
|
+
# #1262: Partials-generated headers occasionally concatenated two adjacent
|
|
569
|
+
# #define lines onto one physical line. Reconstructing the joined text
|
|
570
|
+
# from element_sequence (as generator_partials.rb does) rules the plain,
|
|
571
|
+
# comment-free case in or out formally, rather than by inspection of
|
|
572
|
+
# #_collect_directive alone. This shape -- three plain register-offset
|
|
573
|
+
# macros in a row, no comments -- is expected to reconstruct cleanly.
|
|
574
|
+
it "reconstructs three adjacent plain #define macros joined by newlines with no text lost between them" do
|
|
575
|
+
file_contents = <<~'CONTENTS'
|
|
576
|
+
#define START_ADDRESS 0x00
|
|
577
|
+
#define IDX_DATARATE (ADS124S08_REG_ADDR_DATARATE - START_ADDRESS)
|
|
578
|
+
#define IDX_REF (ADS124S08_REG_ADDR_REF - START_ADDRESS)
|
|
579
|
+
CONTENTS
|
|
580
|
+
|
|
581
|
+
contents = extract_from.call(file_contents)
|
|
582
|
+
|
|
583
|
+
expect( contents.macro_definitions.length ).to eq 3
|
|
584
|
+
joined = contents.element_sequence.map(&:text).join("\n")
|
|
585
|
+
expect( joined ).to eq(
|
|
586
|
+
"#define START_ADDRESS 0x00\n" +
|
|
587
|
+
"#define IDX_DATARATE (ADS124S08_REG_ADDR_DATARATE - START_ADDRESS)\n" +
|
|
588
|
+
"#define IDX_REF (ADS124S08_REG_ADDR_REF - START_ADDRESS)"
|
|
589
|
+
)
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
# #1262's actual reported trigger: a trailing // comment on one #define containing
|
|
593
|
+
# an ordinary English contraction. The stray apostrophe used to be scanned as the
|
|
594
|
+
# start of a char literal, sending the directive scanner hunting for a closing
|
|
595
|
+
# match straight through this macro's own newline and into the next two #defines
|
|
596
|
+
# -- merging all three into one macro_definitions entry with no separating newline,
|
|
597
|
+
# which is exactly what later caused Partials-generated headers to concatenate two
|
|
598
|
+
# #define lines onto one physical line ("stray '#' in program").
|
|
599
|
+
it "reconstructs three adjacent #define macros when one has a trailing comment with an apostrophe" do
|
|
600
|
+
file_contents = <<~'CONTENTS'
|
|
601
|
+
#define START_ADDRESS 0x00 // don't change this
|
|
602
|
+
#define IDX_DATARATE (ADS124S08_REG_ADDR_DATARATE - START_ADDRESS)
|
|
603
|
+
#define IDX_REF (ADS124S08_REG_ADDR_REF - START_ADDRESS)
|
|
604
|
+
CONTENTS
|
|
605
|
+
|
|
606
|
+
contents = extract_from.call(file_contents)
|
|
607
|
+
|
|
608
|
+
expect( contents.macro_definitions.length ).to eq 3
|
|
609
|
+
expect( contents.macro_definitions[0].text ).to eq "#define START_ADDRESS 0x00 // don't change this"
|
|
610
|
+
expect( contents.macro_definitions[1].text ).to eq "#define IDX_DATARATE (ADS124S08_REG_ADDR_DATARATE - START_ADDRESS)"
|
|
611
|
+
expect( contents.macro_definitions[2].text ).to eq "#define IDX_REF (ADS124S08_REG_ADDR_REF - START_ADDRESS)"
|
|
612
|
+
|
|
613
|
+
joined = contents.element_sequence.map(&:text).join("\n")
|
|
614
|
+
expect( joined ).to eq(
|
|
615
|
+
"#define START_ADDRESS 0x00 // don't change this\n" +
|
|
616
|
+
"#define IDX_DATARATE (ADS124S08_REG_ADDR_DATARATE - START_ADDRESS)\n" +
|
|
617
|
+
"#define IDX_REF (ADS124S08_REG_ADDR_REF - START_ADDRESS)"
|
|
618
|
+
)
|
|
619
|
+
end
|
|
620
|
+
|
|
568
621
|
it "should extract a function definition following a #define with an escaped character in a string literal (GH #1184)" do
|
|
569
622
|
file_contents = <<~'CONTENTS'
|
|
570
623
|
#include "world.h"
|
|
@@ -954,6 +1007,24 @@ describe CExtractor do
|
|
|
954
1007
|
expect(plain.decorators).to eq([])
|
|
955
1008
|
end
|
|
956
1009
|
|
|
1010
|
+
it "normalizes a preprocessor-inserted space before ## in a backslash-continued macro definition" do
|
|
1011
|
+
file_contents = <<~CONTENTS
|
|
1012
|
+
#define MODULE_DEV_TYPE_CB(dev_type) \\
|
|
1013
|
+
void MODULE_TrigCbDev ##dev_type(void) \\
|
|
1014
|
+
{ \\
|
|
1015
|
+
MODULE_CommonTrigCb(); \\
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
static AlertEntry_t s_alert_table[ALERT_MANAGER_MAX_ALERTS];
|
|
1019
|
+
CONTENTS
|
|
1020
|
+
|
|
1021
|
+
contents = extract_from.call(file_contents)
|
|
1022
|
+
|
|
1023
|
+
expect(contents.macro_definitions.length).to eq 1
|
|
1024
|
+
expect(contents.macro_definitions[0].text).to include('MODULE_TrigCbDev##dev_type')
|
|
1025
|
+
expect(contents.macro_definitions[0].text).not_to include('MODULE_TrigCbDev ##dev_type')
|
|
1026
|
+
end
|
|
1027
|
+
|
|
957
1028
|
end
|
|
958
1029
|
|
|
959
1030
|
end
|
|
@@ -336,6 +336,51 @@ describe CExtractorPreprocessing do
|
|
|
336
336
|
expect(pos).to eq "#define FOO 1\n".length
|
|
337
337
|
end
|
|
338
338
|
|
|
339
|
+
# --- Regression: GH #1262 — a trailing // comment's apostrophe/quote merges
|
|
340
|
+
# the next directive into this one ---
|
|
341
|
+
# A '\'' or '"' inside an ordinary // comment (e.g. an English contraction
|
|
342
|
+
# like "don't") is not a string/char literal delimiter. Scanned as one
|
|
343
|
+
# anyway, it sends skip_c_string hunting for a closing match straight through
|
|
344
|
+
# this directive's own newline and into whatever follows, silently merging
|
|
345
|
+
# the next #define's text (and losing the newline between them) into this one.
|
|
346
|
+
|
|
347
|
+
it "extracts a #define with a trailing // comment containing an apostrophe" do
|
|
348
|
+
input = "#define START_ADDRESS 0x00 // don't change this\n"
|
|
349
|
+
result, pos = try_directive(input)
|
|
350
|
+
expect(result).to eq [true, input.rstrip]
|
|
351
|
+
expect(pos).to eq input.length
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
it "does not consume a following #define after a // comment containing an apostrophe" do
|
|
355
|
+
input = "#define START_ADDRESS 0x00 // don't change this\n#define IDX 1\n"
|
|
356
|
+
result, pos = try_directive(input)
|
|
357
|
+
expect(result).to eq [true, "#define START_ADDRESS 0x00 // don't change this"]
|
|
358
|
+
expect(pos).to eq "#define START_ADDRESS 0x00 // don't change this\n".length
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
it "does not consume a following #define after a // comment containing a double quote" do
|
|
362
|
+
input = %(#define LABEL "unbalanced) + %( quote in a comment: "\n#define IDX 1\n)
|
|
363
|
+
result, pos = try_directive(input)
|
|
364
|
+
expect(result[0]).to eq true
|
|
365
|
+
expect(result[1]).to start_with('#define LABEL')
|
|
366
|
+
expect(pos).to be < input.length
|
|
367
|
+
expect(input[pos..]).to eq "#define IDX 1\n"
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
it "extracts a #define with a trailing /* */ comment containing an apostrophe" do
|
|
371
|
+
input = "#define START_ADDRESS 0x00 /* don't change this */\n"
|
|
372
|
+
result, pos = try_directive(input)
|
|
373
|
+
expect(result).to eq [true, input.rstrip]
|
|
374
|
+
expect(pos).to eq input.length
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
it "carries a directive across physical lines when a // comment's own line ends in a backslash" do
|
|
378
|
+
input = "#define MACRO(x) \\\n // comment continues \\\n do_thing(x);\n"
|
|
379
|
+
result, pos = try_directive(input)
|
|
380
|
+
expect(result).to eq [true, input.rstrip]
|
|
381
|
+
expect(pos).to eq input.length
|
|
382
|
+
end
|
|
383
|
+
|
|
339
384
|
it "leaves scanner position unchanged on failure" do
|
|
340
385
|
scanner = StringScanner.new("int x;")
|
|
341
386
|
scanner.pos = 0
|
|
@@ -343,6 +388,83 @@ describe CExtractorPreprocessing do
|
|
|
343
388
|
expect(scanner.pos).to eq 0
|
|
344
389
|
end
|
|
345
390
|
|
|
391
|
+
context "whitespace around # and ## operators" do
|
|
392
|
+
it "trims a space before ##" do
|
|
393
|
+
result, _pos = try_directive("#define M(x) void f ##x(void) { g(); }\n")
|
|
394
|
+
expect(result).to eq [true, "#define M(x) void f##x(void) { g(); }"]
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
it "trims a space after ##" do
|
|
398
|
+
result, _pos = try_directive("#define M(x) void f## x(void) { g(); }\n")
|
|
399
|
+
expect(result).to eq [true, "#define M(x) void f##x(void) { g(); }"]
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
it "trims space on both sides of ##" do
|
|
403
|
+
result, _pos = try_directive("#define M(x) void f ## x(void) { g(); }\n")
|
|
404
|
+
expect(result).to eq [true, "#define M(x) void f##x(void) { g(); }"]
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
it "trims a space after the stringize operator #" do
|
|
408
|
+
result, _pos = try_directive("#define S(x) # x\n")
|
|
409
|
+
expect(result).to eq [true, "#define S(x) #x"]
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
it "trims a space before a stringize operator directly gluing onto a preceding identifier" do
|
|
413
|
+
result, _pos = try_directive("#define M(a, b) a #b\n")
|
|
414
|
+
expect(result).to eq [true, "#define M(a, b) a#b"]
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
it "does not touch whitespace before # or ## when preceded by punctuation, not an identifier being pasted" do
|
|
418
|
+
# The space here separates the macro's own parameter list from a
|
|
419
|
+
# replacement list that happens to start with the operator -- that's
|
|
420
|
+
# ordinary formatting, not preprocessor-reconstruction spacing around an
|
|
421
|
+
# operand being pasted, so it's out of this fix's scope.
|
|
422
|
+
result, _pos = try_directive("#define S(x) #x\n")
|
|
423
|
+
expect(result).to eq [true, "#define S(x) #x"]
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
it "normalizes the GNU comma-deletion ,##__VA_ARGS__ form" do
|
|
427
|
+
result, _pos = try_directive("#define LOG(fmt, ...) printf(fmt, ## __VA_ARGS__)\n")
|
|
428
|
+
expect(result).to eq [true, "#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__)"]
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
it "is idempotent for already-canonical ## spacing" do
|
|
432
|
+
input = "#define M(x) void f##x(void) { g(); }\n"
|
|
433
|
+
result, _pos = try_directive(input)
|
|
434
|
+
expect(result).to eq [true, input.rstrip]
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
it "is idempotent for already-canonical # spacing" do
|
|
438
|
+
input = "#define S(x) #x\n"
|
|
439
|
+
result, _pos = try_directive(input)
|
|
440
|
+
expect(result).to eq [true, input.rstrip]
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
it "does not touch ## inside a string literal" do
|
|
444
|
+
input = %q{#define M(x) "a ## b"} + "\n"
|
|
445
|
+
result, _pos = try_directive(input)
|
|
446
|
+
expect(result).to eq [true, input.rstrip]
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
it "does not touch ## inside a // comment" do
|
|
450
|
+
input = "#define M(x) x // a ## b\n"
|
|
451
|
+
result, _pos = try_directive(input)
|
|
452
|
+
expect(result).to eq [true, input.rstrip]
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
it "does not touch ## inside a /* */ comment" do
|
|
456
|
+
input = "#define M(x) x /* a ## b */\n"
|
|
457
|
+
result, _pos = try_directive(input)
|
|
458
|
+
expect(result).to eq [true, input.rstrip]
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
it "leaves ordinary whitespace unrelated to # or ## untouched" do
|
|
462
|
+
input = "#define M(x) void f ( ) ;\n"
|
|
463
|
+
result, _pos = try_directive(input)
|
|
464
|
+
expect(result).to eq [true, input.rstrip]
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
|
|
346
468
|
end
|
|
347
469
|
|
|
348
470
|
context "#filter_directive" do
|