ceedling 1.1.2 → 1.1.4

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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/GIT_COMMIT_SHA +1 -1
  3. data/README.md +1 -1
  4. data/assets/fake_crash_test_fixture.rb +14 -0
  5. data/assets/partial_all_module.h +11 -0
  6. data/assets/run_with_ubsan.rb +15 -0
  7. data/assets/same_dir_pairing_module.c +4 -0
  8. data/assets/same_dir_pairing_module.h +8 -0
  9. data/assets/same_dir_pairing_module_func.h +6 -0
  10. data/assets/same_dir_pairing_other.c +4 -0
  11. data/assets/same_dir_pairing_other.h +8 -0
  12. data/assets/test_example_file_crash_sigsegv_with_param.c +32 -0
  13. data/assets/test_example_file_ub_shift_overflow.c +22 -0
  14. data/assets/test_partial_all_module.c +16 -0
  15. data/assets/test_same_dir_pairing.c +23 -0
  16. data/bin/actions_wrapper.rb +7 -6
  17. data/docs/Changelog.md +29 -1
  18. data/docs/mkdocs/configuration/reference/index.md +8 -0
  19. data/docs/mkdocs/configuration/reference/partials.md +38 -0
  20. data/examples/wondrous_forest/src/LightSensor.c +21 -4
  21. data/examples/wondrous_forest/src/LightSensor.h +12 -4
  22. data/examples/wondrous_forest/test/TestLightSensor.c +34 -4
  23. data/lib/ceedling/c_extractor/c_extractor.rb +37 -11
  24. data/lib/ceedling/c_extractor/c_extractor_constants.rb +0 -3
  25. data/lib/ceedling/c_extractor/c_extractor_declarations.rb +26 -25
  26. data/lib/ceedling/c_extractor/c_extractor_functions.rb +5 -12
  27. data/lib/ceedling/config/configurator.rb +8 -1
  28. data/lib/ceedling/config/configurator_setup.rb +26 -0
  29. data/lib/ceedling/defaults.rb +16 -2
  30. data/lib/ceedling/file_path_utils.rb +7 -0
  31. data/lib/ceedling/file_wrapper.rb +8 -0
  32. data/lib/ceedling/generators/generator.rb +15 -0
  33. data/lib/ceedling/generators/generator_helper.rb +25 -6
  34. data/lib/ceedling/generators/generator_partials.rb +84 -3
  35. data/lib/ceedling/generators/generator_test_results_backtrace.rb +268 -100
  36. data/lib/ceedling/generators/generator_test_runner.rb +20 -2
  37. data/lib/ceedling/includes/includes.rb +66 -11
  38. data/lib/ceedling/objects.yml +5 -0
  39. data/lib/ceedling/partials/partializer.rb +18 -3
  40. data/lib/ceedling/partials/partializer_utils.rb +6 -1
  41. data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +43 -16
  42. data/lib/ceedling/test_invoker/test_build_executor.rb +19 -8
  43. data/lib/version.rb +1 -1
  44. data/mkdocs.yml +1 -0
  45. data/plugins/gcov/lib/gcov.rb +2 -2
  46. data/site-local/404.html +30 -0
  47. data/site-local/configuration/environment-vars.html +30 -0
  48. data/site-local/configuration/global-collections.html +30 -0
  49. data/site-local/configuration/index.html +30 -0
  50. data/site-local/configuration/loading.html +30 -0
  51. data/site-local/configuration/mixins.html +30 -0
  52. data/site-local/configuration/parallel-builds.html +30 -0
  53. data/site-local/configuration/project-file.html +30 -0
  54. data/site-local/configuration/reference/cexception.html +30 -0
  55. data/site-local/configuration/reference/cmock.html +30 -0
  56. data/site-local/configuration/reference/defines.html +30 -0
  57. data/site-local/configuration/reference/environment.html +30 -0
  58. data/site-local/configuration/reference/extension.html +30 -0
  59. data/site-local/configuration/reference/files.html +30 -0
  60. data/site-local/configuration/reference/flags.html +30 -0
  61. data/site-local/configuration/reference/index.html +36 -0
  62. data/site-local/configuration/reference/libraries.html +30 -0
  63. data/site-local/configuration/reference/mixins.html +30 -0
  64. data/site-local/configuration/reference/partials.html +4257 -0
  65. data/site-local/configuration/reference/paths.html +30 -0
  66. data/site-local/configuration/reference/plugins.html +30 -0
  67. data/site-local/configuration/reference/project.html +30 -0
  68. data/site-local/configuration/reference/release-build.html +31 -1
  69. data/site-local/configuration/reference/test-build.html +31 -1
  70. data/site-local/configuration/reference/test-runner.html +30 -0
  71. data/site-local/configuration/reference/tools.html +30 -0
  72. data/site-local/configuration/reference/unity.html +30 -0
  73. data/site-local/configuration/which-ceedling.html +30 -0
  74. data/site-local/development/index.html +30 -0
  75. data/site-local/development/plugins/configuration.html +30 -0
  76. data/site-local/development/plugins/index.html +30 -0
  77. data/site-local/development/plugins/plugin-subclass.html +30 -0
  78. data/site-local/development/plugins/rake-tasks.html +30 -0
  79. data/site-local/development/workflow.html +30 -0
  80. data/site-local/getting-started/command-line.html +30 -0
  81. data/site-local/getting-started/example-projects/cipher-quest.html +30 -0
  82. data/site-local/getting-started/example-projects/index.html +30 -0
  83. data/site-local/getting-started/example-projects/temp-sensor.html +30 -0
  84. data/site-local/getting-started/example-projects/wondrous-forest.html +30 -0
  85. data/site-local/getting-started/index.html +30 -0
  86. data/site-local/getting-started/installation.html +30 -0
  87. data/site-local/getting-started/quick-start.html +30 -0
  88. data/site-local/help.html +30 -0
  89. data/site-local/index.html +30 -0
  90. data/site-local/overview/build-system.html +30 -0
  91. data/site-local/overview/index.html +30 -0
  92. data/site-local/overview/test-environments.html +30 -0
  93. data/site-local/overview/tools-and-frameworks.html +30 -0
  94. data/site-local/plugins/beep.html +30 -0
  95. data/site-local/plugins/bullseye.html +30 -0
  96. data/site-local/plugins/command-hooks.html +30 -0
  97. data/site-local/plugins/compile-commands-json-db.html +30 -0
  98. data/site-local/plugins/cppcheck.html +30 -0
  99. data/site-local/plugins/dependencies.html +30 -0
  100. data/site-local/plugins/fff.html +30 -0
  101. data/site-local/plugins/gcov/examples.html +30 -0
  102. data/site-local/plugins/gcov/gcovr.html +30 -0
  103. data/site-local/plugins/gcov/index.html +30 -0
  104. data/site-local/plugins/gcov/overview.html +30 -0
  105. data/site-local/plugins/gcov/reportgenerator.html +30 -0
  106. data/site-local/plugins/gcov/reporting.html +30 -0
  107. data/site-local/plugins/gcov/setup.html +30 -0
  108. data/site-local/plugins/gcov/tool-versions.html +30 -0
  109. data/site-local/plugins/gcov/troubleshooting.html +30 -0
  110. data/site-local/plugins/index.html +30 -0
  111. data/site-local/plugins/module-generator.html +30 -0
  112. data/site-local/plugins/report-build-warnings-log.html +30 -0
  113. data/site-local/plugins/report-tests-gtestlike-stdout.html +30 -0
  114. data/site-local/plugins/report-tests-ide-stdout.html +30 -0
  115. data/site-local/plugins/report-tests-log-factory.html +30 -0
  116. data/site-local/plugins/report-tests-pretty-stdout.html +30 -0
  117. data/site-local/plugins/report-tests-raw-output-log.html +30 -0
  118. data/site-local/plugins/report-tests-teamcity-stdout.html +30 -0
  119. data/site-local/plugins/valgrind.html +30 -0
  120. data/site-local/project/release-history.html +30 -0
  121. data/site-local/project/upgrade.html +30 -0
  122. data/site-local/reference/build-directives.html +30 -0
  123. data/site-local/reference/command-line.html +30 -0
  124. data/site-local/reference/environment-vars.html +30 -0
  125. data/site-local/reference/gcov-plugin.html +30 -0
  126. data/site-local/reference/global-collections.html +30 -0
  127. data/site-local/reference/index.html +30 -0
  128. data/site-local/reference/partials-macros.html +30 -0
  129. data/site-local/reference/project-configuration.html +30 -0
  130. data/site-local/sitemap.xml.gz +0 -0
  131. data/site-local/snapshot/examples/cipher_quest/index.html +30 -0
  132. data/site-local/snapshot/examples/temp_sensor/index.html +30 -0
  133. data/site-local/snapshot/examples/wondrous_forest/index.html +30 -0
  134. data/site-local/testing-guide/build-directives.html +30 -0
  135. data/site-local/testing-guide/conventions.html +30 -0
  136. data/site-local/testing-guide/frameworks.html +30 -0
  137. data/site-local/testing-guide/index.html +30 -0
  138. data/site-local/testing-guide/partials/configuration.html +30 -0
  139. data/site-local/testing-guide/partials/conventions.html +30 -0
  140. data/site-local/testing-guide/partials/directives.html +30 -0
  141. data/site-local/testing-guide/partials/example.html +30 -0
  142. data/site-local/testing-guide/partials/index.html +30 -0
  143. data/site-local/testing-guide/partials/overview.html +30 -0
  144. data/site-local/testing-guide/partials/variables.html +30 -0
  145. data/site-local/testing-guide/test-cases.html +30 -0
  146. data/site-local/testing-guide/test-sample.html +30 -0
  147. data/site-local/testing-guide/test-suite-anatomy.html +30 -0
  148. data/spec/support/system/spec_system_helper.rb +25 -0
  149. data/spec/support/system/system_context.rb +15 -0
  150. data/spec/system/deployment_as_gem_spec.rb +14 -0
  151. data/spec/system/deployment_as_vendor_spec.rb +19 -0
  152. data/spec/system/encoding_stress_spec.rb +4 -4
  153. data/spec/system/example_wondrous_forest_spec.rb +2 -2
  154. data/spec/system/support/common_test_cases.rb +227 -0
  155. data/spec/units/c_extractor/c_extractor_declarations_spec.rb +27 -4
  156. data/spec/units/c_extractor/c_extractor_functions_spec.rb +1 -11
  157. data/spec/units/c_extractor/c_extractor_integration_spec.rb +18 -11
  158. data/spec/units/c_extractor/c_extractor_spec.rb +57 -1
  159. data/spec/units/config/configurator_setup_spec.rb +61 -0
  160. data/spec/units/configurator_spec.rb +45 -0
  161. data/spec/units/file_path_utils_spec.rb +17 -0
  162. data/spec/units/generators/generator_helper_spec.rb +34 -7
  163. data/spec/units/generators/generator_partials_spec.rb +120 -12
  164. data/spec/units/generators/generator_test_results_backtrace_spec.rb +248 -41
  165. data/spec/units/generators/generator_test_runner_spec.rb +26 -2
  166. data/spec/units/includes/include_spec.rb +30 -18
  167. data/spec/units/includes/includes_spec.rb +100 -10
  168. data/spec/units/partials/partializer_spec.rb +113 -1
  169. data/spec/units/partials/partializer_utils_spec.rb +11 -0
  170. data/spec/units/preprocess/preprocessinator_includes_handler_spec.rb +96 -7
  171. data/spec/units/system_wrapper_spec.rb +72 -0
  172. data/spec/units/test_context_extractor_spec.rb +3 -3
  173. metadata +20 -2
@@ -34,7 +34,11 @@ describe CExtractor do
34
34
  c_extractor_functions: functions,
35
35
  c_extractor_declarations: declarations,
36
36
  c_extractor_preprocessing: preprocessing,
37
- c_extractor_definitions: definitions
37
+ c_extractor_definitions: definitions,
38
+ # Bare double (no :partials_max_extraction_length stub) so #respond_to? is
39
+ # false and CExtractor falls back to its own built-in default buffer length.
40
+ configurator: double('Configurator'),
41
+ loginator: double('Loginator').as_null_object
38
42
  }
39
43
  )
40
44
  extractor.setup()
@@ -602,18 +606,21 @@ describe CExtractor do
602
606
  CONTENTS
603
607
 
604
608
  # TODO: Test for function name extraction after implementing generic handling of feature summaries
605
- expect { extractor.from_string(content: file_contents, chunk_size: 10, max_buffer_length: 20) }.to raise_error(CeedlingException, /Feature extraction exceeded maximum length/)
609
+ expect { extractor.from_string(content: file_contents, chunk_size: 10, max_buffer_length: 20) }.to raise_error(CeedlingException, /Extraction exceeded maximum length/)
606
610
  end
607
611
 
608
- it "should fail to extract a signature longer than max length" do
609
- file_contents = <<~CONTENTS
610
- void a_function(void) {
611
- int a = 1 + 1;
612
- }
613
- CONTENTS
614
-
615
- contents = extractor.from_string(content: file_contents, chunk_size: 10, max_line_length: 10)
616
- expect(contents.function_definitions.length ).to eq 0
612
+ it "should extract a variable declaration longer than 1000 characters" do
613
+ # A single limit governs every feature type (see extract_next_feature); this confirms a
614
+ # declaration comfortably longer than the old, now-removed 1000-character inner limit is
615
+ # captured rather than silently dropped, as long as it's within the (much larger) buffer
616
+ # ceiling -- the default here, since none is passed explicitly.
617
+ values = (1..400).map { |n| n.to_s }.join(', ')
618
+ file_contents = "static const int a_table[] = { #{values} };\n"
619
+ expect(file_contents.length).to be > 1000
620
+
621
+ contents = extractor.from_string(content: file_contents)
622
+ expect(contents.variable_declarations.length).to eq 1
623
+ expect(contents.variable_declarations[0].name).to eq 'a_table'
617
624
  end
618
625
 
619
626
  it "should extract typedef definitions and no other features from a typedefs-only input" do
@@ -21,6 +21,58 @@ require 'stringio'
21
21
  ##
22
22
  describe CExtractor do
23
23
 
24
+ ###
25
+ ### setup() / default_max_buffer_length() -- :partials -> :max_extraction_length wiring
26
+ ###
27
+ describe "#setup (private method testing)" do
28
+ # Helper to build a CExtractor with a given configurator double, without wiring the
29
+ # other collaborators -- default_max_buffer_length only reads @configurator
30
+ let(:build_extractor) do
31
+ ->(configurator) do
32
+ extractor = CExtractor.new(
33
+ {
34
+ c_extractor_code_text: CExtractorCodeText.new,
35
+ c_extractor_functions: CExtractorFunctions.new({ c_extractor_code_text: CExtractorCodeText.new }),
36
+ c_extractor_declarations: CExtractorDeclarations.new({ c_extractor_code_text: CExtractorCodeText.new }),
37
+ c_extractor_preprocessing: CExtractorPreprocessing.new({ c_extractor_code_text: CExtractorCodeText.new }),
38
+ c_extractor_definitions: CExtractorDefinitions.new({ c_extractor_code_text: CExtractorCodeText.new }),
39
+ configurator: configurator,
40
+ loginator: double('Loginator').as_null_object
41
+ }
42
+ )
43
+ extractor.setup()
44
+ extractor
45
+ end
46
+ end
47
+
48
+ it "scales :partials_max_extraction_length by 1000 when the configurator provides it" do
49
+ configurator = double('Configurator', partials_max_extraction_length: 7)
50
+ extractor = build_extractor.call(configurator)
51
+
52
+ expect(extractor.send(:default_max_buffer_length)).to eq(7000)
53
+ end
54
+
55
+ it "falls back to the built-in default when the configurator doesn't provide it" do
56
+ # A bare double with no stub -- #respond_to? is false, exactly like direct/test
57
+ # construction of this class where no real project configuration is available.
58
+ configurator = double('Configurator')
59
+ extractor = build_extractor.call(configurator)
60
+
61
+ expect(extractor.send(:default_max_buffer_length)).to eq(CExtractorConstants::DEFAULT_MAX_FUNCTION_LENGTH)
62
+ end
63
+
64
+ it "setup() itself assigns @max_buffer_length from default_max_buffer_length" do
65
+ configurator = double('Configurator', partials_max_extraction_length: 3)
66
+ extractor = build_extractor.call(configurator)
67
+
68
+ # @max_buffer_length has no reader; drive behavior through from_string, which raises
69
+ # once the buffer set up by setup() is exceeded.
70
+ expect {
71
+ extractor.from_string(content: ('x' * 3001), chunk_size: 500)
72
+ }.to raise_error(CeedlingException, /exceeded maximum length of 3000 characters/)
73
+ end
74
+ end
75
+
24
76
  ###
25
77
  ### extract_next_feature()
26
78
  ###
@@ -55,7 +107,11 @@ describe CExtractor do
55
107
  c_extractor_functions: functions,
56
108
  c_extractor_declarations: declarations,
57
109
  c_extractor_preprocessing: preprocessing,
58
- c_extractor_definitions: definitions
110
+ c_extractor_definitions: definitions,
111
+ # Bare double (no :partials_max_extraction_length stub) so #respond_to? is
112
+ # false and CExtractor falls back to its own built-in default buffer length.
113
+ configurator: double('Configurator'),
114
+ loginator: double('Loginator').as_null_object
59
115
  }
60
116
  )
61
117
  extractor.setup()
@@ -0,0 +1,61 @@
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_helper'
9
+ require 'ceedling/config/configurator_setup'
10
+ require 'ceedling/reportinator'
11
+
12
+ # Only #validate_partials is covered here. The rest of ConfiguratorSetup has no unit spec
13
+ # at all today (its closest sibling, #validate_threads, is untested too) -- this file scopes
14
+ # itself to the new method rather than backfilling that existing gap.
15
+ describe ConfiguratorSetup do
16
+ before(:each) do
17
+ @configurator_builder = double('ConfiguratorBuilder')
18
+ @configurator_validator = double('ConfiguratorValidator')
19
+ @configurator_plugins = double('ConfiguratorPlugins')
20
+ @loginator = double('Loginator')
21
+ @reportinator = Reportinator.new
22
+ @file_wrapper = double('FileWrapper')
23
+
24
+ @setup = described_class.new(
25
+ {
26
+ configurator_builder: @configurator_builder,
27
+ configurator_validator: @configurator_validator,
28
+ configurator_plugins: @configurator_plugins,
29
+ loginator: @loginator,
30
+ reportinator: @reportinator,
31
+ file_wrapper: @file_wrapper
32
+ }
33
+ )
34
+ end
35
+
36
+ context "#validate_partials" do
37
+ it "accepts an integer at the minimum" do
38
+ config = { partials: { max_extraction_length: 10 } }
39
+ expect(@setup.validate_partials(config)).to be true
40
+ end
41
+
42
+ it "accepts an integer above the minimum" do
43
+ config = { partials: { max_extraction_length: 5000 } }
44
+ expect(@setup.validate_partials(config)).to be true
45
+ end
46
+
47
+ it "rejects a non-integer value" do
48
+ config = { partials: { max_extraction_length: '5000' } }
49
+ expect(@loginator).to receive(:log)
50
+ .with(/:partials ↳ :max_extraction_length is not an integer/, Verbosity::ERRORS)
51
+ expect(@setup.validate_partials(config)).to be false
52
+ end
53
+
54
+ it "rejects an integer below the minimum" do
55
+ config = { partials: { max_extraction_length: 9 } }
56
+ expect(@loginator).to receive(:log)
57
+ .with(/:partials ↳ :max_extraction_length must be at least 10/, Verbosity::ERRORS)
58
+ expect(@setup.validate_partials(config)).to be false
59
+ end
60
+ end
61
+ end
@@ -9,6 +9,7 @@ require 'spec_helper'
9
9
  require 'ceedling/config/configurator'
10
10
  require 'ceedling/ruby_expandinator'
11
11
  require 'ceedling/exceptions'
12
+ require 'ceedling/constants'
12
13
 
13
14
  describe Configurator do
14
15
 
@@ -184,4 +185,48 @@ describe Configurator do
184
185
 
185
186
  end
186
187
 
188
+ describe "#set_partials_derived_config" do
189
+
190
+ def partials_config
191
+ { project: { use_partials: true }, defines: { test: ['TEST'] }, cmock: {} }
192
+ end
193
+
194
+ it "does nothing when :use_partials is disabled" do
195
+ config = partials_config
196
+ config[:project][:use_partials] = false
197
+
198
+ @configurator.set_partials_derived_config( config )
199
+
200
+ expect( config[:defines][:test] ).to eq( ['TEST'] )
201
+ expect( config[:defines] ).to_not have_key( :preprocess )
202
+ end
203
+
204
+ it "appends the partials prefix symbol to a simple :test: defines list" do
205
+ config = partials_config
206
+
207
+ @configurator.set_partials_derived_config( config )
208
+
209
+ expect( config[:defines][:test] ).to include( "CEEDLING_PARTIALS_PREFIX=#{PARTIAL_FILENAME_PREFIX}" )
210
+ end
211
+
212
+ it "leaves :preprocess: defines untouched when the project does not define that section" do
213
+ config = partials_config
214
+
215
+ @configurator.set_partials_derived_config( config )
216
+
217
+ expect( config[:defines] ).to_not have_key( :preprocess )
218
+ end
219
+
220
+ it "appends the partials prefix symbol under the :* matcher when the project defines its own :preprocess: matcher hash" do
221
+ config = partials_config
222
+ config[:defines][:preprocess] = { 'TestSoilMoisture.c' => ['CEEDLING_DELTA_PROBE'] }
223
+
224
+ @configurator.set_partials_derived_config( config )
225
+
226
+ expect( config[:defines][:preprocess][:*] ).to include( "CEEDLING_PARTIALS_PREFIX=#{PARTIAL_FILENAME_PREFIX}" )
227
+ expect( config[:defines][:preprocess]['TestSoilMoisture.c'] ).to eq( ['CEEDLING_DELTA_PROBE'] )
228
+ end
229
+
230
+ end
231
+
187
232
  end
@@ -271,4 +271,21 @@ describe FilePathUtils do
271
271
  end
272
272
  end
273
273
 
274
+ describe '#form_partial_types_header_filename' do
275
+ before(:each) do
276
+ @fpu = described_class.new({
277
+ :configurator => double('configurator'),
278
+ :file_wrapper => double('file_wrapper')
279
+ })
280
+ end
281
+
282
+ it 'forms a filename from the module name, the partial prefix, and a _types suffix' do
283
+ # Mirrors the sibling _interface/_impl header filename conventions -- a shared
284
+ # location for a module's typedefs and aggregate definitions needs a name
285
+ # distinct from those two generated headers so all three can coexist.
286
+ expect( @fpu.form_partial_types_header_filename('LightSensor') )
287
+ .to eq('ceedling_partial_LightSensor_types.h')
288
+ end
289
+ end
290
+
274
291
  end
@@ -21,30 +21,57 @@ describe GeneratorHelper do
21
21
  @helper = described_class.new({ :loginator => @loginator })
22
22
 
23
23
  # A minimal status double for shell_result[:status].
24
- @ok_status = double('status', termsig: nil)
24
+ @ok_status = double('status', termsig: nil, signaled?: false, exitstatus: 0, success?: true)
25
25
  end
26
26
 
27
27
 
28
28
  # ---------------------------------------------------------------------------
29
29
  # Helper: build a shell_result hash
30
30
  # ---------------------------------------------------------------------------
31
- def shell_result(output: VALID_STATS_OUTPUT, stderr: '', termsig: nil)
32
- status = double('status', termsig: termsig)
31
+ # `success` left unspecified derives from `termsig`/`exitstatus` the way a real
32
+ # Process::Status would -- signaled or nonzero-exit is never success. Callers that
33
+ # need an inconsistent combination (a status that looks successful despite a nonzero
34
+ # exit, exercised nowhere here) can still pass `success:` explicitly.
35
+ def shell_result(output: VALID_STATS_OUTPUT, stderr: '', termsig: nil, exitstatus: 0, success: nil)
36
+ success = (termsig.nil? && exitstatus == 0) if success.nil?
37
+ status = double(
38
+ 'status', termsig: termsig, signaled?: !termsig.nil?, exitstatus: exitstatus, success?: success
39
+ )
33
40
  { :output => output, :stderr => stderr, :status => status }
34
41
  end
35
42
 
36
43
 
37
44
  describe '#test_crash?' do
38
45
 
39
- context 'SIGSEGV detection (termsig == 11)' do
40
- it 'returns true when the process was terminated by signal 11' do
46
+ context 'signal death (any signal, not just SIGSEGV)' do
47
+ it 'returns true when the process was terminated by signal 11 (SIGSEGV)' do
41
48
  result = shell_result(termsig: 11)
42
49
  expect( @helper.test_crash?('test_foo.c', 'test_foo.out', result) ).to be true
43
50
  end
44
51
 
45
- it 'does not flag SIGSEGV for other signal numbers' do
46
- result = shell_result(termsig: 6) # SIGABRT
52
+ it 'returns true when the process was terminated by signal 6 (SIGABRT) -- e.g. UBSan/ASan halt_on_error' do
53
+ result = shell_result(termsig: 6)
47
54
  # output still has stats, stderr is empty → only the signal check matters here
55
+ expect( @helper.test_crash?('test_foo.c', 'test_foo.out', result) ).to be true
56
+ end
57
+ end
58
+
59
+ context 'a clean summary contradicted by the real exit status' do
60
+ it 'returns true when 0 failures are reported but the real process exited nonzero and unsignaled' do
61
+ # e.g. LeakSanitizer detecting a leak during teardown, after every test case
62
+ # already printed and Unity's own summary already looked clean.
63
+ result = shell_result(output: VALID_STATS_OUTPUT, exitstatus: 1, success: false)
64
+ expect( @helper.test_crash?('test_foo.c', 'test_foo.out', result) ).to be true
65
+ end
66
+
67
+ it 'returns false when a nonzero real exit is consistent with Unity reporting real failures' do
68
+ failing_output = "\n---------\n2 Tests 1 Failures 0 Ignored\nFAIL\n"
69
+ result = shell_result(output: failing_output, exitstatus: 1, success: false)
70
+ expect( @helper.test_crash?('test_foo.c', 'test_foo.out', result) ).to be false
71
+ end
72
+
73
+ it 'returns false when 0 failures are reported and the real process exited successfully' do
74
+ result = shell_result(output: VALID_STATS_OUTPUT, exitstatus: 0, success: true)
48
75
  expect( @helper.test_crash?('test_foo.c', 'test_foo.out', result) ).to be false
49
76
  end
50
77
  end
@@ -222,6 +222,80 @@ describe GeneratorPartials do
222
222
  end
223
223
  end
224
224
 
225
+ context "#generate_types" do
226
+ it "writes nothing and returns nil when the module has no typedefs or aggregate definitions" do
227
+ allow(@file_wrapper).to receive(:open)
228
+
229
+ c_module = make_module(
230
+ make_stmt(text: "#define FOO 1", line_num: 1),
231
+ make_var(name: 'counter', type: 'int', text: 'int counter;', line_num: 2)
232
+ )
233
+
234
+ result = @generator.generate_types(
235
+ name: 'my_module',
236
+ c_module: c_module,
237
+ output_path: '/path/to/output'
238
+ )
239
+
240
+ expect(@file_wrapper).not_to have_received(:open)
241
+ expect(result).to be_nil
242
+ end
243
+
244
+ it "writes only the typedefs and aggregate definitions, guarded and in element_sequence order, and returns the bare filename" do
245
+ file_contents = <<~CONTENTS
246
+ #ifndef __CEEDLING_GENERATED_MY_MODULE_TYPES_H__
247
+ #define __CEEDLING_GENERATED_MY_MODULE_TYPES_H__
248
+
249
+ typedef uint8_t Byte;
250
+ struct Config { int id; };
251
+
252
+ #endif // __CEEDLING_GENERATED_MY_MODULE_TYPES_H__
253
+
254
+ CONTENTS
255
+
256
+ output_path = '/path/to/output'
257
+ name = 'my_module'
258
+ header_filename = 'my_module_types.h'
259
+ expected_filepath = File.join(output_path, header_filename)
260
+
261
+ allow(@file_path_utils).to receive(:form_partial_types_header_filename)
262
+ .with(name)
263
+ .and_return(header_filename)
264
+
265
+ buf = StringIO.new()
266
+ allow(@file_wrapper).to receive(:open)
267
+ .with(expected_filepath, 'wb')
268
+ .and_yield(buf)
269
+
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.
274
+ 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)
277
+ aggregate_stmt = CExtractorTypes::CStatement.new(text: "struct Config { int id; };", line_num: 4)
278
+
279
+ c_module = CExtractorTypes::CModule.new(
280
+ type_definitions: [typedef_stmt],
281
+ macro_definitions: [macro_stmt],
282
+ variable_declarations: [var_decl],
283
+ aggregate_definitions: [aggregate_stmt],
284
+ element_sequence: [typedef_stmt, macro_stmt, var_decl, aggregate_stmt]
285
+ )
286
+
287
+ result = @generator.generate_types(
288
+ name: name,
289
+ c_module: c_module,
290
+ output_path: output_path
291
+ )
292
+
293
+ expect( buf.string.strip() ).to eq file_contents.strip()
294
+ expect(@file_wrapper).to have_received(:open).with(expected_filepath, 'wb')
295
+ expect(result).to eq(header_filename)
296
+ end
297
+ end
298
+
225
299
  context "#generate_header (private method)" do
226
300
  # Define common StringIO buffer
227
301
  let(:buf) { StringIO.new() }
@@ -282,6 +356,24 @@ describe GeneratorPartials do
282
356
  expect( buf.string.strip() ).to eq file_contents.strip()
283
357
  end
284
358
 
359
+ it "should reapply a leading qualifier decorator to an extern declaration" do
360
+ c_module = make_module(
361
+ make_var(name: 'cmd', type: 'cmd_t', text: 'cmd_t cmd = { 0 };', decorators: ['static', 'volatile'])
362
+ )
363
+ @generator.send(:generate_header, buf, 'mymod', [], [], c_module, true)
364
+
365
+ expect(buf.string).to include('extern volatile cmd_t cmd;')
366
+ end
367
+
368
+ it "should not reapply a qualifier already embedded in a pointer-level declarator" do
369
+ c_module = make_module(
370
+ make_var(name: 'lookup_table', type: 'uint8_t* const', text: 'uint8_t* const lookup_table;', decorators: ['static', 'const'])
371
+ )
372
+ @generator.send(:generate_header, buf, 'mymod', [], [], c_module, true)
373
+
374
+ expect(buf.string).to include('extern const uint8_t* const lookup_table;')
375
+ end
376
+
285
377
  it "should generate a header file with #include statements, variable declarations, and function signatures" do
286
378
  file_contents = <<~CONTENTS
287
379
  #ifndef __CEEDLING_GENERATED_APPLES_AND_BANANAS_H__
@@ -360,23 +452,29 @@ describe GeneratorPartials do
360
452
  expect( buf.string.strip() ).to eq file_contents.strip()
361
453
  end
362
454
 
363
- it "should emit all four injectable statement categories correctly in element_sequence order" do
364
- # One item of each category that can be injected into a generated Partial header:
365
- # macro_definitions → CStatement emitted as-is
366
- # type_definitions → CStatement emitted as-is
367
- # aggregate_definitions → CStatement emitted as-is
368
- # variable_declarations → CVariableDeclaration emitted as extern declaration
455
+ it "should emit macro and variable statements inline while routing typedefs and aggregates to the shared types header" do
456
+ # One item of each category that can appear in a generated Partial header:
457
+ # macro_definitions → CStatement emitted as-is, inline
458
+ # type_definitions → CStatement skipped here; belongs to generate_types instead
459
+ # aggregate_definitions → CStatement skipped here; belongs to generate_types instead
460
+ # variable_declarations → CVariableDeclaration emitted as extern declaration, inline
461
+ #
462
+ # A typedef or aggregate defines a type, and C rejects a second definition of the same
463
+ # type in one translation unit even when it's textually identical -- unlike a macro,
464
+ # which may legally repeat. Since a module's implementation and interface headers can
465
+ # both be included in the same test file, generate_header always defers type-defining
466
+ # content to the one shared types header generate_types produces, rather than risking
467
+ # two independent copies landing in the same translation unit.
369
468
  #
370
469
  # Ordering is intentionally interleaved (not grouped by category) to confirm that
371
- # element_sequence — not typed-array membership — governs emit order.
470
+ # element_sequence — not typed-array membership — governs emit order for the items
471
+ # that do remain inline.
372
472
  file_contents = <<~CONTENTS
373
473
  #ifndef __CEEDLING_GENERATED_ALL_STATEMENTS_H__
374
474
  #define __CEEDLING_GENERATED_ALL_STATEMENTS_H__
375
475
 
376
476
  #define MAX_ITEMS 16
377
- typedef uint8_t Byte;
378
477
  extern int item_count;
379
- struct Config { int id; int flags; };
380
478
 
381
479
  #endif // __CEEDLING_GENERATED_ALL_STATEMENTS_H__
382
480
 
@@ -443,13 +541,11 @@ describe GeneratorPartials do
443
541
  expect( buf.string.strip() ).to eq file_contents.strip()
444
542
  end
445
543
 
446
- it "should interleave functions with other elements in element_sequence order" do
544
+ it "should interleave functions with other elements in element_sequence order, skipping a leading typedef routed to the shared types header" do
447
545
  file_contents = <<~CONTENTS
448
546
  #ifndef __CEEDLING_GENERATED_INTERLEAVED_H__
449
547
  #define __CEEDLING_GENERATED_INTERLEAVED_H__
450
548
 
451
- typedef uint8_t Byte;
452
-
453
549
  void foo(void);
454
550
 
455
551
  extern int counter;
@@ -460,6 +556,8 @@ describe GeneratorPartials do
460
556
 
461
557
  CONTENTS
462
558
 
559
+ # typedef_stmt is a member of type_definitions, so generate_header leaves it out here;
560
+ # it's the sole responsibility of generate_types (see the "shared types header" tests)
463
561
  typedef_stmt = make_stmt(text: "typedef uint8_t Byte;", line_num: 1)
464
562
  var_decl = make_var( name: 'counter', type: 'int', text: 'int counter;', line_num: 5)
465
563
 
@@ -568,6 +666,16 @@ describe GeneratorPartials do
568
666
  expect( buf.string.strip() ).to eq file_contents.strip()
569
667
  end
570
668
 
669
+ it "should reapply a leading qualifier decorator to a variable definition" do
670
+ c_module = make_module(
671
+ make_var(name: 'cmd', type: 'cmd_t', text: 'cmd_t cmd = { 0 };', decorators: ['static', 'volatile'])
672
+ )
673
+
674
+ @generator.send(:generate_source, buf, [], [], c_module)
675
+
676
+ expect(buf.string).to include('volatile cmd_t cmd = { 0 };')
677
+ end
678
+
571
679
  it "should generate a source file with include directives, variable declarations, and functions" do
572
680
  file_contents = <<~CONTENTS
573
681
  // Ceeding generated file