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
@@ -20,6 +20,7 @@ $: << File.join(here, '../../../lib')
20
20
  # real bin/ceedling bootstrap always loads ceedling/constants first, so this load
21
21
  # order only ever bites a spec harness requiring cli_handler.rb directly.
22
22
  require 'ceedling/constants'
23
+ require 'version' # The launcher's own version, which #version reports for this repository
23
24
  require 'cli_handler'
24
25
  require 'app_cfg'
25
26
  require 'path_validator'
@@ -40,16 +41,22 @@ require 'ceedling/rake_app/rake_task_registry'
40
41
  # exercising the real filtering/standardizing behavior it coordinates).
41
42
  describe CliHandler do
42
43
  before(:each) do
43
- @loginator = double('loginator').as_null_object
44
- @helper = double('cli_helper').as_null_object
44
+ @loginator = double('loginator').as_null_object
45
+ @helper = double('cli_helper').as_null_object
46
+ @composinator = double('composinator').as_null_object
47
+ @projectinator = double('projectinator').as_null_object
48
+ @path_validator = double('path_validator').as_null_object
49
+ @actionator = double('actionator').as_null_object
50
+ @launchinator = double('launchinator').as_null_object
45
51
 
46
52
  @cli_handler = described_class.new({
47
- :composinator => double('composinator').as_null_object,
48
- :projectinator => double('projectinator').as_null_object,
53
+ :composinator => @composinator,
54
+ :projectinator => @projectinator,
49
55
  :cli_helper => @helper,
50
- :path_validator => double('path_validator').as_null_object,
56
+ :path_validator => @path_validator,
51
57
  :rake_task_registry => double('rake_task_registry').as_null_object,
52
- :actionator => double('actionator').as_null_object,
58
+ :actionator => @actionator,
59
+ :launchinator => @launchinator,
53
60
  :loginator => @loginator,
54
61
  })
55
62
  end
@@ -77,6 +84,9 @@ describe CliHandler do
77
84
  describe '#version' do
78
85
  before(:each) do
79
86
  @app_cfg = CeedlingAppConfig.new
87
+ # No project file unless an example says otherwise
88
+ allow(@projectinator).to receive(:config_available?).and_return( false )
89
+ allow(@path_validator).to receive(:standardize_paths) { |*paths| paths }
80
90
  end
81
91
 
82
92
  def fake_application_version(install_path)
@@ -116,6 +126,271 @@ describe CliHandler do
116
126
 
117
127
  @cli_handler.version( {}, @app_cfg )
118
128
  end
129
+
130
+ # GH #1365: `version` reported the launcher for a project selecting another installation
131
+ context 'with a project' do
132
+ before(:each) do
133
+ allow(@helper).to receive(:manufacture_app_version).and_return( fake_application_version( '/elsewhere' ) )
134
+ allow(@helper).to receive(:process_mixin_filepaths) { |mixins| mixins }
135
+ end
136
+
137
+ it 'resolves which Ceedling from an explicit --project and its mixins, loading silently' do
138
+ config = {:project => {:which_ceedling => '/elsewhere'}}
139
+ expect(@composinator).to receive(:loadinate).with(
140
+ hash_including( filepath: 'other/project.yml', mixins: ['ci.yml'], silent: true )
141
+ ).and_return( ['other/project.yml', config] )
142
+ expect(@launchinator).to receive(:resolve).with( env: {}, app_cfg: @app_cfg, config: config )
143
+
144
+ @cli_handler.version( {}, @app_cfg, {:project => 'other/project.yml', :mixin => ['ci.yml']} )
145
+ end
146
+
147
+ it 'raises when an explicit --project cannot be loaded' do
148
+ allow(@composinator).to receive(:loadinate).and_raise( 'Could not find project filepath' )
149
+
150
+ expect {
151
+ @cli_handler.version( {}, @app_cfg, {:project => 'missing.yml', :mixin => []} )
152
+ }.to raise_error( /Could not find project filepath/ )
153
+ end
154
+
155
+ it 'raises when an explicit --mixin cannot be loaded with the default project file' do
156
+ allow(@composinator).to receive(:loadinate).and_raise( 'Command line failed validation' )
157
+
158
+ expect {
159
+ @cli_handler.version( {}, @app_cfg, {:project => nil, :mixin => ['missing.yml']} )
160
+ }.to raise_error( /Command line failed validation/ )
161
+ end
162
+
163
+ it 'reads the default project file when one is available' do
164
+ config = {:project => {:which_ceedling => 'vendor/ceedling'}}
165
+ allow(@projectinator).to receive(:config_available?).and_return( true )
166
+ allow(@composinator).to receive(:loadinate).and_return( ['project.yml', config] )
167
+ expect(@launchinator).to receive(:resolve).with( env: {}, app_cfg: @app_cfg, config: config )
168
+
169
+ @cli_handler.version( {}, @app_cfg )
170
+ end
171
+
172
+ it 'falls back to resolving without a project when the default project file cannot be loaded' do
173
+ allow(@projectinator).to receive(:config_available?).and_return( true )
174
+ allow(@composinator).to receive(:loadinate).and_raise( 'Project configuration file section :mixins failed validation' )
175
+ expect(@launchinator).to receive(:resolve).with( env: {}, app_cfg: @app_cfg, config: {} )
176
+
177
+ expect { @cli_handler.version( {}, @app_cfg ) }.to_not raise_error
178
+ end
179
+
180
+ it 'resolves without a project when no project file is available' do
181
+ expect(@composinator).to_not receive(:loadinate)
182
+ expect(@launchinator).to receive(:resolve).with( env: {}, app_cfg: @app_cfg, config: {} )
183
+
184
+ @cli_handler.version( {}, @app_cfg )
185
+ end
186
+
187
+ it 'never hands off, since it reports on both installations' do
188
+ @cli_handler.version( {}, @app_cfg )
189
+
190
+ expect(@launchinator).to_not have_received(:launch)
191
+ end
192
+
193
+ it 'reports the launcher from where it runs, even after resolution moves the application paths' do
194
+ allow(@launchinator).to receive(:resolve) { |app_cfg:, **| app_cfg.set_paths( '/elsewhere' ) }
195
+
196
+ expect(@loginator).to receive(:console) do |message, _label|
197
+ expect(message).to include( "Ceedling Launcher => #{Ceedling::Version::GEM}" )
198
+ expect(message).to include( "#{@app_cfg[:launcher_root_path]}/" )
199
+ end
200
+
201
+ @cli_handler.version( {}, @app_cfg )
202
+ end
203
+ end
204
+ end
205
+
206
+ # GH #1365: every command but `version` runs in the installation it selects. An exec
207
+ # never returns, so a raise stands in for it: nothing after the hand-off may run.
208
+ describe 'hand-off' do
209
+ HandedOff = Class.new( StandardError )
210
+
211
+ before(:each) do
212
+ @app_cfg = CeedlingAppConfig.new
213
+ @config = {:project => {:which_ceedling => 'vendor/ceedling'}}
214
+ allow(@path_validator).to receive(:standardize_paths) { |*paths| paths }
215
+ allow(@helper).to receive(:process_mixin_filepaths) { |mixins| mixins }
216
+ allow(@composinator).to receive(:loadinate).and_return( ['project.yml', @config] )
217
+ end
218
+
219
+ def expect_no_work_done
220
+ [:log_project_name, :console_project_name, :load_ceedling, :run_rake_tasks, :dump_yaml,
221
+ :project_exists?, :vendor_tools, :copy_docs, :lookup_example_projects, :help_footer].each do |work|
222
+ expect(@helper).to_not have_received( work )
223
+ end
224
+ expect(@actionator).to_not have_received(:make_directory)
225
+ expect(@actionator).to_not have_received(:remove_directory)
226
+ expect(@loginator).to_not have_received(:console)
227
+ end
228
+
229
+ {
230
+ 'build' => ->(h, cfg) { h.build( env: {}, app_cfg: cfg, options: {:project => nil, :mixin => []}, tasks: ['test:all'] ) },
231
+ 'check' => ->(h, cfg) { h.check( {}, cfg, {:project => nil, :mixin => []} ) },
232
+ 'environment' => ->(h, cfg) { h.environment( {}, cfg, {:project => nil, :mixin => []} ) },
233
+ 'dumpconfig' => ->(h, cfg) { h.dumpconfig( {}, cfg, {:project => nil, :mixin => [], :app => true}, 'out.yml', [] ) },
234
+ 'dumpconfig --no-app' => ->(h, cfg) { h.dumpconfig( {}, cfg, {:project => nil, :mixin => [], :app => false}, 'out.yml', [] ) },
235
+ '-T' => ->(h, cfg) { h.rake_help( env: {}, app_cfg: cfg ) },
236
+ }.each do |name, command|
237
+ it "#{name} hands off with the project's configuration before doing anything else" do
238
+ expect(@launchinator).to receive(:launch).with( env: {}, app_cfg: @app_cfg, config: @config ).and_raise( HandedOff )
239
+
240
+ expect { command.call( @cli_handler, @app_cfg ) }.to raise_error( HandedOff )
241
+ expect_no_work_done()
242
+ end
243
+ end
244
+
245
+ it 'help hands off with the project\'s configuration before printing any help' do
246
+ allow(@projectinator).to receive(:config_available?).and_return( true )
247
+ expect(@launchinator).to receive(:launch).with( env: {}, app_cfg: @app_cfg, config: @config ).and_raise( HandedOff )
248
+ printed = false
249
+
250
+ expect { @cli_handler.app_help( {}, @app_cfg, {:project => nil, :mixin => []}, nil ) { printed = true } }.to raise_error( HandedOff )
251
+ expect(printed).to be( false )
252
+ expect_no_work_done()
253
+ end
254
+
255
+ it 'help for one command hands off without the project when the project fails to load' do
256
+ allow(@projectinator).to receive(:config_available?).and_return( true )
257
+ allow(@composinator).to receive(:loadinate).and_raise( 'Project configuration file section :mixins failed validation' )
258
+ expect(@launchinator).to receive(:launch).with( env: {}, app_cfg: @app_cfg, config: {} ).and_raise( HandedOff )
259
+
260
+ expect { @cli_handler.app_help( {}, @app_cfg, {:project => nil, :mixin => []}, 'build' ) { } }.to raise_error( HandedOff )
261
+ end
262
+
263
+ it 'help for every command still fails when the project fails to load' do
264
+ allow(@projectinator).to receive(:config_available?).and_return( true )
265
+ allow(@composinator).to receive(:loadinate).and_raise( 'Project configuration file section :mixins failed validation' )
266
+
267
+ expect {
268
+ @cli_handler.app_help( {}, @app_cfg, {:project => nil, :mixin => []}, nil ) { }
269
+ }.to raise_error( /failed validation/ )
270
+ expect(@launchinator).to_not have_received(:launch)
271
+ end
272
+
273
+ it 'help without a project hands off by environment and vendor detection alone' do
274
+ allow(@projectinator).to receive(:config_available?).and_return( false )
275
+ expect(@launchinator).to receive(:launch).with( env: {}, app_cfg: @app_cfg, config: {} ).and_raise( HandedOff )
276
+
277
+ expect { @cli_handler.app_help( {}, @app_cfg, {:project => nil, :mixin => []}, 'build' ) { } }.to raise_error( HandedOff )
278
+ expect(@composinator).to_not have_received(:loadinate)
279
+ end
280
+
281
+ {
282
+ 'new' => ->(h, cfg) { h.new_project( {}, cfg, '1.2.2', {:local => true, :configs => true}, 'proj' ) },
283
+ 'examples' => ->(h, cfg) { h.list_examples( {}, cfg, {} ) },
284
+ 'example' => ->(h, cfg) { h.create_example( {}, cfg, {:local => true}, 'temp_sensor', nil ) },
285
+ }.each do |name, command|
286
+ it "#{name} hands off by environment and vendor detection before doing anything else" do
287
+ expect(@launchinator).to receive(:launch).with( env: {}, app_cfg: @app_cfg ).and_raise( HandedOff )
288
+
289
+ expect { command.call( @cli_handler, @app_cfg ) }.to raise_error( HandedOff )
290
+ expect_no_work_done()
291
+ end
292
+ end
293
+
294
+ # GH #1365: a `--local` copy carries no documentation bundle, so it would export only licenses
295
+ describe 'docs' do
296
+ before(:each) do
297
+ @launcher = Installation.new( @app_cfg[:launcher_root_path] )
298
+ @copy = Installation.new( 'vendor/ceedling' )
299
+ allow(@launchinator).to receive(:resolve) { |app_cfg:, **| app_cfg.set_paths( @copy.root ); @copy }
300
+ end
301
+
302
+ it 'hands off to the selected installation when it holds a documentation bundle' do
303
+ allow(@helper).to receive(:docs_bundle?).with( @copy.root ).and_return( true )
304
+ expect(@launchinator).to receive(:proceed).with( target: @copy, app_cfg: @app_cfg ).and_raise( HandedOff )
305
+
306
+ expect { @cli_handler.docs( {}, @app_cfg, 'out' ) }.to raise_error( HandedOff )
307
+ expect_no_work_done()
308
+ end
309
+
310
+ it 'exports the launcher\'s documentation, saying so, when the selected installation has none' do
311
+ allow(@helper).to receive(:docs_bundle?).with( @copy.root ).and_return( false )
312
+
313
+ expect(@loginator).to receive(:console).with(
314
+ "#{@copy.describe} holds no documentation bundle to export. " +
315
+ "Exporting the documentation of #{@launcher.describe( 'Ceedling launcher' )} instead.",
316
+ LogLabels::NOTICE
317
+ ).ordered
318
+ expect(@helper).to receive(:copy_docs).with( @launcher.root, 'out' ).ordered
319
+
320
+ @cli_handler.docs( {}, @app_cfg, 'out' )
321
+
322
+ expect(@launchinator).to_not have_received(:proceed)
323
+ expect(@app_cfg[:ceedling_root_path]).to eq( @launcher.root )
324
+ end
325
+
326
+ it 'exports the launcher\'s own documentation with no notice when it is selected' do
327
+ allow(@launchinator).to receive(:resolve).and_return( @launcher )
328
+
329
+ expect(@helper).to receive(:copy_docs).with( @launcher.root, 'out' )
330
+
331
+ @cli_handler.docs( {}, @app_cfg, 'out' )
332
+
333
+ expect(@loginator).to_not have_received(:console)
334
+ end
335
+ end
336
+
337
+ it 'upgrade hands off by environment alone, never to a vendor/ceedling it detects' do
338
+ source = Installation.new( 'elsewhere/ceedling' )
339
+ expect(@launchinator).to receive(:resolve).with( env: {}, app_cfg: @app_cfg, detect_vendor: false ).and_return( source )
340
+ expect(@launchinator).to receive(:proceed).with( target: source, app_cfg: @app_cfg ).and_raise( HandedOff )
341
+
342
+ expect { @cli_handler.upgrade_project( {}, @app_cfg, {:project => 'project.yml'}, 'proj' ) }.to raise_error( HandedOff )
343
+ expect_no_work_done()
344
+ end
345
+
346
+ # GH #1365: WHICH_CEEDLING naming the project's own copy would hand off to it, and a copy
347
+ # before 1.2.2 deletes itself as its own source
348
+ it 'upgrade refuses to re-vendor a project from its own vendored copy, before handing off to it' do
349
+ allow(@launchinator).to receive(:resolve) { |app_cfg:, **| app_cfg.set_paths( 'proj/vendor/ceedling' ); Installation.new( 'proj/vendor/ceedling' ) }
350
+
351
+ expect {
352
+ @cli_handler.upgrade_project( {'WHICH_CEEDLING' => 'proj/vendor/ceedling'}, @app_cfg, {:project => 'project.yml'}, 'proj' )
353
+ }.to raise_error( CeedlingException, /Cannot upgrade #{Regexp.escape( File.expand_path( 'proj/vendor/ceedling' ) )}\/ from itself/ )
354
+ expect(@launchinator).to_not have_received(:proceed)
355
+ expect_no_work_done()
356
+ end
357
+ end
358
+
359
+ # GH #1371: exported documentation was never refreshed, and refreshing it removed all of docs/
360
+ describe '#upgrade_project documentation' do
361
+ before(:each) do
362
+ @app_cfg = CeedlingAppConfig.new
363
+ @source = Installation.new( 'elsewhere/ceedling' )
364
+ @docs = File.join( 'proj', 'docs' )
365
+ allow(@path_validator).to receive(:standardize_paths) { |*paths| paths }
366
+ allow(@launchinator).to receive(:resolve) { |app_cfg:, **| app_cfg.set_paths( @source.root ); @source }
367
+ allow(@helper).to receive(:project_exists?).and_return( true )
368
+ end
369
+
370
+ def upgrade
371
+ @cli_handler.upgrade_project( {}, @app_cfg, {:project => 'project.yml'}, 'proj' )
372
+ end
373
+
374
+ it 'replaces what an export wrote, keeping the bundle when the source has none, then exports again' do
375
+ allow(@helper).to receive(:exported_docs?).with( @docs ).and_return( true )
376
+ allow(@helper).to receive(:docs_bundle?).with( @source.root ).and_return( false )
377
+
378
+ expect(@helper).to receive(:remove_exported_docs).with( @docs, keep_bundle: true ).ordered
379
+ expect(@helper).to receive(:copy_docs).with( @source.root, @docs ).ordered
380
+
381
+ upgrade()
382
+
383
+ expect(@actionator).to_not have_received(:remove_directory).with( @docs )
384
+ end
385
+
386
+ it 'leaves docs/ alone when it holds no export' do
387
+ allow(@helper).to receive(:exported_docs?).with( @docs ).and_return( false )
388
+
389
+ upgrade()
390
+
391
+ expect(@helper).to_not have_received(:remove_exported_docs)
392
+ expect(@helper).to_not have_received(:copy_docs)
393
+ end
119
394
  end
120
395
 
121
396
  describe '#list_examples' do
@@ -168,6 +443,7 @@ describe CliHandler do
168
443
  :path_validator => real_path_validator,
169
444
  :rake_task_registry => double('rake_task_registry').as_null_object,
170
445
  :actionator => double('actionator').as_null_object,
446
+ :launchinator => double('launchinator').as_null_object,
171
447
  :loginator => double('loginator').as_null_object,
172
448
  })
173
449
  end
@@ -7,6 +7,7 @@
7
7
 
8
8
  require 'spec_helper'
9
9
  require 'tmpdir'
10
+ require 'fileutils'
10
11
  require 'rake'
11
12
 
12
13
  # bin/versionator.rb (pulled in transitively by cli_helper.rb) uses bare `require`
@@ -22,6 +23,8 @@ $: << File.join(here, '../../../lib')
22
23
  require 'cli_helper'
23
24
  require 'app_cfg'
24
25
  require 'ceedling/ruby_expandinator'
26
+ require 'ceedling/config/config_walkinator'
27
+ require 'ceedling/filesystem/file_wrapper'
25
28
  # CliHelper#test_task? references RakeTaskRegistry::TAG_TEST, but only
26
29
  # bin/cli_handler.rb requires this file in the real bootstrap (loaded before
27
30
  # cli_helper.rb's methods are ever called) -- required directly here since this
@@ -121,70 +124,107 @@ describe CliHelper do
121
124
  end
122
125
  end
123
126
 
124
- describe '#which_ceedling?' do
125
- before(:each) do
126
- @app_cfg = CeedlingAppConfig.new
127
- # Baseline: no :project ↳ :which_ceedling entry found. Tests exercising that
128
- # config path override this with a more specific `.with(...)` stub below.
129
- allow(@config_walkinator).to receive(:fetch_value).and_return( [nil, nil] )
127
+ describe '#docs_bundle?' do
128
+ it 'is true when the installation holds an HTML documentation bundle' do
129
+ allow(@file_wrapper).to receive(:directory?).with( File.join( '/ceedling', DOCS_SITE_LOCAL_PATH ) ).and_return( true )
130
+
131
+ expect(@cli_helper.docs_bundle?( '/ceedling' )).to be( true )
130
132
  end
131
133
 
132
- it 'uses the WHICH_CEEDLING environment variable at highest priority' do
133
- which, path = @cli_helper.which_ceedling?( env: {'WHICH_CEEDLING' => 'gem'}, app_cfg: @app_cfg )
134
+ it 'is false when it does not, as for any --local copy' do
135
+ allow(@file_wrapper).to receive(:directory?).with( File.join( '/ceedling', DOCS_SITE_LOCAL_PATH ) ).and_return( false )
134
136
 
135
- expect(which).to eq( :gem )
136
- expect(path).to eq( @app_cfg[:ceedling_rakefile_filepath] )
137
+ expect(@cli_helper.docs_bundle?( '/ceedling' )).to be( false )
137
138
  end
139
+ end
138
140
 
139
- it 'falls back to config :project ↳ :which_ceedling when no environment variable is set' do
140
- config = {:project => {:which_ceedling => 'gem'}}
141
- allow(@config_walkinator).to receive(:fetch_value).with( :project, :which_ceedling, hash: config ).and_return( ['gem', nil] )
141
+ # GH #1371: `upgrade` detected exported documentation by a file exports stopped writing in
142
+ # 1.1.0, and refreshed it by deleting all of docs/, the project's own files included
143
+ describe 'exported documentation' do
144
+ LEGACY_PAGES = %w[BreakingChanges.md CODE_OF_CONDUCT.md CONTRIBUTING.md CeedlingPacket.md CeedlingUpgrade.md
145
+ Changelog.md PluginDevelopmentGuide.md ReleaseNotes.md ThrowTheSwitchCodingStandard.md]
142
146
 
143
- which, path = @cli_helper.which_ceedling?( env: {}, config: config, app_cfg: @app_cfg )
147
+ around(:each) do |example|
148
+ Dir.mktmpdir { |dir| @docs = dir; example.run }
149
+ end
144
150
 
145
- expect(which).to eq( :gem )
146
- expect(path).to eq( @app_cfg[:ceedling_rakefile_filepath] )
151
+ before(:each) do
152
+ @actions = double('actionator')
153
+ @removed = []
154
+ allow(@actions).to receive(:remove_directory) { |path| @removed << path }
155
+ @helper = described_class.new({
156
+ :file_wrapper => FileWrapper.new( {:loginator => @loginator, :verbosinator => double('verbosinator').as_null_object} ),
157
+ :actionator => @actions,
158
+ :config_walkinator => @config_walkinator,
159
+ :path_validator => @path_validator,
160
+ :rake_task_registry => @rake_task_registry,
161
+ :loginator => @loginator,
162
+ :reportinator => double('reportinator').as_null_object,
163
+ :system_wrapper => double('system_wrapper').as_null_object,
164
+ :ruby_expandinator => @ruby_expandinator,
165
+ })
166
+ end
167
+
168
+ def make(*entries)
169
+ entries.each do |entry|
170
+ path = File.join( @docs, entry )
171
+ entry.end_with?( '/' ) ? FileUtils.mkdir_p( path ) : (FileUtils.mkdir_p( File.dirname( path ) ); File.write( path, '' ))
172
+ end
147
173
  end
148
174
 
149
- it 'falls back to a vendor/ceedling directory when neither environment nor config specify anything' do
150
- allow(@file_wrapper).to receive(:directory?).with( 'vendor/ceedling' ).and_return( true )
151
- allow(@file_wrapper).to receive(:exist?).and_return( true )
175
+ def removed
176
+ return @removed.map { |path| path.delete_prefix( File.join( @docs, '' ) ) }.sort
177
+ end
152
178
 
153
- which, path = @cli_helper.which_ceedling?( env: {}, app_cfg: @app_cfg )
179
+ describe '#exported_docs?' do
180
+ it 'finds an export of 1.1.0 or later by its framework or bundle directories' do
181
+ %w[ceedling/ unity/ cmock/ c_exception/].each do |entry|
182
+ Dir.mktmpdir do |dir|
183
+ FileUtils.mkdir_p( File.join( dir, entry ) )
184
+ expect(@helper.exported_docs?( dir )).to be( true ), entry
185
+ end
186
+ end
187
+ end
154
188
 
155
- expect(which).to eq( :path )
156
- expect(path).to eq( @app_cfg[:ceedling_rakefile_filepath] )
157
- expect(@app_cfg[:ceedling_root_path]).to eq( File.expand_path('vendor/ceedling') )
158
- end
189
+ it 'finds a 1.0.x export by its CeedlingPacket.md' do
190
+ make( 'CeedlingPacket.md' )
159
191
 
160
- it 'defaults to :gem when nothing is set and no vendor/ceedling directory exists' do
161
- allow(@file_wrapper).to receive(:directory?).with( 'vendor/ceedling' ).and_return( false )
192
+ expect(@helper.exported_docs?( @docs )).to be( true )
193
+ end
162
194
 
163
- which, path = @cli_helper.which_ceedling?( env: {}, app_cfg: @app_cfg )
195
+ it 'finds nothing in a docs/ holding only the project\'s own files, or in no docs/ at all' do
196
+ make( 'notes.md', 'design/' )
164
197
 
165
- expect(which).to eq( :gem )
166
- expect(path).to eq( @app_cfg[:ceedling_rakefile_filepath] )
198
+ expect(@helper.exported_docs?( @docs )).to be( false )
199
+ expect(@helper.exported_docs?( File.join( @docs, 'absent' ) )).to be( false )
200
+ end
167
201
  end
168
202
 
169
- it 'raises when a configured launch path does not exist as a directory' do
170
- config = {:project => {:which_ceedling => 'nonexistent/path'}}
171
- allow(@config_walkinator).to receive(:fetch_value).with( :project, :which_ceedling, hash: config ).and_return( ['nonexistent/path', nil] )
172
- allow(@file_wrapper).to receive(:directory?).and_return( false )
203
+ describe '#remove_exported_docs' do
204
+ it 'removes what an export of 1.1.0 or later wrote, and leaves the project\'s own files' do
205
+ make( 'ceedling/', 'unity/', 'cmock/', 'c_exception/', 'license.txt', 'LICENSE.md', 'notes.md', 'design/' )
173
206
 
174
- expect {
175
- @cli_helper.which_ceedling?( env: {}, config: config, app_cfg: @app_cfg )
176
- }.to raise_error( /does not exist/ )
177
- end
207
+ @helper.remove_exported_docs( @docs, keep_bundle: false )
208
+
209
+ # The export overwrites its own license, so a license file in docs/ may be the project's
210
+ expect(removed).to eq( %w[c_exception ceedling cmock unity] )
211
+ end
178
212
 
179
- it 'raises when a configured launch path exists but contains no Ceedling installation' do
180
- config = {:project => {:which_ceedling => 'empty/path'}}
181
- allow(@config_walkinator).to receive(:fetch_value).with( :project, :which_ceedling, hash: config ).and_return( ['empty/path', nil] )
182
- allow(@file_wrapper).to receive(:directory?).and_return( true )
183
- allow(@file_wrapper).to receive(:exist?).and_return( false )
213
+ it 'removes what a 1.0.x export wrote, and leaves the project\'s own files' do
214
+ make( *LEGACY_PAGES, 'plugins/', 'unity/', 'LICENSE.txt', 'notes.md' )
184
215
 
185
- expect {
186
- @cli_helper.which_ceedling?( env: {}, config: config, app_cfg: @app_cfg )
187
- }.to raise_error( /contains no Ceedling installation/ )
216
+ @helper.remove_exported_docs( @docs, keep_bundle: false )
217
+
218
+ expect(removed).to eq( (LEGACY_PAGES + %w[plugins unity]).sort )
219
+ end
220
+
221
+ it 'keeps the HTML bundle when the source has none to replace it with' do
222
+ make( 'ceedling/', 'unity/' )
223
+
224
+ @helper.remove_exported_docs( @docs, keep_bundle: true )
225
+
226
+ expect(removed).to eq( %w[unity] )
227
+ end
188
228
  end
189
229
  end
190
230
 
@@ -232,6 +272,37 @@ describe CliHelper do
232
272
  end
233
273
  end
234
274
 
275
+ # The path handling audit around GH #1370: every other build path reads the build root
276
+ # after Ruby expansion
277
+ describe 'G17: #process_logging_path' do
278
+ it 'places the log directory below the build root as expanded' do
279
+ helper = described_class.new({
280
+ :file_wrapper => @file_wrapper, :actionator => double('actionator').as_null_object, :config_walkinator => ConfigWalkinator.new,
281
+ :path_validator => @path_validator, :rake_task_registry => @rake_task_registry, :loginator => @loginator,
282
+ :reportinator => double('reportinator').as_null_object, :system_wrapper => double('system_wrapper').as_null_object,
283
+ :ruby_expandinator => @ruby_expandinator
284
+ })
285
+ helper.set_ruby_replacement( true )
286
+
287
+ expect(helper.process_logging_path( { project: { build_root: "\#{'out'}/build" } } )).to eq( 'out/build/logs' )
288
+ end
289
+
290
+ # An unset environment variable expands to nothing, which would place the logs at the
291
+ # filesystem root before configuration could refuse the build root
292
+ it 'refuses a build root that expands to nothing, as configuration does' do
293
+ helper = described_class.new({
294
+ :file_wrapper => @file_wrapper, :actionator => double('actionator').as_null_object, :config_walkinator => ConfigWalkinator.new,
295
+ :path_validator => @path_validator, :rake_task_registry => @rake_task_registry, :loginator => @loginator,
296
+ :reportinator => double('reportinator').as_null_object, :system_wrapper => double('system_wrapper').as_null_object,
297
+ :ruby_expandinator => @ruby_expandinator
298
+ })
299
+ helper.set_ruby_replacement( true )
300
+
301
+ expect { helper.process_logging_path( { project: { build_root: "\#{''}" } } ) }
302
+ .to raise_error( CeedlingException, ":project ↳ :build_root => '' names no directory. Give the build its own directory, such as 'build'." )
303
+ end
304
+ end
305
+
235
306
  describe '#process_log_filepath' do
236
307
  around(:each) do |example|
237
308
  Dir.mktmpdir do |dir|
@@ -0,0 +1,114 @@
1
+ # =========================================================================
2
+ # Ceedling - Test-Centered Build System for C
3
+ # ThrowTheSwitch.org
4
+ # Copyright (c) 2010-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
5
+ # SPDX-License-Identifier: MIT
6
+ # =========================================================================
7
+
8
+ require 'spec_helper'
9
+ require 'ceedling/constants'
10
+ require 'installation_selector'
11
+ require 'path_validator'
12
+ require 'app_cfg'
13
+
14
+ describe InstallationSelector do
15
+ before(:each) do
16
+ @file_wrapper = double('file_wrapper').as_null_object
17
+ @config_walkinator = double('config_walkinator').as_null_object
18
+ @loginator = double('loginator').as_null_object
19
+ @app_cfg = CeedlingAppConfig.new
20
+
21
+ @selector = described_class.new({
22
+ :file_wrapper => @file_wrapper,
23
+ :config_walkinator => @config_walkinator,
24
+ # Real, so path standardization is exercised rather than assumed
25
+ :path_validator => PathValidator.new( {:file_wrapper => @file_wrapper, :loginator => @loginator} ),
26
+ :loginator => @loginator,
27
+ })
28
+
29
+ # Baseline: no :project ↳ :which_ceedling entry found. Examples exercising that
30
+ # config path override this with a more specific `.with(...)` stub.
31
+ allow(@config_walkinator).to receive(:fetch_value).and_return( [nil, nil] )
32
+ end
33
+
34
+ it 'uses the WHICH_CEEDLING environment variable at highest priority' do
35
+ installation = @selector.select( env: {'WHICH_CEEDLING' => 'gem'}, app_cfg: @app_cfg )
36
+
37
+ expect(installation).to eq( Installation.new( @app_cfg[:launcher_root_path] ) )
38
+ end
39
+
40
+ # A console line would print at every verbosity, into `dumpconfig --stdout` YAML among others
41
+ it 'logs its use of WHICH_CEEDLING at obnoxious verbosity, as for every other source' do
42
+ @selector.select( env: {'WHICH_CEEDLING' => 'gem'}, app_cfg: @app_cfg )
43
+
44
+ expect(@loginator).to have_received(:log).with( " > Set which Ceedling using environment variable WHICH_CEEDLING", Verbosity::OBNOXIOUS )
45
+ expect(@loginator).to_not have_received(:console)
46
+ end
47
+
48
+ it 'falls back to config :project ↳ :which_ceedling when no environment variable is set' do
49
+ config = {:project => {:which_ceedling => 'gem'}}
50
+ allow(@config_walkinator).to receive(:fetch_value).with( :project, :which_ceedling, hash: config ).and_return( ['gem', nil] )
51
+
52
+ installation = @selector.select( env: {}, config: config, app_cfg: @app_cfg )
53
+
54
+ expect(installation).to eq( Installation.new( @app_cfg[:launcher_root_path] ) )
55
+ end
56
+
57
+ it 'falls back to a vendor/ceedling directory when neither environment nor config specify anything' do
58
+ allow(@file_wrapper).to receive(:directory?).with( 'vendor/ceedling' ).and_return( true )
59
+ allow(@file_wrapper).to receive(:exist?).and_return( true )
60
+
61
+ installation = @selector.select( env: {}, app_cfg: @app_cfg )
62
+
63
+ expect(installation.root).to eq( File.expand_path( 'vendor/ceedling' ) )
64
+ expect(@app_cfg[:ceedling_root_path]).to eq( File.expand_path( 'vendor/ceedling' ) )
65
+ end
66
+
67
+ # GH #1365: `upgrade` run inside a project took the project's own copy as its source
68
+ it 'ignores a vendor/ceedling directory when vendor detection is off' do
69
+ allow(@file_wrapper).to receive(:directory?).with( 'vendor/ceedling' ).and_return( true )
70
+
71
+ installation = @selector.select( env: {}, app_cfg: @app_cfg, detect_vendor: false )
72
+
73
+ expect(installation).to eq( Installation.new( @app_cfg[:launcher_root_path] ) )
74
+ end
75
+
76
+ it 'defaults to the gem when nothing is set and no vendor/ceedling directory exists' do
77
+ allow(@file_wrapper).to receive(:directory?).with( 'vendor/ceedling' ).and_return( false )
78
+
79
+ installation = @selector.select( env: {}, app_cfg: @app_cfg )
80
+
81
+ expect(installation).to eq( Installation.new( @app_cfg[:launcher_root_path] ) )
82
+ end
83
+
84
+ it 'standardizes backslashes in a selected path' do
85
+ allow(@file_wrapper).to receive(:directory?).and_return( true )
86
+ allow(@file_wrapper).to receive(:exist?).and_return( true )
87
+
88
+ installation = @selector.select( env: {'WHICH_CEEDLING' => 'tools\\ceedling'}, app_cfg: @app_cfg )
89
+
90
+ expect(installation.root).to eq( File.expand_path( 'tools/ceedling' ) )
91
+ expect(@file_wrapper).to have_received(:directory?).with( 'tools/ceedling' )
92
+ end
93
+
94
+ it 'raises when a configured launch path does not exist as a directory' do
95
+ config = {:project => {:which_ceedling => 'nonexistent/path'}}
96
+ allow(@config_walkinator).to receive(:fetch_value).with( :project, :which_ceedling, hash: config ).and_return( ['nonexistent/path', nil] )
97
+ allow(@file_wrapper).to receive(:directory?).and_return( false )
98
+
99
+ expect {
100
+ @selector.select( env: {}, config: config, app_cfg: @app_cfg )
101
+ }.to raise_error( /does not exist/ )
102
+ end
103
+
104
+ it 'raises when a configured launch path exists but contains no Ceedling installation' do
105
+ config = {:project => {:which_ceedling => 'empty/path'}}
106
+ allow(@config_walkinator).to receive(:fetch_value).with( :project, :which_ceedling, hash: config ).and_return( ['empty/path', nil] )
107
+ allow(@file_wrapper).to receive(:directory?).and_return( true )
108
+ allow(@file_wrapper).to receive(:exist?).and_return( false )
109
+
110
+ expect {
111
+ @selector.select( env: {}, config: config, app_cfg: @app_cfg )
112
+ }.to raise_error( /contains no Ceedling installation/ )
113
+ end
114
+ end