ceedling 1.1.3 → 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 (165) 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_ub_shift_overflow.c +22 -0
  13. data/assets/test_partial_all_module.c +16 -0
  14. data/assets/test_same_dir_pairing.c +23 -0
  15. data/docs/Changelog.md +21 -1
  16. data/docs/mkdocs/configuration/reference/index.md +8 -0
  17. data/docs/mkdocs/configuration/reference/partials.md +38 -0
  18. data/examples/wondrous_forest/src/LightSensor.c +21 -4
  19. data/examples/wondrous_forest/src/LightSensor.h +12 -4
  20. data/examples/wondrous_forest/test/TestLightSensor.c +34 -4
  21. data/lib/ceedling/c_extractor/c_extractor.rb +37 -11
  22. data/lib/ceedling/c_extractor/c_extractor_constants.rb +0 -3
  23. data/lib/ceedling/c_extractor/c_extractor_declarations.rb +26 -25
  24. data/lib/ceedling/c_extractor/c_extractor_functions.rb +5 -12
  25. data/lib/ceedling/config/configurator.rb +1 -0
  26. data/lib/ceedling/config/configurator_setup.rb +26 -0
  27. data/lib/ceedling/defaults.rb +8 -0
  28. data/lib/ceedling/file_path_utils.rb +7 -0
  29. data/lib/ceedling/file_wrapper.rb +8 -0
  30. data/lib/ceedling/generators/generator.rb +15 -0
  31. data/lib/ceedling/generators/generator_helper.rb +25 -6
  32. data/lib/ceedling/generators/generator_partials.rb +84 -3
  33. data/lib/ceedling/generators/generator_test_results_backtrace.rb +183 -83
  34. data/lib/ceedling/includes/includes.rb +66 -11
  35. data/lib/ceedling/objects.yml +5 -0
  36. data/lib/ceedling/partials/partializer.rb +18 -3
  37. data/lib/ceedling/partials/partializer_utils.rb +6 -1
  38. data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +43 -16
  39. data/lib/ceedling/test_invoker/test_build_executor.rb +19 -8
  40. data/lib/version.rb +1 -1
  41. data/mkdocs.yml +1 -0
  42. data/site-local/404.html +30 -0
  43. data/site-local/configuration/environment-vars.html +30 -0
  44. data/site-local/configuration/global-collections.html +30 -0
  45. data/site-local/configuration/index.html +30 -0
  46. data/site-local/configuration/loading.html +30 -0
  47. data/site-local/configuration/mixins.html +30 -0
  48. data/site-local/configuration/parallel-builds.html +30 -0
  49. data/site-local/configuration/project-file.html +30 -0
  50. data/site-local/configuration/reference/cexception.html +30 -0
  51. data/site-local/configuration/reference/cmock.html +30 -0
  52. data/site-local/configuration/reference/defines.html +30 -0
  53. data/site-local/configuration/reference/environment.html +30 -0
  54. data/site-local/configuration/reference/extension.html +30 -0
  55. data/site-local/configuration/reference/files.html +30 -0
  56. data/site-local/configuration/reference/flags.html +30 -0
  57. data/site-local/configuration/reference/index.html +36 -0
  58. data/site-local/configuration/reference/libraries.html +30 -0
  59. data/site-local/configuration/reference/mixins.html +30 -0
  60. data/site-local/configuration/reference/partials.html +4257 -0
  61. data/site-local/configuration/reference/paths.html +30 -0
  62. data/site-local/configuration/reference/plugins.html +30 -0
  63. data/site-local/configuration/reference/project.html +30 -0
  64. data/site-local/configuration/reference/release-build.html +31 -1
  65. data/site-local/configuration/reference/test-build.html +31 -1
  66. data/site-local/configuration/reference/test-runner.html +30 -0
  67. data/site-local/configuration/reference/tools.html +30 -0
  68. data/site-local/configuration/reference/unity.html +30 -0
  69. data/site-local/configuration/which-ceedling.html +30 -0
  70. data/site-local/development/index.html +30 -0
  71. data/site-local/development/plugins/configuration.html +30 -0
  72. data/site-local/development/plugins/index.html +30 -0
  73. data/site-local/development/plugins/plugin-subclass.html +30 -0
  74. data/site-local/development/plugins/rake-tasks.html +30 -0
  75. data/site-local/development/workflow.html +30 -0
  76. data/site-local/getting-started/command-line.html +30 -0
  77. data/site-local/getting-started/example-projects/cipher-quest.html +30 -0
  78. data/site-local/getting-started/example-projects/index.html +30 -0
  79. data/site-local/getting-started/example-projects/temp-sensor.html +30 -0
  80. data/site-local/getting-started/example-projects/wondrous-forest.html +30 -0
  81. data/site-local/getting-started/index.html +30 -0
  82. data/site-local/getting-started/installation.html +30 -0
  83. data/site-local/getting-started/quick-start.html +30 -0
  84. data/site-local/help.html +30 -0
  85. data/site-local/index.html +30 -0
  86. data/site-local/overview/build-system.html +30 -0
  87. data/site-local/overview/index.html +30 -0
  88. data/site-local/overview/test-environments.html +30 -0
  89. data/site-local/overview/tools-and-frameworks.html +30 -0
  90. data/site-local/plugins/beep.html +30 -0
  91. data/site-local/plugins/bullseye.html +30 -0
  92. data/site-local/plugins/command-hooks.html +30 -0
  93. data/site-local/plugins/compile-commands-json-db.html +30 -0
  94. data/site-local/plugins/cppcheck.html +30 -0
  95. data/site-local/plugins/dependencies.html +30 -0
  96. data/site-local/plugins/fff.html +30 -0
  97. data/site-local/plugins/gcov/examples.html +30 -0
  98. data/site-local/plugins/gcov/gcovr.html +30 -0
  99. data/site-local/plugins/gcov/index.html +30 -0
  100. data/site-local/plugins/gcov/overview.html +30 -0
  101. data/site-local/plugins/gcov/reportgenerator.html +30 -0
  102. data/site-local/plugins/gcov/reporting.html +30 -0
  103. data/site-local/plugins/gcov/setup.html +30 -0
  104. data/site-local/plugins/gcov/tool-versions.html +30 -0
  105. data/site-local/plugins/gcov/troubleshooting.html +30 -0
  106. data/site-local/plugins/index.html +30 -0
  107. data/site-local/plugins/module-generator.html +30 -0
  108. data/site-local/plugins/report-build-warnings-log.html +30 -0
  109. data/site-local/plugins/report-tests-gtestlike-stdout.html +30 -0
  110. data/site-local/plugins/report-tests-ide-stdout.html +30 -0
  111. data/site-local/plugins/report-tests-log-factory.html +30 -0
  112. data/site-local/plugins/report-tests-pretty-stdout.html +30 -0
  113. data/site-local/plugins/report-tests-raw-output-log.html +30 -0
  114. data/site-local/plugins/report-tests-teamcity-stdout.html +30 -0
  115. data/site-local/plugins/valgrind.html +30 -0
  116. data/site-local/project/release-history.html +30 -0
  117. data/site-local/project/upgrade.html +30 -0
  118. data/site-local/reference/build-directives.html +30 -0
  119. data/site-local/reference/command-line.html +30 -0
  120. data/site-local/reference/environment-vars.html +30 -0
  121. data/site-local/reference/gcov-plugin.html +30 -0
  122. data/site-local/reference/global-collections.html +30 -0
  123. data/site-local/reference/index.html +30 -0
  124. data/site-local/reference/partials-macros.html +30 -0
  125. data/site-local/reference/project-configuration.html +30 -0
  126. data/site-local/sitemap.xml.gz +0 -0
  127. data/site-local/snapshot/examples/cipher_quest/index.html +30 -0
  128. data/site-local/snapshot/examples/temp_sensor/index.html +30 -0
  129. data/site-local/snapshot/examples/wondrous_forest/index.html +30 -0
  130. data/site-local/testing-guide/build-directives.html +30 -0
  131. data/site-local/testing-guide/conventions.html +30 -0
  132. data/site-local/testing-guide/frameworks.html +30 -0
  133. data/site-local/testing-guide/index.html +30 -0
  134. data/site-local/testing-guide/partials/configuration.html +30 -0
  135. data/site-local/testing-guide/partials/conventions.html +30 -0
  136. data/site-local/testing-guide/partials/directives.html +30 -0
  137. data/site-local/testing-guide/partials/example.html +30 -0
  138. data/site-local/testing-guide/partials/index.html +30 -0
  139. data/site-local/testing-guide/partials/overview.html +30 -0
  140. data/site-local/testing-guide/partials/variables.html +30 -0
  141. data/site-local/testing-guide/test-cases.html +30 -0
  142. data/site-local/testing-guide/test-sample.html +30 -0
  143. data/site-local/testing-guide/test-suite-anatomy.html +30 -0
  144. data/spec/support/system/spec_system_helper.rb +25 -0
  145. data/spec/system/deployment_as_gem_spec.rb +12 -0
  146. data/spec/system/deployment_as_vendor_spec.rb +17 -0
  147. data/spec/system/encoding_stress_spec.rb +4 -4
  148. data/spec/system/example_wondrous_forest_spec.rb +2 -2
  149. data/spec/system/support/common_test_cases.rb +137 -0
  150. data/spec/units/c_extractor/c_extractor_declarations_spec.rb +27 -4
  151. data/spec/units/c_extractor/c_extractor_functions_spec.rb +1 -11
  152. data/spec/units/c_extractor/c_extractor_integration_spec.rb +18 -11
  153. data/spec/units/c_extractor/c_extractor_spec.rb +57 -1
  154. data/spec/units/config/configurator_setup_spec.rb +61 -0
  155. data/spec/units/file_path_utils_spec.rb +17 -0
  156. data/spec/units/generators/generator_helper_spec.rb +34 -7
  157. data/spec/units/generators/generator_partials_spec.rb +120 -12
  158. data/spec/units/generators/generator_test_results_backtrace_spec.rb +200 -37
  159. data/spec/units/includes/include_spec.rb +30 -18
  160. data/spec/units/includes/includes_spec.rb +100 -10
  161. data/spec/units/partials/partializer_spec.rb +113 -1
  162. data/spec/units/partials/partializer_utils_spec.rb +11 -0
  163. data/spec/units/preprocess/preprocessinator_includes_handler_spec.rb +96 -7
  164. data/spec/units/system_wrapper_spec.rb +72 -0
  165. metadata +19 -2
@@ -1865,6 +1865,8 @@
1865
1865
 
1866
1866
 
1867
1867
 
1868
+
1869
+
1868
1870
 
1869
1871
 
1870
1872
 
@@ -2012,6 +2014,34 @@
2012
2014
 
2013
2015
 
2014
2016
 
2017
+ <li class="md-nav__item">
2018
+ <a href="../configuration/reference/partials.html" class="md-nav__link">
2019
+
2020
+
2021
+
2022
+ <span class="md-ellipsis">
2023
+
2024
+
2025
+ Partials Settings
2026
+
2027
+
2028
+
2029
+ </span>
2030
+
2031
+
2032
+
2033
+ </a>
2034
+ </li>
2035
+
2036
+
2037
+
2038
+
2039
+
2040
+
2041
+
2042
+
2043
+
2044
+
2015
2045
  <li class="md-nav__item">
2016
2046
  <a href="../configuration/reference/release-build.html" class="md-nav__link">
2017
2047
 
@@ -1876,6 +1876,8 @@
1876
1876
 
1877
1877
 
1878
1878
 
1879
+
1880
+
1879
1881
 
1880
1882
 
1881
1883
 
@@ -2023,6 +2025,34 @@
2023
2025
 
2024
2026
 
2025
2027
 
2028
+ <li class="md-nav__item">
2029
+ <a href="../configuration/reference/partials.html" class="md-nav__link">
2030
+
2031
+
2032
+
2033
+ <span class="md-ellipsis">
2034
+
2035
+
2036
+ Partials Settings
2037
+
2038
+
2039
+
2040
+ </span>
2041
+
2042
+
2043
+
2044
+ </a>
2045
+ </li>
2046
+
2047
+
2048
+
2049
+
2050
+
2051
+
2052
+
2053
+
2054
+
2055
+
2026
2056
  <li class="md-nav__item">
2027
2057
  <a href="../configuration/reference/release-build.html" class="md-nav__link">
2028
2058
 
@@ -185,6 +185,21 @@ def valgrind_available?
185
185
  tool_available?('valgrind --version 2>&1')
186
186
  end
187
187
 
188
+ # Real UBSan support is Linux-specific in practice for this test suite -- macOS's
189
+ # system `gcc` is really Clang under a compatibility shim with different sanitizer
190
+ # runtime behavior, and MinGW gcc on Windows doesn't ship the sanitizer runtime at
191
+ # all. Restricting to Linux avoids flaky, platform-dependent compiler probing.
192
+ def ubsan_available?
193
+ return false unless RUBY_PLATFORM.downcase.include?('linux')
194
+
195
+ Dir.mktmpdir do |dir|
196
+ source = File.join(dir, 'ubsan_check.c')
197
+ binary = File.join(dir, 'ubsan_check')
198
+ File.write(source, "int main(void) { return 0; }\n")
199
+ tool_available?(%(gcc -fsanitize=undefined -o "#{binary}" "#{source}" 2>&1))
200
+ end
201
+ end
202
+
188
203
  RSpec.shared_context "requires gdb" do
189
204
  before :all do
190
205
  @gdb_available = gdb_available?
@@ -204,3 +219,13 @@ RSpec.shared_context "requires valgrind" do
204
219
  skip "valgrind is not installed or not in PATH" unless @valgrind_available
205
220
  end
206
221
  end
222
+
223
+ RSpec.shared_context "requires gcc with UBSan" do
224
+ before :all do
225
+ @ubsan_available = ubsan_available?
226
+ end
227
+
228
+ before do
229
+ skip "gcc UBSan support not available (Linux + gcc -fsanitize=undefined required)" unless @ubsan_available
230
+ end
231
+ end
@@ -62,6 +62,11 @@ ceedling_system_tests do
62
62
  test_case :test_project_with_preprocessing_for_mocks
63
63
  test_case :test_project_with_preprocessing_for_missing_mock
64
64
  test_case :test_project_with_preprocessing_all
65
+ test_case :test_project_preprocessing_same_dir_mock_pairing
66
+ end
67
+
68
+ describe "Partials" do
69
+ test_case :test_project_partial_all_module_mocks_declaration_only_function
65
70
  end
66
71
 
67
72
  describe "Defines and configuration" do
@@ -92,6 +97,13 @@ ceedling_system_tests do
92
97
  test_case :crash_simple_sigsegv_all_test_cases
93
98
  test_case :crash_simple_sigabrt_assert_failure
94
99
  test_case :crash_simple_sigsegv_with_parameterized_test
100
+ test_case :crash_simple_diagnostic_retry_disagrees_with_main_fixture
101
+ end
102
+
103
+ describe "Crash handling with real UBSan" do
104
+ include_context "requires gcc with UBSan"
105
+
106
+ test_case :crash_simple_ubsan_diagnostic_retry_disagrees_with_main_fixture
95
107
  end
96
108
 
97
109
  describe "Backtrace with GDB" do
@@ -65,6 +65,11 @@ ceedling_system_tests do
65
65
  test_case :test_project_with_preprocessing_for_mocks
66
66
  test_case :test_project_with_preprocessing_for_missing_mock
67
67
  test_case :test_project_with_preprocessing_all
68
+ test_case :test_project_preprocessing_same_dir_mock_pairing
69
+ end
70
+
71
+ describe "Partials" do
72
+ test_case :test_project_partial_all_module_mocks_declaration_only_function
68
73
  end
69
74
 
70
75
  describe "Defines and configuration" do
@@ -95,6 +100,13 @@ ceedling_system_tests do
95
100
  test_case :crash_simple_sigsegv_all_test_cases
96
101
  test_case :crash_simple_sigabrt_assert_failure
97
102
  test_case :crash_simple_sigsegv_with_parameterized_test
103
+ test_case :crash_simple_diagnostic_retry_disagrees_with_main_fixture
104
+ end
105
+
106
+ describe "Crash handling with real UBSan" do
107
+ include_context "requires gcc with UBSan"
108
+
109
+ test_case :crash_simple_ubsan_diagnostic_retry_disagrees_with_main_fixture
98
110
  end
99
111
 
100
112
  describe "Backtrace with GDB" do
@@ -172,6 +184,11 @@ ceedling_system_tests do
172
184
  test_case :test_project_with_preprocessing_for_mocks
173
185
  test_case :test_project_with_preprocessing_for_missing_mock
174
186
  test_case :test_project_with_preprocessing_all
187
+ test_case :test_project_preprocessing_same_dir_mock_pairing
188
+ end
189
+
190
+ describe "Partials" do
191
+ test_case :test_project_partial_all_module_mocks_declaration_only_function
175
192
  end
176
193
 
177
194
  describe "Defines and configuration" do
@@ -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+65/)
135
- expect(output).to match(/PASSED:\s+65/)
134
+ expect(output).to match(/TESTED:\s+67/)
135
+ expect(output).to match(/PASSED:\s+67/)
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+65/)
148
- expect(output).to match(/PASSED:\s+65/)
147
+ expect(output).to match(/TESTED:\s+67/)
148
+ expect(output).to match(/PASSED:\s+67/)
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+65/)
50
- expect(@output).to match(/PASSED:\s+65/)
49
+ expect(@output).to match(/TESTED:\s+67/)
50
+ expect(@output).to match(/PASSED:\s+67/)
51
51
  expect(@output).to match(/FAILED:\s+0/)
52
52
  end
53
53
  end
@@ -5,6 +5,8 @@
5
5
  # SPDX-License-Identifier: MIT
6
6
  # =========================================================================
7
7
 
8
+ require 'rbconfig'
9
+
8
10
  module CommonSystemTestCases
9
11
  def can_report_version_no_git_commit_sha
10
12
  @c.with_context do
@@ -437,6 +439,69 @@ module CommonSystemTestCases
437
439
  end
438
440
  end
439
441
 
442
+ # A module splits its function declarations (same_dir_pairing_module_func.h, mocked by
443
+ # this test) from its type declarations (same_dir_pairing_module.h, reached only
444
+ # transitively via same_dir_pairing_other.h, never included directly by the test). All
445
+ # of these files are colocated in the test's own directory, which is what exercises
446
+ # GCC's same-directory quoted #include resolution during bare-includes extraction.
447
+ # Auto source-pairing must not pull the real source implementing the type header's
448
+ # like-named stem into this build alongside its mock of the differently-named header.
449
+ def test_project_preprocessing_same_dir_mock_pairing
450
+ @c.with_context do
451
+ Dir.chdir @proj_name do
452
+ FileUtils.cp test_asset_path("same_dir_pairing_module.h"), 'test/'
453
+ FileUtils.cp test_asset_path("same_dir_pairing_module_func.h"), 'test/'
454
+ FileUtils.cp test_asset_path("same_dir_pairing_module.c"), 'test/'
455
+ FileUtils.cp test_asset_path("same_dir_pairing_other.h"), 'test/'
456
+ FileUtils.cp test_asset_path("same_dir_pairing_other.c"), 'test/'
457
+ FileUtils.cp test_asset_path("test_same_dir_pairing.c"), 'test/'
458
+
459
+ settings = {
460
+ :project => { :use_test_preprocessor => :all },
461
+ :paths => {
462
+ :source => ['src/**', 'test/**'],
463
+ :include => ['src/**', 'test/**']
464
+ }
465
+ }
466
+ @c.merge_project_yml_for_test(settings)
467
+
468
+ output = @c.ceedling_build_exec("test:same_dir_pairing")
469
+ expect(@c.last_exit_status).to eq(0) # Successful build and tests
470
+ expect(output).to match(/TESTED:\s+1/)
471
+ expect(output).to match(/PASSED:\s+1/)
472
+ expect(output).to match(/FAILED:\s+0/)
473
+ expect(output).not_to match(/multiple definition|duplicate symbol/)
474
+ end
475
+ end
476
+ end
477
+
478
+ # A module header with both a plain (non-inline) function prototype and a `static
479
+ # inline` function, with no paired .c source providing a body for the plain prototype
480
+ # (so it exists only as a bare declaration, never a definition). MOCK_PARTIAL_ALL_MODULE()
481
+ # must mock both functions, not just the one with a body.
482
+ def test_project_partial_all_module_mocks_declaration_only_function
483
+ @c.with_context do
484
+ Dir.chdir @proj_name do
485
+ FileUtils.cp test_asset_path("partial_all_module.h"), 'src/'
486
+ FileUtils.cp test_asset_path("test_partial_all_module.c"), 'test/'
487
+
488
+ settings = {
489
+ :project => {
490
+ :use_partials => true,
491
+ :use_test_preprocessor => :all
492
+ }
493
+ }
494
+ @c.merge_project_yml_for_test(settings)
495
+
496
+ output = @c.ceedling_build_exec("test:partial_all_module")
497
+ expect(@c.last_exit_status).to eq(0) # Successful build and tests
498
+ expect(output).to match(/TESTED:\s+1/)
499
+ expect(output).to match(/PASSED:\s+1/)
500
+ expect(output).to match(/FAILED:\s+0/)
501
+ end
502
+ end
503
+ end
504
+
440
505
  def test_project_fail
441
506
  @c.with_context do
442
507
  Dir.chdir @proj_name do
@@ -982,6 +1047,78 @@ module CommonSystemTestCases
982
1047
  end
983
1048
  end
984
1049
 
1050
+ # Regression test for issue #1198: a custom :test_fixture tool (e.g. a wrapper
1051
+ # enabling a sanitizer's halt-on-error) can correctly detect a crash on the main run,
1052
+ # only for that crash to be silently overwritten by a :simple backtrace diagnostic
1053
+ # retry that runs through a *different*, independently-configured tool
1054
+ # (test_fixture_simple_backtrace) and behaves differently there -- legitimately
1055
+ # passing in that other environment. fake_crash_test_fixture.rb reproduces the
1056
+ # mismatch without needing a real sanitizer: it always exits nonzero without ever
1057
+ # running the real, bug-free test executable it's given, while the unmodified default
1058
+ # backtrace retry tool runs that same executable directly and passes.
1059
+ def crash_simple_diagnostic_retry_disagrees_with_main_fixture
1060
+ @c.with_context do
1061
+ Dir.chdir @proj_name do
1062
+ FileUtils.cp test_asset_path("example_file.h"), 'src/'
1063
+ FileUtils.cp test_asset_path("example_file.c"), 'src/'
1064
+ FileUtils.cp test_asset_path("test_example_file_success.c"), 'test/'
1065
+ FileUtils.cp test_asset_path("fake_crash_test_fixture.rb"), 'test/'
1066
+
1067
+ @c.merge_project_yml_for_test({
1068
+ :project => { :use_backtrace => :simple },
1069
+ :tools => {
1070
+ :test_fixture => {
1071
+ :executable => RbConfig.ruby,
1072
+ :arguments => ['test/fake_crash_test_fixture.rb']
1073
+ }
1074
+ }
1075
+ })
1076
+
1077
+ output = @c.ceedling_build_exec("test:all")
1078
+ expect(@c.last_exit_status).not_to eq(0)
1079
+ expect(output).to match(/Unit test failures/)
1080
+ expect(output).not_to match(/PASSED:\s+1/)
1081
+ end
1082
+ end
1083
+ end
1084
+
1085
+ # Same regression coverage as crash_simple_diagnostic_retry_disagrees_with_main_fixture
1086
+ # above, but with a real UBSan-triggered crash (matching issue #1198's own
1087
+ # reproduction exactly) instead of the portable simulated fixture. UBSan doesn't
1088
+ # produce an OS-level crash signal the way a segfault does -- run_with_ubsan.rb's
1089
+ # halt_on_error setting is what turns the undefined behavior into a hard stop, and
1090
+ # only the main :test_fixture invocation goes through that wrapper. Requires a Linux
1091
+ # gcc toolchain with -fsanitize=undefined support -- see "requires gcc with UBSan".
1092
+ def crash_simple_ubsan_diagnostic_retry_disagrees_with_main_fixture
1093
+ @c.with_context do
1094
+ Dir.chdir @proj_name do
1095
+ FileUtils.cp test_asset_path("test_example_file_ub_shift_overflow.c"), 'test/'
1096
+ FileUtils.cp test_asset_path("run_with_ubsan.rb"), 'test/'
1097
+
1098
+ @c.merge_project_yml_for_test({
1099
+ :project => { :use_backtrace => :simple },
1100
+ :flags => {
1101
+ :test => {
1102
+ :compile => { '*' => ['-fsanitize=undefined'] },
1103
+ :link => { '*' => ['-fsanitize=undefined'] }
1104
+ }
1105
+ },
1106
+ :tools => {
1107
+ :test_fixture => {
1108
+ :executable => RbConfig.ruby,
1109
+ :arguments => ['test/run_with_ubsan.rb', '${1}']
1110
+ }
1111
+ }
1112
+ })
1113
+
1114
+ output = @c.ceedling_build_exec("test:all")
1115
+ expect(@c.last_exit_status).not_to eq(0)
1116
+ expect(output).to match(/Unit test failures/)
1117
+ expect(output).not_to match(/PASSED:\s+1/)
1118
+ end
1119
+ end
1120
+ end
1121
+
985
1122
  def project_with_test_file_directly_including_source_file
986
1123
  @c.with_context do
987
1124
  Dir.chdir @proj_name do
@@ -19,11 +19,10 @@ describe CExtractorDeclarations do
19
19
  describe "#try_extract_variable" do
20
20
  # Helper to create extractor and test variable extraction
21
21
  let(:extract_variable) do
22
- ->(content, max_line_length=1000) do
22
+ ->(content) do
23
23
  scanner = StringScanner.new(content)
24
24
  declarations = CExtractorDeclarations.new({ c_extractor_code_text: CExtractorCodeText.new() })
25
25
  declarations.setup()
26
- declarations.max_line_length = max_line_length
27
26
  success, variable = declarations.try_extract_variable(scanner)
28
27
  return [success, variable, scanner.pos, scanner.rest]
29
28
  end
@@ -194,11 +193,35 @@ describe CExtractorDeclarations do
194
193
  success, variable, pos, rest = extract_variable.call(content)
195
194
 
196
195
  expect(success).to be true
197
- # Leading `const` is a decorator; gsub removes all `const` occurrences
198
- check_single(variable, name: 'ptr', type: 'int*', decorators: ['const'], text: 'int* ptr;')
196
+ # Leading `const` (qualifying `int`) is a decorator; the second `const`
197
+ # (qualifying the pointer itself) is part of the declarator and stays put
198
+ check_single(variable, name: 'ptr', type: 'int* const', decorators: ['const'], text: 'int* const ptr;')
199
199
  expect(pos).to eq(21)
200
200
  expect(rest).to eq("")
201
201
  end
202
+
203
+ it "extracts volatile pointer to volatile" do
204
+ content = "volatile int* volatile ptr;"
205
+ success, variable, pos, rest = extract_variable.call(content)
206
+
207
+ expect(success).to be true
208
+ # Same as const/const above: only the leading qualifier is a decorator
209
+ check_single(variable, name: 'ptr', type: 'int* volatile', decorators: ['volatile'], text: 'int* volatile ptr;')
210
+ expect(pos).to eq(27)
211
+ expect(rest).to eq("")
212
+ end
213
+
214
+ it "extracts static const pointer to const" do
215
+ content = "static const int* const ptr;"
216
+ success, variable, pos, rest = extract_variable.call(content)
217
+
218
+ expect(success).to be true
219
+ # `static` and the leading `const` are decorators; the trailing `const`
220
+ # on the pointer itself is retained in both `.type` and `.text`
221
+ check_single(variable, name: 'ptr', type: 'int* const', decorators: ['static', 'const'], text: 'int* const ptr;')
222
+ expect(pos).to eq(28)
223
+ expect(rest).to eq("")
224
+ end
202
225
  end
203
226
 
204
227
  context "array variable declarations" do
@@ -20,11 +20,10 @@ describe CExtractorFunctions do
20
20
  describe "#extract_function_signature (private method testing)" do
21
21
  # Helper to access private method
22
22
  let(:extract_signature) do
23
- ->(content, type, max_line_length=1000) do
23
+ ->(content, type) do
24
24
  scanner = StringScanner.new(content)
25
25
  functions = CExtractorFunctions.new({ c_extractor_code_text: CExtractorCodeText.new() })
26
26
  functions.setup()
27
- functions.max_line_length = max_line_length
28
27
  signature = functions.send( :extract_function_signature, scanner, type )
29
28
  return [signature, scanner.pos, scanner.rest]
30
29
  end
@@ -797,7 +796,6 @@ describe CExtractorFunctions do
797
796
  ->(signature) do
798
797
  functions = CExtractorFunctions.new({ c_extractor_code_text: CExtractorCodeText.new() })
799
798
  functions.setup()
800
- functions.max_line_length = 1000
801
799
  name = functions.send( :extract_function_name, signature )
802
800
  return name
803
801
  end
@@ -1485,7 +1483,6 @@ describe CExtractorFunctions do
1485
1483
  scanner = StringScanner.new(content)
1486
1484
  functions = CExtractorFunctions.new({ c_extractor_code_text: CExtractorCodeText.new() })
1487
1485
  functions.setup()
1488
- functions.max_line_length = 1000
1489
1486
  success, func = functions.try_extract_function_definition( scanner, filepath )
1490
1487
  return [success, func, scanner.pos, scanner.rest]
1491
1488
  end
@@ -2036,7 +2033,6 @@ describe CExtractorFunctions do
2036
2033
  scanner = StringScanner.new(content)
2037
2034
  functions = CExtractorFunctions.new({ c_extractor_code_text: CExtractorCodeText.new() })
2038
2035
  functions.setup()
2039
- functions.max_line_length = 1000
2040
2036
 
2041
2037
  success1, func1 = functions.try_extract_function_definition(scanner, filepath)
2042
2038
  expect(success1).to be true
@@ -2067,7 +2063,6 @@ describe CExtractorFunctions do
2067
2063
  code_text = CExtractorCodeText.new()
2068
2064
  functions = CExtractorFunctions.new({ c_extractor_code_text: code_text })
2069
2065
  functions.setup()
2070
- functions.max_line_length = 1000
2071
2066
 
2072
2067
  success1, func1 = functions.try_extract_function_definition(scanner, filepath)
2073
2068
  expect(success1).to be true
@@ -2106,7 +2101,6 @@ describe CExtractorFunctions do
2106
2101
  scanner = StringScanner.new(content)
2107
2102
  functions = CExtractorFunctions.new({ c_extractor_code_text: CExtractorCodeText.new() })
2108
2103
  functions.setup()
2109
- functions.max_line_length = 1000
2110
2104
 
2111
2105
  success1, func1 = functions.try_extract_function_definition(scanner, filepath)
2112
2106
  expect(success1).to be true
@@ -2130,7 +2124,6 @@ describe CExtractorFunctions do
2130
2124
  let(:functions) do
2131
2125
  f = CExtractorFunctions.new({ c_extractor_code_text: CExtractorCodeText.new() })
2132
2126
  f.setup()
2133
- f.max_line_length = 1000
2134
2127
  f
2135
2128
  end
2136
2129
 
@@ -2364,7 +2357,6 @@ describe CExtractorFunctions do
2364
2357
  let(:functions) do
2365
2358
  f = CExtractorFunctions.new({ c_extractor_code_text: CExtractorCodeText.new() })
2366
2359
  f.setup()
2367
- f.max_line_length = 1000
2368
2360
  f
2369
2361
  end
2370
2362
 
@@ -2482,7 +2474,6 @@ describe CExtractorFunctions do
2482
2474
  scanner = StringScanner.new(content)
2483
2475
  functions = CExtractorFunctions.new({ c_extractor_code_text: CExtractorCodeText.new() })
2484
2476
  functions.setup()
2485
- functions.max_line_length = 1000
2486
2477
  success, decl = functions.try_extract_function_declaration(scanner)
2487
2478
  return [success, decl, scanner.pos, scanner.rest]
2488
2479
  end
@@ -2711,7 +2702,6 @@ describe CExtractorFunctions do
2711
2702
  scanner = StringScanner.new(content)
2712
2703
  functions = CExtractorFunctions.new({ c_extractor_code_text: CExtractorCodeText.new() })
2713
2704
  functions.setup()
2714
- functions.max_line_length = 1000
2715
2705
  success, func = functions.try_extract_function_definition( scanner, filepath )
2716
2706
  return [success, func]
2717
2707
  end
@@ -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()