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.
- checksums.yaml +4 -4
- data/GIT_COMMIT_SHA +1 -1
- data/README.md +11 -9
- data/Rakefile +2 -1
- data/assets/features/ceedling +1 -1
- data/assets/fixtures/test_source_file_duplicate_basenames/test_calc_typo.c +14 -0
- data/assets/fixtures/tests_with_duplicate_test_names/test_foo.c +21 -0
- data/bin/actionator.rb +6 -12
- data/bin/app_cfg.rb +14 -1
- data/bin/ceedling +13 -3
- data/bin/cli.rb +25 -6
- data/bin/cli_handler.rb +113 -73
- data/bin/cli_helper.rb +51 -78
- data/bin/installation_selector.rb +81 -0
- data/bin/launchinator.rb +69 -0
- data/bin/mixin_resolvinator.rb +26 -36
- data/bin/objects.yml +13 -0
- data/bin/path_validator.rb +25 -29
- data/bin/versionator.rb +47 -48
- data/ceedling.gemspec +19 -15
- data/docs/BreakingChanges.md +70 -1
- data/docs/Changelog.md +74 -3
- data/docs/KnownIssues.md +90 -4
- data/docs/ReleaseNotes.md +13 -0
- data/lib/ceedling/build_log_writer.rb +33 -0
- data/lib/ceedling/config/build_root_policy.rb +70 -0
- data/lib/ceedling/config/configurator.rb +14 -16
- data/lib/ceedling/config/configurator_builder.rb +58 -28
- data/lib/ceedling/config/configurator_paths_validator.rb +179 -0
- data/lib/ceedling/config/configurator_plugins.rb +1 -1
- data/lib/ceedling/config/configurator_setup.rb +15 -28
- data/lib/ceedling/config/configurator_validator.rb +1 -110
- data/lib/ceedling/defaults.rb +4 -4
- data/lib/ceedling/dependencies/README.md +18 -16
- data/lib/ceedling/dependencies/dependency_cache_store.rb +20 -3
- data/lib/ceedling/dependencies/dependency_hasher.rb +35 -1
- data/lib/ceedling/filename_extension.rb +7 -1
- data/lib/ceedling/filesystem/FILE_TREE.md +253 -0
- data/lib/ceedling/filesystem/PATH_HANDLING.md +304 -0
- data/lib/ceedling/filesystem/README.md +41 -0
- data/lib/ceedling/filesystem/build_subpath.rb +102 -0
- data/lib/ceedling/filesystem/destination_collision.rb +25 -0
- data/lib/ceedling/{file_finder.rb → filesystem/file_finder.rb} +159 -104
- data/lib/ceedling/{file_finder_helper.rb → filesystem/file_finder_helper.rb} +36 -31
- data/lib/ceedling/filesystem/file_path_collection_utils.rb +183 -0
- data/lib/ceedling/{file_path_utils.rb → filesystem/file_path_utils.rb} +108 -97
- data/lib/ceedling/{file_system_wrapper.rb → filesystem/file_system_wrapper.rb} +6 -5
- data/lib/ceedling/filesystem/file_tree.rb +241 -0
- data/lib/ceedling/{file_wrapper.rb → filesystem/file_wrapper.rb} +45 -38
- data/lib/ceedling/filesystem/path_exclusions.rb +30 -0
- data/lib/ceedling/{path_matcher.rb → filesystem/path_matcher.rb} +27 -23
- data/lib/ceedling/{path_mirror.rb → filesystem/path_mirror.rb} +19 -18
- data/lib/ceedling/filesystem/path_spellings.rb +55 -0
- data/lib/ceedling/filesystem/project_paths.rb +184 -0
- data/lib/ceedling/filesystem/working_dir.rb +40 -0
- data/lib/ceedling/generators/generator.rb +5 -6
- data/lib/ceedling/generators/generator_partials.rb +1 -1
- data/lib/ceedling/generators/generator_test_results_backtrace.rb +7 -7
- data/lib/ceedling/handoff.rb +121 -0
- data/lib/ceedling/includes/include_factory.rb +3 -3
- data/lib/ceedling/includes/include_pathinator.rb +67 -10
- data/lib/ceedling/includes/includes.rb +1 -1
- data/lib/ceedling/installation.rb +92 -0
- data/lib/ceedling/library_resolver.rb +99 -0
- data/lib/ceedling/objects.yml +23 -2
- data/lib/ceedling/partials/partializer.rb +4 -4
- data/lib/ceedling/partials/partializer_config.rb +3 -1
- data/lib/ceedling/plugins/report_log_writer_plugin.rb +3 -5
- data/lib/ceedling/preprocess/preprocessinator_file_assembler.rb +1 -1
- data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +1 -1
- data/lib/ceedling/preprocess/preprocessinator_line_marker_includes_extractor.rb +1 -1
- data/lib/ceedling/rake_app/rakefile_component_resolver.rb +10 -17
- data/lib/ceedling/rakefile.rb +15 -1
- data/lib/ceedling/rakefiles/base/tasks_base.rake +1 -1
- data/lib/ceedling/rakefiles/release/rules_release.rake +4 -7
- data/lib/ceedling/release_invoker/release_build_executor.rb +33 -44
- data/lib/ceedling/release_invoker/release_build_planner.rb +14 -6
- data/lib/ceedling/reportinator.rb +3 -4
- data/lib/ceedling/setupinator.rb +94 -90
- data/lib/ceedling/test_context_extractor.rb +5 -1
- data/lib/ceedling/test_invoker/PIPELINE.md +9 -7
- data/lib/ceedling/test_invoker/mock_locator.rb +60 -2
- data/lib/ceedling/test_invoker/partials_manager.rb +12 -0
- data/lib/ceedling/test_invoker/standin_headerinator.rb +22 -13
- data/lib/ceedling/test_invoker/test_build_executor.rb +24 -40
- data/lib/ceedling/test_invoker/test_build_planner.rb +37 -18
- data/lib/ceedling/test_invoker/test_build_setup.rb +17 -12
- data/lib/ceedling/test_invoker/test_build_validations.rb +16 -31
- data/lib/ceedling/test_invoker/test_file_preprocessinator.rb +1 -1
- data/lib/ceedling/test_invoker/test_invoker.rb +6 -1
- data/lib/ceedling/test_invoker/test_pipeline_helpers.rb +1 -1
- data/lib/ceedling/test_invoker/test_source_file_directive_resolver.rb +40 -20
- data/lib/ceedling/tool_executor.rb +9 -3
- data/lib/ceedling/yaml_wrapper.rb +3 -2
- data/lib/version.rb +1 -1
- data/plugins/bullseye/config/defaults_bullseye.rb +1 -1
- data/plugins/bullseye/lib/bullseye.rb +9 -7
- data/plugins/cppcheck/config/defaults_cppcheck.rb +1 -1
- data/plugins/dependencies/config/defaults.yml +6 -6
- data/plugins/dependencies/dependencies.rake +4 -1
- data/plugins/dependencies/lib/dependencies.rb +18 -8
- data/plugins/dependencies/lib/dependency_spec.rb +17 -5
- data/plugins/fff/lib/fff.rb +45 -39
- data/plugins/fff/spec/fff_cmock_wrapper_spec.rb +128 -0
- data/plugins/gcov/config/defaults_gcov.rb +1 -1
- data/plugins/gcov/lib/console_reportinator.rb +4 -18
- data/plugins/gcov/lib/gcov.rb +9 -2
- data/plugins/gcov/lib/gcovr_reportinator.rb +8 -40
- data/plugins/gcov/lib/gcovr_thresholds.rb +74 -0
- data/plugins/gcov/lib/reportgenerator_reportinator.rb +1 -1
- data/plugins/module_generator/lib/module_generator.rb +3 -3
- data/plugins/report_tests_teamcity_stdout/lib/report_tests_teamcity_stdout.rb +53 -80
- data/plugins/sanitize/lib/sanitize_records.rb +3 -6
- data/plugins/valgrind/lib/valgrind.rb +14 -7
- data/site-local/configuration/reference/cmock.html +2 -1
- data/site-local/configuration/reference/files.html +9 -3
- data/site-local/configuration/reference/libraries.html +21 -3
- data/site-local/configuration/reference/paths.html +29 -6
- data/site-local/configuration/reference/project.html +17 -7
- data/site-local/configuration/reference/release-build.html +2 -0
- data/site-local/configuration/reference/tools.html +9 -0
- data/site-local/configuration/which-ceedling.html +377 -17
- data/site-local/development/releases.html +5 -1
- data/site-local/development/workflow.html +48 -43
- data/site-local/getting-started/builds.html +2 -0
- data/site-local/getting-started/command-line.html +56 -4
- data/site-local/plugins/dependencies.html +6 -3
- data/site-local/plugins/fff.html +42 -11
- data/site-local/plugins/gcov/gcovr.html +15 -12
- data/site-local/plugins/gcov/setup.html +2 -1
- data/site-local/plugins/report-tests-raw-output-log.html +3 -1
- data/site-local/plugins/report-tests-teamcity-stdout.html +6 -3
- data/site-local/plugins/sanitize.html +7 -6
- data/site-local/plugins/valgrind.html +8 -7
- data/site-local/project/upgrade.html +3 -0
- data/site-local/reference/command-line.html +9 -2
- data/site-local/sitemap.xml.gz +0 -0
- data/site-local/snapshot/plugins/bullseye/config/defaults_bullseye.rb +1 -1
- data/site-local/snapshot/plugins/gcov/config/defaults_gcov.rb +1 -1
- data/site-local/testing-guide/conventions.html +53 -0
- data/spec/integration/actionator_file_operations_spec.rb +91 -1
- data/spec/integration/compile_commands_json_db_spec.rb +1 -1
- data/spec/integration/configurator_validation_spec.rb +1 -1
- data/spec/integration/dependencies/dependency_tracker_cache_hardening_spec.rb +26 -0
- data/spec/integration/dependencies/dependency_tracker_core_spec.rb +22 -0
- data/spec/integration/launcher_handoff_spec.rb +297 -0
- data/spec/integration/mixin_loading_spec.rb +1 -1
- data/spec/integration/module_generator_spec.rb +3 -3
- data/spec/integration/nested_test_artifacts_spec.rb +99 -0
- data/spec/integration/partials_module_resolution_spec.rb +7 -4
- data/spec/integration/path_intake_scenarios_spec.rb +101 -0
- data/spec/integration/path_scenarios_spec.rb +322 -0
- data/spec/integration/quote_include_isolator_spec.rb +1 -1
- data/spec/integration/report_build_warnings_log_spec.rb +1 -1
- data/spec/integration/report_tests_log_factory_spec.rb +1 -1
- data/spec/integration/test_executable_manifest_spec.rb +1 -1
- data/spec/integration/test_includes_extractinator_spec.rb +2 -1
- data/spec/integration/test_naming_spec.rb +198 -0
- data/spec/integration/test_source_file_checks_spec.rb +166 -0
- data/spec/integration/tool_validator_spec.rb +1 -1
- data/spec/integration/upgrade_docs_spec.rb +84 -0
- data/spec/integration/vendor_copy_transient_io_retry_spec.rb +74 -5
- data/spec/support/file_tree_helper.rb +88 -0
- data/spec/support/integration/configurator_integration_helper.rb +6 -6
- data/spec/support/integration/dependency_tracker_integration_helper.rb +1 -1
- data/spec/support/integration/partials_generation_integration_helper.rb +2 -2
- data/spec/support/integration/path_scenario_harness.rb +173 -0
- data/spec/support/integration/spec_integration_helper.rb +1 -1
- data/spec/support/integration/test_includes_harness.rb +13 -2
- data/spec/support/spec_helper.rb +2 -0
- data/spec/support/system/spec_system_helper.rb +7 -6
- data/spec/support/system/system_context.rb +21 -3
- data/spec/support/working_dir_tracking.rb +49 -0
- data/spec/system/build_output_mirroring_spec.rb +2 -2
- data/spec/system/conditional_mock_includes_spec.rb +1 -1
- data/spec/system/delta_builds_spec.rb +137 -11
- data/spec/system/gcov_deployment_spec.rb +1 -0
- data/spec/system/header_include_path_validation_spec.rb +4 -4
- data/spec/system/launcher_handoff_spec.rb +82 -0
- data/spec/system/mock_include_folders_spec.rb +91 -0
- data/spec/system/parent_directory_reference_spec.rb +5 -3
- data/spec/system/path_handling_probes_spec.rb +192 -0
- data/spec/system/preprocessing_parameterized_tests_spec.rb +1 -1
- data/spec/system/rake_release_task_path_disambiguation_spec.rb +6 -6
- data/spec/system/rake_test_task_path_disambiguation_spec.rb +85 -0
- data/spec/system/special_path_characters_spec.rb +86 -0
- data/spec/system/support/common_test_cases.rb +23 -21
- data/spec/system/support/gcov_common_test_cases.rb +37 -9
- data/spec/system/support/sanitize_common_test_cases.rb +2 -2
- data/spec/system/support/valgrind_common_test_cases.rb +12 -12
- data/spec/system/test_pipeline_stop_points_spec.rb +4 -4
- data/spec/system/test_runner_system_include_spec.rb +1 -1
- data/spec/system/test_source_file_directive_spec.rb +34 -5
- data/spec/units/bin/actionator_spec.rb +14 -17
- data/spec/units/bin/app_cfg_spec.rb +37 -0
- data/spec/units/bin/cli_handler_spec.rb +282 -6
- data/spec/units/bin/cli_helper_spec.rb +116 -45
- data/spec/units/bin/installation_selector_spec.rb +114 -0
- data/spec/units/bin/launchinator_spec.rb +171 -0
- data/spec/units/bin/path_validator_spec.rb +10 -0
- data/spec/units/bin/versionator_spec.rb +38 -6
- data/spec/units/build_log_writer_spec.rb +57 -0
- data/spec/units/config/build_root_policy_spec.rb +61 -0
- data/spec/units/config/configurator_builder_spec.rb +37 -6
- data/spec/units/config/configurator_paths_validator_spec.rb +292 -0
- data/spec/units/config/configurator_setup_spec.rb +9 -65
- data/spec/units/config/configurator_spec.rb +27 -5
- data/spec/units/config/configurator_validator_spec.rb +1 -172
- data/spec/units/default_tools_quoting_spec.rb +83 -0
- data/spec/units/dependencies/dependency_cache_store_spec.rb +33 -2
- data/spec/units/dependencies/dependency_debug_tree_spec.rb +1 -1
- data/spec/units/dependencies/dependency_hasher_spec.rb +42 -1
- data/spec/units/dependencies/dependency_path_normalizer_spec.rb +1 -1
- data/spec/units/dependencies/dependency_tracker_spec.rb +3 -1
- data/spec/units/filename_extension_spec.rb +11 -0
- data/spec/units/filesystem/build_subpath_spec.rb +122 -0
- data/spec/units/filesystem/destination_collision_spec.rb +30 -0
- data/spec/units/{file_finder_helper_spec.rb → filesystem/file_finder_helper_spec.rb} +46 -1
- data/spec/units/{file_finder_spec.rb → filesystem/file_finder_spec.rb} +188 -22
- data/spec/units/{file_path_collection_utils_spec.rb → filesystem/file_path_collection_utils_spec.rb} +181 -1
- data/spec/units/{file_path_utils_spec.rb → filesystem/file_path_utils_spec.rb} +204 -87
- data/spec/units/filesystem/file_system_wrapper_spec.rb +33 -0
- data/spec/units/filesystem/file_tree_spec.rb +511 -0
- data/spec/units/filesystem/file_tree_windows_spec.rb +78 -0
- data/spec/units/{file_wrapper_spec.rb → filesystem/file_wrapper_spec.rb} +95 -12
- data/spec/units/{path_matcher_spec.rb → filesystem/path_matcher_spec.rb} +12 -1
- data/spec/units/{path_mirror_spec.rb → filesystem/path_mirror_spec.rb} +32 -4
- data/spec/units/filesystem/project_paths_spec.rb +147 -0
- data/spec/units/filesystem/working_dir_spec.rb +71 -0
- data/spec/units/generators/generator_spec.rb +12 -6
- data/spec/units/generators/generator_test_results_backtrace_spec.rb +50 -46
- data/spec/units/generators/generator_test_results_spec.rb +1 -1
- data/spec/units/handoff_spec.rb +205 -0
- data/spec/units/includes/include_factory_spec.rb +3 -3
- data/spec/units/includes/include_pathinator_spec.rb +46 -0
- data/spec/units/installation_spec.rb +228 -0
- data/spec/units/library_resolver_spec.rb +157 -0
- data/spec/units/mock_locator_spec.rb +76 -1
- data/spec/units/partials/partializer_config_spec.rb +7 -0
- data/spec/units/partials/partializer_spec.rb +1 -0
- data/spec/units/partials_manager_spec.rb +17 -0
- data/spec/units/plugins/bullseye_spec.rb +15 -2
- data/spec/units/plugins/console_reportinator_spec.rb +18 -20
- data/spec/units/plugins/dependencies_spec.rb +33 -7
- data/spec/units/plugins/dependency_spec_spec.rb +17 -0
- data/spec/units/plugins/gcov_spec.rb +28 -3
- data/spec/units/plugins/gcovr_reportinator_spec.rb +19 -47
- data/spec/units/plugins/gcovr_thresholds_spec.rb +95 -0
- data/spec/units/plugins/module_generator_spec.rb +5 -5
- data/spec/units/plugins/report_build_warnings_log_spec.rb +3 -3
- data/spec/units/plugins/report_log_writer_plugin_spec.rb +10 -1
- data/spec/units/plugins/report_tests_raw_output_log_spec.rb +13 -2
- data/spec/units/plugins/report_tests_teamcity_stdout_spec.rb +77 -3
- data/spec/units/plugins/reportgenerator_reportinator_spec.rb +1 -1
- data/spec/units/plugins/sanitize_records_spec.rb +3 -1
- data/spec/units/plugins/sanitize_spec.rb +1 -1
- data/spec/units/plugins/valgrind_spec.rb +23 -8
- data/spec/units/preprocess/preprocessinator_comment_stripper_spec.rb +1 -1
- data/spec/units/rake_app/rakefile_component_resolver_spec.rb +21 -0
- data/spec/units/release_build_executor_spec.rb +53 -14
- data/spec/units/release_build_planner_spec.rb +15 -3
- data/spec/units/reportinator_spec.rb +5 -5
- data/spec/units/standin_headerinator_spec.rb +31 -1
- data/spec/units/test_build_executor_spec.rb +65 -3
- data/spec/units/test_build_planner_spec.rb +104 -2
- data/spec/units/test_build_setup_spec.rb +96 -12
- data/spec/units/test_context_extractor_spec.rb +25 -0
- data/spec/units/test_file_preprocessinator_spec.rb +17 -1
- data/spec/units/test_includes_extractinator_spec.rb +5 -1
- data/spec/units/test_invoker/objects_wiring_spec.rb +6 -3
- data/spec/units/test_invoker_spec.rb +14 -1
- data/spec/units/test_source_file_directive_resolver_spec.rb +38 -7
- data/spec/units/tool_executor_spec.rb +26 -0
- data/spec/units/yaml_wrapper_spec.rb +11 -0
- metadata +109 -25
- data/lib/ceedling/file_path_collection_utils.rb +0 -164
|
@@ -0,0 +1,198 @@
|
|
|
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 tests are named, across project layouts with same-named tests (GH #1364). A project's YAML runs through the real
|
|
9
|
+
# configuration pipeline in a temporary project, so test paths and collections come from
|
|
10
|
+
# real globs. A real FilePathUtils names each test, and a real TestBuildSetup prepares its
|
|
11
|
+
# directories, as stage 1 of a build does.
|
|
12
|
+
|
|
13
|
+
require 'spec_helper'
|
|
14
|
+
require 'configurator_integration_helper'
|
|
15
|
+
require 'ceedling/filesystem/file_path_utils'
|
|
16
|
+
require 'ceedling/filesystem/file_finder'
|
|
17
|
+
require 'ceedling/filesystem/file_finder_helper'
|
|
18
|
+
require 'ceedling/test_invoker/test_build_setup'
|
|
19
|
+
require 'ceedling/test_invoker/test_invoker_types'
|
|
20
|
+
|
|
21
|
+
describe 'Test naming (integration)' do
|
|
22
|
+
include ConfiguratorIntegrationHelpers
|
|
23
|
+
|
|
24
|
+
# Runs each test of a batch in turn, in place of the thread pool
|
|
25
|
+
class TestNamingBatchinator
|
|
26
|
+
def exec(things:, **_options)
|
|
27
|
+
things.each { |thing| yield thing }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# name => [test paths, `:paths ↳ :test` entries, `:files ↳ :test` entries]
|
|
32
|
+
TEST_NAMING_LAYOUTS = {
|
|
33
|
+
'flat and nested under one root' => [%w[test/test_calc.c test/unit/test_calc.c], %w[test/**], []],
|
|
34
|
+
'the same subdirectory under two roots' => [%w[test/unit/test_calc.c extra/unit/test_calc.c], %w[test/unit/** extra/**], []],
|
|
35
|
+
'overlapping roots' => [%w[test/test_calc.c test/unit/test_calc.c], %w[test/** test/unit/**], []],
|
|
36
|
+
'the same layout under two roots' => [%w[test/foo/test_x.c extra/foo/test_x.c], %w[test/** extra/**], []],
|
|
37
|
+
'a :files test outside every root' => [%w[test/test_calc.c other/test_calc.c], %w[test/**], %w[other/test_calc.c]]
|
|
38
|
+
}.freeze
|
|
39
|
+
|
|
40
|
+
def project_yaml(test_paths, test_files)
|
|
41
|
+
return <<~YAML
|
|
42
|
+
:project:
|
|
43
|
+
:build_root: build
|
|
44
|
+
:paths:
|
|
45
|
+
:test: #{test_paths.inspect}
|
|
46
|
+
:source: [src/**]
|
|
47
|
+
:include: [src/**]
|
|
48
|
+
:files:
|
|
49
|
+
:test: #{test_files.inspect}
|
|
50
|
+
YAML
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def project_files(tests)
|
|
54
|
+
files = { 'src/calc.c' => "int calc(void) { return 1; }\n", 'src/calc.h' => "int calc(void);\n" }
|
|
55
|
+
tests.each { |test| files[test] = "#include \"unity.h\"\nvoid test_it(void) {}\n" }
|
|
56
|
+
return files
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Stage 1 of a build of `tests`, with real naming and real directories
|
|
60
|
+
def prepare(configurator, tests)
|
|
61
|
+
file_wrapper = FileWrapper.new( loginator: double( 'loginator', log: nil ), verbosinator: double( 'verbosinator', should_output?: false ) )
|
|
62
|
+
file_path_utils = FilePathUtils.new( configurator: configurator, file_wrapper: file_wrapper )
|
|
63
|
+
|
|
64
|
+
collaborators = TestBuildSetup.constructor_keys.to_h { |key| [key, double( key.to_s )] }
|
|
65
|
+
setup = TestBuildSetup.new( collaborators.merge(
|
|
66
|
+
configurator: configurator, file_wrapper: file_wrapper, file_path_utils: file_path_utils, batchinator: TestNamingBatchinator.new
|
|
67
|
+
) )
|
|
68
|
+
|
|
69
|
+
state = TestInvokerTypes::PipelineState.new( tests: tests, testables: {}, context: :test, options: [], lock: Mutex.new )
|
|
70
|
+
setup.stage_prepare_build_paths( state )
|
|
71
|
+
return [state, file_path_utils]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def finder(configurator, file_path_utils)
|
|
75
|
+
loginator = double( 'loginator', log: nil )
|
|
76
|
+
return FileFinder.new(
|
|
77
|
+
configurator: configurator, file_finder_helper: FileFinderHelper.new( loginator: loginator ), file_path_utils: file_path_utils,
|
|
78
|
+
file_wrapper: FileWrapper.new( loginator: loginator, verbosinator: double( 'verbosinator', should_output?: false ) ), yaml_wrapper: nil
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Each test's own object resolves to that test, by name and from its object path alone
|
|
83
|
+
def expect_own_objects_resolve(configurator, state, file_path_utils)
|
|
84
|
+
finder = finder( configurator, file_path_utils )
|
|
85
|
+
state.testables.each_value do |testable|
|
|
86
|
+
object = File.join( testable.paths[:build], File.basename( testable.filepath ).ext( '.o' ) )
|
|
87
|
+
expect(finder.find_build_input_file( filepath: object, context: :test, test: testable.name )).to eq( testable.filepath )
|
|
88
|
+
expect(finder.find_build_input_file( filepath: object, context: :test )).to eq( testable.filepath )
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
TEST_NAMING_LAYOUTS.each do |layout, (tests, test_paths, test_files)|
|
|
93
|
+
context "with #{layout}" do
|
|
94
|
+
it 'names each test by its path, inside the build directory, with results where summaries look' do
|
|
95
|
+
in_temp_project( project_files( tests ) ) do
|
|
96
|
+
configurator = configure( project_yaml( test_paths, test_files ) )[:configurator]
|
|
97
|
+
expect(configurator.collection_all_tests).to match_array( tests )
|
|
98
|
+
|
|
99
|
+
state, file_path_utils = prepare( configurator, configurator.collection_all_tests )
|
|
100
|
+
|
|
101
|
+
expect(state.testables.keys.map( &:to_s )).to match_array( tests.map { |test| test.ext( '' ) } )
|
|
102
|
+
|
|
103
|
+
build = File.expand_path( 'build' ) + '/'
|
|
104
|
+
state.testables.each_value do |testable|
|
|
105
|
+
testable.paths.each_value { |path| expect(File.expand_path( path )).to start_with( build ) }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
expected = state.testables.values.map { |testable| file_path_utils.form_pass_results_filepath( testable.paths[:results], testable.filepath ) }
|
|
109
|
+
expect(file_path_utils.form_pass_results_filelist( configurator.project_test_results_path, tests )).to match_array( expected )
|
|
110
|
+
expect(expected.uniq.size).to eq( tests.size )
|
|
111
|
+
|
|
112
|
+
expect_own_objects_resolve( configurator, state, file_path_utils )
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# One test's name continues another's, so its directories nest inside the other's
|
|
119
|
+
context 'with a test directory named like a test beside it' do
|
|
120
|
+
it "keeps each test's mocks its own" do
|
|
121
|
+
tests = %w[test/test_foo.c test/test_foo/test_bar.c]
|
|
122
|
+
in_temp_project( project_files( tests ) ) do
|
|
123
|
+
configurator = configure( project_yaml( %w[test/**], [] ) )[:configurator]
|
|
124
|
+
state, file_path_utils = prepare( configurator, configurator.collection_all_tests )
|
|
125
|
+
expect_own_objects_resolve( configurator, state, file_path_utils )
|
|
126
|
+
|
|
127
|
+
mock = "#{configurator.cmock_mock_prefix}widget"
|
|
128
|
+
state.testables.each_value do |testable|
|
|
129
|
+
FileUtils.mkdir_p( testable.paths[:mocks] )
|
|
130
|
+
File.write( File.join( testable.paths[:mocks], "#{mock}.c" ), "\n" )
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
finder = finder( configurator, file_path_utils )
|
|
134
|
+
state.testables.each_value do |testable|
|
|
135
|
+
object = File.join( testable.paths[:build], "#{mock}.o" )
|
|
136
|
+
expect(finder.find_build_input_file( filepath: object, context: :test, test: testable.name )).to eq( File.join( testable.paths[:mocks], "#{mock}.c" ) )
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
context 'with tests outside the project' do
|
|
143
|
+
def outside_files
|
|
144
|
+
return project_files( [] ).transform_keys { |path| File.join( 'app', path ) }.merge(
|
|
145
|
+
'shared/test/unit/test_s.c' => "void test_it(void) {}\n",
|
|
146
|
+
'other/test_o.c' => "void test_it(void) {}\n"
|
|
147
|
+
)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it 'keeps their names and directories inside the build directory' do
|
|
151
|
+
in_temp_project( outside_files ) do
|
|
152
|
+
Dir.chdir( 'app' ) do
|
|
153
|
+
configurator = configure( project_yaml( %w[../shared/test/**], %w[../other/test_o.c] ) )[:configurator]
|
|
154
|
+
|
|
155
|
+
state, file_path_utils = prepare( configurator, configurator.collection_all_tests )
|
|
156
|
+
|
|
157
|
+
expect(state.testables.keys.map( &:to_s )).to match_array( %w[_up_/shared/test/unit/test_s _up_/other/test_o] )
|
|
158
|
+
expect_own_objects_resolve( configurator, state, file_path_utils )
|
|
159
|
+
build = File.expand_path( 'build' ) + '/'
|
|
160
|
+
state.testables.each_value do |testable|
|
|
161
|
+
testable.paths.each_value { |path| expect(File.expand_path( path )).to start_with( build ) }
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# A project directory named like the marker a test outside the project is named by keeps
|
|
168
|
+
# a name of its own
|
|
169
|
+
it 'names a test below a project directory called `_up_` apart from one above the project' do
|
|
170
|
+
files = outside_files.merge( 'app/_up_/other/test_o.c' => "void test_it(void) {}\n" )
|
|
171
|
+
|
|
172
|
+
in_temp_project( files ) do
|
|
173
|
+
Dir.chdir( 'app' ) do
|
|
174
|
+
configurator = configure( project_yaml( %w[../shared/test/** _up_/**], %w[../other/test_o.c] ) )[:configurator]
|
|
175
|
+
|
|
176
|
+
state, _file_path_utils = prepare( configurator, ['_up_/other/test_o.c', '../other/test_o.c'] )
|
|
177
|
+
|
|
178
|
+
expect(state.testables.keys.map( &:to_s )).to match_array( %w[__up_/other/test_o _up_/other/test_o] )
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it 'stops a build that has two tests sharing one name, naming both' do
|
|
184
|
+
files = outside_files.merge( 'app/test/test_d.c' => "void test_it(void) {}\n", 'app/test/test_d.cpp' => "void test_it(void) {}\n" )
|
|
185
|
+
|
|
186
|
+
in_temp_project( files ) do
|
|
187
|
+
Dir.chdir( 'app' ) do
|
|
188
|
+
yaml = project_yaml( %w[test/**], [] ) + ":extension:\n :source: ['.c', '.cpp']\n"
|
|
189
|
+
configurator = configure( yaml )[:configurator]
|
|
190
|
+
|
|
191
|
+
expect { prepare( configurator, ['test/test_d.c'] ) }.to raise_error( CeedlingException ) { |error|
|
|
192
|
+
expect(error.message).to include( 'test/test_d.c', 'test/test_d.cpp', '`test/test_d`' )
|
|
193
|
+
}
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
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
|
+
# Checking TEST_SOURCE_FILE() entries against a real project. Real test files are scanned by
|
|
9
|
+
# the real context extractor, and the real resolver looks each entry up through the real
|
|
10
|
+
# file finder in a temporary project configured by the real configuration pipeline.
|
|
11
|
+
#
|
|
12
|
+
# Each test's entries are checked once, under that test's own name, with test preprocessing
|
|
13
|
+
# on or off, so a misspelled entry stops the build naming the test that holds it (GH #1364).
|
|
14
|
+
|
|
15
|
+
require 'spec_helper'
|
|
16
|
+
require 'configurator_integration_helper'
|
|
17
|
+
require 'ceedling/parsing_parcels'
|
|
18
|
+
require 'ceedling/includes/include_factory'
|
|
19
|
+
require 'ceedling/filesystem/file_finder'
|
|
20
|
+
require 'ceedling/filesystem/file_finder_helper'
|
|
21
|
+
require 'ceedling/filesystem/file_path_utils'
|
|
22
|
+
require 'ceedling/test_context_extractor'
|
|
23
|
+
require 'ceedling/test_invoker/test_source_file_directive_resolver'
|
|
24
|
+
require 'ceedling/test_invoker/test_build_setup'
|
|
25
|
+
require 'ceedling/test_invoker/test_invoker_types'
|
|
26
|
+
|
|
27
|
+
describe 'TEST_SOURCE_FILE() checks (integration)' do
|
|
28
|
+
include ConfiguratorIntegrationHelpers
|
|
29
|
+
|
|
30
|
+
# Runs each test of a batch in turn, in place of the thread pool
|
|
31
|
+
class TestSourceFileChecksBatchinator
|
|
32
|
+
def exec(things:, **_options)
|
|
33
|
+
things.each { |key, value| yield key, value }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_file(entries)
|
|
38
|
+
lines = entries.map { |entry| "TEST_SOURCE_FILE(\"#{entry}\")\n" }.join
|
|
39
|
+
return "#include \"unity.h\"\n#{lines}void test_it(void) {}\n"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def project_files
|
|
43
|
+
return {
|
|
44
|
+
'src/calc.c' => "int calc(void) { return 1; }\n",
|
|
45
|
+
'src/calc.h' => "int calc(void);\n",
|
|
46
|
+
'src/a/dup.c' => "int dup(void) { return 1; }\n",
|
|
47
|
+
'lib/a/dup.c' => "int dup(void) { return 2; }\n",
|
|
48
|
+
'lib/extra.c' => "int extra(void) { return 7; }\n",
|
|
49
|
+
'test/test_m1.c' => test_file( ['calc.c'] ),
|
|
50
|
+
'test/test_m2.c' => test_file( ['calc.c'] ),
|
|
51
|
+
'test/unit/test_m3.c' => test_file( ['../../lib/extra.c'] )
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def project_yaml(preprocessor: :none)
|
|
56
|
+
return <<~YAML
|
|
57
|
+
:project:
|
|
58
|
+
:build_root: build
|
|
59
|
+
:use_test_preprocessor: :#{preprocessor}
|
|
60
|
+
:paths:
|
|
61
|
+
:test: [test/**]
|
|
62
|
+
:source: [src/**, lib/**]
|
|
63
|
+
:include: [src/**]
|
|
64
|
+
YAML
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def objects
|
|
68
|
+
graph = configure( project_yaml )
|
|
69
|
+
configurator = graph[:configurator]
|
|
70
|
+
loginator = graph[:loginator]
|
|
71
|
+
file_wrapper = graph[:file_wrapper]
|
|
72
|
+
|
|
73
|
+
file_path_utils = FilePathUtils.new( configurator: configurator, file_wrapper: file_wrapper )
|
|
74
|
+
file_finder = FileFinder.new(
|
|
75
|
+
configurator: configurator, file_finder_helper: FileFinderHelper.new( loginator: loginator ),
|
|
76
|
+
file_path_utils: file_path_utils, file_wrapper: file_wrapper, yaml_wrapper: graph[:yaml_wrapper]
|
|
77
|
+
)
|
|
78
|
+
extractor = TestContextExtractor.new(
|
|
79
|
+
configurator: configurator, parsing_parcels: ParsingParcels.new, include_factory: IncludeFactory.new( configurator: configurator ),
|
|
80
|
+
partializer_config: double( 'partializer_config' ).as_null_object, file_path_utils: file_path_utils, file_wrapper: file_wrapper,
|
|
81
|
+
yaml_wrapper: graph[:yaml_wrapper], loginator: loginator
|
|
82
|
+
)
|
|
83
|
+
resolver = TestSourceFileDirectiveResolver.new(
|
|
84
|
+
test_context_extractor: extractor, file_finder: file_finder, configurator: configurator, loginator: loginator
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return { configurator: configurator, file_path_utils: file_path_utils, file_finder: file_finder, extractor: extractor, resolver: resolver }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def scan(extractor, tests)
|
|
91
|
+
tests.each { |test| extractor.collect_simple_context_from_file( test, nil, TestContextExtractor::Context::BUILD_DIRECTIVE_SOURCE_FILES ) }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "passes a test's real entries, including one reached through .." do
|
|
95
|
+
in_temp_project( project_files ) do
|
|
96
|
+
o = objects
|
|
97
|
+
tests = %w[test/test_m1.c test/test_m2.c test/unit/test_m3.c]
|
|
98
|
+
scan( o[:extractor], tests )
|
|
99
|
+
|
|
100
|
+
tests.each do |test|
|
|
101
|
+
expect { o[:resolver].validate!( test: test.ext( '' ), test_filepath: test, context: :test ) }.to_not raise_error
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it 'names the test that holds a missing entry' do
|
|
107
|
+
in_temp_project( project_files.merge( 'test/test_m2.c' => test_file( ['calc_typo.c'] ) ) ) do
|
|
108
|
+
o = objects
|
|
109
|
+
scan( o[:extractor], %w[test/test_m1.c test/test_m2.c] )
|
|
110
|
+
|
|
111
|
+
expect { o[:resolver].validate!( test: 'test/test_m1', test_filepath: 'test/test_m1.c', context: :test ) }.to_not raise_error
|
|
112
|
+
expect { o[:resolver].validate!( test: 'test/test_m2', test_filepath: 'test/test_m2.c', context: :test ) }.to raise_error(
|
|
113
|
+
CeedlingException, %r{'calc_typo\.c' specified with TEST_SOURCE_FILE\(\) in test/test_m2 cannot be found}
|
|
114
|
+
)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'stops for a misspelled removal entry, which otherwise leaves its file in the build' do
|
|
119
|
+
in_temp_project( project_files.merge( 'test/test_m1.c' => test_file( ['-:clac.c'] ) ) ) do
|
|
120
|
+
o = objects
|
|
121
|
+
scan( o[:extractor], %w[test/test_m1.c] )
|
|
122
|
+
|
|
123
|
+
expect { o[:resolver].validate!( test: 'test/test_m1', test_filepath: 'test/test_m1.c', context: :test ) }.to raise_error(
|
|
124
|
+
CeedlingException, /'clac\.c'.*cannot be found/
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it 'notes an entry matching several files once, however many tests name it' do
|
|
130
|
+
files = project_files.merge( %w[test/test_m1.c test/test_m2.c test/unit/test_m3.c].to_h { |test| [test, test_file( ['a/dup.c'] )] } )
|
|
131
|
+
|
|
132
|
+
in_temp_project( files ) do
|
|
133
|
+
o = objects
|
|
134
|
+
tests = %w[test/test_m1.c test/test_m2.c test/unit/test_m3.c]
|
|
135
|
+
scan( o[:extractor], tests )
|
|
136
|
+
|
|
137
|
+
tests.each do |test|
|
|
138
|
+
o[:resolver].validate!( test: test.ext( '' ), test_filepath: test, context: :test )
|
|
139
|
+
o[:resolver].resolve( test, :test )
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
expect( logged.grep( /Multiple files matched 'a\/dup\.c'/ ).size ).to eq( 1 )
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Without test preprocessing, stage 2 scans the entries and checks them
|
|
147
|
+
it 'checks each test under its own name in stage 2 without test preprocessing' do
|
|
148
|
+
in_temp_project( project_files.merge( 'test/unit/test_m3.c' => test_file( ['nowhere.c'] ) ) ) do
|
|
149
|
+
o = objects
|
|
150
|
+
setup = TestBuildSetup.new( TestBuildSetup.constructor_keys.to_h { |key| [key, double( key.to_s ).as_null_object] }.merge(
|
|
151
|
+
configurator: o[:configurator], batchinator: TestSourceFileChecksBatchinator.new, test_context_extractor: o[:extractor],
|
|
152
|
+
file_path_utils: o[:file_path_utils], file_finder: o[:file_finder], test_source_file_directive_resolver: o[:resolver],
|
|
153
|
+
file_wrapper: FileWrapper.new( loginator: double( 'loginator', log: nil ), verbosinator: double( 'verbosinator', should_output?: false ) ),
|
|
154
|
+
dependinator: double( 'dependinator', register: nil, stale?: true, mark_fresh: nil ),
|
|
155
|
+
reportinator: double( 'reportinator', generate_progress: '', generate_skip_summary: nil )
|
|
156
|
+
) )
|
|
157
|
+
tests = %w[test/test_m1.c test/test_m2.c test/unit/test_m3.c]
|
|
158
|
+
state = TestInvokerTypes::PipelineState.new( tests: tests, testables: {}, context: :test, options: [], lock: Mutex.new )
|
|
159
|
+
setup.stage_prepare_build_paths( state )
|
|
160
|
+
|
|
161
|
+
expect { setup.stage_collect_test_context( state ) }.to raise_error(
|
|
162
|
+
CeedlingException, %r{'nowhere\.c' specified with TEST_SOURCE_FILE\(\) in test/unit/test_m3 cannot be found}
|
|
163
|
+
)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -17,7 +17,7 @@ require 'spec_helper'
|
|
|
17
17
|
require 'spec_integration_helper'
|
|
18
18
|
require 'fileutils'
|
|
19
19
|
require 'ceedling/tool_validator'
|
|
20
|
-
require 'ceedling/file_wrapper'
|
|
20
|
+
require 'ceedling/filesystem/file_wrapper'
|
|
21
21
|
require 'ceedling/system_wrapper'
|
|
22
22
|
require 'ceedling/ruby_expandinator'
|
|
23
23
|
require 'ceedling/filename_extension'
|
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
# GH #1371: `upgrade` never refreshed documentation that `new --docs` exported, because it
|
|
9
|
+
# looked for a file exports stopped writing in 1.1.0. For a 1.0.x export, which it did
|
|
10
|
+
# find, it removed all of docs/, the project's own files included.
|
|
11
|
+
#
|
|
12
|
+
# These run the real launcher against real projects. Nothing compiles, so no toolchain is
|
|
13
|
+
# needed.
|
|
14
|
+
|
|
15
|
+
require 'spec_helper'
|
|
16
|
+
require 'tmpdir'
|
|
17
|
+
require 'fileutils'
|
|
18
|
+
require 'open3'
|
|
19
|
+
require 'rbconfig'
|
|
20
|
+
|
|
21
|
+
describe '`upgrade` and exported documentation' do
|
|
22
|
+
REPO = File.expand_path( '../..', __dir__ )
|
|
23
|
+
UNITY_PAGE = File.join( 'unity', 'UnityGettingStartedGuide.md' )
|
|
24
|
+
|
|
25
|
+
def ceedling(*args)
|
|
26
|
+
stdout, stderr, status = Open3.capture3(
|
|
27
|
+
{'CEEDLING_DECORATORS' => 'false'}, RbConfig.ruby, File.join( REPO, 'bin', 'ceedling' ), *args, chdir: @tmp
|
|
28
|
+
)
|
|
29
|
+
raise "`ceedling #{args.join( ' ' )}` failed:\n#{stdout}#{stderr}" unless status.success?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def docs(*entry)
|
|
33
|
+
return File.join( @tmp, 'proj', 'docs', *entry )
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def write(entry, text='')
|
|
37
|
+
FileUtils.mkdir_p( File.dirname( docs( entry ) ) )
|
|
38
|
+
File.write( docs( entry ), text )
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
around(:each) do |example|
|
|
42
|
+
Dir.mktmpdir( 'upgrade_docs' ) { |dir| @tmp = dir; example.run }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'refreshes an export of 1.1.0 or later and keeps the project\'s own files' do
|
|
46
|
+
ceedling( 'new', '--local', '--docs', 'proj' )
|
|
47
|
+
write( 'notes.md', 'mine' )
|
|
48
|
+
write( File.join( 'unity', 'stale.md' ) )
|
|
49
|
+
write( UNITY_PAGE, 'edited' )
|
|
50
|
+
|
|
51
|
+
ceedling( 'upgrade', 'proj' )
|
|
52
|
+
|
|
53
|
+
expect(File.exist?( docs( 'unity', 'stale.md' ) )).to be( false )
|
|
54
|
+
expect(File.read( docs( UNITY_PAGE ) )).to eq( File.read( File.join( REPO, 'vendor', 'unity', 'docs', 'UnityGettingStartedGuide.md' ) ) )
|
|
55
|
+
expect(File.read( docs( 'notes.md' ) )).to eq( 'mine' )
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'replaces a 1.0.x export with the current layout and keeps the project\'s own files' do
|
|
59
|
+
ceedling( 'new', '--local', 'proj' )
|
|
60
|
+
write( 'CeedlingPacket.md' )
|
|
61
|
+
write( 'Changelog.md' )
|
|
62
|
+
write( File.join( 'plugins', 'gcov.md' ) )
|
|
63
|
+
write( File.join( 'unity', 'stale.md' ) )
|
|
64
|
+
write( 'license.txt' )
|
|
65
|
+
write( 'notes.md', 'mine' )
|
|
66
|
+
|
|
67
|
+
ceedling( 'upgrade', 'proj' )
|
|
68
|
+
|
|
69
|
+
['CeedlingPacket.md', 'Changelog.md', 'plugins', File.join( 'unity', 'stale.md' )].each do |entry|
|
|
70
|
+
expect(File.exist?( docs( entry ) )).to be( false ), entry
|
|
71
|
+
end
|
|
72
|
+
expect(File.exist?( docs( UNITY_PAGE ) )).to be( true )
|
|
73
|
+
expect(File.read( docs( 'notes.md' ) )).to eq( 'mine' )
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'leaves a docs/ holding only the project\'s own files alone' do
|
|
77
|
+
ceedling( 'new', '--local', 'proj' )
|
|
78
|
+
write( 'notes.md', 'mine' )
|
|
79
|
+
|
|
80
|
+
ceedling( 'upgrade', 'proj' )
|
|
81
|
+
|
|
82
|
+
expect(Dir.children( docs )).to eq( ['notes.md'] )
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -8,26 +8,29 @@
|
|
|
8
8
|
# ConfiguratorSetup#vendor_frameworks_and_support_files with a real FileWrapper on a real
|
|
9
9
|
# filesystem. A build brings build/vendor up to date with the gem's frameworks. It writes only
|
|
10
10
|
# a file that differs from its source, and replaces it whole by renaming a temporary copy over
|
|
11
|
-
# it, so a concurrent build never reads a half-written header (GH #1361).
|
|
12
|
-
#
|
|
11
|
+
# it, so a concurrent build never reads a half-written header (GH #1361). An entry of the
|
|
12
|
+
# wrong type, such as a directory where a file belongs, is replaced (GH #1292, #1376). A
|
|
13
|
+
# transient copy fault, such as a scanner holding a file open, is retried, and one that never
|
|
13
14
|
# clears names the destination.
|
|
14
15
|
#
|
|
15
16
|
# Faults are injected at FileWrapper#replace_file, the one write of each file, so the retry
|
|
16
|
-
# loop in
|
|
17
|
+
# loop in FileTree is what runs.
|
|
17
18
|
|
|
18
19
|
require 'spec_helper'
|
|
19
20
|
require 'spec_integration_helper'
|
|
21
|
+
require 'file_tree_helper'
|
|
20
22
|
require 'ceedling/config/configurator_setup'
|
|
21
23
|
require 'ceedling/constants'
|
|
22
24
|
|
|
23
25
|
describe 'Vendor copy into build/vendor (integration)' do
|
|
24
26
|
include IntegrationSpecHelpers
|
|
27
|
+
include FileTreeHelper
|
|
25
28
|
|
|
26
29
|
# A real FileWrapper whose #replace_file raises for files beneath `fault_dir` the first
|
|
27
30
|
# `raises_left` times. Named apart from other specs' faulting wrappers, since top-level
|
|
28
31
|
# classes share one RSpec process.
|
|
29
32
|
class ReplaceFaultingFileWrapper < FileWrapper
|
|
30
|
-
def initialize(fault_dir:, raises_left: 1, error: Errno::
|
|
33
|
+
def initialize(fault_dir:, raises_left: 1, error: Errno::EACCES, **kwargs)
|
|
31
34
|
super(**kwargs)
|
|
32
35
|
@fault_dir = fault_dir
|
|
33
36
|
@raises_left = raises_left
|
|
@@ -37,7 +40,7 @@ describe 'Vendor copy into build/vendor (integration)' do
|
|
|
37
40
|
def replace_file(source, destination)
|
|
38
41
|
if destination.start_with?( @fault_dir ) && @raises_left > 0
|
|
39
42
|
@raises_left -= 1
|
|
40
|
-
raise @error, "simulated
|
|
43
|
+
raise @error, "simulated fault on #{destination}"
|
|
41
44
|
end
|
|
42
45
|
super
|
|
43
46
|
end
|
|
@@ -52,6 +55,7 @@ describe 'Vendor copy into build/vendor (integration)' do
|
|
|
52
55
|
ConfiguratorSetup.new(
|
|
53
56
|
configurator_builder: IntegrationSpecHelpers::NULL,
|
|
54
57
|
configurator_validator: IntegrationSpecHelpers::NULL,
|
|
58
|
+
configurator_paths_validator: IntegrationSpecHelpers::NULL,
|
|
55
59
|
loginator: IntegrationSpecHelpers::NULL,
|
|
56
60
|
reportinator: IntegrationSpecHelpers::NULL,
|
|
57
61
|
file_wrapper: file_wrapper,
|
|
@@ -175,4 +179,69 @@ describe 'Vendor copy into build/vendor (integration)' do
|
|
|
175
179
|
}.to raise_error( CeedlingException, /#{Regexp.escape( dest )}/ )
|
|
176
180
|
end
|
|
177
181
|
end
|
|
182
|
+
# GH #1376: a deduplicated file the reporter's lstat called a directory left one where any
|
|
183
|
+
# vendored file belongs, not only the framework's own .c file
|
|
184
|
+
it "replaces a directory where any vendored file belongs" do
|
|
185
|
+
with_source_tree({}) do |dir|
|
|
186
|
+
config = flattened_config( dir )
|
|
187
|
+
dest = config[:project_build_vendor_unity_path]
|
|
188
|
+
FileUtils.mkdir_p( File.join( dest, 'unity.h' ) )
|
|
189
|
+
write( File.join( dest, 'internal', 'unity_internals.h', 'junk' ) )
|
|
190
|
+
|
|
191
|
+
vendor( file_wrapper, dir, config )
|
|
192
|
+
|
|
193
|
+
expect( File.read( File.join( dest, 'unity.h' ) ) ).to eq( "// stand-in for unity.h\n" )
|
|
194
|
+
expect( File.file?( File.join( dest, 'internal', 'unity_internals.h' ) ) ).to be(true)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it "vendors every file as a file when lstat reports files as directories" do
|
|
199
|
+
with_source_tree({}) do |dir|
|
|
200
|
+
config = flattened_config( dir )
|
|
201
|
+
dest = config[:project_build_vendor_unity_path]
|
|
202
|
+
lie_about_lstat( %r{gem_vendor/} )
|
|
203
|
+
|
|
204
|
+
begin
|
|
205
|
+
vendor( file_wrapper, dir, config )
|
|
206
|
+
ensure
|
|
207
|
+
# The temporary directory is removed by an lstat-based remover
|
|
208
|
+
allow(File).to receive(:lstat).and_call_original
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
expect( File.file?( File.join( dest, 'unity.c' ) ) ).to be(true)
|
|
212
|
+
expect( File.file?( File.join( dest, 'internal', 'unity_internals.h' ) ) ).to be(true)
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# A read-only gem install would otherwise vendor read-only files, which Windows will not
|
|
217
|
+
# let a later rename replace
|
|
218
|
+
it "vendors owner-writable files from a read-only source, and replaces a read-only destination" do
|
|
219
|
+
with_source_tree({}) do |dir|
|
|
220
|
+
config = flattened_config( dir )
|
|
221
|
+
dest = config[:project_build_vendor_unity_path]
|
|
222
|
+
source = File.join( config[:unity_vendor_path], 'unity', 'src', 'unity.h' )
|
|
223
|
+
File.chmod( 0o444, source )
|
|
224
|
+
vendor( file_wrapper, dir, config )
|
|
225
|
+
expect( File.stat( File.join( dest, 'unity.h' ) ).mode & 0o200 ).to eq( 0o200 )
|
|
226
|
+
|
|
227
|
+
File.chmod( 0o644, source )
|
|
228
|
+
File.write( source, "// a newer unity.h\n" )
|
|
229
|
+
File.chmod( 0o444, File.join( dest, 'unity.h' ) )
|
|
230
|
+
vendor( file_wrapper, dir, config )
|
|
231
|
+
|
|
232
|
+
expect( File.read( File.join( dest, 'unity.h' ) ) ).to eq( "// a newer unity.h\n" )
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
it "fails at once on an error retrying cannot clear" do
|
|
237
|
+
with_source_tree({}) do |dir|
|
|
238
|
+
config = flattened_config( dir )
|
|
239
|
+
dest = config[:project_build_vendor_unity_path]
|
|
240
|
+
faulting = file_wrapper( fault_dir: dest, raises_left: 999, error: Errno::ENOSPC )
|
|
241
|
+
allow(faulting).to receive(:sleep)
|
|
242
|
+
|
|
243
|
+
expect { vendor( faulting, dir, config ) }.to raise_error( Errno::ENOSPC )
|
|
244
|
+
expect(faulting).not_to have_received(:sleep)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
178
247
|
end
|
|
@@ -0,0 +1,88 @@
|
|
|
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 'delegate'
|
|
9
|
+
require 'fileutils'
|
|
10
|
+
require 'tmpdir'
|
|
11
|
+
|
|
12
|
+
# Support for specs that copy real file trees (`filesystem: true`).
|
|
13
|
+
#
|
|
14
|
+
# CEEDLING_TEST_FS_ROOT names a directory on another filesystem, such as a tmpfs, a vfat
|
|
15
|
+
# image, a UNC share, or a VHD volume. These specs then build their trees there instead of
|
|
16
|
+
# the system temp directory, so CI can run one suite on several filesystems.
|
|
17
|
+
module FileTreeHelper
|
|
18
|
+
ROOT_ENV = 'CEEDLING_TEST_FS_ROOT'
|
|
19
|
+
|
|
20
|
+
# A stat result that claims a directory. Reproduces a filesystem whose `lstat` reports a
|
|
21
|
+
# regular file as a directory.
|
|
22
|
+
class LyingStat < SimpleDelegator
|
|
23
|
+
def directory?
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def file?
|
|
28
|
+
false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def symlink?
|
|
32
|
+
false
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def ftype
|
|
36
|
+
'directory'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def mode
|
|
40
|
+
(__getobj__.mode & ~0o170000) | 0o040000
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# A temporary directory on the filesystem under test, removed afterwards
|
|
45
|
+
def with_tree_root
|
|
46
|
+
root = ENV[ROOT_ENV].to_s
|
|
47
|
+
dir = root.empty? ? Dir.mktmpdir( 'tree' ) : Dir.mktmpdir( 'tree', root )
|
|
48
|
+
yield File.realpath( dir )
|
|
49
|
+
ensure
|
|
50
|
+
FileUtils.rm_rf( dir ) if dir
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def write(path, content = File.basename( path ))
|
|
54
|
+
FileUtils.mkdir_p( File.dirname( path ) )
|
|
55
|
+
File.binwrite( path, content )
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def symlink_or_skip(target, link)
|
|
59
|
+
File.symlink( target, link )
|
|
60
|
+
rescue NotImplementedError, SystemCallError => e
|
|
61
|
+
skip "symlinks are unavailable here (#{e.class})"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Every entry below `dir`, dotfiles included, as sorted relative paths
|
|
65
|
+
def tree_of(dir)
|
|
66
|
+
Dir.glob( '**/*', File::FNM_DOTMATCH, base: dir ).reject { |path| path == '.' || path.end_with?( '/.' ) }.sort
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Makes `File.lstat` lie, claiming a directory, for each path matching `pattern`
|
|
70
|
+
def lie_about_lstat(pattern)
|
|
71
|
+
allow(File).to receive(:lstat).and_wrap_original do |original, path, *rest|
|
|
72
|
+
status = original.call( path, *rest )
|
|
73
|
+
path.to_s.match?( pattern ) ? LyingStat.new( status ) : status
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Fails any `File.lstat` call, proving code under test never classifies by it
|
|
78
|
+
def forbid_lstat
|
|
79
|
+
allow(File).to receive(:lstat).and_raise( RuntimeError, 'File.lstat must not be called' )
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# The publish step FileWrapper#replace_file performs: a whole copy renamed into place
|
|
83
|
+
PUBLISH = lambda do |source, destination|
|
|
84
|
+
temp = "#{destination}.publishing"
|
|
85
|
+
FileUtils.cp( source, temp )
|
|
86
|
+
File.rename( temp, destination )
|
|
87
|
+
end
|
|
88
|
+
end
|