ceedling 1.2.1 → 1.2.2

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 (276) hide show
  1. checksums.yaml +4 -4
  2. data/GIT_COMMIT_SHA +1 -1
  3. data/README.md +11 -9
  4. data/Rakefile +2 -1
  5. data/assets/features/ceedling +1 -1
  6. data/assets/fixtures/test_source_file_duplicate_basenames/test_calc_typo.c +14 -0
  7. data/assets/fixtures/tests_with_duplicate_test_names/test_foo.c +21 -0
  8. data/bin/actionator.rb +6 -12
  9. data/bin/app_cfg.rb +14 -1
  10. data/bin/ceedling +13 -3
  11. data/bin/cli.rb +25 -6
  12. data/bin/cli_handler.rb +113 -73
  13. data/bin/cli_helper.rb +51 -78
  14. data/bin/installation_selector.rb +81 -0
  15. data/bin/launchinator.rb +69 -0
  16. data/bin/mixin_resolvinator.rb +26 -36
  17. data/bin/objects.yml +13 -0
  18. data/bin/path_validator.rb +25 -29
  19. data/bin/versionator.rb +47 -48
  20. data/ceedling.gemspec +19 -15
  21. data/docs/BreakingChanges.md +70 -1
  22. data/docs/Changelog.md +74 -3
  23. data/docs/KnownIssues.md +90 -4
  24. data/docs/ReleaseNotes.md +13 -0
  25. data/lib/ceedling/build_log_writer.rb +33 -0
  26. data/lib/ceedling/config/build_root_policy.rb +70 -0
  27. data/lib/ceedling/config/configurator.rb +14 -16
  28. data/lib/ceedling/config/configurator_builder.rb +58 -28
  29. data/lib/ceedling/config/configurator_paths_validator.rb +179 -0
  30. data/lib/ceedling/config/configurator_plugins.rb +1 -1
  31. data/lib/ceedling/config/configurator_setup.rb +15 -28
  32. data/lib/ceedling/config/configurator_validator.rb +1 -110
  33. data/lib/ceedling/defaults.rb +4 -4
  34. data/lib/ceedling/dependencies/README.md +18 -16
  35. data/lib/ceedling/dependencies/dependency_cache_store.rb +20 -3
  36. data/lib/ceedling/dependencies/dependency_hasher.rb +35 -1
  37. data/lib/ceedling/filename_extension.rb +7 -1
  38. data/lib/ceedling/filesystem/FILE_TREE.md +253 -0
  39. data/lib/ceedling/filesystem/PATH_HANDLING.md +304 -0
  40. data/lib/ceedling/filesystem/README.md +41 -0
  41. data/lib/ceedling/filesystem/build_subpath.rb +102 -0
  42. data/lib/ceedling/filesystem/destination_collision.rb +25 -0
  43. data/lib/ceedling/{file_finder.rb → filesystem/file_finder.rb} +159 -104
  44. data/lib/ceedling/{file_finder_helper.rb → filesystem/file_finder_helper.rb} +36 -31
  45. data/lib/ceedling/filesystem/file_path_collection_utils.rb +183 -0
  46. data/lib/ceedling/{file_path_utils.rb → filesystem/file_path_utils.rb} +108 -97
  47. data/lib/ceedling/{file_system_wrapper.rb → filesystem/file_system_wrapper.rb} +6 -5
  48. data/lib/ceedling/filesystem/file_tree.rb +241 -0
  49. data/lib/ceedling/{file_wrapper.rb → filesystem/file_wrapper.rb} +45 -38
  50. data/lib/ceedling/filesystem/path_exclusions.rb +30 -0
  51. data/lib/ceedling/{path_matcher.rb → filesystem/path_matcher.rb} +27 -23
  52. data/lib/ceedling/{path_mirror.rb → filesystem/path_mirror.rb} +19 -18
  53. data/lib/ceedling/filesystem/path_spellings.rb +55 -0
  54. data/lib/ceedling/filesystem/project_paths.rb +184 -0
  55. data/lib/ceedling/filesystem/working_dir.rb +40 -0
  56. data/lib/ceedling/generators/generator.rb +5 -6
  57. data/lib/ceedling/generators/generator_partials.rb +1 -1
  58. data/lib/ceedling/generators/generator_test_results_backtrace.rb +7 -7
  59. data/lib/ceedling/handoff.rb +121 -0
  60. data/lib/ceedling/includes/include_factory.rb +3 -3
  61. data/lib/ceedling/includes/include_pathinator.rb +67 -10
  62. data/lib/ceedling/includes/includes.rb +1 -1
  63. data/lib/ceedling/installation.rb +92 -0
  64. data/lib/ceedling/library_resolver.rb +99 -0
  65. data/lib/ceedling/objects.yml +23 -2
  66. data/lib/ceedling/partials/partializer.rb +4 -4
  67. data/lib/ceedling/partials/partializer_config.rb +3 -1
  68. data/lib/ceedling/plugins/report_log_writer_plugin.rb +3 -5
  69. data/lib/ceedling/preprocess/preprocessinator_file_assembler.rb +1 -1
  70. data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +1 -1
  71. data/lib/ceedling/preprocess/preprocessinator_line_marker_includes_extractor.rb +1 -1
  72. data/lib/ceedling/rake_app/rakefile_component_resolver.rb +10 -17
  73. data/lib/ceedling/rakefile.rb +15 -1
  74. data/lib/ceedling/rakefiles/base/tasks_base.rake +1 -1
  75. data/lib/ceedling/rakefiles/release/rules_release.rake +4 -7
  76. data/lib/ceedling/release_invoker/release_build_executor.rb +33 -44
  77. data/lib/ceedling/release_invoker/release_build_planner.rb +14 -6
  78. data/lib/ceedling/reportinator.rb +3 -4
  79. data/lib/ceedling/setupinator.rb +94 -90
  80. data/lib/ceedling/test_context_extractor.rb +5 -1
  81. data/lib/ceedling/test_invoker/PIPELINE.md +9 -7
  82. data/lib/ceedling/test_invoker/mock_locator.rb +60 -2
  83. data/lib/ceedling/test_invoker/partials_manager.rb +12 -0
  84. data/lib/ceedling/test_invoker/standin_headerinator.rb +22 -13
  85. data/lib/ceedling/test_invoker/test_build_executor.rb +24 -40
  86. data/lib/ceedling/test_invoker/test_build_planner.rb +37 -18
  87. data/lib/ceedling/test_invoker/test_build_setup.rb +17 -12
  88. data/lib/ceedling/test_invoker/test_build_validations.rb +16 -31
  89. data/lib/ceedling/test_invoker/test_file_preprocessinator.rb +1 -1
  90. data/lib/ceedling/test_invoker/test_invoker.rb +6 -1
  91. data/lib/ceedling/test_invoker/test_pipeline_helpers.rb +1 -1
  92. data/lib/ceedling/test_invoker/test_source_file_directive_resolver.rb +40 -20
  93. data/lib/ceedling/tool_executor.rb +9 -3
  94. data/lib/ceedling/yaml_wrapper.rb +3 -2
  95. data/lib/version.rb +1 -1
  96. data/plugins/bullseye/config/defaults_bullseye.rb +1 -1
  97. data/plugins/bullseye/lib/bullseye.rb +9 -7
  98. data/plugins/cppcheck/config/defaults_cppcheck.rb +1 -1
  99. data/plugins/dependencies/config/defaults.yml +6 -6
  100. data/plugins/dependencies/dependencies.rake +4 -1
  101. data/plugins/dependencies/lib/dependencies.rb +18 -8
  102. data/plugins/dependencies/lib/dependency_spec.rb +17 -5
  103. data/plugins/fff/lib/fff.rb +45 -39
  104. data/plugins/fff/spec/fff_cmock_wrapper_spec.rb +128 -0
  105. data/plugins/gcov/config/defaults_gcov.rb +1 -1
  106. data/plugins/gcov/lib/console_reportinator.rb +4 -18
  107. data/plugins/gcov/lib/gcov.rb +9 -2
  108. data/plugins/gcov/lib/gcovr_reportinator.rb +8 -40
  109. data/plugins/gcov/lib/gcovr_thresholds.rb +74 -0
  110. data/plugins/gcov/lib/reportgenerator_reportinator.rb +1 -1
  111. data/plugins/module_generator/lib/module_generator.rb +3 -3
  112. data/plugins/report_tests_teamcity_stdout/lib/report_tests_teamcity_stdout.rb +53 -80
  113. data/plugins/sanitize/lib/sanitize_records.rb +3 -6
  114. data/plugins/valgrind/lib/valgrind.rb +14 -7
  115. data/site-local/configuration/reference/cmock.html +2 -1
  116. data/site-local/configuration/reference/files.html +9 -3
  117. data/site-local/configuration/reference/libraries.html +21 -3
  118. data/site-local/configuration/reference/paths.html +29 -6
  119. data/site-local/configuration/reference/project.html +17 -7
  120. data/site-local/configuration/reference/release-build.html +2 -0
  121. data/site-local/configuration/reference/tools.html +9 -0
  122. data/site-local/configuration/which-ceedling.html +377 -17
  123. data/site-local/development/releases.html +5 -1
  124. data/site-local/development/workflow.html +48 -43
  125. data/site-local/getting-started/builds.html +2 -0
  126. data/site-local/getting-started/command-line.html +56 -4
  127. data/site-local/plugins/dependencies.html +6 -3
  128. data/site-local/plugins/fff.html +42 -11
  129. data/site-local/plugins/gcov/gcovr.html +15 -12
  130. data/site-local/plugins/gcov/setup.html +2 -1
  131. data/site-local/plugins/report-tests-raw-output-log.html +3 -1
  132. data/site-local/plugins/report-tests-teamcity-stdout.html +6 -3
  133. data/site-local/plugins/sanitize.html +7 -6
  134. data/site-local/plugins/valgrind.html +8 -7
  135. data/site-local/project/upgrade.html +3 -0
  136. data/site-local/reference/command-line.html +9 -2
  137. data/site-local/sitemap.xml.gz +0 -0
  138. data/site-local/snapshot/plugins/bullseye/config/defaults_bullseye.rb +1 -1
  139. data/site-local/snapshot/plugins/gcov/config/defaults_gcov.rb +1 -1
  140. data/site-local/testing-guide/conventions.html +53 -0
  141. data/spec/integration/actionator_file_operations_spec.rb +91 -1
  142. data/spec/integration/compile_commands_json_db_spec.rb +1 -1
  143. data/spec/integration/configurator_validation_spec.rb +1 -1
  144. data/spec/integration/dependencies/dependency_tracker_cache_hardening_spec.rb +26 -0
  145. data/spec/integration/dependencies/dependency_tracker_core_spec.rb +22 -0
  146. data/spec/integration/launcher_handoff_spec.rb +297 -0
  147. data/spec/integration/mixin_loading_spec.rb +1 -1
  148. data/spec/integration/module_generator_spec.rb +3 -3
  149. data/spec/integration/nested_test_artifacts_spec.rb +99 -0
  150. data/spec/integration/partials_module_resolution_spec.rb +7 -4
  151. data/spec/integration/path_intake_scenarios_spec.rb +101 -0
  152. data/spec/integration/path_scenarios_spec.rb +322 -0
  153. data/spec/integration/quote_include_isolator_spec.rb +1 -1
  154. data/spec/integration/report_build_warnings_log_spec.rb +1 -1
  155. data/spec/integration/report_tests_log_factory_spec.rb +1 -1
  156. data/spec/integration/test_executable_manifest_spec.rb +1 -1
  157. data/spec/integration/test_includes_extractinator_spec.rb +2 -1
  158. data/spec/integration/test_naming_spec.rb +198 -0
  159. data/spec/integration/test_source_file_checks_spec.rb +166 -0
  160. data/spec/integration/tool_validator_spec.rb +1 -1
  161. data/spec/integration/upgrade_docs_spec.rb +84 -0
  162. data/spec/integration/vendor_copy_transient_io_retry_spec.rb +74 -5
  163. data/spec/support/file_tree_helper.rb +88 -0
  164. data/spec/support/integration/configurator_integration_helper.rb +6 -6
  165. data/spec/support/integration/dependency_tracker_integration_helper.rb +1 -1
  166. data/spec/support/integration/partials_generation_integration_helper.rb +2 -2
  167. data/spec/support/integration/path_scenario_harness.rb +173 -0
  168. data/spec/support/integration/spec_integration_helper.rb +1 -1
  169. data/spec/support/integration/test_includes_harness.rb +13 -2
  170. data/spec/support/spec_helper.rb +2 -0
  171. data/spec/support/system/spec_system_helper.rb +7 -6
  172. data/spec/support/system/system_context.rb +21 -3
  173. data/spec/support/working_dir_tracking.rb +49 -0
  174. data/spec/system/build_output_mirroring_spec.rb +2 -2
  175. data/spec/system/conditional_mock_includes_spec.rb +1 -1
  176. data/spec/system/delta_builds_spec.rb +137 -11
  177. data/spec/system/gcov_deployment_spec.rb +1 -0
  178. data/spec/system/header_include_path_validation_spec.rb +4 -4
  179. data/spec/system/launcher_handoff_spec.rb +82 -0
  180. data/spec/system/mock_include_folders_spec.rb +91 -0
  181. data/spec/system/parent_directory_reference_spec.rb +5 -3
  182. data/spec/system/path_handling_probes_spec.rb +192 -0
  183. data/spec/system/preprocessing_parameterized_tests_spec.rb +1 -1
  184. data/spec/system/rake_release_task_path_disambiguation_spec.rb +6 -6
  185. data/spec/system/rake_test_task_path_disambiguation_spec.rb +85 -0
  186. data/spec/system/special_path_characters_spec.rb +86 -0
  187. data/spec/system/support/common_test_cases.rb +23 -21
  188. data/spec/system/support/gcov_common_test_cases.rb +37 -9
  189. data/spec/system/support/sanitize_common_test_cases.rb +2 -2
  190. data/spec/system/support/valgrind_common_test_cases.rb +12 -12
  191. data/spec/system/test_pipeline_stop_points_spec.rb +4 -4
  192. data/spec/system/test_runner_system_include_spec.rb +1 -1
  193. data/spec/system/test_source_file_directive_spec.rb +34 -5
  194. data/spec/units/bin/actionator_spec.rb +14 -17
  195. data/spec/units/bin/app_cfg_spec.rb +37 -0
  196. data/spec/units/bin/cli_handler_spec.rb +282 -6
  197. data/spec/units/bin/cli_helper_spec.rb +116 -45
  198. data/spec/units/bin/installation_selector_spec.rb +114 -0
  199. data/spec/units/bin/launchinator_spec.rb +171 -0
  200. data/spec/units/bin/path_validator_spec.rb +10 -0
  201. data/spec/units/bin/versionator_spec.rb +38 -6
  202. data/spec/units/build_log_writer_spec.rb +57 -0
  203. data/spec/units/config/build_root_policy_spec.rb +61 -0
  204. data/spec/units/config/configurator_builder_spec.rb +37 -6
  205. data/spec/units/config/configurator_paths_validator_spec.rb +292 -0
  206. data/spec/units/config/configurator_setup_spec.rb +9 -65
  207. data/spec/units/config/configurator_spec.rb +27 -5
  208. data/spec/units/config/configurator_validator_spec.rb +1 -172
  209. data/spec/units/default_tools_quoting_spec.rb +83 -0
  210. data/spec/units/dependencies/dependency_cache_store_spec.rb +33 -2
  211. data/spec/units/dependencies/dependency_debug_tree_spec.rb +1 -1
  212. data/spec/units/dependencies/dependency_hasher_spec.rb +42 -1
  213. data/spec/units/dependencies/dependency_path_normalizer_spec.rb +1 -1
  214. data/spec/units/dependencies/dependency_tracker_spec.rb +3 -1
  215. data/spec/units/filename_extension_spec.rb +11 -0
  216. data/spec/units/filesystem/build_subpath_spec.rb +122 -0
  217. data/spec/units/filesystem/destination_collision_spec.rb +30 -0
  218. data/spec/units/{file_finder_helper_spec.rb → filesystem/file_finder_helper_spec.rb} +46 -1
  219. data/spec/units/{file_finder_spec.rb → filesystem/file_finder_spec.rb} +188 -22
  220. data/spec/units/{file_path_collection_utils_spec.rb → filesystem/file_path_collection_utils_spec.rb} +181 -1
  221. data/spec/units/{file_path_utils_spec.rb → filesystem/file_path_utils_spec.rb} +204 -87
  222. data/spec/units/filesystem/file_system_wrapper_spec.rb +33 -0
  223. data/spec/units/filesystem/file_tree_spec.rb +511 -0
  224. data/spec/units/filesystem/file_tree_windows_spec.rb +78 -0
  225. data/spec/units/{file_wrapper_spec.rb → filesystem/file_wrapper_spec.rb} +95 -12
  226. data/spec/units/{path_matcher_spec.rb → filesystem/path_matcher_spec.rb} +12 -1
  227. data/spec/units/{path_mirror_spec.rb → filesystem/path_mirror_spec.rb} +32 -4
  228. data/spec/units/filesystem/project_paths_spec.rb +147 -0
  229. data/spec/units/filesystem/working_dir_spec.rb +71 -0
  230. data/spec/units/generators/generator_spec.rb +12 -6
  231. data/spec/units/generators/generator_test_results_backtrace_spec.rb +50 -46
  232. data/spec/units/generators/generator_test_results_spec.rb +1 -1
  233. data/spec/units/handoff_spec.rb +205 -0
  234. data/spec/units/includes/include_factory_spec.rb +3 -3
  235. data/spec/units/includes/include_pathinator_spec.rb +46 -0
  236. data/spec/units/installation_spec.rb +228 -0
  237. data/spec/units/library_resolver_spec.rb +157 -0
  238. data/spec/units/mock_locator_spec.rb +76 -1
  239. data/spec/units/partials/partializer_config_spec.rb +7 -0
  240. data/spec/units/partials/partializer_spec.rb +1 -0
  241. data/spec/units/partials_manager_spec.rb +17 -0
  242. data/spec/units/plugins/bullseye_spec.rb +15 -2
  243. data/spec/units/plugins/console_reportinator_spec.rb +18 -20
  244. data/spec/units/plugins/dependencies_spec.rb +33 -7
  245. data/spec/units/plugins/dependency_spec_spec.rb +17 -0
  246. data/spec/units/plugins/gcov_spec.rb +28 -3
  247. data/spec/units/plugins/gcovr_reportinator_spec.rb +19 -47
  248. data/spec/units/plugins/gcovr_thresholds_spec.rb +95 -0
  249. data/spec/units/plugins/module_generator_spec.rb +5 -5
  250. data/spec/units/plugins/report_build_warnings_log_spec.rb +3 -3
  251. data/spec/units/plugins/report_log_writer_plugin_spec.rb +10 -1
  252. data/spec/units/plugins/report_tests_raw_output_log_spec.rb +13 -2
  253. data/spec/units/plugins/report_tests_teamcity_stdout_spec.rb +77 -3
  254. data/spec/units/plugins/reportgenerator_reportinator_spec.rb +1 -1
  255. data/spec/units/plugins/sanitize_records_spec.rb +3 -1
  256. data/spec/units/plugins/sanitize_spec.rb +1 -1
  257. data/spec/units/plugins/valgrind_spec.rb +23 -8
  258. data/spec/units/preprocess/preprocessinator_comment_stripper_spec.rb +1 -1
  259. data/spec/units/rake_app/rakefile_component_resolver_spec.rb +21 -0
  260. data/spec/units/release_build_executor_spec.rb +53 -14
  261. data/spec/units/release_build_planner_spec.rb +15 -3
  262. data/spec/units/reportinator_spec.rb +5 -5
  263. data/spec/units/standin_headerinator_spec.rb +31 -1
  264. data/spec/units/test_build_executor_spec.rb +65 -3
  265. data/spec/units/test_build_planner_spec.rb +104 -2
  266. data/spec/units/test_build_setup_spec.rb +96 -12
  267. data/spec/units/test_context_extractor_spec.rb +25 -0
  268. data/spec/units/test_file_preprocessinator_spec.rb +17 -1
  269. data/spec/units/test_includes_extractinator_spec.rb +5 -1
  270. data/spec/units/test_invoker/objects_wiring_spec.rb +6 -3
  271. data/spec/units/test_invoker_spec.rb +14 -1
  272. data/spec/units/test_source_file_directive_resolver_spec.rb +38 -7
  273. data/spec/units/tool_executor_spec.rb +26 -0
  274. data/spec/units/yaml_wrapper_spec.rb +11 -0
  275. metadata +109 -25
  276. data/lib/ceedling/file_path_collection_utils.rb +0 -164
@@ -142,7 +142,7 @@ describe GeneratorTestResultsBacktrace do
142
142
  @tool_executor = instance_double('ToolExecutor')
143
143
  @generator_test_results = instance_double('GeneratorTestResults')
144
144
  @file_path_utils = instance_double('FilePathUtils')
145
- @file_wrapper = instance_double('FileWrapper')
145
+ @build_log_writer = instance_double('BuildLogWriter')
146
146
  @loginator = double('loginator').as_null_object
147
147
  # A real GeneratorHelper, not a double -- these tests exercise the actual crash
148
148
  # detection logic a retry's status is checked against, not a stubbed stand-in for it.
@@ -154,7 +154,7 @@ describe GeneratorTestResultsBacktrace do
154
154
  :generator_test_results => @generator_test_results,
155
155
  :generator_helper => @generator_helper,
156
156
  :file_path_utils => @file_path_utils,
157
- :file_wrapper => @file_wrapper,
157
+ :build_log_writer => @build_log_writer,
158
158
  :loginator => @loginator
159
159
  })
160
160
  @backtrace.setup()
@@ -170,11 +170,15 @@ describe GeneratorTestResultsBacktrace do
170
170
  allow(@file_path_utils).to receive(:form_test_gdb_log) do |test, context:, name:|
171
171
  "/build/logs/#{context}/#{test}/#{name}.gdb.log"
172
172
  end
173
- allow(@file_wrapper).to receive(:mkdir)
174
- allow(@file_wrapper).to receive(:write)
173
+ allow(@build_log_writer).to receive(:write)
175
174
  allow(@generator_test_results).to receive(:regenerate_test_executable_stdout).and_return('regenerated')
176
175
  end
177
176
 
177
+ # A test is named by its path from the project root
178
+ def test_name_of(filename)
179
+ return "test/#{File.basename( filename, '.*' )}"
180
+ end
181
+
178
182
  # ── #do_gdb ────────────────────────────────────────────────────────────────
179
183
 
180
184
  describe '#do_gdb' do
@@ -203,7 +207,7 @@ describe GeneratorTestResultsBacktrace do
203
207
  time: 0.1, exit_code: 0, stderr: '', status: @ok_status }
204
208
  )
205
209
 
206
- expect(@file_wrapper).to receive(:write).once.with(%r{test_crashes_elsewhere}, anything, anything)
210
+ expect(@build_log_writer).to receive(:write).once.with(%r{test_crashes_elsewhere}, anything)
207
211
 
208
212
  expected_output_lines = []
209
213
  allow(@generator_test_results).to receive(:regenerate_test_executable_stdout) do |**kwargs|
@@ -211,7 +215,7 @@ describe GeneratorTestResultsBacktrace do
211
215
  'regenerated'
212
216
  end
213
217
 
214
- @backtrace.do_gdb( filename, executable, shell_result, [companion_case] + test_cases, context: :test )
218
+ @backtrace.do_gdb( filename, executable, shell_result, [companion_case] + test_cases, context: :test, test_name: test_name_of( filename ) )
215
219
 
216
220
  expect(expected_output_lines).to include('test_lib.c:9:test_asserting:PASS')
217
221
  end
@@ -223,7 +227,7 @@ describe GeneratorTestResultsBacktrace do
223
227
  time: 0.1, exit_code: 0, stderr: '', status: @ok_status }
224
228
  )
225
229
 
226
- expect(@file_wrapper).to receive(:write).once.with(%r{test_crashes_elsewhere}, anything, anything)
230
+ expect(@build_log_writer).to receive(:write).once.with(%r{test_crashes_elsewhere}, anything)
227
231
 
228
232
  expected_output_lines = []
229
233
  allow(@generator_test_results).to receive(:regenerate_test_executable_stdout) do |**kwargs|
@@ -231,7 +235,7 @@ describe GeneratorTestResultsBacktrace do
231
235
  'regenerated'
232
236
  end
233
237
 
234
- @backtrace.do_gdb( filename, executable, shell_result, [companion_case] + test_cases, context: :test )
238
+ @backtrace.do_gdb( filename, executable, shell_result, [companion_case] + test_cases, context: :test, test_name: test_name_of( filename ) )
235
239
 
236
240
  expect(expected_output_lines).to include('test_lib.c:9:test_asserting:IGNORE')
237
241
  end
@@ -243,7 +247,7 @@ describe GeneratorTestResultsBacktrace do
243
247
  time: 0.1, exit_code: 1, stderr: '', status: @ok_status }
244
248
  )
245
249
 
246
- expect(@file_wrapper).to receive(:write).once.with(%r{test_crashes_elsewhere}, anything, anything)
250
+ expect(@build_log_writer).to receive(:write).once.with(%r{test_crashes_elsewhere}, anything)
247
251
 
248
252
  expected_output_lines = []
249
253
  allow(@generator_test_results).to receive(:regenerate_test_executable_stdout) do |**kwargs|
@@ -251,7 +255,7 @@ describe GeneratorTestResultsBacktrace do
251
255
  'regenerated'
252
256
  end
253
257
 
254
- @backtrace.do_gdb( filename, executable, shell_result, [companion_case] + test_cases, context: :test )
258
+ @backtrace.do_gdb( filename, executable, shell_result, [companion_case] + test_cases, context: :test, test_name: test_name_of( filename ) )
255
259
 
256
260
  expect(expected_output_lines).to include('test_lib.c:9:test_asserting:FAIL: Expected 1 Was 2')
257
261
  end
@@ -259,7 +263,7 @@ describe GeneratorTestResultsBacktrace do
259
263
  # Ignored-crash false positive: a TEST_IGNORE_MESSAGE(...) test case carries a
260
264
  # trailing message, same as FAIL. Before this fix, only FAIL tolerated one --
261
265
  # a message-carrying IGNORE fell through to "unresolved" and was misreported
262
- # as crash evidence (a second @file_wrapper.write, for a companion_case that
266
+ # as crash evidence (a second log write, for a companion_case that
263
267
  # never actually crashed).
264
268
  it 'handles an IGNORE test case with a trailing message and does not write a log' do
265
269
  allow(@tool_executor).to receive(:exec).and_return(
@@ -268,7 +272,7 @@ describe GeneratorTestResultsBacktrace do
268
272
  time: 0.1, exit_code: 0, stderr: '', status: @ok_status }
269
273
  )
270
274
 
271
- expect(@file_wrapper).to receive(:write).once.with(%r{test_crashes_elsewhere}, anything, anything)
275
+ expect(@build_log_writer).to receive(:write).once.with(%r{test_crashes_elsewhere}, anything)
272
276
 
273
277
  expected_output_lines = []
274
278
  allow(@generator_test_results).to receive(:regenerate_test_executable_stdout) do |**kwargs|
@@ -276,7 +280,7 @@ describe GeneratorTestResultsBacktrace do
276
280
  'regenerated'
277
281
  end
278
282
 
279
- @backtrace.do_gdb( filename, executable, shell_result, [companion_case] + test_cases, context: :test )
283
+ @backtrace.do_gdb( filename, executable, shell_result, [companion_case] + test_cases, context: :test, test_name: test_name_of( filename ) )
280
284
 
281
285
  expect(expected_output_lines).to include('test_lib.c:9:test_asserting:IGNORE: Not yet implemented')
282
286
  end
@@ -297,7 +301,7 @@ describe GeneratorTestResultsBacktrace do
297
301
  'regenerated'
298
302
  end
299
303
 
300
- result = @backtrace.do_gdb( filename, executable, shell_result, test_cases, context: :test )
304
+ result = @backtrace.do_gdb( filename, executable, shell_result, test_cases, context: :test, test_name: test_name_of( filename ) )
301
305
 
302
306
  expect(result[:exit_code]).to eq(1)
303
307
  expect(expected_output_lines.first).to include(':FAIL:')
@@ -317,7 +321,7 @@ describe GeneratorTestResultsBacktrace do
317
321
  .with(filename, shell_result, test_cases)
318
322
  .and_return(fallback_result)
319
323
 
320
- result = @backtrace.do_gdb( filename, executable, shell_result, test_cases, context: :test )
324
+ result = @backtrace.do_gdb( filename, executable, shell_result, test_cases, context: :test, test_name: test_name_of( filename ) )
321
325
 
322
326
  expect(result).to eq(fallback_result)
323
327
  end
@@ -335,17 +339,17 @@ describe GeneratorTestResultsBacktrace do
335
339
  'regenerated'
336
340
  end
337
341
 
338
- expect(@file_wrapper).to receive(:write)
339
- .with('/build/logs/test/TestUsartModel/testCrash.gdb.log', /=== testCrash ===/, 'a')
342
+ expect(@build_log_writer).to receive(:write)
343
+ .with('/build/logs/test/test/TestUsartModel/testCrash.gdb.log', /=== testCrash ===/)
340
344
 
341
- @backtrace.do_gdb( filename_sigsegv, executable, shell_result, test_cases_sigsegv, context: :test )
345
+ @backtrace.do_gdb( filename_sigsegv, executable, shell_result, test_cases_sigsegv, context: :test, test_name: test_name_of( filename_sigsegv ) )
342
346
 
343
347
  crash_line = expected_output_lines.first
344
348
  expect(crash_line).to include('>> [SIGSEGV] Segmentation fault')
345
349
  expect(crash_line).not_to include('testCrash() at TestUsartModel.c:40')
346
350
  expect(crash_line).to include("#{NEWLINE_TOKEN}`uint32_t i = *null_ptr;`")
347
351
  expect(crash_line).not_to include('(log: ')
348
- expect(crash_line).to include('(/build/logs/test/TestUsartModel/testCrash.gdb.log)')
352
+ expect(crash_line).to include('(/build/logs/test/test/TestUsartModel/testCrash.gdb.log)')
349
353
  end
350
354
 
351
355
  it 'attributes a crash in a parameterized test to its wrapper symbol, not its display name' do
@@ -365,10 +369,10 @@ describe GeneratorTestResultsBacktrace do
365
369
  'regenerated'
366
370
  end
367
371
 
368
- expect(@file_wrapper).to receive(:write)
369
- .with('/build/logs/test/test_module_d/test_value_out_of_range_good(101, 1).gdb.log', /=== test_value_out_of_range_good\(101, 1\) ===/, 'a')
372
+ expect(@build_log_writer).to receive(:write)
373
+ .with('/build/logs/test/test/test_module_d/test_value_out_of_range_good(101, 1).gdb.log', /=== test_value_out_of_range_good\(101, 1\) ===/)
370
374
 
371
- @backtrace.do_gdb( filename_param, executable, shell_result, test_cases_param, context: :test )
375
+ @backtrace.do_gdb( filename_param, executable, shell_result, test_cases_param, context: :test, test_name: test_name_of( filename_param ) )
372
376
 
373
377
  crash_line = expected_output_lines.first
374
378
  # The FAIL line reports the human-facing display name (with args)...
@@ -399,10 +403,10 @@ describe GeneratorTestResultsBacktrace do
399
403
  allow(@tool_executor).to receive(:exec)
400
404
  .and_return({ output: crash_output, time: 0.5, exit_code: 139, stderr: '', status: @ok_status })
401
405
 
402
- expect(@file_wrapper).to receive(:write)
403
- .with('/build/logs/test/test_module/test_x(2).gdb.log', /=== test_x\(2\) ===/, 'a')
406
+ expect(@build_log_writer).to receive(:write)
407
+ .with('/build/logs/test/test/test_module/test_x(2).gdb.log', /=== test_x\(2\) ===/)
404
408
 
405
- @backtrace.do_gdb( filename_suffix, executable, shell_result, test_cases_suffix, context: :test )
409
+ @backtrace.do_gdb( filename_suffix, executable, shell_result, test_cases_suffix, context: :test, test_name: test_name_of( filename_suffix ) )
406
410
  end
407
411
 
408
412
  it 'handles a SIGABRT crash from assert() — shows assertion text, no source line' do
@@ -417,7 +421,7 @@ describe GeneratorTestResultsBacktrace do
417
421
  'regenerated'
418
422
  end
419
423
 
420
- @backtrace.do_gdb( filename, executable, shell_result, test_cases_assert, context: :test )
424
+ @backtrace.do_gdb( filename, executable, shell_result, test_cases_assert, context: :test, test_name: test_name_of( filename ) )
421
425
 
422
426
  crash_line = expected_output_lines.first
423
427
  expect(crash_line).to include('>> [SIGABRT]')
@@ -425,7 +429,7 @@ describe GeneratorTestResultsBacktrace do
425
429
  expect(crash_line).not_to include('Aborted')
426
430
  expect(crash_line).not_to include('asserting() at lib.c:5')
427
431
  expect(crash_line).not_to include('(log: ')
428
- expect(crash_line).to include('(/build/logs/test/test_lib/test_asserting.gdb.log)')
432
+ expect(crash_line).to include('(/build/logs/test/test/test_lib/test_asserting.gdb.log)')
429
433
  end
430
434
 
431
435
  it 'handles a crash with no identifiable signal — fallback message, log path without "log: " prefix' do
@@ -438,12 +442,12 @@ describe GeneratorTestResultsBacktrace do
438
442
  'regenerated'
439
443
  end
440
444
 
441
- @backtrace.do_gdb( filename, executable, shell_result, test_cases, context: :test )
445
+ @backtrace.do_gdb( filename, executable, shell_result, test_cases, context: :test, test_name: test_name_of( filename ) )
442
446
 
443
447
  crash_line = expected_output_lines.first
444
448
  expect(crash_line).not_to include(' >> ')
445
449
  expect(crash_line).not_to include('(log: ')
446
- expect(crash_line).to include('(/build/logs/test/test_lib/test_asserting.gdb.log)')
450
+ expect(crash_line).to include('(/build/logs/test/test/test_lib/test_asserting.gdb.log)')
447
451
  end
448
452
 
449
453
  # A crashed test case that resolves to no useful gdb report is worth flagging on
@@ -458,7 +462,7 @@ describe GeneratorTestResultsBacktrace do
458
462
  expect(@loginator).to receive(:log)
459
463
  .with(a_string_including('test_asserting'), Verbosity::COMPLAIN)
460
464
 
461
- @backtrace.do_gdb( filename, executable, shell_result, test_cases, context: :test )
465
+ @backtrace.do_gdb( filename, executable, shell_result, test_cases, context: :test, test_name: test_name_of( filename ) )
462
466
  end
463
467
  end
464
468
 
@@ -489,8 +493,8 @@ describe GeneratorTestResultsBacktrace do
489
493
  allow(@tool_executor).to receive(:exec)
490
494
  .and_return({ output: GDB_NO_SIGNAL_OUTPUT, time: 0.1, exit_code: 1, stderr: '', status: @ok_status })
491
495
 
492
- @backtrace.do_gdb( 'test_lib.c', executable, shell_result.dup, test_cases, context: :test )
493
- @backtrace.do_gdb( 'test_module.c', executable, shell_result.dup, test_cases, context: :test )
496
+ @backtrace.do_gdb( 'test_lib.c', executable, shell_result.dup, test_cases, context: :test, test_name: test_name_of( 'test_lib.c' ) )
497
+ @backtrace.do_gdb( 'test_module.c', executable, shell_result.dup, test_cases, context: :test, test_name: test_name_of( 'test_module.c' ) )
494
498
 
495
499
  expect(@loginator).to receive(:log)
496
500
  .with(a_string_including('test suite run'), Verbosity::ERRORS, LogLabels::WARNING).once
@@ -504,12 +508,12 @@ describe GeneratorTestResultsBacktrace do
504
508
  )
505
509
  @backtrace.do_gdb(
506
510
  'TestUsartModel.c', executable, shell_result.dup,
507
- [{ test: 'testCrash', symbol: 'testCrash', line_number: 37 }], context: :test
511
+ [{ test: 'testCrash', symbol: 'testCrash', line_number: 37 }], context: :test, test_name: test_name_of( 'TestUsartModel.c' )
508
512
  )
509
513
 
510
514
  allow(@tool_executor).to receive(:exec)
511
515
  .and_return({ output: GDB_NO_SIGNAL_OUTPUT, time: 0.1, exit_code: 1, stderr: '', status: @ok_status })
512
- @backtrace.do_gdb( 'test_lib.c', executable, shell_result.dup, test_cases, context: :test )
516
+ @backtrace.do_gdb( 'test_lib.c', executable, shell_result.dup, test_cases, context: :test, test_name: test_name_of( 'test_lib.c' ) )
513
517
 
514
518
  expect(@loginator).to_not receive(:log)
515
519
  .with(anything, Verbosity::ERRORS, LogLabels::WARNING)
@@ -520,7 +524,7 @@ describe GeneratorTestResultsBacktrace do
520
524
  it 'only flushes once -- a second call logs nothing further without a new crash' do
521
525
  allow(@tool_executor).to receive(:exec)
522
526
  .and_return({ output: GDB_NO_SIGNAL_OUTPUT, time: 0.1, exit_code: 1, stderr: '', status: @ok_status })
523
- @backtrace.do_gdb( filename, executable, shell_result.dup, test_cases, context: :test )
527
+ @backtrace.do_gdb( filename, executable, shell_result.dup, test_cases, context: :test, test_name: test_name_of( filename ) )
524
528
 
525
529
  expect(@loginator).to receive(:log)
526
530
  .with(anything, Verbosity::ERRORS, LogLabels::WARNING).once
@@ -563,7 +567,7 @@ describe GeneratorTestResultsBacktrace do
563
567
  'regenerated'
564
568
  end
565
569
 
566
- @backtrace.do_simple( filename, executable, shell_result, test_cases_multi, context: :test )
570
+ @backtrace.do_simple( filename, executable, shell_result, test_cases_multi )
567
571
 
568
572
  expect(expected_output_lines).to include('test_lib.c:3:test_empty:PASS')
569
573
  end
@@ -582,7 +586,7 @@ describe GeneratorTestResultsBacktrace do
582
586
  'regenerated'
583
587
  end
584
588
 
585
- @backtrace.do_simple( filename, executable, shell_result, test_cases_fail, context: :test )
589
+ @backtrace.do_simple( filename, executable, shell_result, test_cases_fail )
586
590
 
587
591
  expect(expected_output_lines).to include('test_lib.c:8:test_bad:FAIL: Expected 1 Was 2')
588
592
  end
@@ -601,7 +605,7 @@ describe GeneratorTestResultsBacktrace do
601
605
  'regenerated'
602
606
  end
603
607
 
604
- @backtrace.do_simple( filename, executable, shell_result, test_cases_ignore, context: :test )
608
+ @backtrace.do_simple( filename, executable, shell_result, test_cases_ignore )
605
609
 
606
610
  expect(expected_output_lines).to include('test_lib.c:12:test_skip:IGNORE')
607
611
  end
@@ -625,7 +629,7 @@ describe GeneratorTestResultsBacktrace do
625
629
  'regenerated'
626
630
  end
627
631
 
628
- @backtrace.do_simple( filename, executable, shell_result, test_cases_ignore, context: :test )
632
+ @backtrace.do_simple( filename, executable, shell_result, test_cases_ignore )
629
633
 
630
634
  expect(expected_output_lines).to include('test_lib.c:12:test_skip:IGNORE: Not yet implemented')
631
635
  expect(expected_output_lines).to_not include(a_string_matching(/test_skip:FAIL/))
@@ -645,7 +649,7 @@ describe GeneratorTestResultsBacktrace do
645
649
  'regenerated'
646
650
  end
647
651
 
648
- result = @backtrace.do_simple( filename, executable, shell_result, test_cases, context: :test )
652
+ result = @backtrace.do_simple( filename, executable, shell_result, test_cases )
649
653
 
650
654
  expect(result[:exit_code]).to eq(1)
651
655
  expect(expected_output_lines.first).to include(':FAIL:')
@@ -666,7 +670,7 @@ describe GeneratorTestResultsBacktrace do
666
670
  .with(filename, shell_result, test_cases)
667
671
  .and_return(fallback_result)
668
672
 
669
- result = @backtrace.do_simple( filename, executable, shell_result, test_cases, context: :test )
673
+ result = @backtrace.do_simple( filename, executable, shell_result, test_cases )
670
674
 
671
675
  expect(result).to eq(fallback_result)
672
676
  end
@@ -683,7 +687,7 @@ describe GeneratorTestResultsBacktrace do
683
687
  'regenerated'
684
688
  end
685
689
 
686
- @backtrace.do_simple( filename, executable, shell_result, test_cases_crash, context: :test )
690
+ @backtrace.do_simple( filename, executable, shell_result, test_cases_crash )
687
691
 
688
692
  crash_line = expected_output_lines.first
689
693
  expect(crash_line).to include(':FAIL: Test case crashed')
@@ -703,7 +707,7 @@ describe GeneratorTestResultsBacktrace do
703
707
  'regenerated'
704
708
  end
705
709
 
706
- @backtrace.do_simple( filename, executable, shell_result, test_cases_crash, context: :test )
710
+ @backtrace.do_simple( filename, executable, shell_result, test_cases_crash )
707
711
 
708
712
  crash_line = expected_output_lines.first
709
713
  expect(crash_line).to include(':FAIL: Test case crashed')
@@ -734,7 +738,7 @@ describe GeneratorTestResultsBacktrace do
734
738
  end
735
739
 
736
740
  def rerun(tool)
737
- @backtrace.do_simple( filename, executable, { exit_code: 1, output: '', time: 0.0 }, test_cases, context: :test, tool: tool )
741
+ @backtrace.do_simple( filename, executable, { exit_code: 1, output: '', time: 0.0 }, test_cases, tool: tool )
738
742
  end
739
743
 
740
744
  it 'reruns through the dedicated backtrace tool when the fixture tool is the configured one' do
@@ -769,7 +773,7 @@ describe GeneratorTestResultsBacktrace do
769
773
  def crashed_lines(explain)
770
774
  lines = []
771
775
  allow(@generator_test_results).to receive(:regenerate_test_executable_stdout) { |**kwargs| lines = kwargs[:output]; 'regenerated' }
772
- @backtrace.do_simple( filename, executable, { exit_code: 1, output: '', time: 0.0 }, test_cases, context: :test, explain: explain )
776
+ @backtrace.do_simple( filename, executable, { exit_code: 1, output: '', time: 0.0 }, test_cases, explain: explain )
773
777
  return lines
774
778
  end
775
779
 
@@ -923,7 +927,7 @@ describe GeneratorTestResultsBacktrace do
923
927
  'regenerated'
924
928
  end
925
929
 
926
- @backtrace.do_gdb( filename_assert, executable, shell_result, test_cases_assert, context: :test )
930
+ @backtrace.do_gdb( filename_assert, executable, shell_result, test_cases_assert, context: :test, test_name: test_name_of( filename_assert ) )
927
931
 
928
932
  crash_line = expected_output_lines.first
929
933
  expect(crash_line).to include(">> Assertion '0' failed")
@@ -12,7 +12,7 @@ require 'fileutils'
12
12
  require 'ceedling/generators/generator_test_results_sanity_checker'
13
13
  require 'ceedling/generators/generator_test_results'
14
14
  require 'ceedling/yaml_wrapper'
15
- require 'ceedling/file_wrapper'
15
+ require 'ceedling/filesystem/file_wrapper'
16
16
  require 'ceedling/constants'
17
17
  require 'ceedling/config/configurator'
18
18
 
@@ -0,0 +1,205 @@
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 'tmpdir'
10
+ require 'fileutils'
11
+ require 'rbconfig'
12
+ require 'ceedling/handoff'
13
+ require 'ceedling/constants'
14
+ require 'app_cfg'
15
+
16
+ describe Handoff do
17
+ def installation_with_version(root, version)
18
+ FileUtils.mkdir_p( File.join( root, 'lib' ) )
19
+ File.write( File.join( root, 'lib', 'version.rb' ), "module Ceedling; module Version; TAG = '#{version}'; end; end\n" )
20
+ return Installation.new( root )
21
+ end
22
+
23
+ describe '.take_markers' do
24
+ it 'removes both markers from the environment and returns them' do
25
+ env = {'CEEDLING_HANDOFF_LAUNCHER' => '/gem', 'CEEDLING_HANDOFF_TARGET' => '/app', 'OTHER' => 'x'}
26
+
27
+ expect(described_class.take_markers( env )).to eq( {launcher: '/gem', target: '/app'} )
28
+ expect(env).to eq( {'OTHER' => 'x'} )
29
+ end
30
+
31
+ it 'returns nil when this process was not handed off' do
32
+ expect(described_class.take_markers( {} )).to be_nil
33
+ end
34
+ end
35
+
36
+ describe '.received?' do
37
+ it 'is true when the markers name this installation as the target' do
38
+ Dir.mktmpdir do |root|
39
+ expect(described_class.received?( {launcher: '/gem', target: root + '/'}, own: Installation.new( root ) )).to be( true )
40
+ end
41
+ end
42
+
43
+ # GH #1365: markers an older target leaks to a nested launcher name someone else
44
+ it 'is false when the markers name another installation' do
45
+ Dir.mktmpdir do |root|
46
+ expect(described_class.received?( {launcher: '/gem', target: '/elsewhere'}, own: Installation.new( root ) )).to be( false )
47
+ end
48
+ end
49
+
50
+ it 'is false without markers' do
51
+ expect(described_class.received?( nil, own: Installation.new( '/gem' ) )).to be( false )
52
+ end
53
+ end
54
+
55
+ describe 'messages' do
56
+ it 'names both installations and their versions when handing off' do
57
+ Dir.mktmpdir do |dir|
58
+ launcher = installation_with_version( File.join( dir, 'gem' ), '1.2.2' )
59
+ target = installation_with_version( File.join( dir, 'app' ), '1.2.0' )
60
+
61
+ expect(described_class.handing_off_message( launcher: launcher, target: target )).to eq(
62
+ "Ceedling launcher 1.2.2 at #{launcher.root}/ is handing off to Ceedling 1.2.0 at #{target.root}/, which runs in its own process."
63
+ )
64
+ end
65
+ end
66
+
67
+ it 'names the launcher a handed-off process came from' do
68
+ Dir.mktmpdir do |dir|
69
+ own = installation_with_version( File.join( dir, 'app' ), '1.2.2' )
70
+ launcher = installation_with_version( File.join( dir, 'gem' ), '1.2.3' )
71
+
72
+ expect(described_class.received_message( {launcher: launcher.root, target: own.root}, own: own )).to eq(
73
+ "Ceedling 1.2.2 at #{own.root}/ is now running, launched from Ceedling 1.2.3 at #{launcher.root}/."
74
+ )
75
+ end
76
+ end
77
+ end
78
+
79
+ # GH #1365: a launcher before 1.2.2 always loads the application into its own process
80
+ describe '.complete_in_process' do
81
+ before(:each) do
82
+ # This repository's launcher stands in for an older one: its CeedlingAppConfig is
83
+ # defined beneath this repository's bin/, so any other application root is foreign
84
+ @app_cfg = CeedlingAppConfig.new
85
+ @launcher = Installation.new( @app_cfg[:launcher_root_path] )
86
+ @loginator = double('loginator').as_null_object
87
+ @system_wrapper = double('system_wrapper', get_cmdline: ['test:all', '-v', 'obnoxious'])
88
+ @handoff_objects = {loginator: @loginator, system_wrapper: @system_wrapper}
89
+ allow(described_class).to receive(:exec)
90
+ end
91
+
92
+ def complete(app_root, env: {})
93
+ described_class.complete_in_process( app_root: app_root, app_cfg: @app_cfg, handoff_objects: @handoff_objects, env: env )
94
+ end
95
+
96
+ it 'restarts a foreign launcher\'s application in its own process, with the original command line' do
97
+ Dir.mktmpdir do |root|
98
+ app = Installation.new( root )
99
+
100
+ expect(@loginator).to receive(:log).with(
101
+ "#{@launcher.describe( 'Ceedling launcher' )} loaded #{app.describe} into its own process. " +
102
+ "Restarting in #{app.name}'s own process. Upgrade Ceedling and review your installations to avoid this.",
103
+ Verbosity::COMPLAIN
104
+ ).ordered
105
+ expect(described_class).to receive(:exec).with(
106
+ target: app, launcher: @launcher, argv: ['test:all', '-v', 'obnoxious'], loginator: @loginator
107
+ ).ordered
108
+
109
+ complete( root )
110
+ end
111
+ end
112
+
113
+ it 'does nothing for the launcher\'s own application' do
114
+ complete( @launcher.root )
115
+
116
+ expect(described_class).to_not have_received(:exec)
117
+ expect(@loginator).to_not have_received(:log)
118
+ end
119
+
120
+ def expect_fallback(app)
121
+ expect(@loginator).to receive(:log).with(
122
+ "#{@launcher.describe( 'Ceedling launcher' )} loaded #{app.describe} into its own process. " +
123
+ "Continuing in the launcher's process, which can fail or misbehave. Upgrade Ceedling, or run this " +
124
+ "installation's own launcher directly with `ruby #{app.bin_path}`. A project created with `--local` has a " +
125
+ "launch script at its root that does this: run `./ceedling` from the project root (`.\\ceedling.cmd` on Windows).",
126
+ Verbosity::COMPLAIN
127
+ )
128
+ end
129
+
130
+ it 'warns and continues when the launcher kept no command line to restart with' do
131
+ Dir.mktmpdir do |root|
132
+ @handoff_objects[:system_wrapper] = double('old_system_wrapper')
133
+ expect_fallback( Installation.new( root ) )
134
+
135
+ complete( root )
136
+
137
+ expect(described_class).to_not have_received(:exec)
138
+ end
139
+ end
140
+
141
+ # A hand-off already happened in this chain; restarting again could cycle
142
+ it 'warns and continues when a launcher handed off to this process' do
143
+ Dir.mktmpdir do |root|
144
+ @app_cfg.set_handoff( {launcher: '/gem', target: @launcher.root} )
145
+ expect_fallback( Installation.new( root ) )
146
+
147
+ complete( root )
148
+
149
+ expect(described_class).to_not have_received(:exec)
150
+ end
151
+ end
152
+
153
+ it 'warns and continues when hand-off markers are still in the environment' do
154
+ Dir.mktmpdir do |root|
155
+ expect_fallback( Installation.new( root ) )
156
+
157
+ complete( root, env: {'CEEDLING_HANDOFF_TARGET' => '/somewhere'} )
158
+
159
+ expect(described_class).to_not have_received(:exec)
160
+ end
161
+ end
162
+
163
+ it 'names the launcher relative to the working directory when it lies beneath it' do
164
+ Dir.mktmpdir do |root|
165
+ Dir.chdir( root ) do
166
+ app = Installation.new( File.join( Dir.pwd, 'vendor', 'ceedling' ) )
167
+ @handoff_objects[:system_wrapper] = double('old_system_wrapper')
168
+
169
+ expect(@loginator).to receive(:log).with( /directly with `ruby vendor\/ceedling\/bin\/ceedling`/, Verbosity::COMPLAIN )
170
+
171
+ complete( app.root )
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ describe '.exec' do
178
+ before(:each) do
179
+ @loginator = double('loginator')
180
+ allow(@loginator).to receive(:wrapup)
181
+ allow(described_class).to receive(:replace_process)
182
+ end
183
+
184
+ it 'replaces this process with the target\'s own launcher, given the original command line and both markers' do
185
+ launcher = Installation.new( '/gem' )
186
+ target = Installation.new( '/app' )
187
+
188
+ described_class.exec( target: target, launcher: launcher, argv: ['test:all', '--mixin', '=:a: b c'], loginator: @loginator )
189
+
190
+ expect(described_class).to have_received(:replace_process).with(
191
+ {'CEEDLING_HANDOFF_LAUNCHER' => launcher.root, 'CEEDLING_HANDOFF_TARGET' => target.root},
192
+ RbConfig.ruby, target.bin_path, 'test:all', '--mixin', '=:a: b c'
193
+ )
194
+ end
195
+
196
+ it 'drains logging and flushes the standard streams before the process is replaced' do
197
+ expect(@loginator).to receive(:wrapup).ordered
198
+ expect($stdout).to receive(:flush).ordered
199
+ expect($stderr).to receive(:flush).ordered
200
+ expect(described_class).to receive(:replace_process).ordered
201
+
202
+ described_class.exec( target: Installation.new( '/app' ), launcher: Installation.new( '/gem' ), argv: [], loginator: @loginator )
203
+ end
204
+ end
205
+ end
@@ -18,15 +18,15 @@ describe IncludeFactory do
18
18
  end
19
19
 
20
20
  describe '#user_include_from_filepath' do
21
- it 'strips a flat test\'s own mock subdirectory down to the bare mock filename' do
22
- include_obj = @factory.user_include_from_filepath( 'build/test/mocks/TestFoo/MockBar.h', test: 'TestFoo' )
21
+ it 'strips a test\'s own mock subdirectory down to the bare mock filename' do
22
+ include_obj = @factory.user_include_from_filepath( 'build/test/mocks/test/TestFoo/MockBar.h', test: 'test/TestFoo' )
23
23
  expect( include_obj.filename ).to eq( 'MockBar.h' )
24
24
  expect( include_obj.filepath ).to eq( 'MockBar.h' )
25
25
  end
26
26
 
27
27
  it 'strips a nested test\'s own multi-segment mock subdirectory down to the bare mock filename' do
28
28
  include_obj = @factory.user_include_from_filepath(
29
- 'build/test/mocks/adc/TestAdcConductor/MockAdcHardware.h', test: 'adc/TestAdcConductor'
29
+ 'build/test/mocks/test/adc/TestAdcConductor/MockAdcHardware.h', test: 'test/adc/TestAdcConductor'
30
30
  )
31
31
  expect( include_obj.filename ).to eq( 'MockAdcHardware.h' )
32
32
  expect( include_obj.filepath ).to eq( 'MockAdcHardware.h' )
@@ -20,6 +20,7 @@ describe IncludePathinator do
20
20
  @file_wrapper = double( "FileWrapper" )
21
21
 
22
22
  allow(@configurator).to receive(:extension_header).and_return( FilenameExtension.new('.h') )
23
+ allow(@configurator).to receive(:project_build_root).and_return( 'build' )
23
24
 
24
25
  @pathinator = described_class.new(
25
26
  {
@@ -63,6 +64,34 @@ describe IncludePathinator do
63
64
 
64
65
  expect(@pathinator.ordered_header_files(['inc', 'inc'])).to eq( ['inc/foo.h'] )
65
66
  end
67
+
68
+ # A test's search paths are listed several times a test, and nothing in a build writes
69
+ # into the project's own directories
70
+ it 'lists a directory outside the build root once' do
71
+ expect(@file_wrapper).to receive(:directory_listing).with(['inc/*.h']).once.and_return( ['inc/foo.h'] )
72
+
73
+ 2.times { expect(@pathinator.ordered_header_files(['inc'])).to eq( ['inc/foo.h'] ) }
74
+ end
75
+
76
+ it 'lists a directory under the build root on every call, so headers the build writes appear' do
77
+ expect(@file_wrapper).to receive(:directory_listing).with(['build/test/mocks/*.h']).twice.and_return(
78
+ ['build/test/mocks/a.h'],
79
+ ['build/test/mocks/a.h', 'build/test/mocks/b.h']
80
+ )
81
+ expect(ProjectPaths).to receive(:contains?).once.and_call_original
82
+
83
+ expect(@pathinator.ordered_header_files(['build/test/mocks'])).to eq( ['build/test/mocks/a.h'] )
84
+ expect(@pathinator.ordered_header_files(['build/test/mocks'])).to eq( ['build/test/mocks/a.h', 'build/test/mocks/b.h'] )
85
+ end
86
+
87
+ it 'lists every directory again after the listings are forgotten' do
88
+ expect(@file_wrapper).to receive(:directory_listing).with(['inc/*.h']).twice.and_return( ['inc/foo.h'], ['inc/foo.h', 'inc/new.h'] )
89
+
90
+ @pathinator.ordered_header_files(['inc'])
91
+ @pathinator.forget_header_listings
92
+
93
+ expect(@pathinator.ordered_header_files(['inc'])).to eq( ['inc/foo.h', 'inc/new.h'] )
94
+ end
66
95
  end
67
96
 
68
97
  # Distinct from ordered_header_files: this returns the whole project collection, since a
@@ -266,4 +295,21 @@ describe IncludePathinator do
266
295
  expect(@pathinator.collect_test_include_paths).to eq(['has_headers'])
267
296
  end
268
297
  end
298
+
299
+ # A search path spelled with `./` or absolutely names the same place (the path handling
300
+ # audit around GH #1370)
301
+ describe 'G23: #prioritized_header_files with other spellings of a search path' do
302
+ before(:each) do
303
+ allow(@configurator).to receive(:collection_all_headers)
304
+ .and_return(Rake::FileList.new(['vendor/config.h', 'local/config.h']))
305
+ end
306
+
307
+ it 'ranks a `./`-spelled search path as the plain one' do
308
+ expect( @pathinator.prioritized_header_files(['./local', 'vendor']) ).to eq(['local/config.h', 'vendor/config.h'])
309
+ end
310
+
311
+ it 'ranks an absolute search path as the relative one' do
312
+ expect( @pathinator.prioritized_header_files([File.join( Dir.pwd, 'local' ), 'vendor']) ).to eq(['local/config.h', 'vendor/config.h'])
313
+ end
314
+ end
269
315
  end