ceedling 1.1.8 → 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.
- checksums.yaml +4 -4
- data/GIT_COMMIT_SHA +1 -1
- data/README.md +4 -3
- data/docs/Changelog.md +28 -0
- data/docs/KnownIssues.md +12 -2
- data/docs/SECURITY.md +40 -2
- data/docs/mkdocs/development/index.md +11 -0
- data/docs/mkdocs/plugins/fff.md +1 -1
- data/docs/mkdocs/plugins/gcov/gcovr.md +117 -3
- data/docs/mkdocs/plugins/gcov/reportgenerator.md +110 -4
- data/lib/ceedling/c_extractor/c_extractor_preprocessing.rb +29 -1
- data/lib/ceedling/file_path_utils.rb +4 -0
- data/lib/ceedling/generators/generator.rb +11 -6
- data/lib/ceedling/generators/generator_partials.rb +54 -18
- data/lib/ceedling/includes/includes.rb +8 -1
- data/lib/ceedling/objects.yml +2 -0
- data/lib/ceedling/partials/partializer.rb +204 -96
- data/lib/ceedling/test_invoker/test_build_executor.rb +21 -7
- data/lib/version.rb +1 -1
- data/site-local/development/index.html +7 -0
- data/site-local/plugins/fff.html +1 -1
- data/site-local/plugins/gcov/gcovr.html +156 -3
- data/site-local/plugins/gcov/reportgenerator.html +201 -14
- data/site-local/sitemap.xml.gz +0 -0
- data/spec/system/partials_types_header_spec.rb +166 -0
- data/spec/units/c_extractor/c_extractor_integration_spec.rb +18 -0
- data/spec/units/c_extractor/c_extractor_preprocessing_spec.rb +77 -0
- data/spec/units/generators/generator_partials_spec.rb +130 -17
- data/spec/units/includes/includes_spec.rb +21 -3
- data/spec/units/partials/partializer_spec.rb +225 -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
|
@@ -21,7 +21,8 @@ class GeneratorPartials
|
|
|
21
21
|
source_includes:,
|
|
22
22
|
header_includes:,
|
|
23
23
|
c_module:,
|
|
24
|
-
output_path
|
|
24
|
+
output_path:,
|
|
25
|
+
include_guard: nil
|
|
25
26
|
)
|
|
26
27
|
source = @file_path_utils.form_partial_implementation_source_filename(name)
|
|
27
28
|
header = @file_path_utils.form_partial_implementation_header_filename(name)
|
|
@@ -34,7 +35,10 @@ class GeneratorPartials
|
|
|
34
35
|
# write, which would alter any line ending already present in that
|
|
35
36
|
# content instead of passing it through unchanged.
|
|
36
37
|
@file_wrapper.open(header_filepath, 'wb') do |file|
|
|
37
|
-
generate_header(
|
|
38
|
+
generate_header(
|
|
39
|
+
file, header, header_includes, function_definitions, c_module,
|
|
40
|
+
include_variables: true, include_guard: include_guard
|
|
41
|
+
)
|
|
38
42
|
end
|
|
39
43
|
|
|
40
44
|
@file_wrapper.open(source_filepath, 'wb') do |file|
|
|
@@ -44,13 +48,16 @@ class GeneratorPartials
|
|
|
44
48
|
return source_filepath
|
|
45
49
|
end
|
|
46
50
|
|
|
47
|
-
def generate_interface(test:, name:, function_declarations:, includes:, c_module:, output_path:)
|
|
51
|
+
def generate_interface(test:, name:, function_declarations:, includes:, c_module:, output_path:, include_guard: nil)
|
|
48
52
|
header = @file_path_utils.form_partial_interface_header_filename(name)
|
|
49
53
|
filepath = File.join(output_path, header)
|
|
50
54
|
|
|
51
55
|
# Binary mode: see generate_implementation above.
|
|
52
56
|
@file_wrapper.open(filepath, 'wb') do |file|
|
|
53
|
-
generate_header(
|
|
57
|
+
generate_header(
|
|
58
|
+
file, header, includes, function_declarations, c_module,
|
|
59
|
+
include_variables: false, include_guard: include_guard
|
|
60
|
+
)
|
|
54
61
|
end
|
|
55
62
|
|
|
56
63
|
return filepath
|
|
@@ -69,7 +76,7 @@ class GeneratorPartials
|
|
|
69
76
|
# @param c_module [CExtractorTypes::CModule] Merged module with type_definitions/aggregate_definitions
|
|
70
77
|
# @param output_path [String] Directory shared with the implementation and interface headers
|
|
71
78
|
# @return [String, nil] The bare filename (for use as a sibling #include), or nil if nothing was generated
|
|
72
|
-
def generate_types(name:, c_module:, output_path:)
|
|
79
|
+
def generate_types(name:, c_module:, output_path:, includes: [], include_guard: nil)
|
|
73
80
|
return nil if c_module.type_definitions.empty? && c_module.aggregate_definitions.empty?
|
|
74
81
|
|
|
75
82
|
header = @file_path_utils.form_partial_types_header_filename(name)
|
|
@@ -78,8 +85,7 @@ class GeneratorPartials
|
|
|
78
85
|
# Binary mode: see generate_implementation above.
|
|
79
86
|
@file_wrapper.open(filepath, 'wb') do |file|
|
|
80
87
|
guard = FileWrapper.generate_include_guard(header)
|
|
81
|
-
file
|
|
82
|
-
file << "#define #{guard}\n\n"
|
|
88
|
+
emit_preamble( file, guard, include_guard, includes )
|
|
83
89
|
|
|
84
90
|
anything_emitted = false
|
|
85
91
|
pending_macros = []
|
|
@@ -88,7 +94,10 @@ class GeneratorPartials
|
|
|
88
94
|
next unless item.is_a?(CExtractorTypes::CStatement)
|
|
89
95
|
|
|
90
96
|
if c_module.macro_definitions.include?(item)
|
|
91
|
-
|
|
97
|
+
# The guard emitted above also arrives among the extracted macros, since the real
|
|
98
|
+
# header's own `#define` survives preprocessing. Carrying it a second time is legal
|
|
99
|
+
# but puts a pointless duplicate in every generated types header.
|
|
100
|
+
pending_macros << item unless guard_macro?(item, include_guard)
|
|
92
101
|
next
|
|
93
102
|
end
|
|
94
103
|
|
|
@@ -116,6 +125,38 @@ class GeneratorPartials
|
|
|
116
125
|
|
|
117
126
|
private
|
|
118
127
|
|
|
128
|
+
# Everything above a generated header's own content: the file's include guard, the spoof of the
|
|
129
|
+
# real module header's guard, and the includes that header carries. Shared by all three
|
|
130
|
+
# generated headers, which open identically.
|
|
131
|
+
#
|
|
132
|
+
# The order of the last two is the whole point. A carried header can transitively reach the
|
|
133
|
+
# real module header, so the spoof has to be satisfied before any include is processed rather
|
|
134
|
+
# than after. And relocated content names things a generated file does not define, so without
|
|
135
|
+
# the includes it compiles only where something earlier in the translation unit happened to
|
|
136
|
+
# supply those names -- which is what made the types header's placement a conflict between two
|
|
137
|
+
# correct requirements rather than a choice.
|
|
138
|
+
def emit_preamble(io, guard, include_guard, includes)
|
|
139
|
+
io << "#ifndef #{guard}\n"
|
|
140
|
+
io << "#define #{guard}\n\n"
|
|
141
|
+
|
|
142
|
+
# A module header with no guard of its own supplies nothing to spoof.
|
|
143
|
+
io << "#define #{include_guard}\n\n" if include_guard
|
|
144
|
+
|
|
145
|
+
return if includes.empty?
|
|
146
|
+
|
|
147
|
+
includes.each { |include| io << "#{include}\n" }
|
|
148
|
+
io << "\n"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Whether a macro statement is nothing but the definition of `include_guard`. Anchored so a
|
|
152
|
+
# macro whose name merely begins with the guard's name is never mistaken for it, and so a
|
|
153
|
+
# guard-named macro carrying an actual value is left alone.
|
|
154
|
+
def guard_macro?(item, include_guard)
|
|
155
|
+
return false if include_guard.nil?
|
|
156
|
+
|
|
157
|
+
item.text.match?(/\A\s*#\s*define\s+#{Regexp.escape( include_guard )}\s*\z/)
|
|
158
|
+
end
|
|
159
|
+
|
|
119
160
|
# A typedef or a non-typedef struct/enum/union tag definition establishes a type; C treats
|
|
120
161
|
# a second definition of the same type in one translation unit as a redefinition error even
|
|
121
162
|
# when the two definitions are textually identical. A macro statement carries no such
|
|
@@ -159,17 +200,9 @@ class GeneratorPartials
|
|
|
159
200
|
# @param function_list [Array] Pre-filtered Partials function objects (respond to :name and :signature)
|
|
160
201
|
# @param c_module [CExtractorTypes::CModule] Merged module with element_sequence
|
|
161
202
|
# @param include_variables [Boolean] True for implementation header (emits extern vars); false for interface
|
|
162
|
-
def generate_header(io, name, includes, function_list, c_module, include_variables)
|
|
203
|
+
def generate_header(io, name, includes, function_list, c_module, include_variables:, include_guard: nil)
|
|
163
204
|
guard = FileWrapper.generate_include_guard( name )
|
|
164
|
-
|
|
165
|
-
io << "#ifndef #{guard}\n"
|
|
166
|
-
io << "#define #{guard}\n\n"
|
|
167
|
-
|
|
168
|
-
includes.each do |include|
|
|
169
|
-
io << "#{include}\n"
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
io << "\n" if !includes.empty?
|
|
205
|
+
emit_preamble( io, guard, include_guard, includes )
|
|
173
206
|
|
|
174
207
|
func_by_name = function_list.to_h { |f| [f.name, f] }
|
|
175
208
|
emitted_funcs = {}
|
|
@@ -192,6 +225,9 @@ class GeneratorPartials
|
|
|
192
225
|
# generate_types instead of here, so that content defines a type exactly once no
|
|
193
226
|
# matter how many of a module's generated headers end up in the same test file.
|
|
194
227
|
next if type_defining?(item, c_module)
|
|
228
|
+
# The spoof above already states this one deliberately; carrying it again would put a
|
|
229
|
+
# pointless duplicate in the file.
|
|
230
|
+
next if guard_macro?(item, include_guard)
|
|
195
231
|
io << item.text << "\n"
|
|
196
232
|
last_was_func = false
|
|
197
233
|
anything_emitted = true
|
|
@@ -254,8 +254,15 @@ class Includes
|
|
|
254
254
|
return _includes
|
|
255
255
|
end
|
|
256
256
|
|
|
257
|
+
# Ruby's sort/sort_by family does not guarantee a stable sort -- the relative order
|
|
258
|
+
# of elements a comparator treats as equal (every UserInclude here, all mapping to
|
|
259
|
+
# the same "1") is free to come out differently across Ruby versions and platforms.
|
|
260
|
+
# partition sidesteps the question entirely: it's a simple filter, not a comparator
|
|
261
|
+
# sort, so each group's own original relative order is preserved by definition, on
|
|
262
|
+
# every Ruby version and platform, with nothing to be unstable about.
|
|
257
263
|
def self.sort!(includes)
|
|
258
|
-
includes.
|
|
264
|
+
system, other = includes.partition { |include| include.is_a?(SystemInclude) }
|
|
265
|
+
includes.replace(system + other)
|
|
259
266
|
return includes
|
|
260
267
|
end
|
|
261
268
|
end
|
data/lib/ceedling/objects.yml
CHANGED
|
@@ -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,
|
|
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,115 +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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
includes: _includes,
|
|
96
|
-
modules: ([name] + partialized_modules)
|
|
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
|
|
97
106
|
)
|
|
98
|
-
|
|
99
|
-
# When this module has any typedefs or aggregate definitions, they live in a shared
|
|
100
|
-
# header generated once and included here rather than duplicated inline.
|
|
101
|
-
_includes << UserInclude.new(types_header) if types_header
|
|
102
|
-
|
|
103
|
-
# Remove any duplicates
|
|
104
|
-
Includes.sanitize!(_includes)
|
|
105
|
-
|
|
106
|
-
@loginator.log_list(
|
|
107
|
-
_includes,
|
|
108
|
-
"Header includes to inject for testable Partial #{test}::#{name}:",
|
|
109
|
-
Verbosity::OBNOXIOUS
|
|
110
|
-
) if test
|
|
111
|
-
|
|
112
|
-
return _includes
|
|
113
107
|
end
|
|
114
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
|
+
#
|
|
115
113
|
# When `test:` is provided, logs the resulting includes at OBNOXIOUS.
|
|
116
|
-
def
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
|
122
123
|
)
|
|
124
|
+
end
|
|
123
125
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
@file_path_utils.form_partial_interface_header_filename(_module)
|
|
140
|
-
)
|
|
141
|
-
# Remember the module for later removal of original header
|
|
142
|
-
mockable_modules << _module
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
# Remove the original module header now that it's remapped to mockable interface
|
|
148
|
-
# Remove our own orginal name as well
|
|
149
|
-
_includes = remove_matching_includes(
|
|
150
|
-
includes: _includes,
|
|
151
|
-
modules: ([name] + 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
|
|
152
141
|
)
|
|
153
|
-
|
|
154
|
-
# Remove any duplicates
|
|
155
|
-
Includes.sanitize!(_includes)
|
|
156
|
-
|
|
157
|
-
@loginator.log_list(
|
|
158
|
-
_includes,
|
|
159
|
-
"Source includes to inject for testable Partial #{test}::#{name}:",
|
|
160
|
-
Verbosity::OBNOXIOUS
|
|
161
|
-
) if test
|
|
162
|
-
|
|
163
|
-
return _includes
|
|
164
142
|
end
|
|
165
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
|
+
#
|
|
166
150
|
# When `test:` is provided, logs the resulting includes at OBNOXIOUS.
|
|
167
|
-
def
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
includes: _includes,
|
|
177
|
-
modules: ([name] + partialized_modules)
|
|
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
|
|
178
160
|
)
|
|
161
|
+
end
|
|
179
162
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
) if test
|
|
192
|
-
|
|
193
|
-
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 )
|
|
194
174
|
end
|
|
195
175
|
|
|
196
176
|
# Extracts and combines C code contents from header and source files
|
|
@@ -452,6 +432,134 @@ class Partializer
|
|
|
452
432
|
)
|
|
453
433
|
end
|
|
454
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
|
+
|
|
523
|
+
# Swaps `name`'s own header include for `replacement` at that same list position,
|
|
524
|
+
# instead of stripping it out and appending the replacement at the very end.
|
|
525
|
+
#
|
|
526
|
+
# Both call sites' generated replacements (the shared types header; the generated
|
|
527
|
+
# implementation header, which itself carries the types header) spoof the real
|
|
528
|
+
# header's own include guard (a top-of-file `#define <ORIGINAL_GUARD>`) so that if the
|
|
529
|
+
# real header is *also* reached a second way -- e.g. a transitively-included,
|
|
530
|
+
# differently-named header that itself does a genuine `#include` of this module's
|
|
531
|
+
# real header -- the real header's guard is already
|
|
532
|
+
# tripped and its content (a second, conflicting copy of the same typedefs/structs)
|
|
533
|
+
# never gets processed. That only works if the spoofing macro is defined *before*
|
|
534
|
+
# such a transitive re-inclusion is reached, not after. Appending at the very end put
|
|
535
|
+
# it after any such re-inclusion instead of before it. Keeping the replacement at the
|
|
536
|
+
# original header's own list position preserves everything the generated content
|
|
537
|
+
# depends on that came before it in the source's own working include order (e.g. a
|
|
538
|
+
# shared Types.h), while still landing ahead of anything transitively re-reaching the
|
|
539
|
+
# real header afterward.
|
|
540
|
+
def splice_in_replacement(includes:, name:, replacement:)
|
|
541
|
+
return remove_matching_includes(includes: includes, modules: [name]) unless replacement
|
|
542
|
+
|
|
543
|
+
replaced = false
|
|
544
|
+
spliced = includes.map do |include|
|
|
545
|
+
if !replaced && include.filename.ext().downcase() == name.downcase()
|
|
546
|
+
replaced = true
|
|
547
|
+
UserInclude.new(replacement)
|
|
548
|
+
else
|
|
549
|
+
include
|
|
550
|
+
end
|
|
551
|
+
end
|
|
552
|
+
# This module's own header wasn't in the includes list at all -- no original
|
|
553
|
+
# position to preserve, so fall back to appending.
|
|
554
|
+
spliced << UserInclude.new(replacement) unless replaced
|
|
555
|
+
|
|
556
|
+
# A duplicate/case-variant entry beyond the first match (e.g. both 'module.h' and
|
|
557
|
+
# 'MODULE.H' present) has no meaningful position of its own to preserve -- drop it
|
|
558
|
+
# like remove_matching_includes always has.
|
|
559
|
+
spliced = remove_matching_includes(includes: spliced, modules: [name])
|
|
560
|
+
spliced
|
|
561
|
+
end
|
|
562
|
+
|
|
455
563
|
# Remove includes that match the given module names (case-insensitive)
|
|
456
564
|
# Returns a new array with matching includes removed
|
|
457
565
|
def remove_matching_includes(includes:, modules:)
|
|
@@ -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:
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
c_module:
|
|
194
|
-
output_path:
|
|
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
|
@@ -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>
|
data/site-local/plugins/fff.html
CHANGED
|
@@ -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
|
|
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
|