ceedling 1.2.0 → 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 (383) hide show
  1. checksums.yaml +4 -4
  2. data/GIT_COMMIT_SHA +1 -1
  3. data/README.md +57 -58
  4. data/Rakefile +5 -3
  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 +35 -1
  10. data/bin/ceedling +13 -3
  11. data/bin/cli.rb +25 -6
  12. data/bin/cli_handler.rb +119 -73
  13. data/bin/cli_helper.rb +56 -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 +84 -3
  23. data/docs/KnownIssues.md +90 -4
  24. data/docs/ReleaseNotes.md +13 -0
  25. data/docs/overrides/main.html +11 -8
  26. data/lib/ceedling/build_log_writer.rb +33 -0
  27. data/lib/ceedling/config/build_root_policy.rb +70 -0
  28. data/lib/ceedling/config/configurator.rb +19 -18
  29. data/lib/ceedling/config/configurator_builder.rb +58 -28
  30. data/lib/ceedling/config/configurator_paths_validator.rb +179 -0
  31. data/lib/ceedling/config/configurator_plugins.rb +1 -1
  32. data/lib/ceedling/config/configurator_setup.rb +39 -41
  33. data/lib/ceedling/config/configurator_validator.rb +1 -110
  34. data/lib/ceedling/defaults.rb +4 -4
  35. data/lib/ceedling/dependencies/README.md +18 -16
  36. data/lib/ceedling/dependencies/dependency_cache_store.rb +20 -3
  37. data/lib/ceedling/dependencies/dependency_hasher.rb +35 -1
  38. data/lib/ceedling/filename_extension.rb +7 -1
  39. data/lib/ceedling/filesystem/FILE_TREE.md +253 -0
  40. data/lib/ceedling/filesystem/PATH_HANDLING.md +304 -0
  41. data/lib/ceedling/filesystem/README.md +41 -0
  42. data/lib/ceedling/filesystem/build_subpath.rb +102 -0
  43. data/lib/ceedling/filesystem/destination_collision.rb +25 -0
  44. data/lib/ceedling/{file_finder.rb → filesystem/file_finder.rb} +159 -104
  45. data/lib/ceedling/{file_finder_helper.rb → filesystem/file_finder_helper.rb} +36 -31
  46. data/lib/ceedling/filesystem/file_path_collection_utils.rb +183 -0
  47. data/lib/ceedling/{file_path_utils.rb → filesystem/file_path_utils.rb} +108 -97
  48. data/lib/ceedling/{file_system_wrapper.rb → filesystem/file_system_wrapper.rb} +6 -5
  49. data/lib/ceedling/filesystem/file_tree.rb +241 -0
  50. data/lib/ceedling/{file_wrapper.rb → filesystem/file_wrapper.rb} +65 -28
  51. data/lib/ceedling/filesystem/path_exclusions.rb +30 -0
  52. data/lib/ceedling/{path_matcher.rb → filesystem/path_matcher.rb} +27 -23
  53. data/lib/ceedling/{path_mirror.rb → filesystem/path_mirror.rb} +19 -18
  54. data/lib/ceedling/filesystem/path_spellings.rb +55 -0
  55. data/lib/ceedling/filesystem/project_paths.rb +184 -0
  56. data/lib/ceedling/filesystem/working_dir.rb +40 -0
  57. data/lib/ceedling/generators/generator.rb +5 -6
  58. data/lib/ceedling/generators/generator_partials.rb +1 -1
  59. data/lib/ceedling/generators/generator_test_results_backtrace.rb +7 -7
  60. data/lib/ceedling/handoff.rb +121 -0
  61. data/lib/ceedling/includes/include_factory.rb +3 -3
  62. data/lib/ceedling/includes/include_pathinator.rb +67 -10
  63. data/lib/ceedling/includes/includes.rb +1 -1
  64. data/lib/ceedling/installation.rb +92 -0
  65. data/lib/ceedling/library_resolver.rb +99 -0
  66. data/lib/ceedling/objects.yml +23 -2
  67. data/lib/ceedling/partials/partializer.rb +4 -4
  68. data/lib/ceedling/partials/partializer_config.rb +3 -1
  69. data/lib/ceedling/plugins/report_log_writer_plugin.rb +3 -5
  70. data/lib/ceedling/preprocess/preprocessinator_file_assembler.rb +1 -1
  71. data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +1 -1
  72. data/lib/ceedling/preprocess/preprocessinator_line_marker_includes_extractor.rb +1 -1
  73. data/lib/ceedling/rake_app/rakefile_component_resolver.rb +10 -17
  74. data/lib/ceedling/rake_app/test_task_name.rb +29 -0
  75. data/lib/ceedling/rakefile.rb +16 -1
  76. data/lib/ceedling/rakefiles/base/tasks_base.rake +1 -1
  77. data/lib/ceedling/rakefiles/release/rules_release.rake +4 -7
  78. data/lib/ceedling/rakefiles/tests/rules_tests.rake +3 -20
  79. data/lib/ceedling/rakefiles/tests/rules_tests_generate.rake +4 -30
  80. data/lib/ceedling/release_invoker/release_build_executor.rb +33 -44
  81. data/lib/ceedling/release_invoker/release_build_planner.rb +14 -6
  82. data/lib/ceedling/reportinator.rb +3 -4
  83. data/lib/ceedling/setupinator.rb +95 -87
  84. data/lib/ceedling/test_context_extractor.rb +5 -1
  85. data/lib/ceedling/test_invoker/PIPELINE.md +9 -7
  86. data/lib/ceedling/test_invoker/mock_locator.rb +60 -2
  87. data/lib/ceedling/test_invoker/partials_manager.rb +12 -0
  88. data/lib/ceedling/test_invoker/standin_headerinator.rb +22 -13
  89. data/lib/ceedling/test_invoker/test_build_executor.rb +42 -41
  90. data/lib/ceedling/test_invoker/test_build_planner.rb +37 -18
  91. data/lib/ceedling/test_invoker/test_build_setup.rb +17 -12
  92. data/lib/ceedling/test_invoker/test_build_validations.rb +16 -31
  93. data/lib/ceedling/test_invoker/test_file_preprocessinator.rb +1 -1
  94. data/lib/ceedling/test_invoker/test_invoker.rb +6 -1
  95. data/lib/ceedling/test_invoker/test_pipeline_helpers.rb +1 -1
  96. data/lib/ceedling/test_invoker/test_source_file_directive_resolver.rb +40 -20
  97. data/lib/ceedling/tool_executor.rb +9 -3
  98. data/lib/ceedling/yaml_wrapper.rb +3 -2
  99. data/lib/version.rb +1 -1
  100. data/plugins/bullseye/bullseye.rake +6 -9
  101. data/plugins/bullseye/config/defaults_bullseye.rb +1 -1
  102. data/plugins/bullseye/lib/bullseye.rb +9 -7
  103. data/plugins/cppcheck/config/defaults_cppcheck.rb +1 -1
  104. data/plugins/dependencies/config/defaults.yml +6 -6
  105. data/plugins/dependencies/dependencies.rake +4 -1
  106. data/plugins/dependencies/lib/dependencies.rb +18 -8
  107. data/plugins/dependencies/lib/dependency_spec.rb +17 -5
  108. data/plugins/fff/lib/fff.rb +45 -39
  109. data/plugins/fff/spec/fff_cmock_wrapper_spec.rb +128 -0
  110. data/plugins/gcov/config/defaults_gcov.rb +1 -1
  111. data/plugins/gcov/gcov.rake +6 -9
  112. data/plugins/gcov/lib/console_reportinator.rb +4 -18
  113. data/plugins/gcov/lib/gcov.rb +9 -2
  114. data/plugins/gcov/lib/gcovr_reportinator.rb +8 -40
  115. data/plugins/gcov/lib/gcovr_thresholds.rb +74 -0
  116. data/plugins/gcov/lib/reportgenerator_reportinator.rb +1 -1
  117. data/plugins/module_generator/lib/module_generator.rb +3 -3
  118. data/plugins/report_tests_teamcity_stdout/lib/report_tests_teamcity_stdout.rb +53 -80
  119. data/plugins/sanitize/lib/sanitize_records.rb +3 -6
  120. data/plugins/sanitize/sanitize.rake +6 -6
  121. data/plugins/valgrind/lib/valgrind.rb +14 -7
  122. data/plugins/valgrind/valgrind.rake +6 -6
  123. data/site-local/404.html +6 -20
  124. data/site-local/assets/javascripts/{bundle.d7400e89.min.js → bundle.c21346df.min.js} +3 -3
  125. data/site-local/assets/javascripts/{bundle.d7400e89.min.js.map → bundle.c21346df.min.js.map} +2 -2
  126. data/site-local/assets/javascripts/workers/{search.2c215733.min.js → search.a7633da7.min.js} +2 -2
  127. data/site-local/assets/javascripts/workers/{search.2c215733.min.js.map → search.a7633da7.min.js.map} +1 -1
  128. data/site-local/assets/stylesheets/main.57a0d79d.min.css +6 -0
  129. data/site-local/assets/stylesheets/main.57a0d79d.min.css.map +1 -0
  130. data/site-local/assets/stylesheets/palette.fb2fffe2.min.css +4 -0
  131. data/site-local/assets/stylesheets/palette.fb2fffe2.min.css.map +1 -0
  132. data/site-local/configuration/environment-vars.html +6 -20
  133. data/site-local/configuration/global-collections.html +6 -20
  134. data/site-local/configuration/index.html +6 -20
  135. data/site-local/configuration/loading.html +6 -20
  136. data/site-local/configuration/mixins.html +10 -24
  137. data/site-local/configuration/parallel-builds.html +6 -20
  138. data/site-local/configuration/project-file.html +8 -22
  139. data/site-local/configuration/reference/cexception.html +6 -20
  140. data/site-local/configuration/reference/cmock.html +8 -21
  141. data/site-local/configuration/reference/defines.html +16 -30
  142. data/site-local/configuration/reference/environment.html +10 -24
  143. data/site-local/configuration/reference/extension.html +6 -20
  144. data/site-local/configuration/reference/files.html +22 -30
  145. data/site-local/configuration/reference/flags.html +13 -27
  146. data/site-local/configuration/reference/index.html +6 -20
  147. data/site-local/configuration/reference/libraries.html +27 -23
  148. data/site-local/configuration/reference/mixins.html +6 -20
  149. data/site-local/configuration/reference/partials.html +6 -20
  150. data/site-local/configuration/reference/paths.html +38 -29
  151. data/site-local/configuration/reference/plugins.html +6 -20
  152. data/site-local/configuration/reference/project.html +32 -36
  153. data/site-local/configuration/reference/release-build.html +8 -20
  154. data/site-local/configuration/reference/test-build.html +6 -20
  155. data/site-local/configuration/reference/test-runner.html +6 -20
  156. data/site-local/configuration/reference/tools.html +27 -32
  157. data/site-local/configuration/reference/unity.html +6 -20
  158. data/site-local/configuration/which-ceedling.html +383 -37
  159. data/site-local/development/index.html +6 -20
  160. data/site-local/development/plugins/configuration.html +6 -20
  161. data/site-local/development/plugins/index.html +6 -20
  162. data/site-local/development/plugins/plugin-subclass.html +54 -68
  163. data/site-local/development/plugins/rake-tasks.html +8 -22
  164. data/site-local/development/releases.html +84 -29
  165. data/site-local/development/workflow.html +62 -71
  166. data/site-local/getting-started/builds.html +8 -20
  167. data/site-local/getting-started/command-line.html +62 -24
  168. data/site-local/getting-started/example-projects/cipher-quest.html +6 -20
  169. data/site-local/getting-started/example-projects/index.html +6 -20
  170. data/site-local/getting-started/example-projects/temp-sensor.html +6 -20
  171. data/site-local/getting-started/example-projects/wondrous-forest.html +6 -20
  172. data/site-local/getting-started/index.html +6 -20
  173. data/site-local/getting-started/installation.html +6 -20
  174. data/site-local/getting-started/quick-start.html +6 -20
  175. data/site-local/help.html +6 -20
  176. data/site-local/index.html +11 -24
  177. data/site-local/overview/build-system.html +6 -20
  178. data/site-local/overview/index.html +6 -20
  179. data/site-local/overview/test-environments.html +6 -20
  180. data/site-local/overview/tools-and-frameworks.html +6 -20
  181. data/site-local/plugins/beep.html +6 -20
  182. data/site-local/plugins/bullseye/index.html +13 -24
  183. data/site-local/plugins/bullseye/licensing.html +14 -28
  184. data/site-local/plugins/command-hooks.html +8 -22
  185. data/site-local/plugins/compile-commands-json-db.html +6 -20
  186. data/site-local/plugins/cppcheck.html +8 -22
  187. data/site-local/plugins/dependencies.html +16 -27
  188. data/site-local/plugins/fff.html +49 -32
  189. data/site-local/plugins/gcov/examples.html +7 -21
  190. data/site-local/plugins/gcov/gcovr.html +22 -33
  191. data/site-local/plugins/gcov/index.html +6 -20
  192. data/site-local/plugins/gcov/overview.html +9 -20
  193. data/site-local/plugins/gcov/reportgenerator.html +9 -23
  194. data/site-local/plugins/gcov/reporting.html +6 -20
  195. data/site-local/plugins/gcov/setup.html +8 -21
  196. data/site-local/plugins/gcov/tool-versions.html +6 -20
  197. data/site-local/plugins/gcov/troubleshooting.html +10 -24
  198. data/site-local/plugins/index.html +6 -20
  199. data/site-local/plugins/module-generator.html +21 -35
  200. data/site-local/plugins/report-build-warnings-log.html +53 -67
  201. data/site-local/plugins/report-tests-gtestlike-stdout.html +6 -20
  202. data/site-local/plugins/report-tests-ide-stdout.html +43 -57
  203. data/site-local/plugins/report-tests-log-factory.html +50 -64
  204. data/site-local/plugins/report-tests-pretty-stdout.html +7 -21
  205. data/site-local/plugins/report-tests-raw-output-log.html +9 -21
  206. data/site-local/plugins/report-tests-teamcity-stdout.html +20 -31
  207. data/site-local/plugins/sanitize.html +56 -65
  208. data/site-local/plugins/valgrind.html +49 -58
  209. data/site-local/project/index.html +6 -20
  210. data/site-local/project/release-history.html +6 -20
  211. data/site-local/project/sbom.html +6 -20
  212. data/site-local/project/upgrade.html +9 -20
  213. data/site-local/reference/build-directives.html +6 -20
  214. data/site-local/reference/command-line.html +15 -22
  215. data/site-local/reference/environment-vars.html +6 -20
  216. data/site-local/reference/gcov-plugin.html +6 -20
  217. data/site-local/reference/global-collections.html +6 -20
  218. data/site-local/reference/index.html +6 -20
  219. data/site-local/reference/partials-macros.html +6 -20
  220. data/site-local/reference/project-configuration.html +6 -20
  221. data/site-local/sitemap.xml.gz +0 -0
  222. data/site-local/snapshot/examples/cipher_quest/index.html +16 -30
  223. data/site-local/snapshot/examples/temp_sensor/index.html +8 -22
  224. data/site-local/snapshot/examples/wondrous_forest/index.html +6 -20
  225. data/site-local/snapshot/plugins/bullseye/config/defaults_bullseye.rb +1 -1
  226. data/site-local/snapshot/plugins/gcov/config/defaults_gcov.rb +1 -1
  227. data/site-local/testing-guide/build-directives.html +14 -28
  228. data/site-local/testing-guide/conventions.html +66 -27
  229. data/site-local/testing-guide/frameworks.html +11 -25
  230. data/site-local/testing-guide/index.html +6 -20
  231. data/site-local/testing-guide/partials/configuration.html +8 -22
  232. data/site-local/testing-guide/partials/conventions.html +8 -22
  233. data/site-local/testing-guide/partials/directives.html +16 -30
  234. data/site-local/testing-guide/partials/example.html +14 -28
  235. data/site-local/testing-guide/partials/index.html +6 -20
  236. data/site-local/testing-guide/partials/overview.html +6 -20
  237. data/site-local/testing-guide/partials/variables.html +11 -25
  238. data/site-local/testing-guide/test-cases.html +13 -27
  239. data/site-local/testing-guide/test-sample.html +12 -26
  240. data/site-local/testing-guide/test-suite-anatomy.html +6 -20
  241. data/spec/integration/actionator_file_operations_spec.rb +91 -1
  242. data/spec/integration/compile_commands_json_db_spec.rb +1 -1
  243. data/spec/integration/configurator_pipeline_spec.rb +10 -0
  244. data/spec/integration/configurator_validation_spec.rb +1 -1
  245. data/spec/integration/dependencies/dependency_tracker_cache_hardening_spec.rb +26 -0
  246. data/spec/integration/dependencies/dependency_tracker_core_spec.rb +62 -0
  247. data/spec/integration/launcher_handoff_spec.rb +297 -0
  248. data/spec/integration/mixin_loading_spec.rb +1 -1
  249. data/spec/integration/module_generator_spec.rb +3 -3
  250. data/spec/integration/nested_test_artifacts_spec.rb +99 -0
  251. data/spec/integration/partials_module_resolution_spec.rb +7 -4
  252. data/spec/integration/path_intake_scenarios_spec.rb +101 -0
  253. data/spec/integration/path_scenarios_spec.rb +322 -0
  254. data/spec/integration/quote_include_isolator_spec.rb +1 -1
  255. data/spec/integration/rake_task_registry_plugins_spec.rb +95 -0
  256. data/spec/integration/report_build_warnings_log_spec.rb +1 -1
  257. data/spec/integration/report_tests_log_factory_spec.rb +1 -1
  258. data/spec/integration/test_executable_manifest_spec.rb +1 -1
  259. data/spec/integration/test_includes_extractinator_spec.rb +2 -1
  260. data/spec/integration/test_naming_spec.rb +198 -0
  261. data/spec/integration/test_source_file_checks_spec.rb +166 -0
  262. data/spec/integration/tool_validator_spec.rb +1 -1
  263. data/spec/integration/upgrade_docs_spec.rb +84 -0
  264. data/spec/integration/vendor_copy_transient_io_retry_spec.rb +192 -75
  265. data/spec/support/file_tree_helper.rb +88 -0
  266. data/spec/support/integration/configurator_integration_helper.rb +10 -7
  267. data/spec/support/integration/dependency_tracker_integration_helper.rb +1 -1
  268. data/spec/support/integration/partials_generation_integration_helper.rb +2 -2
  269. data/spec/support/integration/path_scenario_harness.rb +173 -0
  270. data/spec/support/integration/spec_integration_helper.rb +1 -1
  271. data/spec/support/integration/test_includes_harness.rb +13 -2
  272. data/spec/support/spec_helper.rb +2 -0
  273. data/spec/support/system/spec_system_helper.rb +7 -6
  274. data/spec/support/system/system_context.rb +21 -3
  275. data/spec/support/working_dir_tracking.rb +49 -0
  276. data/spec/system/build_output_mirroring_spec.rb +2 -2
  277. data/spec/system/conditional_mock_includes_spec.rb +1 -1
  278. data/spec/system/delta_builds_spec.rb +137 -11
  279. data/spec/system/gcov_deployment_spec.rb +1 -0
  280. data/spec/system/header_include_path_validation_spec.rb +4 -4
  281. data/spec/system/launcher_handoff_spec.rb +82 -0
  282. data/spec/system/mock_include_folders_spec.rb +91 -0
  283. data/spec/system/parent_directory_reference_spec.rb +5 -3
  284. data/spec/system/path_handling_probes_spec.rb +192 -0
  285. data/spec/system/preprocessing_parameterized_tests_spec.rb +1 -1
  286. data/spec/system/rake_release_task_path_disambiguation_spec.rb +6 -6
  287. data/spec/system/rake_test_task_path_disambiguation_spec.rb +85 -0
  288. data/spec/system/special_path_characters_spec.rb +86 -0
  289. data/spec/system/support/common_test_cases.rb +23 -21
  290. data/spec/system/support/gcov_common_test_cases.rb +37 -9
  291. data/spec/system/support/sanitize_common_test_cases.rb +2 -2
  292. data/spec/system/support/valgrind_common_test_cases.rb +12 -12
  293. data/spec/system/test_pipeline_stop_points_spec.rb +4 -4
  294. data/spec/system/test_runner_system_include_spec.rb +1 -1
  295. data/spec/system/test_source_file_directive_spec.rb +34 -5
  296. data/spec/units/bin/actionator_spec.rb +14 -17
  297. data/spec/units/bin/app_cfg_spec.rb +47 -0
  298. data/spec/units/bin/cli_handler_spec.rb +284 -7
  299. data/spec/units/bin/cli_helper_spec.rb +126 -45
  300. data/spec/units/bin/installation_selector_spec.rb +114 -0
  301. data/spec/units/bin/launchinator_spec.rb +171 -0
  302. data/spec/units/bin/path_validator_spec.rb +10 -0
  303. data/spec/units/bin/versionator_spec.rb +38 -6
  304. data/spec/units/build_log_writer_spec.rb +57 -0
  305. data/spec/units/config/build_root_policy_spec.rb +61 -0
  306. data/spec/units/config/configurator_builder_spec.rb +37 -6
  307. data/spec/units/config/configurator_paths_validator_spec.rb +292 -0
  308. data/spec/units/config/configurator_setup_spec.rb +65 -103
  309. data/spec/units/config/configurator_spec.rb +37 -6
  310. data/spec/units/config/configurator_validator_spec.rb +1 -172
  311. data/spec/units/default_tools_quoting_spec.rb +83 -0
  312. data/spec/units/dependencies/dependency_cache_store_spec.rb +33 -2
  313. data/spec/units/dependencies/dependency_debug_tree_spec.rb +1 -1
  314. data/spec/units/dependencies/dependency_hasher_spec.rb +42 -1
  315. data/spec/units/dependencies/dependency_path_normalizer_spec.rb +1 -1
  316. data/spec/units/dependencies/dependency_tracker_spec.rb +3 -1
  317. data/spec/units/filename_extension_spec.rb +11 -0
  318. data/spec/units/filesystem/build_subpath_spec.rb +122 -0
  319. data/spec/units/filesystem/destination_collision_spec.rb +30 -0
  320. data/spec/units/{file_finder_helper_spec.rb → filesystem/file_finder_helper_spec.rb} +46 -1
  321. data/spec/units/{file_finder_spec.rb → filesystem/file_finder_spec.rb} +188 -22
  322. data/spec/units/{file_path_collection_utils_spec.rb → filesystem/file_path_collection_utils_spec.rb} +181 -1
  323. data/spec/units/{file_path_utils_spec.rb → filesystem/file_path_utils_spec.rb} +204 -87
  324. data/spec/units/filesystem/file_system_wrapper_spec.rb +33 -0
  325. data/spec/units/filesystem/file_tree_spec.rb +511 -0
  326. data/spec/units/filesystem/file_tree_windows_spec.rb +78 -0
  327. data/spec/units/{file_wrapper_spec.rb → filesystem/file_wrapper_spec.rb} +154 -32
  328. data/spec/units/{path_matcher_spec.rb → filesystem/path_matcher_spec.rb} +12 -1
  329. data/spec/units/{path_mirror_spec.rb → filesystem/path_mirror_spec.rb} +32 -4
  330. data/spec/units/filesystem/project_paths_spec.rb +147 -0
  331. data/spec/units/filesystem/working_dir_spec.rb +71 -0
  332. data/spec/units/generators/generator_spec.rb +12 -6
  333. data/spec/units/generators/generator_test_results_backtrace_spec.rb +50 -46
  334. data/spec/units/generators/generator_test_results_spec.rb +1 -1
  335. data/spec/units/handoff_spec.rb +205 -0
  336. data/spec/units/includes/include_factory_spec.rb +3 -3
  337. data/spec/units/includes/include_pathinator_spec.rb +46 -0
  338. data/spec/units/installation_spec.rb +228 -0
  339. data/spec/units/library_resolver_spec.rb +157 -0
  340. data/spec/units/mock_locator_spec.rb +76 -1
  341. data/spec/units/partials/partializer_config_spec.rb +7 -0
  342. data/spec/units/partials/partializer_spec.rb +1 -0
  343. data/spec/units/partials_manager_spec.rb +17 -0
  344. data/spec/units/plugins/bullseye_spec.rb +15 -2
  345. data/spec/units/plugins/console_reportinator_spec.rb +18 -20
  346. data/spec/units/plugins/dependencies_spec.rb +33 -7
  347. data/spec/units/plugins/dependency_spec_spec.rb +17 -0
  348. data/spec/units/plugins/gcov_spec.rb +28 -3
  349. data/spec/units/plugins/gcovr_reportinator_spec.rb +19 -47
  350. data/spec/units/plugins/gcovr_thresholds_spec.rb +95 -0
  351. data/spec/units/plugins/module_generator_spec.rb +5 -5
  352. data/spec/units/plugins/report_build_warnings_log_spec.rb +3 -3
  353. data/spec/units/plugins/report_log_writer_plugin_spec.rb +10 -1
  354. data/spec/units/plugins/report_tests_raw_output_log_spec.rb +13 -2
  355. data/spec/units/plugins/report_tests_teamcity_stdout_spec.rb +77 -3
  356. data/spec/units/plugins/reportgenerator_reportinator_spec.rb +1 -1
  357. data/spec/units/plugins/sanitize_records_spec.rb +3 -1
  358. data/spec/units/plugins/sanitize_spec.rb +1 -1
  359. data/spec/units/plugins/valgrind_spec.rb +23 -8
  360. data/spec/units/preprocess/preprocessinator_comment_stripper_spec.rb +1 -1
  361. data/spec/units/rake_app/rakefile_component_resolver_spec.rb +21 -0
  362. data/spec/units/rake_app/test_task_name_spec.rb +47 -0
  363. data/spec/units/release_build_executor_spec.rb +53 -14
  364. data/spec/units/release_build_planner_spec.rb +15 -3
  365. data/spec/units/reportinator_spec.rb +5 -5
  366. data/spec/units/standin_headerinator_spec.rb +31 -1
  367. data/spec/units/test_build_executor_spec.rb +107 -5
  368. data/spec/units/test_build_planner_spec.rb +104 -2
  369. data/spec/units/test_build_setup_spec.rb +96 -12
  370. data/spec/units/test_context_extractor_spec.rb +25 -0
  371. data/spec/units/test_file_preprocessinator_spec.rb +17 -1
  372. data/spec/units/test_includes_extractinator_spec.rb +5 -1
  373. data/spec/units/test_invoker/objects_wiring_spec.rb +6 -3
  374. data/spec/units/test_invoker_spec.rb +14 -1
  375. data/spec/units/test_source_file_directive_resolver_spec.rb +38 -7
  376. data/spec/units/tool_executor_spec.rb +26 -0
  377. data/spec/units/yaml_wrapper_spec.rb +11 -0
  378. metadata +122 -33
  379. data/lib/ceedling/file_path_collection_utils.rb +0 -164
  380. data/site-local/assets/stylesheets/main.ec1eaa64.min.css +0 -1
  381. data/site-local/assets/stylesheets/main.ec1eaa64.min.css.map +0 -1
  382. data/site-local/assets/stylesheets/palette.ab4e12ef.min.css +0 -1
  383. data/site-local/assets/stylesheets/palette.ab4e12ef.min.css.map +0 -1
@@ -0,0 +1,166 @@
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
+ # Checking TEST_SOURCE_FILE() entries against a real project. Real test files are scanned by
9
+ # the real context extractor, and the real resolver looks each entry up through the real
10
+ # file finder in a temporary project configured by the real configuration pipeline.
11
+ #
12
+ # Each test's entries are checked once, under that test's own name, with test preprocessing
13
+ # on or off, so a misspelled entry stops the build naming the test that holds it (GH #1364).
14
+
15
+ require 'spec_helper'
16
+ require 'configurator_integration_helper'
17
+ require 'ceedling/parsing_parcels'
18
+ require 'ceedling/includes/include_factory'
19
+ require 'ceedling/filesystem/file_finder'
20
+ require 'ceedling/filesystem/file_finder_helper'
21
+ require 'ceedling/filesystem/file_path_utils'
22
+ require 'ceedling/test_context_extractor'
23
+ require 'ceedling/test_invoker/test_source_file_directive_resolver'
24
+ require 'ceedling/test_invoker/test_build_setup'
25
+ require 'ceedling/test_invoker/test_invoker_types'
26
+
27
+ describe 'TEST_SOURCE_FILE() checks (integration)' do
28
+ include ConfiguratorIntegrationHelpers
29
+
30
+ # Runs each test of a batch in turn, in place of the thread pool
31
+ class TestSourceFileChecksBatchinator
32
+ def exec(things:, **_options)
33
+ things.each { |key, value| yield key, value }
34
+ end
35
+ end
36
+
37
+ def test_file(entries)
38
+ lines = entries.map { |entry| "TEST_SOURCE_FILE(\"#{entry}\")\n" }.join
39
+ return "#include \"unity.h\"\n#{lines}void test_it(void) {}\n"
40
+ end
41
+
42
+ def project_files
43
+ return {
44
+ 'src/calc.c' => "int calc(void) { return 1; }\n",
45
+ 'src/calc.h' => "int calc(void);\n",
46
+ 'src/a/dup.c' => "int dup(void) { return 1; }\n",
47
+ 'lib/a/dup.c' => "int dup(void) { return 2; }\n",
48
+ 'lib/extra.c' => "int extra(void) { return 7; }\n",
49
+ 'test/test_m1.c' => test_file( ['calc.c'] ),
50
+ 'test/test_m2.c' => test_file( ['calc.c'] ),
51
+ 'test/unit/test_m3.c' => test_file( ['../../lib/extra.c'] )
52
+ }
53
+ end
54
+
55
+ def project_yaml(preprocessor: :none)
56
+ return <<~YAML
57
+ :project:
58
+ :build_root: build
59
+ :use_test_preprocessor: :#{preprocessor}
60
+ :paths:
61
+ :test: [test/**]
62
+ :source: [src/**, lib/**]
63
+ :include: [src/**]
64
+ YAML
65
+ end
66
+
67
+ def objects
68
+ graph = configure( project_yaml )
69
+ configurator = graph[:configurator]
70
+ loginator = graph[:loginator]
71
+ file_wrapper = graph[:file_wrapper]
72
+
73
+ file_path_utils = FilePathUtils.new( configurator: configurator, file_wrapper: file_wrapper )
74
+ file_finder = FileFinder.new(
75
+ configurator: configurator, file_finder_helper: FileFinderHelper.new( loginator: loginator ),
76
+ file_path_utils: file_path_utils, file_wrapper: file_wrapper, yaml_wrapper: graph[:yaml_wrapper]
77
+ )
78
+ extractor = TestContextExtractor.new(
79
+ configurator: configurator, parsing_parcels: ParsingParcels.new, include_factory: IncludeFactory.new( configurator: configurator ),
80
+ partializer_config: double( 'partializer_config' ).as_null_object, file_path_utils: file_path_utils, file_wrapper: file_wrapper,
81
+ yaml_wrapper: graph[:yaml_wrapper], loginator: loginator
82
+ )
83
+ resolver = TestSourceFileDirectiveResolver.new(
84
+ test_context_extractor: extractor, file_finder: file_finder, configurator: configurator, loginator: loginator
85
+ )
86
+
87
+ return { configurator: configurator, file_path_utils: file_path_utils, file_finder: file_finder, extractor: extractor, resolver: resolver }
88
+ end
89
+
90
+ def scan(extractor, tests)
91
+ tests.each { |test| extractor.collect_simple_context_from_file( test, nil, TestContextExtractor::Context::BUILD_DIRECTIVE_SOURCE_FILES ) }
92
+ end
93
+
94
+ it "passes a test's real entries, including one reached through .." do
95
+ in_temp_project( project_files ) do
96
+ o = objects
97
+ tests = %w[test/test_m1.c test/test_m2.c test/unit/test_m3.c]
98
+ scan( o[:extractor], tests )
99
+
100
+ tests.each do |test|
101
+ expect { o[:resolver].validate!( test: test.ext( '' ), test_filepath: test, context: :test ) }.to_not raise_error
102
+ end
103
+ end
104
+ end
105
+
106
+ it 'names the test that holds a missing entry' do
107
+ in_temp_project( project_files.merge( 'test/test_m2.c' => test_file( ['calc_typo.c'] ) ) ) do
108
+ o = objects
109
+ scan( o[:extractor], %w[test/test_m1.c test/test_m2.c] )
110
+
111
+ expect { o[:resolver].validate!( test: 'test/test_m1', test_filepath: 'test/test_m1.c', context: :test ) }.to_not raise_error
112
+ expect { o[:resolver].validate!( test: 'test/test_m2', test_filepath: 'test/test_m2.c', context: :test ) }.to raise_error(
113
+ CeedlingException, %r{'calc_typo\.c' specified with TEST_SOURCE_FILE\(\) in test/test_m2 cannot be found}
114
+ )
115
+ end
116
+ end
117
+
118
+ it 'stops for a misspelled removal entry, which otherwise leaves its file in the build' do
119
+ in_temp_project( project_files.merge( 'test/test_m1.c' => test_file( ['-:clac.c'] ) ) ) do
120
+ o = objects
121
+ scan( o[:extractor], %w[test/test_m1.c] )
122
+
123
+ expect { o[:resolver].validate!( test: 'test/test_m1', test_filepath: 'test/test_m1.c', context: :test ) }.to raise_error(
124
+ CeedlingException, /'clac\.c'.*cannot be found/
125
+ )
126
+ end
127
+ end
128
+
129
+ it 'notes an entry matching several files once, however many tests name it' do
130
+ files = project_files.merge( %w[test/test_m1.c test/test_m2.c test/unit/test_m3.c].to_h { |test| [test, test_file( ['a/dup.c'] )] } )
131
+
132
+ in_temp_project( files ) do
133
+ o = objects
134
+ tests = %w[test/test_m1.c test/test_m2.c test/unit/test_m3.c]
135
+ scan( o[:extractor], tests )
136
+
137
+ tests.each do |test|
138
+ o[:resolver].validate!( test: test.ext( '' ), test_filepath: test, context: :test )
139
+ o[:resolver].resolve( test, :test )
140
+ end
141
+
142
+ expect( logged.grep( /Multiple files matched 'a\/dup\.c'/ ).size ).to eq( 1 )
143
+ end
144
+ end
145
+
146
+ # Without test preprocessing, stage 2 scans the entries and checks them
147
+ it 'checks each test under its own name in stage 2 without test preprocessing' do
148
+ in_temp_project( project_files.merge( 'test/unit/test_m3.c' => test_file( ['nowhere.c'] ) ) ) do
149
+ o = objects
150
+ setup = TestBuildSetup.new( TestBuildSetup.constructor_keys.to_h { |key| [key, double( key.to_s ).as_null_object] }.merge(
151
+ configurator: o[:configurator], batchinator: TestSourceFileChecksBatchinator.new, test_context_extractor: o[:extractor],
152
+ file_path_utils: o[:file_path_utils], file_finder: o[:file_finder], test_source_file_directive_resolver: o[:resolver],
153
+ file_wrapper: FileWrapper.new( loginator: double( 'loginator', log: nil ), verbosinator: double( 'verbosinator', should_output?: false ) ),
154
+ dependinator: double( 'dependinator', register: nil, stale?: true, mark_fresh: nil ),
155
+ reportinator: double( 'reportinator', generate_progress: '', generate_skip_summary: nil )
156
+ ) )
157
+ tests = %w[test/test_m1.c test/test_m2.c test/unit/test_m3.c]
158
+ state = TestInvokerTypes::PipelineState.new( tests: tests, testables: {}, context: :test, options: [], lock: Mutex.new )
159
+ setup.stage_prepare_build_paths( state )
160
+
161
+ expect { setup.stage_collect_test_context( state ) }.to raise_error(
162
+ CeedlingException, %r{'nowhere\.c' specified with TEST_SOURCE_FILE\(\) in test/unit/test_m3 cannot be found}
163
+ )
164
+ end
165
+ end
166
+ end
@@ -17,7 +17,7 @@ require 'spec_helper'
17
17
  require 'spec_integration_helper'
18
18
  require 'fileutils'
19
19
  require 'ceedling/tool_validator'
20
- require 'ceedling/file_wrapper'
20
+ require 'ceedling/filesystem/file_wrapper'
21
21
  require 'ceedling/system_wrapper'
22
22
  require 'ceedling/ruby_expandinator'
23
23
  require 'ceedling/filename_extension'
@@ -0,0 +1,84 @@
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
+ # GH #1371: `upgrade` never refreshed documentation that `new --docs` exported, because it
9
+ # looked for a file exports stopped writing in 1.1.0. For a 1.0.x export, which it did
10
+ # find, it removed all of docs/, the project's own files included.
11
+ #
12
+ # These run the real launcher against real projects. Nothing compiles, so no toolchain is
13
+ # needed.
14
+
15
+ require 'spec_helper'
16
+ require 'tmpdir'
17
+ require 'fileutils'
18
+ require 'open3'
19
+ require 'rbconfig'
20
+
21
+ describe '`upgrade` and exported documentation' do
22
+ REPO = File.expand_path( '../..', __dir__ )
23
+ UNITY_PAGE = File.join( 'unity', 'UnityGettingStartedGuide.md' )
24
+
25
+ def ceedling(*args)
26
+ stdout, stderr, status = Open3.capture3(
27
+ {'CEEDLING_DECORATORS' => 'false'}, RbConfig.ruby, File.join( REPO, 'bin', 'ceedling' ), *args, chdir: @tmp
28
+ )
29
+ raise "`ceedling #{args.join( ' ' )}` failed:\n#{stdout}#{stderr}" unless status.success?
30
+ end
31
+
32
+ def docs(*entry)
33
+ return File.join( @tmp, 'proj', 'docs', *entry )
34
+ end
35
+
36
+ def write(entry, text='')
37
+ FileUtils.mkdir_p( File.dirname( docs( entry ) ) )
38
+ File.write( docs( entry ), text )
39
+ end
40
+
41
+ around(:each) do |example|
42
+ Dir.mktmpdir( 'upgrade_docs' ) { |dir| @tmp = dir; example.run }
43
+ end
44
+
45
+ it 'refreshes an export of 1.1.0 or later and keeps the project\'s own files' do
46
+ ceedling( 'new', '--local', '--docs', 'proj' )
47
+ write( 'notes.md', 'mine' )
48
+ write( File.join( 'unity', 'stale.md' ) )
49
+ write( UNITY_PAGE, 'edited' )
50
+
51
+ ceedling( 'upgrade', 'proj' )
52
+
53
+ expect(File.exist?( docs( 'unity', 'stale.md' ) )).to be( false )
54
+ expect(File.read( docs( UNITY_PAGE ) )).to eq( File.read( File.join( REPO, 'vendor', 'unity', 'docs', 'UnityGettingStartedGuide.md' ) ) )
55
+ expect(File.read( docs( 'notes.md' ) )).to eq( 'mine' )
56
+ end
57
+
58
+ it 'replaces a 1.0.x export with the current layout and keeps the project\'s own files' do
59
+ ceedling( 'new', '--local', 'proj' )
60
+ write( 'CeedlingPacket.md' )
61
+ write( 'Changelog.md' )
62
+ write( File.join( 'plugins', 'gcov.md' ) )
63
+ write( File.join( 'unity', 'stale.md' ) )
64
+ write( 'license.txt' )
65
+ write( 'notes.md', 'mine' )
66
+
67
+ ceedling( 'upgrade', 'proj' )
68
+
69
+ ['CeedlingPacket.md', 'Changelog.md', 'plugins', File.join( 'unity', 'stale.md' )].each do |entry|
70
+ expect(File.exist?( docs( entry ) )).to be( false ), entry
71
+ end
72
+ expect(File.exist?( docs( UNITY_PAGE ) )).to be( true )
73
+ expect(File.read( docs( 'notes.md' ) )).to eq( 'mine' )
74
+ end
75
+
76
+ it 'leaves a docs/ holding only the project\'s own files alone' do
77
+ ceedling( 'new', '--local', 'proj' )
78
+ write( 'notes.md', 'mine' )
79
+
80
+ ceedling( 'upgrade', 'proj' )
81
+
82
+ expect(Dir.children( docs )).to eq( ['notes.md'] )
83
+ end
84
+ end
@@ -5,126 +5,243 @@
5
5
  # SPDX-License-Identifier: MIT
6
6
  # =========================================================================
7
7
 
8
- # Integration coverage for Issue #1292: a vendor destination (build/vendor/unity/src,
9
- # etc.) occasionally turns up as the wrong file/directory type mid-copy, crashing with
10
- # Errno::ENOTDIR/EISDIR. Does FileWrapper#cp_r_with_retry actually let
11
- # ConfiguratorSetup#vendor_frameworks_and_support_files ride out a transient fault of
12
- # that shape, end to end against a real filesystem -- and does a fault that never
13
- # clears still surface as a clear, path-naming CeedlingException rather than a raw Errno?
8
+ # ConfiguratorSetup#vendor_frameworks_and_support_files with a real FileWrapper on a real
9
+ # filesystem. A build brings build/vendor up to date with the gem's frameworks. It writes only
10
+ # a file that differs from its source, and replaces it whole by renaming a temporary copy over
11
+ # it, so a concurrent build never reads a half-written header (GH #1361). An entry of the
12
+ # wrong type, such as a directory where a file belongs, is replaced (GH #1292, #1376). A
13
+ # transient copy fault, such as a scanner holding a file open, is retried, and one that never
14
+ # clears names the destination.
14
15
  #
15
- # Modeled directly on spec/integration/preprocessing_transient_io_retry_spec.rb: a real
16
- # ConfiguratorSetup + real FileWrapper subclass that controls exactly one #cp_r call
17
- # (matched by destination path) so the fault is deterministic. Faulting at #cp_r rather
18
- # than #cp_r_with_retry itself matters here -- cp_r_with_retry's own retry loop (already
19
- # proven exhaustively in file_wrapper_spec.rb) is exactly the mechanism this spec needs
20
- # to actually run.
16
+ # Faults are injected at FileWrapper#replace_file, the one write of each file, so the retry
17
+ # loop in FileTree is what runs.
21
18
 
22
19
  require 'spec_helper'
23
20
  require 'spec_integration_helper'
21
+ require 'file_tree_helper'
24
22
  require 'ceedling/config/configurator_setup'
25
23
  require 'ceedling/constants'
26
24
 
27
- describe 'Vendor-copy transient I/O retry (integration)' do
25
+ describe 'Vendor copy into build/vendor (integration)' do
28
26
  include IntegrationSpecHelpers
27
+ include FileTreeHelper
29
28
 
30
- # A real FileWrapper whose #cp_r raises for `fault_dest` the first `raises_left`
31
- # times it's called, then behaves normally -- every other destination is untouched.
32
- # Real FileWrapper#cp_r_with_retry (inherited, not overridden) is what actually retries.
33
- # Named distinctly from preprocessing_transient_io_retry_spec.rb's own FaultingFileWrapper
34
- # -- both are top-level classes loaded into the same RSpec process, and Ruby's open
35
- # classes would otherwise let the second file's definition silently clobber the first's.
36
- class CpRFaultingFileWrapper < FileWrapper
37
- def initialize(fault_dest:, raises_left: 1, error: Errno::EISDIR, **kwargs)
29
+ # A real FileWrapper whose #replace_file raises for files beneath `fault_dir` the first
30
+ # `raises_left` times. Named apart from other specs' faulting wrappers, since top-level
31
+ # classes share one RSpec process.
32
+ class ReplaceFaultingFileWrapper < FileWrapper
33
+ def initialize(fault_dir:, raises_left: 1, error: Errno::EACCES, **kwargs)
38
34
  super(**kwargs)
39
- @fault_dest = fault_dest
35
+ @fault_dir = fault_dir
40
36
  @raises_left = raises_left
41
37
  @error = error
42
38
  end
43
39
 
44
- def cp_r(source, destination, options={})
45
- if destination == @fault_dest && @raises_left > 0
40
+ def replace_file(source, destination)
41
+ if destination.start_with?( @fault_dir ) && @raises_left > 0
46
42
  @raises_left -= 1
47
- raise @error, "simulated transient type-mismatch on #{destination}"
43
+ raise @error, "simulated fault on #{destination}"
48
44
  end
49
45
  super
50
46
  end
51
47
  end
52
48
 
53
- # Real ConfiguratorSetup wired to `file_wrapper`; every other collaborator is
54
- # untouched by #vendor_frameworks_and_support_files, so NULL stands in for them.
49
+ def file_wrapper(**fault)
50
+ options = { loginator: IntegrationSpecHelpers::NULL, verbosinator: IntegrationSpecHelpers::NULL }
51
+ return fault.empty? ? FileWrapper.new( options ) : ReplaceFaultingFileWrapper.new( **fault, **options )
52
+ end
53
+
55
54
  def build_configurator_setup(file_wrapper)
56
55
  ConfiguratorSetup.new(
57
56
  configurator_builder: IntegrationSpecHelpers::NULL,
58
57
  configurator_validator: IntegrationSpecHelpers::NULL,
59
- loginator: IntegrationSpecHelpers::NULL,
60
- reportinator: IntegrationSpecHelpers::NULL,
61
- file_wrapper: file_wrapper,
62
- system_wrapper: IntegrationSpecHelpers::NULL,
63
- tool_executor: IntegrationSpecHelpers::NULL
58
+ configurator_paths_validator: IntegrationSpecHelpers::NULL,
59
+ loginator: IntegrationSpecHelpers::NULL,
60
+ reportinator: IntegrationSpecHelpers::NULL,
61
+ file_wrapper: file_wrapper,
62
+ system_wrapper: IntegrationSpecHelpers::NULL,
63
+ tool_executor: IntegrationSpecHelpers::NULL
64
64
  )
65
65
  end
66
66
 
67
- # A minimal but real fixture standing in for the gem's own vendor/unity/src -- one
68
- # real file, so a real FileUtils.cp_r has real directory-tree content to walk.
67
+ # A stand-in for the gem's vendor/unity/src, with a nested file to show subdirectories copy
69
68
  def flattened_config(dir)
70
- unity_src = File.join(dir, 'gem_vendor', 'unity', 'src')
71
- FileUtils.mkdir_p(unity_src)
72
- File.write(File.join(unity_src, 'unity.c'), "// stand-in for the real unity.c\n")
69
+ unity_src = File.join( dir, 'gem_vendor', 'unity', 'src' )
70
+ FileUtils.mkdir_p( File.join( unity_src, 'internal' ) )
71
+ File.write( File.join( unity_src, 'unity.c' ), "// stand-in for unity.c\n" )
72
+ File.write( File.join( unity_src, 'unity.h' ), "// stand-in for unity.h\n" )
73
+ File.write( File.join( unity_src, 'internal', 'unity_internals.h' ), "// stand-in\n" )
73
74
 
74
75
  {
75
- # UNITY_LIB_PATH ('unity/src') is joined onto this root -- it must be the
76
- # vendor root, not 'unity' itself, matching CEEDLING_VENDOR's real shape.
77
- unity_vendor_path: File.join(dir, 'gem_vendor'),
78
- project_build_vendor_unity_path: File.join(dir, 'build', 'vendor', 'unity', 'src'),
79
- project_use_mocks: false,
80
- project_use_exceptions: false,
81
- project_use_backtrace: false,
82
- project_use_partials: false,
76
+ # UNITY_LIB_PATH ('unity/src') joins this vendor root
77
+ unity_vendor_path: File.join( dir, 'gem_vendor' ),
78
+ project_build_vendor_unity_path: File.join( dir, 'build', 'vendor', 'unity', 'src' ),
79
+ project_use_mocks: false,
80
+ project_use_exceptions: false,
81
+ project_use_backtrace: :none,
82
+ project_use_partials: false,
83
83
  }
84
84
  end
85
85
 
86
- it "recovers from a transient copy failure on its own vendor destination instead of crashing the build" do
86
+ def vendor(file_wrapper, dir, config)
87
+ build_configurator_setup( file_wrapper ).vendor_frameworks_and_support_files( dir, config, test_build: true, release_build: false )
88
+ end
89
+
90
+ def snapshot(dest)
91
+ return Dir.glob( File.join( dest, '**', '*' ) ).select { |path| File.file?( path ) }.to_h do |path|
92
+ stat = File.stat( path )
93
+ [path, [stat.ino, stat.mtime]]
94
+ end
95
+ end
96
+
97
+ def leftovers(dest)
98
+ return Dir.glob( File.join( dest, '**', '*' ), File::FNM_DOTMATCH ).select { |path| File.basename( path ).include?( '.tmp' ) }
99
+ end
100
+
101
+ it "copies every file, including those in subdirectories, into a fresh destination" do
87
102
  with_source_tree({}) do |dir|
88
- config = flattened_config(dir)
103
+ config = flattened_config( dir )
89
104
  dest = config[:project_build_vendor_unity_path]
90
- # Real usage always runs build_directory_structure (which mkdir_p's every
91
- # vendor destination) before this copy -- match that precondition here so the
92
- # only fault in play is the one this spec injects, not a missing parent dir.
93
- FileUtils.mkdir_p(dest)
94
-
95
- # Exactly one raise, well within cp_r_with_retry's own retry budget -- the
96
- # source is otherwise 100% fine, the real copy proceeds either way.
97
- faulting_fw = CpRFaultingFileWrapper.new(
98
- fault_dest: dest, raises_left: 1,
99
- loginator: IntegrationSpecHelpers::NULL, verbosinator: IntegrationSpecHelpers::NULL
100
- )
101
- setup = build_configurator_setup(faulting_fw)
102
105
 
103
- expect {
104
- setup.vendor_frameworks_and_support_files(dir, config)
105
- }.not_to raise_error
106
+ vendor( file_wrapper, dir, config )
107
+
108
+ expect( File.read( File.join( dest, 'unity.h' ) ) ).to eq( "// stand-in for unity.h\n" )
109
+ expect( File.exist?( File.join( dest, 'internal', 'unity_internals.h' ) ) ).to be(true)
110
+ expect( leftovers( dest ) ).to be_empty
111
+ end
112
+ end
113
+
114
+ it "rewrites nothing when the destination already matches" do
115
+ with_source_tree({}) do |dir|
116
+ config = flattened_config( dir )
117
+ dest = config[:project_build_vendor_unity_path]
118
+ vendor( file_wrapper, dir, config )
119
+ before = snapshot( dest )
120
+ sleep( 1.1 ) # Past the coarsest file modification time resolution
106
121
 
107
- expect(File.exist?(File.join(dest, 'unity.c'))).to be true
122
+ vendor( file_wrapper, dir, config )
123
+
124
+ expect( snapshot( dest ) ).to eq( before )
125
+ end
126
+ end
127
+
128
+ it "restores an edited file by replacing it whole, and leaves matching files alone" do
129
+ with_source_tree({}) do |dir|
130
+ config = flattened_config( dir )
131
+ dest = config[:project_build_vendor_unity_path]
132
+ vendor( file_wrapper, dir, config )
133
+ edited = File.join( dest, 'unity.h' )
134
+ File.write( edited, "// an errant edit\n" )
135
+ before = snapshot( dest )
136
+
137
+ vendor( file_wrapper, dir, config )
138
+
139
+ expect( File.read( edited ) ).to eq( "// stand-in for unity.h\n" )
140
+ expect( File.stat( edited ).ino ).not_to eq( before[edited].first ) unless SystemWrapper.windows?
141
+ untouched = File.join( dest, 'unity.c' )
142
+ expect( snapshot( dest )[untouched] ).to eq( before[untouched] )
143
+ expect( leftovers( dest ) ).to be_empty
108
144
  end
109
145
  end
110
146
 
111
- it "still raises a clear, path-naming CeedlingException when the fault never clears" do
147
+ it "restores a missing file" do
112
148
  with_source_tree({}) do |dir|
113
- config = flattened_config(dir)
149
+ config = flattened_config( dir )
114
150
  dest = config[:project_build_vendor_unity_path]
115
- FileUtils.mkdir_p(dest)
151
+ vendor( file_wrapper, dir, config )
152
+ FileUtils.rm( File.join( dest, 'internal', 'unity_internals.h' ) )
153
+
154
+ vendor( file_wrapper, dir, config )
155
+
156
+ expect( File.exist?( File.join( dest, 'internal', 'unity_internals.h' ) ) ).to be(true)
157
+ end
158
+ end
159
+
160
+ it "recovers from a transient copy failure instead of crashing the build" do
161
+ with_source_tree({}) do |dir|
162
+ config = flattened_config( dir )
163
+ dest = config[:project_build_vendor_unity_path]
164
+
165
+ expect { vendor( file_wrapper( fault_dir: dest, raises_left: 1 ), dir, config ) }.not_to raise_error
116
166
 
117
- # More raises than cp_r_with_retry's own retry budget (initial attempt + 4
118
- # retries) -- every attempt fails, so the fault genuinely never clears.
119
- permanently_faulting_fw = CpRFaultingFileWrapper.new(
120
- fault_dest: dest, raises_left: 999,
121
- loginator: IntegrationSpecHelpers::NULL, verbosinator: IntegrationSpecHelpers::NULL
122
- )
123
- setup = build_configurator_setup(permanently_faulting_fw)
167
+ expect( File.exist?( File.join( dest, 'unity.c' ) ) ).to be(true)
168
+ expect( leftovers( dest ) ).to be_empty
169
+ end
170
+ end
171
+
172
+ it "raises a clear, path-naming CeedlingException when the fault never clears" do
173
+ with_source_tree({}) do |dir|
174
+ config = flattened_config( dir )
175
+ dest = config[:project_build_vendor_unity_path]
124
176
 
125
177
  expect {
126
- setup.vendor_frameworks_and_support_files(dir, config)
127
- }.to raise_error(CeedlingException, /#{Regexp.escape(dest)}/)
178
+ vendor( file_wrapper( fault_dir: dest, raises_left: 999 ), dir, config )
179
+ }.to raise_error( CeedlingException, /#{Regexp.escape( dest )}/ )
180
+ end
181
+ end
182
+ # GH #1376: a deduplicated file the reporter's lstat called a directory left one where any
183
+ # vendored file belongs, not only the framework's own .c file
184
+ it "replaces a directory where any vendored file belongs" do
185
+ with_source_tree({}) do |dir|
186
+ config = flattened_config( dir )
187
+ dest = config[:project_build_vendor_unity_path]
188
+ FileUtils.mkdir_p( File.join( dest, 'unity.h' ) )
189
+ write( File.join( dest, 'internal', 'unity_internals.h', 'junk' ) )
190
+
191
+ vendor( file_wrapper, dir, config )
192
+
193
+ expect( File.read( File.join( dest, 'unity.h' ) ) ).to eq( "// stand-in for unity.h\n" )
194
+ expect( File.file?( File.join( dest, 'internal', 'unity_internals.h' ) ) ).to be(true)
195
+ end
196
+ end
197
+
198
+ it "vendors every file as a file when lstat reports files as directories" do
199
+ with_source_tree({}) do |dir|
200
+ config = flattened_config( dir )
201
+ dest = config[:project_build_vendor_unity_path]
202
+ lie_about_lstat( %r{gem_vendor/} )
203
+
204
+ begin
205
+ vendor( file_wrapper, dir, config )
206
+ ensure
207
+ # The temporary directory is removed by an lstat-based remover
208
+ allow(File).to receive(:lstat).and_call_original
209
+ end
210
+
211
+ expect( File.file?( File.join( dest, 'unity.c' ) ) ).to be(true)
212
+ expect( File.file?( File.join( dest, 'internal', 'unity_internals.h' ) ) ).to be(true)
213
+ end
214
+ end
215
+
216
+ # A read-only gem install would otherwise vendor read-only files, which Windows will not
217
+ # let a later rename replace
218
+ it "vendors owner-writable files from a read-only source, and replaces a read-only destination" do
219
+ with_source_tree({}) do |dir|
220
+ config = flattened_config( dir )
221
+ dest = config[:project_build_vendor_unity_path]
222
+ source = File.join( config[:unity_vendor_path], 'unity', 'src', 'unity.h' )
223
+ File.chmod( 0o444, source )
224
+ vendor( file_wrapper, dir, config )
225
+ expect( File.stat( File.join( dest, 'unity.h' ) ).mode & 0o200 ).to eq( 0o200 )
226
+
227
+ File.chmod( 0o644, source )
228
+ File.write( source, "// a newer unity.h\n" )
229
+ File.chmod( 0o444, File.join( dest, 'unity.h' ) )
230
+ vendor( file_wrapper, dir, config )
231
+
232
+ expect( File.read( File.join( dest, 'unity.h' ) ) ).to eq( "// a newer unity.h\n" )
233
+ end
234
+ end
235
+
236
+ it "fails at once on an error retrying cannot clear" do
237
+ with_source_tree({}) do |dir|
238
+ config = flattened_config( dir )
239
+ dest = config[:project_build_vendor_unity_path]
240
+ faulting = file_wrapper( fault_dir: dest, raises_left: 999, error: Errno::ENOSPC )
241
+ allow(faulting).to receive(:sleep)
242
+
243
+ expect { vendor( faulting, dir, config ) }.to raise_error( Errno::ENOSPC )
244
+ expect(faulting).not_to have_received(:sleep)
128
245
  end
129
246
  end
130
247
  end
@@ -0,0 +1,88 @@
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 'delegate'
9
+ require 'fileutils'
10
+ require 'tmpdir'
11
+
12
+ # Support for specs that copy real file trees (`filesystem: true`).
13
+ #
14
+ # CEEDLING_TEST_FS_ROOT names a directory on another filesystem, such as a tmpfs, a vfat
15
+ # image, a UNC share, or a VHD volume. These specs then build their trees there instead of
16
+ # the system temp directory, so CI can run one suite on several filesystems.
17
+ module FileTreeHelper
18
+ ROOT_ENV = 'CEEDLING_TEST_FS_ROOT'
19
+
20
+ # A stat result that claims a directory. Reproduces a filesystem whose `lstat` reports a
21
+ # regular file as a directory.
22
+ class LyingStat < SimpleDelegator
23
+ def directory?
24
+ true
25
+ end
26
+
27
+ def file?
28
+ false
29
+ end
30
+
31
+ def symlink?
32
+ false
33
+ end
34
+
35
+ def ftype
36
+ 'directory'
37
+ end
38
+
39
+ def mode
40
+ (__getobj__.mode & ~0o170000) | 0o040000
41
+ end
42
+ end
43
+
44
+ # A temporary directory on the filesystem under test, removed afterwards
45
+ def with_tree_root
46
+ root = ENV[ROOT_ENV].to_s
47
+ dir = root.empty? ? Dir.mktmpdir( 'tree' ) : Dir.mktmpdir( 'tree', root )
48
+ yield File.realpath( dir )
49
+ ensure
50
+ FileUtils.rm_rf( dir ) if dir
51
+ end
52
+
53
+ def write(path, content = File.basename( path ))
54
+ FileUtils.mkdir_p( File.dirname( path ) )
55
+ File.binwrite( path, content )
56
+ end
57
+
58
+ def symlink_or_skip(target, link)
59
+ File.symlink( target, link )
60
+ rescue NotImplementedError, SystemCallError => e
61
+ skip "symlinks are unavailable here (#{e.class})"
62
+ end
63
+
64
+ # Every entry below `dir`, dotfiles included, as sorted relative paths
65
+ def tree_of(dir)
66
+ Dir.glob( '**/*', File::FNM_DOTMATCH, base: dir ).reject { |path| path == '.' || path.end_with?( '/.' ) }.sort
67
+ end
68
+
69
+ # Makes `File.lstat` lie, claiming a directory, for each path matching `pattern`
70
+ def lie_about_lstat(pattern)
71
+ allow(File).to receive(:lstat).and_wrap_original do |original, path, *rest|
72
+ status = original.call( path, *rest )
73
+ path.to_s.match?( pattern ) ? LyingStat.new( status ) : status
74
+ end
75
+ end
76
+
77
+ # Fails any `File.lstat` call, proving code under test never classifies by it
78
+ def forbid_lstat
79
+ allow(File).to receive(:lstat).and_raise( RuntimeError, 'File.lstat must not be called' )
80
+ end
81
+
82
+ # The publish step FileWrapper#replace_file performs: a whole copy renamed into place
83
+ PUBLISH = lambda do |source, destination|
84
+ temp = "#{destination}.publishing"
85
+ FileUtils.cp( source, temp )
86
+ File.rename( temp, destination )
87
+ end
88
+ end