ceedling 1.1.9 → 1.1.10

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.
@@ -383,6 +383,8 @@ partializer:
383
383
  - file_finder
384
384
  - c_extractor
385
385
  - file_path_utils
386
+ - preprocessinator_reconstructor
387
+ - file_wrapper
386
388
  - reportinator
387
389
  - loginator
388
390
 
@@ -14,12 +14,18 @@ require 'ceedling/c_extractor/c_extractor'
14
14
  require 'ceedling/c_extractor/c_extractor_constants'
15
15
  require 'ceedling/c_extractor/c_extractor_types'
16
16
  require 'ceedling/constants'
17
+ require 'ceedling/encodinator'
17
18
 
18
19
  class Partializer
19
20
 
21
+ # Enough of a header to reach its include guard. The guard pair sits at the top of a header by
22
+ # construction, so a bounded read beats loading a large file to find it.
23
+ GUARD_SCAN_BYTES = 2048
24
+
20
25
  include Partials
21
26
 
22
- constructor :partializer_helper, :file_finder, :c_extractor, :file_path_utils, :reportinator, :loginator
27
+ constructor :partializer_helper, :file_finder, :c_extractor, :file_path_utils,
28
+ :preprocessinator_reconstructor, :file_wrapper, :reportinator, :loginator
23
29
 
24
30
  def setup()
25
31
  # Alias
@@ -82,120 +88,89 @@ class Partializer
82
88
  return configs
83
89
  end
84
90
 
91
+ # When `test:` is provided, logs the resulting includes at OBNOXIOUS.
92
+ # Includes for the generated implementation header. This module's own header is replaced by
93
+ # the shared types header at its original list position; every other partialized module's
94
+ # include is dropped.
95
+ #
85
96
  # When `test:` is provided, logs the resulting includes at OBNOXIOUS.
86
97
  def remap_implementation_header_includes(name:, includes:, partials:, types_header: nil, test: nil)
87
- _includes = includes.clone()
88
-
89
- # This module's own header include is spliced out in favor of the shared types
90
- # header at its exact original list position -- see #splice_in_replacement.
91
- _includes = splice_in_replacement(includes: _includes, name: name, replacement: types_header)
92
-
93
- # Remove includes for every other partialized module (this module's own name was
94
- # already handled above)
95
- _includes = remove_matching_includes(
96
- includes: _includes,
97
- modules: (partials.keys - [name])
98
+ _remap_includes(
99
+ name: name,
100
+ includes: includes,
101
+ partials: partials,
102
+ replacement: types_header,
103
+ others: :drop,
104
+ noun: 'Header includes to inject for testable Partial',
105
+ test: test
98
106
  )
99
-
100
- # Remove any duplicates
101
- Includes.sanitize!(_includes)
102
-
103
- @loginator.log_list(
104
- _includes,
105
- "Header includes to inject for testable Partial #{test}::#{name}:",
106
- Verbosity::OBNOXIOUS
107
- ) if test
108
-
109
- return _includes
110
107
  end
111
108
 
109
+ # Includes for the generated mockable interface header. Same treatment as the implementation
110
+ # header: both files carry the shared types header, which is how a module tested and mocked
111
+ # in one test file still gets exactly one definition of each of its types.
112
+ #
112
113
  # When `test:` is provided, logs the resulting includes at OBNOXIOUS.
113
- def remap_implementation_source_includes(name:, includes:, partials:, test: nil)
114
- _includes = includes.clone()
115
-
116
- # Splice the implementation header in at this module's own header's original list
117
- # position, same rationale as #splice_in_replacement -- the generated
118
- # implementation header carries the shared types header in correctly-ordered
119
- # position internally, but that alone doesn't help if THIS file's own separate
120
- # include list still reaches an unrelated header that transitively re-includes the
121
- # real module header before the implementation header (and everything it carries)
122
- # is ever reached. Appending it at the very end (after this file's own copy of every
123
- # other real include) would put it after exactly that kind of transitive
124
- # re-inclusion instead of before it.
125
- _includes = splice_in_replacement(
126
- includes: _includes,
127
- name: name,
128
- replacement: @file_path_utils.form_partial_implementation_header_filename(name)
114
+ def remap_interface_header_includes(name:, includes:, partials:, types_header: nil, test: nil)
115
+ _remap_includes(
116
+ name: name,
117
+ includes: includes,
118
+ partials: partials,
119
+ replacement: types_header,
120
+ others: :drop,
121
+ noun: 'Header includes to inject for mockable Partial',
122
+ test: test
129
123
  )
124
+ end
130
125
 
131
- mockable_modules = []
132
-
133
- partials.each do |_module, config|
134
- # Remap mockable interface headers that will be injected into generated partial implementation
135
- #
136
- # Any real (non-nil) mock mode means this module has a generated mock interface header
137
- # to redirect to -- PUBLIC/PRIVATE alone once covered every mode that existed, but DEDUCT
138
- # (MOCK_PARTIAL_ALL_MODULE) and ACCUMULATE (MOCK_PARTIAL_MODULE) were added later without
139
- # updating this check, so a module mocked via either of those was silently never
140
- # redirected: its real header (and any static inline/static function bodies in it) stayed
141
- # #include'd verbatim, compiling straight past the mock instead of being replaced by it.
142
- if includes.any? { |include| include.filename.ext().downcase() == _module.downcase() }
143
- if !config.mocks.type.nil?
144
- # Insert mockable interface header from remapping of module name
145
- _includes << UserInclude.new(
146
- @file_path_utils.form_partial_interface_header_filename(_module)
147
- )
148
- # Remember the module for later removal of original header
149
- mockable_modules << _module
150
- end
151
- end
152
- end
153
-
154
- # Remove the original headers of any OTHER modules now remapped to mockable
155
- # interfaces above -- this module's own original header was already handled by
156
- # the splice above.
157
- _includes = remove_matching_includes(
158
- includes: _includes,
159
- modules: mockable_modules
126
+ # Includes for the generated implementation source. This module's own header is replaced by
127
+ # the generated implementation header, which carries the types header internally. Another
128
+ # partialized module that is mocked is redirected to its generated interface header; one that
129
+ # is not mocked keeps its real header, which the generated code still needs.
130
+ #
131
+ # When `test:` is provided, logs the resulting includes at OBNOXIOUS.
132
+ def remap_implementation_source_includes(name:, includes:, partials:, test: nil)
133
+ _remap_includes(
134
+ name: name,
135
+ includes: includes,
136
+ partials: partials,
137
+ replacement: @file_path_utils.form_partial_implementation_header_filename(name),
138
+ others: :mock_or_keep,
139
+ noun: 'Source includes to inject for testable Partial',
140
+ test: test
160
141
  )
161
-
162
- # Remove any duplicates
163
- Includes.sanitize!(_includes)
164
-
165
- @loginator.log_list(
166
- _includes,
167
- "Source includes to inject for testable Partial #{test}::#{name}:",
168
- Verbosity::OBNOXIOUS
169
- ) if test
170
-
171
- return _includes
172
142
  end
173
143
 
144
+ # Includes the shared types header carries so the extracted types resolve wherever that file
145
+ # lands. Nothing replaces this module's own header here -- the types header cannot include
146
+ # itself -- so it is simply dropped. Another partialized module is redirected to its generated
147
+ # interface header when mocked and dropped otherwise: its real header would reintroduce the
148
+ # very content that module's own Partial replaced.
149
+ #
174
150
  # When `test:` is provided, logs the resulting includes at OBNOXIOUS.
175
- def remap_interface_header_includes(name:, includes:, partials:, types_header: nil, test: nil)
176
- _includes = includes.clone()
177
-
178
- # This module's own header include is spliced out in favor of the shared types
179
- # header at its exact original list position -- see #splice_in_replacement.
180
- _includes = splice_in_replacement(includes: _includes, name: name, replacement: types_header)
181
-
182
- # Remove includes for every other partialized module (this module's own name was
183
- # already handled above)
184
- _includes = remove_matching_includes(
185
- includes: _includes,
186
- modules: (partials.keys - [name])
151
+ def remap_types_header_includes(name:, includes:, partials:, test: nil)
152
+ _remap_includes(
153
+ name: name,
154
+ includes: includes,
155
+ partials: partials,
156
+ replacement: nil,
157
+ others: :mock_or_drop,
158
+ noun: 'Dependency includes to carry into the shared types header for Partial',
159
+ test: test
187
160
  )
161
+ end
188
162
 
189
- # Remove any duplicates
190
- Includes.sanitize!(_includes)
191
-
192
- @loginator.log_list(
193
- _includes,
194
- "Header includes to inject for mockable Partial #{test}::#{name}:",
195
- Verbosity::OBNOXIOUS
196
- ) if test
197
-
198
- return _includes
163
+ # The include guard macro the module's real header defines, or nil when it has none.
164
+ #
165
+ # Read from the original header rather than from the reconstituted copy or from extracted
166
+ # macros. The reconstituted copy carries a synthetic guard that #sanitize strips, so neither is
167
+ # a dependable source. A nil return means the header guards itself some other way --
168
+ # `#pragma once` -- and offers no macro for a generated file to spoof.
169
+ def extract_module_include_guard(filepath)
170
+ return nil if filepath.nil?
171
+
172
+ text = @file_wrapper.read( filepath, GUARD_SCAN_BYTES ).clean_encoding
173
+ @preprocessinator_reconstructor.extract_include_guard( text )
199
174
  end
200
175
 
201
176
  # Extracts and combines C code contents from header and source files
@@ -457,6 +432,94 @@ class Partializer
457
432
  )
458
433
  end
459
434
 
435
+ # One body behind all four public remaps. Each differs only in what replaces this module's
436
+ # own header, what becomes of other partialized modules, and the noun it logs under -- the
437
+ # splice, the sanitize, and the logging are identical in every case.
438
+ #
439
+ # `others` selects the treatment of every OTHER partialized module in the same test file:
440
+ # :drop -- remove its include outright, with no substitute
441
+ # :mock_or_keep -- redirect to its interface header when mocked, else leave its real header
442
+ # :mock_or_drop -- redirect to its interface header when mocked, else drop it
443
+ def _remap_includes(name:, includes:, partials:, replacement:, others:, noun:, test: nil)
444
+ _includes = includes.clone()
445
+
446
+ # A nil replacement strips this module's own header and substitutes nothing.
447
+ _includes = splice_in_replacement( includes: _includes, name: name, replacement: replacement )
448
+
449
+ _includes =
450
+ case others
451
+ when :drop
452
+ remove_matching_includes( includes: _includes, modules: (partials.keys - [name]) )
453
+ when :mock_or_keep, :mock_or_drop
454
+ # The types header differs from the generated source in two ways at once, which is why
455
+ # both flags below read from one condition.
456
+ #
457
+ # It skips this module: the interface header includes the types header, so carrying the
458
+ # interface back in would fold that file's declarations into this one. The generated
459
+ # source needs the opposite -- a module tested and mocked in one test file keeps its
460
+ # public bodies in the implementation and the private declarations they call in the
461
+ # interface, so it needs both.
462
+ #
463
+ # And it drops an unmocked partialized dependency rather than keeping its real header,
464
+ # which would reintroduce content that module's own Partial replaced.
465
+ shared_types_header = ( others == :mock_or_drop )
466
+
467
+ _redirect_mocked_partials(
468
+ includes: _includes,
469
+ original: includes,
470
+ name: name,
471
+ partials: partials,
472
+ drop_unmocked: shared_types_header,
473
+ skip_self: shared_types_header
474
+ )
475
+ else
476
+ PartializerRuntime.raise_on_option( others )
477
+ end
478
+
479
+ # Remove any duplicates
480
+ Includes.sanitize!(_includes)
481
+
482
+ @loginator.log_list(
483
+ _includes,
484
+ "#{noun} #{test}::#{name}:",
485
+ Verbosity::OBNOXIOUS
486
+ ) if test
487
+
488
+ return _includes
489
+ end
490
+
491
+ # Appends the generated interface header for each mocked partialized module the original list
492
+ # named, then removes the real headers those replaced. `skip_self` decides whether the module
493
+ # being generated counts as one of them, which it does everywhere except the types header --
494
+ # see the call site.
495
+ #
496
+ # Mutates `includes`, which always arrives fresh from splice_in_replacement.
497
+ #
498
+ # Any real (non-nil) mock mode counts. PUBLIC/PRIVATE alone once covered every mode that
499
+ # existed, but DEDUCT (MOCK_PARTIAL_ALL_MODULE) and ACCUMULATE (MOCK_PARTIAL_MODULE) were
500
+ # added later without updating the check, so a module mocked via either was silently never
501
+ # redirected: its real header, and any static inline or static function bodies in it, stayed
502
+ # #include'd verbatim and compiled straight past the mock.
503
+ def _redirect_mocked_partials(includes:, original:, name:, partials:, drop_unmocked:, skip_self:)
504
+ retired = []
505
+
506
+ partials.each do |_module, config|
507
+ next if skip_self && _module == name
508
+ next unless original.any? { |include| include.filename.ext().downcase() == _module.downcase() }
509
+
510
+ if config.mocks.type.nil?
511
+ retired << _module if drop_unmocked
512
+ else
513
+ includes << UserInclude.new(
514
+ @file_path_utils.form_partial_interface_header_filename(_module)
515
+ )
516
+ retired << _module
517
+ end
518
+ end
519
+
520
+ remove_matching_includes( includes: includes, modules: retired )
521
+ end
522
+
460
523
  # Swaps `name`'s own header include for `replacement` at that same list position,
461
524
  # instead of stripping it out and appending the replacement at the very end.
462
525
  #
@@ -183,15 +183,27 @@ class TestBuildExecutor
183
183
 
184
184
  @partializer.sanitize( module_contents )
185
185
 
186
+ # The real header's guard is spoofed deliberately in every generated file, and the types
187
+ # header carries the dependencies its extracted types name.
188
+ module_include_guard = @partializer.extract_module_include_guard( config.header.filepath )
189
+ types_header_includes = @partializer.remap_types_header_includes(
190
+ name: config.module,
191
+ includes: (config.source.includes + config.header.includes),
192
+ partials: testable.partials.configs,
193
+ test: name
194
+ )
195
+
186
196
  # Generated once and shared by the implementation and interface headers below (via
187
197
  # their own includes lists), so a module tested and mocked in the same test file gets
188
198
  # exactly one C definition of each of its typedefs and aggregate types.
189
199
  types_header = @generator.generate_partial_types(
190
- name: config.module, # Module name, not test name -- two modules Partialed
191
- # in the same test file must not collide on one
192
- # shared types header filename
193
- c_module: module_contents,
194
- output_path: testable.paths[:partials]
200
+ name: config.module, # Module name, not test name -- two modules Partialed
201
+ # in the same test file must not collide on one
202
+ # shared types header filename
203
+ c_module: module_contents,
204
+ output_path: testable.paths[:partials],
205
+ includes: types_header_includes,
206
+ include_guard: module_include_guard
195
207
  )
196
208
 
197
209
  implementation = @partializer.extract_implementation_functions(
@@ -235,7 +247,8 @@ class TestBuildExecutor
235
247
  test: name
236
248
  ),
237
249
  input_filepath: config.source.filepath,
238
- output_path: testable.paths[:partials]
250
+ output_path: testable.paths[:partials],
251
+ include_guard: module_include_guard
239
252
  }
240
253
 
241
254
  unless implementation.nil?
@@ -256,7 +269,8 @@ class TestBuildExecutor
256
269
  ),
257
270
  c_module: module_contents,
258
271
  input_filepath: config.header.filepath,
259
- output_path: testable.paths[:partials]
272
+ output_path: testable.paths[:partials],
273
+ include_guard: module_include_guard
260
274
  }
261
275
 
262
276
  unless interface.nil?
data/lib/version.rb CHANGED
@@ -15,7 +15,7 @@
15
15
  module Ceedling
16
16
  module Version
17
17
  # Convenience constants for gem building, etc.
18
- GEM = '1.1.9'
18
+ GEM = '1.1.10'
19
19
  TAG = GEM
20
20
 
21
21
  # If run as a script print Ceedling's version to $stdout
@@ -4080,6 +4080,13 @@ Please familiarize yourself with it before participating.</p>
4080
4080
  <p>Guidelines for contributing to this project — be it code, reviews,
4081
4081
  documentation, or issue reports.</p>
4082
4082
  </li>
4083
+ <li>
4084
+ <p><span class="twemoji"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 1 3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5zm0 6c1.4 0 2.8 1.1 2.8 2.5V11c.6 0 1.2.6 1.2 1.3v3.5c0 .6-.6 1.2-1.3 1.2H9.2c-.6 0-1.2-.6-1.2-1.3v-3.5c0-.6.6-1.2 1.2-1.2V9.5C9.2 8.1 10.6 7 12 7m0 1.2c-.8 0-1.5.5-1.5 1.3V11h3V9.5c0-.8-.7-1.3-1.5-1.3"/></svg></span> <strong><a href="https://github.com/ThrowTheSwitch/Ceedling?tab=security-ov-file">Security</a></strong></p>
4085
+ <hr />
4086
+ <p>See the <a href="https://github.com/ThrowTheSwitch/Ceedling/blob/master/docs/SECURITY.md">policy</a> for disclosure details and
4087
+ guidance on issues that vulnerability inspection tools may surface.
4088
+ Report a vulnerability through a <a href="https://github.com/ThrowTheSwitch/Ceedling/security/advisories/new">private advisory</a>.</p>
4089
+ </li>
4083
4090
  </ul>
4084
4091
  </div>
4085
4092
  <h2 id="projects">Projects</h2>
@@ -4424,7 +4424,7 @@
4424
4424
  <h1 id="fake-function-framework-for-ceedling">Fake Function Framework for Ceedling</h1>
4425
4425
  <p>This plugin causes Ceedling to use the <a href="https://github.com/meekrosoft/fff">Fake Function Framework</a> for mocking instead of CMock, the default mocking framework packaged with Ceedling.</p>
4426
4426
  <p>Using <em>FFF</em> provides less strict mocking than CMock and affords more loosely-coupled tests.</p>
4427
- <p>This Ceedling 1.x plugin incorporates a snapshot of <em>FFF</em> version 0.1.1 and supersedes a separately available <a href="https://github.com/ElectronVector/fake_function_framework">FFF Ceedling plugin project</a>. The built-in <em>FFF</em> plugin that now comes with Ceedling was derived from the ElectronVector project and is now maintained along with Ceedling and tracks its updates.</p>
4427
+ <p>This Ceedling 1.x plugin incorporates a snapshot of <em>FFF</em> version 1.1 and supersedes a separately available <a href="https://github.com/ElectronVector/fake_function_framework">FFF Ceedling plugin project</a>. The built-in <em>FFF</em> plugin that now comes with Ceedling was derived from the ElectronVector project and is now maintained along with Ceedling and tracks its updates.</p>
4428
4428
  <div class="admonition note">
4429
4429
  <p class="admonition-title">Special thanks to Matt Chernosky</p>
4430
4430
  <p><a href="http://www.electronvector.com">Matt Chernosky</a> originally developed this plugin
@@ -2999,6 +2999,34 @@
2999
2999
  </ul>
3000
3000
  </nav>
3001
3001
 
3002
+ </li>
3003
+
3004
+ <li class="md-nav__item">
3005
+ <a href="#results-filtering" class="md-nav__link">
3006
+ <span class="md-ellipsis">
3007
+
3008
+ Results filtering
3009
+
3010
+ </span>
3011
+ </a>
3012
+
3013
+ <nav class="md-nav" aria-label="Results filtering">
3014
+ <ul class="md-nav__list">
3015
+
3016
+ <li class="md-nav__item">
3017
+ <a href="#overriding-the-defaults" class="md-nav__link">
3018
+ <span class="md-ellipsis">
3019
+
3020
+ Overriding the defaults
3021
+
3022
+ </span>
3023
+ </a>
3024
+
3025
+ </li>
3026
+
3027
+ </ul>
3028
+ </nav>
3029
+
3002
3030
  </li>
3003
3031
 
3004
3032
  <li class="md-nav__item">
@@ -4641,6 +4669,34 @@
4641
4669
  </ul>
4642
4670
  </nav>
4643
4671
 
4672
+ </li>
4673
+
4674
+ <li class="md-nav__item">
4675
+ <a href="#results-filtering" class="md-nav__link">
4676
+ <span class="md-ellipsis">
4677
+
4678
+ Results filtering
4679
+
4680
+ </span>
4681
+ </a>
4682
+
4683
+ <nav class="md-nav" aria-label="Results filtering">
4684
+ <ul class="md-nav__list">
4685
+
4686
+ <li class="md-nav__item">
4687
+ <a href="#overriding-the-defaults" class="md-nav__link">
4688
+ <span class="md-ellipsis">
4689
+
4690
+ Overriding the defaults
4691
+
4692
+ </span>
4693
+ </a>
4694
+
4695
+ </li>
4696
+
4697
+ </ul>
4698
+ </nav>
4699
+
4644
4700
  </li>
4645
4701
 
4646
4702
  <li class="md-nav__item">
@@ -5297,9 +5353,14 @@ plugin configuration. This prevents overriding config file settings with
5297
5353
  CLI arguments. You must provide any settings that would have been provided
5298
5354
  by the Gcov plugin.</p>
5299
5355
  </div>
5300
- <p>To preserve filtering of test and build files from coverage results when
5301
- using a Gcovr config file, you must provide explicit exclusion patterns matching
5302
- your project layout (example below).</p>
5356
+ <p>A Gcovr config file replaces Ceedling's own automatically generated exclusion
5357
+ patterns (see <a href="#results-filtering">Results filtering</a> below) entirely — it is
5358
+ the only way to override or remove one of those defaults, since the
5359
+ project-configuration <code>:report_exclude</code> option can only add further
5360
+ exclusions on top of them, never take one away. To preserve the same
5361
+ filtering of test and build files when switching to a Gcovr config file, you
5362
+ must provide equivalent explicit exclusion patterns matching your project
5363
+ layout yourself (example below).</p>
5303
5364
  <div class="highlight"><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="c1">; You will need to revise these example exclude patterns to match your</span>
5304
5365
  <a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="c1">; project directories and file naming as they cannot be automatically </span>
5305
5366
  <a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="c1">; provided to Gcovr when a Gcovr configuration file is in use.</span>
@@ -5312,6 +5373,84 @@ your project layout (example below).</p>
5312
5373
  <a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a><span class="c1">; Build path exlcude for all generated C files (runners, mocks, partials).</span>
5313
5374
  <a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a><span class="na">exclude</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">.*build/.+\.c$</span>
5314
5375
  </code></pre></div>
5376
+ <h2 id="results-filtering">Results filtering</h2>
5377
+ <p>By default, this plugin configures <code>gcovr</code> to excludes three categories of
5378
+ <code>.c</code> files from coverage results. These defaults exist because a
5379
+ coverage percentage is typically only meaningful over the production code
5380
+ you're actually trying to exercise with test coverage. Test code, test
5381
+ support code, and every file Ceedling itself generates as part of a build
5382
+ will skew that number if left in coverage reporting.</p>
5383
+ <p>The list that follows details the filtering this plugin injects into
5384
+ Gcovr coverage report generation. The examples are usable regular
5385
+ expressions that mirror the defaults in use. The plugin dynamically
5386
+ generates these regular expressions from your project configuration.
5387
+ When creating them youself, you will need to match your project
5388
+ configuration settings with static strings.</p>
5389
+ <ol>
5390
+ <li>
5391
+ <p><strong>Test files</strong> — matched by <code>:test_file_prefix</code> within your configured
5392
+ <code>:paths</code> ↳ <code>:test</code> directories.</p>
5393
+ <p>Given <code>:test_file_prefix</code> ⇒ <code>test_</code> and a <code>:paths</code> ↳ <code>:test</code> entry of
5394
+ <code>test/</code>:</p>
5395
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a>.*test/.*/test_.+\.c$
5396
+ </code></pre></div>
5397
+ </li>
5398
+ <li>
5399
+ <p><strong>Test support files</strong> — any <code>.c</code> file within your configured <code>:paths</code> ↳
5400
+ <code>:support</code> directories, regardless of name. Helpers, stubs, and fixtures
5401
+ living there are no more production code than the test files themselves.</p>
5402
+ <p>Given a <code>:paths</code> ↳ <code>:support</code> entry of <code>test/support/</code>:</p>
5403
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a>.*test/support/.+\.c$
5404
+ </code></pre></div>
5405
+ </li>
5406
+ <li>
5407
+ <p><strong>Generated and vendored files</strong> — any <code>.c</code> file anywhere below your
5408
+ <code>:build_root</code>: generated mocks, test runners, Partials output, and the
5409
+ vendored Unity/CMock/CException framework sources Ceedling copies in to
5410
+ build against. This pattern always matches a literal <code>.c</code> extension
5411
+ regardless of your project's own <code>:extension</code> ↳ <code>:source</code> setting, since
5412
+ every file it catches is one Ceedling itself writes in plain C.</p>
5413
+ <p>Given <code>:build_root</code> ⇒ <code>build/</code>:</p>
5414
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a>.*build/.+\.c$
5415
+ </code></pre></div>
5416
+ </li>
5417
+ </ol>
5418
+ <p>These patterns are generated automatically and combined with whatever you
5419
+ provide via <a href="#report_exclude"><code>:report_exclude</code></a> below.</p>
5420
+ <div class="admonition warning">
5421
+ <p class="admonition-title">Not applied when using a Gcovr configuration file</p>
5422
+ <p>These defaults are only generated when Ceedling builds <code>gcovr</code>'s command
5423
+ line directly. As covered in
5424
+ <a href="#gcovr-configuration-file">Gcovr configuration file</a> above, setting
5425
+ <code>:config_file</code> bypasses this entirely — none of the three patterns above
5426
+ are applied, and you must supply equivalent exclusions in the config file
5427
+ yourself.</p>
5428
+ </div>
5429
+ <h3 id="overriding-the-defaults">Overriding the defaults</h3>
5430
+ <p><a href="#report_exclude"><code>:report_exclude</code></a> can only add exclusions on top of the
5431
+ three defaults above — <code>gcovr --exclude</code> is a deny-list with no way to
5432
+ "un-exclude" a pattern already passed to it, and this plugin always passes its
5433
+ own three patterns ahead of anything you configure there. Setting
5434
+ <a href="#report_include"><code>:report_include</code></a> doesn't help either; it narrows which
5435
+ files are considered at all, but an exclude pattern still wins over it for
5436
+ any file matching both.</p>
5437
+ <p>The only way to actually remove or replace one of these defaults — for
5438
+ example, to include test files in coverage results so you can confirm a
5439
+ conditional test build compiled the branches you expect — is a
5440
+ <a href="#gcovr-configuration-file">Gcovr configuration file</a>. Setting <code>:config_file</code>
5441
+ stops Ceedling from generating any of the three patterns at all, handing you
5442
+ full control:</p>
5443
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="c1">; gcovr.cfg — omits the test-file exclude pattern so test files remain in</span>
5444
+ <a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a><span class="c1">; coverage results, while still keeping generated/vendored build output out.</span>
5445
+ <a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a><span class="na">exclude</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">.*build/.+\.c$</span>
5446
+ </code></pre></div>
5447
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="nt">:gcov</span><span class="p">:</span>
5448
+ <a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a><span class="w"> </span><span class="nt">:gcovr</span><span class="p">:</span>
5449
+ <a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a><span class="w"> </span><span class="nt">:config_file</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">gcovr.cfg</span>
5450
+ </code></pre></div>
5451
+ <p>Note that this isn't scoped per category. Once a default is dropped, any
5452
+ file it would have excluded is folded into the same combined report as your
5453
+ production code, not broken out separately.</p>
5315
5454
  <h2 id="plugin-configuration">Plugin configuration</h2>
5316
5455
  <p>The following options are exposed through your project configuration file, all
5317
5456
  beneath <code>:gcov</code> ↳ <code>:gcovr</code>. Only specify those you need or those whose defaults
@@ -5409,11 +5548,25 @@ is in addition to any other configured reports. (<code>gcovr --print-summary</co
5409
5548
  <h3 id="report_include"><code>:report_include</code></h3>
5410
5549
  <p>Keep only source files that match this filter. Filters are regular
5411
5550
  expressions. (<code>gcovr --filter</code>)</p>
5551
+ <p>This narrows which files are considered at all, but does not override
5552
+ <a href="#results-filtering">Results filtering</a>'s automatic exclusion defaults — a
5553
+ file excluded by one of those three default patterns stays excluded even if
5554
+ it also matches <code>:report_include</code>. See
5555
+ <a href="#overriding-the-defaults">Overriding the defaults</a> if you need to remove one
5556
+ of them.</p>
5412
5557
  <p><strong>Example:</strong> <code>"^src"</code></p>
5413
5558
  <hr />
5414
5559
  <h3 id="report_exclude"><code>:report_exclude</code></h3>
5415
5560
  <p>Exclude source files that match this filter. Filters are regular expressions.
5416
5561
  (<code>gcovr --exclude</code>)</p>
5562
+ <p>Ceedling automatically generates and prepends its own exclusion patterns for
5563
+ test files, test support files, and generated/vendored build files — see
5564
+ <a href="#results-filtering">Results filtering</a> above for exactly what these cover
5565
+ and why. Anything you provide here is combined with those defaults, not a
5566
+ replacement for them. This option can only add further exclusions, never
5567
+ remove or override one of the three defaults.
5568
+ See <a href="#overriding-the-defaults">Overriding the defaults</a> for how to do
5569
+ that instead.</p>
5417
5570
  <p><strong>Example:</strong> <code>"^vendor.*|^build.*|^test.*|^lib.*"</code></p>
5418
5571
  <hr />
5419
5572
  <h3 id="gcov_filter"><code>:gcov_filter</code></h3>