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
@@ -0,0 +1,101 @@
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
+ # How configuration takes in the paths a project file names, before any build: where each
9
+ # lands, and which a build can't use safely. Each example names the gap it probes (the path
10
+ # handling audit around GH #1370). Where the right behavior is a policy still to be chosen,
11
+ # the example states the one it assumes.
12
+
13
+ require 'spec_helper'
14
+ require 'configurator_integration_helper'
15
+
16
+ describe 'Path intake (integration)' do
17
+ include ConfiguratorIntegrationHelpers
18
+
19
+ def yaml(project: {}, paths: {}, extra: '')
20
+ project_lines = { build_root: 'build' }.merge( project ).map { |key, value| " :#{key}: #{value.inspect}" }.join( "\n" )
21
+ path_lines = { test: ['test/**'], source: ['src/**'], include: ['src/**'] }.merge( paths ).map { |key, value| " :#{key}: #{value.inspect}" }.join( "\n" )
22
+ return ":project:\n#{project_lines}\n :use_test_preprocessor: :none\n:paths:\n#{path_lines}\n#{extra}"
23
+ end
24
+
25
+ # Clobber empties the build root's test and release trees, so a build root holding the
26
+ # whole project is refused. An empty build root is left to the unit specs: it names the
27
+ # filesystem root, where a configuration that got past it would create directories.
28
+ { '.' => 'the project directory', '..' => 'a directory above the project' }.each do |root, what|
29
+ it "G16: refuses a build root that is #{what}" do
30
+ in_temp_project( minimal_project_files ) do
31
+ expect { configure( yaml( project: { build_root: root } ) ) }.to raise_error( CeedlingException )
32
+ expect(logged.join( "\n" )).to match( /build_root.*'#{Regexp.escape( root )}'/ )
33
+ end
34
+ end
35
+ end
36
+
37
+ it 'G16: accepts a build root beside the project\'s own directories' do
38
+ in_temp_project( minimal_project_files ) do
39
+ expect { configure( yaml( project: { build_root: 'out/build' } ) ) }.to_not raise_error
40
+ end
41
+ end
42
+
43
+ it 'G16: accepts a build root holding sources, warning of the entry' do
44
+ in_temp_project( minimal_project_files ) do
45
+ expect { configure( yaml( project: { build_root: 'src' } ) ) }.to_not raise_error
46
+ expect(logged.join( "\n" )).to match( %r{:paths ↳ :source => 'src/\*\*' lies in the build root 'src'} )
47
+ end
48
+ end
49
+
50
+ # A `-:` entry names what a build leaves out, and nothing there to leave out is no error.
51
+ # Below the build root, where nothing exists before a first build, it is not worth a word.
52
+ it 'G24: accepts a `-:` path below the build root that names nothing, silently' do
53
+ in_temp_project( minimal_project_files ) do
54
+ configure( yaml( paths: { include: ['src/**', '-:build/gen'] } ) )
55
+
56
+ expect(logged.join( "\n" )).to_not match( %r{build/gen} )
57
+ end
58
+ end
59
+
60
+ it 'G24: accepts a `-:` path elsewhere that names nothing, warning of it' do
61
+ in_temp_project( minimal_project_files ) do
62
+ configure( yaml( paths: { include: ['src/**', '-:src/gone'] } ) )
63
+
64
+ expect(logged.join( "\n" )).to match( %r{'-:src/gone' leaves out nothing} )
65
+ end
66
+ end
67
+
68
+ it "G18: includes a Unity helper written with backslashes in every mock by its own filename" do
69
+ files = minimal_project_files.merge( 'test/support/helper.h' => "\n" )
70
+ in_temp_project( files ) do
71
+ config = configure( yaml( extra: ":cmock:\n :unity_helper_path: 'test\\support\\helper.h'\n" ) )[:configurator]
72
+
73
+ expect(config.cmock_includes).to include( 'helper.h' )
74
+ end
75
+ end
76
+
77
+ # Assumes an absolute release output names the release itself
78
+ it 'G19: places a release named by an absolute :output where it names' do
79
+ in_temp_project( minimal_project_files ) do |dir|
80
+ output = File.join( File.realpath( dir ), 'out', 'app.elf' )
81
+ config = configure( yaml( project: { release_build: true }, extra: ":release_build:\n :output: #{output}\n" ) )[:configurator]
82
+
83
+ expect(config.project_release_build_target).to eq( output )
84
+ end
85
+ end
86
+
87
+ # Assumes `~` means the home directory, as it does in a shell
88
+ it 'G21: reads a `~` path as below the home directory' do
89
+ in_temp_project( minimal_project_files.merge( 'home/inc/extra.h' => "\n" ) ) do |dir|
90
+ home = ENV['HOME']
91
+ begin
92
+ ENV['HOME'] = File.join( File.realpath( dir ), 'home' )
93
+ config = configure( yaml( paths: { include: ['src/**', '~/inc'] } ) )[:configurator]
94
+
95
+ expect(config.collection_all_headers.map { |header| File.basename( header ) }).to include( 'extra.h' )
96
+ ensure
97
+ ENV['HOME'] = home
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,322 @@
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
+ # Path handling across project layouts and the spellings a project file may use for one
9
+ # place: through configuration, collection, build-path mapping, and the lookups that invert
10
+ # it. Every build file must land inside the build root, distinct inputs must stay distinct,
11
+ # and every spelling of one layout must place files alike.
12
+ #
13
+ # Each example names the gap it probes (GH #1370 and the path handling audit around it).
14
+
15
+ require 'spec_helper'
16
+ require 'path_scenario_harness'
17
+
18
+ describe 'Path scenarios (integration)' do
19
+ include PathScenarioHarness
20
+
21
+ TEST_C = "#include \"unity.h\"\nvoid setUp(void) {}\nvoid tearDown(void) {}\nvoid test_it(void) {}\n"
22
+
23
+ def c(name)
24
+ "int #{name}(void) { return 0; }\n"
25
+ end
26
+
27
+ def h(name)
28
+ "int #{name}(void);\n"
29
+ end
30
+
31
+ # --- Baseline -------------------------------------------------------------------------
32
+
33
+ it 'baseline: mirrors same-named files below one plain root, inside the build root' do
34
+ files = {
35
+ 'app/src/a/util.c' => c( 'a' ), 'app/src/b/util.c' => c( 'b' ),
36
+ 'app/src/a/util.h' => h( 'a' ), 'app/src/b/util.h' => h( 'b' ),
37
+ 'app/test/test_u.c' => TEST_C
38
+ }
39
+ in_workspace( files ) do |ws, app|
40
+ scene = scenario( scenario_yaml( ws: ws, app: app, paths: { test: ['test/**'], source: ['src/**'], include: ['src/**'] } ) )
41
+ placed = placements( scene )
42
+
43
+ placed.each_value { |paths| expect_inside_build( paths ) }
44
+ expect_distinct( placed[:test_objects], 'test objects' )
45
+ expect_distinct( placed[:release_objects], 'release objects' )
46
+ expect_distinct( placed[:preprocessed], 'preprocessed headers' )
47
+ end
48
+ end
49
+
50
+ # --- G1 (#1370): a `..` root beside a header outside the project ------------------------
51
+
52
+ # The report's vendor SDK sat at its drive's root, far above the project, so its headers'
53
+ # paths climb past the `..` root. A sibling one level up is truly below `..` and mirrors.
54
+ context 'G1 (#1370): a `..` root beside an include path far outside the project' do
55
+ def files
56
+ {
57
+ 'app/src/main.c' => c( 'main' ), 'app/src/main.h' => h( 'main' ),
58
+ 'app/test/test_m.c' => TEST_C,
59
+ 'vendor/inc/cmsis.h' => h( 'cmsis' )
60
+ }
61
+ end
62
+
63
+ [
64
+ ['an absolute vendor path far outside', '%{far}/inc'],
65
+ ['a sibling vendor path', '../vendor/inc']
66
+ ].each do |label, vendor|
67
+ it "places a mock of the vendor header inside the build root, with #{label}" do
68
+ in_workspace( files, far: { 'inc/sdk.h' => h( 'sdk' ) } ) do |ws, app|
69
+ scene = scenario( scenario_yaml( ws: ws, app: app, paths: { test: ['test/**'], source: ['src/**'], include: ['../**', vendor] } ) )
70
+ placed = placements( scene )
71
+
72
+ expect_inside_build( placed[:mocks] )
73
+ expect_inside_build( placed[:preprocessed] )
74
+ expect_inside_build( placed[:test_objects] )
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ # --- G2: equivalent spellings of one root ------------------------------------------------
81
+
82
+ context 'G2: one layout under each spelling of its roots' do
83
+ def files
84
+ {
85
+ 'app/src/a/util.c' => c( 'a' ), 'app/src/b/util.c' => c( 'b' ),
86
+ 'app/src/a/util.h' => h( 'a' ), 'app/src/b/util.h' => h( 'b' ),
87
+ 'app/test/test_u.c' => TEST_C
88
+ }
89
+ end
90
+
91
+ def placed_with(spelling, ws, app)
92
+ spell = PathScenarioHarness::ROOT_SPELLINGS[spelling]
93
+ roots = ->(root) { [spell.call( root, ws: ws, app: app )] }
94
+ scene = scenario( scenario_yaml( ws: ws, app: app, paths: { test: roots.( 'test/**' ), source: roots.( 'src/**' ), include: roots.( 'src/**' ) } ) )
95
+ return [scene, relative_placements( scene )]
96
+ end
97
+
98
+ (PathScenarioHarness::ROOT_SPELLINGS.keys - [:plain]).each do |spelling|
99
+ it "places every build file as the plain spelling does, with #{spelling} roots" do
100
+ in_workspace( files, symlink: spelling == :symlink ) do |ws, app|
101
+ _, plain = placed_with( :plain, ws, app )
102
+ scene, spelled = placed_with( spelling, ws, app )
103
+
104
+ expect_one_spelling_each( scene[:configurator] )
105
+ expect(spelled).to eq( plain )
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ # --- G3 and G13: exclusions ---------------------------------------------------------------
112
+
113
+ context 'G3 and G13: a `-:` exclusion' do
114
+ def files
115
+ {
116
+ 'app/src/sub/x.c' => c( 'x1' ), 'app/src/exp/sub/x.c' => c( 'x2' ), 'app/src/exp/deep/y.c' => c( 'y' ),
117
+ 'app/src/main.h' => h( 'main' ), 'app/test/test_m.c' => TEST_C
118
+ }
119
+ end
120
+
121
+ it 'G13: excludes the whole subtree it names' do
122
+ in_workspace( files ) do |ws, app|
123
+ scene = scenario( scenario_yaml( ws: ws, app: app, paths: { test: ['test/**'], source: ['+:src/**', '-:src/exp'], include: ['src/**'] } ) )
124
+
125
+ expect(scene[:configurator].collection_all_source).to match_array( %w[src/sub/x.c] )
126
+ end
127
+ end
128
+
129
+ # A file the exclusion fails to drop (G13) must not mirror below the exclusion itself
130
+ it 'G3: is never a root that kept files mirror below' do
131
+ in_workspace( files ) do |ws, app|
132
+ scene = scenario( scenario_yaml( ws: ws, app: app, paths: { test: ['test/**'], source: ['+:src/**', '-:src/exp'], include: ['src/**'] } ) )
133
+ placed = placements( scene )
134
+
135
+ expect_distinct( placed[:release_objects], 'release objects' )
136
+ end
137
+ end
138
+ end
139
+
140
+ # --- G4: header-to-source correlation ---------------------------------------------------
141
+
142
+ context 'G4: a header found beside its source by convention' do
143
+ def files
144
+ {
145
+ 'app/include/drv/x.h' => h( 'x' ), 'app/src/drv/x.c' => c( 'x' ),
146
+ 'app/components/foo/include/foo.h' => h( 'foo' ), 'app/components/foo/src/foo.c' => c( 'foo' ),
147
+ 'app/test/test_x.c' => TEST_C
148
+ }
149
+ end
150
+
151
+ {
152
+ 'a plain include root' => ['include/**'],
153
+ 'a `./` include root' => ['./include/**'],
154
+ 'an absolute include root' => ['%{app}/include/**']
155
+ }.each do |label, include|
156
+ it "finds the source of a header below #{label}" do
157
+ in_workspace( files ) do |ws, app|
158
+ scene = scenario( scenario_yaml( ws: ws, app: app, paths: { test: ['test/**'], source: ['src/**'], include: include } ) )
159
+
160
+ expect(scene[:finder].find_build_input_file( filepath: 'include/drv/x.h', complain: :ignore, context: :test )).to eq( 'src/drv/x.c' )
161
+ end
162
+ end
163
+ end
164
+
165
+ it 'finds the source of a header below a glob-middle include root' do
166
+ in_workspace( files ) do |ws, app|
167
+ scene = scenario( scenario_yaml( ws: ws, app: app, paths: { test: ['test/**'], source: ['components/*/src'], include: ['components/*/include'] } ) )
168
+
169
+ expect(scene[:finder].find_build_input_file( filepath: 'components/foo/include/foo.h', complain: :ignore, context: :test )).to eq( 'components/foo/src/foo.c' )
170
+ end
171
+ end
172
+ end
173
+
174
+ # --- G5 and G6: collisions --------------------------------------------------------------
175
+
176
+ context 'G5: the same relative path below two roots' do
177
+ it 'gives each source its own release object' do
178
+ files = { 'app/src/foo.c' => c( 'a' ), 'app/lib/foo.c' => c( 'b' ), 'app/src/foo.h' => h( 'a' ), 'app/test/test_f.c' => TEST_C }
179
+ in_workspace( files ) do |ws, app|
180
+ scene = scenario( scenario_yaml( ws: ws, app: app, paths: { test: ['test/**'], source: ['src/**', 'lib/**'], include: ['src/**'] } ) )
181
+
182
+ expect_distinct( placements( scene )[:release_objects], 'release objects' )
183
+ end
184
+ end
185
+ end
186
+
187
+ context 'G6: nested roots' do
188
+ it 'gives a file in the inner root and one in the outer root their own objects' do
189
+ files = { 'app/src/util.c' => c( 'a' ), 'app/src/legacy/util.c' => c( 'b' ), 'app/src/util.h' => h( 'a' ), 'app/test/test_u.c' => TEST_C }
190
+ in_workspace( files ) do |ws, app|
191
+ scene = scenario( scenario_yaml( ws: ws, app: app, paths: { test: ['test/**'], source: ['src/**', 'src/legacy/**'], include: ['src/**'] } ) )
192
+ placed = placements( scene )
193
+
194
+ expect_distinct( placed[:test_objects], 'test objects' )
195
+ expect_distinct( placed[:release_objects], 'release objects' )
196
+ end
197
+ end
198
+ end
199
+
200
+ # --- G8: outside every root -------------------------------------------------------------
201
+
202
+ context 'G8: sources reached only through :files' do
203
+ it 'gives same-named files outside every root their own objects' do
204
+ files = { 'app/src/main.c' => c( 'main' ), 'app/src/main.h' => h( 'main' ), 'app/a/util.c' => c( 'a' ), 'app/b/util.c' => c( 'b' ), 'app/test/test_m.c' => TEST_C }
205
+ in_workspace( files ) do |ws, app|
206
+ scene = scenario( scenario_yaml( ws: ws, app: app, paths: { test: ['test/**'], source: ['src/**'], include: ['src/**'] }, files: { source: ['a/util.c', 'b/util.c'] } ) )
207
+
208
+ expect_distinct( placements( scene )[:release_objects], 'release objects' )
209
+ end
210
+ end
211
+ end
212
+
213
+ # --- Sources outside the project --------------------------------------------------------
214
+
215
+ context 'sources and headers in a sibling directory' do
216
+ it 'places their build files inside the build root, each its own' do
217
+ files = {
218
+ 'shared/src/a/util.c' => c( 'a' ), 'shared/src/b/util.c' => c( 'b' ),
219
+ 'shared/src/a/util.h' => h( 'a' ), 'shared/src/b/util.h' => h( 'b' ),
220
+ 'app/test/test_u.c' => TEST_C
221
+ }
222
+ in_workspace( files ) do |ws, app|
223
+ scene = scenario( scenario_yaml( ws: ws, app: app, paths: { test: ['test/**'], source: ['../shared/src/**'], include: ['../shared/src/**'] } ) )
224
+ placed = placements( scene )
225
+
226
+ placed.each_value { |paths| expect_inside_build( paths ) }
227
+ expect_distinct( placed[:release_objects], 'release objects' )
228
+ end
229
+ end
230
+ end
231
+
232
+ # --- G15: a root above the build root --------------------------------------------------
233
+
234
+ context 'G15: a root that holds the build root' do
235
+ it 'collects nothing from an existing build directory' do
236
+ files = {
237
+ 'app/src/main.c' => c( 'main' ), 'app/src/main.h' => h( 'main' ), 'app/test/test_m.c' => TEST_C,
238
+ 'app/build/test/mocks/test/test_m/mock_main.h' => h( 'mock' ), 'app/build/test/runners/test/test_m/test_m_runner.c' => c( 'r' )
239
+ }
240
+ in_workspace( files ) do |ws, app|
241
+ scene = scenario( scenario_yaml( ws: ws, app: app, paths: { test: ['test/**'], source: ['src/**'], include: ['../**'] } ) )
242
+
243
+ expect_collections_outside_build( scene[:configurator] )
244
+ end
245
+ end
246
+
247
+ # A release compiles every collected source, so generated mocks and runners broke it
248
+ it 'collects nothing from an existing build directory through a `:files` glob' do
249
+ files = {
250
+ 'app/src/main.c' => c( 'main' ), 'app/src/main.h' => h( 'main' ), 'app/test/test_m.c' => TEST_C,
251
+ 'app/build/test/mocks/test/test_m/mock_main.c' => c( 'mock' ), 'app/build/vendor/unity/src/unity.c' => c( 'unity' )
252
+ }
253
+ in_workspace( files ) do |ws, app|
254
+ yaml = scenario_yaml( ws: ws, app: app, paths: { test: ['test/**'], source: ['src/**'], include: ['src/**'] }, files: { source: ['+:**/*.c'] } )
255
+ scene = scenario( yaml )
256
+
257
+ expect_collections_outside_build( scene[:configurator] )
258
+ expect(scene[:configurator].collection_release_build_input.to_a.grep( %r{\Abuild/} )).to be_empty
259
+ expect(scene[:configurator].collection_existing_test_build_input.to_a.grep( %r{\Abuild/test/} )).to be_empty
260
+ end
261
+ end
262
+
263
+ # Generated code may sit in the build root, beside the directories Ceedling creates there
264
+ it 'collects generated code an entry names in the build root, but nothing Ceedling created there' do
265
+ files = {
266
+ 'app/src/main.c' => c( 'main' ), 'app/src/main.h' => h( 'main' ), 'app/test/test_m.c' => TEST_C,
267
+ 'app/build/gen/table.c' => c( 'table' ), 'app/build/gen/table.h' => h( 'table' ),
268
+ 'app/build/test/mocks/test/test_m/mock_main.c' => c( 'mock' ), 'app/build/test/mocks/test/test_m/mock_main.h' => h( 'mock' )
269
+ }
270
+ in_workspace( files ) do |ws, app|
271
+ paths = { test: ['test/**'], source: ['src/**', 'build/**'], include: ['src/**', 'build/gen'] }
272
+ yaml = scenario_yaml( ws: ws, app: app, paths: paths, files: { source: ['+:build/**/*.c'] } )
273
+ scene = scenario( yaml )
274
+
275
+ expect(scene[:configurator].collection_all_source.to_a).to contain_exactly( 'src/main.c', 'build/gen/table.c' )
276
+ expect(scene[:configurator].collection_all_headers.to_a).to include( 'build/gen/table.h' )
277
+ expect(scene[:configurator].collection_all_headers.to_a.grep( %r{\Abuild/test/} )).to be_empty
278
+ end
279
+ end
280
+ end
281
+
282
+ # --- Test files kept out of the sources and the release --------------------------------
283
+
284
+ context 'test files among the sources' do
285
+ # A release compiles every source it collects, and a test file holds a second main()
286
+ it 'collects no test file into the sources or the release through a `:files` glob' do
287
+ files = { 'app/src/main.c' => c( 'main' ), 'app/src/main.h' => h( 'main' ), 'app/test/test_m.c' => TEST_C }
288
+ in_workspace( files ) do |ws, app|
289
+ yaml = scenario_yaml( ws: ws, app: app, paths: { test: ['test/**'], source: ['src/**'], include: ['src/**'] }, files: { source: ['+:**/*.c'] } )
290
+ scene = scenario( yaml )
291
+
292
+ expect(scene[:configurator].collection_all_tests.to_a).to eq( ['test/test_m.c'] )
293
+ expect(scene[:configurator].collection_all_source.to_a).to eq( ['src/main.c'] )
294
+ expect(scene[:configurator].collection_release_build_input.to_a).to eq( ['src/main.c'] )
295
+ end
296
+ end
297
+
298
+ # A test root beside the sources reaches the release through :paths as well
299
+ it 'collects no test file into the release from a source root that holds the tests' do
300
+ files = { 'app/src/main.c' => c( 'main' ), 'app/src/main.h' => h( 'main' ), 'app/src/test/test_m.c' => TEST_C }
301
+ in_workspace( files ) do |ws, app|
302
+ scene = scenario( scenario_yaml( ws: ws, app: app, paths: { test: ['src/test/**'], source: ['src/**'], include: ['src/**'] } ) )
303
+
304
+ expect(scene[:configurator].collection_release_build_input.to_a).to eq( ['src/main.c'] )
305
+ end
306
+ end
307
+
308
+ end
309
+
310
+ # --- G12: the working directory reached through a symlink -------------------------------
311
+
312
+ context 'G12: absolute roots spelled through a symlink to the project' do
313
+ it 'collects each file once' do
314
+ files = { 'app/src/main.c' => c( 'main' ), 'app/src/main.h' => h( 'main' ), 'app/test/test_m.c' => TEST_C }
315
+ in_workspace( files, symlink: true ) do |ws, app|
316
+ scene = scenario( scenario_yaml( ws: ws, app: app, paths: { test: ['test/**'], source: ['src/**', '%{ws}/link/src/**'], include: ['src/**'] } ) )
317
+
318
+ expect_one_spelling_each( scene[:configurator] )
319
+ end
320
+ end
321
+ end
322
+ end
@@ -24,7 +24,7 @@ require 'spec_integration_helper'
24
24
  require 'fileutils'
25
25
  require 'tmpdir'
26
26
  require 'open3'
27
- require 'ceedling/file_wrapper'
27
+ require 'ceedling/filesystem/file_wrapper'
28
28
  require 'ceedling/quote_include_isolator'
29
29
  require 'ceedling/constants'
30
30
 
@@ -16,7 +16,7 @@ require 'open3'
16
16
  require 'json'
17
17
  require 'ceedling/constants'
18
18
  require 'ceedling/plugins/plugin'
19
- require 'ceedling/file_wrapper'
19
+ require 'ceedling/filesystem/file_wrapper'
20
20
 
21
21
  $LOAD_PATH.unshift( File.expand_path( '../../plugins/report_build_warnings_log/lib', __dir__ ) )
22
22
 
@@ -16,7 +16,7 @@ require 'spec_helper'
16
16
  require 'json'
17
17
  require 'tmpdir'
18
18
  require 'rexml/document'
19
- require 'ceedling/file_wrapper'
19
+ require 'ceedling/filesystem/file_wrapper'
20
20
  require 'ceedling/config/config_walkinator'
21
21
 
22
22
  $: << File.expand_path('../../../plugins/report_tests_log_factory/lib', __FILE__)
@@ -14,7 +14,7 @@ require 'spec_integration_helper'
14
14
  require 'open3'
15
15
  require 'tmpdir'
16
16
  require 'ceedling/constants'
17
- require 'ceedling/file_wrapper'
17
+ require 'ceedling/filesystem/file_wrapper'
18
18
  require 'ceedling/generators/generator'
19
19
  require 'ceedling/system_wrapper'
20
20
  require 'fileutils'
@@ -140,7 +140,8 @@ describe 'Test file #include extraction (integration)' do
140
140
 
141
141
  extract_in_mode( tree, 'test_reader.c' ) do |harness, testable|
142
142
  expect(harness.mocks( 'test/test_reader.c' )).to eq( ['mock_widget.h'] )
143
- expect(testable.mock_search_paths).to eq( ['build/mocks/test_reader/drivers'] )
143
+ # The macro's #include resolves as `drivers/mock_widget.h`, reached from the mock root
144
+ expect(testable.mock_search_paths).to eq( ['build/mocks/test_reader/drivers', 'build/mocks/test_reader'] )
144
145
  end
145
146
  end
146
147