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,184 @@
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 'pathname'
9
+ require 'rbconfig'
10
+ require 'ceedling/filesystem/working_dir'
11
+
12
+ # One spelling for every path a project names.
13
+ #
14
+ # A place can be written many ways: relative or absolute, with `./`, `..`, `~`, backslashes,
15
+ # or through a symlink. Intake spells each path once, relative to the working directory
16
+ # (`canonical`). Comparisons then use whole directories, never text prefixes (`contains?`).
17
+ # BuildSubpath places build files from these spellings.
18
+ #
19
+ # Pure functions over strings, except where a function says it reads the disk, and no
20
+ # dependency on DIY, so the launcher in bin/ shares it.
21
+ module ProjectPaths
22
+
23
+ DECORATOR = /\A[+-]:/
24
+ WINDOWS = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|msys|ucrt/i) ? true : false
25
+
26
+ # What `absolute` makes of a path, which `clean_absolute?` finds already so: rooted, `C:/`
27
+ # on Windows, and free of `.`/`..` segments, doubled `/`, and backslashes. Windows'
28
+ # File.expand_path also lengthens a short `~` name and drops a trailing dot or space.
29
+ ROOTED = WINDOWS ? %r{\A[A-Z]:/} : %r{\A/}
30
+ ROOT_LENGTH = WINDOWS ? 3 : 1
31
+ UNCLEAN = WINDOWS ? %r{//|/\.\.?(?:/|\z)|\\|~|[. ](?:/|\z)} : %r{//|/\.\.?(?:/|\z)|\\}
32
+
33
+ @lock = Mutex.new
34
+ @absolutes = {}
35
+
36
+ # The working directory with every symlink resolved, read once (see WorkingDir)
37
+ def self.working_dir
38
+ return WorkingDir.path
39
+ end
40
+
41
+ # A path written as a project file writes it, in Ceedling's form: trimmed, `/`-separated,
42
+ # with no trailing separator, and `~` read as the home directory. A `+:`/`-:` decorator is
43
+ # kept. `~user` is left as written.
44
+ def self.standardize(path)
45
+ path = path.to_s.strip.tr( '\\', '/' )
46
+ decorator = path[DECORATOR] || ''
47
+ path = expand_home( path.delete_prefix( decorator ).strip )
48
+ path = path.sub( %r{/+\z}, '' ) unless path.match?( %r{\A(?:[A-Za-z]:)?/+\z} )
49
+ return decorator + path
50
+ end
51
+
52
+ # `path` as an absolute path, its `..` and `.` resolved against the working directory
53
+ # without reading the disk. A drive letter is upper case. Remembered for each working
54
+ # directory and home directory, since a build compares the same few roots many times.
55
+ def self.absolute(path)
56
+ return path if clean_absolute?( path )
57
+
58
+ key = [working_dir, Dir.home, path.to_s]
59
+ cached = @lock.synchronize { @absolutes[key] }
60
+ return cached unless cached.nil?
61
+
62
+ absolute = absolute_of( path ).freeze
63
+ return @lock.synchronize { @absolutes[key] = absolute }
64
+ end
65
+
66
+ # The one spelling intake records for `path`: relative to the working directory, with the
67
+ # symlinks along its existing part resolved. A path with no relative form (another drive,
68
+ # a UNC share) stays absolute. Reads the disk.
69
+ def self.canonical(path)
70
+ return relative( real_absolute( path ) )
71
+ end
72
+
73
+ # `absolute` with the symlinks along its existing part resolved. Reads the disk.
74
+ def self.real_absolute(path)
75
+ return real( absolute( path ) )
76
+ end
77
+
78
+ # `absolute` relative to the working directory, or absolute when it has no relative form.
79
+ # The working directory itself is `.`.
80
+ def self.relative(absolute)
81
+ return absolute if unc?( absolute )
82
+
83
+ # Most paths lie below the working directory, which needs no Pathname walk
84
+ return '.' if same?( absolute, working_dir )
85
+ return absolute[(working_dir.length + 1)..] if clean_absolute?( absolute ) and starts_with?( absolute, "#{working_dir}/" )
86
+
87
+ return Pathname.new( absolute ).relative_path_from( Pathname.new( working_dir ) ).to_s
88
+ rescue ::ArgumentError
89
+ return absolute
90
+ end
91
+
92
+ # Whether `path` is `root` or lies below it, compared as whole directories. Windows folds case.
93
+ def self.contains?(root, path)
94
+ return !subdir_below( root, path ).nil?
95
+ end
96
+
97
+ # The directories from `root` down to `path`: '' for `root` itself, or nil when `path`
98
+ # doesn't lie below `root`. Never holds `..`, and is never absolute.
99
+ def self.subdir_below(root, path)
100
+ base = absolute( root )
101
+ full = absolute( path )
102
+ return '' if same?( base, full )
103
+
104
+ prefix = base.end_with?( '/' ) ? base : "#{base}/"
105
+ return nil unless starts_with?( full, prefix )
106
+
107
+ return full[prefix.length..]
108
+ end
109
+
110
+ # A path's segments, `.` and empty segments dropped
111
+ def self.segments(path)
112
+ return path.to_s.split( %r{[\\/]} ).reject { |segment| segment.empty? || segment == '.' }
113
+ end
114
+
115
+ def self.absolute?(path)
116
+ return path.start_with?( '/' ) || path.match?( %r{\A[A-Za-z]:/} )
117
+ end
118
+
119
+ # A Windows share. Elsewhere a doubled leading separator is the filesystem root.
120
+ def self.unc?(path)
121
+ return WINDOWS && path.start_with?( '//' )
122
+ end
123
+
124
+ # Whether two spellings are the same text, folding case on Windows
125
+ def self.same?(a, b)
126
+ return WINDOWS ? a.casecmp?( b ) : a == b
127
+ end
128
+
129
+ def self.starts_with?(path, prefix)
130
+ return WINDOWS ? path.downcase.start_with?( prefix.downcase ) : path.start_with?( prefix )
131
+ end
132
+
133
+ # Already absolute as `absolute` would spell it: rooted (with an upper case drive on
134
+ # Windows), with no `.` or `..` segment, doubled or trailing `/`, backslash, or surrounding
135
+ # space. On Windows, File.expand_path also lengthens a short `~` name and drops a trailing
136
+ # dot or space, so a segment with any of those is never clean.
137
+ def self.clean_absolute?(path)
138
+ return false unless path.is_a?( String ) and path.match?( ROOTED )
139
+ return false if path.match?( UNCLEAN ) or path != path.strip
140
+
141
+ return path.length == ROOT_LENGTH || !path.end_with?( '/' )
142
+ end
143
+
144
+ # A `~user` path names a directory of that name, which File.expand_path would read as the
145
+ # user's home (or raise for no such user). A share's `..` never leaves the share.
146
+ def self.absolute_of(path)
147
+ path = standardize( path )
148
+ return unc_absolute( path ) if unc?( path )
149
+
150
+ path = "./#{path}" if path.start_with?( '~' )
151
+ return File.expand_path( path, working_dir ).squeeze( '/' ).sub( /\A[a-z](?=:)/ ) { |drive| drive.upcase }
152
+ end
153
+
154
+ def self.unc_absolute(path)
155
+ server, share, *rest = path.split( '/' ).reject { |segment| segment.empty? || segment == '.' }
156
+ kept = rest.each_with_object( [] ) { |segment, resolved| segment == '..' ? resolved.pop : resolved.push( segment ) }
157
+ return "//#{[server, share, *kept].compact.join( '/' )}"
158
+ end
159
+
160
+ def self.expand_home(path)
161
+ return path unless path == '~' || path.start_with?( '~/' )
162
+
163
+ return Dir.home + path.delete_prefix( '~' )
164
+ end
165
+
166
+ # The real path of the longest existing part of `absolute`, with the rest appended
167
+ def self.real(absolute)
168
+ return File.realpath( absolute ) if File.exist?( absolute )
169
+
170
+ existing = absolute
171
+ missing = []
172
+ until File.exist?( existing ) || existing == File.dirname( existing )
173
+ missing.unshift( File.basename( existing ) )
174
+ existing = File.dirname( existing )
175
+ end
176
+
177
+ return absolute unless File.exist?( existing )
178
+
179
+ return File.join( File.realpath( existing ), *missing )
180
+ end
181
+
182
+ private_class_method :clean_absolute?, :absolute_of, :unc_absolute, :expand_home, :real
183
+
184
+ end
@@ -0,0 +1,40 @@
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
+ # The directory Ceedling runs in, read once.
9
+ #
10
+ # A build resolves paths against the working directory tens of thousands of times, and
11
+ # asking the operating system for it costs tens of microseconds on macOS. Ceedling changes
12
+ # directory only through `chdir`, which forgets the remembered directory going in and
13
+ # coming out.
14
+ #
15
+ # No dependency on DIY, so the launcher in bin/ shares it.
16
+ module WorkingDir
17
+
18
+ @lock = Mutex.new
19
+ @path = nil
20
+
21
+ # The working directory with every symlink resolved
22
+ def self.path
23
+ return @path || @lock.synchronize { @path ||= File.realpath( Dir.pwd ).freeze }
24
+ end
25
+
26
+ # Changes directory for the block, as Dir.chdir does, so paths resolved inside it resolve
27
+ # against the new directory and paths resolved after it against the old one
28
+ def self.chdir(dir, &block)
29
+ forget
30
+ return Dir.chdir( dir, &block )
31
+ ensure
32
+ forget
33
+ end
34
+
35
+ # The next `path` reads the working directory again
36
+ def self.forget
37
+ @lock.synchronize { @path = nil }
38
+ end
39
+
40
+ end
@@ -8,7 +8,7 @@
8
8
  require 'ceedling/constants'
9
9
  require 'ceedling/exceptions'
10
10
  require 'ceedling/includes/includes'
11
- require 'ceedling/file_path_utils'
11
+ require 'ceedling/filesystem/file_path_utils'
12
12
  require 'rake'
13
13
 
14
14
  class Generator
@@ -120,7 +120,7 @@ class Generator
120
120
  return @generator_partials.generate_implementation( **arg_hash )
121
121
  end
122
122
 
123
- def generate_mock(context:, mock:, test:, input_filepath:, output_path:, folder: nil, overrides: {})
123
+ def generate_mock(context:, mock:, test:, input_filepath:, output_path:, mock_root:, folder: nil, overrides: {})
124
124
  arg_hash = {
125
125
  :header_file => input_filepath,
126
126
  :test => test,
@@ -139,8 +139,7 @@ class Generator
139
139
  # - Make CMock thread-safe
140
140
 
141
141
  # Get default config created by Ceedling and customize it
142
- # CMock writes to its root plus `folder`. That folder always ends `output_path`.
143
- mock_root = folder ? output_path.delete_suffix( File.join( '', folder ) ) : output_path
142
+ # CMock writes below `mock_root` at `folder`, which leads to `output_path`
144
143
  config = @generator_mocks.build_configuration( mock_root, overrides: overrides )
145
144
 
146
145
  # Generate mock
@@ -514,8 +513,8 @@ class Generator
514
513
  explain = ->(result) { crash_message( crash_explanation( arg_hash, result ) ) }
515
514
 
516
515
  case backtrace
517
- when :gdb then @backtrace.do_gdb( filename, executable, shell_result, test_cases, context: context )
518
- when :simple then @backtrace.do_simple( filename, executable, shell_result, test_cases, context: context, tool: arg_hash[:tool], explain: explain )
516
+ when :gdb then @backtrace.do_gdb( filename, executable, shell_result, test_cases, context: context, test_name: arg_hash[:test_name] )
517
+ when :simple then @backtrace.do_simple( filename, executable, shell_result, test_cases, tool: arg_hash[:tool], explain: explain )
519
518
  # A crash is otherwise reported as a single failure
520
519
  else @generator_test_results.create_crash_failure( filename, shell_result, test_cases, message: crash_message( explanation ) || 'Test executable crashed' )
521
520
  end
@@ -5,7 +5,7 @@
5
5
  # SPDX-License-Identifier: MIT
6
6
  # =========================================================================
7
7
 
8
- require 'ceedling/file_wrapper'
8
+ require 'ceedling/filesystem/file_wrapper'
9
9
  require 'ceedling/partials/partials'
10
10
  require 'ceedling/c_extractor/c_extractor_types'
11
11
  require 'ceedling/c_extractor/c_extractor_constants'
@@ -8,7 +8,7 @@
8
8
  class GeneratorTestResultsBacktrace
9
9
 
10
10
  constructor :configurator, :tool_executor, :generator_test_results, :generator_helper,
11
- :file_path_utils, :file_wrapper, :loginator
11
+ :file_path_utils, :build_log_writer, :loginator
12
12
 
13
13
  def setup()
14
14
  @RESULTS_COLLECTOR = Struct.new( :passed, :failed, :ignored, :output, keyword_init:true )
@@ -31,7 +31,10 @@ class GeneratorTestResultsBacktrace
31
31
  # Writes the full gdb transcript to a per-test-case log file and assembles a
32
32
  # terse crash label (signal + description, optional source line in backticks)
33
33
  # for each failing test case. Returns a modified shell_result with regenerated output.
34
- def do_gdb(filename, executable, shell_result, test_cases, context:)
34
+ #
35
+ # `test_name` places the logs, so same-named tests keep separate ones. Each log holds only
36
+ # this build's crash.
37
+ def do_gdb(filename, executable, shell_result, test_cases, context:, test_name:)
35
38
  gdb_script_filepath = File.join( @configurator.project_build_tests_root, BACKTRACE_GDB_SCRIPT_FILE )
36
39
 
37
40
  # Clean stats tracker
@@ -40,8 +43,6 @@ class GeneratorTestResultsBacktrace
40
43
  # Reset time
41
44
  shell_result[:time] = 0
42
45
 
43
- test_name = File.basename( filename, '.*' )
44
-
45
46
  # True once some retry group has actually shown crash evidence of its own -- an
46
47
  # unresolved member, or (below) a group whose real status contradicts a fully clean
47
48
  # set of matches. If this stays false across every group, the whole diagnostic never
@@ -120,8 +121,7 @@ class GeneratorTestResultsBacktrace
120
121
 
121
122
  # Per-test-case log file: <log_path>/<context>/<test_name>/<test_case>.gdb.log
122
123
  log_path = @file_path_utils.form_test_gdb_log( test_name, context: context, name: crashed_case[:test] )
123
- @file_wrapper.mkdir( File.dirname( log_path ) )
124
- @file_wrapper.write( log_path, "=== #{crashed_case[:test]} ===\n#{crash_result[:output]}\n", 'a' )
124
+ @build_log_writer.write( log_path, "=== #{crashed_case[:test]} ===\n#{crash_result[:output]}\n" )
125
125
 
126
126
  unresolved.each do |test_case|
127
127
  group_results[:failed] += 1
@@ -296,7 +296,7 @@ class GeneratorTestResultsBacktrace
296
296
  # such as Valgrind's, keeps the main run's report.
297
297
  #
298
298
  # `explain` names the cause of a rerun's crash, if a plugin recognizes it.
299
- def do_simple(filename, executable, shell_result, test_cases, context:, tool: nil, explain: nil)
299
+ def do_simple(filename, executable, shell_result, test_cases, tool: nil, explain: nil)
300
300
  results = @RESULTS_COLLECTOR.new( passed:0, failed:0, ignored:0, output:[] )
301
301
  shell_result[:time] = 0 # Summed over the per-group sub-process runs
302
302
  any_crashed = false
@@ -0,0 +1,121 @@
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 'rbconfig'
9
+ require_relative 'installation'
10
+
11
+ # The protocol by which one Ceedling installation hands a command off to another: the
12
+ # selected installation's own launcher replaces this process, given the original command
13
+ # line. Environment markers tell that process who launched it and stop it handing off again.
14
+ #
15
+ # Like Installation, this file depends on nothing else in lib/, so an application loaded
16
+ # by an older launcher can load it with require_relative.
17
+ module Handoff
18
+
19
+ LAUNCHER_VAR = 'CEEDLING_HANDOFF_LAUNCHER'
20
+ TARGET_VAR = 'CEEDLING_HANDOFF_TARGET'
21
+
22
+ # Removes the markers from env, so no subprocess of this build inherits them, and
23
+ # returns them, or nil when no launcher handed off to this process
24
+ def self.take_markers(env)
25
+ launcher = env.delete( LAUNCHER_VAR )
26
+ target = env.delete( TARGET_VAR )
27
+ return nil if target.nil?
28
+
29
+ return {launcher: launcher, target: target}
30
+ end
31
+
32
+ # Whether the markers name own as the installation a launcher handed off to
33
+ def self.received?(markers, own:)
34
+ return false if markers.nil? or markers[:target].nil?
35
+
36
+ return Installation.new( markers[:target] ) == own
37
+ end
38
+
39
+ def self.handing_off_message(launcher:, target:)
40
+ return "#{launcher.describe( 'Ceedling launcher' )} is handing off to #{target.describe()}, which runs in its own process."
41
+ end
42
+
43
+ def self.received_message(markers, own:)
44
+ return "#{own.describe()} is now running, launched from #{Installation.new( markers[:launcher] ).describe()}."
45
+ end
46
+
47
+ # Replaces this process with target's own launcher. Nothing after this call runs, not
48
+ # even END blocks, so logging drains and the standard streams flush first.
49
+ def self.exec(target:, launcher:, argv:, loginator:)
50
+ loginator.wrapup()
51
+ $stdout.flush()
52
+ $stderr.flush()
53
+
54
+ markers = {LAUNCHER_VAR => launcher.root, TARGET_VAR => target.root}
55
+ replace_process( markers, RbConfig.ruby, target.bin_path, *argv )
56
+ end
57
+
58
+ # Finishes a hand-off that a launcher before 1.2.2 cannot make: it loads this application
59
+ # into its own process. Every such launcher kept the original command line in the
60
+ # SystemWrapper it hands over, so the application restarts as its own launcher. Without
61
+ # that command line, or inside a hand-off already, it warns and continues in-process,
62
+ # so a restart never cycles.
63
+ def self.complete_in_process(app_root:, app_cfg:, handoff_objects:, env:)
64
+ launcher = Installation.new( launcher_root_of( app_cfg ) )
65
+ app = Installation.new( app_root )
66
+ return if launcher == app
67
+
68
+ loginator = handoff_objects[:loginator]
69
+ argv = command_line( handoff_objects[:system_wrapper] )
70
+ restart = !(argv.nil? or in_handoff?( app_cfg, env ))
71
+
72
+ loginator.log( loaded_message( launcher, app, restart ), Verbosity::COMPLAIN )
73
+ exec( target:app, launcher:launcher, argv:argv, loginator:loginator ) if restart
74
+ end
75
+
76
+ # The one seam to the operating system, so specs can stand in for it
77
+ def self.replace_process(env, *command)
78
+ Kernel.exec( env, *command )
79
+ end
80
+
81
+ # The launcher's CeedlingAppConfig is defined in its own bin/
82
+ def self.launcher_root_of(app_cfg)
83
+ filepath, _ = app_cfg.class.instance_method( :initialize ).source_location
84
+ return File.expand_path( '..', File.dirname( filepath ) )
85
+ end
86
+
87
+ def self.command_line(system_wrapper)
88
+ return nil if !system_wrapper.respond_to?( :get_cmdline )
89
+
90
+ return system_wrapper.get_cmdline()
91
+ end
92
+
93
+ def self.in_handoff?(app_cfg, env)
94
+ return true if !app_cfg[:handoff].nil?
95
+
96
+ return !(env[TARGET_VAR].nil? and env[LAUNCHER_VAR].nil?)
97
+ end
98
+
99
+ def self.loaded_message(launcher, app, restart)
100
+ loaded = "#{launcher.describe( 'Ceedling launcher' )} loaded #{app.describe()} into its own process."
101
+ return "#{loaded} Restarting in #{app.name()}'s own process. Upgrade Ceedling and review your installations to avoid this." if restart
102
+
103
+ return "#{loaded} #{continuing_advice( app )}"
104
+ end
105
+
106
+ def self.continuing_advice(app)
107
+ return "Continuing in the launcher's process, which can fail or misbehave. Upgrade Ceedling, or run this " +
108
+ "installation's own launcher directly with `ruby #{display_path( app.bin_path )}`. A project created " +
109
+ "with `--local` has a launch script at its root that does this: run `./ceedling` from the project root " +
110
+ "(`.\\ceedling.cmd` on Windows)."
111
+ end
112
+
113
+ # Relative to the working directory when beneath it, as a user would type it
114
+ def self.display_path(filepath)
115
+ cwd = File.join( Dir.pwd, '' )
116
+ return filepath.start_with?( cwd ) ? filepath.delete_prefix( cwd ) : filepath
117
+ end
118
+
119
+ private_class_method :launcher_root_of, :command_line, :in_handoff?, :loaded_message, :continuing_advice, :display_path
120
+
121
+ end
@@ -40,9 +40,9 @@ class IncludeFactory
40
40
 
41
41
  # A resolved mock include's filepath may carry a leading build directory path that
42
42
  # snuck in from discovering an empty mock stand-in or an already-generated mock file.
43
- # That leading path is the current test's own mock subdirectory -- which mirrors the
44
- # test's path below its configured :paths -> :test root, so it can be more than one
45
- # segment deep (e.g. `adc/TestFoo`) -- followed by however many further segments mirror
43
+ # That leading path is the current test's own mock subdirectory -- named by the test's
44
+ # path from the project root, so it can be more than one segment deep (e.g.
45
+ # `test/adc/TestFoo`) -- followed by however many further segments mirror
46
46
  # the mocked header's own relative directory (e.g. `calculators/`). Knowing the current
47
47
  # test's identity lets exactly the first part be removed, leaving any header-mirroring
48
48
  # segments after it untouched. Without that identity, fall back to stripping everything
@@ -7,10 +7,18 @@
7
7
 
8
8
  require 'pathname'
9
9
  require 'ceedling/exceptions'
10
- require 'ceedling/file_path_utils'
10
+ require 'ceedling/filesystem/file_path_utils'
11
+ require 'ceedling/filesystem/project_paths'
11
12
 
12
13
  class IncludePathinator
13
14
 
15
+ NO_HEADERS_MESSAGE = "No header files found in project.\n" +
16
+ "Add search paths to :paths ↳ :include in your project file and/or use TEST_INCLUDE_PATH() in your test files.\n" +
17
+ "Verify header files with `ceedling paths:include` and\\or `ceedling files:include`."
18
+
19
+ # What `header_listing` remembers for a directory under the build root
20
+ LISTED_FRESH = :listed_fresh
21
+
14
22
  constructor :configurator, :test_context_extractor, :loginator, :file_wrapper
15
23
 
16
24
  def setup
@@ -20,6 +28,10 @@ class IncludePathinator
20
28
 
21
29
  # Alias for brevity
22
30
  @extractor = @test_context_extractor
31
+
32
+ # Each search path's header listing for one test build (see `header_listing`)
33
+ @listings = {}
34
+ @listings_lock = Mutex.new
23
35
  end
24
36
 
25
37
  def validate_test_build_directive_paths
@@ -52,12 +64,7 @@ class IncludePathinator
52
64
 
53
65
  headers.uniq!
54
66
 
55
- if headers.length == 0
56
- error = "No header files found in project.\n" +
57
- "Add search paths to :paths ↳ :include in your project file and/or use TEST_INCLUDE_PATH() in your test files.\n" +
58
- "Verify header files with `ceedling paths:include` and\\or `ceedling files:include`."
59
- @loginator.log( error, Verbosity::COMPLAIN )
60
- end
67
+ @loginator.log( NO_HEADERS_MESSAGE, Verbosity::COMPLAIN ) if headers.empty?
61
68
 
62
69
  return headers
63
70
  end
@@ -70,11 +77,16 @@ class IncludePathinator
70
77
  def ordered_header_files(search_paths)
71
78
  headers = []
72
79
  search_paths.each do |path|
73
- headers += @file_wrapper.directory_listing( @configurator.extension_header.glob_patterns(path) )
80
+ headers.concat( header_listing( path ) )
74
81
  end
75
82
  return headers.uniq
76
83
  end
77
84
 
85
+ # Starts a test build with every search path listed afresh
86
+ def forget_header_listings
87
+ @listings_lock.synchronize { @listings.clear }
88
+ end
89
+
78
90
  # The project's whole header collection, reordered so headers beneath this test's
79
91
  # own earlier search paths come first.
80
92
  #
@@ -88,10 +100,10 @@ class IncludePathinator
88
100
  # reference that distinguishes nothing resolving exactly as it did before.
89
101
  def prioritized_header_files(search_paths)
90
102
  roots = search_paths.map { |path| FilePathUtils.no_decorators( path ) }.reject(&:empty?)
103
+ ranks = root_ranks( roots )
91
104
 
92
105
  @configurator.collection_all_headers.to_a.each_with_index.sort_by do |header, index|
93
- rank = roots.index { |root| header == root || header.start_with?( root + '/' ) } || roots.length
94
- [rank, index]
106
+ [header_rank( header, ranks, roots.length ), index]
95
107
  end.map(&:first)
96
108
  end
97
109
 
@@ -116,4 +128,49 @@ class IncludePathinator
116
128
  return paths
117
129
  end
118
130
 
131
+ private
132
+
133
+ # One search path's headers. A directory outside the build root keeps its listing for the
134
+ # test build, since a build never writes there, and a test's search paths are listed
135
+ # several times a test. One under the build root is listed every time, since the build
136
+ # fills it as it runs.
137
+ def header_listing(path)
138
+ key = [ProjectPaths.working_dir, path]
139
+ remembered = @listings_lock.synchronize { @listings[key] }
140
+ return remembered if remembered.is_a?( Array )
141
+
142
+ listing = @file_wrapper.directory_listing( @configurator.extension_header.glob_patterns( path ) ).freeze
143
+ return listing if remembered == LISTED_FRESH
144
+
145
+ kept = ProjectPaths.contains?( @configurator.project_build_root, path ) ? LISTED_FRESH : listing
146
+ @listings_lock.synchronize { @listings[key] ||= kept }
147
+ return listing
148
+ end
149
+
150
+ # Each root's place in the search order by its absolute path, the first one kept, so a
151
+ # header's rank comes from walking up its own directories rather than trying every root
152
+ def root_ranks(roots)
153
+ return roots.each_with_index.with_object( {} ) { |(root, index), ranks| ranks[rank_key( ProjectPaths.absolute( root ) )] ||= index }
154
+ end
155
+
156
+ # The earliest-searched root a header lies below, or `none` for no root
157
+ def header_rank(header, ranks, none)
158
+ rank = none
159
+ path = ProjectPaths.absolute( header )
160
+ loop do
161
+ parent = File.dirname( path )
162
+ break if parent == path
163
+
164
+ path = parent
165
+ rank = [rank, ranks.fetch( rank_key( path ), none )].min
166
+ end
167
+
168
+ return rank
169
+ end
170
+
171
+ # Windows compares paths without regard to case
172
+ def rank_key(path)
173
+ return ProjectPaths::WINDOWS ? path.downcase : path
174
+ end
175
+
119
176
  end
@@ -7,7 +7,7 @@
7
7
 
8
8
  require 'set'
9
9
  require 'ceedling/exceptions'
10
- require 'ceedling/path_matcher'
10
+ require 'ceedling/filesystem/path_matcher'
11
11
 
12
12
  class Includes
13
13
  # Class method to convert mixed list of Include objects into an order-preserving list of hashes