ceedling 1.1.4 → 1.1.6
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/assets/tests_with_conditional_includes/src/feature_config.h +13 -0
- data/assets/tests_with_conditional_includes/src/feature_extra.h +13 -0
- data/assets/tests_with_conditional_includes/src/local_flag_extra.h +13 -0
- data/assets/tests_with_conditional_includes/src/nested_extra.h +13 -0
- data/assets/tests_with_conditional_includes/src/nested_wrapper.h +16 -0
- data/assets/tests_with_conditional_includes/src/project_flag_extra.h +13 -0
- data/assets/tests_with_conditional_includes/src/widget.c +48 -0
- data/assets/tests_with_conditional_includes/src/widget.h +15 -0
- data/assets/tests_with_conditional_includes/src/widget_feature.c +29 -0
- data/assets/tests_with_conditional_includes/src/widget_feature.h +13 -0
- data/assets/tests_with_conditional_includes/test/test_widget.c +44 -0
- data/assets/tests_with_conditional_includes/test/test_widget_feature.c +31 -0
- data/docs/Changelog.md +31 -1
- data/docs/mkdocs/configuration/reference/partials.md +7 -7
- data/examples/wondrous_forest/src/SensorHal.h +13 -0
- data/examples/wondrous_forest/src/TemperatureSensor.c +12 -5
- data/examples/wondrous_forest/src/TemperatureSensor.h +5 -4
- data/examples/wondrous_forest/test/TestTemperatureSensor.c +32 -3
- data/lib/ceedling/generators/generator_partials.rb +20 -1
- data/lib/ceedling/generators/generator_test_runner.rb +25 -1
- data/lib/ceedling/partials/partializer.rb +8 -1
- data/lib/ceedling/preprocess/preprocessinator.rb +22 -0
- data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +33 -0
- data/lib/ceedling/test_invoker/test_build_executor.rb +3 -1
- data/lib/version.rb +1 -1
- data/site-local/configuration/reference/partials.html +11 -10
- data/site-local/sitemap.xml.gz +0 -0
- data/spec/support/system/spec_system_helper.rb +11 -0
- data/spec/support/system/system_context.rb +18 -9
- data/spec/system/conditional_include_macro_visibility_spec.rb +152 -0
- data/spec/system/encoding_stress_spec.rb +4 -4
- data/spec/system/example_wondrous_forest_spec.rb +2 -2
- data/spec/system/support/common_test_cases.rb +5 -1
- data/spec/system/test_runner_system_include_spec.rb +90 -0
- data/spec/units/generators/generator_partials_spec.rb +111 -8
- data/spec/units/generators/generator_test_runner_spec.rb +69 -0
- data/spec/units/partials/partializer_spec.rb +69 -0
- data/spec/units/preprocess/preprocessinator_includes_handler_spec.rb +61 -0
- data/spec/units/preprocess/preprocessinator_spec.rb +136 -0
- data/vendor/c_exception/lib/CException.h +1 -1
- data/vendor/c_exception/project.yml +2 -2
- data/vendor/c_exception/test/TestException.c +1 -1
- data/vendor/c_exception/test/support/CExceptionConfig.h +5 -11
- data/vendor/cmock/docs/CMockChangeLog.md +23 -22
- data/vendor/cmock/scripts/create_makefile.rb +4 -3
- data/vendor/cmock/src/cmock.h +1 -1
- data/vendor/cmock/vendor/c_exception/lib/CException.h +1 -1
- data/vendor/cmock/vendor/c_exception/project.yml +2 -2
- data/vendor/cmock/vendor/c_exception/test/TestException.c +1 -1
- data/vendor/cmock/vendor/c_exception/test/support/CExceptionConfig.h +5 -11
- data/vendor/cmock/vendor/unity/auto/generate_test_runner.rb +85 -29
- data/vendor/cmock/vendor/unity/docs/UnityChangeLog.md +1 -0
- data/vendor/cmock/vendor/unity/examples/unity_config.h +8 -0
- data/vendor/cmock/vendor/unity/src/unity.c +9 -1
- data/vendor/cmock/vendor/unity/src/unity.h +1 -1
- data/vendor/cmock/vendor/unity/test/rakefile +2 -1
- data/vendor/cmock/vendor/unity/test/testdata/testRunnerGeneratorEmpty.c +13 -0
- data/vendor/cmock/vendor/unity/test/tests/test_generate_test_runner.rb +31 -0
- data/vendor/unity/auto/generate_test_runner.rb +63 -57
- data/vendor/unity/docs/UnityChangeLog.md +1 -0
- data/vendor/unity/examples/unity_config.h +8 -0
- data/vendor/unity/src/unity.c +9 -1
- data/vendor/unity/src/unity.h +1 -1
- data/vendor/unity/test/rakefile +2 -1
- data/vendor/unity/test/tests/test_generate_test_runner.rb +20 -0
- metadata +21 -3
- data/vendor/cmock/vendor/unity/auto/run_test.erb +0 -37
|
@@ -115,6 +115,39 @@ class PreprocessinatorIncludesHandler
|
|
|
115
115
|
return includes
|
|
116
116
|
end
|
|
117
117
|
|
|
118
|
+
# Scan the original file's own text for every #include line, unconditionally --
|
|
119
|
+
# no #if/#ifdef tracking at all, just literal presence. Supplements the gcc-based
|
|
120
|
+
# bare pass (`extract_bare_includes`), which runs against an isolated, sibling-free
|
|
121
|
+
# copy of the file and so can never open another header to resolve a conditional
|
|
122
|
+
# that depends on a macro that header defines (e.g. `#if SOME_MACRO_FROM_ANOTHER_HEADER`)
|
|
123
|
+
# -- GCC treats the macro as undefined there and silently drops the #include line
|
|
124
|
+
# from that pass's output even though it's a perfectly ordinary, real, top-level
|
|
125
|
+
# #include once actually evaluated with the real file in place. This method's result
|
|
126
|
+
# is meant to be unioned with the gcc-based bare pass's own result (see
|
|
127
|
+
# Preprocessinator#preprocess_file_includes_common), not used on its own -- an
|
|
128
|
+
# `Includes.reconcile` call downstream still only keeps an entry here if the
|
|
129
|
+
# accurate directives-only pass also reports it, so unconditionally capturing every
|
|
130
|
+
# literal #include line (regardless of whether its own guard is really true) is
|
|
131
|
+
# safe: it can only ever let back in an entry the accurate pass already confirmed,
|
|
132
|
+
# never introduce a spurious one on its own. A supplement, not a replacement --
|
|
133
|
+
# the gcc pass can still do something this literal scan structurally can't: resolve
|
|
134
|
+
# an #include whose own target is a macro rather than a literal filename.
|
|
135
|
+
def extract_bare_includes_from_text(filepath:)
|
|
136
|
+
includes = []
|
|
137
|
+
|
|
138
|
+
# Open in binary mode: code_lines applies clean_encoding per-line, but each_line
|
|
139
|
+
# itself can raise on invalid byte sequences before clean_encoding is reached.
|
|
140
|
+
@file_wrapper.open(filepath, 'rb') do |input|
|
|
141
|
+
@parsing_parcels.code_lines( input ) do |line|
|
|
142
|
+
_include = @include_factory.user_include_from_directive( line ) ||
|
|
143
|
+
@include_factory.system_include_from_directive( line )
|
|
144
|
+
includes << Include.new( _include.filepath ) if !_include.nil?
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
return clean_self_reference( filepath, includes )
|
|
149
|
+
end
|
|
150
|
+
|
|
118
151
|
def extract_user_includes_preprocess(name:, filepath:, preprocessed_filepath:)
|
|
119
152
|
includes = []
|
|
120
153
|
|
|
@@ -187,7 +187,9 @@ class TestBuildExecutor
|
|
|
187
187
|
# their own includes lists), so a module tested and mocked in the same test file gets
|
|
188
188
|
# exactly one C definition of each of its typedefs and aggregate types.
|
|
189
189
|
types_header = @generator.generate_partial_types(
|
|
190
|
-
name: name,
|
|
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
|
|
191
193
|
c_module: module_contents,
|
|
192
194
|
output_path: testable.paths[:partials]
|
|
193
195
|
)
|
data/lib/version.rb
CHANGED
|
@@ -4162,22 +4162,23 @@
|
|
|
4162
4162
|
<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="w"> </span><span class="nt">:max_extraction_length</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">8000</span>
|
|
4163
4163
|
</code></pre></div>
|
|
4164
4164
|
<h2 id="max_extraction_length"><code>:max_extraction_length</code></h2>
|
|
4165
|
-
<p>Notes
|
|
4166
|
-
|
|
4167
|
-
|
|
4165
|
+
<p>Notes:</p>
|
|
4166
|
+
<ul>
|
|
4167
|
+
<li>The default should be sufficient for nearly all test builds. Modification should only be necessary on error.</li>
|
|
4168
|
+
<li>This configuration option is only available in Ceedling 1.1.4+.</li>
|
|
4169
|
+
</ul>
|
|
4168
4170
|
<p>Building a Partial requires Ceedling to extract each C construct — a
|
|
4169
4171
|
variable declaration, a function, a macro, and so on — from your source
|
|
4170
4172
|
files one at a time. Extraction reads a construct into a growing buffer
|
|
4171
|
-
until it finds that construct
|
|
4173
|
+
until it finds that construct’s natural end (a terminating <code>;</code>, a closing
|
|
4172
4174
|
<code>}</code>, etc.). This setting bounds how large that buffer may grow before
|
|
4173
|
-
extraction fails outright
|
|
4174
|
-
|
|
4175
|
-
<
|
|
4176
|
-
<code>8000</code> means 8,000,000 characters. It must be a whole number no smaller
|
|
4175
|
+
extraction fails outright.</p>
|
|
4176
|
+
<p>The value is a multiplier of 1000 characters, not a raw character count. For example,
|
|
4177
|
+
<code>8000</code> means 8,000,000 characters (approx. 7.6MB). It must be a whole number no smaller
|
|
4177
4178
|
than <code>10</code> (10,000 characters).</p>
|
|
4178
4179
|
<p>Raise this value if Ceedling reports an extraction failure for a
|
|
4179
|
-
legitimately large single construct in your codebase
|
|
4180
|
-
lookup table,
|
|
4180
|
+
legitimately large single construct in your codebase (e.g. a sizable
|
|
4181
|
+
lookup table). Ordinarily, there’s no reason to lower it.</p>
|
|
4181
4182
|
<p><strong>Default</strong>: 5000 (5,000,000 characters)</p>
|
|
4182
4183
|
<p><br/><br/></p>
|
|
4183
4184
|
|
data/site-local/sitemap.xml.gz
CHANGED
|
Binary file
|
|
@@ -150,6 +150,17 @@ def test_asset_path(asset_file_name)
|
|
|
150
150
|
File.join(File.dirname(__FILE__), '..', '..', '..', 'assets', asset_file_name)
|
|
151
151
|
end
|
|
152
152
|
|
|
153
|
+
# `ceedling new`/`upgrade` only populates a project's docs/ceedling/ by copying this
|
|
154
|
+
# repo's own site-local/ -- the local mkdocs HTML bundle, built only by `rake
|
|
155
|
+
# docs:build:local` (see bin/cli_helper.rb's own identical check). That's a step CI
|
|
156
|
+
# runs in a dedicated job before the test suite, but a plain local `bundle install &&
|
|
157
|
+
# rspec` never runs at all, so docs/ceedling/ is legitimately absent there. Tests
|
|
158
|
+
# asserting a deployed project's doc contents check this first rather than hardcoding
|
|
159
|
+
# an assumption that only ever holds in CI's own environment (issue #1216).
|
|
160
|
+
def html_docs_bundle_available?
|
|
161
|
+
Dir.exist?(File.join(File.dirname(__FILE__), '..', '..', '..', 'site-local'))
|
|
162
|
+
end
|
|
163
|
+
|
|
153
164
|
def unique_proj_name(prefix)
|
|
154
165
|
safe = RSpec.current_example.description
|
|
155
166
|
.downcase
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
# =========================================================================
|
|
7
7
|
|
|
8
8
|
require 'fileutils'
|
|
9
|
+
require 'bundler'
|
|
9
10
|
require 'ceedling/encodinator'
|
|
10
11
|
require_relative 'gem_dir_layout'
|
|
11
12
|
|
|
@@ -44,16 +45,26 @@ class SystemContext
|
|
|
44
45
|
)
|
|
45
46
|
|
|
46
47
|
Dir.chdir(shared_dir) do
|
|
47
|
-
saved = ENV.to_hash
|
|
48
48
|
begin
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
|
|
49
|
+
# Bundler.with_unbundled_env restores the pre-`bundle exec` environment for this
|
|
50
|
+
# block -- clearing not just BUNDLE_GEMFILE/BUNDLE_BIN_PATH/RUBYOPT (the previous,
|
|
51
|
+
# incomplete hand-rolled list here) but also GEM_HOME/GEM_PATH/RUBYLIB/PATH and every
|
|
52
|
+
# other var Bundler sets when this spec suite itself runs under `bundle exec` against
|
|
53
|
+
# a non-default GEM_HOME (e.g. `bundle install --path vendor/bundle`). Any one of
|
|
54
|
+
# those left in place is enough on its own for the `bundle exec ruby -S ceedling
|
|
55
|
+
# version` verification below to load the *outer* project's bundler/gems instead of
|
|
56
|
+
# this shared gem's own `--local path` install, and fail to find this Gemfile's exact
|
|
57
|
+
# pinned versions there.
|
|
58
|
+
deploy_output = Bundler.with_unbundled_env do
|
|
59
|
+
output = `bundle config set --local path '#{shared_gem.install_dir}' 2>&1`
|
|
60
|
+
# --prefer-local: Without it, Bundler resolves gems (e.g. `erb`) fresh from
|
|
61
|
+
# rubygems repository even when Ruby's default-gem copy satisfies the Gemfile constraint.
|
|
62
|
+
output += `bundle install --prefer-local 2>&1`
|
|
63
|
+
output
|
|
64
|
+
end
|
|
54
65
|
raise VerificationFailed, "bundle install failed:\n#{deploy_output}" unless $?.success?
|
|
55
66
|
|
|
56
|
-
verify = `bundle exec ruby -S ceedling version 2>&1`
|
|
67
|
+
verify = Bundler.with_unbundled_env { `bundle exec ruby -S ceedling version 2>&1` }
|
|
57
68
|
unless $?.success?
|
|
58
69
|
raise VerificationFailed,
|
|
59
70
|
"Ceedling does not appear to be installed or ready for use.\n" \
|
|
@@ -62,8 +73,6 @@ class SystemContext
|
|
|
62
73
|
rescue
|
|
63
74
|
FileUtils.rm_rf(shared_dir)
|
|
64
75
|
raise
|
|
65
|
-
ensure
|
|
66
|
-
ENV.replace(saved)
|
|
67
76
|
end
|
|
68
77
|
end
|
|
69
78
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# =========================================================================
|
|
2
|
+
# Ceedling - Test-Centered Build System for C
|
|
3
|
+
# ThrowTheSwitch.org
|
|
4
|
+
# Copyright (c) 2010-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
|
|
5
|
+
# SPDX-License-Identifier: MIT
|
|
6
|
+
# =========================================================================
|
|
7
|
+
|
|
8
|
+
require 'spec_system_helper'
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
## Conditional-#include Macro-Visibility Tests (directives-only preprocessing)
|
|
12
|
+
## =============================================================================
|
|
13
|
+
##
|
|
14
|
+
## Ceedling discovers a file's #includes via two preprocessor passes reconciled
|
|
15
|
+
## into one list: a "bare" pass and an accurate pass (gcc -E -fdirectives-only
|
|
16
|
+
## against the real file with real search paths -- genuinely opens every
|
|
17
|
+
## header). Reconciliation keeps an accurate-pass entry only if bare also found
|
|
18
|
+
## it, to filter out headers reached only via a deeper, nested path.
|
|
19
|
+
##
|
|
20
|
+
## "Bare" is itself two things unioned together: a gcc -M -MG -MP pass against
|
|
21
|
+
## an isolated, sibling-free copy of the file (so it never actually opens any
|
|
22
|
+
## header the file #includes, but can still resolve an #include whose own
|
|
23
|
+
## target is a macro, since command-line -D defines are visible even in
|
|
24
|
+
## isolation), and a plain literal text scan of the file's own #include lines
|
|
25
|
+
## (no conditional evaluation at all, so it sees past a guard the isolated gcc
|
|
26
|
+
## pass can't evaluate -- but also can't resolve a macro-computed #include
|
|
27
|
+
## target, since there's no literal filename in the source text to find).
|
|
28
|
+
## Neither replaces the other; each catches what the other structurally can't.
|
|
29
|
+
##
|
|
30
|
+
## These tests characterize that reconciliation's behavior for #include
|
|
31
|
+
## directives guarded by an #if/#ifdef, across the different places the
|
|
32
|
+
## guarding macro can come from -- some already handled correctly, one
|
|
33
|
+
## (issue #1223) fixed by adding the text-scan half of "bare" above.
|
|
34
|
+
##
|
|
35
|
+
## Test assets: assets/tests_with_conditional_includes/
|
|
36
|
+
## - widget.c/.h: three scenarios that already work correctly:
|
|
37
|
+
## (a) conditional include gated on a project :defines macro
|
|
38
|
+
## (b) conditional include gated on a same-file #define
|
|
39
|
+
## (c) a header reached only transitively (nested_wrapper.h's own
|
|
40
|
+
## #include, never itself directly #include'd by widget.c) --
|
|
41
|
+
## must not appear duplicated in as a false top-level entry
|
|
42
|
+
## - widget_feature.c/.h + feature_config.h + feature_extra.h: issue #1223
|
|
43
|
+
## itself -- a conditional include gated on a macro defined by an
|
|
44
|
+
## *earlier #include in the same file* (feature_config.h's FEATURE_LEVEL),
|
|
45
|
+
## which the isolated bare pass can never see, silently dropping
|
|
46
|
+
## feature_extra.h and leaving FEATURE_EXTRA_MACRO undeclared.
|
|
47
|
+
##
|
|
48
|
+
|
|
49
|
+
ceedling_system_tests do
|
|
50
|
+
|
|
51
|
+
before :all do
|
|
52
|
+
@c = SystemContext.new
|
|
53
|
+
@c.deploy_gem
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
after :all do
|
|
57
|
+
@c.done!
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
before { @proj_name = unique_proj_name("cond_inc") }
|
|
61
|
+
|
|
62
|
+
describe "Deployed as a gem" do
|
|
63
|
+
before do
|
|
64
|
+
@c.with_context do
|
|
65
|
+
@c.ceedling_appcmd_exec("new #{@proj_name}")
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# =========================================================================
|
|
70
|
+
describe "Conditional includes that already resolve correctly today" do
|
|
71
|
+
# =========================================================================
|
|
72
|
+
|
|
73
|
+
before do
|
|
74
|
+
@c.with_context do
|
|
75
|
+
Dir.chdir @proj_name do
|
|
76
|
+
FileUtils.cp test_asset_path("tests_with_conditional_includes/src/widget.h"), 'src/'
|
|
77
|
+
FileUtils.cp test_asset_path("tests_with_conditional_includes/src/widget.c"), 'src/'
|
|
78
|
+
FileUtils.cp test_asset_path("tests_with_conditional_includes/src/project_flag_extra.h"), 'src/'
|
|
79
|
+
FileUtils.cp test_asset_path("tests_with_conditional_includes/src/local_flag_extra.h"), 'src/'
|
|
80
|
+
FileUtils.cp test_asset_path("tests_with_conditional_includes/src/nested_wrapper.h"), 'src/'
|
|
81
|
+
FileUtils.cp test_asset_path("tests_with_conditional_includes/src/nested_extra.h"), 'src/'
|
|
82
|
+
FileUtils.cp test_asset_path("tests_with_conditional_includes/test/test_widget.c"), 'test/'
|
|
83
|
+
|
|
84
|
+
settings = {
|
|
85
|
+
:project => { :use_partials => true },
|
|
86
|
+
:defines => { :test => ['PROJECT_FLAG'] }
|
|
87
|
+
}
|
|
88
|
+
@c.merge_project_yml_for_test(settings)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "resolves a project-:defines-gated include, a same-file-#define-gated include, and correctly excludes a transitively-nested header from duplication" do
|
|
94
|
+
@c.with_context do
|
|
95
|
+
Dir.chdir @proj_name do
|
|
96
|
+
output = @c.ceedling_build_exec("test:widget")
|
|
97
|
+
expect(@c.last_exit_status).to eq(0)
|
|
98
|
+
expect(output).to match(/TESTED:\s+3/)
|
|
99
|
+
expect(output).to match(/PASSED:\s+3/)
|
|
100
|
+
expect(output).to match(/FAILED:\s+0/)
|
|
101
|
+
|
|
102
|
+
# (c) nested_extra.h is reached only transitively, through
|
|
103
|
+
# nested_wrapper.h's own #include -- confirm it is not promoted
|
|
104
|
+
# into a spurious, duplicated top-level #include in the generated
|
|
105
|
+
# Partial implementation (the exact filtering property the #1223
|
|
106
|
+
# fix must not break).
|
|
107
|
+
generated = Dir.glob('build/test/partials/**/*_impl.c').first
|
|
108
|
+
expect(generated).not_to be_nil
|
|
109
|
+
contents = File.read(generated)
|
|
110
|
+
expect(contents.scan(/#include\s+"nested_extra\.h"/).length).to eq(0)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# =========================================================================
|
|
118
|
+
describe "Conditional include gated on a macro from an earlier #include in the same file (issue #1223)" do
|
|
119
|
+
# =========================================================================
|
|
120
|
+
|
|
121
|
+
before do
|
|
122
|
+
@c.with_context do
|
|
123
|
+
Dir.chdir @proj_name do
|
|
124
|
+
FileUtils.cp test_asset_path("tests_with_conditional_includes/src/widget_feature.h"), 'src/'
|
|
125
|
+
FileUtils.cp test_asset_path("tests_with_conditional_includes/src/widget_feature.c"), 'src/'
|
|
126
|
+
FileUtils.cp test_asset_path("tests_with_conditional_includes/src/feature_config.h"), 'src/'
|
|
127
|
+
FileUtils.cp test_asset_path("tests_with_conditional_includes/src/feature_extra.h"), 'src/'
|
|
128
|
+
FileUtils.cp test_asset_path("tests_with_conditional_includes/test/test_widget_feature.c"), 'test/'
|
|
129
|
+
|
|
130
|
+
settings = { :project => { :use_partials => true } }
|
|
131
|
+
@c.merge_project_yml_for_test(settings)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "keeps FEATURE_EXTRA_MACRO visible in the generated Partial so the build compiles" do
|
|
137
|
+
@c.with_context do
|
|
138
|
+
Dir.chdir @proj_name do
|
|
139
|
+
output = @c.ceedling_build_exec("test:widget_feature")
|
|
140
|
+
expect(@c.last_exit_status).to eq(0)
|
|
141
|
+
expect(output).to match(/TESTED:\s+1/)
|
|
142
|
+
expect(output).to match(/PASSED:\s+1/)
|
|
143
|
+
expect(output).to match(/FAILED:\s+0/)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
end
|
|
@@ -131,8 +131,8 @@ ceedling_system_tests do
|
|
|
131
131
|
@c.merge_project_yml_for_test({ :test_build => { :preprocess_force_fallback => false } })
|
|
132
132
|
output = @c.ceedling_build_exec("test:all")
|
|
133
133
|
expect(@c.last_exit_status).to eq(0)
|
|
134
|
-
expect(output).to match(/TESTED:\s+
|
|
135
|
-
expect(output).to match(/PASSED:\s+
|
|
134
|
+
expect(output).to match(/TESTED:\s+68/)
|
|
135
|
+
expect(output).to match(/PASSED:\s+68/)
|
|
136
136
|
end
|
|
137
137
|
end
|
|
138
138
|
end
|
|
@@ -144,8 +144,8 @@ ceedling_system_tests do
|
|
|
144
144
|
@c.merge_project_yml_for_test({ :test_build => { :preprocess_force_fallback => true } })
|
|
145
145
|
output = @c.ceedling_build_exec("test:all")
|
|
146
146
|
expect(@c.last_exit_status).to eq(0)
|
|
147
|
-
expect(output).to match(/TESTED:\s+
|
|
148
|
-
expect(output).to match(/PASSED:\s+
|
|
147
|
+
expect(output).to match(/TESTED:\s+68/)
|
|
148
|
+
expect(output).to match(/PASSED:\s+68/)
|
|
149
149
|
end
|
|
150
150
|
end
|
|
151
151
|
end
|
|
@@ -46,8 +46,8 @@ ceedling_system_tests do
|
|
|
46
46
|
@c.with_context do
|
|
47
47
|
Dir.chdir "wondrous_forest" do
|
|
48
48
|
@output = @c.ceedling_build_exec("test:all")
|
|
49
|
-
expect(@output).to match(/TESTED:\s+
|
|
50
|
-
expect(@output).to match(/PASSED:\s+
|
|
49
|
+
expect(@output).to match(/TESTED:\s+68/)
|
|
50
|
+
expect(@output).to match(/PASSED:\s+68/)
|
|
51
51
|
expect(@output).to match(/FAILED:\s+0/)
|
|
52
52
|
end
|
|
53
53
|
end
|
|
@@ -114,7 +114,11 @@ module CommonSystemTestCases
|
|
|
114
114
|
@c.with_context do
|
|
115
115
|
Dir.chdir @proj_name do
|
|
116
116
|
all_docs = Dir["docs/*"]
|
|
117
|
-
|
|
117
|
+
# docs/ceedling only exists when this repo's own local HTML docs bundle
|
|
118
|
+
# (site-local/) was available to copy in -- see html_docs_bundle_available?.
|
|
119
|
+
expected = ['docs/unity', 'docs/cmock', 'docs/c_exception', 'docs/license.txt']
|
|
120
|
+
expected << 'docs/ceedling' if html_docs_bundle_available?
|
|
121
|
+
expect(all_docs).to contain_exactly(*expected)
|
|
118
122
|
end
|
|
119
123
|
end
|
|
120
124
|
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# =========================================================================
|
|
2
|
+
# Ceedling - Test-Centered Build System for C
|
|
3
|
+
# ThrowTheSwitch.org
|
|
4
|
+
# Copyright (c) 2010-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
|
|
5
|
+
# SPDX-License-Identifier: MIT
|
|
6
|
+
# =========================================================================
|
|
7
|
+
|
|
8
|
+
require 'spec_system_helper'
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
## Generated Runner Preserves System #include Directory/Brackets (issue #1236)
|
|
12
|
+
## =============================================================================
|
|
13
|
+
##
|
|
14
|
+
## A test file directly #include-ing a system header with a directory component
|
|
15
|
+
## (e.g. `#include <sys/stat.h>`) previously produced a generated runner containing
|
|
16
|
+
## `#include "stat.h"` -- the directory component and the system-include `<>`
|
|
17
|
+
## delimiters were both lost, since GeneratorTestRunner#generate rendered every
|
|
18
|
+
## include (mocks aside) down to its bare basename with no `<>` for system headers.
|
|
19
|
+
## The test file itself compiled fine; only the separately generated runner file
|
|
20
|
+
## failed, with a "No such file or directory" error on the mangled include.
|
|
21
|
+
##
|
|
22
|
+
|
|
23
|
+
TEST_RUNNER_SYSTEM_INCLUDE_C = <<~C
|
|
24
|
+
#include <sys/stat.h>
|
|
25
|
+
|
|
26
|
+
#include "unity.h"
|
|
27
|
+
|
|
28
|
+
void setUp(void) {}
|
|
29
|
+
void tearDown(void) {}
|
|
30
|
+
|
|
31
|
+
void test_runner_preserves_system_include_path(void)
|
|
32
|
+
{
|
|
33
|
+
struct stat value;
|
|
34
|
+
TEST_ASSERT_EQUAL_UINT(sizeof(value), sizeof(struct stat));
|
|
35
|
+
}
|
|
36
|
+
C
|
|
37
|
+
|
|
38
|
+
ceedling_system_tests do
|
|
39
|
+
|
|
40
|
+
before :all do
|
|
41
|
+
@c = SystemContext.new
|
|
42
|
+
@c.deploy_gem
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
after :all do
|
|
46
|
+
@c.done!
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
before { @proj_name = unique_proj_name("runner_sys_inc") }
|
|
50
|
+
|
|
51
|
+
describe "Deployed as a gem" do
|
|
52
|
+
before do
|
|
53
|
+
@c.with_context do
|
|
54
|
+
@c.ceedling_appcmd_exec("new #{@proj_name}")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
before do
|
|
59
|
+
@c.with_context do
|
|
60
|
+
Dir.chdir @proj_name do
|
|
61
|
+
File.write('test/test_runner_system_include.c', TEST_RUNNER_SYSTEM_INCLUDE_C)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "preserves a system include's directory component and angle brackets in the generated runner" do
|
|
67
|
+
@c.with_context do
|
|
68
|
+
Dir.chdir @proj_name do
|
|
69
|
+
output = @c.ceedling_build_exec("test:all")
|
|
70
|
+
|
|
71
|
+
expect(@c.last_exit_status).to eq(0)
|
|
72
|
+
expect(output).to match(/TESTED:\s+1/)
|
|
73
|
+
expect(output).to match(/PASSED:\s+1/)
|
|
74
|
+
expect(output).to match(/FAILED:\s+0/)
|
|
75
|
+
|
|
76
|
+
# The generated runner is what actually failed to compile in the reported
|
|
77
|
+
# issue -- assert on its content directly rather than just overall build
|
|
78
|
+
# success, since only the runner (not the test file itself) was affected.
|
|
79
|
+
runner_path = Dir.glob('build/test/runners/*_runner.c').first
|
|
80
|
+
expect(runner_path).not_to be_nil
|
|
81
|
+
|
|
82
|
+
runner_contents = File.read(runner_path)
|
|
83
|
+
expect(runner_contents).to include('#include <sys/stat.h>')
|
|
84
|
+
expect(runner_contents).not_to include('#include "stat.h"')
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
end
|
|
@@ -241,12 +241,13 @@ describe GeneratorPartials do
|
|
|
241
241
|
expect(result).to be_nil
|
|
242
242
|
end
|
|
243
243
|
|
|
244
|
-
it "writes
|
|
244
|
+
it "writes the typedefs and aggregate definitions, guarded and in element_sequence order, carrying forward a macro that precedes one of them, and returns the bare filename" do
|
|
245
245
|
file_contents = <<~CONTENTS
|
|
246
246
|
#ifndef __CEEDLING_GENERATED_MY_MODULE_TYPES_H__
|
|
247
247
|
#define __CEEDLING_GENERATED_MY_MODULE_TYPES_H__
|
|
248
248
|
|
|
249
249
|
typedef uint8_t Byte;
|
|
250
|
+
#define FOO 1
|
|
250
251
|
struct Config { int id; };
|
|
251
252
|
|
|
252
253
|
#endif // __CEEDLING_GENERATED_MY_MODULE_TYPES_H__
|
|
@@ -267,13 +268,15 @@ describe GeneratorPartials do
|
|
|
267
268
|
.with(expected_filepath, 'wb')
|
|
268
269
|
.and_yield(buf)
|
|
269
270
|
|
|
270
|
-
# A
|
|
271
|
-
# element_sequence
|
|
272
|
-
#
|
|
273
|
-
#
|
|
271
|
+
# A variable and a macro sit between the two type-defining statements in
|
|
272
|
+
# element_sequence. The variable (which stays behind for generate_header) is
|
|
273
|
+
# entirely absent here, but the macro precedes the aggregate and is carried
|
|
274
|
+
# forward immediately before it -- the aggregate's definition may depend on it
|
|
275
|
+
# (e.g. an array-size constant), and this header is #included before
|
|
276
|
+
# generate_header's own later, inline copy of that same macro would exist.
|
|
274
277
|
typedef_stmt = CExtractorTypes::CStatement.new(text: "typedef uint8_t Byte;", line_num: 1)
|
|
275
|
-
|
|
276
|
-
|
|
278
|
+
var_decl = make_var(name: 'counter', type: 'int', text: 'int counter;', line_num: 2)
|
|
279
|
+
macro_stmt = CExtractorTypes::CStatement.new(text: "#define FOO 1", line_num: 3)
|
|
277
280
|
aggregate_stmt = CExtractorTypes::CStatement.new(text: "struct Config { int id; };", line_num: 4)
|
|
278
281
|
|
|
279
282
|
c_module = CExtractorTypes::CModule.new(
|
|
@@ -281,7 +284,7 @@ describe GeneratorPartials do
|
|
|
281
284
|
macro_definitions: [macro_stmt],
|
|
282
285
|
variable_declarations: [var_decl],
|
|
283
286
|
aggregate_definitions: [aggregate_stmt],
|
|
284
|
-
element_sequence: [typedef_stmt,
|
|
287
|
+
element_sequence: [typedef_stmt, var_decl, macro_stmt, aggregate_stmt]
|
|
285
288
|
)
|
|
286
289
|
|
|
287
290
|
result = @generator.generate_types(
|
|
@@ -294,6 +297,106 @@ describe GeneratorPartials do
|
|
|
294
297
|
expect(@file_wrapper).to have_received(:open).with(expected_filepath, 'wb')
|
|
295
298
|
expect(result).to eq(header_filename)
|
|
296
299
|
end
|
|
300
|
+
|
|
301
|
+
it "carries a macro forward directly before the single typedef it immediately precedes" do
|
|
302
|
+
output_path = '/path/to/output'
|
|
303
|
+
name = 'my_module'
|
|
304
|
+
header_filename = 'my_module_types.h'
|
|
305
|
+
expected_filepath = File.join(output_path, header_filename)
|
|
306
|
+
|
|
307
|
+
allow(@file_path_utils).to receive(:form_partial_types_header_filename).and_return(header_filename)
|
|
308
|
+
|
|
309
|
+
buf = StringIO.new()
|
|
310
|
+
allow(@file_wrapper).to receive(:open).and_yield(buf)
|
|
311
|
+
|
|
312
|
+
macro_stmt = CExtractorTypes::CStatement.new(text: "#define LOCAL_CAPACITY 4", line_num: 1)
|
|
313
|
+
typedef_stmt = CExtractorTypes::CStatement.new(text: "typedef struct { int values[LOCAL_CAPACITY]; } Context;", line_num: 3)
|
|
314
|
+
|
|
315
|
+
c_module = CExtractorTypes::CModule.new(
|
|
316
|
+
type_definitions: [typedef_stmt],
|
|
317
|
+
macro_definitions: [macro_stmt],
|
|
318
|
+
element_sequence: [macro_stmt, typedef_stmt]
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
@generator.generate_types(name: name, c_module: c_module, output_path: output_path)
|
|
322
|
+
|
|
323
|
+
expect( buf.string ).to include( "#define LOCAL_CAPACITY 4\ntypedef struct { int values[LOCAL_CAPACITY]; } Context;\n" )
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
it "carries forward multiple macros preceding one type-defining item, in their own original relative order" do
|
|
327
|
+
output_path = '/path/to/output'
|
|
328
|
+
name = 'my_module'
|
|
329
|
+
header_filename = 'my_module_types.h'
|
|
330
|
+
|
|
331
|
+
allow(@file_path_utils).to receive(:form_partial_types_header_filename).and_return(header_filename)
|
|
332
|
+
|
|
333
|
+
buf = StringIO.new()
|
|
334
|
+
allow(@file_wrapper).to receive(:open).and_yield(buf)
|
|
335
|
+
|
|
336
|
+
macro_a = CExtractorTypes::CStatement.new(text: "#define WIDTH 4", line_num: 1)
|
|
337
|
+
macro_b = CExtractorTypes::CStatement.new(text: "#define HEIGHT 8", line_num: 2)
|
|
338
|
+
typedef_stmt = CExtractorTypes::CStatement.new(text: "typedef int Grid[WIDTH][HEIGHT];", line_num: 3)
|
|
339
|
+
|
|
340
|
+
c_module = CExtractorTypes::CModule.new(
|
|
341
|
+
type_definitions: [typedef_stmt],
|
|
342
|
+
macro_definitions: [macro_a, macro_b],
|
|
343
|
+
element_sequence: [macro_a, macro_b, typedef_stmt]
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
@generator.generate_types(name: name, c_module: c_module, output_path: output_path)
|
|
347
|
+
|
|
348
|
+
expect( buf.string ).to include( "#define WIDTH 4\n#define HEIGHT 8\ntypedef int Grid[WIDTH][HEIGHT];\n" )
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
it "carries a macro forward only once, before the first type-defining item it precedes, not before a later one too" do
|
|
352
|
+
output_path = '/path/to/output'
|
|
353
|
+
name = 'my_module'
|
|
354
|
+
header_filename = 'my_module_types.h'
|
|
355
|
+
|
|
356
|
+
allow(@file_path_utils).to receive(:form_partial_types_header_filename).and_return(header_filename)
|
|
357
|
+
|
|
358
|
+
buf = StringIO.new()
|
|
359
|
+
allow(@file_wrapper).to receive(:open).and_yield(buf)
|
|
360
|
+
|
|
361
|
+
macro_stmt = CExtractorTypes::CStatement.new(text: "#define FOO 1", line_num: 1)
|
|
362
|
+
typedef_stmt = CExtractorTypes::CStatement.new(text: "typedef int Foo;", line_num: 2)
|
|
363
|
+
aggregate_stmt = CExtractorTypes::CStatement.new(text: "struct Bar { int x; };", line_num: 3)
|
|
364
|
+
|
|
365
|
+
c_module = CExtractorTypes::CModule.new(
|
|
366
|
+
type_definitions: [typedef_stmt],
|
|
367
|
+
macro_definitions: [macro_stmt],
|
|
368
|
+
aggregate_definitions: [aggregate_stmt],
|
|
369
|
+
element_sequence: [macro_stmt, typedef_stmt, aggregate_stmt]
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
@generator.generate_types(name: name, c_module: c_module, output_path: output_path)
|
|
373
|
+
|
|
374
|
+
expect( buf.string.scan("#define FOO 1").length ).to eq(1)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
it "does not carry a macro into the shared header when it appears only after every type-defining item" do
|
|
378
|
+
output_path = '/path/to/output'
|
|
379
|
+
name = 'my_module'
|
|
380
|
+
header_filename = 'my_module_types.h'
|
|
381
|
+
|
|
382
|
+
allow(@file_path_utils).to receive(:form_partial_types_header_filename).and_return(header_filename)
|
|
383
|
+
|
|
384
|
+
buf = StringIO.new()
|
|
385
|
+
allow(@file_wrapper).to receive(:open).and_yield(buf)
|
|
386
|
+
|
|
387
|
+
typedef_stmt = CExtractorTypes::CStatement.new(text: "typedef int Foo;", line_num: 1)
|
|
388
|
+
macro_stmt = CExtractorTypes::CStatement.new(text: "#define FOO 1", line_num: 2)
|
|
389
|
+
|
|
390
|
+
c_module = CExtractorTypes::CModule.new(
|
|
391
|
+
type_definitions: [typedef_stmt],
|
|
392
|
+
macro_definitions: [macro_stmt],
|
|
393
|
+
element_sequence: [typedef_stmt, macro_stmt]
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
@generator.generate_types(name: name, c_module: c_module, output_path: output_path)
|
|
397
|
+
|
|
398
|
+
expect( buf.string ).to_not include("#define FOO 1")
|
|
399
|
+
end
|
|
297
400
|
end
|
|
298
401
|
|
|
299
402
|
context "#generate_header (private method)" do
|