ceedling 1.1.5 → 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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/GIT_COMMIT_SHA +1 -1
  3. data/assets/tests_with_conditional_includes/src/feature_config.h +13 -0
  4. data/assets/tests_with_conditional_includes/src/feature_extra.h +13 -0
  5. data/assets/tests_with_conditional_includes/src/local_flag_extra.h +13 -0
  6. data/assets/tests_with_conditional_includes/src/nested_extra.h +13 -0
  7. data/assets/tests_with_conditional_includes/src/nested_wrapper.h +16 -0
  8. data/assets/tests_with_conditional_includes/src/project_flag_extra.h +13 -0
  9. data/assets/tests_with_conditional_includes/src/widget.c +48 -0
  10. data/assets/tests_with_conditional_includes/src/widget.h +15 -0
  11. data/assets/tests_with_conditional_includes/src/widget_feature.c +29 -0
  12. data/assets/tests_with_conditional_includes/src/widget_feature.h +13 -0
  13. data/assets/tests_with_conditional_includes/test/test_widget.c +44 -0
  14. data/assets/tests_with_conditional_includes/test/test_widget_feature.c +31 -0
  15. data/docs/Changelog.md +20 -1
  16. data/examples/wondrous_forest/src/SensorHal.h +13 -0
  17. data/examples/wondrous_forest/src/TemperatureSensor.c +12 -5
  18. data/examples/wondrous_forest/src/TemperatureSensor.h +5 -4
  19. data/examples/wondrous_forest/test/TestTemperatureSensor.c +32 -3
  20. data/lib/ceedling/generators/generator_test_runner.rb +25 -1
  21. data/lib/ceedling/partials/partializer.rb +8 -1
  22. data/lib/ceedling/preprocess/preprocessinator.rb +22 -0
  23. data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +33 -0
  24. data/lib/version.rb +1 -1
  25. data/site-local/sitemap.xml.gz +0 -0
  26. data/spec/support/system/spec_system_helper.rb +11 -0
  27. data/spec/support/system/system_context.rb +18 -9
  28. data/spec/system/conditional_include_macro_visibility_spec.rb +152 -0
  29. data/spec/system/encoding_stress_spec.rb +4 -4
  30. data/spec/system/example_wondrous_forest_spec.rb +2 -2
  31. data/spec/system/support/common_test_cases.rb +5 -1
  32. data/spec/system/test_runner_system_include_spec.rb +90 -0
  33. data/spec/units/generators/generator_test_runner_spec.rb +69 -0
  34. data/spec/units/partials/partializer_spec.rb +69 -0
  35. data/spec/units/preprocess/preprocessinator_includes_handler_spec.rb +61 -0
  36. data/spec/units/preprocess/preprocessinator_spec.rb +136 -0
  37. data/vendor/c_exception/lib/CException.h +1 -1
  38. data/vendor/c_exception/project.yml +2 -2
  39. data/vendor/c_exception/test/TestException.c +1 -1
  40. data/vendor/c_exception/test/support/CExceptionConfig.h +5 -11
  41. data/vendor/cmock/docs/CMockChangeLog.md +23 -22
  42. data/vendor/cmock/scripts/create_makefile.rb +4 -3
  43. data/vendor/cmock/src/cmock.h +1 -1
  44. data/vendor/cmock/vendor/c_exception/lib/CException.h +1 -1
  45. data/vendor/cmock/vendor/c_exception/project.yml +2 -2
  46. data/vendor/cmock/vendor/c_exception/test/TestException.c +1 -1
  47. data/vendor/cmock/vendor/c_exception/test/support/CExceptionConfig.h +5 -11
  48. data/vendor/cmock/vendor/unity/auto/generate_test_runner.rb +85 -29
  49. data/vendor/cmock/vendor/unity/docs/UnityChangeLog.md +1 -0
  50. data/vendor/cmock/vendor/unity/examples/unity_config.h +8 -0
  51. data/vendor/cmock/vendor/unity/src/unity.c +9 -1
  52. data/vendor/cmock/vendor/unity/src/unity.h +1 -1
  53. data/vendor/cmock/vendor/unity/test/rakefile +2 -1
  54. data/vendor/cmock/vendor/unity/test/testdata/testRunnerGeneratorEmpty.c +13 -0
  55. data/vendor/cmock/vendor/unity/test/tests/test_generate_test_runner.rb +31 -0
  56. data/vendor/unity/auto/generate_test_runner.rb +63 -57
  57. data/vendor/unity/docs/UnityChangeLog.md +1 -0
  58. data/vendor/unity/examples/unity_config.h +8 -0
  59. data/vendor/unity/src/unity.c +9 -1
  60. data/vendor/unity/src/unity.h +1 -1
  61. data/vendor/unity/test/rakefile +2 -1
  62. data/vendor/unity/test/tests/test_generate_test_runner.rb +20 -0
  63. metadata +21 -3
  64. data/vendor/cmock/vendor/unity/auto/run_test.erb +0 -37
@@ -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
- %w{BUNDLE_GEMFILE BUNDLE_BIN_PATH RUBYOPT}.each { |k| ENV.delete(k) }
50
- deploy_output = `bundle config set --local path '#{shared_gem.install_dir}' 2>&1`
51
- # --prefer-local: Without it, Bundler resolves gems (e.g. `erb`) fresh from
52
- # rubygems repository even when Ruby's default-gem copy satisfies the Gemfile constraint.
53
- deploy_output += `bundle install --prefer-local 2>&1`
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+67/)
135
- expect(output).to match(/PASSED:\s+67/)
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+67/)
148
- expect(output).to match(/PASSED:\s+67/)
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+67/)
50
- expect(@output).to match(/PASSED:\s+67/)
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
- expect(all_docs).to contain_exactly('docs/ceedling', 'docs/unity', 'docs/cmock', 'docs/c_exception', 'docs/license.txt')
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
@@ -8,6 +8,7 @@
8
8
  require 'spec_helper'
9
9
  require 'ceedling/generators/generator_test_runner'
10
10
  require 'ceedling/parsing_parcels'
11
+ require 'ceedling/includes/includes'
11
12
 
12
13
  describe GeneratorTestRunner do
13
14
  before(:each) do
@@ -141,4 +142,72 @@ describe GeneratorTestRunner do
141
142
  )
142
143
  end
143
144
  end
145
+
146
+ # ===========================================================================
147
+ describe '#generate' do
148
+ # ===========================================================================
149
+ # Regression coverage for https://github.com/ThrowTheSwitch/Ceedling/issues/1236:
150
+ # a system #include with a directory component (e.g. <sys/stat.h>) was rendered
151
+ # into the generated runner as a bare, unbracketed basename ("stat.h"), losing
152
+ # both the directory and the user/system distinction, breaking runner compilation.
153
+ #
154
+ # @unity_runner_generator is a real UnityTestRunnerGenerator, constructed directly
155
+ # inside #initialize rather than injected -- swapped out for a double here so these
156
+ # specs assert on exactly what GeneratorTestRunner itself hands off, independent of
157
+ # Unity's own generated file formatting.
158
+ def build_and_capture_includes(includes:, mocks: [])
159
+ runner = build_runner( test_file_contents: "void test_Thing(void) {}\n" )
160
+
161
+ captured = nil
162
+ fake_generator = double('unity_runner_generator')
163
+ allow(fake_generator).to receive(:generate) do |*args|
164
+ captured = args[4]
165
+ end
166
+ runner.instance_variable_set(:@unity_runner_generator, fake_generator)
167
+
168
+ runner.generate(
169
+ module_name: 'test_thing',
170
+ runner_filepath: '/build/test/runners/test_thing_runner.c',
171
+ mocks: mocks,
172
+ includes: includes
173
+ )
174
+
175
+ return captured
176
+ end
177
+
178
+ it 'renders a system include with a directory component in angle brackets, directory intact' do
179
+ captured = build_and_capture_includes( includes: [ SystemInclude.new('sys/stat.h') ] )
180
+
181
+ expect( captured ).to eq( ['<sys/stat.h>'] )
182
+ end
183
+
184
+ it 'prefers a reconciled system include\'s include_path over its resolved filepath' do
185
+ # Mirrors what Includes.reconcile actually produces: filepath is the real,
186
+ # resolved (possibly absolute) location; include_path is the original,
187
+ # as-written directive text that must be what's rendered.
188
+ reconciled = SystemInclude.new('/usr/include/sys/stat.h', include_path: 'sys/stat.h')
189
+
190
+ captured = build_and_capture_includes( includes: [ reconciled ] )
191
+
192
+ expect( captured ).to eq( ['<sys/stat.h>'] )
193
+ end
194
+
195
+ it 'renders a user include with a directory component down to its bare filename, unchanged from prior behavior' do
196
+ # Unlike a system header, a project's own directories are exposed to the
197
+ # compiler as a flat list of -I search paths -- rendering a user include's
198
+ # own directory component here breaks resolution rather than fixing anything,
199
+ # so this behavior is intentionally unchanged by the issue #1236 fix.
200
+ captured = build_and_capture_includes( includes: [ UserInclude.new('sub/header.h') ] )
201
+
202
+ expect( captured ).to eq( ['header.h'] )
203
+ end
204
+
205
+ it 'renders a mixed list of system and user includes each in their own correct form' do
206
+ captured = build_and_capture_includes(
207
+ includes: [ SystemInclude.new('sys/stat.h'), UserInclude.new('sub/header.h') ]
208
+ )
209
+
210
+ expect( captured ).to eq( ['<sys/stat.h>', 'header.h'] )
211
+ end
212
+ end
144
213
  end
@@ -871,6 +871,75 @@ describe Partializer do
871
871
  expect(result).not_to include(UserInclude.new('partial_module.h'))
872
872
  end
873
873
 
874
+ # Regression test for #1215: PUBLIC/PRIVATE were once the only mock modes that existed,
875
+ # so this method's own "does this module have a mock to redirect to" check only ever
876
+ # listed those two. DEDUCT (MOCK_PARTIAL_ALL_MODULE) was added later without updating
877
+ # this check, so a module mocked via MOCK_PARTIAL_ALL_MODULE -- e.g. a vendor header
878
+ # whose only mockable functions are static inline, which requires DEDUCT to select at
879
+ # all -- was silently never redirected to its mock interface header.
880
+ it "remaps mockable deduct (ALL_MODULE) partial to interface header" do
881
+ includes = [UserInclude.new('header1.h'), UserInclude.new('partial_module.h'), UserInclude.new('header2.h')]
882
+ partials = {
883
+ 'partial_module' => make_partial_config(mocks_type: Partials::DEDUCT)
884
+ }
885
+ impl_filename = 'module_impl.h'
886
+ interface_filename = 'partial_module_interface.h'
887
+ impl_header = UserInclude.new(impl_filename)
888
+ interface_header = UserInclude.new(interface_filename)
889
+
890
+ allow(@file_path_utils).to receive(:form_partial_implementation_header_filename)
891
+ .with('module')
892
+ .and_return(impl_filename)
893
+
894
+ allow(@file_path_utils).to receive(:form_partial_interface_header_filename)
895
+ .with('partial_module')
896
+ .and_return(interface_filename)
897
+
898
+ result = @partializer.remap_implementation_source_includes(
899
+ name: 'module',
900
+ includes: includes,
901
+ partials: partials
902
+ )
903
+
904
+ expect(result).to include(impl_header)
905
+ expect(result).to include(interface_header)
906
+ expect(result).to include(UserInclude.new('header1.h'))
907
+ expect(result).to include(UserInclude.new('header2.h'))
908
+ expect(result).not_to include(UserInclude.new('partial_module.h'))
909
+ end
910
+
911
+ # Same regression as the DEDUCT case above, for ACCUMULATE (MOCK_PARTIAL_MODULE).
912
+ it "remaps mockable accumulate partial to interface header" do
913
+ includes = [UserInclude.new('header1.h'), UserInclude.new('partial_module.h'), UserInclude.new('header2.h')]
914
+ partials = {
915
+ 'partial_module' => make_partial_config(mocks_type: Partials::ACCUMULATE)
916
+ }
917
+ impl_filename = 'module_impl.h'
918
+ interface_filename = 'partial_module_interface.h'
919
+ impl_header = UserInclude.new(impl_filename)
920
+ interface_header = UserInclude.new(interface_filename)
921
+
922
+ allow(@file_path_utils).to receive(:form_partial_implementation_header_filename)
923
+ .with('module')
924
+ .and_return(impl_filename)
925
+
926
+ allow(@file_path_utils).to receive(:form_partial_interface_header_filename)
927
+ .with('partial_module')
928
+ .and_return(interface_filename)
929
+
930
+ result = @partializer.remap_implementation_source_includes(
931
+ name: 'module',
932
+ includes: includes,
933
+ partials: partials
934
+ )
935
+
936
+ expect(result).to include(impl_header)
937
+ expect(result).to include(interface_header)
938
+ expect(result).to include(UserInclude.new('header1.h'))
939
+ expect(result).to include(UserInclude.new('header2.h'))
940
+ expect(result).not_to include(UserInclude.new('partial_module.h'))
941
+ end
942
+
874
943
  it "remaps multiple mockable partials to interface headers" do
875
944
  includes = [
876
945
  UserInclude.new('header1.h'),
@@ -339,6 +339,67 @@ RSpec.describe PreprocessinatorIncludesHandler do
339
339
  end
340
340
 
341
341
 
342
+ # ===========================================================================
343
+ describe '#extract_bare_includes_from_text' do
344
+ # ===========================================================================
345
+
346
+ let(:filepath) { '/src/module.c' }
347
+
348
+ it 'extracts a simple user include' do
349
+ stub_file_open(filepath, "#include \"foo.h\"\n")
350
+ result = subject.extract_bare_includes_from_text(filepath: filepath)
351
+ expect(result.map(&:filename)).to include('foo.h')
352
+ end
353
+
354
+ it 'extracts a system include too, unlike extract_user_includes_from_text' do
355
+ stub_file_open(filepath, "#include <stdio.h>\n")
356
+ result = subject.extract_bare_includes_from_text(filepath: filepath)
357
+ expect(result.map(&:filename)).to include('stdio.h')
358
+ end
359
+
360
+ it 'returns plain Include objects, not UserInclude/SystemInclude' do
361
+ stub_file_open(filepath, "#include \"foo.h\"\n#include <stdio.h>\n")
362
+ result = subject.extract_bare_includes_from_text(filepath: filepath)
363
+ expect(result).to all( be_an_instance_of(Include) )
364
+ end
365
+
366
+ it 'ignores includes inside line comments' do
367
+ stub_file_open(filepath, "// #include \"commented_out.h\"\n")
368
+ result = subject.extract_bare_includes_from_text(filepath: filepath)
369
+ expect(result).to be_empty
370
+ end
371
+
372
+ it 'ignores includes inside block comments' do
373
+ content = "/* #include \"in_block.h\" */\n#include \"real.h\"\n"
374
+ stub_file_open(filepath, content)
375
+ result = subject.extract_bare_includes_from_text(filepath: filepath)
376
+ expect(result.map(&:filename)).to contain_exactly('real.h')
377
+ end
378
+
379
+ # The defining difference from extract_user_includes_from_text: no conditional
380
+ # tracking at all -- an #include inside an #ifdef for an undefined macro is
381
+ # still captured, since this method's whole purpose is to see past a guard
382
+ # whose condition can't be evaluated without opening another header (issue #1223).
383
+ it 'captures a conditionally-guarded include regardless of whether the guard could be evaluated true' do
384
+ content = <<~C
385
+ #ifdef UNDEFINED_MACRO
386
+ #include "conditional.h"
387
+ #endif
388
+ C
389
+ stub_file_open(filepath, content)
390
+ result = subject.extract_bare_includes_from_text(filepath: filepath)
391
+ expect(result.map(&:filename)).to include('conditional.h')
392
+ end
393
+
394
+ it 'removes a self-referential include matching the file being scanned' do
395
+ stub_file_open(filepath, "#include \"#{filepath}\"\n#include \"foo.h\"\n")
396
+ result = subject.extract_bare_includes_from_text(filepath: filepath)
397
+ expect(result.map(&:filename)).to contain_exactly('foo.h')
398
+ end
399
+
400
+ end
401
+
402
+
342
403
  # ===========================================================================
343
404
  describe '#extract_system_includes_from_text' do
344
405
  # ===========================================================================