ceedling 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (383) hide show
  1. checksums.yaml +4 -4
  2. data/GIT_COMMIT_SHA +1 -1
  3. data/README.md +57 -58
  4. data/Rakefile +5 -3
  5. data/assets/features/ceedling +1 -1
  6. data/assets/fixtures/test_source_file_duplicate_basenames/test_calc_typo.c +14 -0
  7. data/assets/fixtures/tests_with_duplicate_test_names/test_foo.c +21 -0
  8. data/bin/actionator.rb +6 -12
  9. data/bin/app_cfg.rb +35 -1
  10. data/bin/ceedling +13 -3
  11. data/bin/cli.rb +25 -6
  12. data/bin/cli_handler.rb +119 -73
  13. data/bin/cli_helper.rb +56 -78
  14. data/bin/installation_selector.rb +81 -0
  15. data/bin/launchinator.rb +69 -0
  16. data/bin/mixin_resolvinator.rb +26 -36
  17. data/bin/objects.yml +13 -0
  18. data/bin/path_validator.rb +25 -29
  19. data/bin/versionator.rb +47 -48
  20. data/ceedling.gemspec +19 -15
  21. data/docs/BreakingChanges.md +70 -1
  22. data/docs/Changelog.md +84 -3
  23. data/docs/KnownIssues.md +90 -4
  24. data/docs/ReleaseNotes.md +13 -0
  25. data/docs/overrides/main.html +11 -8
  26. data/lib/ceedling/build_log_writer.rb +33 -0
  27. data/lib/ceedling/config/build_root_policy.rb +70 -0
  28. data/lib/ceedling/config/configurator.rb +19 -18
  29. data/lib/ceedling/config/configurator_builder.rb +58 -28
  30. data/lib/ceedling/config/configurator_paths_validator.rb +179 -0
  31. data/lib/ceedling/config/configurator_plugins.rb +1 -1
  32. data/lib/ceedling/config/configurator_setup.rb +39 -41
  33. data/lib/ceedling/config/configurator_validator.rb +1 -110
  34. data/lib/ceedling/defaults.rb +4 -4
  35. data/lib/ceedling/dependencies/README.md +18 -16
  36. data/lib/ceedling/dependencies/dependency_cache_store.rb +20 -3
  37. data/lib/ceedling/dependencies/dependency_hasher.rb +35 -1
  38. data/lib/ceedling/filename_extension.rb +7 -1
  39. data/lib/ceedling/filesystem/FILE_TREE.md +253 -0
  40. data/lib/ceedling/filesystem/PATH_HANDLING.md +304 -0
  41. data/lib/ceedling/filesystem/README.md +41 -0
  42. data/lib/ceedling/filesystem/build_subpath.rb +102 -0
  43. data/lib/ceedling/filesystem/destination_collision.rb +25 -0
  44. data/lib/ceedling/{file_finder.rb → filesystem/file_finder.rb} +159 -104
  45. data/lib/ceedling/{file_finder_helper.rb → filesystem/file_finder_helper.rb} +36 -31
  46. data/lib/ceedling/filesystem/file_path_collection_utils.rb +183 -0
  47. data/lib/ceedling/{file_path_utils.rb → filesystem/file_path_utils.rb} +108 -97
  48. data/lib/ceedling/{file_system_wrapper.rb → filesystem/file_system_wrapper.rb} +6 -5
  49. data/lib/ceedling/filesystem/file_tree.rb +241 -0
  50. data/lib/ceedling/{file_wrapper.rb → filesystem/file_wrapper.rb} +65 -28
  51. data/lib/ceedling/filesystem/path_exclusions.rb +30 -0
  52. data/lib/ceedling/{path_matcher.rb → filesystem/path_matcher.rb} +27 -23
  53. data/lib/ceedling/{path_mirror.rb → filesystem/path_mirror.rb} +19 -18
  54. data/lib/ceedling/filesystem/path_spellings.rb +55 -0
  55. data/lib/ceedling/filesystem/project_paths.rb +184 -0
  56. data/lib/ceedling/filesystem/working_dir.rb +40 -0
  57. data/lib/ceedling/generators/generator.rb +5 -6
  58. data/lib/ceedling/generators/generator_partials.rb +1 -1
  59. data/lib/ceedling/generators/generator_test_results_backtrace.rb +7 -7
  60. data/lib/ceedling/handoff.rb +121 -0
  61. data/lib/ceedling/includes/include_factory.rb +3 -3
  62. data/lib/ceedling/includes/include_pathinator.rb +67 -10
  63. data/lib/ceedling/includes/includes.rb +1 -1
  64. data/lib/ceedling/installation.rb +92 -0
  65. data/lib/ceedling/library_resolver.rb +99 -0
  66. data/lib/ceedling/objects.yml +23 -2
  67. data/lib/ceedling/partials/partializer.rb +4 -4
  68. data/lib/ceedling/partials/partializer_config.rb +3 -1
  69. data/lib/ceedling/plugins/report_log_writer_plugin.rb +3 -5
  70. data/lib/ceedling/preprocess/preprocessinator_file_assembler.rb +1 -1
  71. data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +1 -1
  72. data/lib/ceedling/preprocess/preprocessinator_line_marker_includes_extractor.rb +1 -1
  73. data/lib/ceedling/rake_app/rakefile_component_resolver.rb +10 -17
  74. data/lib/ceedling/rake_app/test_task_name.rb +29 -0
  75. data/lib/ceedling/rakefile.rb +16 -1
  76. data/lib/ceedling/rakefiles/base/tasks_base.rake +1 -1
  77. data/lib/ceedling/rakefiles/release/rules_release.rake +4 -7
  78. data/lib/ceedling/rakefiles/tests/rules_tests.rake +3 -20
  79. data/lib/ceedling/rakefiles/tests/rules_tests_generate.rake +4 -30
  80. data/lib/ceedling/release_invoker/release_build_executor.rb +33 -44
  81. data/lib/ceedling/release_invoker/release_build_planner.rb +14 -6
  82. data/lib/ceedling/reportinator.rb +3 -4
  83. data/lib/ceedling/setupinator.rb +95 -87
  84. data/lib/ceedling/test_context_extractor.rb +5 -1
  85. data/lib/ceedling/test_invoker/PIPELINE.md +9 -7
  86. data/lib/ceedling/test_invoker/mock_locator.rb +60 -2
  87. data/lib/ceedling/test_invoker/partials_manager.rb +12 -0
  88. data/lib/ceedling/test_invoker/standin_headerinator.rb +22 -13
  89. data/lib/ceedling/test_invoker/test_build_executor.rb +42 -41
  90. data/lib/ceedling/test_invoker/test_build_planner.rb +37 -18
  91. data/lib/ceedling/test_invoker/test_build_setup.rb +17 -12
  92. data/lib/ceedling/test_invoker/test_build_validations.rb +16 -31
  93. data/lib/ceedling/test_invoker/test_file_preprocessinator.rb +1 -1
  94. data/lib/ceedling/test_invoker/test_invoker.rb +6 -1
  95. data/lib/ceedling/test_invoker/test_pipeline_helpers.rb +1 -1
  96. data/lib/ceedling/test_invoker/test_source_file_directive_resolver.rb +40 -20
  97. data/lib/ceedling/tool_executor.rb +9 -3
  98. data/lib/ceedling/yaml_wrapper.rb +3 -2
  99. data/lib/version.rb +1 -1
  100. data/plugins/bullseye/bullseye.rake +6 -9
  101. data/plugins/bullseye/config/defaults_bullseye.rb +1 -1
  102. data/plugins/bullseye/lib/bullseye.rb +9 -7
  103. data/plugins/cppcheck/config/defaults_cppcheck.rb +1 -1
  104. data/plugins/dependencies/config/defaults.yml +6 -6
  105. data/plugins/dependencies/dependencies.rake +4 -1
  106. data/plugins/dependencies/lib/dependencies.rb +18 -8
  107. data/plugins/dependencies/lib/dependency_spec.rb +17 -5
  108. data/plugins/fff/lib/fff.rb +45 -39
  109. data/plugins/fff/spec/fff_cmock_wrapper_spec.rb +128 -0
  110. data/plugins/gcov/config/defaults_gcov.rb +1 -1
  111. data/plugins/gcov/gcov.rake +6 -9
  112. data/plugins/gcov/lib/console_reportinator.rb +4 -18
  113. data/plugins/gcov/lib/gcov.rb +9 -2
  114. data/plugins/gcov/lib/gcovr_reportinator.rb +8 -40
  115. data/plugins/gcov/lib/gcovr_thresholds.rb +74 -0
  116. data/plugins/gcov/lib/reportgenerator_reportinator.rb +1 -1
  117. data/plugins/module_generator/lib/module_generator.rb +3 -3
  118. data/plugins/report_tests_teamcity_stdout/lib/report_tests_teamcity_stdout.rb +53 -80
  119. data/plugins/sanitize/lib/sanitize_records.rb +3 -6
  120. data/plugins/sanitize/sanitize.rake +6 -6
  121. data/plugins/valgrind/lib/valgrind.rb +14 -7
  122. data/plugins/valgrind/valgrind.rake +6 -6
  123. data/site-local/404.html +6 -20
  124. data/site-local/assets/javascripts/{bundle.d7400e89.min.js → bundle.c21346df.min.js} +3 -3
  125. data/site-local/assets/javascripts/{bundle.d7400e89.min.js.map → bundle.c21346df.min.js.map} +2 -2
  126. data/site-local/assets/javascripts/workers/{search.2c215733.min.js → search.a7633da7.min.js} +2 -2
  127. data/site-local/assets/javascripts/workers/{search.2c215733.min.js.map → search.a7633da7.min.js.map} +1 -1
  128. data/site-local/assets/stylesheets/main.57a0d79d.min.css +6 -0
  129. data/site-local/assets/stylesheets/main.57a0d79d.min.css.map +1 -0
  130. data/site-local/assets/stylesheets/palette.fb2fffe2.min.css +4 -0
  131. data/site-local/assets/stylesheets/palette.fb2fffe2.min.css.map +1 -0
  132. data/site-local/configuration/environment-vars.html +6 -20
  133. data/site-local/configuration/global-collections.html +6 -20
  134. data/site-local/configuration/index.html +6 -20
  135. data/site-local/configuration/loading.html +6 -20
  136. data/site-local/configuration/mixins.html +10 -24
  137. data/site-local/configuration/parallel-builds.html +6 -20
  138. data/site-local/configuration/project-file.html +8 -22
  139. data/site-local/configuration/reference/cexception.html +6 -20
  140. data/site-local/configuration/reference/cmock.html +8 -21
  141. data/site-local/configuration/reference/defines.html +16 -30
  142. data/site-local/configuration/reference/environment.html +10 -24
  143. data/site-local/configuration/reference/extension.html +6 -20
  144. data/site-local/configuration/reference/files.html +22 -30
  145. data/site-local/configuration/reference/flags.html +13 -27
  146. data/site-local/configuration/reference/index.html +6 -20
  147. data/site-local/configuration/reference/libraries.html +27 -23
  148. data/site-local/configuration/reference/mixins.html +6 -20
  149. data/site-local/configuration/reference/partials.html +6 -20
  150. data/site-local/configuration/reference/paths.html +38 -29
  151. data/site-local/configuration/reference/plugins.html +6 -20
  152. data/site-local/configuration/reference/project.html +32 -36
  153. data/site-local/configuration/reference/release-build.html +8 -20
  154. data/site-local/configuration/reference/test-build.html +6 -20
  155. data/site-local/configuration/reference/test-runner.html +6 -20
  156. data/site-local/configuration/reference/tools.html +27 -32
  157. data/site-local/configuration/reference/unity.html +6 -20
  158. data/site-local/configuration/which-ceedling.html +383 -37
  159. data/site-local/development/index.html +6 -20
  160. data/site-local/development/plugins/configuration.html +6 -20
  161. data/site-local/development/plugins/index.html +6 -20
  162. data/site-local/development/plugins/plugin-subclass.html +54 -68
  163. data/site-local/development/plugins/rake-tasks.html +8 -22
  164. data/site-local/development/releases.html +84 -29
  165. data/site-local/development/workflow.html +62 -71
  166. data/site-local/getting-started/builds.html +8 -20
  167. data/site-local/getting-started/command-line.html +62 -24
  168. data/site-local/getting-started/example-projects/cipher-quest.html +6 -20
  169. data/site-local/getting-started/example-projects/index.html +6 -20
  170. data/site-local/getting-started/example-projects/temp-sensor.html +6 -20
  171. data/site-local/getting-started/example-projects/wondrous-forest.html +6 -20
  172. data/site-local/getting-started/index.html +6 -20
  173. data/site-local/getting-started/installation.html +6 -20
  174. data/site-local/getting-started/quick-start.html +6 -20
  175. data/site-local/help.html +6 -20
  176. data/site-local/index.html +11 -24
  177. data/site-local/overview/build-system.html +6 -20
  178. data/site-local/overview/index.html +6 -20
  179. data/site-local/overview/test-environments.html +6 -20
  180. data/site-local/overview/tools-and-frameworks.html +6 -20
  181. data/site-local/plugins/beep.html +6 -20
  182. data/site-local/plugins/bullseye/index.html +13 -24
  183. data/site-local/plugins/bullseye/licensing.html +14 -28
  184. data/site-local/plugins/command-hooks.html +8 -22
  185. data/site-local/plugins/compile-commands-json-db.html +6 -20
  186. data/site-local/plugins/cppcheck.html +8 -22
  187. data/site-local/plugins/dependencies.html +16 -27
  188. data/site-local/plugins/fff.html +49 -32
  189. data/site-local/plugins/gcov/examples.html +7 -21
  190. data/site-local/plugins/gcov/gcovr.html +22 -33
  191. data/site-local/plugins/gcov/index.html +6 -20
  192. data/site-local/plugins/gcov/overview.html +9 -20
  193. data/site-local/plugins/gcov/reportgenerator.html +9 -23
  194. data/site-local/plugins/gcov/reporting.html +6 -20
  195. data/site-local/plugins/gcov/setup.html +8 -21
  196. data/site-local/plugins/gcov/tool-versions.html +6 -20
  197. data/site-local/plugins/gcov/troubleshooting.html +10 -24
  198. data/site-local/plugins/index.html +6 -20
  199. data/site-local/plugins/module-generator.html +21 -35
  200. data/site-local/plugins/report-build-warnings-log.html +53 -67
  201. data/site-local/plugins/report-tests-gtestlike-stdout.html +6 -20
  202. data/site-local/plugins/report-tests-ide-stdout.html +43 -57
  203. data/site-local/plugins/report-tests-log-factory.html +50 -64
  204. data/site-local/plugins/report-tests-pretty-stdout.html +7 -21
  205. data/site-local/plugins/report-tests-raw-output-log.html +9 -21
  206. data/site-local/plugins/report-tests-teamcity-stdout.html +20 -31
  207. data/site-local/plugins/sanitize.html +56 -65
  208. data/site-local/plugins/valgrind.html +49 -58
  209. data/site-local/project/index.html +6 -20
  210. data/site-local/project/release-history.html +6 -20
  211. data/site-local/project/sbom.html +6 -20
  212. data/site-local/project/upgrade.html +9 -20
  213. data/site-local/reference/build-directives.html +6 -20
  214. data/site-local/reference/command-line.html +15 -22
  215. data/site-local/reference/environment-vars.html +6 -20
  216. data/site-local/reference/gcov-plugin.html +6 -20
  217. data/site-local/reference/global-collections.html +6 -20
  218. data/site-local/reference/index.html +6 -20
  219. data/site-local/reference/partials-macros.html +6 -20
  220. data/site-local/reference/project-configuration.html +6 -20
  221. data/site-local/sitemap.xml.gz +0 -0
  222. data/site-local/snapshot/examples/cipher_quest/index.html +16 -30
  223. data/site-local/snapshot/examples/temp_sensor/index.html +8 -22
  224. data/site-local/snapshot/examples/wondrous_forest/index.html +6 -20
  225. data/site-local/snapshot/plugins/bullseye/config/defaults_bullseye.rb +1 -1
  226. data/site-local/snapshot/plugins/gcov/config/defaults_gcov.rb +1 -1
  227. data/site-local/testing-guide/build-directives.html +14 -28
  228. data/site-local/testing-guide/conventions.html +66 -27
  229. data/site-local/testing-guide/frameworks.html +11 -25
  230. data/site-local/testing-guide/index.html +6 -20
  231. data/site-local/testing-guide/partials/configuration.html +8 -22
  232. data/site-local/testing-guide/partials/conventions.html +8 -22
  233. data/site-local/testing-guide/partials/directives.html +16 -30
  234. data/site-local/testing-guide/partials/example.html +14 -28
  235. data/site-local/testing-guide/partials/index.html +6 -20
  236. data/site-local/testing-guide/partials/overview.html +6 -20
  237. data/site-local/testing-guide/partials/variables.html +11 -25
  238. data/site-local/testing-guide/test-cases.html +13 -27
  239. data/site-local/testing-guide/test-sample.html +12 -26
  240. data/site-local/testing-guide/test-suite-anatomy.html +6 -20
  241. data/spec/integration/actionator_file_operations_spec.rb +91 -1
  242. data/spec/integration/compile_commands_json_db_spec.rb +1 -1
  243. data/spec/integration/configurator_pipeline_spec.rb +10 -0
  244. data/spec/integration/configurator_validation_spec.rb +1 -1
  245. data/spec/integration/dependencies/dependency_tracker_cache_hardening_spec.rb +26 -0
  246. data/spec/integration/dependencies/dependency_tracker_core_spec.rb +62 -0
  247. data/spec/integration/launcher_handoff_spec.rb +297 -0
  248. data/spec/integration/mixin_loading_spec.rb +1 -1
  249. data/spec/integration/module_generator_spec.rb +3 -3
  250. data/spec/integration/nested_test_artifacts_spec.rb +99 -0
  251. data/spec/integration/partials_module_resolution_spec.rb +7 -4
  252. data/spec/integration/path_intake_scenarios_spec.rb +101 -0
  253. data/spec/integration/path_scenarios_spec.rb +322 -0
  254. data/spec/integration/quote_include_isolator_spec.rb +1 -1
  255. data/spec/integration/rake_task_registry_plugins_spec.rb +95 -0
  256. data/spec/integration/report_build_warnings_log_spec.rb +1 -1
  257. data/spec/integration/report_tests_log_factory_spec.rb +1 -1
  258. data/spec/integration/test_executable_manifest_spec.rb +1 -1
  259. data/spec/integration/test_includes_extractinator_spec.rb +2 -1
  260. data/spec/integration/test_naming_spec.rb +198 -0
  261. data/spec/integration/test_source_file_checks_spec.rb +166 -0
  262. data/spec/integration/tool_validator_spec.rb +1 -1
  263. data/spec/integration/upgrade_docs_spec.rb +84 -0
  264. data/spec/integration/vendor_copy_transient_io_retry_spec.rb +192 -75
  265. data/spec/support/file_tree_helper.rb +88 -0
  266. data/spec/support/integration/configurator_integration_helper.rb +10 -7
  267. data/spec/support/integration/dependency_tracker_integration_helper.rb +1 -1
  268. data/spec/support/integration/partials_generation_integration_helper.rb +2 -2
  269. data/spec/support/integration/path_scenario_harness.rb +173 -0
  270. data/spec/support/integration/spec_integration_helper.rb +1 -1
  271. data/spec/support/integration/test_includes_harness.rb +13 -2
  272. data/spec/support/spec_helper.rb +2 -0
  273. data/spec/support/system/spec_system_helper.rb +7 -6
  274. data/spec/support/system/system_context.rb +21 -3
  275. data/spec/support/working_dir_tracking.rb +49 -0
  276. data/spec/system/build_output_mirroring_spec.rb +2 -2
  277. data/spec/system/conditional_mock_includes_spec.rb +1 -1
  278. data/spec/system/delta_builds_spec.rb +137 -11
  279. data/spec/system/gcov_deployment_spec.rb +1 -0
  280. data/spec/system/header_include_path_validation_spec.rb +4 -4
  281. data/spec/system/launcher_handoff_spec.rb +82 -0
  282. data/spec/system/mock_include_folders_spec.rb +91 -0
  283. data/spec/system/parent_directory_reference_spec.rb +5 -3
  284. data/spec/system/path_handling_probes_spec.rb +192 -0
  285. data/spec/system/preprocessing_parameterized_tests_spec.rb +1 -1
  286. data/spec/system/rake_release_task_path_disambiguation_spec.rb +6 -6
  287. data/spec/system/rake_test_task_path_disambiguation_spec.rb +85 -0
  288. data/spec/system/special_path_characters_spec.rb +86 -0
  289. data/spec/system/support/common_test_cases.rb +23 -21
  290. data/spec/system/support/gcov_common_test_cases.rb +37 -9
  291. data/spec/system/support/sanitize_common_test_cases.rb +2 -2
  292. data/spec/system/support/valgrind_common_test_cases.rb +12 -12
  293. data/spec/system/test_pipeline_stop_points_spec.rb +4 -4
  294. data/spec/system/test_runner_system_include_spec.rb +1 -1
  295. data/spec/system/test_source_file_directive_spec.rb +34 -5
  296. data/spec/units/bin/actionator_spec.rb +14 -17
  297. data/spec/units/bin/app_cfg_spec.rb +47 -0
  298. data/spec/units/bin/cli_handler_spec.rb +284 -7
  299. data/spec/units/bin/cli_helper_spec.rb +126 -45
  300. data/spec/units/bin/installation_selector_spec.rb +114 -0
  301. data/spec/units/bin/launchinator_spec.rb +171 -0
  302. data/spec/units/bin/path_validator_spec.rb +10 -0
  303. data/spec/units/bin/versionator_spec.rb +38 -6
  304. data/spec/units/build_log_writer_spec.rb +57 -0
  305. data/spec/units/config/build_root_policy_spec.rb +61 -0
  306. data/spec/units/config/configurator_builder_spec.rb +37 -6
  307. data/spec/units/config/configurator_paths_validator_spec.rb +292 -0
  308. data/spec/units/config/configurator_setup_spec.rb +65 -103
  309. data/spec/units/config/configurator_spec.rb +37 -6
  310. data/spec/units/config/configurator_validator_spec.rb +1 -172
  311. data/spec/units/default_tools_quoting_spec.rb +83 -0
  312. data/spec/units/dependencies/dependency_cache_store_spec.rb +33 -2
  313. data/spec/units/dependencies/dependency_debug_tree_spec.rb +1 -1
  314. data/spec/units/dependencies/dependency_hasher_spec.rb +42 -1
  315. data/spec/units/dependencies/dependency_path_normalizer_spec.rb +1 -1
  316. data/spec/units/dependencies/dependency_tracker_spec.rb +3 -1
  317. data/spec/units/filename_extension_spec.rb +11 -0
  318. data/spec/units/filesystem/build_subpath_spec.rb +122 -0
  319. data/spec/units/filesystem/destination_collision_spec.rb +30 -0
  320. data/spec/units/{file_finder_helper_spec.rb → filesystem/file_finder_helper_spec.rb} +46 -1
  321. data/spec/units/{file_finder_spec.rb → filesystem/file_finder_spec.rb} +188 -22
  322. data/spec/units/{file_path_collection_utils_spec.rb → filesystem/file_path_collection_utils_spec.rb} +181 -1
  323. data/spec/units/{file_path_utils_spec.rb → filesystem/file_path_utils_spec.rb} +204 -87
  324. data/spec/units/filesystem/file_system_wrapper_spec.rb +33 -0
  325. data/spec/units/filesystem/file_tree_spec.rb +511 -0
  326. data/spec/units/filesystem/file_tree_windows_spec.rb +78 -0
  327. data/spec/units/{file_wrapper_spec.rb → filesystem/file_wrapper_spec.rb} +154 -32
  328. data/spec/units/{path_matcher_spec.rb → filesystem/path_matcher_spec.rb} +12 -1
  329. data/spec/units/{path_mirror_spec.rb → filesystem/path_mirror_spec.rb} +32 -4
  330. data/spec/units/filesystem/project_paths_spec.rb +147 -0
  331. data/spec/units/filesystem/working_dir_spec.rb +71 -0
  332. data/spec/units/generators/generator_spec.rb +12 -6
  333. data/spec/units/generators/generator_test_results_backtrace_spec.rb +50 -46
  334. data/spec/units/generators/generator_test_results_spec.rb +1 -1
  335. data/spec/units/handoff_spec.rb +205 -0
  336. data/spec/units/includes/include_factory_spec.rb +3 -3
  337. data/spec/units/includes/include_pathinator_spec.rb +46 -0
  338. data/spec/units/installation_spec.rb +228 -0
  339. data/spec/units/library_resolver_spec.rb +157 -0
  340. data/spec/units/mock_locator_spec.rb +76 -1
  341. data/spec/units/partials/partializer_config_spec.rb +7 -0
  342. data/spec/units/partials/partializer_spec.rb +1 -0
  343. data/spec/units/partials_manager_spec.rb +17 -0
  344. data/spec/units/plugins/bullseye_spec.rb +15 -2
  345. data/spec/units/plugins/console_reportinator_spec.rb +18 -20
  346. data/spec/units/plugins/dependencies_spec.rb +33 -7
  347. data/spec/units/plugins/dependency_spec_spec.rb +17 -0
  348. data/spec/units/plugins/gcov_spec.rb +28 -3
  349. data/spec/units/plugins/gcovr_reportinator_spec.rb +19 -47
  350. data/spec/units/plugins/gcovr_thresholds_spec.rb +95 -0
  351. data/spec/units/plugins/module_generator_spec.rb +5 -5
  352. data/spec/units/plugins/report_build_warnings_log_spec.rb +3 -3
  353. data/spec/units/plugins/report_log_writer_plugin_spec.rb +10 -1
  354. data/spec/units/plugins/report_tests_raw_output_log_spec.rb +13 -2
  355. data/spec/units/plugins/report_tests_teamcity_stdout_spec.rb +77 -3
  356. data/spec/units/plugins/reportgenerator_reportinator_spec.rb +1 -1
  357. data/spec/units/plugins/sanitize_records_spec.rb +3 -1
  358. data/spec/units/plugins/sanitize_spec.rb +1 -1
  359. data/spec/units/plugins/valgrind_spec.rb +23 -8
  360. data/spec/units/preprocess/preprocessinator_comment_stripper_spec.rb +1 -1
  361. data/spec/units/rake_app/rakefile_component_resolver_spec.rb +21 -0
  362. data/spec/units/rake_app/test_task_name_spec.rb +47 -0
  363. data/spec/units/release_build_executor_spec.rb +53 -14
  364. data/spec/units/release_build_planner_spec.rb +15 -3
  365. data/spec/units/reportinator_spec.rb +5 -5
  366. data/spec/units/standin_headerinator_spec.rb +31 -1
  367. data/spec/units/test_build_executor_spec.rb +107 -5
  368. data/spec/units/test_build_planner_spec.rb +104 -2
  369. data/spec/units/test_build_setup_spec.rb +96 -12
  370. data/spec/units/test_context_extractor_spec.rb +25 -0
  371. data/spec/units/test_file_preprocessinator_spec.rb +17 -1
  372. data/spec/units/test_includes_extractinator_spec.rb +5 -1
  373. data/spec/units/test_invoker/objects_wiring_spec.rb +6 -3
  374. data/spec/units/test_invoker_spec.rb +14 -1
  375. data/spec/units/test_source_file_directive_resolver_spec.rb +38 -7
  376. data/spec/units/tool_executor_spec.rb +26 -0
  377. data/spec/units/yaml_wrapper_spec.rb +11 -0
  378. metadata +122 -33
  379. data/lib/ceedling/file_path_collection_utils.rb +0 -164
  380. data/site-local/assets/stylesheets/main.ec1eaa64.min.css +0 -1
  381. data/site-local/assets/stylesheets/main.ec1eaa64.min.css.map +0 -1
  382. data/site-local/assets/stylesheets/palette.ab4e12ef.min.css +0 -1
  383. data/site-local/assets/stylesheets/palette.ab4e12ef.min.css.map +0 -1
@@ -0,0 +1,128 @@
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 'tmpdir'
9
+ require 'fileutils'
10
+
11
+ repo_root = File.expand_path( '../../..', __dir__ )
12
+ $LOAD_PATH.unshift( File.join( repo_root, 'lib' ), File.join( repo_root, 'vendor/cmock/lib' ) )
13
+
14
+ require 'fff.rb'
15
+
16
+ # GH #1377: A fake gets the extra includes CMock would give a mock of the same header.
17
+ # Each example runs the wrapper Ceedling swaps in for CMock, with Ceedling's own
18
+ # :includes default, and reads back the files it writes.
19
+ describe FffCMockWrapper do
20
+
21
+ around(:each) do |example|
22
+ Dir.mktmpdir do |root|
23
+ @root = root
24
+ @header = File.join( root, 'src', 'bar.h' )
25
+ FileUtils.mkdir_p( File.dirname( @header ) )
26
+ File.write( @header, "void bar_turn_on(void);\n" )
27
+ example.run
28
+ end
29
+ end
30
+
31
+ def fake(options = {})
32
+ mock_path = File.join( @root, 'mocks' )
33
+ config = { :mock_prefix => 'mock_', :mock_path => mock_path, :includes => [], :verbosity => 0 }
34
+ FffCMockWrapper.new( config.merge( options ) ).setup_mocks( @header )
35
+ return {
36
+ :h => File.readlines( File.join( mock_path, 'mock_bar.h' ), chomp: true ),
37
+ :c => File.readlines( File.join( mock_path, 'mock_bar.c' ), chomp: true )
38
+ }
39
+ end
40
+
41
+ def position(lines, line)
42
+ index = lines.index( line )
43
+ expect( index ).not_to be_nil, "expected #{line.inspect} in:\n#{lines.join( "\n" )}"
44
+ return index
45
+ end
46
+
47
+ def extra_includes(lines)
48
+ standard = ['#include <string.h>', '#include "fff.h"', '#include "fff_unity_helper.h"', '#include "bar.h"', '#include "mock_bar.h"']
49
+ return lines.grep( /\A#include / ) - standard
50
+ end
51
+
52
+ it 'adds no includes when no setting names any' do
53
+ files = fake
54
+
55
+ expect( extra_includes( files[:h] ) ).to eq( [] )
56
+ expect( extra_includes( files[:c] ) ).to eq( [] )
57
+ end
58
+
59
+ it 'includes :includes_c_pre_header in the source before the fake header' do
60
+ files = fake( :includes_c_pre_header => ['extra_c.h'] )
61
+
62
+ expect( position( files[:c], '#include "extra_c.h"' ) ).to be < position( files[:c], '#include "mock_bar.h"' )
63
+ end
64
+
65
+ it 'includes :includes_c_post_header in the source after the fake header' do
66
+ files = fake( :includes_c_post_header => ['extra_c.h'] )
67
+
68
+ expect( position( files[:c], '#include "extra_c.h"' ) ).to be > position( files[:c], '#include "mock_bar.h"' )
69
+ end
70
+
71
+ it 'includes :includes_h_pre_orig_header in the header before the original header' do
72
+ files = fake( :includes_h_pre_orig_header => ['extra_h.h'] )
73
+
74
+ expect( position( files[:h], '#include "extra_h.h"' ) ).to be < position( files[:h], '#include "bar.h"' )
75
+ end
76
+
77
+ it 'includes :includes_h_post_orig_header in the header after the original header' do
78
+ files = fake( :includes_h_post_orig_header => ['extra_h.h'] )
79
+
80
+ expect( position( files[:h], '#include "extra_h.h"' ) ).to be > position( files[:h], '#include "bar.h"' )
81
+ end
82
+
83
+ it 'merges :includes with :includes_h_pre_orig_header, :includes first, each once' do
84
+ files = fake( :includes => ['first.h', 'both.h'], :includes_h_pre_orig_header => ['both.h', 'second.h'] )
85
+
86
+ expect( extra_includes( files[:h] ) ).to eq( ['#include "first.h"', '#include "both.h"', '#include "second.h"'] )
87
+ end
88
+
89
+ it 'keeps an angle-bracket include as written and quotes a bare name' do
90
+ files = fake( :includes_c_pre_header => ['<stdint.h>', 'extra_c.h'] )
91
+
92
+ expect( extra_includes( files[:c] ) ).to eq( ['#include <stdint.h>', '#include "extra_c.h"'] )
93
+ end
94
+
95
+ it 'includes a Unity helper in the source after the fake header, by its path from the mock root' do
96
+ helper = File.join( @root, 'support', 'helper.h' )
97
+ FileUtils.mkdir_p( File.dirname( helper ) )
98
+ File.write( helper, "\n" )
99
+
100
+ files = fake( :unity_helper_path => [helper] )
101
+
102
+ expect( position( files[:c], '#include "../support/helper.h"' ) ).to be > position( files[:c], '#include "mock_bar.h"' )
103
+ end
104
+
105
+ # CMock's :verbosity levels: 0 errors only, 1 warnings, 2 normal, 3 verbose
106
+ context 'at CMock\'s verbosity levels' do
107
+
108
+ it 'prints nothing for errors only or warnings' do
109
+ expect { fake( :verbosity => 0 ) }.not_to output.to_stdout
110
+ expect { fake( :verbosity => 1 ) }.not_to output.to_stdout
111
+ end
112
+
113
+ it 'announces each fake at normal verbosity, as CMock announces each mock' do
114
+ expect { fake( :verbosity => 2 ) }.to output( "Creating fake functions for bar...\n" ).to_stdout
115
+ end
116
+
117
+ it 'also names the files written at verbose' do
118
+ path = File.join( @root, 'mocks', 'mock_bar' )
119
+
120
+ expect { fake( :verbosity => 3 ) }.to output(
121
+ "Creating fake functions for bar...\n" \
122
+ "Creating fake functions: #{path}.h and #{path}.c\n"
123
+ ).to_stdout
124
+ end
125
+
126
+ end
127
+
128
+ end
@@ -41,7 +41,7 @@ DEFAULT_GCOV_LINKER_TOOL = {
41
41
  }
42
42
 
43
43
  DEFAULT_GCOV_FIXTURE_TOOL = {
44
- :executable => '${1}'.freeze,
44
+ :executable => '"${1}"'.freeze, # Test executable, quoted as a path
45
45
  :name => 'default_gcov_fixture'.freeze,
46
46
  :optional => false.freeze,
47
47
  :arguments => [].freeze
@@ -38,11 +38,12 @@ namespace GCOV_SYM do
38
38
  @ceedling[:gcov].process_untested_sources( sources:COLLECTION_ALL_SOURCE )
39
39
  end
40
40
 
41
- desc 'Run single test w/ coverage ([*] test or source file name, no path).'
41
+ desc 'Run single test w/ coverage ([*] test or source file name, with optional path).'
42
42
  task :* do
43
43
  message = "Oops! '#{GCOV_ROOT_NAME}:*' isn't a real task. " \
44
- "Use a real test or source file name (no path) in place of the wildcard.\n" \
45
- "Example: `ceedling #{GCOV_ROOT_NAME}:foo.c`"
44
+ "Use a real test or source file name in place of the wildcard, " \
45
+ "adding as much of its path as needed to identify one file among any same-named tests.\n" \
46
+ "Example: `ceedling #{GCOV_ROOT_NAME}:foo.c` or `ceedling #{GCOV_ROOT_NAME}:unit/foo.c`"
46
47
 
47
48
  @ceedling[:loginator].log( message, Verbosity::ERRORS )
48
49
  end
@@ -83,12 +84,8 @@ namespace GCOV_SYM do
83
84
  # Use a rule to increase efficiency for large projects
84
85
  rule(/^#{GCOV_TASK_ROOT}\S+$/ => [ # gcov test tasks by regex
85
86
  proc do |task_name|
86
- # Yield clean test name => Strip the task string, remove Rake test task prefix, and remove any code file extension
87
- test = task_name.strip().sub(/^#{GCOV_TASK_ROOT}/, '')
88
- EXTENSION_SOURCE.each { |ext| test = test.chomp( ext ) }
89
-
90
- # Ensure the test name begins with a test name prefix
91
- test = PROJECT_TEST_FILE_PREFIX + test if not (test.start_with?( PROJECT_TEST_FILE_PREFIX ))
87
+ # Any directory typed ahead of the name is kept. See TestTaskName.
88
+ test = TestTaskName.test_name( task_name, root: GCOV_TASK_ROOT )
92
89
 
93
90
  # Provide the filepath for the target test task back to the Rake task
94
91
  @ceedling[:file_finder].find_test_file_from_name( test )
@@ -6,7 +6,7 @@
6
6
  # =========================================================================
7
7
 
8
8
  require 'ceedling/constants'
9
- require 'ceedling/path_mirror'
9
+ require 'ceedling/filesystem/path_mirror'
10
10
  require 'gcov_reportinator'
11
11
 
12
12
  class ConsoleReportinator < GcovReportinator
@@ -173,22 +173,8 @@ class ConsoleReportinator < GcovReportinator
173
173
  def run_gcov_summary(test, source, opts)
174
174
  filename = File.basename(source)
175
175
 
176
- # A module-under-test's object (and its accompanying .gcno/.gcda) mirrors its source's own
177
- # subdirectory below whichever configured root it came from -- the same convention its actual
178
- # compile step already follows -- so the directory gcov is told to search must include that
179
- # mirrored subdirectory too, not just the flat <build>/gcov/out/<test name> root.
180
- # A generated Partial sits below its own test's Partials build root rather than a
181
- # configured source root, so the source roots mirror nothing for it. Its own root is
182
- # what carries the module's subdirectory.
183
- roots =
184
- if File.basename(source).match?(PATTERNS::PARTIAL_IMPL_FILENAME)
185
- [File.join( @configurator.project_test_partials_path, test )]
186
- else
187
- @configurator.paths_source + @configurator.paths_support
188
- end
189
-
190
- subdir = PathMirror.relative_subdir( source, roots )
191
- obj_dir = subdir.empty? ? File.join(GCOV_BUILD_OUTPUT_PATH, test) : File.join(GCOV_BUILD_OUTPUT_PATH, test, subdir)
176
+ # The coverage data sits beside the object, wherever the test's build placed it
177
+ obj_dir = File.dirname( @file_path_utils.form_test_object_filepath( source, name: test, context: GCOV_SYM ) )
192
178
 
193
179
  # Run gcov to extract the coverage summary
194
180
  command = @tool_executor.build_command_line(
@@ -197,7 +183,7 @@ class ConsoleReportinator < GcovReportinator
197
183
  (opts[:gcov_mcdc] ? ['-g'] : []),
198
184
  # Argument replacement
199
185
  filename, # .c source file compiled with coverage
200
- obj_dir # <build>/gcov/out/<test name>[/mirrored subdir] for coverage data files
186
+ obj_dir # <build>/gcov/out/<test name>[/source directory] for coverage data files
201
187
  )
202
188
 
203
189
  # Do not raise an exception if `gcov` terminates with a non-zero exit code, just note it and move on.
@@ -12,6 +12,7 @@ require 'gcov_constants'
12
12
  require 'gcov_types'
13
13
  require 'gcov_reportinator'
14
14
  require 'console_reportinator'
15
+ require 'gcovr_thresholds'
15
16
  require 'gcovr_reportinator'
16
17
  require 'reportgenerator_reportinator'
17
18
 
@@ -141,7 +142,7 @@ class Gcov < Plugin
141
142
 
142
143
  # Full filepaths (not basenames)
143
144
  header = "Untested source files not in the coverage report (:untested_sources is :list):"
144
- @loginator.log_list( untested_sources.sort, header, Verbosity::COMPLAIN, LogLabels::WARNING )
145
+ @loginator.log_list( untested_sources.sort, header, Verbosity::COMPLAIN )
145
146
 
146
147
  when GCOV_UNTESTED_SOURCES_COMPILE
147
148
  validate_mcdc_gcc_version!
@@ -163,7 +164,7 @@ class Gcov < Plugin
163
164
  untested_sources.each do |filepath|
164
165
  filename = File.basename(filepath)
165
166
  object = @file_path_utils.form_test_object_filepath( filepath, context:GCOV_SYM )
166
- dependencies = @file_path_utils.form_test_dependencies_filepath( filepath, context:GCOV_SYM )
167
+ dependencies = @file_path_utils.form_test_dependencies_filepath( object, context:GCOV_SYM )
167
168
  search_paths = @configurator.collection_paths_include
168
169
  # Defines and flags resolve as a tested source's do, against this source's filepath
169
170
  defines = @test_build_setup.context_defines( context:GCOV_SYM, filepath:filepath )
@@ -196,6 +197,9 @@ class Gcov < Plugin
196
197
  next
197
198
  end
198
199
 
200
+ @file_wrapper.mkdir( File.dirname( object ) )
201
+ @file_wrapper.mkdir( File.dirname( dependencies ) )
202
+
199
203
  begin
200
204
  @generator.generate_object_file_c(
201
205
  tool: TOOLS_GCOV_COMPILER,
@@ -382,6 +386,9 @@ class Gcov < Plugin
382
386
 
383
387
  begin
384
388
  reportinator.generate_reports( @configurator.project_config_hash )
389
+ rescue GcovrThresholdError => ex
390
+ # Each violated threshold is its own build failure
391
+ ex.violations.each { |violation| @ceedling[:plugin_manager].register_build_failure( GCOV_SYM, violation ) }
385
392
  rescue => ex
386
393
  # Register the exception as a build failure
387
394
  @ceedling[:plugin_manager].register_build_failure( GCOV_SYM, ex.message )
@@ -7,8 +7,10 @@
7
7
 
8
8
  require 'ceedling/exceptions'
9
9
  require 'ceedling/constants'
10
+ require 'ceedling/filename_extension'
10
11
  require 'gcov_types'
11
12
  require 'gcov_reportinator'
13
+ require 'gcovr_thresholds'
12
14
 
13
15
  class GcovrReportinator < GcovReportinator
14
16
 
@@ -507,9 +509,8 @@ class GcovrReportinator < GcovReportinator
507
509
  data = build_exclusion_data
508
510
  patterns = []
509
511
 
510
- # A source extension can be configured as more than one string, so its escaped forms
511
- # are joined into one alternation rather than assuming there's only ever one to match.
512
- src_extension_alternation = data[:src_extension].to_a.map { |ext| Regexp.escape(ext) }.join('|')
512
+ # A source extension can be configured as more than one string, so any one of them matches
513
+ src_extension_alternation = FilenameExtension.new( data[:src_extension] ).pattern
513
514
  test_prefix = Regexp.escape(data[:test_prefix].to_s)
514
515
  build_root = Regexp.escape(strip_leading_dot_slash(data[:build_root].to_s))
515
516
 
@@ -576,23 +577,6 @@ class GcovrReportinator < GcovReportinator
576
577
  end
577
578
 
578
579
 
579
- # Returns the error message text from gcovr stderr, or nil if none found.
580
- # Matches lines beginning with "error" or "(ERROR)" and extracts the trailing message.
581
- def extract_gcovr_error_message(shell_result)
582
- return nil if shell_result.nil?
583
-
584
- stderr = shell_result[:stderr]
585
- return nil if stderr.nil? || stderr.empty?
586
-
587
- stderr.each_line do |line|
588
- match = line.match( /^\(?error\S*\s+(.+)/i )
589
- return match[1].strip.capitalize if match
590
- end
591
-
592
- nil
593
- end
594
-
595
-
596
580
  # Extract the last contiguous block of lines containing '%' from gcovr stdout.
597
581
  # gcovr --print-summary emits lines like "lines: 69.6% (80 out of 115)" — this
598
582
  # locates that block generically without depending on exact line labels.
@@ -620,33 +604,17 @@ class GcovrReportinator < GcovReportinator
620
604
  end
621
605
 
622
606
 
623
- # Output to console a human-friendly message on certain coverage failure exit codes.
624
- # Prefers the actual gcovr error text from stderr; falls back to descriptive strings.
625
- # gcovr's composite exit code bit-ORs every violated :fail_under_* threshold together --
626
- # every violated bit is collected and reported, not just whichever is checked first.
607
+ # Reports every :fail_under_* threshold a gcovr run violated, raising when boom is set and
608
+ # logging otherwise. Returns whether the exit code holds a problem no threshold explains.
627
609
  def gcovr_exec_exception?(opts, exitcode, boom, shell_result=nil)
628
- violations = []
629
-
630
- {
631
- 2 => [:fail_under_line, 'Line'],
632
- 4 => [:fail_under_branch, 'Branch'],
633
- 8 => [:fail_under_decision, 'Decision'],
634
- 16 => [:fail_under_function, 'Function'],
635
- }.each do |bit, (option, label)|
636
- next unless ((exitcode & bit) == bit) && !opts[option].nil?
637
-
638
- fallback = "#{label} coverage is less than the configured minimum of #{opts[option]}%"
639
- violations << "Gcovr ⏩️ #{extract_gcovr_error_message( shell_result ) || fallback}"
640
- exitcode &= ~bit
641
- end
610
+ violations, exitcode = GcovrThresholds.decode( opts: opts, exitcode: exitcode, stderr: shell_result&.dig( :stderr ) )
642
611
 
643
612
  if boom
644
- raise CeedlingException.new( violations.join("\n") ) unless violations.empty?
613
+ raise GcovrThresholdError.new( violations ) unless violations.empty?
645
614
  else
646
615
  violations.each { |msg| @loginator.log( msg, Verbosity::COMPLAIN ) }
647
616
  end
648
617
 
649
- # A non-zero exit code is a problem
650
618
  return (exitcode != 0)
651
619
  end
652
620
 
@@ -0,0 +1,74 @@
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 'ceedling/exceptions'
9
+
10
+ # Coverage thresholds a gcovr run violated, one message each
11
+ class GcovrThresholdError < CeedlingException
12
+
13
+ attr_reader :violations
14
+
15
+ def initialize(violations)
16
+ @violations = violations
17
+ super( violations.join( "\n" ) )
18
+ end
19
+
20
+ end
21
+
22
+ # Decodes gcovr's exit status and stderr into the :fail_under_* thresholds a run violated.
23
+ # Each violated threshold sets a bit of the exit status and writes a line of its own to
24
+ # stderr. Bit 8 covers both condition and decision coverage, which from gcovr 8.7 may
25
+ # each write a line.
26
+ module GcovrThresholds
27
+
28
+ BITS = {
29
+ 2 => { option: :fail_under_line, label: 'Line', kinds: ['line'] },
30
+ 4 => { option: :fail_under_branch, label: 'Branch', kinds: ['branch'] },
31
+ 8 => { option: :fail_under_decision, label: 'Condition or decision', kinds: ['condition', 'decision'] },
32
+ 16 => { option: :fail_under_function, label: 'Function', kinds: ['function'] },
33
+ }.freeze
34
+
35
+ MESSAGE_PATTERN = /^\(?error\)?:?\s+(failed minimum (\w+) coverage.*?)\s*$/i
36
+
37
+ # Returns the violation messages, and the exit status bits no configured threshold explains
38
+ def self.decode(opts:, exitcode:, stderr:)
39
+ messages = messages_by_kind( stderr )
40
+ violations = []
41
+
42
+ BITS.each do |bit, threshold|
43
+ next unless (exitcode & bit) == bit && !opts[threshold[:option]].nil?
44
+
45
+ violations.concat( violations_for( threshold, opts, messages ) )
46
+ exitcode &= ~bit
47
+ end
48
+
49
+ return violations, exitcode
50
+ end
51
+
52
+ # gcovr's own messages, grouped by the coverage kind each names. Older gcovr writes them in
53
+ # lowercase, so the first letter is capitalized, and nothing else is touched.
54
+ def self.messages_by_kind(stderr)
55
+ messages = Hash.new { |hash, kind| hash[kind] = [] }
56
+
57
+ stderr.to_s.each_line do |line|
58
+ match = line.match( MESSAGE_PATTERN )
59
+ messages[match[2].downcase] << match[1].sub( /\A./ ) { |first| first.upcase } if match
60
+ end
61
+
62
+ return messages
63
+ end
64
+
65
+ def self.violations_for(threshold, opts, messages)
66
+ found = threshold[:kinds].flat_map { |kind| messages[kind] }
67
+ found = ["#{threshold[:label]} coverage is less than the configured minimum of #{opts[threshold[:option]]}%"] if found.empty?
68
+
69
+ return found.map { |message| "Gcovr ⏩️ #{message}" }
70
+ end
71
+
72
+ private_class_method :messages_by_kind, :violations_for
73
+
74
+ end
@@ -8,7 +8,7 @@
8
8
  require 'benchmark'
9
9
  require 'ceedling/constants'
10
10
  require 'ceedling/exceptions'
11
- require 'ceedling/file_path_utils'
11
+ require 'ceedling/filesystem/file_path_utils'
12
12
  require 'gcov_reportinator'
13
13
 
14
14
  class ReportGeneratorReportinator < GcovReportinator
@@ -110,7 +110,7 @@ class ModuleGenerator < Plugin
110
110
 
111
111
  @ceedling[:loginator].log(
112
112
  'Module Generator :update_svn is deprecated and ignored. Add generated files to source control with your own tools.',
113
- Verbosity::COMPLAIN, LogLabels::WARNING
113
+ Verbosity::COMPLAIN
114
114
  )
115
115
  end
116
116
 
@@ -119,7 +119,7 @@ class ModuleGenerator < Plugin
119
119
  project.same_named( files.map { |file| file[:path].to_s } ).each do |planned, existing|
120
120
  @ceedling[:loginator].log(
121
121
  "Module file `#{planned}` shares its name with #{existing.map { |path| "`#{path}`" }.join( ', ' )}.",
122
- Verbosity::COMPLAIN, LogLabels::WARNING
122
+ Verbosity::COMPLAIN
123
123
  )
124
124
  end
125
125
  end
@@ -133,7 +133,7 @@ class ModuleGenerator < Plugin
133
133
  @ceedling[:loginator].log(
134
134
  "Generated test `#{path}` is wrapped in `##{options.test_guard} #{define}`, but its build does not " \
135
135
  "define #{define}. Add it under :defines ↳ :test, or the test compiles to nothing.",
136
- Verbosity::COMPLAIN, LogLabels::WARNING
136
+ Verbosity::COMPLAIN
137
137
  )
138
138
  end
139
139
  end
@@ -39,18 +39,13 @@ class ReportTestsTeamcityStdout < Plugin
39
39
  def pre_test(test)
40
40
  return if !@output_enabled
41
41
 
42
- # Generate a new Flow ID and store it in test hash
42
+ # Generate a new Flow ID and open the test's suite. A suite is named for its test,
43
+ # which tells same-named tests in different directories apart.
43
44
  @mutex.synchronize do
44
45
  @flowid_count += 1
45
- @suites[test] = { :flowid => @flowid_count }
46
- end
46
+ @suites[test] = { :flowid => @flowid_count, :name => escape( @ceedling[:file_path_utils].form_test_name( test ) ) }
47
47
 
48
- # Generate first TeamCity service message
49
- @mutex.synchronize do
50
- teamcity_service_message(
51
- "testSuiteStarted name='#{File.basename(test, '.*')}'",
52
- @suites[test][:flowid]
53
- )
48
+ teamcity_service_message( "testSuiteStarted name='#{@suites[test][:name]}'", @suites[test][:flowid] )
54
49
  end
55
50
  end
56
51
 
@@ -58,103 +53,81 @@ class ReportTestsTeamcityStdout < Plugin
58
53
  def post_test_fixture_execute(arg_hash)
59
54
  return if !@output_enabled
60
55
 
61
- flowId = nil
62
- test_key = arg_hash[:test_filepath]
63
-
64
- # Get unique Flow ID
65
- @mutex.synchronize do
66
- flowId = @suites[test_key][:flowid]
67
- end
56
+ flowId = @mutex.synchronize { @suites[arg_hash[:test_filepath]][:flowid] }
68
57
 
69
58
  # Gather test results for this test executable
70
59
  results = @ceedling[:plugin_reportinator].assemble_test_results( [arg_hash[:result_file]] )
71
-
72
- # Apportion total run time of test executable equally to each test case within it.
73
- duration_ms = results[:total_time] * 1000.0
74
- avg_duration = (duration_ms / [1, results[:counts][:passed] + results[:counts][:failed]].max).round
60
+ duration = average_duration( results )
75
61
 
76
62
  context = arg_hash[:context]
77
63
 
78
- # Handle test case successes within the test executable
79
- results[:successes].each do |success|
80
- filepath = File.join( success[:source][:dirname], File.basename( success[:source][:basename], '.*' ) )
64
+ report_successes( results[:successes], context, flowId, duration )
65
+ report_failures( results[:failures], context, flowId, duration )
66
+ report_ignores( results[:ignores], context, flowId )
67
+ end
81
68
 
82
- # puts(success)
83
- success[:collection].each do |test|
84
- _test = test[:test]
85
-
86
- # Create Java-like name per TeamCity convention `package_or_namespace.ClassName.TestName`
87
- # ==> `context.TestFilepath.TestCaseName`
88
- teamcity_service_message(
89
- "testStarted name='#{context}.#{filepath}.#{_test}'",
90
- flowId
91
- )
92
-
93
- teamcity_service_message(
94
- "testFinished name='#{context}.#{filepath}.#{_test}' duration='#{avg_duration}'",
95
- flowId
96
- )
97
- end
69
+ # `Plugin` build step hook
70
+ def post_test(test)
71
+ return if !@output_enabled
72
+
73
+ @mutex.synchronize do
74
+ teamcity_service_message(
75
+ "testSuiteFinished name='#{@suites[test][:name]}'",
76
+ @suites[test][:flowid]
77
+ )
98
78
  end
79
+ end
99
80
 
100
- # Handle test case failures within the test executable
101
- results[:failures].each do |failure|
102
- failure[:collection].each do |test|
103
- filepath = File.join( failure[:source][:dirname], File.basename( failure[:source][:basename], '.*' ) )
81
+ ### Private
104
82
 
105
- _test = test[:test]
106
- _message = test[:message]
83
+ private
107
84
 
108
- teamcity_service_message(
109
- "testStarted name='#{context}.#{filepath}.#{_test}'",
110
- flowId
111
- )
85
+ # Total run time of a test executable, apportioned equally to each test case within it, in ms
86
+ def average_duration(results)
87
+ duration_ms = results[:total_time] * 1000.0
112
88
 
113
- # Create Java-like name per TeamCity convention `package_or_namespace.ClassName.TestName`
114
- # ==> `context.TestFilepath.TestCaseName`
115
- _message =
116
- "testFailed name='#{context}.#{filepath}.#{_test}' " +
117
- "message='#{escape(_message)}' " +
118
- "details='File: #{failure[:source][:file]} Line: #{test[:line]}'"
89
+ return (duration_ms / [1, results[:counts][:passed] + results[:counts][:failed]].max).round
90
+ end
119
91
 
120
- teamcity_service_message( _message, flowId )
92
+ def report_successes(successes, context, flowId, duration)
93
+ successes.each do |success|
94
+ success[:collection].each do |test|
95
+ name = test_name( context, success[:source], test[:test] )
121
96
 
122
- teamcity_service_message(
123
- "testFinished name='#{context}.#{filepath}.#{_test}' duration='#{avg_duration}'",
124
- flowId
125
- )
97
+ teamcity_service_message( "testStarted name='#{name}'", flowId )
98
+ teamcity_service_message( "testFinished name='#{name}' duration='#{duration}'", flowId )
126
99
  end
127
100
  end
101
+ end
128
102
 
129
- # Handle ignored tests
130
- results[:ignores].each do |ignored|
131
- ignored[:collection].each do |test|
132
- filepath = File.join( ignored[:source][:dirname], File.basename( ignored[:source][:basename], '.*' ) )
133
- _test = test[:test]
134
-
135
- service_message = "testIgnored name='#{context}.#{filepath}.#{_test}'"
103
+ def report_failures(failures, context, flowId, duration)
104
+ failures.each do |failure|
105
+ failure[:collection].each do |test|
106
+ name = test_name( context, failure[:source], test[:test] )
107
+ details = escape( "File: #{failure[:source][:file]} Line: #{test[:line]}" )
136
108
 
137
- teamcity_service_message( service_message, flowId )
109
+ teamcity_service_message( "testStarted name='#{name}'", flowId )
110
+ teamcity_service_message( "testFailed name='#{name}' message='#{escape( test[:message] )}' details='#{details}'", flowId )
111
+ teamcity_service_message( "testFinished name='#{name}' duration='#{duration}'", flowId )
138
112
  end
139
113
  end
140
-
141
114
  end
142
115
 
143
- # `Plugin` build step hook
144
- def post_test(test)
145
- return if !@output_enabled
146
-
147
- @mutex.synchronize do
148
- teamcity_service_message(
149
- "testSuiteFinished name='#{File.basename(test, '.*')}'",
150
- @suites[test][:flowid]
151
- )
116
+ def report_ignores(ignores, context, flowId)
117
+ ignores.each do |ignored|
118
+ ignored[:collection].each do |test|
119
+ teamcity_service_message( "testIgnored name='#{test_name( context, ignored[:source], test[:test] )}'", flowId )
120
+ end
152
121
  end
153
122
  end
154
123
 
155
- ### Private
124
+ # A Java-like name per TeamCity convention `package_or_namespace.ClassName.TestName`
125
+ # ==> `context.TestFilepath.TestCaseName`, escaped
126
+ def test_name(context, source, test)
127
+ filepath = File.join( source[:dirname], File.basename( source[:basename], '.*' ) )
156
128
 
157
- private
129
+ return escape( "#{context}.#{filepath}.#{test}" )
130
+ end
158
131
 
159
132
  def escape(string)
160
133
  # https://www.jetbrains.com/help/teamcity/service-messages.html#Escaped+Values
@@ -6,6 +6,7 @@
6
6
  # =========================================================================
7
7
 
8
8
  require 'ceedling/constants'
9
+ require 'ceedling/build_log_writer'
9
10
  require 'sanitize_constants'
10
11
  require 'sanitize_reports'
11
12
 
@@ -20,7 +21,7 @@ class SanitizeRecords
20
21
  @loginator = loginator
21
22
  @reports = reports
22
23
  @records = []
23
- @logged_tests = []
24
+ @log_writer = BuildLogWriter.new( file_wrapper: file_wrapper )
24
25
  @written = []
25
26
  @mutex = Mutex.new
26
27
  end
@@ -101,11 +102,7 @@ class SanitizeRecords
101
102
 
102
103
  # A build's first report for a test replaces the last build's log, and later ones follow it
103
104
  def write_log(log, findings)
104
- first = @mutex.synchronize { !@logged_tests.include?( log ) && @logged_tests << log }
105
- text = findings.map( &:text ).join( "\n" )
106
-
107
- @file_wrapper.mkdir( File.dirname( log ) )
108
- @file_wrapper.write( log, first ? text : "\n#{text}", first ? 'w' : 'a' )
105
+ @log_writer.write( log, findings.map( &:text ).join( "\n" ), separator: "\n" )
109
106
  end
110
107
 
111
108
  def pluralize(count, word)