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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/GIT_COMMIT_SHA +1 -1
  3. data/README.md +1 -1
  4. data/assets/tests_with_conditional_includes/src/feature_config.h +13 -0
  5. data/assets/tests_with_conditional_includes/src/feature_extra.h +13 -0
  6. data/assets/tests_with_conditional_includes/src/local_flag_extra.h +13 -0
  7. data/assets/tests_with_conditional_includes/src/nested_extra.h +13 -0
  8. data/assets/tests_with_conditional_includes/src/nested_wrapper.h +16 -0
  9. data/assets/tests_with_conditional_includes/src/project_flag_extra.h +13 -0
  10. data/assets/tests_with_conditional_includes/src/widget.c +48 -0
  11. data/assets/tests_with_conditional_includes/src/widget.h +15 -0
  12. data/assets/tests_with_conditional_includes/src/widget_feature.c +29 -0
  13. data/assets/tests_with_conditional_includes/src/widget_feature.h +13 -0
  14. data/assets/tests_with_conditional_includes/test/test_widget.c +44 -0
  15. data/assets/tests_with_conditional_includes/test/test_widget_feature.c +31 -0
  16. data/docs/Changelog.md +31 -1
  17. data/docs/mkdocs/configuration/reference/partials.md +7 -7
  18. data/examples/wondrous_forest/src/SensorHal.h +13 -0
  19. data/examples/wondrous_forest/src/TemperatureSensor.c +12 -5
  20. data/examples/wondrous_forest/src/TemperatureSensor.h +5 -4
  21. data/examples/wondrous_forest/test/TestTemperatureSensor.c +32 -3
  22. data/lib/ceedling/generators/generator_partials.rb +20 -1
  23. data/lib/ceedling/generators/generator_test_runner.rb +25 -1
  24. data/lib/ceedling/partials/partializer.rb +8 -1
  25. data/lib/ceedling/preprocess/preprocessinator.rb +22 -0
  26. data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +33 -0
  27. data/lib/ceedling/test_invoker/test_build_executor.rb +3 -1
  28. data/lib/version.rb +1 -1
  29. data/site-local/configuration/reference/partials.html +11 -10
  30. data/site-local/sitemap.xml.gz +0 -0
  31. data/spec/support/system/spec_system_helper.rb +11 -0
  32. data/spec/support/system/system_context.rb +18 -9
  33. data/spec/system/conditional_include_macro_visibility_spec.rb +152 -0
  34. data/spec/system/encoding_stress_spec.rb +4 -4
  35. data/spec/system/example_wondrous_forest_spec.rb +2 -2
  36. data/spec/system/support/common_test_cases.rb +5 -1
  37. data/spec/system/test_runner_system_include_spec.rb +90 -0
  38. data/spec/units/generators/generator_partials_spec.rb +111 -8
  39. data/spec/units/generators/generator_test_runner_spec.rb +69 -0
  40. data/spec/units/partials/partializer_spec.rb +69 -0
  41. data/spec/units/preprocess/preprocessinator_includes_handler_spec.rb +61 -0
  42. data/spec/units/preprocess/preprocessinator_spec.rb +136 -0
  43. data/vendor/c_exception/lib/CException.h +1 -1
  44. data/vendor/c_exception/project.yml +2 -2
  45. data/vendor/c_exception/test/TestException.c +1 -1
  46. data/vendor/c_exception/test/support/CExceptionConfig.h +5 -11
  47. data/vendor/cmock/docs/CMockChangeLog.md +23 -22
  48. data/vendor/cmock/scripts/create_makefile.rb +4 -3
  49. data/vendor/cmock/src/cmock.h +1 -1
  50. data/vendor/cmock/vendor/c_exception/lib/CException.h +1 -1
  51. data/vendor/cmock/vendor/c_exception/project.yml +2 -2
  52. data/vendor/cmock/vendor/c_exception/test/TestException.c +1 -1
  53. data/vendor/cmock/vendor/c_exception/test/support/CExceptionConfig.h +5 -11
  54. data/vendor/cmock/vendor/unity/auto/generate_test_runner.rb +85 -29
  55. data/vendor/cmock/vendor/unity/docs/UnityChangeLog.md +1 -0
  56. data/vendor/cmock/vendor/unity/examples/unity_config.h +8 -0
  57. data/vendor/cmock/vendor/unity/src/unity.c +9 -1
  58. data/vendor/cmock/vendor/unity/src/unity.h +1 -1
  59. data/vendor/cmock/vendor/unity/test/rakefile +2 -1
  60. data/vendor/cmock/vendor/unity/test/testdata/testRunnerGeneratorEmpty.c +13 -0
  61. data/vendor/cmock/vendor/unity/test/tests/test_generate_test_runner.rb +31 -0
  62. data/vendor/unity/auto/generate_test_runner.rb +63 -57
  63. data/vendor/unity/docs/UnityChangeLog.md +1 -0
  64. data/vendor/unity/examples/unity_config.h +8 -0
  65. data/vendor/unity/src/unity.c +9 -1
  66. data/vendor/unity/src/unity.h +1 -1
  67. data/vendor/unity/test/rakefile +2 -1
  68. data/vendor/unity/test/tests/test_generate_test_runner.rb +20 -0
  69. metadata +21 -3
  70. 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
@@ -15,7 +15,7 @@
15
15
  module Ceedling
16
16
  module Version
17
17
  # Convenience constants for gem building, etc.
18
- GEM = '1.1.4'
18
+ GEM = '1.1.6'
19
19
  TAG = GEM
20
20
 
21
21
  # If run as a script print Ceedling's version to $stdout
@@ -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
- - The default should be sufficient for nearly all test builds. Modification should only be necessary on error.
4167
- - This configuration option is only available in Ceedling 1.1.4+.</p>
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's natural end (a terminating <code>;</code>, a closing
4173
+ until it finds that constructs 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, rather than continuing to search indefinitely
4174
- into whatever else follows in the file.</p>
4175
- <p>The value is a multiplier of 1000 characters, not a raw character count
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 a sizable generated
4180
- lookup table, for instance. There's ordinarily no reason to lower it.</p>
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
 
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
- %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
@@ -241,12 +241,13 @@ describe GeneratorPartials do
241
241
  expect(result).to be_nil
242
242
  end
243
243
 
244
- it "writes only the typedefs and aggregate definitions, guarded and in element_sequence order, and returns the bare filename" do
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 macro and a variable sit between the two type-defining statements in
271
- # element_sequence; only the typedef and the aggregate belong in the shared
272
- # types header, in their original relative order, and neither the macro nor
273
- # the variable (which stay behind for generate_header) appear here at all.
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
- macro_stmt = CExtractorTypes::CStatement.new(text: "#define FOO 1", line_num: 2)
276
- var_decl = make_var(name: 'counter', type: 'int', text: 'int counter;', line_num: 3)
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, macro_stmt, var_decl, aggregate_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