ceedling 1.2.1 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. checksums.yaml +4 -4
  2. data/GIT_COMMIT_SHA +1 -1
  3. data/README.md +11 -9
  4. data/Rakefile +2 -1
  5. data/assets/features/ceedling +1 -1
  6. data/assets/fixtures/test_source_file_duplicate_basenames/test_calc_typo.c +14 -0
  7. data/assets/fixtures/tests_with_duplicate_test_names/test_foo.c +21 -0
  8. data/bin/actionator.rb +6 -12
  9. data/bin/app_cfg.rb +14 -1
  10. data/bin/ceedling +13 -3
  11. data/bin/cli.rb +25 -6
  12. data/bin/cli_handler.rb +113 -73
  13. data/bin/cli_helper.rb +51 -78
  14. data/bin/installation_selector.rb +81 -0
  15. data/bin/launchinator.rb +69 -0
  16. data/bin/mixin_resolvinator.rb +26 -36
  17. data/bin/objects.yml +13 -0
  18. data/bin/path_validator.rb +25 -29
  19. data/bin/versionator.rb +47 -48
  20. data/ceedling.gemspec +19 -15
  21. data/docs/BreakingChanges.md +70 -1
  22. data/docs/Changelog.md +74 -3
  23. data/docs/KnownIssues.md +90 -4
  24. data/docs/ReleaseNotes.md +13 -0
  25. data/lib/ceedling/build_log_writer.rb +33 -0
  26. data/lib/ceedling/config/build_root_policy.rb +70 -0
  27. data/lib/ceedling/config/configurator.rb +14 -16
  28. data/lib/ceedling/config/configurator_builder.rb +58 -28
  29. data/lib/ceedling/config/configurator_paths_validator.rb +179 -0
  30. data/lib/ceedling/config/configurator_plugins.rb +1 -1
  31. data/lib/ceedling/config/configurator_setup.rb +15 -28
  32. data/lib/ceedling/config/configurator_validator.rb +1 -110
  33. data/lib/ceedling/defaults.rb +4 -4
  34. data/lib/ceedling/dependencies/README.md +18 -16
  35. data/lib/ceedling/dependencies/dependency_cache_store.rb +20 -3
  36. data/lib/ceedling/dependencies/dependency_hasher.rb +35 -1
  37. data/lib/ceedling/filename_extension.rb +7 -1
  38. data/lib/ceedling/filesystem/FILE_TREE.md +253 -0
  39. data/lib/ceedling/filesystem/PATH_HANDLING.md +304 -0
  40. data/lib/ceedling/filesystem/README.md +41 -0
  41. data/lib/ceedling/filesystem/build_subpath.rb +102 -0
  42. data/lib/ceedling/filesystem/destination_collision.rb +25 -0
  43. data/lib/ceedling/{file_finder.rb → filesystem/file_finder.rb} +159 -104
  44. data/lib/ceedling/{file_finder_helper.rb → filesystem/file_finder_helper.rb} +36 -31
  45. data/lib/ceedling/filesystem/file_path_collection_utils.rb +183 -0
  46. data/lib/ceedling/{file_path_utils.rb → filesystem/file_path_utils.rb} +108 -97
  47. data/lib/ceedling/{file_system_wrapper.rb → filesystem/file_system_wrapper.rb} +6 -5
  48. data/lib/ceedling/filesystem/file_tree.rb +241 -0
  49. data/lib/ceedling/{file_wrapper.rb → filesystem/file_wrapper.rb} +45 -38
  50. data/lib/ceedling/filesystem/path_exclusions.rb +30 -0
  51. data/lib/ceedling/{path_matcher.rb → filesystem/path_matcher.rb} +27 -23
  52. data/lib/ceedling/{path_mirror.rb → filesystem/path_mirror.rb} +19 -18
  53. data/lib/ceedling/filesystem/path_spellings.rb +55 -0
  54. data/lib/ceedling/filesystem/project_paths.rb +184 -0
  55. data/lib/ceedling/filesystem/working_dir.rb +40 -0
  56. data/lib/ceedling/generators/generator.rb +5 -6
  57. data/lib/ceedling/generators/generator_partials.rb +1 -1
  58. data/lib/ceedling/generators/generator_test_results_backtrace.rb +7 -7
  59. data/lib/ceedling/handoff.rb +121 -0
  60. data/lib/ceedling/includes/include_factory.rb +3 -3
  61. data/lib/ceedling/includes/include_pathinator.rb +67 -10
  62. data/lib/ceedling/includes/includes.rb +1 -1
  63. data/lib/ceedling/installation.rb +92 -0
  64. data/lib/ceedling/library_resolver.rb +99 -0
  65. data/lib/ceedling/objects.yml +23 -2
  66. data/lib/ceedling/partials/partializer.rb +4 -4
  67. data/lib/ceedling/partials/partializer_config.rb +3 -1
  68. data/lib/ceedling/plugins/report_log_writer_plugin.rb +3 -5
  69. data/lib/ceedling/preprocess/preprocessinator_file_assembler.rb +1 -1
  70. data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +1 -1
  71. data/lib/ceedling/preprocess/preprocessinator_line_marker_includes_extractor.rb +1 -1
  72. data/lib/ceedling/rake_app/rakefile_component_resolver.rb +10 -17
  73. data/lib/ceedling/rakefile.rb +15 -1
  74. data/lib/ceedling/rakefiles/base/tasks_base.rake +1 -1
  75. data/lib/ceedling/rakefiles/release/rules_release.rake +4 -7
  76. data/lib/ceedling/release_invoker/release_build_executor.rb +33 -44
  77. data/lib/ceedling/release_invoker/release_build_planner.rb +14 -6
  78. data/lib/ceedling/reportinator.rb +3 -4
  79. data/lib/ceedling/setupinator.rb +94 -90
  80. data/lib/ceedling/test_context_extractor.rb +5 -1
  81. data/lib/ceedling/test_invoker/PIPELINE.md +9 -7
  82. data/lib/ceedling/test_invoker/mock_locator.rb +60 -2
  83. data/lib/ceedling/test_invoker/partials_manager.rb +12 -0
  84. data/lib/ceedling/test_invoker/standin_headerinator.rb +22 -13
  85. data/lib/ceedling/test_invoker/test_build_executor.rb +24 -40
  86. data/lib/ceedling/test_invoker/test_build_planner.rb +37 -18
  87. data/lib/ceedling/test_invoker/test_build_setup.rb +17 -12
  88. data/lib/ceedling/test_invoker/test_build_validations.rb +16 -31
  89. data/lib/ceedling/test_invoker/test_file_preprocessinator.rb +1 -1
  90. data/lib/ceedling/test_invoker/test_invoker.rb +6 -1
  91. data/lib/ceedling/test_invoker/test_pipeline_helpers.rb +1 -1
  92. data/lib/ceedling/test_invoker/test_source_file_directive_resolver.rb +40 -20
  93. data/lib/ceedling/tool_executor.rb +9 -3
  94. data/lib/ceedling/yaml_wrapper.rb +3 -2
  95. data/lib/version.rb +1 -1
  96. data/plugins/bullseye/config/defaults_bullseye.rb +1 -1
  97. data/plugins/bullseye/lib/bullseye.rb +9 -7
  98. data/plugins/cppcheck/config/defaults_cppcheck.rb +1 -1
  99. data/plugins/dependencies/config/defaults.yml +6 -6
  100. data/plugins/dependencies/dependencies.rake +4 -1
  101. data/plugins/dependencies/lib/dependencies.rb +18 -8
  102. data/plugins/dependencies/lib/dependency_spec.rb +17 -5
  103. data/plugins/fff/lib/fff.rb +45 -39
  104. data/plugins/fff/spec/fff_cmock_wrapper_spec.rb +128 -0
  105. data/plugins/gcov/config/defaults_gcov.rb +1 -1
  106. data/plugins/gcov/lib/console_reportinator.rb +4 -18
  107. data/plugins/gcov/lib/gcov.rb +9 -2
  108. data/plugins/gcov/lib/gcovr_reportinator.rb +8 -40
  109. data/plugins/gcov/lib/gcovr_thresholds.rb +74 -0
  110. data/plugins/gcov/lib/reportgenerator_reportinator.rb +1 -1
  111. data/plugins/module_generator/lib/module_generator.rb +3 -3
  112. data/plugins/report_tests_teamcity_stdout/lib/report_tests_teamcity_stdout.rb +53 -80
  113. data/plugins/sanitize/lib/sanitize_records.rb +3 -6
  114. data/plugins/valgrind/lib/valgrind.rb +14 -7
  115. data/site-local/configuration/reference/cmock.html +2 -1
  116. data/site-local/configuration/reference/files.html +9 -3
  117. data/site-local/configuration/reference/libraries.html +21 -3
  118. data/site-local/configuration/reference/paths.html +29 -6
  119. data/site-local/configuration/reference/project.html +17 -7
  120. data/site-local/configuration/reference/release-build.html +2 -0
  121. data/site-local/configuration/reference/tools.html +9 -0
  122. data/site-local/configuration/which-ceedling.html +377 -17
  123. data/site-local/development/releases.html +5 -1
  124. data/site-local/development/workflow.html +48 -43
  125. data/site-local/getting-started/builds.html +2 -0
  126. data/site-local/getting-started/command-line.html +56 -4
  127. data/site-local/plugins/dependencies.html +6 -3
  128. data/site-local/plugins/fff.html +42 -11
  129. data/site-local/plugins/gcov/gcovr.html +15 -12
  130. data/site-local/plugins/gcov/setup.html +2 -1
  131. data/site-local/plugins/report-tests-raw-output-log.html +3 -1
  132. data/site-local/plugins/report-tests-teamcity-stdout.html +6 -3
  133. data/site-local/plugins/sanitize.html +7 -6
  134. data/site-local/plugins/valgrind.html +8 -7
  135. data/site-local/project/upgrade.html +3 -0
  136. data/site-local/reference/command-line.html +9 -2
  137. data/site-local/sitemap.xml.gz +0 -0
  138. data/site-local/snapshot/plugins/bullseye/config/defaults_bullseye.rb +1 -1
  139. data/site-local/snapshot/plugins/gcov/config/defaults_gcov.rb +1 -1
  140. data/site-local/testing-guide/conventions.html +53 -0
  141. data/spec/integration/actionator_file_operations_spec.rb +91 -1
  142. data/spec/integration/compile_commands_json_db_spec.rb +1 -1
  143. data/spec/integration/configurator_validation_spec.rb +1 -1
  144. data/spec/integration/dependencies/dependency_tracker_cache_hardening_spec.rb +26 -0
  145. data/spec/integration/dependencies/dependency_tracker_core_spec.rb +22 -0
  146. data/spec/integration/launcher_handoff_spec.rb +297 -0
  147. data/spec/integration/mixin_loading_spec.rb +1 -1
  148. data/spec/integration/module_generator_spec.rb +3 -3
  149. data/spec/integration/nested_test_artifacts_spec.rb +99 -0
  150. data/spec/integration/partials_module_resolution_spec.rb +7 -4
  151. data/spec/integration/path_intake_scenarios_spec.rb +101 -0
  152. data/spec/integration/path_scenarios_spec.rb +322 -0
  153. data/spec/integration/quote_include_isolator_spec.rb +1 -1
  154. data/spec/integration/report_build_warnings_log_spec.rb +1 -1
  155. data/spec/integration/report_tests_log_factory_spec.rb +1 -1
  156. data/spec/integration/test_executable_manifest_spec.rb +1 -1
  157. data/spec/integration/test_includes_extractinator_spec.rb +2 -1
  158. data/spec/integration/test_naming_spec.rb +198 -0
  159. data/spec/integration/test_source_file_checks_spec.rb +166 -0
  160. data/spec/integration/tool_validator_spec.rb +1 -1
  161. data/spec/integration/upgrade_docs_spec.rb +84 -0
  162. data/spec/integration/vendor_copy_transient_io_retry_spec.rb +74 -5
  163. data/spec/support/file_tree_helper.rb +88 -0
  164. data/spec/support/integration/configurator_integration_helper.rb +6 -6
  165. data/spec/support/integration/dependency_tracker_integration_helper.rb +1 -1
  166. data/spec/support/integration/partials_generation_integration_helper.rb +2 -2
  167. data/spec/support/integration/path_scenario_harness.rb +173 -0
  168. data/spec/support/integration/spec_integration_helper.rb +1 -1
  169. data/spec/support/integration/test_includes_harness.rb +13 -2
  170. data/spec/support/spec_helper.rb +2 -0
  171. data/spec/support/system/spec_system_helper.rb +7 -6
  172. data/spec/support/system/system_context.rb +21 -3
  173. data/spec/support/working_dir_tracking.rb +49 -0
  174. data/spec/system/build_output_mirroring_spec.rb +2 -2
  175. data/spec/system/conditional_mock_includes_spec.rb +1 -1
  176. data/spec/system/delta_builds_spec.rb +137 -11
  177. data/spec/system/gcov_deployment_spec.rb +1 -0
  178. data/spec/system/header_include_path_validation_spec.rb +4 -4
  179. data/spec/system/launcher_handoff_spec.rb +82 -0
  180. data/spec/system/mock_include_folders_spec.rb +91 -0
  181. data/spec/system/parent_directory_reference_spec.rb +5 -3
  182. data/spec/system/path_handling_probes_spec.rb +192 -0
  183. data/spec/system/preprocessing_parameterized_tests_spec.rb +1 -1
  184. data/spec/system/rake_release_task_path_disambiguation_spec.rb +6 -6
  185. data/spec/system/rake_test_task_path_disambiguation_spec.rb +85 -0
  186. data/spec/system/special_path_characters_spec.rb +86 -0
  187. data/spec/system/support/common_test_cases.rb +23 -21
  188. data/spec/system/support/gcov_common_test_cases.rb +37 -9
  189. data/spec/system/support/sanitize_common_test_cases.rb +2 -2
  190. data/spec/system/support/valgrind_common_test_cases.rb +12 -12
  191. data/spec/system/test_pipeline_stop_points_spec.rb +4 -4
  192. data/spec/system/test_runner_system_include_spec.rb +1 -1
  193. data/spec/system/test_source_file_directive_spec.rb +34 -5
  194. data/spec/units/bin/actionator_spec.rb +14 -17
  195. data/spec/units/bin/app_cfg_spec.rb +37 -0
  196. data/spec/units/bin/cli_handler_spec.rb +282 -6
  197. data/spec/units/bin/cli_helper_spec.rb +116 -45
  198. data/spec/units/bin/installation_selector_spec.rb +114 -0
  199. data/spec/units/bin/launchinator_spec.rb +171 -0
  200. data/spec/units/bin/path_validator_spec.rb +10 -0
  201. data/spec/units/bin/versionator_spec.rb +38 -6
  202. data/spec/units/build_log_writer_spec.rb +57 -0
  203. data/spec/units/config/build_root_policy_spec.rb +61 -0
  204. data/spec/units/config/configurator_builder_spec.rb +37 -6
  205. data/spec/units/config/configurator_paths_validator_spec.rb +292 -0
  206. data/spec/units/config/configurator_setup_spec.rb +9 -65
  207. data/spec/units/config/configurator_spec.rb +27 -5
  208. data/spec/units/config/configurator_validator_spec.rb +1 -172
  209. data/spec/units/default_tools_quoting_spec.rb +83 -0
  210. data/spec/units/dependencies/dependency_cache_store_spec.rb +33 -2
  211. data/spec/units/dependencies/dependency_debug_tree_spec.rb +1 -1
  212. data/spec/units/dependencies/dependency_hasher_spec.rb +42 -1
  213. data/spec/units/dependencies/dependency_path_normalizer_spec.rb +1 -1
  214. data/spec/units/dependencies/dependency_tracker_spec.rb +3 -1
  215. data/spec/units/filename_extension_spec.rb +11 -0
  216. data/spec/units/filesystem/build_subpath_spec.rb +122 -0
  217. data/spec/units/filesystem/destination_collision_spec.rb +30 -0
  218. data/spec/units/{file_finder_helper_spec.rb → filesystem/file_finder_helper_spec.rb} +46 -1
  219. data/spec/units/{file_finder_spec.rb → filesystem/file_finder_spec.rb} +188 -22
  220. data/spec/units/{file_path_collection_utils_spec.rb → filesystem/file_path_collection_utils_spec.rb} +181 -1
  221. data/spec/units/{file_path_utils_spec.rb → filesystem/file_path_utils_spec.rb} +204 -87
  222. data/spec/units/filesystem/file_system_wrapper_spec.rb +33 -0
  223. data/spec/units/filesystem/file_tree_spec.rb +511 -0
  224. data/spec/units/filesystem/file_tree_windows_spec.rb +78 -0
  225. data/spec/units/{file_wrapper_spec.rb → filesystem/file_wrapper_spec.rb} +95 -12
  226. data/spec/units/{path_matcher_spec.rb → filesystem/path_matcher_spec.rb} +12 -1
  227. data/spec/units/{path_mirror_spec.rb → filesystem/path_mirror_spec.rb} +32 -4
  228. data/spec/units/filesystem/project_paths_spec.rb +147 -0
  229. data/spec/units/filesystem/working_dir_spec.rb +71 -0
  230. data/spec/units/generators/generator_spec.rb +12 -6
  231. data/spec/units/generators/generator_test_results_backtrace_spec.rb +50 -46
  232. data/spec/units/generators/generator_test_results_spec.rb +1 -1
  233. data/spec/units/handoff_spec.rb +205 -0
  234. data/spec/units/includes/include_factory_spec.rb +3 -3
  235. data/spec/units/includes/include_pathinator_spec.rb +46 -0
  236. data/spec/units/installation_spec.rb +228 -0
  237. data/spec/units/library_resolver_spec.rb +157 -0
  238. data/spec/units/mock_locator_spec.rb +76 -1
  239. data/spec/units/partials/partializer_config_spec.rb +7 -0
  240. data/spec/units/partials/partializer_spec.rb +1 -0
  241. data/spec/units/partials_manager_spec.rb +17 -0
  242. data/spec/units/plugins/bullseye_spec.rb +15 -2
  243. data/spec/units/plugins/console_reportinator_spec.rb +18 -20
  244. data/spec/units/plugins/dependencies_spec.rb +33 -7
  245. data/spec/units/plugins/dependency_spec_spec.rb +17 -0
  246. data/spec/units/plugins/gcov_spec.rb +28 -3
  247. data/spec/units/plugins/gcovr_reportinator_spec.rb +19 -47
  248. data/spec/units/plugins/gcovr_thresholds_spec.rb +95 -0
  249. data/spec/units/plugins/module_generator_spec.rb +5 -5
  250. data/spec/units/plugins/report_build_warnings_log_spec.rb +3 -3
  251. data/spec/units/plugins/report_log_writer_plugin_spec.rb +10 -1
  252. data/spec/units/plugins/report_tests_raw_output_log_spec.rb +13 -2
  253. data/spec/units/plugins/report_tests_teamcity_stdout_spec.rb +77 -3
  254. data/spec/units/plugins/reportgenerator_reportinator_spec.rb +1 -1
  255. data/spec/units/plugins/sanitize_records_spec.rb +3 -1
  256. data/spec/units/plugins/sanitize_spec.rb +1 -1
  257. data/spec/units/plugins/valgrind_spec.rb +23 -8
  258. data/spec/units/preprocess/preprocessinator_comment_stripper_spec.rb +1 -1
  259. data/spec/units/rake_app/rakefile_component_resolver_spec.rb +21 -0
  260. data/spec/units/release_build_executor_spec.rb +53 -14
  261. data/spec/units/release_build_planner_spec.rb +15 -3
  262. data/spec/units/reportinator_spec.rb +5 -5
  263. data/spec/units/standin_headerinator_spec.rb +31 -1
  264. data/spec/units/test_build_executor_spec.rb +65 -3
  265. data/spec/units/test_build_planner_spec.rb +104 -2
  266. data/spec/units/test_build_setup_spec.rb +96 -12
  267. data/spec/units/test_context_extractor_spec.rb +25 -0
  268. data/spec/units/test_file_preprocessinator_spec.rb +17 -1
  269. data/spec/units/test_includes_extractinator_spec.rb +5 -1
  270. data/spec/units/test_invoker/objects_wiring_spec.rb +6 -3
  271. data/spec/units/test_invoker_spec.rb +14 -1
  272. data/spec/units/test_source_file_directive_resolver_spec.rb +38 -7
  273. data/spec/units/tool_executor_spec.rb +26 -0
  274. data/spec/units/yaml_wrapper_spec.rb +11 -0
  275. metadata +109 -25
  276. data/lib/ceedling/file_path_collection_utils.rb +0 -164
@@ -129,10 +129,9 @@ class Reportinator
129
129
  # Sanitze -- ensure it's a string and strip any filename extension
130
130
  _module_name = module_name.to_s().ext('')
131
131
 
132
- # If filename is the module name, don't add the module label. A module name
133
- # mirroring its file's path below a configured root (e.g. `adc/TestFoo`) is
134
- # still the same module as far as this comparison is concerned, so only its
135
- # own last segment is weighed against filename's bare basename.
132
+ # If filename is the module name, don't add the module label. A test's name is
133
+ # its path from the project root (e.g. `test/adc/TestFoo`), so only its last
134
+ # segment is weighed against filename's bare basename.
136
135
  label = (File.basename(filename).ext('') == File.basename(_module_name)) ? '' : "#{_module_name}::"
137
136
  return generate_progress("#{operation} #{label}#{filename}")
138
137
  end
@@ -7,6 +7,29 @@
7
7
 
8
8
  class Setupinator
9
9
 
10
+ # Configurator steps that fill out the configuration from user configuration and defaults,
11
+ # in the order they run:
12
+ # - Unity and CMock values that tie vendor tool configurations together
13
+ # - The Partials configuration reference for dependency-tracker meta
14
+ # - Test runner generation settings
15
+ # - Exceptions, enabled automagically by CMock settings
16
+ # - Environment variables again, before configuration that may reference them by inline
17
+ # Ruby string expansion
18
+ # - Paths, flags, and defines expanded, then paths standardized
19
+ # - Unity helpers, included in mocks by their standardized filenames
20
+ COMPLETION_STEPS = %i[populate_unity_config populate_cmock_config populate_partials_config populate_test_runner_generation_config
21
+ populate_exceptions_config eval_environment_variables eval_paths eval_flags eval_defines standardize_paths populate_cmock_helpers].freeze
22
+
23
+ # Configurator settings taken from the command line, each by its application
24
+ # configuration key: test case filters, whether the requested tasks build tests or a
25
+ # release (which decides what build/vendor needs), and forced test re-execution
26
+ COMMAND_LINE_SETTINGS = { include_test_case: :include_test_case, exclude_test_case: :exclude_test_case,
27
+ test_build_tasks: :test_build_tasks?, release_build_tasks: :release_build_tasks?, force_test_rerun: :force_test_rerun }.freeze
28
+
29
+ # The application objects setup uses, taken from the object hash rather than a DIY
30
+ # constructor, which validates aspects of setup
31
+ COLLABORATORS = %i[configurator loginator reportinator plugin_manager plugin_reportinator test_runner_manager].freeze
32
+
10
33
  attr_reader :config_hash
11
34
 
12
35
  def setup
@@ -26,36 +49,40 @@ class Setupinator
26
49
  def ceedling=(value)
27
50
  # Capture application objects hash as instance variable
28
51
  @ceedling = value
29
-
30
- # Get our dependencies from object hash rather than DIY constructor
31
- # This is a shortcut / validates aspects of our setup
32
- @configurator = value[:configurator]
33
- @loginator = value[:loginator]
34
- @reportinator = value[:reportinator]
35
- @plugin_manager = value[:plugin_manager]
36
- @plugin_reportinator = value[:plugin_reportinator]
37
- @test_runner_manager = value[:test_runner_manager]
52
+ COLLABORATORS.each { |name| instance_variable_set( "@#{name}", value[name] ) }
38
53
  end
39
54
 
40
55
 
41
56
  # Load up all the constants and accessors our rake files, objects, & external scripts will need.
57
+ # Each numbered step's order matters.
42
58
  def do_setup( app_cfg )
43
59
  @config_hash = app_cfg[:project_config]
44
60
 
45
- ##
46
- ## 1. Miscellaneous handling and essential configuration prep
47
- ##
61
+ apply_command_line( app_cfg ) # 1.
62
+ prepare_essential_configuration() # 1.
63
+ plugins_paths_hash = handle_base_configuration( app_cfg ) # 2.
64
+ handle_plugins( plugins_paths_hash ) # 3.
65
+ assemble_defaults( plugins_paths_hash ) # 4.
66
+ complete_configuration( app_cfg ) # 5.
67
+ validate_final_configuration() # 6.
68
+
69
+ # 7. Partially flatten config + build Configurator accessors and globals
70
+ @configurator.build( app_cfg[:ceedling_lib_path], app_cfg[:logging_path], config_hash, :environment )
71
+
72
+ load_plugins() # 8.
73
+ end
74
+
48
75
 
49
- # Set special purpose test case filters (from command line)
50
- @configurator.include_test_case = app_cfg[:include_test_case]
51
- @configurator.exclude_test_case = app_cfg[:exclude_test_case]
76
+ ### Private
77
+
78
+ private
52
79
 
53
- # Whether the requested tasks build tests or a release, which decides what build/vendor needs
54
- @configurator.test_build_tasks = app_cfg[:test_build_tasks?]
55
- @configurator.release_build_tasks = app_cfg[:release_build_tasks?]
80
+ ##
81
+ ## 1. Miscellaneous handling and essential configuration prep
82
+ ##
56
83
 
57
- # Set special purpose forced test re-execution (from command line)
58
- @configurator.force_test_rerun = app_cfg[:force_test_rerun]
84
+ def apply_command_line(app_cfg)
85
+ COMMAND_LINE_SETTINGS.each { |setting, key| @configurator.public_send( "#{setting}=", app_cfg[key] ) }
59
86
 
60
87
  # Verbosity handling
61
88
  @configurator.set_verbosity()
@@ -63,7 +90,9 @@ class Setupinator
63
90
  # Logging configuration
64
91
  @loginator.set_logfile( app_cfg[:log_filepath] )
65
92
  @configurator.project_logging = @loginator.project_logging
93
+ end
66
94
 
95
+ def prepare_essential_configuration
67
96
  log_step( 'Validating configuration contains minimum required sections', heading: false )
68
97
 
69
98
  # Complain early about anything essential that's missing
@@ -72,39 +101,44 @@ class Setupinator
72
101
  # Merge any needed runtime settings into user configuration
73
102
  @configurator.merge_ceedling_runtime_config( config_hash, CEEDLING_RUNTIME_CONFIG.deep_clone )
74
103
 
75
- if config_hash[:project][:use_partials]
76
- log_step( 'Processing Partials configuration', heading: false, verbosity: Verbosity::NORMAL )
77
- # Set configuration settings derived from enabling partials
78
- @configurator.set_partials_derived_config( config_hash )
79
- end
104
+ return unless config_hash[:project][:use_partials]
80
105
 
81
- ##
82
- ## 2. Handle basic configuration
83
- ##
106
+ log_step( 'Processing Partials configuration', heading: false, verbosity: Verbosity::NORMAL )
107
+ # Set configuration settings derived from enabling partials
108
+ @configurator.set_partials_derived_config( config_hash )
109
+ end
84
110
 
111
+ ##
112
+ ## 2. Handle basic configuration
113
+ ##
114
+
115
+ def handle_base_configuration(app_cfg)
85
116
  log_step( 'Base configuration handling', heading: false )
86
117
 
87
118
  # Evaluate environment vars before plugin configurations that might reference with inline Ruby string expansion
88
119
  @configurator.eval_environment_variables( config_hash )
89
120
 
90
121
  # Standardize paths and add to Ruby load paths
91
- plugins_paths_hash = @configurator.prepare_plugins_load_paths( app_cfg[:ceedling_plugins_path], config_hash )
122
+ return @configurator.prepare_plugins_load_paths( app_cfg[:ceedling_plugins_path], config_hash )
123
+ end
92
124
 
93
- ##
94
- ## 3. Plugin Handling
95
- ##
125
+ ##
126
+ ## 3. Plugin Handling
127
+ ##
96
128
 
129
+ def handle_plugins(plugins_paths_hash)
97
130
  log_step( 'Plugin Handling' )
98
131
 
99
- # Plugin handling
100
132
  @configurator.discover_plugins( plugins_paths_hash, config_hash )
101
133
  @configurator.merge_config_plugins( config_hash )
102
134
  @configurator.populate_plugins_config( plugins_paths_hash, config_hash )
135
+ end
103
136
 
104
- ##
105
- ## 4. Collect and apply defaults to user configuration
106
- ##
137
+ ##
138
+ ## 4. Collect and apply defaults to user configuration
139
+ ##
107
140
 
141
+ def assemble_defaults(plugins_paths_hash)
108
142
  log_step( 'Assembling Default Settings' )
109
143
 
110
144
  # Assemble defaults
@@ -115,37 +149,20 @@ class Setupinator
115
149
 
116
150
  # Set any missing essential or plugin values in configuration with assembled default values
117
151
  @configurator.populate_with_defaults( config_hash, defaults_hash )
152
+ end
118
153
 
119
- ##
120
- ## 5. Fill out / modify remaining configuration from user configuration + defaults
121
- ##
154
+ ##
155
+ ## 5. Fill out / modify remaining configuration from user configuration + defaults
156
+ ##
122
157
 
158
+ def complete_configuration(app_cfg)
123
159
  log_step( 'Completing Project Configuration' )
124
160
 
125
- # Populate Unity configuration with values to tie vendor tool configurations together
126
- @configurator.populate_unity_config( config_hash )
127
-
128
- # Populate CMock configuration with values to tie vendor tool configurations together
129
- @configurator.populate_cmock_config( config_hash )
130
-
131
- # Populate Partials configuration reference for dependency-tracker meta
132
- @configurator.populate_partials_config( config_hash )
133
-
134
- # Configure test runner generation
135
- @configurator.populate_test_runner_generation_config( config_hash )
136
-
137
- # Automagically enable use of exceptions based on CMock settings
138
- @configurator.populate_exceptions_config( config_hash )
139
-
140
- # Evaluate environment vars again before subsequent configurations that might reference with inline Ruby string expansion
141
- @configurator.eval_environment_variables( config_hash )
142
-
143
- # Standardize values and expand inline Ruby string substitutions
144
- @configurator.eval_paths( config_hash )
145
- @configurator.eval_flags( config_hash )
146
- @configurator.eval_defines( config_hash )
147
- @configurator.standardize_paths( config_hash )
161
+ COMPLETION_STEPS.each { |step| @configurator.public_send( step, config_hash ) }
162
+ complete_tools_and_runners( app_cfg )
163
+ end
148
164
 
165
+ def complete_tools_and_runners(app_cfg)
149
166
  # Fill out any missing tool config value
150
167
  @configurator.populate_tools_config( config_hash )
151
168
 
@@ -163,57 +180,44 @@ class Setupinator
163
180
  # Configure test runner build & runtime options
164
181
  @test_runner_manager.configure_build_options( config_hash )
165
182
  @test_runner_manager.configure_runtime_options( app_cfg[:include_test_case], app_cfg[:exclude_test_case] )
183
+ end
166
184
 
167
- ##
168
- ## 6. Validate configuration
169
- ##
185
+ ##
186
+ ## 6. Validate configuration
187
+ ##
170
188
 
189
+ def validate_final_configuration
171
190
  log_step( 'Validating final project configuration', heading: false )
172
191
 
173
192
  @configurator.validate_final( config_hash )
193
+ end
174
194
 
175
- ##
176
- ## 7. Flatten configuration + process it into globals and accessors
177
- ##
178
-
179
- # Skip logging this step as the end user doesn't care about this internal preparation
180
-
181
- # Partially flatten config + build Configurator accessors and globals
182
- @configurator.build( app_cfg[:ceedling_lib_path], app_cfg[:logging_path], config_hash, :environment )
183
-
184
- ##
185
- ## 8. Final plugins handling
186
- ##
195
+ ##
196
+ ## 8. Final plugins handling
197
+ ##
187
198
 
199
+ def load_plugins
188
200
  # Detailed logging already happend for plugin processing
189
201
  log_step( 'Loading Plugins' )
190
202
 
191
203
  @configurator.insert_rake_plugins( @configurator.rake_plugins )
192
-
204
+
193
205
  # Merge in any environment variables that plugins specify after the main build
194
206
  @plugin_manager.load_programmatic_plugins( @configurator.programmatic_plugins, @ceedling ) do |env|
195
207
  # Evaluate environment vars that plugins may have added
196
208
  @configurator.eval_environment_variables( env )
197
209
  @configurator.build_supplement( config_hash, env )
198
210
  end
199
-
211
+
200
212
  # Inject dependencies for plugin needs
201
213
  @plugin_reportinator.set_system_objects( @ceedling )
202
214
  end
203
215
 
204
-
205
- ### Private
206
-
207
- private
208
-
209
216
  # Neaten up a build step with progress message and some scope encapsulation
210
217
  def log_step(msg, heading: true, verbosity: Verbosity::OBNOXIOUS )
211
- @loginator.lazy( verbosity ) do
212
- if heading
213
- @reportinator.generate_heading( @loginator.decorate( msg, LogLabels::CONSTRUCT ) )
214
- else # Progress message
215
- "\n" + @reportinator.generate_progress( @loginator.decorate( msg, LogLabels::CONSTRUCT ) )
216
- end
218
+ @loginator.lazy( verbosity ) do
219
+ decorated = @loginator.decorate( msg, LogLabels::CONSTRUCT )
220
+ heading ? @reportinator.generate_heading( decorated ) : "\n" + @reportinator.generate_progress( decorated )
217
221
  end
218
222
  end
219
223
 
@@ -7,9 +7,10 @@
7
7
 
8
8
  require 'ceedling/exceptions'
9
9
  require 'ceedling/partials/partials'
10
- require 'ceedling/file_path_utils'
10
+ require 'ceedling/filesystem/file_path_utils'
11
11
  require 'ceedling/generators/generator_test_runner' # From lib/ not vendor/unity/auto
12
12
  require 'ceedling/encodinator'
13
+ require 'ceedling/filesystem/project_paths'
13
14
 
14
15
  class TestContextExtractor
15
16
 
@@ -304,9 +305,12 @@ class TestContextExtractor
304
305
  end
305
306
  end
306
307
 
308
+ # Each path is recorded in the one spelling collections use, so its headers are never
309
+ # collected a second time under another
307
310
  def ingest_build_directive_include_paths(filepath, include_paths)
308
311
  return if include_paths.empty?
309
312
 
313
+ include_paths = include_paths.map { |path| ProjectPaths.canonical( path ) }
310
314
  key = form_file_key( filepath )
311
315
 
312
316
  @lock.synchronize do
@@ -23,14 +23,14 @@ The seven stage classes never call each other. `TestPipelineManager` alone seque
23
23
  Three collaborators hold logic more than one stage needs, so the stages cannot drift apart:
24
24
 
25
25
  - **`TestSearchPaths`** composes a test's search paths in the order the compiler searches them, and sets a test's mock search paths (stages 2, 3, and 4).
26
- - **`MockLocator`** resolves a mock's real header and the directory mirroring it below the test's mock root (stages 2, 4, and 5).
26
+ - **`MockLocator`** resolves a mock's real header and places the mock: its directory below the test's mock root, the folder and root CMock writes it by, and the directory its `#include` as written reaches it from (stages 2, 4, 5, and 10).
27
27
  - **`StandinHeaderinator`** places stand-in headers for mocks and Partials (stage 4).
28
28
 
29
29
  ## `PipelineState` and `Testable`
30
30
 
31
31
  `PipelineState` exists once for the whole run, and a `Testable` exists once per test file. Between them they hold everything a stage reads or writes:
32
32
 
33
- - **Identity**: a `Testable`'s `filepath` and `name`, set in the first stage and never changed.
33
+ - **Identity**: a `Testable`'s `filepath` and `name`, set in the first stage and never changed. The name is the test's path from the project root without its extension (`FilePathUtils#form_test_name`), unique in every build, and it keys `PipelineState#testables` and places every per-test directory. A path outside the project writes each `..` as `_up_`, so no name reaches outside the build directory. `FileFinder` maps a name back to its test file exactly, never by search.
34
34
  - **Paths and preprocessing scratch state**: where this test's build output, mocks, Partials, and preprocessed files live, and the bookkeeping preprocessing needs along the way. These stay plain Hashes because their keys are sparse and conditional. A project with mocking disabled has no `:mocks` path at all, which a Hash tolerates better than a struct field with no meaningful default.
35
35
  - **Flags, defines, and search paths**: the compiler, linker, and preprocessor arguments resolved once per test and reused by every stage that runs a tool. `mock_search_paths` holds the part of `search_paths` that points at this test's mirrored mock directories (see [Mock search paths](#mock-search-paths)), so a stage can take them back out when it needs the ordering without them.
36
36
  - **Generated-content records**: `runner` (a `RunnerInfo`), each resolved mock in `mocks` (a `MockDetails`), and `partials` (a `TestablePartials`, holding a config map and the two lists of module names Partials generation has produced so far). These have a fixed shape every instance shares, which is what a struct is for: a mistyped field name fails loudly instead of silently doing nothing.
@@ -55,8 +55,8 @@ Stages 2 and 3 run before stage 4, so anything they derive from the `#include`s
55
55
 
56
56
  **Context collection (stages 1-4)** finds out what a test needs, without generating or compiling anything.
57
57
 
58
- - **Stage 1** creates each test's build directories and its `Testable`.
59
- - **Stage 2** scans the test file as text: its `#include`s, its include path build directives (cached between runs), and its Partials configuration. With test preprocessing off, it also scans `TEST_SOURCE_FILE()` directives and test case names, since nothing later will. It checks that the test uses mocks or Partials only where the project enables them, and it collects the test's first mock search paths from the text-scan list.
58
+ - **Stage 1** names each test, creates its build directories, and creates its `Testable`. It first stops a build that includes a test whose name another test maps onto, which only two paths differing in extension alone, or a path outside the project mapping onto a real `_up_` or `_abs_` directory, can cause.
59
+ - **Stage 2** scans the test file as text: its `#include`s, its include path build directives (cached between runs), and its Partials configuration. With test preprocessing off, it also scans `TEST_SOURCE_FILE()` directives and test case names, since nothing later will, and checks this test's `TEST_SOURCE_FILE()` entries. It checks that the test uses mocks or Partials only where the project enables them, and it collects the test's first mock search paths from the text-scan list.
60
60
  - **Stage 3** resolves the flags, defines, and search paths every later tool call for this test uses.
61
61
  - **Stage 4** runs only with test preprocessing on. It extracts the test file's `#include`s through the real preprocessor in three passes, described below, ends by replacing the text-scan list with the reconciled one, and narrows the mock search paths to the mocks that remain.
62
62
 
@@ -66,9 +66,9 @@ Stages 2 and 3 run before stage 4, so anything they derive from the `#include`s
66
66
 
67
67
  **Mocks (stages 9-10)** run only when the project uses mocks and this run has any. A transform flattens every test's planned mocks into one list, as the Partials transform does. Stage 9, with mock preprocessing on (`:mocks` or `:all`), preprocesses each header to be mocked. Stage 10 generates each mock's source and header with CMock, into a directory that mirrors its real header's location below the test's mock root. The `:mocking` stop point ends the run here.
68
68
 
69
- Two same-named headers mocked by directory stay distinct through `:cmock` ↳ `:mock_name_include_folder`, on by default. CMock names a mock's include guard and lifecycle functions by the folder its `#include` names, and the runner names its calls by the same `#include`, so the two always agree. `TestBuildExecutor#mock_folder_and_path` hands CMock that folder. When the folder ends the mirrored directory, the mock stays there. An `#include` spelling more path than the mirror holds puts its mock at the written path below the test's mock root instead, which the root search path still reaches.
69
+ Two same-named headers mocked by directory stay distinct through `:cmock` ↳ `:mock_name_include_folder`, on by default. CMock names a mock's include guard and lifecycle functions by the folder its `#include` names, and the runner names its calls by the same `#include`, so the two always agree. `MockLocator#placement` decides where each mock goes, and generation, the planner's mock sources, the stand-ins, and the mock search paths all use it. When the folder ends the header's mirrored subdirectory, the mock stays in the mirrored directory. An `#include` spelling more path than the mirror holds puts its mock at the written path below the test's mock root instead, and one climbing with `..` stays below the mock root with an `_up_` directory for each `..`. The directory the `#include` as written reaches the mock from joins the mock search paths.
70
70
 
71
- **Test file preprocessing and runners (stages 11-13)** work on the test file itself. Stages 11 and 12 run only with test preprocessing on. Stage 11 preprocesses the test file and extracts its `TEST_SOURCE_FILE()` directives from the result. Stage 12 parses test case names from that output. Stage 13 always runs and generates the Unity test runner, from the preprocessed test file when there is one and the original otherwise. The `:test_runner` stop point ends the run here.
71
+ **Test file preprocessing and runners (stages 11-13)** work on the test file itself. Stages 11 and 12 run only with test preprocessing on. Stage 11 preprocesses the test file, extracts its `TEST_SOURCE_FILE()` directives from the result, and checks them. Each test checks only its own entries, once, under its own name. Stage 12 parses test case names from that output. Stage 13 always runs and generates the Unity test runner, from the preprocessed test file when there is one and the original otherwise. The `:test_runner` stop point ends the run here.
72
72
 
73
73
  Each of these stages is skipped when nothing its output is built from has changed, which takes in more than the test file. Stage 11's full expansion reads every header the test reaches, so the project headers named in its line markers are among stage 11's dependencies, as they are for stage 4's second pass. Editing the macro in a header that enables a test case therefore reruns stage 11. When stage 11 is skipped, its `TEST_SOURCE_FILE()` directives are recalled from a cache. Stages 12 and 13 check one runner target. It depends on the test file, the file test case names are parsed from, the runner and Unity configuration, and the test's mock and include lists. With test preprocessing, those lists come from stage 4's reconciliation, so a header that changes which mocks a test has regenerates its runner although the test file is unchanged.
74
74
 
@@ -110,7 +110,7 @@ Pass 1 places stand-ins whether its list was just extracted or recalled from cac
110
110
 
111
111
  ### Mock search paths
112
112
 
113
- A mock is generated into a directory mirroring its real header's location, and the compiler finds it only if that directory is on the search path. C's `#include` resolution has no recursive search path. So each test's search paths carry one mirrored directory per mock, after the flat mock root, and `mock_search_paths` records which entries those are.
113
+ A mock is generated into a directory mirroring its real header's location, and the compiler finds it only if that directory is on the search path. C's `#include` resolution has no recursive search path. So each test's search paths carry each mock's directory, and the directory its `#include` reaches it from when that differs, after the flat mock root. `mock_search_paths` records which entries those are.
114
114
 
115
115
  Where the paths come from follows which `#include` list drives the build:
116
116
 
@@ -120,6 +120,8 @@ Where the paths come from follows which `#include` list drives the build:
120
120
 
121
121
  Every stage that resolves a mock's header does so through `MockLocator`, against the test's search paths minus its `mock_search_paths`, so stages 2, 4, and 5 resolve each mock identically.
122
122
 
123
+ Resolving needs every header on those search paths, in search order. `IncludePathinator#ordered_header_files` builds that list from each search path's own listing. A directory outside the build root keeps its listing for the whole test build: the build never writes there, and every stage asks for the same directories for every test. A directory under the build root, such as a mock or Partials directory, is listed on every call, since the build fills it as it runs. Stage 5 builds the list once per test and shares it across the test's mocks. `TestInvoker` forgets the listings at the start of each test build, after the `pre_test_build` hook.
124
+
123
125
  ## Stop points
124
126
 
125
127
  A run can stop early by including one of four mutually exclusive Symbols in `PipelineState#options`. Each names a different place to stop, so supplying more than one is rejected.
@@ -6,6 +6,8 @@
6
6
  # =========================================================================
7
7
 
8
8
  require 'ceedling/constants'
9
+ require 'ceedling/filesystem/build_subpath'
10
+ require 'ceedling/filesystem/path_matcher'
9
11
  require 'ceedling/test_invoker/test_pipeline_helpers'
10
12
 
11
13
  # Where a test's mocks come from and where they go. A mock's real header resolves
@@ -16,6 +18,11 @@ class MockLocator
16
18
 
17
19
  include TestPipelineHelpers
18
20
 
21
+ # Where a mock is written (`directory`), the `folder` and `root` CMock writes it by (CMock
22
+ # writes below its root at the folder), and the directory the #include as written reaches
23
+ # the mock from (`reach`), which a search path must name
24
+ MockPlacement = Struct.new( :folder, :directory, :root, :reach, keyword_init: true )
25
+
19
26
  constructor :configurator, :include_pathinator, :file_finder, :file_path_utils
20
27
 
21
28
  # The headers a test's mocks resolve against: those on its search paths, in search
@@ -40,10 +47,61 @@ class MockLocator
40
47
  source, subdir = locate( include, collection: collection, complain: complain )
41
48
  next if source.nil?
42
49
 
43
- @file_path_utils.form_mock_output_path( mocks_root, subdir )
50
+ search_directories( placement( mocks_root: mocks_root, subdir: subdir, include_path: include.filepath ) )
51
+ end
52
+
53
+ return dirs.flatten.uniq
54
+ end
55
+
56
+ # A mock's directory, and the directory its #include as written reaches it from
57
+ def search_directories(placement)
58
+ return [placement.directory, placement.reach].uniq
59
+ end
60
+
61
+ # Where a mock of the header mirrored at `subdir` is written, for the #include that
62
+ # names it as `include_path`.
63
+ #
64
+ # CMock names a mock's guard and lifecycle functions by the folder its #include names, and
65
+ # the runner names its calls by the same #include, so the folder reaches CMock as written.
66
+ # A folder that ends the mirrored subdirectory keeps that directory. One spelling more
67
+ # path than the mirror holds places the mock at the written path below the mock root. One
68
+ # that climbs stays below the mock root too: each `..` it begins with becomes an `_up_`
69
+ # directory, and the #include reaches the mock from that many levels deeper still.
70
+ # Without :mock_name_include_folder no folder is passed, and CMock names by filename
71
+ # alone, writing into the mock's directory itself.
72
+ def placement(mocks_root:, subdir:, include_path:)
73
+ written = written_folder( include_path )
74
+ spot = written.nil? ? mirrored_spot( mocks_root, subdir ) : written_spot( mocks_root, subdir.to_s, written )
75
+ folder = @configurator.cmock_mock_name_include_folder ? written : nil
76
+
77
+ return MockPlacement.new( folder: folder, directory: spot[:directory], root: (folder ? spot[:reach] : spot[:directory]), reach: spot[:reach] )
78
+ end
79
+
80
+ private
81
+
82
+ # The folder of a mock's #include as written, without a leading ./, or nil for none
83
+ def written_folder(include_path)
84
+ folder = File.dirname( include_path.to_s ).sub( %r{\A(?:\./)+}, '' )
85
+ return (folder.empty? || folder == '.') ? nil : folder
86
+ end
87
+
88
+ def mirrored_spot(mocks_root, subdir)
89
+ directory = @file_path_utils.form_mock_output_path( mocks_root, subdir )
90
+ return { directory: directory, reach: directory }
91
+ end
92
+
93
+ # The mock's directory for a #include written with `written`, and the directory that
94
+ # folder leads down or climbs from to it
95
+ def written_spot(mocks_root, subdir, written)
96
+ if subdir == written or subdir.end_with?( "/#{written}" )
97
+ return { directory: File.join( mocks_root, subdir ), reach: File.join( mocks_root, subdir.delete_suffix( written ) ).chomp( '/' ) }
44
98
  end
45
99
 
46
- return dirs.uniq
100
+ segments = PathMatcher.resolve_relative( written, anchor: '' ).split( '/' )
101
+ climbs = segments.take_while { |segment| segment == '..' }.length
102
+ ups = [BuildSubpath::PARENT_SEGMENT] * climbs
103
+
104
+ return { directory: File.join( mocks_root, *ups, *segments.drop( climbs ) ), reach: File.join( mocks_root, *ups, *ups ) }
47
105
  end
48
106
 
49
107
  end
@@ -269,6 +269,8 @@ class PartialsManager
269
269
  end
270
270
  end
271
271
 
272
+ order_partials( partials.map { |partial| partial[:testable] }.uniq )
273
+
272
274
  log_skip_summary( task: "Partial types generation", count: skipped_types, noun: "types headers" )
273
275
  log_skip_summary( task: "Partial implementation generation", count: skipped_impl, noun: "implementations" )
274
276
  log_skip_summary( task: "Partial interface generation", count: skipped_interface, noun: "interfaces" )
@@ -296,6 +298,16 @@ class PartialsManager
296
298
  # and `config.includes` is recalled from the on-disk list a prior stale run wrote
297
299
  # -- so `config` ends up populated identically either way, and stage 8 (which
298
300
  # reads only `config`) needs no knowledge of which path produced it.
301
+ # Build threads finish in any order. Each test's Partial modules return to its configuration
302
+ # order, which its link and mock lists follow, so an unchanged build sees unchanged lists.
303
+ def order_partials(testables)
304
+ testables.each do |testable|
305
+ order = testable.partials.configs.values.map( &:module )
306
+ testable.partials.tests.sort_by! { |_module| order.index( _module ) }
307
+ testable.partials.mocks.sort_by! { |_module| order.index( _module ) }
308
+ end
309
+ end
310
+
299
311
  def preprocess_partials(items:, kind:, noun:, preserve_macros_method:, expand_macros_method:)
300
312
  directives_only = @configurator.test_build_preprocess_directives_only_available
301
313
  skipped = 0
@@ -8,6 +8,7 @@
8
8
  require 'ceedling/constants'
9
9
  require 'ceedling/includes/includes'
10
10
  require 'ceedling/test_invoker/test_pipeline_helpers'
11
+ require 'ceedling/filesystem/project_paths'
11
12
 
12
13
  # Stand-in headers: files placed where a test's mocks and Partials will be generated, so
13
14
  # the preprocessor finds something at each of their #includes before stage 8 or stage 10
@@ -46,7 +47,7 @@ class StandinHeaderinator
46
47
  mock_dirs = place_mocks( testable, mocks, complain )
47
48
  place_partials( testable, Includes.filter( includes, /^#{PARTIAL_FILENAME_PREFIX}/ ) )
48
49
 
49
- return mock_dirs.uniq
50
+ return mock_dirs.flatten.uniq
50
51
  end
51
52
 
52
53
  private
@@ -67,18 +68,25 @@ class StandinHeaderinator
67
68
  end
68
69
  end
69
70
 
70
- # Gives the mock's mirrored directory, or nil when its missing header is tolerated or
71
- # the test has no mock root
71
+ # Gives the search paths that reach the mock, or nil when its missing header is tolerated
72
+ # or the test has no mock root
72
73
  def place_mock(testable, include, collection, complain)
74
+ return nil if testable.paths[:mocks].nil?
75
+
73
76
  source, subdir = @mock_locator.locate( include, collection: collection, complain: complain )
74
77
  return nil if source.nil?
75
78
 
76
- mock_dir = subdir.empty? ? testable.name : File.join( testable.name, subdir )
77
- write_mock( testable.name, @file_path_utils.form_mock_header_filepath( mock_dir, include.filename ), include, source )
79
+ placement = @mock_locator.placement( mocks_root: testable.paths[:mocks], subdir: subdir, include_path: include.filepath )
80
+ write_placed_mock( testable.name, placement, include, source )
78
81
 
79
- return nil if testable.paths[:mocks].nil?
82
+ return @mock_locator.search_directories( placement )
83
+ end
80
84
 
81
- return @file_path_utils.form_mock_output_path( testable.paths[:mocks], subdir )
85
+ # The test's #include reaches a climbing folder's mock only from the directory it climbs
86
+ # from, so that is made too
87
+ def write_placed_mock(test, placement, include, source)
88
+ @file_wrapper.mkdir( placement.reach ) unless ProjectPaths.contains?( placement.reach, placement.directory )
89
+ write_mock( test, File.join( placement.directory, include.filename.ext( EXTENSION_CORE_HEADER ) ), include, source, placement.folder )
82
90
  end
83
91
 
84
92
  # A Partial's generated #include names whatever directory its module was named by, and
@@ -97,8 +105,8 @@ class StandinHeaderinator
97
105
  @file_wrapper.write_blank_file( filepath )
98
106
  end
99
107
 
100
- def write_mock(test, filepath, include, header)
101
- contents = "#{MARKER}\n#{header_include( header )}\n"
108
+ def write_mock(test, filepath, include, header, folder)
109
+ contents = "#{MARKER}\n#{header_include( header, folder )}\n"
102
110
  return if current?( filepath, contents )
103
111
 
104
112
  log_placement( test, include )
@@ -106,11 +114,12 @@ class StandinHeaderinator
106
114
  @file_wrapper.write( filepath, contents )
107
115
  end
108
116
 
109
- # The line CMock opens a mock with: its :orig_header_include_fmt around the header's name.
110
- # Ceedling hands CMock no folder, so the name is the header's own.
111
- def header_include( header )
117
+ # The line CMock opens a mock with: its :orig_header_include_fmt around the header's name,
118
+ # below the folder CMock is given, if any
119
+ def header_include( header, folder )
112
120
  format = @configurator.get_cmock_config[:orig_header_include_fmt] || '#include "%s"'
113
- return format.gsub( '%s', File.basename( header ) )
121
+ name = folder.nil? ? File.basename( header ) : File.join( folder, File.basename( header ) )
122
+ return format.gsub( '%s', name )
114
123
  end
115
124
 
116
125
  # A file that is not a stand-in, such as a real mock, counts as current. Only its first