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
@@ -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
@@ -279,6 +279,21 @@ class SystemContext
279
279
  sections << stderr.strip
280
280
  end
281
281
 
282
+ # Loginator routes `Verbosity::DEBUG` messages (including the backtrace
283
+ # `log_debug_backtrace` emits for a caught exception) to stdout rather than
284
+ # stderr, and this method's stdout scan above only pulls lines containing
285
+ # the literal substrings 'ERROR'/'EXCEPTION' -- a bare backtrace line
286
+ # (file:line:in `method') contains neither, so without surfacing it here
287
+ # explicitly, the one piece of stdout most useful for diagnosing *why* a
288
+ # command failed would otherwise only ever reach the full raw-output log
289
+ # file, not this console-visible summary (what CI output actually shows).
290
+ label = 'Debug Backtrace ==>'
291
+ backtrace_block = extract_section(stdout, label)
292
+ unless backtrace_block.empty?
293
+ sections << ">> DEBUG BACKTRACE"
294
+ sections.concat(backtrace_block)
295
+ end
296
+
282
297
  label = 'FAILED TEST SUMMARY'
283
298
  failed_block = extract_section(stdout, label)
284
299
  unless failed_block.empty?
@@ -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
@@ -91,6 +96,14 @@ ceedling_system_tests do
91
96
  test_case :crash_none_writes_fail_results_file
92
97
  test_case :crash_simple_sigsegv_all_test_cases
93
98
  test_case :crash_simple_sigabrt_assert_failure
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
94
107
  end
95
108
 
96
109
  describe "Backtrace with GDB" do
@@ -100,6 +113,7 @@ ceedling_system_tests do
100
113
  test_case :crash_gdb_sigsegv_targets_test_case_filter
101
114
  test_case :crash_gdb_sigsegv_excludes_test_case_filter
102
115
  test_case :crash_gdb_sigabrt_assert_failure
116
+ test_case :crash_gdb_sigsegv_with_parameterized_test
103
117
  end
104
118
 
105
119
  describe "Test filtering" 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
@@ -94,6 +99,14 @@ ceedling_system_tests do
94
99
  test_case :crash_none_writes_fail_results_file
95
100
  test_case :crash_simple_sigsegv_all_test_cases
96
101
  test_case :crash_simple_sigabrt_assert_failure
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
97
110
  end
98
111
 
99
112
  describe "Backtrace with GDB" do
@@ -103,6 +116,7 @@ ceedling_system_tests do
103
116
  test_case :crash_gdb_sigsegv_targets_test_case_filter
104
117
  test_case :crash_gdb_sigsegv_excludes_test_case_filter
105
118
  test_case :crash_gdb_sigabrt_assert_failure
119
+ test_case :crash_gdb_sigsegv_with_parameterized_test
106
120
  end
107
121
 
108
122
  describe "Test filtering" do
@@ -170,6 +184,11 @@ ceedling_system_tests do
170
184
  test_case :test_project_with_preprocessing_for_mocks
171
185
  test_case :test_project_with_preprocessing_for_missing_mock
172
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
173
192
  end
174
193
 
175
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
@@ -892,6 +957,168 @@ module CommonSystemTestCases
892
957
  end
893
958
  end
894
959
 
960
+ # Regression test for issue #1185: a parameterized test (TEST_CASE) defined after a
961
+ # crashing test in the same file must still report its own real PASS results instead
962
+ # of being swept up as "crashed" alongside the actual crash. Uses :use_backtrace => :simple
963
+ # (Ceedling's default), which is the mode in which the bug was reported.
964
+ def crash_simple_sigsegv_with_parameterized_test
965
+ @c.with_context do
966
+ Dir.chdir @proj_name do
967
+ FileUtils.cp test_asset_path("example_file.h"), 'src/'
968
+ FileUtils.cp test_asset_path("example_file.c"), 'src/'
969
+ FileUtils.cp test_asset_path("test_example_file_crash_sigsegv_with_param.c"), 'test/'
970
+
971
+ @c.merge_project_yml_for_test({
972
+ :project => { :use_backtrace => :simple },
973
+ :unity => { :use_param_tests => true }
974
+ })
975
+
976
+ output = @c.ceedling_build_exec("test:all")
977
+ expect(@c.last_exit_status).to eq(1) # Test should fail because of crash
978
+ expect(output).to match(/Unit test failures/)
979
+ # 1 crashing test + 3 parameterized cases
980
+ expect(output).to match(/TESTED:\s+4/)
981
+ expect(output).to match(/PASSED:\s+3/)
982
+ expect(output).to match(/FAILED:\s+1/)
983
+ expect(output).to match(/IGNORED:\s+0/)
984
+
985
+ result_file = './build/test/results/test_example_file_crash_sigsegv_with_param.fail'
986
+ expect(File.exist?(result_file)).to be(true)
987
+ results = YamlWrapper.new.load(result_file)
988
+
989
+ # Only the truly crashing test is reported as a failure/crash
990
+ expect(results[:failures].map { |f| f[:test] }).to eq(['test_add_numbers_will_fail'])
991
+ expect(results[:failures].first[:message]).to match(/Test case crashed/)
992
+
993
+ # Each parameterized case reports its own real (passing) result, by name, not a crash
994
+ expect(results[:successes].map { |s| s[:test] }).to contain_exactly(
995
+ 'test_difference_between_numbers_is_correct(5, 3, 2)',
996
+ 'test_difference_between_numbers_is_correct(10, 4, 6)',
997
+ 'test_difference_between_numbers_is_correct(0, 0, 0)'
998
+ )
999
+ end
1000
+ end
1001
+ end
1002
+
1003
+ # Same regression coverage as `crash_simple_sigsegv_with_parameterized_test`, but for
1004
+ # :use_backtrace => :gdb: confirms gdb-based isolation also correctly distinguishes the
1005
+ # crashing test from the unrelated parameterized test, and still attributes the crash to
1006
+ # the correct source line (exercising the gdb crash-frame symbol matching fix).
1007
+ def crash_gdb_sigsegv_with_parameterized_test
1008
+ @c.with_context do
1009
+ Dir.chdir @proj_name do
1010
+ FileUtils.cp test_asset_path("example_file.h"), 'src/'
1011
+ FileUtils.cp test_asset_path("example_file.c"), 'src/'
1012
+ FileUtils.cp test_asset_path("test_example_file_crash_sigsegv_with_param.c"), 'test/'
1013
+
1014
+ @c.merge_project_yml_for_test({
1015
+ :project => { :use_backtrace => :gdb },
1016
+ :unity => { :use_param_tests => true }
1017
+ })
1018
+
1019
+ output = @c.ceedling_build_exec("test:all")
1020
+ expect(@c.last_exit_status).to eq(1) # Test should fail because of crash
1021
+ expect(output).to match(/Unit test failures/)
1022
+ expect(output).to match(/TESTED:\s+4/)
1023
+ expect(output).to match(/PASSED:\s+3/)
1024
+ expect(output).to match(/FAILED:\s+1/)
1025
+ expect(output).to match(/IGNORED:\s+0/)
1026
+
1027
+ result_file = './build/test/results/test_example_file_crash_sigsegv_with_param.fail'
1028
+ expect(File.exist?(result_file)).to be(true)
1029
+ results = YamlWrapper.new.load(result_file)
1030
+
1031
+ # Only the truly crashing test is reported as a failure/crash
1032
+ expect(results[:failures].map { |f| f[:test] }).to eq(['test_add_numbers_will_fail'])
1033
+ expect(results[:failures].first[:message]).to match(/Test case crashed/)
1034
+ expect(results[:failures].first[:message]).to match(/SIGSEGV/i)
1035
+
1036
+ # Each parameterized case reports its own real (passing) result, by name, not a crash
1037
+ expect(results[:successes].map { |s| s[:test] }).to contain_exactly(
1038
+ 'test_difference_between_numbers_is_correct(5, 3, 2)',
1039
+ 'test_difference_between_numbers_is_correct(10, 4, 6)',
1040
+ 'test_difference_between_numbers_is_correct(0, 0, 0)'
1041
+ )
1042
+
1043
+ log_path = './build/logs/test/test_example_file_crash_sigsegv_with_param/test_add_numbers_will_fail.gdb.log'
1044
+ expect(File.exist?(log_path)).to be(true)
1045
+ expect(File.read(log_path)).to match(/SIGSEGV|Segmentation fault/i)
1046
+ end
1047
+ end
1048
+ end
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
+
895
1122
  def project_with_test_file_directly_including_source_file
896
1123
  @c.with_context do
897
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