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
@@ -25,6 +25,8 @@ EXTENSION_HEADER = FilenameExtension.new('.h') unless defined?(EXTENSION_
25
25
  describe TestBuildPlanner do
26
26
  before(:each) do
27
27
  @configurator = double( "Configurator" )
28
+ # Ceedling always defaults it, and the mock locator reads it
29
+ allow(@configurator).to receive(:cmock_mock_name_include_folder).and_return( true )
28
30
  @loginator = double( "Loginator" )
29
31
  @reportinator = double( "Reportinator" )
30
32
  @batchinator = double( "Batchinator" )
@@ -167,6 +169,7 @@ describe TestBuildPlanner do
167
169
 
168
170
  context "with a non-Partial mocked header" do
169
171
  before(:each) do
172
+ allow(@file_path_utils).to receive(:form_mock_output_path) { |root, subdir| subdir.to_s.empty? ? root : File.join( root, subdir ) }
170
173
  mock = MockInclude.new( 'MockFoo.h' )
171
174
  allow(@test_context_extractor).to receive(:lookup_mock_header_includes_list)
172
175
  .with( 'test/TestFoo.c' ).and_return( [mock] )
@@ -204,6 +207,45 @@ describe TestBuildPlanner do
204
207
  end
205
208
  end
206
209
 
210
+ # Two headers directly in two include roots mirror alike, and only their #include
211
+ # folders tell their mocks apart
212
+ it "keeps two same-named mocks whose #include folders alone differ" do
213
+ allow(@test_context_extractor).to receive(:lookup_mock_header_includes_list)
214
+ .with( 'test/TestFoo.c' ).and_return( [MockInclude.new( 'a/MockFoo.h' ), MockInclude.new( 'b/MockFoo.h' )] )
215
+ allow(@configurator).to receive(:cmock_mock_prefix).and_return( 'Mock' )
216
+ allow(@configurator).to receive(:project_use_test_preprocessor_mocks).and_return( false )
217
+ allow(@file_finder).to receive(:resolve_mock).with( 'a/MockFoo.h', collection: [], complain: :error ).and_return( ['inc/a/foo.h', ''] )
218
+ allow(@file_finder).to receive(:resolve_mock).with( 'b/MockFoo.h', collection: [], complain: :error ).and_return( ['inc/b/foo.h', ''] )
219
+ allow(@file_path_utils).to receive(:form_preprocessed_file_filepath).and_return( 'unused' )
220
+
221
+ @planner.stage_determine_files( @state )
222
+
223
+ expect(@testable.mocks.keys).to contain_exactly( :'a/MockFoo', :'b/MockFoo' )
224
+ expect(@testable.mocks.values.map( &:source )).to contain_exactly( 'inc/a/foo.h', 'inc/b/foo.h' )
225
+ end
226
+
227
+ # Nothing in the loop over a test's mocks changes its search paths, so one collection
228
+ # serves every mock. Validation lists the whole search paths, mock directories included.
229
+ it "builds one mock header collection for all of a test's real mocks" do
230
+ mocks = %w[MockFoo.h MockBar.h MockBaz.h].map { |name| MockInclude.new( name ) }
231
+ allow(@test_context_extractor).to receive(:lookup_mock_header_includes_list).with( 'test/TestFoo.c' ).and_return( mocks )
232
+ allow(@configurator).to receive(:cmock_mock_prefix).and_return( 'Mock' )
233
+ allow(@configurator).to receive(:project_use_test_preprocessor_mocks).and_return( false )
234
+ allow(@file_finder).to receive(:resolve_mock) { |mock, **| ["src/#{mock.delete_prefix( 'Mock' )}", ''] }
235
+ allow(@file_path_utils).to receive(:form_preprocessed_file_filepath).and_return( 'unused' )
236
+ allow(@file_path_utils).to receive(:form_mock_output_path) { |root, subdir| subdir.to_s.empty? ? root : File.join( root, subdir ) }
237
+ @testable.search_paths = ['build/test/mocks/a_test', 'src']
238
+ @testable.mock_search_paths = ['build/test/mocks/a_test']
239
+
240
+ collections = 0
241
+ allow(@include_pathinator).to receive(:ordered_header_files).with( ['src'] ) { collections += 1; ['src/Foo.h'] }
242
+
243
+ @planner.stage_determine_files( @state )
244
+
245
+ expect(@testable.mocks.length).to eq( 3 )
246
+ expect(collections).to eq( 1 )
247
+ end
248
+
207
249
  context "with a Partial mocked header" do
208
250
  before(:each) do
209
251
  mock = MockInclude.new( 'Mockceedling_partial_foo_interface.h' )
@@ -217,6 +259,15 @@ describe TestBuildPlanner do
217
259
 
218
260
  # A Partial mock has no real header to resolve against, so its directory comes
219
261
  # from the generated #include rather than from a mirrored source location.
262
+ it "builds no mock header collection, having no real header to resolve" do
263
+ @testable.search_paths = ['build/test/mocks/a_test', 'src']
264
+ @testable.mock_search_paths = ['build/test/mocks/a_test']
265
+
266
+ expect(@include_pathinator).to_not receive(:ordered_header_files).with( ['src'] )
267
+
268
+ @planner.stage_determine_files( @state )
269
+ end
270
+
220
271
  it "stays flat when its generated #include names no directory" do
221
272
  expect(@file_finder).to_not receive(:resolve_mock)
222
273
 
@@ -431,6 +482,23 @@ describe TestBuildPlanner do
431
482
  expect( captured ).to include('build/test/mocks/a_test/drivers/spi/Mockconfig.c')
432
483
  end
433
484
 
485
+ it "compiles a mock whose #include folder climbs from where it is written, below the mock root" do
486
+ allow(@configurator).to receive(:cmock_mock_name_include_folder).and_return( true )
487
+ @testable.mocks = {
488
+ :'x/Mockconfig' => TestInvokerTypes::MockDetails.new( name: 'Mockconfig', path: 'x', partial: false, include_path: '../x/Mockconfig.h' )
489
+ }
490
+
491
+ captured = nil
492
+ allow(@file_path_utils).to receive(:form_test_build_objects_filelist) do |_path, files, **_kw|
493
+ captured ||= files
494
+ []
495
+ end
496
+
497
+ @planner.stage_determine_artifacts( @state )
498
+
499
+ expect( captured ).to include('build/test/mocks/a_test/_up_/x/Mockconfig.c')
500
+ end
501
+
434
502
  # A mock and a generated Partial both sit below their own generated root rather than a
435
503
  # configured source root, so nothing in the standard roots mirrors them. Without those
436
504
  # roots, two same-named mocks or modules collapse onto one object filename.
@@ -484,7 +552,9 @@ describe TestBuildPlanner do
484
552
  allow(@test_source_file_directive_resolver).to receive(:resolve)
485
553
  .with( 'test/TestFoo.c', :test ).and_return( [['src/Foo.c'], {}] )
486
554
  allow(@test_context_extractor).to receive(:lookup_source_includes_list)
487
- .with( 'test/TestFoo.c' ).and_return( ['src/Foo.c'] )
555
+ .with( 'test/TestFoo.c' ).and_return( [UserInclude.new( 'src/Foo.c' )] )
556
+ allow(@file_finder).to receive(:find_build_input_file)
557
+ .with( filepath: 'src/Foo.c', complain: :ignore, context: :test, test: 'a_test' ).and_return( 'src/Foo.c' )
488
558
  end
489
559
 
490
560
  it "resolves it as a source to compile but excludes its object from what gets linked" do
@@ -494,6 +564,25 @@ describe TestBuildPlanner do
494
564
  expect(@testable.objects).to_not include( 'src/Foo.o' )
495
565
  end
496
566
  end
567
+
568
+ # A test #includes a source by the name its own #include spells, and that source's object
569
+ # sits at the source's path, not the #include's
570
+ context "when a test #includes a source by its name alone" do
571
+ before(:each) do
572
+ allow(@test_source_file_directive_resolver).to receive(:resolve)
573
+ .with( 'test/TestFoo.c', :test ).and_return( [['src/Foo.c'], {}] )
574
+ allow(@test_context_extractor).to receive(:lookup_source_includes_list)
575
+ .with( 'test/TestFoo.c' ).and_return( [UserInclude.new( 'Foo.c' )] )
576
+ allow(@file_finder).to receive(:find_build_input_file)
577
+ .with( filepath: 'Foo.c', complain: :ignore, context: :test, test: 'a_test' ).and_return( 'src/Foo.c' )
578
+ end
579
+
580
+ it "excludes the object of the source it names from what gets linked" do
581
+ @planner.stage_determine_artifacts( @state )
582
+
583
+ expect(@testable.objects).to_not include( 'src/Foo.o' )
584
+ end
585
+ end
497
586
  end
498
587
 
499
588
  describe "#stage_flatten_objects_list" do
@@ -696,7 +785,20 @@ describe TestBuildPlanner do
696
785
 
697
786
  result = @planner.collect_test_framework_sources( true )
698
787
 
699
- expect(result).to include( 'test/support/unity_helper' )
788
+ expect(result).to include( 'test/support/unity_helper.c' )
789
+ end
790
+
791
+ # The path handling audit around GH #1370: :unity_helper_path names the helper's header,
792
+ # and its source is what compiles
793
+ it "G25: compiles the source beside a Unity helper named by its header" do
794
+ allow(@configurator).to receive(:project_use_mocks).and_return( true )
795
+ allow(@configurator).to receive(:cmock_unity_helper_path).and_return( ['test/support/unity_helper.h'] )
796
+ allow(@file_wrapper).to receive(:exist?).with( 'test/support/unity_helper.c' ).and_return( true )
797
+
798
+ result = @planner.collect_test_framework_sources( true )
799
+
800
+ expect(result).to include( 'test/support/unity_helper.c' )
801
+ expect(result).to_not include( 'test/support/unity_helper.h' )
700
802
  end
701
803
 
702
804
  it "omits a Unity helper source file that doesn't exist on disk" do
@@ -14,6 +14,8 @@ require 'ceedling/test_invoker/mock_locator'
14
14
  describe TestBuildSetup do
15
15
  before(:each) do
16
16
  @configurator = double( "Configurator" )
17
+ # Ceedling always defaults it, and the mock locator reads it
18
+ allow(@configurator).to receive(:cmock_mock_name_include_folder).and_return( true )
17
19
  @loginator = double( "Loginator" )
18
20
  @reportinator = double( "Reportinator" )
19
21
  @batchinator = double( "Batchinator" )
@@ -66,6 +68,9 @@ describe TestBuildSetup do
66
68
  allow(@configurator).to receive(:collection_paths_vendor).and_return( [] )
67
69
  allow(@configurator).to receive(:collection_paths_test_toolchain_include).and_return( [] )
68
70
 
71
+ @test_source_file_directive_resolver = double( "TestSourceFileDirectiveResolver" )
72
+ allow(@test_source_file_directive_resolver).to receive(:validate!)
73
+
69
74
  @mock_locator = MockLocator.new(
70
75
  configurator: @configurator, include_pathinator: @include_pathinator, file_finder: @file_finder, file_path_utils: @file_path_utils
71
76
  )
@@ -89,7 +94,8 @@ describe TestBuildSetup do
89
94
  :test_search_paths => TestSearchPaths.new(
90
95
  configurator: @configurator, include_pathinator: @include_pathinator, test_context_extractor: @test_context_extractor
91
96
  ),
92
- :mock_locator => @mock_locator
97
+ :mock_locator => @mock_locator,
98
+ :test_source_file_directive_resolver => @test_source_file_directive_resolver
93
99
  }
94
100
  )
95
101
 
@@ -119,15 +125,17 @@ describe TestBuildSetup do
119
125
  allow(@file_path_utils).to receive(:form_test_preprocess_files_directives_only_path).and_return( 'build/test/preprocess/directives/a_test' )
120
126
  allow(@file_path_utils).to receive(:form_test_preprocess_files_raw_directives_only_path).and_return( 'build/test/preprocess/raw/a_test' )
121
127
  allow(@file_path_utils).to receive(:form_test_preprocess_build_directives_path).and_return( 'build/test/preprocess/build_directives/a_test' )
128
+ allow(@file_path_utils).to receive(:form_test_name) { |filepath| filepath.to_s.ext( '' ).sub( '../', '_up_/' ) }
122
129
  allow(@file_wrapper).to receive(:mkdir)
130
+ allow(@configurator).to receive(:collection_all_tests).and_return( ['test/TestFoo.c'] )
123
131
  allow(@configurator).to receive(:project_use_mocks).and_return( false )
124
132
  allow(@configurator).to receive(:project_use_partials).and_return( false )
125
133
  allow(@configurator).to receive(:project_use_test_preprocessor).and_return( :none )
126
134
  end
127
135
 
128
- def prepare_state
136
+ def prepare_state(tests = ['test/TestFoo.c'])
129
137
  TestInvokerTypes::PipelineState.new(
130
- :tests => ['test/TestFoo.c'], :testables => {}, :context => :test, :options => [], :lock => Mutex.new
138
+ :tests => tests, :testables => {}, :context => :test, :options => [], :lock => Mutex.new
131
139
  )
132
140
  end
133
141
 
@@ -136,9 +144,53 @@ describe TestBuildSetup do
136
144
 
137
145
  @setup.stage_prepare_build_paths( state )
138
146
 
139
- testable = state.testables[:TestFoo]
147
+ testable = state.testables[:"test/TestFoo"]
140
148
  expect(testable.filepath).to eq( 'test/TestFoo.c' )
141
- expect(testable.name).to eq( 'TestFoo' )
149
+ expect(testable.name).to eq( 'test/TestFoo' )
150
+ end
151
+
152
+ # GH #1364: same-named tests at a root and below it
153
+ it "keeps same-named tests apart by their names" do
154
+ tests = ['test/test_calc.c', 'test/unit/test_calc.c']
155
+ allow(@configurator).to receive(:collection_all_tests).and_return( tests )
156
+ state = prepare_state( tests )
157
+
158
+ @setup.stage_prepare_build_paths( state )
159
+
160
+ expect(state.testables.keys).to contain_exactly( :'test/test_calc', :'test/unit/test_calc' )
161
+ expect(state.testables.values.map(&:filepath)).to contain_exactly( *tests )
162
+ end
163
+
164
+ # Two configured source extensions let one stem name two test files
165
+ it "stops a build with two test files differing only in extension, naming both" do
166
+ allow(@configurator).to receive(:collection_all_tests).and_return( ['test/test_a.c', 'test/test_a.cpp'] )
167
+
168
+ expect { @setup.stage_prepare_build_paths( prepare_state( ['test/test_a.c'] ) ) }.to raise_error( CeedlingException ) { |error|
169
+ expect(error.message).to include( 'test/test_a.c', 'test/test_a.cpp', '`test/test_a`' )
170
+ }
171
+ end
172
+
173
+ context "when two tests map to one name" do
174
+ before(:each) do
175
+ allow(@configurator).to receive(:collection_all_tests).and_return( ['../x/test_a.c', '_up_/x/test_a.c', 'test/test_b.c'] )
176
+ end
177
+
178
+ it "stops before making any directory or testable, naming both files" do
179
+ state = prepare_state( ['_up_/x/test_a.c'] )
180
+
181
+ expect(@file_wrapper).to_not receive(:mkdir)
182
+ expect { @setup.stage_prepare_build_paths( state ) }.to raise_error( CeedlingException ) { |error|
183
+ expect(error.message).to include( '../x/test_a.c', '_up_/x/test_a.c', '`_up_/x/test_a`' )
184
+ }
185
+ expect(state.testables).to be_empty
186
+ end
187
+
188
+ it "lets a build go ahead when neither test is in it" do
189
+ state = prepare_state( ['test/test_b.c'] )
190
+
191
+ expect { @setup.stage_prepare_build_paths( state ) }.to_not raise_error
192
+ expect(state.testables.keys).to eq( [:'test/test_b'] )
193
+ end
142
194
  end
143
195
 
144
196
  it "always assembles the build, results, dependencies, and runners paths" do
@@ -146,7 +198,7 @@ describe TestBuildSetup do
146
198
 
147
199
  @setup.stage_prepare_build_paths( state )
148
200
 
149
- paths = state.testables[:TestFoo].paths
201
+ paths = state.testables[:"test/TestFoo"].paths
150
202
  expect(paths[:build]).to eq( 'build/test/out/a_test' )
151
203
  expect(paths[:results]).to eq( 'build/test/results/a_test' )
152
204
  expect(paths[:dependencies]).to eq( 'build/test/dependencies/a_test' )
@@ -159,7 +211,7 @@ describe TestBuildSetup do
159
211
 
160
212
  @setup.stage_prepare_build_paths( state )
161
213
 
162
- expect(state.testables[:TestFoo].paths).to_not have_key( :mocks )
214
+ expect(state.testables[:"test/TestFoo"].paths).to_not have_key( :mocks )
163
215
  end
164
216
 
165
217
  it "assembles the mocks path when the project has mocking enabled" do
@@ -168,7 +220,7 @@ describe TestBuildSetup do
168
220
 
169
221
  @setup.stage_prepare_build_paths( state )
170
222
 
171
- expect(state.testables[:TestFoo].paths[:mocks]).to eq( 'build/test/mocks/a_test' )
223
+ expect(state.testables[:"test/TestFoo"].paths[:mocks]).to eq( 'build/test/mocks/a_test' )
172
224
  end
173
225
 
174
226
  it "omits the partials path when the project has Partials disabled" do
@@ -176,7 +228,7 @@ describe TestBuildSetup do
176
228
 
177
229
  @setup.stage_prepare_build_paths( state )
178
230
 
179
- expect(state.testables[:TestFoo].paths).to_not have_key( :partials )
231
+ expect(state.testables[:"test/TestFoo"].paths).to_not have_key( :partials )
180
232
  end
181
233
 
182
234
  it "assembles the partials path when the project has Partials enabled" do
@@ -185,7 +237,7 @@ describe TestBuildSetup do
185
237
 
186
238
  @setup.stage_prepare_build_paths( state )
187
239
 
188
- expect(state.testables[:TestFoo].paths[:partials]).to eq( 'build/test/partials/a_test' )
240
+ expect(state.testables[:"test/TestFoo"].paths[:partials]).to eq( 'build/test/partials/a_test' )
189
241
  end
190
242
 
191
243
  it "omits the preprocessing paths and scratch state when preprocessing is off" do
@@ -193,7 +245,7 @@ describe TestBuildSetup do
193
245
 
194
246
  @setup.stage_prepare_build_paths( state )
195
247
 
196
- testable = state.testables[:TestFoo]
248
+ testable = state.testables[:"test/TestFoo"]
197
249
  expect(testable.paths).to_not have_key( :preprocess_includes )
198
250
  expect(testable.preprocess).to_not have_key( :includes )
199
251
  end
@@ -204,7 +256,7 @@ describe TestBuildSetup do
204
256
 
205
257
  @setup.stage_prepare_build_paths( state )
206
258
 
207
- testable = state.testables[:TestFoo]
259
+ testable = state.testables[:"test/TestFoo"]
208
260
  expect(testable.paths[:preprocess_includes]).to eq( 'build/test/preprocess/includes/a_test' )
209
261
  expect(testable.paths[:preprocess_files]).to eq( 'build/test/preprocess/files/a_test' )
210
262
  expect(testable.paths[:preprocess_files_full_expansion]).to eq( 'build/test/preprocess/full/a_test' )
@@ -312,6 +364,38 @@ describe TestBuildSetup do
312
364
 
313
365
  @setup.stage_collect_test_context( @state )
314
366
  end
367
+
368
+ # Stage 11, which checks entries otherwise, does not run without test preprocessing
369
+ it "checks each test's TEST_SOURCE_FILE() entries once, under its own name, scanned or cached" do
370
+ expect(@test_source_file_directive_resolver).to receive(:validate!).with( test: 'a_test', test_filepath: 'test/TestFoo.c', context: :test ).twice
371
+
372
+ [true, false].each do |stale|
373
+ allow(@dependinator).to receive(:stale?).and_return( stale )
374
+ @setup.stage_collect_test_context( @state )
375
+ end
376
+ end
377
+ end
378
+
379
+ # Only a scan without test preprocessing caches TEST_SOURCE_FILE() entries, so switching
380
+ # modes must rescan rather than recall a cache missing them
381
+ it "caches build directives against the test preprocessing mode that scanned them" do
382
+ [true, false].each do |preprocessing|
383
+ allow(@configurator).to receive(:project_use_test_preprocessor_tests).and_return( preprocessing )
384
+ expect(@dependinator).to receive(:register).with(
385
+ 'build/preprocess/build_directives/a_test/Foo.c.yml', files: ['test/TestFoo.c'], meta: { source_files_scanned: !preprocessing }
386
+ )
387
+
388
+ @setup.stage_collect_test_context( @state )
389
+ end
390
+ end
391
+
392
+ it "leaves TEST_SOURCE_FILE() entries to stage 11 when test preprocessing is on" do
393
+ allow(@configurator).to receive(:project_use_test_preprocessor_tests).and_return( true )
394
+ allow(@dependinator).to receive(:stale?).and_return( true )
395
+
396
+ expect(@test_source_file_directive_resolver).to_not receive(:validate!)
397
+
398
+ @setup.stage_collect_test_context( @state )
315
399
  end
316
400
 
317
401
  it "logs one summary line stating how many test files' build directives were recalled from cache" do
@@ -124,6 +124,17 @@ describe TestContextExtractor do
124
124
  end
125
125
  end
126
126
 
127
+ # GH #1370 audit (G10): a TEST_INCLUDE_PATH() spelled another way gave its headers a
128
+ # second spelling beside the collection's own
129
+ context "#ingest_build_directive_include_paths" do
130
+ it "records each path in the one spelling every collection uses" do
131
+ @extractor.ingest_build_directive_include_paths( "path", ["./include/dir", "include\\other/", File.join( Dir.pwd, "inc" )] )
132
+
133
+ expect( @extractor.lookup_include_paths_list( "path" ) ).to eq( ["include/dir", "include/other", "inc"] )
134
+ expect( @extractor.lookup_all_include_paths ).to eq( ["include/dir", "include/other", "inc"] )
135
+ end
136
+ end
137
+
127
138
  context "#store_build_directives_cache and #load_build_directives_cache" do
128
139
  it "writes the currently known source files and include paths for a test file to the given cache filepath" do
129
140
  @extractor.ingest_build_directive_source_files( "path", ["extra.c"] )
@@ -174,6 +185,20 @@ describe TestContextExtractor do
174
185
  expect{ @extractor.collect_context( "path", StringIO.new(), :bad ) }.to raise_error( CeedlingException )
175
186
  end
176
187
 
188
+ # A #include in a branch that is off, or a `..` spelling the mock search paths reach, both
189
+ # build, so a climbing mock #include is read like any other
190
+ it "resolves a mock #include that climbs with `..` against the test file's directory" do
191
+ @extractor.collect_context( "test/test_c.c", StringIO.new( "#include \"../x/mock_foo.h\"\n" ), TestContextExtractor::Context::INCLUDES )
192
+
193
+ expect( @extractor.lookup_mock_header_includes_list( "test/test_c.c" ) ).to eq( [MockInclude.new( 'x/mock_foo.h' )] )
194
+ end
195
+
196
+ it "keeps a non-mock #include that climbs with `..`, resolved against the test file's directory" do
197
+ @extractor.collect_context( "test/test_c.c", StringIO.new( "#include \"../inc/x/foo.h\"\n" ), TestContextExtractor::Context::INCLUDES )
198
+
199
+ expect( @extractor.lookup_all_header_includes_list( "test/test_c.c" ) ).to eq( [UserInclude.new( 'inc/x/foo.h' )] )
200
+ end
201
+
177
202
  # collect_context() + lookup_all_header_includes_list() + lookup_mock_header_includes_list()
178
203
  it "should extract contents of #include directives" do
179
204
  filepath = "path/tests/test_file.c"
@@ -156,10 +156,26 @@ describe TestFilePreprocessinator do
156
156
  it "validates a test's TEST_SOURCE_FILE() entries through the resolver, regardless of staleness" do
157
157
  allow(@dependinator).to receive(:stale?).and_return( false )
158
158
 
159
- expect(@test_source_file_directive_resolver).to receive(:validate!).with( test: 'a_test', filepath: 'test/TestFoo.c' )
159
+ expect(@test_source_file_directive_resolver).to receive(:validate!).with( test: 'a_test', test_filepath: 'test/TestFoo.c', context: :test )
160
160
 
161
161
  @preprocessinator_stage.preprocess( @state )
162
162
  end
163
+
164
+ # Each test checks its own entries, not every test's
165
+ it "validates each test's entries once, under that test's own name" do
166
+ allow(@dependinator).to receive(:stale?).and_return( false )
167
+ others = %w[b_test c_test].to_h do |name|
168
+ [name.to_sym, @testable.dup.tap { |testable| testable.name = name; testable.filepath = "test/#{name}.c" }]
169
+ end
170
+ @state.testables.merge!( others )
171
+
172
+ checked = []
173
+ allow(@test_source_file_directive_resolver).to receive(:validate!) { |test:, test_filepath:, context:| checked << [test, test_filepath, context] }
174
+
175
+ @preprocessinator_stage.preprocess( @state )
176
+
177
+ expect(checked).to contain_exactly( ['a_test', 'test/TestFoo.c', :test], ['b_test', 'test/b_test.c', :test], ['c_test', 'test/c_test.c', :test] )
178
+ end
163
179
  end
164
180
 
165
181
  # The full expansion reads every header the test reaches, so a change to one of them
@@ -18,6 +18,8 @@ require 'ceedling/test_invoker/standin_headerinator'
18
18
  describe TestIncludesExtractinator do
19
19
  before(:each) do
20
20
  @configurator = double( "Configurator" )
21
+ # Ceedling always defaults it, and the mock locator reads it
22
+ allow(@configurator).to receive(:cmock_mock_name_include_folder).and_return( true )
21
23
  @loginator = double( "Loginator" )
22
24
  @reportinator = double( "Reportinator" )
23
25
  @batchinator = double( "Batchinator" )
@@ -393,8 +395,10 @@ describe TestIncludesExtractinator do
393
395
  allow(@file_wrapper).to receive(:exist?).and_return( false )
394
396
  allow(@file_wrapper).to receive(:mkdir)
395
397
  allow(@file_wrapper).to receive(:write)
396
- allow(@file_path_utils).to receive(:form_mock_header_filepath).and_return( 'build/test/mocks/a_test/MockFoo.h' )
397
398
  allow(@file_finder).to receive(:resolve_mock).and_return( ['src/Foo.h', ''] )
399
+ allow(@file_path_utils).to receive(:form_mock_output_path) { |root, subdir| subdir.to_s.empty? ? root : File.join( root, subdir ) }
400
+ # A test mocks only when its build mocks, which gives it a mock root
401
+ @testable.paths = { mocks: 'build/test/mocks/a_test' }
398
402
  end
399
403
 
400
404
  it "places a stand-in for each mock on a freshly extracted bare-includes list" do
@@ -19,10 +19,13 @@ describe 'Test pipeline object wiring' do
19
19
  test_invoker test_pipeline_manager test_build_setup test_includes_extractinator test_bare_includes_extractinator
20
20
  test_includes_preprocessinator test_includes_reconcilinator test_build_planner partials_manager
21
21
  test_build_executor test_file_preprocessinator test_runner_generatinator test_search_paths mock_locator
22
- standin_headerinator
23
- ].each do |name|
22
+ standin_headerinator test_source_file_directive_resolver
23
+ ].map { |name| [name, "ceedling/test_invoker/#{name}"] }.concat( [
24
+ ['build_log_writer', 'ceedling/build_log_writer'],
25
+ ['generator_test_results_backtrace', 'ceedling/generators/generator_test_results_backtrace']
26
+ ] ).each do |name, path|
24
27
  it "composes exactly what #{name}'s constructor takes" do
25
- require "ceedling/test_invoker/#{name}"
28
+ require path
26
29
  klass = Object.const_get( name.split( '_' ).map( &:capitalize ).join )
27
30
 
28
31
  expect(OBJECTS_WIRING.dig( name, 'compose' )).to match_array( klass.constructor_keys.map( &:to_s ) )
@@ -18,7 +18,9 @@ describe TestInvoker do
18
18
  @loginator = double( "Loginator" )
19
19
  @verbosinator = double( "Verbosinator" )
20
20
  @test_pipeline_manager = double( "TestPipelineManager" )
21
+ @include_pathinator = double( "IncludePathinator" )
21
22
 
23
+ allow(@include_pathinator).to receive(:forget_header_listings)
22
24
  allow(@plugin_manager).to receive(:pre_test_build)
23
25
  allow(@plugin_manager).to receive(:post_test_build)
24
26
  allow(@dependinator).to receive(:open)
@@ -32,7 +34,8 @@ describe TestInvoker do
32
34
  :dependinator => @dependinator,
33
35
  :loginator => @loginator,
34
36
  :verbosinator => @verbosinator,
35
- :test_pipeline_manager => @test_pipeline_manager
37
+ :test_pipeline_manager => @test_pipeline_manager,
38
+ :include_pathinator => @include_pathinator
36
39
  }
37
40
  )
38
41
  end
@@ -67,6 +70,16 @@ describe TestInvoker do
67
70
  @invoker.setup_and_invoke( tests: [] )
68
71
  end
69
72
 
73
+ # A header a `pre_test_build` hook writes is listed, and a later test build in the same
74
+ # process lists each directory afresh
75
+ it "forgets remembered header listings after the pre-test-build hook and before the run" do
76
+ expect(@plugin_manager).to receive(:pre_test_build).ordered
77
+ expect(@include_pathinator).to receive(:forget_header_listings).ordered
78
+ expect(@test_pipeline_manager).to receive(:run).ordered
79
+
80
+ @invoker.setup_and_invoke( tests: [] )
81
+ end
82
+
70
83
  # Isolates each pipeline identifier's own cache file (see Dependinator#open) --
71
84
  # sharing one cache file between, say, an ordinary test run and a plugin's own
72
85
  # context (:gcov, :valgrind, ...) would let one's full-run pruning flush (see
@@ -124,7 +124,7 @@ describe TestSourceFileDirectiveResolver do
124
124
  .with( filepath: 'foo.c', complain: :ignore, context: TEST_SYM ).and_return( 'src/foo.c' )
125
125
 
126
126
  expect {
127
- @resolver.validate!( test: 'TestFoo', filepath: 'test/TestFoo.c' )
127
+ @resolver.validate!( test: 'TestFoo', test_filepath: 'test/TestFoo.c', context: TEST_SYM )
128
128
  }.to_not raise_error
129
129
  end
130
130
 
@@ -135,7 +135,7 @@ describe TestSourceFileDirectiveResolver do
135
135
  .with( filepath: 'foo.c', complain: :ignore, context: TEST_SYM ).and_return( nil )
136
136
 
137
137
  expect {
138
- @resolver.validate!( test: 'TestFoo', filepath: 'test/TestFoo.c' )
138
+ @resolver.validate!( test: 'TestFoo', test_filepath: 'test/TestFoo.c', context: TEST_SYM )
139
139
  }.to raise_error( CeedlingException, /'foo\.c'.*cannot be found in the source file collection/ )
140
140
  end
141
141
 
@@ -144,7 +144,7 @@ describe TestSourceFileDirectiveResolver do
144
144
  .with( 'test/TestFoo.c' ).and_return( ['-:foo.txt'] )
145
145
 
146
146
  expect {
147
- @resolver.validate!( test: 'TestFoo', filepath: 'test/TestFoo.c' )
147
+ @resolver.validate!( test: 'TestFoo', test_filepath: 'test/TestFoo.c', context: TEST_SYM )
148
148
  }.to raise_error( CeedlingException, /'foo\.txt'.*not a .*\.c.*source file/ )
149
149
  end
150
150
 
@@ -155,10 +155,41 @@ describe TestSourceFileDirectiveResolver do
155
155
  .with( filepath: 'foo.c', complain: :ignore, context: TEST_SYM ).and_return( nil )
156
156
 
157
157
  expect {
158
- @resolver.validate!( test: 'TestFoo', filepath: 'test/TestFoo.c' )
158
+ @resolver.validate!( test: 'TestFoo', test_filepath: 'test/TestFoo.c', context: TEST_SYM )
159
159
  }.to raise_error( CeedlingException, /^File 'foo\.c'/ )
160
160
  end
161
161
 
162
+ # The check and the build find an entry the same way, so an entry the build uses passes
163
+ it "anchors a .. entry to the test's own directory, as the build does" do
164
+ allow(@test_context_extractor).to receive(:lookup_build_directive_sources_list)
165
+ .with( 'test/unit/TestFoo.c' ).and_return( ['../common/extra.c'] )
166
+ expect(@file_finder).to receive(:find_build_input_file)
167
+ .with( filepath: 'test/common/extra.c', complain: :ignore, context: TEST_SYM ).and_return( 'test/common/extra.c' )
168
+
169
+ expect {
170
+ @resolver.validate!( test: 'TestFoo', test_filepath: 'test/unit/TestFoo.c', context: TEST_SYM )
171
+ }.to_not raise_error
172
+ end
173
+
174
+ it "looks entries up in the build's own context" do
175
+ allow(@test_context_extractor).to receive(:lookup_build_directive_sources_list)
176
+ .with( 'test/TestFoo.c' ).and_return( ['foo.c'] )
177
+ expect(@file_finder).to receive(:find_build_input_file)
178
+ .with( filepath: 'foo.c', complain: :ignore, context: :gcov ).and_return( 'src/foo.c' )
179
+
180
+ @resolver.validate!( test: 'TestFoo', test_filepath: 'test/TestFoo.c', context: :gcov )
181
+ end
182
+
183
+ it "names the test it was given in its error" do
184
+ allow(@test_context_extractor).to receive(:lookup_build_directive_sources_list)
185
+ .with( 'test/m13/test_m13.c' ).and_return( ['mod_13_typo.c'] )
186
+ allow(@file_finder).to receive(:find_build_input_file).and_return( nil )
187
+
188
+ expect {
189
+ @resolver.validate!( test: 'test/m13/test_m13', test_filepath: 'test/m13/test_m13.c', context: TEST_SYM )
190
+ }.to raise_error( CeedlingException, %r{'mod_13_typo\.c' specified with TEST_SOURCE_FILE\(\) in test/m13/test_m13 cannot be found} )
191
+ end
192
+
162
193
  context "when assembly support is enabled" do
163
194
  before(:each) do
164
195
  allow(@configurator).to receive(:test_build_use_assembly).and_return( true )
@@ -172,7 +203,7 @@ describe TestSourceFileDirectiveResolver do
172
203
  .with( filepath: 'foo.asm', complain: :ignore, context: TEST_SYM ).and_return( 'src/foo.asm' )
173
204
 
174
205
  expect {
175
- @resolver.validate!( test: 'TestFoo', filepath: 'test/TestFoo.c' )
206
+ @resolver.validate!( test: 'TestFoo', test_filepath: 'test/TestFoo.c', context: TEST_SYM )
176
207
  }.to_not raise_error
177
208
  end
178
209
 
@@ -183,7 +214,7 @@ describe TestSourceFileDirectiveResolver do
183
214
  .with( filepath: 'foo.c', complain: :ignore, context: TEST_SYM ).and_return( 'src/foo.c' )
184
215
 
185
216
  expect {
186
- @resolver.validate!( test: 'TestFoo', filepath: 'test/TestFoo.c' )
217
+ @resolver.validate!( test: 'TestFoo', test_filepath: 'test/TestFoo.c', context: TEST_SYM )
187
218
  }.to_not raise_error
188
219
  end
189
220
 
@@ -192,7 +223,7 @@ describe TestSourceFileDirectiveResolver do
192
223
  .with( 'test/TestFoo.c' ).and_return( ['foo.txt'] )
193
224
 
194
225
  expect {
195
- @resolver.validate!( test: 'TestFoo', filepath: 'test/TestFoo.c' )
226
+ @resolver.validate!( test: 'TestFoo', test_filepath: 'test/TestFoo.c', context: TEST_SYM )
196
227
  }.to raise_error( CeedlingException, /not a \.c or \.asm source file/ )
197
228
  end
198
229
  end
@@ -81,6 +81,15 @@ describe ToolExecutor do
81
81
 
82
82
  expect(command[:line]).to include('--flag=2')
83
83
  end
84
+
85
+ # GH #1373: a value was a replacement string, so its \0, \1 and \\ were read as back-references
86
+ it 'substitutes a value holding backslash sequences exactly as given' do
87
+ tool_config = { :name => 't', :executable => 'exe', :arguments => [ { '-I"$"' => ['C:\proj\2024\0x', 'a\\\\b'] } ] }
88
+
89
+ command = @tool_executor.build_command_line( tool_config, [] )
90
+
91
+ expect(command[:line]).to eq( 'exe -I"C:\proj\2024\0x" -I"a\\\\b"' )
92
+ end
84
93
  end
85
94
 
86
95
  # A command run with :boom false always reports an :exit_code of 0, so a caller handling
@@ -453,6 +462,23 @@ describe ToolExecutor do
453
462
  expect(command[:line]).to include('-o "out.o"')
454
463
  end
455
464
 
465
+ # GH #1373: a value was a replacement string, so its \0, \1 and \\ were read as back-references
466
+ it 'substitutes a value holding backslash sequences exactly as given' do
467
+ tool_config = { :name => 't', :executable => 'exe', :arguments => ['-c "${1}"', '${2}'] }
468
+
469
+ command = @tool_executor.build_command_line(tool_config, [], 'C:\proj\2024\0x', 'a\\\\b\&c')
470
+
471
+ expect(command[:line]).to eq( 'exe -c "C:\proj\2024\0x" a\\\\b\&c' )
472
+ end
473
+
474
+ it 'substitutes each value of an Array exactly as given' do
475
+ tool_config = { :name => 't', :executable => 'exe', :arguments => ['"${1}"'] }
476
+
477
+ command = @tool_executor.build_command_line(tool_config, [], ['C:\a\1', 'C:\b\0'])
478
+
479
+ expect(command[:line]).to eq( 'exe "C:\a\1" "C:\b\0"' )
480
+ end
481
+
456
482
  it 'raises CeedlingException when args is empty but a ${n} element is present' do
457
483
  tool_config = { :name => 'my_tool', :executable => 'exe', :arguments => ['${1}'] }
458
484