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
@@ -111,20 +111,38 @@ describe "Includes serialization" do
111
111
  expect(restored.map(&:filename)).to eq(["first.h", "second.h", "third.h", "fourth.h"])
112
112
  end
113
113
 
114
- it "handles includes with paths during serialization" do
114
+ it "handles includes with an include_path override during serialization" do
115
115
  original = [
116
- UserInclude.new("path/to/header.h", use_path: true),
117
- SystemInclude.new("sys/stdio.h", use_path: true),
118
- MockInclude.new("mocks/mock_module.h", use_path: true)
116
+ UserInclude.new("path/to/header.h", include_path: "path/to/header.h"),
117
+ SystemInclude.new("sys/stdio.h", include_path: "sys/stdio.h"),
118
+ MockInclude.new("mocks/mock_module.h")
119
119
  ]
120
-
120
+
121
121
  hashes = Includes.to_hashes(original)
122
122
  restored = Includes.from_hashes(hashes)
123
-
123
+
124
124
  expect(restored.length).to eq(3)
125
125
  expect(restored[0].filepath).to eq("path/to/header.h")
126
126
  expect(restored[1].filepath).to eq("sys/stdio.h")
127
127
  expect(restored[2].filepath).to eq("mocks/mock_module.h")
128
+ # `include_path` must itself round-trip, not merely `filepath` -- otherwise a cached
129
+ # build silently reverts to filename-only rendering on its next, cache-hit run.
130
+ expect("#{restored[0]}").to eq('#include "path/to/header.h"')
131
+ expect("#{restored[1]}").to eq('#include <sys/stdio.h>')
132
+ end
133
+
134
+ it "round-trips include_path, and has it take rendering precedence over filepath/filename" do
135
+ original = [
136
+ SystemInclude.new("/usr/include/x86_64-linux-gnu/sys/stat.h", include_path: "sys/stat.h")
137
+ ]
138
+
139
+ hashes = Includes.to_hashes(original)
140
+ restored = Includes.from_hashes(hashes)
141
+
142
+ expect(restored.length).to eq(1)
143
+ expect(restored[0].filepath).to eq("/usr/include/x86_64-linux-gnu/sys/stat.h")
144
+ expect(restored[0].include_path).to eq("sys/stat.h")
145
+ expect("#{restored[0]}").to eq("#include <sys/stat.h>")
128
146
  end
129
147
 
130
148
  it "handles empty array" do
@@ -1054,16 +1072,88 @@ describe "Includes reconciliation" do
1054
1072
  Include.new("subdir/header.h"),
1055
1073
  Include.new("sys/types.h")
1056
1074
  ]
1057
-
1075
+
1058
1076
  user = [UserInclude.new("header.h")]
1059
1077
  system = [SystemInclude.new("types.h")]
1060
-
1078
+
1061
1079
  result = Includes.reconcile(bare: bare, user: user, system: system)
1062
-
1080
+
1063
1081
  # Should match by filename only
1064
1082
  expect(result.length).to eq(2)
1065
1083
  expect(result[0].filename).to eq("types.h")
1066
1084
  expect(result[1].filename).to eq("header.h")
1067
1085
  end
1086
+
1087
+ it "preserves a system include directive's original directory components instead of collapsing to its bare filename" do
1088
+ bare = [Include.new("sys/stat.h")]
1089
+ user = []
1090
+ system = [SystemInclude.new("/usr/include/x86_64-linux-gnu/sys/stat.h")]
1091
+
1092
+ result = Includes.reconcile(bare: bare, user: user, system: system)
1093
+
1094
+ expect(result.length).to eq(1)
1095
+ expect(result[0]).to be_a(SystemInclude)
1096
+ expect(result[0].filepath).to eq("/usr/include/x86_64-linux-gnu/sys/stat.h")
1097
+ expect("#{result[0]}").to eq("#include <sys/stat.h>")
1098
+ expect(Includes.from_hashes(Includes.to_hashes(result)).map(&:to_s)).to eq(["#include <sys/stat.h>"])
1099
+ end
1100
+
1101
+ it "prefers the more specific bare entry when a system header's basename collides with an unrelated bare entry" do
1102
+ # A project's own quoted `#include "stat.h"` sitting alongside a system
1103
+ # `#include <sys/stat.h>` in the same file: both reach `bare` as plain, untyped
1104
+ # Include objects, and the short entry trivially "corresponds" to any longer
1105
+ # resolved path ending in the same filename. The longer, genuinely-corresponding
1106
+ # entry must still win, regardless of which one happens to appear first in `bare`.
1107
+ bare = [
1108
+ Include.new("stat.h"),
1109
+ Include.new("sys/stat.h")
1110
+ ]
1111
+ user = []
1112
+ system = [SystemInclude.new("/usr/include/x86_64-linux-gnu/sys/stat.h")]
1113
+
1114
+ result = Includes.reconcile(bare: bare, user: user, system: system)
1115
+
1116
+ expect(result.length).to eq(1)
1117
+ expect("#{result[0]}").to eq("#include <sys/stat.h>")
1118
+ end
1119
+
1120
+ it "falls back to a same-filename bare entry when nothing corresponds by path" do
1121
+ # The compiler's real search-path structure can diverge from a directive's own
1122
+ # literal spelling (a symlinked or vendored include root, for instance), so a
1123
+ # resolved system path may not share any trailing segment with its bare entry
1124
+ # beyond the filename itself. Rendering the bare entry's own spelling is still
1125
+ # strictly better than collapsing to the bare filename alone.
1126
+ bare = [Include.new("compat/stat.h")]
1127
+ user = []
1128
+ system = [SystemInclude.new("/usr/include/x86_64-linux-gnu/bits/stat.h")]
1129
+
1130
+ result = Includes.reconcile(bare: bare, user: user, system: system)
1131
+
1132
+ expect(result.length).to eq(1)
1133
+ expect("#{result[0]}").to eq("#include <compat/stat.h>")
1134
+ end
1135
+
1136
+ it "does not hard-error over two same-named system headers, a normal toolchain wrapper-chain pattern rather than a project-file conflict" do
1137
+ # e.g. a compiler-provided stdint.h #include_next-ing its libc counterpart --
1138
+ # both real, both legitimately entered, both sharing a basename, neither a
1139
+ # project file whose identity is actually in question.
1140
+ bare = [Include.new("stdint.h")]
1141
+
1142
+ user = []
1143
+ system = [
1144
+ SystemInclude.new("/usr/lib/gcc/x86_64-linux-gnu/14/include/stdint.h"),
1145
+ SystemInclude.new("/usr/include/stdint.h")
1146
+ ]
1147
+
1148
+ result = nil
1149
+ expect {
1150
+ result = Includes.reconcile(bare: bare, user: user, system: system)
1151
+ }.not_to raise_error
1152
+
1153
+ expect(result.length).to eq(2)
1154
+ expect(result.map(&:filepath)).to include(
1155
+ "/usr/lib/gcc/x86_64-linux-gnu/14/include/stdint.h", "/usr/include/stdint.h"
1156
+ )
1157
+ end
1068
1158
  end
1069
- end
1159
+ end
@@ -522,6 +522,31 @@ describe Partializer do
522
522
  expect(result).not_to include(UserInclude.new('partial1.h'))
523
523
  expect(result).not_to include(UserInclude.new('partial2.h'))
524
524
  end
525
+
526
+ it "appends the shared types header when one was generated for this module" do
527
+ includes = [UserInclude.new('header1.h')]
528
+ partials = {}
529
+ result = @partializer.remap_implementation_header_includes(
530
+ name: 'module',
531
+ includes: includes,
532
+ partials: partials,
533
+ types_header: 'ceedling_partial_module_types.h'
534
+ )
535
+
536
+ expect(result).to include(UserInclude.new('ceedling_partial_module_types.h'))
537
+ end
538
+
539
+ it "does not append a types header when the module has none" do
540
+ includes = [UserInclude.new('header1.h')]
541
+ partials = {}
542
+ result = @partializer.remap_implementation_header_includes(
543
+ name: 'module',
544
+ includes: includes,
545
+ partials: partials
546
+ )
547
+
548
+ expect(result).to match_array([UserInclude.new('header1.h')])
549
+ end
525
550
  end
526
551
 
527
552
  ###
@@ -683,6 +708,31 @@ describe Partializer do
683
708
  expect(result).not_to include(UserInclude.new('partial1.h'))
684
709
  expect(result).not_to include(UserInclude.new('partial2.h'))
685
710
  end
711
+
712
+ it "appends the shared types header when one was generated for this module" do
713
+ includes = [UserInclude.new('header1.h')]
714
+ partials = {}
715
+ result = @partializer.remap_interface_header_includes(
716
+ name: 'module',
717
+ includes: includes,
718
+ partials: partials,
719
+ types_header: 'ceedling_partial_module_types.h'
720
+ )
721
+
722
+ expect(result).to include(UserInclude.new('ceedling_partial_module_types.h'))
723
+ end
724
+
725
+ it "does not append a types header when the module has none" do
726
+ includes = [UserInclude.new('header1.h')]
727
+ partials = {}
728
+ result = @partializer.remap_interface_header_includes(
729
+ name: 'module',
730
+ includes: includes,
731
+ partials: partials
732
+ )
733
+
734
+ expect(result).to match_array([UserInclude.new('header1.h')])
735
+ end
686
736
  end
687
737
 
688
738
  ###
@@ -1521,6 +1571,66 @@ describe Partializer do
1521
1571
  expect(result).to eq(filtered)
1522
1572
  end
1523
1573
 
1574
+ it "merges declaration-only functions into the DEDUCT candidate pool" do
1575
+ defn = double('inline_func', name: 'inline_fn')
1576
+ decl = double('proto_func', name: 'proto_fn')
1577
+ defs = [defn]
1578
+ decls = [decl]
1579
+ filtered = [double('iface_inline'), double('iface_proto')]
1580
+ pf = make_pf(type: Partials::DEDUCT)
1581
+
1582
+ expect(@partializer_helper).to receive(:filter_and_transform_funcs)
1583
+ .with([defn, decl], Partials::DEDUCT, :interface).and_return(filtered)
1584
+ allow(@partializer_helper).to receive(:subtract_funcs)
1585
+ .with(funcs: filtered, names: []).and_return(filtered)
1586
+
1587
+ result = @partializer.extract_interface_functions(
1588
+ test: 'test_mod', partial: 'mod',
1589
+ definitions: defs, declarations: decls, config: make_config(mocks: pf)
1590
+ )
1591
+ expect(result).to eq(filtered)
1592
+ end
1593
+
1594
+ it "merges declaration-only functions into the PUBLIC candidate pool too" do
1595
+ defn = double('def_func', name: 'pub_def')
1596
+ decl = double('decl_func', name: 'pub_decl')
1597
+ defs = [defn]
1598
+ decls = [decl]
1599
+ filtered = [double('iface_func')]
1600
+ pf = make_pf(type: Partials::PUBLIC)
1601
+
1602
+ expect(@partializer_helper).to receive(:filter_and_transform_funcs)
1603
+ .with([defn, decl], Partials::PUBLIC, :interface).and_return(filtered)
1604
+ allow(@partializer_helper).to receive(:subtract_funcs)
1605
+ .with(funcs: filtered, names: []).and_return(filtered)
1606
+
1607
+ result = @partializer.extract_interface_functions(
1608
+ test: 'test_mod', partial: 'mod',
1609
+ definitions: defs, declarations: decls, config: make_config(mocks: pf)
1610
+ )
1611
+ expect(result).to eq(filtered)
1612
+ end
1613
+
1614
+ it "excludes a declaration already present as a definition with the same name (dedup)" do
1615
+ defn = double('def_func', name: 'shared')
1616
+ decl = double('decl_func', name: 'shared') # e.g. header prototype for the same function
1617
+ defs = [defn]
1618
+ decls = [decl]
1619
+ filtered = [double('iface_func')]
1620
+ pf = make_pf(type: Partials::DEDUCT)
1621
+
1622
+ expect(@partializer_helper).to receive(:filter_and_transform_funcs)
1623
+ .with([defn], Partials::DEDUCT, :interface).and_return(filtered)
1624
+ allow(@partializer_helper).to receive(:subtract_funcs)
1625
+ .with(funcs: filtered, names: []).and_return(filtered)
1626
+
1627
+ result = @partializer.extract_interface_functions(
1628
+ test: 'test_mod', partial: 'mod',
1629
+ definitions: defs, declarations: decls, config: make_config(mocks: pf)
1630
+ )
1631
+ expect(result).to eq(filtered)
1632
+ end
1633
+
1524
1634
  it "fills list from additions only for ACCUMULATE type" do
1525
1635
  defs = [double('def', name: 'named')]
1526
1636
  decls = []
@@ -1548,8 +1658,10 @@ describe Partializer do
1548
1658
  found = double('iface_func', name: 'decl_only')
1549
1659
  pf = make_pf(type: Partials::ACCUMULATE, additions: ['decl_only'])
1550
1660
 
1661
+ # candidates = definitions + declarations (ACCUMULATE ignores this list regardless --
1662
+ # it always starts empty -- but it's still built before the type check discards it).
1551
1663
  allow(@partializer_helper).to receive(:filter_and_transform_funcs)
1552
- .with(defs, Partials::ACCUMULATE, :interface).and_return([])
1664
+ .with(decls, Partials::ACCUMULATE, :interface).and_return([])
1553
1665
  expect(@partializer_helper).to receive(:find_and_transform_func)
1554
1666
  .with(name: 'decl_only', primary_funcs: defs, secondary_funcs: decls, output_type: :interface)
1555
1667
  .and_return(found)
@@ -166,6 +166,17 @@ describe PartializerUtils do
166
166
  expect(result.signature).to eq(signature)
167
167
  expect(result).not_to respond_to(:code_block)
168
168
  end
169
+
170
+ it "strips a trailing semicolon from a declaration-sourced signature" do
171
+ # CFunctionDeclaration#signature_stripped always carries its own trailing
172
+ # semicolon (it's a bare prototype); CFunctionDefinition#signature_stripped never
173
+ # does. Callers render "#{signature};" uniformly, so this must not double up.
174
+ signature = 'void testFunc(int x);'
175
+
176
+ result = @utils.transform_function(mock_func, signature, :interface)
177
+
178
+ expect(result.signature).to eq('void testFunc(int x)')
179
+ end
169
180
  end
170
181
 
171
182
  context "when output_type is invalid" do
@@ -19,16 +19,20 @@ RSpec.describe PreprocessinatorIncludesHandler do
19
19
  let(:real_parsing_parcels) { ParsingParcels.new }
20
20
 
21
21
  before :each do
22
- @configurator = double('configurator')
23
- @include_factory = double('include_factory')
24
- @file_wrapper = double('file_wrapper')
25
- @yaml_wrapper = double('yaml_wrapper')
26
- @loginator = double('loginator')
27
- @reportinator = double('reportinator')
22
+ @configurator = double('configurator')
23
+ @include_factory = double('include_factory')
24
+ @file_wrapper = double('file_wrapper')
25
+ @file_path_utils = double('file_path_utils')
26
+ @tool_executor = double('tool_executor')
27
+ @yaml_wrapper = double('yaml_wrapper')
28
+ @loginator = double('loginator')
29
+ @reportinator = double('reportinator')
28
30
  @preprocessinator_line_marker_includes_extractor =
29
31
  double('preprocessinator_line_marker_includes_extractor')
30
32
 
31
33
  allow(@loginator).to receive(:log)
34
+ allow(@loginator).to receive(:lazy)
35
+ allow(@loginator).to receive(:log_list)
32
36
  allow(@reportinator).to receive(:generate_module_progress).and_return('')
33
37
 
34
38
  # Include factory helpers: return typed Include objects based on the path
@@ -50,8 +54,9 @@ RSpec.describe PreprocessinatorIncludesHandler do
50
54
  include_factory: @include_factory,
51
55
  preprocessinator_line_marker_includes_extractor:
52
56
  @preprocessinator_line_marker_includes_extractor,
53
- tool_executor: double('tool_executor'),
57
+ tool_executor: @tool_executor,
54
58
  file_wrapper: @file_wrapper,
59
+ file_path_utils: @file_path_utils,
55
60
  yaml_wrapper: @yaml_wrapper,
56
61
  parsing_parcels: real_parsing_parcels,
57
62
  loginator: @loginator,
@@ -66,6 +71,90 @@ RSpec.describe PreprocessinatorIncludesHandler do
66
71
  end
67
72
 
68
73
 
74
+ # ===========================================================================
75
+ describe '#extract_bare_includes' do
76
+ # ===========================================================================
77
+ # Extraction stages an isolated, sibling-free copy of the file being scanned before running
78
+ # GCC's bare-includes preprocessor pass, so that GCC's own directory-relative #include
79
+ # resolution has no real sibling header available to find and recurse into.
80
+
81
+ let(:test_name) { 'test_other' }
82
+ let(:filepath) { '/project/test/test_other.c' }
83
+ let(:isolation_parent) { '/project/build/test/preprocess/files/test_other' }
84
+ let(:isolation_dir) { "#{isolation_parent}/tmp1234" }
85
+ let(:isolated_filepath) { "#{isolation_dir}/test_other.c" }
86
+ let(:make_rules) do
87
+ "test_other.o: #{isolated_filepath} unity.h mock_foo_func.h other.h\n\n" \
88
+ "unity.h:\n\nmock_foo_func.h:\n\nother.h:\n"
89
+ end
90
+
91
+ before :each do
92
+ allow(@file_path_utils).to receive(:form_test_preprocess_files_path)
93
+ .with(test_name).and_return(isolation_parent)
94
+ allow(@file_wrapper).to receive(:mkdir_tmp)
95
+ .with(nil, isolation_parent).and_return(isolation_dir)
96
+ allow(@file_wrapper).to receive(:cp).with(filepath, isolated_filepath)
97
+ allow(@file_wrapper).to receive(:rm_rf).with(isolation_dir)
98
+
99
+ allow(@configurator).to receive(:tools_test_bare_includes_preprocessor).and_return(:bare_tool)
100
+
101
+ allow(@tool_executor).to receive(:build_command_line) do |tool, extra_args, fp, defs, flgs, paths|
102
+ { options: {}, tool: tool, filepath: fp, defines: defs, flags: flgs, search_paths: paths }
103
+ end
104
+
105
+ allow(@tool_executor).to receive(:exec).and_return(output: make_rules)
106
+ end
107
+
108
+ def call_it
109
+ subject.extract_bare_includes(
110
+ test: test_name, filepath: filepath, search_paths: [], flags: [], defines: []
111
+ )
112
+ end
113
+
114
+ it 'stages a copy of the file into an isolated, sibling-free directory before extraction' do
115
+ call_it()
116
+
117
+ expect(@file_wrapper).to have_received(:mkdir_tmp).with(nil, isolation_parent)
118
+ expect(@file_wrapper).to have_received(:cp).with(filepath, isolated_filepath)
119
+ end
120
+
121
+ it 'builds the bare-includes command against the isolated staged path, not the original' do
122
+ call_it()
123
+
124
+ expect(@tool_executor).to have_received(:build_command_line)
125
+ .with(:bare_tool, [], isolated_filepath, [], [], [])
126
+ end
127
+
128
+ it 'returns the includes parsed from the staged file make-rule output' do
129
+ result = call_it()
130
+ expect(result.map(&:filename)).to contain_exactly('unity.h', 'mock_foo_func.h', 'other.h')
131
+ end
132
+
133
+ it 'cleans up the isolation directory after a successful run' do
134
+ call_it()
135
+ expect(@file_wrapper).to have_received(:rm_rf).with(isolation_dir)
136
+ end
137
+
138
+ it 'cleans up the isolation directory even when the tool executor raises' do
139
+ allow(@tool_executor).to receive(:exec).and_raise(StandardError.new('boom'))
140
+
141
+ expect { call_it() }.to raise_error(StandardError, 'boom')
142
+
143
+ expect(@file_wrapper).to have_received(:rm_rf).with(isolation_dir)
144
+ end
145
+
146
+ it 'still cleans self-reference against the original filepath identity, not the staged copy' do
147
+ self_ref_rules =
148
+ "test_other.o: #{isolated_filepath} #{filepath} other.h\n\n#{filepath}:\n\nother.h:\n"
149
+ allow(@tool_executor).to receive(:exec).and_return(output: self_ref_rules)
150
+
151
+ result = call_it()
152
+ expect(result.map(&:filename)).to contain_exactly('other.h')
153
+ end
154
+
155
+ end
156
+
157
+
69
158
  # ===========================================================================
70
159
  describe '#extract_user_includes_from_text' do
71
160
  # ===========================================================================
@@ -0,0 +1,72 @@
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/system_wrapper'
10
+
11
+ describe SystemWrapper do
12
+ before(:each) do
13
+ @sys_wrapper = described_class.new
14
+ end
15
+
16
+ # Portable, real subprocess invocations -- no shell scripting, no signals (those are
17
+ # covered where they're actually consumed, via doubles, in generator_helper_spec.rb).
18
+ # This spec exists to characterize shell_capture3's own contract: it is the sole place
19
+ # a subprocess's real Process::Status is captured, and the one thing downstream crash
20
+ # detection depends on entirely.
21
+ def ruby_command(code)
22
+ "#{RbConfig.ruby} -e #{code.inspect}"
23
+ end
24
+
25
+ describe '#shell_capture3' do
26
+ context 'boom: false (the default, and how test-fixture execution always runs)' do
27
+ it 'forces exit_code to 0 even though the real subprocess exited nonzero' do
28
+ result = @sys_wrapper.shell_capture3( command: ruby_command('exit 3'), boom: false )
29
+ expect(result[:exit_code]).to eq(0)
30
+ end
31
+
32
+ it 'still relays the real, un-neutered Process::Status' do
33
+ result = @sys_wrapper.shell_capture3( command: ruby_command('exit 3'), boom: false )
34
+ expect(result[:status]).not_to be_nil
35
+ expect(result[:status].exitstatus).to eq(3)
36
+ expect(result[:status].success?).to be false
37
+ end
38
+
39
+ it 'forces exit_code to 0 for a genuinely successful subprocess too' do
40
+ result = @sys_wrapper.shell_capture3( command: ruby_command('exit 0'), boom: false )
41
+ expect(result[:exit_code]).to eq(0)
42
+ expect(result[:status].success?).to be true
43
+ end
44
+ end
45
+
46
+ context 'boom: true' do
47
+ it 'surfaces the real nonzero exit code' do
48
+ result = @sys_wrapper.shell_capture3( command: ruby_command('exit 3'), boom: true )
49
+ expect(result[:exit_code]).to eq(3)
50
+ end
51
+
52
+ it 'reports 0 for a genuinely successful subprocess' do
53
+ result = @sys_wrapper.shell_capture3( command: ruby_command('exit 0'), boom: true )
54
+ expect(result[:exit_code]).to eq(0)
55
+ end
56
+
57
+ it 'still relays the same real Process::Status as boom: false does' do
58
+ result = @sys_wrapper.shell_capture3( command: ruby_command('exit 3'), boom: true )
59
+ expect(result[:status].exitstatus).to eq(3)
60
+ end
61
+ end
62
+
63
+ it 'combines stdout and stderr into :output while keeping each stream separately available' do
64
+ result = @sys_wrapper.shell_capture3(
65
+ command: ruby_command('STDOUT.print "out"; STDERR.print "err"'), boom: false
66
+ )
67
+ expect(result[:stdout]).to eq('out')
68
+ expect(result[:stderr]).to eq('err')
69
+ expect(result[:output]).to eq('outerr')
70
+ end
71
+ end
72
+ end
@@ -391,8 +391,8 @@ describe TestContextExtractor do
391
391
  @extractor.collect_context( filepath, input, TestContextExtractor::Context::TEST_RUNNER_DETAILS )
392
392
 
393
393
  expected = [
394
- {:line_number => 2, :test => 'test_this_function'},
395
- {:line_number => 12, :test => 'test_another_function'},
394
+ {:line_number => 2, :test => 'test_this_function', :symbol => 'test_this_function'},
395
+ {:line_number => 12, :test => 'test_another_function', :symbol => 'test_another_function'},
396
396
  ]
397
397
 
398
398
  expect( @extractor.lookup_test_cases( filepath ) ).to eq expected
@@ -411,7 +411,7 @@ describe TestContextExtractor do
411
411
  @extractor.collect_context( filepath, input, TestContextExtractor::Context::TEST_RUNNER_DETAILS )
412
412
  }.not_to raise_error
413
413
 
414
- expected = [{ :line_number => 2, :test => 'test_this_function' }]
414
+ expected = [{ :line_number => 2, :test => 'test_this_function', :symbol => 'test_this_function' }]
415
415
  expect( @extractor.lookup_test_cases( filepath ) ).to eq expected
416
416
  end
417
417
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ceedling
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark VanderVoord
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2026-07-27 00:00:00.000000000 Z
13
+ date: 2026-08-14 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rake
@@ -178,19 +178,31 @@ files:
178
178
  - assets/example_file_with_statics.c
179
179
  - assets/example_file_with_stdio.c
180
180
  - assets/example_file_with_stdio.h
181
+ - assets/fake_crash_test_fixture.rb
182
+ - assets/partial_all_module.h
181
183
  - assets/project.yml
184
+ - assets/run_with_ubsan.rb
185
+ - assets/same_dir_pairing_module.c
186
+ - assets/same_dir_pairing_module.h
187
+ - assets/same_dir_pairing_module_func.h
188
+ - assets/same_dir_pairing_other.c
189
+ - assets/same_dir_pairing_other.h
182
190
  - assets/test_example_file.c
183
191
  - assets/test_example_file_2.c
184
192
  - assets/test_example_file_boom.c
185
193
  - assets/test_example_file_crash_assert.c
186
194
  - assets/test_example_file_crash_sigsegv.c
195
+ - assets/test_example_file_crash_sigsegv_with_param.c
187
196
  - assets/test_example_file_source_include.c
188
197
  - assets/test_example_file_success.c
198
+ - assets/test_example_file_ub_shift_overflow.c
189
199
  - assets/test_example_file_unity_printf.c
190
200
  - assets/test_example_file_verbose.c
191
201
  - assets/test_example_file_with_mock.c
192
202
  - assets/test_example_file_with_stdio.c
193
203
  - assets/test_example_with_parameterized_tests.c
204
+ - assets/test_partial_all_module.c
205
+ - assets/test_same_dir_pairing.c
194
206
  - assets/tests_with_defines/src/adc_hardware.c
195
207
  - assets/tests_with_defines/src/adc_hardware.h
196
208
  - assets/tests_with_defines/src/adc_hardware_configurator.c
@@ -281,6 +293,7 @@ files:
281
293
  - docs/mkdocs/configuration/reference/index.md
282
294
  - docs/mkdocs/configuration/reference/libraries.md
283
295
  - docs/mkdocs/configuration/reference/mixins.md
296
+ - docs/mkdocs/configuration/reference/partials.md
284
297
  - docs/mkdocs/configuration/reference/paths.md
285
298
  - docs/mkdocs/configuration/reference/plugins.md
286
299
  - docs/mkdocs/configuration/reference/project.md
@@ -807,6 +820,7 @@ files:
807
820
  - site-local/configuration/reference/index.html
808
821
  - site-local/configuration/reference/libraries.html
809
822
  - site-local/configuration/reference/mixins.html
823
+ - site-local/configuration/reference/partials.html
810
824
  - site-local/configuration/reference/paths.html
811
825
  - site-local/configuration/reference/plugins.html
812
826
  - site-local/configuration/reference/project.html
@@ -946,6 +960,7 @@ files:
946
960
  - spec/units/c_extractor/c_extractor_integration_spec.rb
947
961
  - spec/units/c_extractor/c_extractor_preprocessing_spec.rb
948
962
  - spec/units/c_extractor/c_extractor_spec.rb
963
+ - spec/units/config/configurator_setup_spec.rb
949
964
  - spec/units/config_matchinator_spec.rb
950
965
  - spec/units/config_walkinator_spec.rb
951
966
  - spec/units/configurator_builder_spec.rb
@@ -978,6 +993,7 @@ files:
978
993
  - spec/units/reportinator_spec.rb
979
994
  - spec/units/ruby_expandinator_spec.rb
980
995
  - spec/units/system_utils_spec.rb
996
+ - spec/units/system_wrapper_spec.rb
981
997
  - spec/units/test_build_executor_spec.rb
982
998
  - spec/units/test_context_extractor_spec.rb
983
999
  - spec/units/tool_executor_helper_spec.rb
@@ -1792,6 +1808,7 @@ test_files:
1792
1808
  - spec/units/c_extractor/c_extractor_integration_spec.rb
1793
1809
  - spec/units/c_extractor/c_extractor_preprocessing_spec.rb
1794
1810
  - spec/units/c_extractor/c_extractor_spec.rb
1811
+ - spec/units/config/configurator_setup_spec.rb
1795
1812
  - spec/units/config_matchinator_spec.rb
1796
1813
  - spec/units/config_walkinator_spec.rb
1797
1814
  - spec/units/configurator_builder_spec.rb
@@ -1824,6 +1841,7 @@ test_files:
1824
1841
  - spec/units/reportinator_spec.rb
1825
1842
  - spec/units/ruby_expandinator_spec.rb
1826
1843
  - spec/units/system_utils_spec.rb
1844
+ - spec/units/system_wrapper_spec.rb
1827
1845
  - spec/units/test_build_executor_spec.rb
1828
1846
  - spec/units/test_context_extractor_spec.rb
1829
1847
  - spec/units/tool_executor_helper_spec.rb