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
@@ -6,8 +6,9 @@
6
6
  # =========================================================================
7
7
 
8
8
  require 'spec_helper'
9
- require 'ceedling/file_finder'
10
- require 'ceedling/file_finder_helper'
9
+ require 'tmpdir'
10
+ require 'ceedling/filesystem/file_finder'
11
+ require 'ceedling/filesystem/file_finder_helper'
11
12
  require 'ceedling/filename_extension'
12
13
  require 'ceedling/exceptions'
13
14
  require 'ceedling/constants'
@@ -124,6 +125,7 @@ describe FileFinder do
124
125
  allow(@configurator).to receive(:paths_test).and_return( [] )
125
126
  allow(@configurator).to receive(:paths_support).and_return( [] )
126
127
  allow(@configurator).to receive(:paths_include).and_return( ['src'] )
128
+ allow(@configurator).to receive(:collection_mirror_roots_headers).and_return( ['src'] )
127
129
  end
128
130
 
129
131
  it 'resolves a mock header and its own mirrored subdirectory below the configured root that contains it' do
@@ -145,6 +147,26 @@ describe FileFinder do
145
147
  expect(bare_subdir).to eq(pathed_subdir)
146
148
  end
147
149
 
150
+ # GH #1370: a `..` root "contained" a header far above it, so its mock left the build directory
151
+ it 'places a mock of a header far above a `..` root flat, below the root that truly holds it' do
152
+ allow(@configurator).to receive(:collection_all_headers).and_return( ['../../../opt/sdk/inc/sdk.h'] )
153
+ allow(@configurator).to receive(:paths_include).and_return( ['../**', File.expand_path( '../../../opt/sdk/inc' )] )
154
+ allow(@configurator).to receive(:collection_mirror_roots_headers).and_return( ['..', '../../../opt/sdk/inc'] )
155
+
156
+ _source, subdir = @file_finder.resolve_mock('Mocksdk.h')
157
+ expect(subdir).to eq('')
158
+ end
159
+
160
+ # GH #1370 audit (G4): a glob in the middle of a root names one root per directory
161
+ it 'places a mock below the directory a glob-middle root names, not below the glob\'s base' do
162
+ allow(@configurator).to receive(:collection_all_headers).and_return( ['components/foo/include/foo.h'] )
163
+ allow(@configurator).to receive(:paths_include).and_return( ['components/*/include'] )
164
+ allow(@configurator).to receive(:collection_mirror_roots_headers).and_return( ['components/foo/include'] )
165
+
166
+ _source, subdir = @file_finder.resolve_mock('Mockfoo.h')
167
+ expect(subdir).to eq('')
168
+ end
169
+
148
170
  it 'still raises on a bogus (not-found) mock reference, exactly as find_header_input_for_mock does' do
149
171
  expect { @file_finder.resolve_mock('nonexistent/dir/Mockfoo.h') }.to raise_error(CeedlingException)
150
172
  end
@@ -234,18 +256,16 @@ describe FileFinder do
234
256
  expect(@file_finder.find_test_file_from_name('unit/test_foo')).to eq('unit/test_foo.c')
235
257
  end
236
258
 
237
- it 'raises a clear CeedlingException naming .. as unsupported in a CLI task name, rather than a generic not-found' do
238
- # A CLI task name has no file of its own to anchor a .. against -- unlike an
239
- # #include or TEST_SOURCE_FILE() entry, which are always scoped to the test file
240
- # that wrote them. Today's generic "Found no file" message happens to also
241
- # contain the query text, so asserting on the query alone would pass for the
242
- # wrong reason -- the message must specifically call out .. as the problem.
243
- expect { @file_finder.find_test_file_from_name('../unit/test_foo') }.to raise_error(CeedlingException) do |error|
244
- expect(error.message).to include('../unit/test_foo')
245
- expect(error.message).not_to match(/Found no file/)
246
- expect(error.message).to match(/\.\./)
247
- expect(error.message).to match(/context/i)
248
- end
259
+ # GH #1370 audit (G9): a task name is relative to the working directory, as every
260
+ # collected path is, so a leading `..` names a test outside the project
261
+ it 'resolves a name climbing out of the project, against the working directory' do
262
+ allow(@configurator).to receive(:collection_all_tests).and_return( ['../tests/test_x.c', 'unit/test_foo.c'] )
263
+
264
+ expect(@file_finder.find_test_file_from_name('../tests/test_x')).to eq('../tests/test_x.c')
265
+ end
266
+
267
+ it 'resolves a name written with `./`' do
268
+ expect(@file_finder.find_test_file_from_name('./unit/test_foo')).to eq('unit/test_foo.c')
249
269
  end
250
270
  end
251
271
 
@@ -272,6 +292,15 @@ describe FileFinder do
272
292
  allow(@configurator).to receive(:project_build_root).and_return('build')
273
293
  # No configured include root contains these queries, so each keeps its own path.
274
294
  allow(@configurator).to receive(:paths_include).and_return( ['include'] )
295
+ allow(@configurator).to receive(:collection_mirror_roots_include).and_return( ['include'] )
296
+
297
+ # A test is named by its path without its extension. Examples that look tests up by
298
+ # name give their own test collection.
299
+ allow(@configurator).to receive(:collection_all_tests).and_return( [] )
300
+ allow(@file_path_utils).to receive(:form_test_name) { |test| test.to_s.ext( '' ) }
301
+ allow(@file_path_utils).to receive(:form_test_runners_path) do |name, **|
302
+ File.dirname( name ) == '.' ? 'build/test/runners' : File.join( 'build/test/runners', File.dirname( name ) )
303
+ end
275
304
  end
276
305
 
277
306
  it 'never raises, rather than silently missing a build, when two release sources share a basename -- resolves to the first by project path order' do
@@ -311,18 +340,72 @@ describe FileFinder do
311
340
  )
312
341
 
313
342
  found = @file_finder.find_build_input_file(
314
- filepath: 'build/test/out/unit/test_foo/test_foo.o', complain: :error, context: TEST_SYM
343
+ filepath: 'build/test/out/test/unit/test_foo/test_foo.o', complain: :error, context: TEST_SYM
315
344
  )
316
345
  expect(found).to eq('test/unit/test_foo.c')
317
346
 
318
347
  found = @file_finder.find_build_input_file(
319
- filepath: 'build/test/out/integration/test_foo/test_foo.o', complain: :error, context: TEST_SYM
348
+ filepath: 'build/test/out/test/integration/test_foo/test_foo.o', complain: :error, context: TEST_SYM
320
349
  )
321
350
  expect(found).to eq('test/integration/test_foo.c')
322
351
  end
323
352
 
353
+ # GH #1364: a test's own source resolves exactly, though its name ends another's path
354
+ {
355
+ 'flat and nested under one root' => ['test/test_calc.c', 'test/unit/test_calc.c'],
356
+ 'the same subdirectory under two roots' => ['test/unit/test_calc.c', 'extra/unit/test_calc.c'],
357
+ 'the same layout under two roots' => ['test/foo/test_x.c', 'extra/foo/test_x.c']
358
+ }.each do |layout, tests|
359
+ it "resolves each test's own source exactly with #{layout}" do
360
+ allow(@configurator).to receive(:collection_all_tests).and_return( tests )
361
+
362
+ tests.each do |test|
363
+ name = test.ext( '' )
364
+ object = File.join( 'build/test/out', name, File.basename( name ) + '.o' )
365
+
366
+ expect(@file_finder.find_build_input_file( filepath: object, complain: :error, context: TEST_SYM, test: name )).to eq( test )
367
+ expect(@file_finder.find_build_input_file( filepath: object, complain: :error, context: TEST_SYM )).to eq( test )
368
+ end
369
+ end
370
+ end
371
+
372
+ it 'resolves the source of a test outside the project, whose name matches no path' do
373
+ allow(@file_path_utils).to receive(:form_test_name) { |test| test.to_s.ext( '' ).gsub( '..', '_up_' ) }
374
+ allow(@configurator).to receive(:collection_all_tests).and_return( ['../shared/test/unit/test_s.c'] )
375
+
376
+ found = @file_finder.find_build_input_file(
377
+ filepath: 'build/test/out/_up_/shared/test/unit/test_s/test_s.o', complain: :error, context: TEST_SYM
378
+ )
379
+ expect(found).to eq('../shared/test/unit/test_s.c')
380
+ end
381
+
382
+ it 'resolves a test whose name ends another test\'s path' do
383
+ allow(@configurator).to receive(:collection_all_tests).and_return( ['test/unit/test_calc.c', 'other/test/unit/test_calc.c'] )
384
+
385
+ found = @file_finder.find_build_input_file(
386
+ filepath: 'build/test/out/test/unit/test_calc/test_calc.o', complain: :error, context: TEST_SYM
387
+ )
388
+ expect(found).to eq('test/unit/test_calc.c')
389
+ end
390
+
391
+ it 'resolves a header named like its test to the test itself' do
392
+ allow(@configurator).to receive(:collection_all_tests).and_return( ['test/test_calc.c', 'test/unit/test_calc.c'] )
393
+
394
+ found = @file_finder.find_build_input_file( filepath: 'test_calc.h', complain: :error, context: TEST_SYM, test: 'test/unit/test_calc' )
395
+ expect(found).to eq('test/unit/test_calc.c')
396
+ end
397
+
398
+ it 'names every test once, however many lookups follow' do
399
+ expect(@configurator).to receive(:collection_all_tests).once.and_return( ['test/test_calc.c', 'test/unit/test_calc.c'] )
400
+
401
+ 2.times do
402
+ @file_finder.find_build_input_file( filepath: 'build/test/out/test/test_calc/test_calc.o', complain: :error, context: TEST_SYM )
403
+ end
404
+ end
405
+
324
406
  it 'resolves a test\'s own runner by its own runner subdirectory, without treating another same-named test\'s runner as ambiguous' do
325
407
  allow(@configurator).to receive(:project_test_runners_path).and_return('build/test/runners')
408
+ allow(@configurator).to receive(:collection_all_tests).and_return( ['unit/test_foo.c', 'integration/test_foo.c'] )
326
409
 
327
410
  all_runners = {
328
411
  'build/test/runners/unit' => ['build/test/runners/unit/test_foo_runner.c'],
@@ -346,6 +429,7 @@ describe FileFinder do
346
429
 
347
430
  it 'resolves a mock generated once per test, by the current test\'s own build directory, without treating that expected repetition as ambiguous' do
348
431
  allow(@configurator).to receive(:cmock_mock_path).and_return('build/test/mocks')
432
+ allow(@configurator).to receive(:collection_all_tests).and_return( ['TestAdcModel.c', 'TestModel.c'] )
349
433
 
350
434
  all_mocks = {
351
435
  'build/test/mocks/TestAdcModel' => ['build/test/mocks/TestAdcModel/MockTemperatureFilter.c'],
@@ -370,6 +454,47 @@ describe FileFinder do
370
454
  expect(found).to eq('build/test/mocks/TestModel/MockTemperatureFilter.c')
371
455
  end
372
456
 
457
+ # One test's name continues another's, so its mock directory sits inside the other's
458
+ it 'keeps a test whose directory nests inside another test\'s from sharing that test\'s mocks' do
459
+ allow(@configurator).to receive(:cmock_mock_path).and_return('build/test/mocks')
460
+ allow(@configurator).to receive(:collection_all_tests).and_return( ['test/test_foo.c', 'test/test_foo/test_bar.c'] )
461
+ mocks = ['build/test/mocks/test/test_foo/Mockwidget.c', 'build/test/mocks/test/test_foo/test_bar/Mockwidget.c']
462
+ allow(@file_wrapper).to receive(:directory_listing) do |glob|
463
+ root = glob.delete_suffix( '/**/*.c' )
464
+ mocks.select { |mock| mock.start_with?( root + '/' ) }
465
+ end
466
+
467
+ found = @file_finder.find_build_input_file( filepath: 'build/test/out/test/test_foo/Mockwidget.o', complain: :error, context: TEST_SYM )
468
+ expect(found).to eq('build/test/mocks/test/test_foo/Mockwidget.c')
469
+
470
+ found = @file_finder.find_build_input_file( filepath: 'build/test/out/test/test_foo/test_bar/Mockwidget.o', complain: :error, context: TEST_SYM )
471
+ expect(found).to eq('build/test/mocks/test/test_foo/test_bar/Mockwidget.c')
472
+ end
473
+
474
+ it 'keeps a test whose directory nests inside another test\'s from sharing that test\'s Partials' do
475
+ allow(@configurator).to receive(:project_test_partials_path).and_return('build/test/partials')
476
+ allow(@configurator).to receive(:collection_all_tests).and_return( ['test/test_foo.c', 'test/test_foo/test_bar.c'] )
477
+ partials = ['build/test/partials/test/test_foo/ceedling_partial_widget_impl.c', 'build/test/partials/test/test_foo/test_bar/ceedling_partial_widget_impl.c']
478
+ allow(@file_wrapper).to receive(:directory_listing) do |glob|
479
+ root = glob.delete_suffix( '/**/*.c' )
480
+ partials.select { |partial| partial.start_with?( root + '/' ) }
481
+ end
482
+
483
+ found = @file_finder.find_build_input_file( filepath: 'build/test/out/test/test_foo/ceedling_partial_widget_impl.o', complain: :error, context: TEST_SYM )
484
+ expect(found).to eq('build/test/partials/test/test_foo/ceedling_partial_widget_impl.c')
485
+ end
486
+
487
+ it 'lists a test\'s mocks with any bracket in its path escaped, so the listing is not a glob class' do
488
+ allow(@configurator).to receive(:cmock_mock_path).and_return('build/test/mocks')
489
+ allow(@configurator).to receive(:collection_all_tests).and_return( ['test[1]/test_foo.c'] )
490
+
491
+ expect(@file_wrapper).to receive(:directory_listing).with( 'build/test/mocks/test\[1\]/test_foo/**/*.c' )
492
+ .and_return( ['build/test/mocks/test[1]/test_foo/Mockwidget.c'] )
493
+
494
+ found = @file_finder.find_build_input_file( filepath: 'build/test/out/test[1]/test_foo/Mockwidget.o', complain: :error, context: TEST_SYM )
495
+ expect(found).to eq('build/test/mocks/test[1]/test_foo/Mockwidget.c')
496
+ end
497
+
373
498
  it 'still raises when the very same test mocks two different same-named headers from different directories' do
374
499
  allow(@configurator).to receive(:cmock_mock_path).and_return('build/test/mocks')
375
500
  allow(@file_wrapper).to receive(:directory_listing).and_return(
@@ -412,39 +537,80 @@ describe FileFinder do
412
537
  expect(found).to eq('build/test/mocks/TestModel/MockTemperatureFilter.c')
413
538
  end
414
539
 
415
- it 'resolves a release object via its own mirrored subdirectory, even when another same-named source exists elsewhere' do
540
+ it 'resolves a release object to the source at its own path, even when another same-named source exists elsewhere' do
416
541
  allow(@configurator).to receive(:collection_release_build_input).and_return(
417
542
  ['drivers/foo/bar.c', 'drivers/baz/bar.c']
418
543
  )
419
544
 
420
545
  found = @file_finder.find_build_input_file(
421
- filepath: 'build/release/out/foo/bar.o', complain: :error, context: RELEASE_SYM
546
+ filepath: 'build/release/out/drivers/foo/bar.o', complain: :error, context: RELEASE_SYM
422
547
  )
423
548
  expect(found).to eq('drivers/foo/bar.c')
424
549
 
425
550
  found = @file_finder.find_build_input_file(
426
- filepath: 'build/release/out/baz/bar.o', complain: :error, context: RELEASE_SYM
551
+ filepath: 'build/release/out/drivers/baz/bar.o', complain: :error, context: RELEASE_SYM
427
552
  )
428
553
  expect(found).to eq('drivers/baz/bar.c')
429
554
  end
430
555
 
431
- it 'resolves a module-under-test object via its own mirrored subdirectory, even when another same-named module exists elsewhere' do
556
+ it 'resolves a module-under-test object to the source at its own path, even when another same-named module exists elsewhere' do
432
557
  allow(@file_path_utils).to receive(:form_test_build_path).with('TestFoo', context: TEST_SYM).and_return('build/test/out/TestFoo')
433
558
  allow(@configurator).to receive(:collection_existing_test_build_input).and_return(
434
559
  ['drivers/foo/bar.c', 'drivers/baz/bar.c']
435
560
  )
436
561
 
437
562
  found = @file_finder.find_build_input_file(
438
- filepath: 'build/test/out/TestFoo/foo/bar.o', complain: :error, context: TEST_SYM, test: 'TestFoo'
563
+ filepath: 'build/test/out/TestFoo/drivers/foo/bar.o', complain: :error, context: TEST_SYM, test: 'TestFoo'
439
564
  )
440
565
  expect(found).to eq('drivers/foo/bar.c')
441
566
 
442
567
  found = @file_finder.find_build_input_file(
443
- filepath: 'build/test/out/TestFoo/baz/bar.o', complain: :error, context: TEST_SYM, test: 'TestFoo'
568
+ filepath: 'build/test/out/TestFoo/drivers/baz/bar.o', complain: :error, context: TEST_SYM, test: 'TestFoo'
444
569
  )
445
570
  expect(found).to eq('drivers/baz/bar.c')
446
571
  end
447
572
 
573
+ # A tail match of `src/util` takes whichever of these comes first
574
+ it 'resolves an object to exactly its own source, never one whose path merely ends the same way' do
575
+ allow(@configurator).to receive(:collection_release_build_input).and_return( ['lib/src/util.c', 'src/util.c'] )
576
+
577
+ found = @file_finder.find_build_input_file( filepath: 'build/release/out/src/util.o', complain: :error, context: RELEASE_SYM )
578
+ expect(found).to eq('src/util.c')
579
+ end
580
+
581
+ it 'resolves an object of a source above the project root' do
582
+ allow(@configurator).to receive(:collection_release_build_input).and_return( ['src/crc.c', '../shared/src/crc.c'] )
583
+
584
+ found = @file_finder.find_build_input_file( filepath: 'build/release/out/_up_/shared/src/crc.o', complain: :error, context: RELEASE_SYM )
585
+ expect(found).to eq('../shared/src/crc.c')
586
+ end
587
+
588
+ it 'resolves an object of a file in the running Ceedling installation, placed below `_ceedling_`' do
589
+ helper = File.join( BuildSubpath::INSTALLATION_ROOT, 'plugins/fff/src/fff_unity_helper.c' )
590
+ allow(@file_path_utils).to receive(:form_test_build_path).with( 'test/test_x', context: TEST_SYM ).and_return( 'build/test/out/test/test_x' )
591
+ allow(@configurator).to receive(:collection_existing_test_build_input).and_return( ['src/fff_unity_helper.c', helper] )
592
+
593
+ Dir.mktmpdir do |project|
594
+ Dir.chdir( project ) do
595
+ found = @file_finder.find_build_input_file(
596
+ filepath: 'build/test/out/test/test_x/_ceedling_/plugins/fff/src/fff_unity_helper.o', complain: :error, context: TEST_SYM, test: 'test/test_x'
597
+ )
598
+ expect(found).to eq( helper )
599
+ end
600
+ end
601
+ end
602
+
603
+ it 'recovers a nested test\'s identity from its object\'s path, rather than its parent test\'s' do
604
+ allow(@configurator).to receive(:collection_all_tests).and_return( ['test/test_foo.c', 'test/test_foo/test_bar.c'] )
605
+ allow(@file_path_utils).to receive(:form_test_build_path) { |name, **| File.join( 'build/test/out', name ) }
606
+ allow(@configurator).to receive(:collection_existing_test_build_input).and_return( ['src/util.c'] )
607
+
608
+ expect(@file_finder.find_build_input_file( filepath: 'build/test/out/test/test_foo/test_bar/test/test_foo/test_bar.o', complain: :error, context: TEST_SYM ))
609
+ .to eq('test/test_foo/test_bar.c')
610
+ expect(@file_finder.find_build_input_file( filepath: 'build/test/out/test/test_foo/test/test_foo.o', complain: :error, context: TEST_SYM ))
611
+ .to eq('test/test_foo.c')
612
+ end
613
+
448
614
  it 'still resolves a flat module-under-test object by basename when no test identity is given' do
449
615
  allow(@configurator).to receive(:collection_existing_test_build_input).and_return( ['drivers/bar.c'] )
450
616
 
@@ -7,7 +7,7 @@
7
7
 
8
8
  require 'rake' # for FileList
9
9
  require 'spec_helper'
10
- require 'ceedling/file_path_collection_utils'
10
+ require 'ceedling/filesystem/file_path_collection_utils'
11
11
 
12
12
  describe FilePathCollectionUtils do
13
13
 
@@ -143,6 +143,49 @@ describe FilePathCollectionUtils do
143
143
  expect( result ).to include( 'src' )
144
144
  end
145
145
 
146
+
147
+ # GH #1370 audit (G13): an exclusion dropped only the directory it named, keeping those below
148
+ it 'excludes every directory below a `-:` exclusion' do
149
+ kept, dropped, deep = File.expand_path( 'src/a' ), File.expand_path( 'src/exp' ), File.expand_path( 'src/exp/deep' )
150
+ allow( @file_wrapper ).to receive( :directory_listing ).with( 'src/**/**' ).and_return( [kept, dropped, deep] )
151
+ allow( @file_wrapper ).to receive( :directory_listing ).with( 'src/exp' ).and_return( [dropped] )
152
+ allow( @file_wrapper ).to receive( :directory? ).and_return( true )
153
+
154
+ expect( @fpcu.collect_paths( ['+:src/**', '-:src/exp'] ) ).to match_array( ['src', 'src/a'] )
155
+ end
156
+
157
+ # GH #1370 audit (G15): a root above the build root collected the build tree
158
+ it 'excludes every directory below an excluded path' do
159
+ src, build = File.expand_path( 'src' ), File.expand_path( 'build/test/mocks' )
160
+ allow( @file_wrapper ).to receive( :directory_listing ).with( './**/**' ).and_return( [src, build] )
161
+ allow( @file_wrapper ).to receive( :directory? ).and_return( true )
162
+
163
+ expect( @fpcu.collect_paths( ['./**'], exclude: ['build'] ) ).to_not include( 'build/test/mocks' )
164
+ end
165
+ end
166
+
167
+
168
+ # GH #1370 audit: the places build files mirror below, each spelled canonically
169
+ describe '#collect_mirror_roots' do
170
+ it 'roots a recursive entry at its base, and a plain entry at itself' do
171
+ expect( @fpcu.collect_mirror_roots( ['src/**', 'include', './lib/*'] ) ).to eq( ['src', 'include', 'lib'] )
172
+ end
173
+
174
+ it 'roots a glob within an entry at each directory it names' do
175
+ named = [File.expand_path( 'components/a/include' ), File.expand_path( 'components/b/include' )]
176
+ allow( @file_wrapper ).to receive( :directory_listing ).with( 'components/*/include' ).and_return( named )
177
+ allow( @file_wrapper ).to receive( :directory? ).and_return( true )
178
+
179
+ expect( @fpcu.collect_mirror_roots( ['components/*/include'] ) ).to eq( ['components/a/include', 'components/b/include'] )
180
+ end
181
+
182
+ it 'roots nothing at a `-:` exclusion, and keeps a literal bracket as part of a name' do
183
+ expect( @fpcu.collect_mirror_roots( ['+:src/**', '-:src/exp', 'src/[legacy]'] ) ).to eq( ['src', 'src/[legacy]'] )
184
+ end
185
+
186
+ it 'returns [] for nil input' do
187
+ expect( @fpcu.collect_mirror_roots( nil ) ).to eq( [] )
188
+ end
146
189
  end
147
190
 
148
191
 
@@ -229,3 +272,140 @@ describe FilePathCollectionUtils do
229
272
  end
230
273
 
231
274
  end
275
+
276
+ # The path handling audit around GH #1370: one place collected under two spellings is still
277
+ # one place, so an exclusion names it however it was reached
278
+ describe FilePathCollectionUtils, 'on disk' do
279
+ require 'tmpdir'
280
+ require 'fileutils'
281
+ require 'ceedling/filesystem/file_wrapper'
282
+
283
+ POSIX_ONLY_COLLECTION = { unless: Gem.win_platform? }.freeze
284
+
285
+ around(:each) do |example|
286
+ Dir.mktmpdir do |dir|
287
+ @ws = File.realpath( dir )
288
+ FileUtils.mkdir_p( File.join( @ws, 'app/src/a' ) )
289
+ FileUtils.mkdir_p( File.join( @ws, 'vendor/ext' ) )
290
+ File.write( File.join( @ws, 'vendor/ext/foo.c' ), '' )
291
+ File.write( File.join( @ws, 'app/src/a/bar.c' ), '' )
292
+ Dir.chdir( File.join( @ws, 'app' ) ) { example.run }
293
+ end
294
+ end
295
+
296
+ before(:each) do
297
+ file_wrapper = FileWrapper.new( { loginator: double( 'loginator' ).as_null_object, verbosinator: double( 'verbosinator' ).as_null_object } )
298
+ @fpcu = described_class.new( { file_wrapper: file_wrapper } )
299
+ end
300
+
301
+ def link_ext
302
+ File.symlink( File.join( @ws, 'vendor/ext' ), File.join( @ws, 'app/src/ext' ) )
303
+ end
304
+
305
+ it 'leaves out a symlinked directory below a root its `-:` entry names', **POSIX_ONLY_COLLECTION do
306
+ link_ext
307
+
308
+ expect(@fpcu.collect_paths( ['+:src/**', '-:src/ext'] )).to eq( ['src', 'src/a'] )
309
+ end
310
+
311
+ it 'collects a symlinked directory once, however many entries reach it', **POSIX_ONLY_COLLECTION do
312
+ link_ext
313
+
314
+ expect(@fpcu.collect_paths( ['+:src/**', '+:src/ext'] ).count { |dir| dir.end_with?( 'ext' ) }).to eq( 1 )
315
+ end
316
+
317
+ # An #include or a test name spells a place below a root through the root as listed,
318
+ # as the compiler and the command line do
319
+ it 'spells a symlinked directory below an entry\'s base as it is listed', **POSIX_ONLY_COLLECTION do
320
+ link_ext
321
+
322
+ expect(@fpcu.collect_paths( ['+:src/**'] )).to include( 'src/ext' )
323
+ expect(@fpcu.revise_filelist( ['src/ext/foo.c'], ['+:src/a/*.c'] ).to_a).to eq( ['src/ext/foo.c', 'src/a/bar.c'] )
324
+ end
325
+
326
+ it 'spells an entry\'s symlinked base as it really is', **POSIX_ONLY_COLLECTION do
327
+ File.symlink( File.join( @ws, 'vendor' ), File.join( @ws, 'app/vend' ) )
328
+
329
+ expect(@fpcu.collect_paths( ['+:vend/**'] )).to eq( ['../vendor', '../vendor/ext'] )
330
+ end
331
+
332
+ it 'removes a file below a symlinked directory that a `-:` revision names', **POSIX_ONLY_COLLECTION do
333
+ link_ext
334
+
335
+ expect(@fpcu.revise_filelist( ['src/a/bar.c', 'src/ext/foo.c'], ['-:src/ext/foo.c'] ).to_a).to eq( ['src/a/bar.c'] )
336
+ expect(@fpcu.revise_filelist( ['src/ext/foo.c'], ['+:src/ext/foo.c'] ).to_a.size).to eq( 1 )
337
+ end
338
+
339
+ # macOS spells /tmp as /private/tmp, and a build root may be written through any symlink
340
+ it 'excludes a build root written through a symlink', **POSIX_ONLY_COLLECTION do
341
+ FileUtils.mkdir_p( 'build/test' )
342
+ File.symlink( File.join( @ws, 'app' ), File.join( @ws, 'link' ) )
343
+
344
+ expect(@fpcu.collect_paths( ['+:./**'], exclude: [File.join( @ws, 'link/build' )] )).to eq( ['.', 'src', 'src/a'] )
345
+ end
346
+
347
+ it 'keeps a directory an additive entry names below a `-:` entry, and leaves out one both name' do
348
+ FileUtils.mkdir_p( ['src/legacy/keep', 'src/legacy/other'] )
349
+
350
+ expect(@fpcu.collect_paths( ['+:src/**', '-:src/legacy', '+:src/legacy/keep'] )).to eq( ['src', 'src/a', 'src/legacy/keep'] )
351
+ expect(@fpcu.collect_paths( ['+:src/**', '+:src/legacy', '-:src/legacy'] )).to eq( ['src', 'src/a'] )
352
+ end
353
+
354
+ it 'leaves out the files a revision glob matches below an excluded root, but keeps those a revision based there names' do
355
+ FileUtils.mkdir_p( ['build/gen', 'build/test/mocks'] )
356
+ File.write( 'build/gen/table.c', '' )
357
+ File.write( 'build/test/mocks/mock_x.c', '' )
358
+
359
+ expect(@fpcu.revise_filelist( ['src/a/bar.c'], ['+:**/*.c'], exclude: ['build'] ).to_a).to eq( ['src/a/bar.c'] )
360
+ expect(@fpcu.revise_filelist( [], ['+:build/gen/table.c'], exclude: ['build'] ).to_a).to eq( ['build/gen/table.c'] )
361
+ expect(@fpcu.revise_filelist( [], ['+:build/**/*.c'], exclude: ['build'] ).to_a).to eq( ['build/gen/table.c', 'build/test/mocks/mock_x.c'] )
362
+ end
363
+
364
+ it 'adds no file below a reserved directory, however a revision names it' do
365
+ FileUtils.mkdir_p( ['build/gen', 'build/test/mocks'] )
366
+ File.write( 'build/gen/table.c', '' )
367
+ File.write( 'build/test/mocks/mock_x.c', '' )
368
+
369
+ expect(@fpcu.revise_filelist( [], ['+:build/**/*.c'], exclude: ['build'], reserved: ['build/test'] ).to_a).to eq( ['build/gen/table.c'] )
370
+ expect(@fpcu.revise_filelist( [], ['+:build/test/mocks/mock_x.c'], reserved: ['build/test'] ).to_a).to be_empty
371
+ end
372
+
373
+ it 'collects what an entry based below an excluded root names, less any reserved directory' do
374
+ FileUtils.mkdir_p( ['build/gen/sub', 'build/test/mocks'] )
375
+
376
+ expect(@fpcu.collect_paths( ['+:build/**'], exclude: ['build'], reserved: ['build/test'] )).to contain_exactly( 'build', 'build/gen', 'build/gen/sub' )
377
+ expect(@fpcu.collect_paths( ['+:build/test/mocks'], exclude: ['build'], reserved: ['build/test'] )).to be_empty
378
+ expect(@fpcu.collect_paths( ['+:./**'], exclude: ['build'], reserved: ['build/test'] )).to_not include( 'build/gen' )
379
+ end
380
+
381
+ # Only the entries that name a directory decide whether it is kept
382
+ it 'judges each directory by the entries that name it, not every entry\'s base' do
383
+ FileUtils.mkdir_p( ['src/legacy/x/inc', 'src/legacy/keep/sub'] )
384
+
385
+ expect(@fpcu.collect_paths( ['+:src/**', '+:src/legacy/*/inc', '-:src'] )).to eq( ['src/legacy/x/inc'] )
386
+ expect(@fpcu.collect_paths( ['+:src/**', '+:src/legacy/keep', '-:src/legacy'] )).to contain_exactly( 'src', 'src/a', 'src/legacy/keep' )
387
+ end
388
+
389
+ it 'leaves out many directories below a `-:` entry quickly' do
390
+ 200.times { |index| FileUtils.mkdir_p( "vendor_tree/d#{index}/x" ) }
391
+ 200.times { |index| FileUtils.mkdir_p( "src/m#{index}" ) }
392
+
393
+ started = Process.clock_gettime( Process::CLOCK_MONOTONIC )
394
+ result = @fpcu.collect_paths( ['+:./**', '-:vendor_tree/**'] )
395
+
396
+ expect(result).to_not include( 'vendor_tree' )
397
+ expect(Process.clock_gettime( Process::CLOCK_MONOTONIC ) - started).to be < 2.0
398
+ end
399
+
400
+ describe '#collect_mirror_roots' do
401
+ it 'reads a literal bracket in an entry with a glob as part of a name (#104)' do
402
+ FileUtils.mkdir_p( 'lib[v2]/x/inc' )
403
+
404
+ expect(@fpcu.collect_mirror_roots( ['lib[v2]/*/inc'] )).to eq( ['lib[v2]/x/inc'] )
405
+ end
406
+
407
+ it 'roots a doubled recursive glob at its base' do
408
+ expect(@fpcu.collect_mirror_roots( ['src/**/**'] )).to eq( ['src'] )
409
+ end
410
+ end
411
+ end