ceedling 1.1.8 → 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 +18 -0
- data/docs/KnownIssues.md +1 -0
- data/lib/ceedling/c_extractor/c_extractor_preprocessing.rb +29 -1
- data/lib/ceedling/file_path_utils.rb +4 -0
- data/lib/ceedling/includes/includes.rb +8 -1
- data/lib/ceedling/partials/partializer.rb +69 -24
- data/lib/version.rb +1 -1
- data/site-local/sitemap.xml.gz +0 -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/includes/includes_spec.rb +21 -3
- data/spec/units/partials/partializer_spec.rb +62 -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 +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 28f18f89f99b7e81f68293c994fa85ff25532ca62bd250c2d1c7e351ec3ec7a0
|
|
4
|
+
data.tar.gz: 1252cbea2a5752315c65826f8068313450cf08d0ea63259e1e8a5d422588140c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7aeb58a943b8df79fa31e12694a16019fba66cf798cc899eccb4d1e7b64d0b5fe09e5965f376791381cbc8c0e47a65c0563dbc5a00e1523714e813b8491a9896
|
|
7
|
+
data.tar.gz: e6cf0643c68cd8fa2c96b99de3e2e95ca276b27bf5ecb5cfa41ae9f1371ed55f3675ec5491d585b989629f2f89b0f16fc65f0586daa6ade065fbe903d9208087
|
data/GIT_COMMIT_SHA
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
2209dc2
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Ceedling 
|
|
2
2
|
========
|
|
3
3
|
|
|
4
|
-
**Ceedling 1.1.
|
|
4
|
+
**Ceedling 1.1.9** is the latest and greatest.
|
|
5
5
|
|
|
6
6
|
See [_Release Notes_][release-notes], [_Changelog_](docs/Changelog.md),
|
|
7
7
|
[_Breaking Changes_][breaking-changes], and [_Known Issues_][known-issues].
|
data/docs/Changelog.md
CHANGED
|
@@ -10,6 +10,24 @@ This changelog is complemented by three other documents:
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
# [1.1.9] — 2026-09-20
|
|
14
|
+
|
|
15
|
+
## 💪 Fixed
|
|
16
|
+
|
|
17
|
+
### Partials
|
|
18
|
+
|
|
19
|
+
- [#1293](https://github.com/ThrowTheSwitch/Ceedling/issues/1293) Fixed a Partials-generated header occasionally redefining a type already declared by the real header it replaces, causing a `typedef redefinition with different types` compilation error. The triggering condition involved a source file whose real header was reachable both directly and through a macro-included intermediate file allowed by incomplete Partials include directive ordering.
|
|
20
|
+
|
|
21
|
+
### Preprocessing
|
|
22
|
+
|
|
23
|
+
- Fixed whitespace occasionally inserted by the underlying compiler's preprocessor around a `#` or `##` operator (e.g. `x ##y` becoming `x ## y`) being carried through verbatim into Partials-generated and reconstructed macro definitions. Extracted macro text is now normalized so preprocessing stringize and token-paste operators always sit directly against their operands, regardless of what a given toolchain's preprocessor happens to emit.
|
|
24
|
+
|
|
25
|
+
## ⚠️ Changed
|
|
26
|
+
|
|
27
|
+
- Updated Unity to latest version.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
13
31
|
# [1.1.8] — 2026-09-10
|
|
14
32
|
|
|
15
33
|
## 💪 Fixed
|
data/docs/KnownIssues.md
CHANGED
|
@@ -22,6 +22,7 @@ Known issues are complemented by three other documents:
|
|
|
22
22
|
1. Any path for a C file specified with `TEST_SOURCE_FILE(...)` is in relation to **_project root_** — that is, from where you execute `ceedling` at the command line. If you move source files or change your directory structure, many of your `TEST_SOURCE_FILE(...)` calls may need to be updated. A more flexible and dynamic approach to path handling will come in a future update.
|
|
23
23
|
1. In certain combinations of conditional preprocessing blocks with dependent symbols defined in another file, Ceedling can silently fail to extract computed includes (e.g. `#include SOME_MACRO()`).
|
|
24
24
|
1. User includes (`#include "path/user.h"`) from source C files lose their relative path when Partial and mock header files are generated from source. Compilation failures can result from certain uncommon cases involving headers of the same names in different directories and a source file including both such headers.
|
|
25
|
+
1. The automatic vendor copying of Unity, CMock, and CException into a project's build directory can intermittently fail with a file/directory type error, most often under antivirus/EDR file locking, cloud-sync filter drivers, or two concurrent Ceedling invocations racing the same destination. Deleting `build/` (or reinstalling the gem) and rebuilding works around it.
|
|
25
26
|
1. The Bullseye code coverage plugin has been temporarily disabled as of 1.0.0. The makers of Bullseye have generously provided a license for development, and the plugin will be available in the next minor release.
|
|
26
27
|
|
|
27
28
|
---
|
|
@@ -246,7 +246,10 @@ class CExtractorPreprocessing
|
|
|
246
246
|
# literal run -- it has to be seen and dispatched on its own below, before
|
|
247
247
|
# any '"' or "'" inside the comment's own text gets mistaken for the start
|
|
248
248
|
# of a string/char literal (see the // and /* branches for why that matters).
|
|
249
|
-
|
|
249
|
+
# '#' is excluded the same way, so a #/## operator is seen and dispatched on
|
|
250
|
+
# its own below rather than absorbed into this run along with whatever
|
|
251
|
+
# whitespace happens to surround it (see the ## and # branches for why).
|
|
252
|
+
text << (scanner.scan(/[^"'\\\n\/#]*/) || '')
|
|
250
253
|
|
|
251
254
|
if scanner.scan(%r{//})
|
|
252
255
|
# A trailing line comment (e.g. "// don't change this") can contain an
|
|
@@ -280,6 +283,31 @@ class CExtractorPreprocessing
|
|
|
280
283
|
elsif scanner.scan(%r{/})
|
|
281
284
|
text << '/'
|
|
282
285
|
|
|
286
|
+
elsif scanner.scan(/##/)
|
|
287
|
+
# The C standard specifies ##'s token-paste semantics but not the exact
|
|
288
|
+
# whitespace a preprocessor emits when reconstructing macro text -- real
|
|
289
|
+
# toolchains can and do disagree here, and Ceedling regenerates this text
|
|
290
|
+
# into partials rather than promising byte-for-byte reproduction of that
|
|
291
|
+
# spacing. Discard whatever immediately follows the operator in the source,
|
|
292
|
+
# so its right-hand operand always glues on directly. The left side only
|
|
293
|
+
# trims when a word character (the operand actually being pasted) is
|
|
294
|
+
# immediately adjacent -- a lookbehind, not a blind rstrip -- so unrelated
|
|
295
|
+
# punctuation spacing that happens to precede the operator (e.g. the space
|
|
296
|
+
# after a macro argument's comma, or after the parameter list's closing
|
|
297
|
+
# paren when the replacement list itself starts with the operator) is left
|
|
298
|
+
# alone; that space was never the preprocessor-reconstruction artifact this
|
|
299
|
+
# is defending against. ## checked before bare '#' below so a real paste
|
|
300
|
+
# operator is never split into two stringize matches.
|
|
301
|
+
text.sub!(/(?<=\w)[ \t]+\z/, '')
|
|
302
|
+
scanner.scan(/[ \t]*/)
|
|
303
|
+
text << '##'
|
|
304
|
+
|
|
305
|
+
elsif scanner.scan(/#/)
|
|
306
|
+
# Same rationale as ## above, for the stringize operator.
|
|
307
|
+
text.sub!(/(?<=\w)[ \t]+\z/, '')
|
|
308
|
+
scanner.scan(/[ \t]*/)
|
|
309
|
+
text << '#'
|
|
310
|
+
|
|
283
311
|
elsif (ch = scanner.peek(1)) == '"' || ch == "'"
|
|
284
312
|
before = scanner.pos
|
|
285
313
|
@c_extractor_code_text.skip_c_string(scanner, ch)
|
|
@@ -124,6 +124,10 @@ class FilePathUtils
|
|
|
124
124
|
pairs = paths.map { |p| [p, p.gsub('\\', '/').chomp('/')] }
|
|
125
125
|
|
|
126
126
|
# Sort shallowest-first so ancestors are always encountered before their descendants.
|
|
127
|
+
# No tiebreaker for same-depth entries needed: Ruby's sort_by doesn't guarantee a
|
|
128
|
+
# stable order among ties, but two same-depth paths can never be each other's
|
|
129
|
+
# ancestor, so the ancestor-exclusion check below is correct regardless of which
|
|
130
|
+
# order same-depth ties come out in.
|
|
127
131
|
pairs.sort_by! { |_, normalized| normalized.count('/') }
|
|
128
132
|
|
|
129
133
|
kept = []
|
|
@@ -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
|
|
@@ -86,20 +86,17 @@ class Partializer
|
|
|
86
86
|
def remap_implementation_header_includes(name:, includes:, partials:, types_header: nil, test: nil)
|
|
87
87
|
_includes = includes.clone()
|
|
88
88
|
|
|
89
|
-
#
|
|
90
|
-
|
|
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)
|
|
91
92
|
|
|
92
|
-
# Remove includes for
|
|
93
|
-
#
|
|
93
|
+
# Remove includes for every other partialized module (this module's own name was
|
|
94
|
+
# already handled above)
|
|
94
95
|
_includes = remove_matching_includes(
|
|
95
96
|
includes: _includes,
|
|
96
|
-
modules: ([name]
|
|
97
|
+
modules: (partials.keys - [name])
|
|
97
98
|
)
|
|
98
99
|
|
|
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
100
|
# Remove any duplicates
|
|
104
101
|
Includes.sanitize!(_includes)
|
|
105
102
|
|
|
@@ -116,9 +113,19 @@ class Partializer
|
|
|
116
113
|
def remap_implementation_source_includes(name:, includes:, partials:, test: nil)
|
|
117
114
|
_includes = includes.clone()
|
|
118
115
|
|
|
119
|
-
#
|
|
120
|
-
|
|
121
|
-
|
|
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)
|
|
122
129
|
)
|
|
123
130
|
|
|
124
131
|
mockable_modules = []
|
|
@@ -144,11 +151,12 @@ class Partializer
|
|
|
144
151
|
end
|
|
145
152
|
end
|
|
146
153
|
|
|
147
|
-
# Remove the original
|
|
148
|
-
#
|
|
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.
|
|
149
157
|
_includes = remove_matching_includes(
|
|
150
158
|
includes: _includes,
|
|
151
|
-
modules:
|
|
159
|
+
modules: mockable_modules
|
|
152
160
|
)
|
|
153
161
|
|
|
154
162
|
# Remove any duplicates
|
|
@@ -167,20 +175,17 @@ class Partializer
|
|
|
167
175
|
def remap_interface_header_includes(name:, includes:, partials:, types_header: nil, test: nil)
|
|
168
176
|
_includes = includes.clone()
|
|
169
177
|
|
|
170
|
-
#
|
|
171
|
-
|
|
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)
|
|
172
181
|
|
|
173
|
-
# Remove includes for
|
|
174
|
-
#
|
|
182
|
+
# Remove includes for every other partialized module (this module's own name was
|
|
183
|
+
# already handled above)
|
|
175
184
|
_includes = remove_matching_includes(
|
|
176
185
|
includes: _includes,
|
|
177
|
-
modules: ([name]
|
|
186
|
+
modules: (partials.keys - [name])
|
|
178
187
|
)
|
|
179
188
|
|
|
180
|
-
# When this module has any typedefs or aggregate definitions, they live in a shared
|
|
181
|
-
# header generated once and included here rather than duplicated inline.
|
|
182
|
-
_includes << UserInclude.new(types_header) if types_header
|
|
183
|
-
|
|
184
189
|
# Remove any duplicates
|
|
185
190
|
Includes.sanitize!(_includes)
|
|
186
191
|
|
|
@@ -452,6 +457,46 @@ class Partializer
|
|
|
452
457
|
)
|
|
453
458
|
end
|
|
454
459
|
|
|
460
|
+
# Swaps `name`'s own header include for `replacement` at that same list position,
|
|
461
|
+
# instead of stripping it out and appending the replacement at the very end.
|
|
462
|
+
#
|
|
463
|
+
# Both call sites' generated replacements (the shared types header; the generated
|
|
464
|
+
# implementation header, which itself carries the types header) spoof the real
|
|
465
|
+
# header's own include guard (a top-of-file `#define <ORIGINAL_GUARD>`) so that if the
|
|
466
|
+
# real header is *also* reached a second way -- e.g. a transitively-included,
|
|
467
|
+
# differently-named header that itself does a genuine `#include` of this module's
|
|
468
|
+
# real header -- the real header's guard is already
|
|
469
|
+
# tripped and its content (a second, conflicting copy of the same typedefs/structs)
|
|
470
|
+
# never gets processed. That only works if the spoofing macro is defined *before*
|
|
471
|
+
# such a transitive re-inclusion is reached, not after. Appending at the very end put
|
|
472
|
+
# it after any such re-inclusion instead of before it. Keeping the replacement at the
|
|
473
|
+
# original header's own list position preserves everything the generated content
|
|
474
|
+
# depends on that came before it in the source's own working include order (e.g. a
|
|
475
|
+
# shared Types.h), while still landing ahead of anything transitively re-reaching the
|
|
476
|
+
# real header afterward.
|
|
477
|
+
def splice_in_replacement(includes:, name:, replacement:)
|
|
478
|
+
return remove_matching_includes(includes: includes, modules: [name]) unless replacement
|
|
479
|
+
|
|
480
|
+
replaced = false
|
|
481
|
+
spliced = includes.map do |include|
|
|
482
|
+
if !replaced && include.filename.ext().downcase() == name.downcase()
|
|
483
|
+
replaced = true
|
|
484
|
+
UserInclude.new(replacement)
|
|
485
|
+
else
|
|
486
|
+
include
|
|
487
|
+
end
|
|
488
|
+
end
|
|
489
|
+
# This module's own header wasn't in the includes list at all -- no original
|
|
490
|
+
# position to preserve, so fall back to appending.
|
|
491
|
+
spliced << UserInclude.new(replacement) unless replaced
|
|
492
|
+
|
|
493
|
+
# A duplicate/case-variant entry beyond the first match (e.g. both 'module.h' and
|
|
494
|
+
# 'MODULE.H' present) has no meaningful position of its own to preserve -- drop it
|
|
495
|
+
# like remove_matching_includes always has.
|
|
496
|
+
spliced = remove_matching_includes(includes: spliced, modules: [name])
|
|
497
|
+
spliced
|
|
498
|
+
end
|
|
499
|
+
|
|
455
500
|
# Remove includes that match the given module names (case-insensitive)
|
|
456
501
|
# Returns a new array with matching includes removed
|
|
457
502
|
def remove_matching_includes(includes:, modules:)
|
data/lib/version.rb
CHANGED
data/site-local/sitemap.xml.gz
CHANGED
|
Binary file
|
|
@@ -1007,6 +1007,24 @@ describe CExtractor do
|
|
|
1007
1007
|
expect(plain.decorators).to eq([])
|
|
1008
1008
|
end
|
|
1009
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
|
+
|
|
1010
1028
|
end
|
|
1011
1029
|
|
|
1012
1030
|
end
|
|
@@ -388,6 +388,83 @@ describe CExtractorPreprocessing do
|
|
|
388
388
|
expect(scanner.pos).to eq 0
|
|
389
389
|
end
|
|
390
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
|
+
|
|
391
468
|
end
|
|
392
469
|
|
|
393
470
|
context "#filter_directive" do
|
|
@@ -412,18 +412,36 @@ describe "Includes sorting" do
|
|
|
412
412
|
MockInclude.new("mock_module.h"),
|
|
413
413
|
UserInclude.new("config.h")
|
|
414
414
|
]
|
|
415
|
-
|
|
415
|
+
|
|
416
416
|
Includes.sort!(includes)
|
|
417
|
-
|
|
417
|
+
|
|
418
418
|
expect(includes.length).to eq(3)
|
|
419
419
|
expect(includes[0]).to be_a(UserInclude)
|
|
420
420
|
expect(includes[1]).to be_a(UserInclude)
|
|
421
421
|
expect(includes[2]).to be_a(UserInclude)
|
|
422
422
|
end
|
|
423
423
|
|
|
424
|
+
# Ruby's sort/sort_by family does not guarantee a stable sort -- every UserInclude
|
|
425
|
+
# here ties on the same comparator value, so a comparator-sort-based implementation
|
|
426
|
+
# is free to reorder them unpredictably across Ruby versions/platforms even with no
|
|
427
|
+
# actual duplicates present. This is the shape that exposed that gap in practice.
|
|
428
|
+
it "preserves each group's own original relative order exactly, not just which group it ends up in" do
|
|
429
|
+
header1 = UserInclude.new("header1.h")
|
|
430
|
+
header2 = UserInclude.new("header2.h")
|
|
431
|
+
header3 = UserInclude.new("header3.h")
|
|
432
|
+
stdio = SystemInclude.new("stdio.h")
|
|
433
|
+
stdlib = SystemInclude.new("stdlib.h")
|
|
434
|
+
|
|
435
|
+
includes = [header1, stdio, header2, stdlib, header3]
|
|
436
|
+
|
|
437
|
+
result = Includes.sort!(includes)
|
|
438
|
+
|
|
439
|
+
expect(result).to eq([stdio, stdlib, header1, header2, header3])
|
|
440
|
+
end
|
|
441
|
+
|
|
424
442
|
it "handles empty array" do
|
|
425
443
|
includes = []
|
|
426
|
-
|
|
444
|
+
|
|
427
445
|
result = Includes.sort!(includes)
|
|
428
446
|
|
|
429
447
|
expect(result).to eq([])
|
|
@@ -547,6 +547,46 @@ describe Partializer do
|
|
|
547
547
|
|
|
548
548
|
expect(result).to match_array([UserInclude.new('header1.h')])
|
|
549
549
|
end
|
|
550
|
+
|
|
551
|
+
# The shared types header's own top-of-file guard-spoofing macro must
|
|
552
|
+
# be defined before anything later in the list gets a chance to transitively
|
|
553
|
+
# re-include this module's real header a second, unguarded way -- splicing the
|
|
554
|
+
# types header in at the module's own original position (rather than appending it
|
|
555
|
+
# after everything else) is what guarantees that ordering.
|
|
556
|
+
it "splices the shared types header in at the module's own original list position, not at the end" do
|
|
557
|
+
includes = [
|
|
558
|
+
UserInclude.new('header1.h'),
|
|
559
|
+
UserInclude.new('module.h'),
|
|
560
|
+
UserInclude.new('header2.h')
|
|
561
|
+
]
|
|
562
|
+
result = @partializer.remap_implementation_header_includes(
|
|
563
|
+
name: 'module',
|
|
564
|
+
includes: includes,
|
|
565
|
+
partials: {},
|
|
566
|
+
types_header: 'ceedling_partial_module_types.h'
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
expect(result).to eq([
|
|
570
|
+
UserInclude.new('header1.h'),
|
|
571
|
+
UserInclude.new('ceedling_partial_module_types.h'),
|
|
572
|
+
UserInclude.new('header2.h')
|
|
573
|
+
])
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
it "appends the shared types header when the module's own header wasn't in the includes list to begin with" do
|
|
577
|
+
includes = [UserInclude.new('header1.h')]
|
|
578
|
+
result = @partializer.remap_implementation_header_includes(
|
|
579
|
+
name: 'module',
|
|
580
|
+
includes: includes,
|
|
581
|
+
partials: {},
|
|
582
|
+
types_header: 'ceedling_partial_module_types.h'
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
expect(result).to eq([
|
|
586
|
+
UserInclude.new('header1.h'),
|
|
587
|
+
UserInclude.new('ceedling_partial_module_types.h')
|
|
588
|
+
])
|
|
589
|
+
end
|
|
550
590
|
end
|
|
551
591
|
|
|
552
592
|
###
|
|
@@ -733,6 +773,28 @@ describe Partializer do
|
|
|
733
773
|
|
|
734
774
|
expect(result).to match_array([UserInclude.new('header1.h')])
|
|
735
775
|
end
|
|
776
|
+
|
|
777
|
+
# See the matching example in #remap_implementation_header_includes for the full
|
|
778
|
+
# rationale; the mockable interface header needs the identical ordering guarantee.
|
|
779
|
+
it "splices the shared types header in at the module's own original list position, not at the end" do
|
|
780
|
+
includes = [
|
|
781
|
+
UserInclude.new('header1.h'),
|
|
782
|
+
UserInclude.new('module.h'),
|
|
783
|
+
UserInclude.new('header2.h')
|
|
784
|
+
]
|
|
785
|
+
result = @partializer.remap_interface_header_includes(
|
|
786
|
+
name: 'module',
|
|
787
|
+
includes: includes,
|
|
788
|
+
partials: {},
|
|
789
|
+
types_header: 'ceedling_partial_module_types.h'
|
|
790
|
+
)
|
|
791
|
+
|
|
792
|
+
expect(result).to eq([
|
|
793
|
+
UserInclude.new('header1.h'),
|
|
794
|
+
UserInclude.new('ceedling_partial_module_types.h'),
|
|
795
|
+
UserInclude.new('header2.h')
|
|
796
|
+
])
|
|
797
|
+
end
|
|
736
798
|
end
|
|
737
799
|
|
|
738
800
|
###
|
|
@@ -200,8 +200,10 @@ class UnityTestRunnerGenerator
|
|
|
200
200
|
source_lines = source.split("\n")
|
|
201
201
|
source_index = 0
|
|
202
202
|
tests_and_line_numbers.size.times do |i|
|
|
203
|
+
# Compile once per test
|
|
204
|
+
name_regex = /(?:^|\s)#{tests_and_line_numbers[i][:test]}(?:\s|\()/
|
|
203
205
|
source_lines[source_index..].each_with_index do |line, index|
|
|
204
|
-
next unless line =~
|
|
206
|
+
next unless line =~ name_regex
|
|
205
207
|
|
|
206
208
|
source_index += index
|
|
207
209
|
tests_and_line_numbers[i][:line_number] = source_index + 1
|
|
@@ -124,7 +124,7 @@ if $0 == __FILE__
|
|
|
124
124
|
begin
|
|
125
125
|
# look in the specified or current directory for result files
|
|
126
126
|
args[0] ||= './'
|
|
127
|
-
targets = "#{
|
|
127
|
+
targets = "#{args[0].tr('\\', '/')}**/*.test*"
|
|
128
128
|
results = Dir[targets]
|
|
129
129
|
|
|
130
130
|
raise "No *.testpass, *.testfail, or *.testresults files found in '#{targets}'" if results.empty?
|
|
@@ -133,7 +133,7 @@ if $0 == __FILE__
|
|
|
133
133
|
|
|
134
134
|
# set the root path
|
|
135
135
|
args[1] ||= "#{Dir.pwd}/"
|
|
136
|
-
uts.root =
|
|
136
|
+
uts.root = args[1]
|
|
137
137
|
|
|
138
138
|
# run the summarizer
|
|
139
139
|
puts uts.run
|
|
@@ -13,6 +13,13 @@ Prior to 2008, the project was an internal project and not released to the publi
|
|
|
13
13
|
|
|
14
14
|
## Log
|
|
15
15
|
|
|
16
|
+
### Unity 2.7.2
|
|
17
|
+
|
|
18
|
+
Significant Bugfixes:
|
|
19
|
+
|
|
20
|
+
- Default `UNITY_INCLUDE_EXEC_TIME` macros compile as ISO C99, are statement-safe, and accept `-Wsign-conversion` (#838)
|
|
21
|
+
- `unity_test_summary.rb` honors its own default result directory and root path again. @youdie006
|
|
22
|
+
|
|
16
23
|
### Unity 2.7.0 (July 2026)
|
|
17
24
|
|
|
18
25
|
New Features:
|
|
@@ -488,6 +488,10 @@ Define this to measure and report execution time for each test in the suite. Whe
|
|
|
488
488
|
it's best to automatically find a way to determine the time in milliseconds. On most Windows, macos, or
|
|
489
489
|
Linux environments, this is automatic. If not, you can give Unity more information.
|
|
490
490
|
|
|
491
|
+
On Unix and macOS, the default uses POSIX `clock_gettime(CLOCK_MONOTONIC)` when that clock is available.
|
|
492
|
+
If it is not (strict ISO C with no POSIX clocks in `<time.h>`), it falls back to the standard `clock()` function,
|
|
493
|
+
which is also the Windows default.
|
|
494
|
+
|
|
491
495
|
#### `UNITY_CLOCK_MS`
|
|
492
496
|
|
|
493
497
|
If you're working on a system (embedded or otherwise) which has an accessible millisecond timer. You can
|
|
@@ -395,33 +395,34 @@ typedef UNITY_FLOAT_TYPE UNITY_FLOAT;
|
|
|
395
395
|
!defined(UNITY_EXEC_TIME_STOP) && \
|
|
396
396
|
!defined(UNITY_PRINT_EXEC_TIME) && \
|
|
397
397
|
!defined(UNITY_TIME_TYPE)
|
|
398
|
-
/* If none
|
|
398
|
+
/* If none of these macros are defined then try to provide a default implementation */
|
|
399
399
|
|
|
400
400
|
#if defined(UNITY_CLOCK_MS)
|
|
401
401
|
/* This is a simple way to get a default implementation on platforms that support getting a millisecond counter */
|
|
402
402
|
#define UNITY_TIME_TYPE UNITY_UINT
|
|
403
403
|
#define UNITY_EXEC_TIME_START() Unity.CurrentTestStartTime = UNITY_CLOCK_MS()
|
|
404
404
|
#define UNITY_EXEC_TIME_STOP() Unity.CurrentTestStopTime = UNITY_CLOCK_MS()
|
|
405
|
-
#define UNITY_PRINT_EXEC_TIME() { \
|
|
405
|
+
#define UNITY_PRINT_EXEC_TIME() do { \
|
|
406
406
|
UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \
|
|
407
407
|
UnityPrint(" ("); \
|
|
408
408
|
UnityPrintNumberUnsigned(execTimeMs); \
|
|
409
409
|
UnityPrint(" ms)"); \
|
|
410
|
-
}
|
|
410
|
+
} while (0)
|
|
411
411
|
#elif defined(_WIN32)
|
|
412
412
|
#include <time.h>
|
|
413
413
|
#define UNITY_TIME_TYPE clock_t
|
|
414
414
|
#define UNITY_GET_TIME(t) t = (clock_t)((clock() * 1000) / CLOCKS_PER_SEC)
|
|
415
415
|
#define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime)
|
|
416
416
|
#define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime)
|
|
417
|
-
#define UNITY_PRINT_EXEC_TIME() { \
|
|
418
|
-
UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \
|
|
417
|
+
#define UNITY_PRINT_EXEC_TIME() do { \
|
|
418
|
+
UNITY_UINT execTimeMs = (UNITY_UINT)(Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \
|
|
419
419
|
UnityPrint(" ("); \
|
|
420
420
|
UnityPrintNumberUnsigned(execTimeMs); \
|
|
421
421
|
UnityPrint(" ms)"); \
|
|
422
|
-
}
|
|
422
|
+
} while (0)
|
|
423
423
|
#elif defined(__unix__) || defined(__APPLE__)
|
|
424
424
|
#include <time.h>
|
|
425
|
+
#if defined(CLOCK_MONOTONIC)
|
|
425
426
|
#define UNITY_TIME_TYPE struct timespec
|
|
426
427
|
#define UNITY_GET_TIME(t) clock_gettime(CLOCK_MONOTONIC, &t)
|
|
427
428
|
#define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime)
|
|
@@ -432,7 +433,20 @@ typedef UNITY_FLOAT_TYPE UNITY_FLOAT;
|
|
|
432
433
|
UnityPrint(" ("); \
|
|
433
434
|
UnityPrintNumberUnsigned(execTimeMs); \
|
|
434
435
|
UnityPrint(" ms)"); \
|
|
435
|
-
} while(0)
|
|
436
|
+
} while (0)
|
|
437
|
+
#else
|
|
438
|
+
/* CLOCK_MONOTONIC is POSIX, not ISO C. Fall back so -std=c99 still compiles. */
|
|
439
|
+
#define UNITY_TIME_TYPE clock_t
|
|
440
|
+
#define UNITY_GET_TIME(t) t = (clock_t)((clock() * 1000) / CLOCKS_PER_SEC)
|
|
441
|
+
#define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime)
|
|
442
|
+
#define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime)
|
|
443
|
+
#define UNITY_PRINT_EXEC_TIME() do { \
|
|
444
|
+
UNITY_UINT execTimeMs = (UNITY_UINT)(Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \
|
|
445
|
+
UnityPrint(" ("); \
|
|
446
|
+
UnityPrintNumberUnsigned(execTimeMs); \
|
|
447
|
+
UnityPrint(" ms)"); \
|
|
448
|
+
} while (0)
|
|
449
|
+
#endif
|
|
436
450
|
#endif
|
|
437
451
|
#endif
|
|
438
452
|
#endif
|
data/vendor/unity/test/Makefile
CHANGED
|
@@ -43,7 +43,7 @@ TARGET = build/testunity-cov.exe
|
|
|
43
43
|
# To generate coverage, call 'make -s', the default target runs.
|
|
44
44
|
# For verbose output of all the tests, run 'make test'.
|
|
45
45
|
default: test
|
|
46
|
-
.PHONY: default coverage test clean
|
|
46
|
+
.PHONY: default coverage test clean intDetection execTimeDetection
|
|
47
47
|
coverage: $(SRC1) $(SRC2) $(SRC3) $(SRC4) $(SRC5) $(SRC6) $(SRC7) $(SRC8)
|
|
48
48
|
cd $(BUILD_DIR) && \
|
|
49
49
|
$(CC) $(CFLAGS) $(DEFINES) $(foreach i,$(SRC1), ../$i) $(COV_FLAGS) -o ../$(TARGET)
|
|
@@ -122,10 +122,17 @@ test: $(SRC1) $(SRC2) $(SRC3) $(SRC4) $(SRC5) $(SRC6) $(SRC7) $(SRC8)
|
|
|
122
122
|
|
|
123
123
|
# Compile only, for testing that preprocessor detection works
|
|
124
124
|
UNITY_C_ONLY =-c ../src/unity.c -o $(BUILD_DIR)/unity.o
|
|
125
|
-
intDetection:
|
|
125
|
+
intDetection: | $(BUILD_DIR)
|
|
126
126
|
$(CC) $(CFLAGS) $(INC_DIR) $(UNITY_C_ONLY) -D UNITY_EXCLUDE_STDINT_H
|
|
127
127
|
$(CC) $(CFLAGS) $(INC_DIR) $(UNITY_C_ONLY) -D UNITY_EXCLUDE_LIMITS_H
|
|
128
128
|
|
|
129
|
+
# UNITY_INCLUDE_EXEC_TIME is optional and was historically untested, so the
|
|
130
|
+
# default timer macros could rot. Compile them with Unity's own warning flags
|
|
131
|
+
# and with -Wsign-conversion (the failure reported in #838).
|
|
132
|
+
execTimeDetection: | $(BUILD_DIR)
|
|
133
|
+
$(CC) $(CFLAGS) $(INC_DIR) $(UNITY_C_ONLY) -D UNITY_INCLUDE_EXEC_TIME
|
|
134
|
+
$(CC) $(CFLAGS) -Wsign-conversion $(INC_DIR) $(UNITY_C_ONLY) -D UNITY_INCLUDE_EXEC_TIME
|
|
135
|
+
|
|
129
136
|
$(BUILD_DIR)/test_unity_arraysRunner.c: tests/test_unity_arrays.c | $(BUILD_DIR)
|
|
130
137
|
awk $(AWK_SCRIPT) tests/test_unity_arrays.c > $@
|
|
131
138
|
|
|
@@ -407,6 +407,7 @@ module RakefileHelpers
|
|
|
407
407
|
combined_output = ''
|
|
408
408
|
[ "make -s", # test with all defaults
|
|
409
409
|
"make -s coverage", # test with coverage
|
|
410
|
+
"make -s execTimeDetection", # compile default UNITY_INCLUDE_EXEC_TIME macros
|
|
410
411
|
"cd #{File.join("..","extras","fixture",'test')} && make -s default noStdlibMalloc",
|
|
411
412
|
"cd #{File.join("..","extras","fixture",'test')} && make -s C89",
|
|
412
413
|
"cd #{File.join("..","extras","memory",'test')} && make -s default noStdlibMalloc",
|
|
@@ -1334,6 +1334,55 @@ should 'GenerateSuiteTeardownWhenBeginAndEndAreOmitted' do
|
|
|
1334
1334
|
$generate_test_runner_tests += 1
|
|
1335
1335
|
end
|
|
1336
1336
|
|
|
1337
|
+
should 'FindTestsLineNumbersWhenNameStartsTheLine' do
|
|
1338
|
+
# The return type may sit on its own line, leaving the test name at column 0.
|
|
1339
|
+
# The line search must still find the definition (and must not scan to the end
|
|
1340
|
+
# of the file for every test, which is what made large files slow).
|
|
1341
|
+
source = "#include \"unity.h\"\n" \
|
|
1342
|
+
"\n" \
|
|
1343
|
+
"void\n" \
|
|
1344
|
+
"test_FirstAtColumnZero(void)\n" \
|
|
1345
|
+
"{\n" \
|
|
1346
|
+
"}\n" \
|
|
1347
|
+
"\n" \
|
|
1348
|
+
"void\n" \
|
|
1349
|
+
"test_SecondAtColumnZero(void)\n" \
|
|
1350
|
+
"{\n" \
|
|
1351
|
+
"}\n"
|
|
1352
|
+
found = UnityTestRunnerGenerator.new({}).find_tests(source).map { |t| [t[:test], t[:line_number]] }
|
|
1353
|
+
expected = [['test_FirstAtColumnZero', 4], ['test_SecondAtColumnZero', 9]]
|
|
1354
|
+
if found == expected
|
|
1355
|
+
report 'Runner_FindTestsLineNumbersWhenNameStartsTheLine:PASS'
|
|
1356
|
+
else
|
|
1357
|
+
report " FAIL: expected #{expected.inspect}, got #{found.inspect}"
|
|
1358
|
+
report 'Runner_FindTestsLineNumbersWhenNameStartsTheLine:FAIL'
|
|
1359
|
+
$generate_test_runner_failures += 1
|
|
1360
|
+
end
|
|
1361
|
+
$generate_test_runner_tests += 1
|
|
1362
|
+
end
|
|
1363
|
+
|
|
1364
|
+
should 'FindTestsLineNumbersWhenOneNameIsAPrefixOfAnother' do
|
|
1365
|
+
# Issue #288: test_my_function must not be located inside
|
|
1366
|
+
# test_my_function_invalid_behavior, which is defined first.
|
|
1367
|
+
source = "void test_my_function_invalid_behavior(void)\n" \
|
|
1368
|
+
"{\n" \
|
|
1369
|
+
"}\n" \
|
|
1370
|
+
"\n" \
|
|
1371
|
+
"void test_my_function(void)\n" \
|
|
1372
|
+
"{\n" \
|
|
1373
|
+
"}\n"
|
|
1374
|
+
found = UnityTestRunnerGenerator.new({}).find_tests(source).map { |t| [t[:test], t[:line_number]] }
|
|
1375
|
+
expected = [['test_my_function_invalid_behavior', 1], ['test_my_function', 5]]
|
|
1376
|
+
if found == expected
|
|
1377
|
+
report 'Runner_FindTestsLineNumbersWhenOneNameIsAPrefixOfAnother:PASS'
|
|
1378
|
+
else
|
|
1379
|
+
report " FAIL: expected #{expected.inspect}, got #{found.inspect}"
|
|
1380
|
+
report 'Runner_FindTestsLineNumbersWhenOneNameIsAPrefixOfAnother:FAIL'
|
|
1381
|
+
$generate_test_runner_failures += 1
|
|
1382
|
+
end
|
|
1383
|
+
$generate_test_runner_tests += 1
|
|
1384
|
+
end
|
|
1385
|
+
|
|
1337
1386
|
RUNNER_TESTS.each do |testset|
|
|
1338
1387
|
basename = File.basename(testset[:testfile], C_EXTENSION)
|
|
1339
1388
|
testset_name = "Runner_#{basename}_#{testset[:name]}"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# =========================================================================
|
|
2
|
+
# Unity - A Test Framework for C
|
|
3
|
+
# ThrowTheSwitch.org
|
|
4
|
+
# Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
|
|
5
|
+
# SPDX-License-Identifier: MIT
|
|
6
|
+
# =========================================================================
|
|
7
|
+
|
|
8
|
+
require 'fileutils'
|
|
9
|
+
require_relative '../../auto/colour_reporter'
|
|
10
|
+
|
|
11
|
+
# the 'test:scripts' rake task tallies these, so only initialize them if we are loaded first
|
|
12
|
+
$generate_test_runner_tests ||= 0
|
|
13
|
+
$generate_test_runner_failures ||= 0
|
|
14
|
+
$unity_test_summary_failures = 0
|
|
15
|
+
|
|
16
|
+
SUMMARY_SCRIPT = File.expand_path('../../auto/unity_test_summary.rb', __dir__)
|
|
17
|
+
SUMMARY_SANDBOX = File.expand_path('../sandbox/test_summary', __dir__)
|
|
18
|
+
SUMMARY_RESULTS = <<~RESULTS
|
|
19
|
+
test/test_a.c:12:test_thing:PASS
|
|
20
|
+
test/test_b.c:34:test_broken:FAIL: Expected 1 Was 2
|
|
21
|
+
test/test_c.c:56:test_skipped:IGNORE: not ready
|
|
22
|
+
|
|
23
|
+
-----------------------
|
|
24
|
+
3 Tests 1 Failures 1 Ignored
|
|
25
|
+
RESULTS
|
|
26
|
+
|
|
27
|
+
def summary_test(name, passed)
|
|
28
|
+
if passed
|
|
29
|
+
report "#{name}:PASS"
|
|
30
|
+
else
|
|
31
|
+
report "#{name}:FAIL"
|
|
32
|
+
$generate_test_runner_failures += 1
|
|
33
|
+
$unity_test_summary_failures += 1
|
|
34
|
+
end
|
|
35
|
+
$generate_test_runner_tests += 1
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
FileUtils.rm_rf(SUMMARY_SANDBOX)
|
|
39
|
+
FileUtils.mkdir_p(SUMMARY_SANDBOX)
|
|
40
|
+
File.write(File.join(SUMMARY_SANDBOX, 'test_sample.testfail'), SUMMARY_RESULTS)
|
|
41
|
+
|
|
42
|
+
begin
|
|
43
|
+
Dir.chdir(SUMMARY_SANDBOX) do
|
|
44
|
+
# with no arguments at all, the result files are looked for in the current directory
|
|
45
|
+
output = `ruby "#{SUMMARY_SCRIPT}" 2>&1`
|
|
46
|
+
summary_test('UnityTestSummary_DefaultsResultDirectoryToCurrentDirectory',
|
|
47
|
+
$?.success? && output.include?('3 TOTAL TESTS 1 TOTAL FAILURES 1 IGNORED'))
|
|
48
|
+
|
|
49
|
+
# with only a result directory given, the root path defaults to the current directory
|
|
50
|
+
expected = "#{Dir.pwd}/test/test_b.c:34".tr('/', '\\')
|
|
51
|
+
output = `ruby "#{SUMMARY_SCRIPT}" ./ 2>&1`
|
|
52
|
+
summary_test('UnityTestSummary_DefaultsRootPathToCurrentDirectory', output.include?(expected))
|
|
53
|
+
end
|
|
54
|
+
ensure
|
|
55
|
+
FileUtils.rm_rf(SUMMARY_SANDBOX)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
raise "There were #{$unity_test_summary_failures} failures while testing unity_test_summary.rb" if $unity_test_summary_failures > 0
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ceedling
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mark VanderVoord
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2026-09-
|
|
13
|
+
date: 2026-09-20 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: rake
|
|
@@ -1739,6 +1739,7 @@ files:
|
|
|
1739
1739
|
- vendor/unity/test/tests/test_unity_parameterized.c
|
|
1740
1740
|
- vendor/unity/test/tests/test_unity_parameterizedDemo.c
|
|
1741
1741
|
- vendor/unity/test/tests/test_unity_strings.c
|
|
1742
|
+
- vendor/unity/test/tests/test_unity_test_summary.rb
|
|
1742
1743
|
- vendor/unity/test/tests/types_for_test.h
|
|
1743
1744
|
- vendor/unity/unityConfig.cmake
|
|
1744
1745
|
homepage: https://throwtheswitch.org/ceedling
|