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
@@ -7,7 +7,7 @@
7
7
 
8
8
  require 'spec_helper'
9
9
  require 'tmpdir'
10
- require 'ceedling/file_wrapper'
10
+ require 'ceedling/filesystem/file_wrapper'
11
11
  require 'ceedling/system_wrapper'
12
12
  require 'ceedling/exceptions'
13
13
 
@@ -211,13 +211,9 @@ describe FileWrapper do
211
211
  end
212
212
  end
213
213
 
214
- # Same transient-visibility race as #open_with_retry, here hitting FileUtils.cp_r's
215
- # own internal stat-then-open sequence instead of a bare File.open (Issue #1292:
216
- # a vendor destination briefly reporting the wrong file/directory type mid-copy).
217
- # Stub-based, matching #open_with_retry's own spec above -- no real filesystem or
218
- # timing involved.
219
- # A transient error mid-copy (a path briefly the wrong type, a file another process holds
220
- # open on Windows) is retried; one that never clears names the destination.
214
+ # A transient error mid-copy, such as a file another process holds open on Windows, is
215
+ # retried; one that never clears names the file. An error retrying cannot clear fails at
216
+ # once (GH #1376). FileTree's own specs cover the walk and the healing.
221
217
  describe '#sync_tree retries' do
222
218
  before(:each) do
223
219
  allow(@file_wrapper).to receive(:sleep)
@@ -241,12 +237,12 @@ describe FileWrapper do
241
237
  end
242
238
  end
243
239
 
244
- it 'retries past a SystemCallError and succeeds once the copy stops raising' do
240
+ it 'retries past a transient error and succeeds once the copy stops raising' do
245
241
  with_tree do |source, dest|
246
242
  calls = 0
247
243
  allow(@file_wrapper).to receive(:replace_file).and_wrap_original do |original, *args|
248
244
  calls += 1
249
- raise Errno::EISDIR, 'transient' if calls < 3
245
+ raise Errno::EACCES, 'transient' if calls < 3
250
246
  original.call( *args )
251
247
  end
252
248
 
@@ -260,15 +256,24 @@ describe FileWrapper do
260
256
 
261
257
  it 'raises a CeedlingException naming the destination once every retry is exhausted, and logs it' do
262
258
  with_tree do |source, dest|
263
- allow(@file_wrapper).to receive(:replace_file).and_raise( Errno::ENOTDIR, 'still wrong type' )
259
+ allow(@file_wrapper).to receive(:replace_file).and_raise( Errno::EACCES, 'still held open' )
264
260
 
265
261
  expect { @file_wrapper.sync_tree( source, dest ) }.to raise_error( CeedlingException, /#{Regexp.escape( dest )}/ )
266
- expect(@loginator).to have_received(:log).with( a_string_including( dest ), Verbosity::ERRORS )
262
+ expect(@loginator).to have_received(:log).with( a_string_including( dest ), Verbosity::ERRORS, LogLabels::AUTO )
267
263
  # Initial attempt plus one retry for each configured delay
268
264
  expect(@file_wrapper).to have_received(:replace_file).exactly( FileWrapper::TRANSIENT_IO_RETRY_DELAYS.size + 1 ).times
269
265
  end
270
266
  end
271
267
 
268
+ it 'fails at once on an error retrying cannot clear' do
269
+ with_tree do |source, dest|
270
+ allow(@file_wrapper).to receive(:replace_file).and_raise( Errno::ENOTDIR, 'wrong type' )
271
+
272
+ expect { @file_wrapper.sync_tree( source, dest ) }.to raise_error( Errno::ENOTDIR )
273
+ expect(@file_wrapper).not_to have_received(:sleep)
274
+ end
275
+ end
276
+
272
277
  it 'propagates an error that is not a SystemCallError without retrying' do
273
278
  with_tree do |source, dest|
274
279
  allow(@file_wrapper).to receive(:replace_file).and_raise( ArgumentError, 'not transient' )
@@ -279,6 +284,19 @@ describe FileWrapper do
279
284
  end
280
285
  end
281
286
 
287
+ describe '#tree_files' do
288
+ it 'lists every regular file below a directory as full paths in sorted path order' do
289
+ Dir.mktmpdir do |dir|
290
+ ['a/b.c', 'a.c', '.hidden', 'dir[1]/x.c'].each do |name|
291
+ FileUtils.mkdir_p( File.dirname( File.join( dir, name ) ) )
292
+ File.write( File.join( dir, name ), name )
293
+ end
294
+
295
+ expect( @file_wrapper.tree_files( dir ) ).to eq( ['.hidden', 'a.c', 'a/b.c', 'dir[1]/x.c'].map { |name| File.join( dir, name ) } )
296
+ end
297
+ end
298
+ end
299
+
282
300
  # The temporary copy beside the destination is the longest path replace_file writes
283
301
  describe '#replace_file' do
284
302
  it 'checks the length of its temporary path, then leaves only the destination' do
@@ -298,6 +316,71 @@ describe FileWrapper do
298
316
  expect( Dir.children( File.dirname( destination ) ) ).to eq( ['unity.h'] )
299
317
  end
300
318
  end
319
+
320
+ # Windows refuses to rename over a read-only file
321
+ it 'leaves the destination owner-writable, from a read-only source over a read-only destination' do
322
+ Dir.mktmpdir do |dir|
323
+ source = File.join( dir, 'unity.h' )
324
+ destination = File.join( dir, 'out', 'unity.h' )
325
+ File.write( source, "new\n" )
326
+ FileUtils.mkdir_p( File.dirname( destination ) )
327
+ File.write( destination, "old\n" )
328
+ File.chmod( 0o444, source )
329
+ File.chmod( 0o444, destination )
330
+ allow(@file_wrapper).to receive(:check_path_length)
331
+
332
+ @file_wrapper.replace_file( source, destination )
333
+
334
+ expect( File.read( destination ) ).to eq( "new\n" )
335
+ expect( File.stat( destination ).mode & 0o200 ).to eq( 0o200 )
336
+ end
337
+ end
338
+
339
+ it 'replaces a destination link without changing the mode of the file it names' do
340
+ Dir.mktmpdir do |dir|
341
+ source = File.join( dir, 'unity.h' )
342
+ outside = File.join( dir, 'outside.h' )
343
+ destination = File.join( dir, 'out', 'unity.h' )
344
+ File.write( source, "new\n" )
345
+ File.write( outside, "outside\n" )
346
+ File.chmod( 0o444, outside )
347
+ FileUtils.mkdir_p( File.dirname( destination ) )
348
+ begin
349
+ File.symlink( outside, destination )
350
+ rescue NotImplementedError, SystemCallError => e
351
+ skip "symlinks are unavailable here (#{e.class})"
352
+ end
353
+ allow(@file_wrapper).to receive(:check_path_length)
354
+
355
+ @file_wrapper.replace_file( source, destination )
356
+
357
+ expect( File.read( destination ) ).to eq( "new\n" )
358
+ expect( File.stat( outside ).mode & 0o777 ).to eq( 0o444 )
359
+ end
360
+ end
361
+ end
362
+
363
+ describe '#mkdir_for' do
364
+ it 'makes every directory above a file and returns the filepath' do
365
+ Dir.mktmpdir do |dir|
366
+ filepath = File.join( dir, 'artifacts', 'test', 'unit', 'test_foo.log' )
367
+
368
+ expect( @file_wrapper.mkdir_for( filepath ) ).to eq( filepath )
369
+ expect( File.directory?( File.dirname( filepath ) ) ).to be true
370
+ expect( File.exist?( filepath ) ).to be false
371
+ end
372
+ end
373
+
374
+ it 'tolerates threads making the same directory at once' do
375
+ Dir.mktmpdir do |dir|
376
+ filepath = File.join( dir, 'a', 'b', 'c', 'test_foo.log' )
377
+
378
+ threads = Array.new( 8 ) { Thread.new { @file_wrapper.mkdir_for( filepath ) } }
379
+
380
+ expect { threads.each( &:join ) }.to_not raise_error
381
+ expect( File.directory?( File.dirname( filepath ) ) ).to be true
382
+ end
383
+ end
301
384
  end
302
385
 
303
386
  # The removal methods are thin pass-throughs to FileUtils. These examples exist
@@ -6,7 +6,7 @@
6
6
  # =========================================================================
7
7
 
8
8
  require 'spec_helper'
9
- require 'ceedling/path_matcher'
9
+ require 'ceedling/filesystem/path_matcher'
10
10
  require 'ceedling/exceptions'
11
11
 
12
12
  describe PathMatcher do
@@ -190,4 +190,15 @@ describe PathMatcher do
190
190
  end
191
191
  end
192
192
 
193
+
194
+ # A `.` segment names nothing (the path handling audit around GH #1370)
195
+ describe 'G9: `.` segments' do
196
+ it 'matches a query with a leading `./`' do
197
+ expect(PathMatcher.candidates( './foo.h', ['inc/foo.h'] )).to eq( ['inc/foo.h'] )
198
+ end
199
+
200
+ it 'matches a query with a `.` segment within it' do
201
+ expect(PathMatcher.candidates( 'sub/./bar.h', ['inc/sub/bar.h'] )).to eq( ['inc/sub/bar.h'] )
202
+ end
203
+ end
193
204
  end
@@ -6,7 +6,7 @@
6
6
  # =========================================================================
7
7
 
8
8
  require 'spec_helper'
9
- require 'ceedling/path_mirror'
9
+ require 'ceedling/filesystem/path_mirror'
10
10
 
11
11
  describe PathMirror do
12
12
 
@@ -20,9 +20,9 @@ describe PathMirror do
20
20
  expect(described_class.relative_subdir('src/drivers/sensors/foo.c', ['src/**'])).to eq('drivers/sensors')
21
21
  end
22
22
 
23
- it 'strips +:/-: aggregation decorators from configured roots before comparing' do
23
+ it 'strips a +: decorator, and never treats a -: exclusion as a root' do
24
24
  expect(described_class.relative_subdir('src/drivers/foo.c', ['+:src/**'])).to eq('drivers')
25
- expect(described_class.relative_subdir('src/drivers/foo.c', ['-:src/**'])).to eq('drivers')
25
+ expect(described_class.relative_subdir('src/drivers/foo.c', ['-:src/**'])).to eq('')
26
26
  end
27
27
 
28
28
  it 'picks the most specific (longest) matching root when configured roots are nested' do
@@ -49,7 +49,7 @@ describe PathMirror do
49
49
  it 'produces the same result as .relative_subdir when roots are cleaned once upfront' do
50
50
  clean = described_class.clean_roots(['+:src/**', '-:src/legacy/**'])
51
51
  expect(described_class.relative_subdir_from_clean_roots('src/drivers/foo.c', clean)).to eq('drivers')
52
- expect(described_class.relative_subdir_from_clean_roots('src/legacy/vendor/foo.c', clean)).to eq('vendor')
52
+ expect(described_class.relative_subdir_from_clean_roots('src/legacy/vendor/foo.c', clean)).to eq('legacy/vendor')
53
53
  end
54
54
 
55
55
  it 'drops decorated roots that clean to nothing' do
@@ -100,4 +100,32 @@ describe PathMirror do
100
100
  expect( PathMirror.path_segments('drivers//uart/') ).to eq(['drivers', 'uart'])
101
101
  end
102
102
  end
103
+
104
+ # Root matching across spellings of one place (GH #1370 and the path handling audit)
105
+ describe 'root containment' do
106
+ it 'G1 (#1370): never answers a subdirectory that climbs out, below a `..` root' do
107
+ subdir = PathMirror.relative_subdir( '../../../opt/vendor/inc/x.h', ['../**'] )
108
+
109
+ expect(subdir.split( '/' )).to_not include( '..' )
110
+ end
111
+
112
+ it 'G2: mirrors below a `./`-spelled root as below the plain one' do
113
+ expect(PathMirror.relative_subdir( 'src/a/x.c', ['./src/**'] )).to eq( 'a' )
114
+ end
115
+
116
+ it 'G2: mirrors below an absolute root spelling of a relative file\'s directory' do
117
+ expect(PathMirror.relative_subdir( 'src/a/x.c', [File.join( Dir.pwd, 'src/**' )] )).to eq( 'a' )
118
+ end
119
+
120
+ it 'G2: mirrors a file spelled relatively below an absolute root outside the working directory' do
121
+ file = File.join( '..', '..', 'opt', 'x', 'a', 'f.c' )
122
+ root = File.join( File.expand_path( '../..' ), 'opt', 'x', '**' )
123
+
124
+ expect(PathMirror.relative_subdir( file, [root] )).to eq( 'a' )
125
+ end
126
+
127
+ it 'G3: never treats a `-:` exclusion as a root' do
128
+ expect(PathMirror.relative_subdir( 'src/exp/sub/x.c', ['+:src/**', '-:src/exp'] )).to eq( 'exp/sub' )
129
+ end
130
+ end
103
131
  end
@@ -0,0 +1,147 @@
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 'ceedling/filesystem/project_paths'
12
+
13
+ # GH #1370 and the path handling audit around it: one place spelled many ways compared
14
+ # as text, so `..` "contained" paths above it and build files escaped the build directory
15
+ describe ProjectPaths do
16
+ WINDOWS_ONLY = { if: Gem.win_platform? }.freeze
17
+ POSIX_ONLY = { unless: Gem.win_platform? }.freeze
18
+
19
+ around(:each) do |example|
20
+ Dir.mktmpdir do |dir|
21
+ @ws = File.realpath( dir )
22
+ FileUtils.mkdir_p( File.join( @ws, 'app' ) )
23
+ Dir.chdir( File.join( @ws, 'app' ) ) { example.run }
24
+ end
25
+ end
26
+
27
+ describe '.standardize' do
28
+ it 'writes a path with forward slashes and no trailing separator' do
29
+ expect(ProjectPaths.standardize( " src\\drv\\ " )).to eq( 'src/drv' )
30
+ end
31
+
32
+ it 'reads `~` as the home directory, after any decorator' do
33
+ expect(ProjectPaths.standardize( '~/inc' )).to eq( File.join( Dir.home, 'inc' ) )
34
+ expect(ProjectPaths.standardize( '-:~/inc' )).to eq( "-:#{File.join( Dir.home, 'inc' )}" )
35
+ end
36
+
37
+ it 'leaves `~user` as written' do
38
+ expect(ProjectPaths.standardize( '~someone/inc' )).to eq( '~someone/inc' )
39
+ end
40
+
41
+ it 'keeps a filesystem root' do
42
+ expect(ProjectPaths.standardize( '/' )).to eq( '/' )
43
+ end
44
+
45
+ it 'reads `~` after a decorator and a space, and drops every trailing separator' do
46
+ expect(ProjectPaths.standardize( '+: ~/inc' )).to eq( "+:#{File.join( Dir.home, 'inc' )}" )
47
+ expect(ProjectPaths.standardize( 'src//' )).to eq( 'src' )
48
+ end
49
+ end
50
+
51
+ describe '.absolute' do
52
+ # Spelled as the full computation spells it, whatever the shortcut for a clean path
53
+ it 'spells a rooted path with a trailing separator or surrounding space without either' do
54
+ expect(ProjectPaths.absolute( '/a/' )).to eq( '/a' ) unless Gem.win_platform?
55
+ expect(ProjectPaths.absolute( " #{@ws}/x " )).to eq( "#{@ws}/x" )
56
+ end
57
+
58
+ it 'reads `~user` as a directory of that name, never the user\'s home' do
59
+ expect(ProjectPaths.absolute( '~nosuchuser/inc' )).to eq( File.join( @ws, 'app', '~nosuchuser/inc' ) )
60
+ end
61
+
62
+ it 'reads a doubled leading separator as the filesystem root', **POSIX_ONLY do
63
+ expect(ProjectPaths.absolute( '//usr/include' )).to eq( '/usr/include' )
64
+ expect(ProjectPaths.contains?( '/usr', '//usr/include' )).to be true
65
+ end
66
+
67
+ it 'keeps a UNC path\'s `..` below its share', **WINDOWS_ONLY do
68
+ expect(ProjectPaths.absolute( '//srv/sh/a/../../../x' )).to eq( '//srv/sh/x' )
69
+ end
70
+ end
71
+
72
+ describe '.canonical' do
73
+ it 'spells every form of one place alike' do
74
+ FileUtils.mkdir_p( 'src/drv' )
75
+ spellings = ['src/drv', './src/drv', 'src/drv/', 'src\\drv', File.join( @ws, 'app/src/drv' ), 'src/../src/drv']
76
+
77
+ expect(spellings.map { |spelling| ProjectPaths.canonical( spelling ) }.uniq).to eq( ['src/drv'] )
78
+ end
79
+
80
+ it 'resolves a symlink along the existing part of a path', **POSIX_ONLY do
81
+ FileUtils.mkdir_p( 'src' )
82
+ File.symlink( File.join( @ws, 'app' ), File.join( @ws, 'link' ) )
83
+
84
+ expect(ProjectPaths.canonical( File.join( @ws, 'link/src/not_yet' ) )).to eq( 'src/not_yet' )
85
+ end
86
+
87
+ it 'climbs to a place outside the project' do
88
+ expect(ProjectPaths.canonical( File.join( @ws, 'shared/src' ) )).to eq( '../shared/src' )
89
+ end
90
+ end
91
+
92
+ describe '.subdir_below' do
93
+ it 'names the directories from a root down to a path below it' do
94
+ expect(ProjectPaths.subdir_below( 'src', 'src/drv/uart' )).to eq( 'drv/uart' )
95
+ expect(ProjectPaths.subdir_below( 'src', 'src' )).to eq( '' )
96
+ end
97
+
98
+ it 'finds nothing below a root that only shares a path\'s leading text' do
99
+ expect(ProjectPaths.subdir_below( 'src', 'src2/x' )).to be_nil
100
+ end
101
+
102
+ it 'finds nothing below `..` for a path climbing past it' do
103
+ expect(ProjectPaths.subdir_below( '..', '../../vendor/inc' )).to be_nil
104
+ expect(ProjectPaths.subdir_below( '..', '../vendor/inc' )).to eq( 'vendor/inc' )
105
+ end
106
+
107
+ it 'compares every spelling of a root and a path' do
108
+ expect(ProjectPaths.subdir_below( './src', File.join( @ws, 'app/src/a' ) )).to eq( 'a' )
109
+ expect(ProjectPaths.subdir_below( File.join( @ws, 'app/src' ), 'src/a' )).to eq( 'a' )
110
+ end
111
+
112
+ it 'never answers a subdirectory that climbs or is absolute' do
113
+ roots = ['..', '../..', '.', 'src', File.join( @ws, 'vendor' ), '/']
114
+ paths = ['../../far/x', '../vendor/inc', 'src/a', '/opt/x', File.join( @ws, 'vendor/inc' )]
115
+
116
+ roots.product( paths ).each do |root, path|
117
+ subdir = ProjectPaths.subdir_below( root, path )
118
+ next if subdir.nil?
119
+
120
+ expect(subdir.split( '/' )).to_not include( '..' ), "#{root} below #{path}: #{subdir}"
121
+ expect(subdir).to_not start_with( '/' ), "#{root} below #{path}: #{subdir}"
122
+ end
123
+ end
124
+
125
+ it 'compares Windows paths without regard to case', **WINDOWS_ONLY do
126
+ expect(ProjectPaths.subdir_below( 'C:/Proj/Src', 'c:/proj/src/a' )).to eq( 'a' )
127
+ end
128
+ end
129
+
130
+ # A build asks for the working directory tens of thousands of times, and asking the
131
+ # operating system costs tens of microseconds on macOS
132
+ describe '.working_dir' do
133
+ it 'reads the working directory once, however often it is asked' do
134
+ ProjectPaths.working_dir
135
+ expect(Dir).not_to receive(:pwd)
136
+
137
+ 3.times { expect(ProjectPaths.working_dir).to eq( File.join( @ws, 'app' ) ) }
138
+ end
139
+
140
+ it 'answers a remembered path without reading the working directory' do
141
+ ProjectPaths.absolute( 'src/drv' )
142
+ expect(Dir).not_to receive(:pwd)
143
+
144
+ expect(ProjectPaths.absolute( 'src/drv' )).to eq( File.join( @ws, 'app/src/drv' ) )
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,71 @@
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 'ceedling/filesystem/working_dir'
12
+ require 'ceedling/filesystem/project_paths'
13
+
14
+ describe WorkingDir do
15
+ around(:each) do |example|
16
+ Dir.mktmpdir do |dir|
17
+ @ws = File.realpath( dir )
18
+ FileUtils.mkdir_p( File.join( @ws, 'app/dep' ) )
19
+ Dir.chdir( File.join( @ws, 'app' ) ) { example.run }
20
+ end
21
+ end
22
+
23
+ describe '.path' do
24
+ it 'reads the working directory once, however often it is asked' do
25
+ WorkingDir.path
26
+ expect(Dir).not_to receive(:pwd)
27
+
28
+ 3.times { expect(WorkingDir.path).to eq( File.join( @ws, 'app' ) ) }
29
+ end
30
+
31
+ it 'keeps answering for the directory Ceedling started in across a plain Dir.chdir' do
32
+ WorkingDir.path
33
+
34
+ answer = WorkingDirTracking.untracked { Dir.chdir( 'dep' ) { WorkingDir.path } }
35
+ expect(answer).to eq( File.join( @ws, 'app' ) )
36
+ end
37
+
38
+ # The spec suite's own Dir.chdir hook, which every in-process spec relies on
39
+ it 'answers for a directory a spec changes to' do
40
+ WorkingDir.path
41
+
42
+ expect(Dir.chdir( 'dep' ) { WorkingDir.path }).to eq( File.join( @ws, 'app/dep' ) )
43
+ expect(WorkingDir.path).to eq( File.join( @ws, 'app' ) )
44
+ end
45
+ end
46
+
47
+ # Untracked, so the spec suite's Dir.chdir hook can't do WorkingDir.chdir's work for it
48
+ describe '.chdir' do
49
+ around(:each) { |example| WorkingDirTracking.untracked { example.run } }
50
+
51
+ before(:each) do
52
+ WorkingDir.path
53
+ end
54
+
55
+ it 'answers for the directory it changes to, then for the one it left' do
56
+ inside = WorkingDir.chdir( 'dep' ) { [WorkingDir.path, ProjectPaths.canonical( File.join( @ws, 'app/dep/x' ) )] }
57
+
58
+ expect(inside).to eq( [File.join( @ws, 'app/dep' ), 'x'] )
59
+ expect(WorkingDir.path).to eq( File.join( @ws, 'app' ) )
60
+ expect(ProjectPaths.canonical( File.join( @ws, 'app/dep/x' ) )).to eq( 'dep/x' )
61
+ end
62
+
63
+ it 'answers for the directory it left after a block that raises' do
64
+ inside = nil
65
+ expect { WorkingDir.chdir( 'dep' ) { inside = WorkingDir.path; raise 'failed' } }.to raise_error( 'failed' )
66
+
67
+ expect(inside).to eq( File.join( @ws, 'app/dep' ) )
68
+ expect(WorkingDir.path).to eq( File.join( @ws, 'app' ) )
69
+ end
70
+ end
71
+ end
@@ -155,7 +155,13 @@ describe Generator do
155
155
 
156
156
  it 'has each simple backtrace rerun explained by the plugins' do
157
157
  run( use_backtrace: :simple )
158
- expect(backtrace).to have_received(:do_simple).with( 'test_a.c', 'build/test_a.out', anything, [], context: :test, tool: :fixture, explain: kind_of(Proc) )
158
+ expect(backtrace).to have_received(:do_simple).with( 'test_a.c', 'build/test_a.out', anything, [], tool: :fixture, explain: kind_of(Proc) )
159
+ end
160
+
161
+ # GH #1364: same-named tests keep separate gdb logs
162
+ it 'places gdb backtraces by the test name' do
163
+ run( use_backtrace: :gdb )
164
+ expect(backtrace).to have_received(:do_gdb).with( 'test_a.c', 'build/test_a.out', anything, [], context: :test, test_name: 'test_a' )
159
165
  end
160
166
 
161
167
  # A tool that runs the reruns itself can't run under gdb
@@ -235,7 +241,7 @@ describe Generator do
235
241
  def generate(**extra)
236
242
  @generator.generate_mock(
237
243
  context: :test, mock: :mock_config, test: 'test_both', input_filepath: 'src/drivers/uart/config.h',
238
- output_path: 'build/test/mocks/test_both/drivers/uart', **extra
244
+ output_path: 'build/test/mocks/test_both/drivers/uart', mock_root: 'build/test/mocks/test_both/drivers/uart', **extra
239
245
  )
240
246
  end
241
247
 
@@ -246,16 +252,16 @@ describe Generator do
246
252
  expect(@cmock).to have_received(:setup_mocks).with( 'src/drivers/uart/config.h', nil )
247
253
  end
248
254
 
249
- # CMock writes below its root at the folder, so the root is the directory less the folder
250
- it 'roots CMock above the folder and hands it the folder, which names the mock' do
251
- generate( folder: 'drivers/uart' )
255
+ # CMock writes below its root at the folder
256
+ it 'roots CMock where it is told and hands it the folder, which names the mock' do
257
+ generate( folder: 'drivers/uart', mock_root: 'build/test/mocks/test_both' )
252
258
 
253
259
  expect(@generator_mocks).to have_received(:build_configuration).with( 'build/test/mocks/test_both', overrides: {} )
254
260
  expect(@cmock).to have_received(:setup_mocks).with( 'src/drivers/uart/config.h', 'drivers/uart' )
255
261
  end
256
262
 
257
263
  it 'still reports the mock\'s own directory to plugins' do
258
- generate( folder: 'drivers/uart' )
264
+ generate( folder: 'drivers/uart', mock_root: 'build/test/mocks/test_both' )
259
265
 
260
266
  expect(@plugin_manager).to have_received(:pre_mock_generate).with( hash_including( output_path: 'build/test/mocks/test_both/drivers/uart' ) )
261
267
  end