ceedling 1.2.0 → 1.2.1
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 +47 -50
- data/Rakefile +3 -2
- data/bin/app_cfg.rb +21 -0
- data/bin/cli_handler.rb +7 -1
- data/bin/cli_helper.rb +5 -0
- data/docs/Changelog.md +10 -0
- data/docs/overrides/main.html +11 -8
- data/lib/ceedling/config/configurator.rb +5 -2
- data/lib/ceedling/config/configurator_setup.rb +26 -15
- data/lib/ceedling/file_wrapper.rb +56 -26
- data/lib/ceedling/rake_app/test_task_name.rb +29 -0
- data/lib/ceedling/rakefile.rb +1 -0
- data/lib/ceedling/rakefiles/tests/rules_tests.rake +3 -20
- data/lib/ceedling/rakefiles/tests/rules_tests_generate.rake +4 -30
- data/lib/ceedling/setupinator.rb +4 -0
- data/lib/ceedling/test_invoker/test_build_executor.rb +18 -1
- data/lib/version.rb +1 -1
- data/plugins/bullseye/bullseye.rake +6 -9
- data/plugins/gcov/gcov.rake +6 -9
- data/plugins/sanitize/sanitize.rake +6 -6
- data/plugins/valgrind/valgrind.rake +6 -6
- data/site-local/404.html +6 -20
- data/site-local/assets/javascripts/{bundle.d7400e89.min.js → bundle.c21346df.min.js} +3 -3
- data/site-local/assets/javascripts/{bundle.d7400e89.min.js.map → bundle.c21346df.min.js.map} +2 -2
- data/site-local/assets/javascripts/workers/{search.2c215733.min.js → search.a7633da7.min.js} +2 -2
- data/site-local/assets/javascripts/workers/{search.2c215733.min.js.map → search.a7633da7.min.js.map} +1 -1
- data/site-local/assets/stylesheets/main.57a0d79d.min.css +6 -0
- data/site-local/assets/stylesheets/main.57a0d79d.min.css.map +1 -0
- data/site-local/assets/stylesheets/palette.fb2fffe2.min.css +4 -0
- data/site-local/assets/stylesheets/palette.fb2fffe2.min.css.map +1 -0
- data/site-local/configuration/environment-vars.html +6 -20
- data/site-local/configuration/global-collections.html +6 -20
- data/site-local/configuration/index.html +6 -20
- data/site-local/configuration/loading.html +6 -20
- data/site-local/configuration/mixins.html +10 -24
- data/site-local/configuration/parallel-builds.html +6 -20
- data/site-local/configuration/project-file.html +8 -22
- data/site-local/configuration/reference/cexception.html +6 -20
- data/site-local/configuration/reference/cmock.html +6 -20
- data/site-local/configuration/reference/defines.html +16 -30
- data/site-local/configuration/reference/environment.html +10 -24
- data/site-local/configuration/reference/extension.html +6 -20
- data/site-local/configuration/reference/files.html +13 -27
- data/site-local/configuration/reference/flags.html +13 -27
- data/site-local/configuration/reference/index.html +6 -20
- data/site-local/configuration/reference/libraries.html +6 -20
- data/site-local/configuration/reference/mixins.html +6 -20
- data/site-local/configuration/reference/partials.html +6 -20
- data/site-local/configuration/reference/paths.html +11 -25
- data/site-local/configuration/reference/plugins.html +6 -20
- data/site-local/configuration/reference/project.html +16 -30
- data/site-local/configuration/reference/release-build.html +6 -20
- data/site-local/configuration/reference/test-build.html +6 -20
- data/site-local/configuration/reference/test-runner.html +6 -20
- data/site-local/configuration/reference/tools.html +18 -32
- data/site-local/configuration/reference/unity.html +6 -20
- data/site-local/configuration/which-ceedling.html +6 -20
- data/site-local/development/index.html +6 -20
- data/site-local/development/plugins/configuration.html +6 -20
- data/site-local/development/plugins/index.html +6 -20
- data/site-local/development/plugins/plugin-subclass.html +54 -68
- data/site-local/development/plugins/rake-tasks.html +8 -22
- data/site-local/development/releases.html +79 -28
- data/site-local/development/workflow.html +14 -28
- data/site-local/getting-started/builds.html +6 -20
- data/site-local/getting-started/command-line.html +6 -20
- data/site-local/getting-started/example-projects/cipher-quest.html +6 -20
- data/site-local/getting-started/example-projects/index.html +6 -20
- data/site-local/getting-started/example-projects/temp-sensor.html +6 -20
- data/site-local/getting-started/example-projects/wondrous-forest.html +6 -20
- data/site-local/getting-started/index.html +6 -20
- data/site-local/getting-started/installation.html +6 -20
- data/site-local/getting-started/quick-start.html +6 -20
- data/site-local/help.html +6 -20
- data/site-local/index.html +11 -24
- data/site-local/overview/build-system.html +6 -20
- data/site-local/overview/index.html +6 -20
- data/site-local/overview/test-environments.html +6 -20
- data/site-local/overview/tools-and-frameworks.html +6 -20
- data/site-local/plugins/beep.html +6 -20
- data/site-local/plugins/bullseye/index.html +13 -24
- data/site-local/plugins/bullseye/licensing.html +14 -28
- data/site-local/plugins/command-hooks.html +8 -22
- data/site-local/plugins/compile-commands-json-db.html +6 -20
- data/site-local/plugins/cppcheck.html +8 -22
- data/site-local/plugins/dependencies.html +10 -24
- data/site-local/plugins/fff.html +7 -21
- data/site-local/plugins/gcov/examples.html +7 -21
- data/site-local/plugins/gcov/gcovr.html +7 -21
- data/site-local/plugins/gcov/index.html +6 -20
- data/site-local/plugins/gcov/overview.html +9 -20
- data/site-local/plugins/gcov/reportgenerator.html +9 -23
- data/site-local/plugins/gcov/reporting.html +6 -20
- data/site-local/plugins/gcov/setup.html +6 -20
- data/site-local/plugins/gcov/tool-versions.html +6 -20
- data/site-local/plugins/gcov/troubleshooting.html +10 -24
- data/site-local/plugins/index.html +6 -20
- data/site-local/plugins/module-generator.html +21 -35
- data/site-local/plugins/report-build-warnings-log.html +53 -67
- data/site-local/plugins/report-tests-gtestlike-stdout.html +6 -20
- data/site-local/plugins/report-tests-ide-stdout.html +43 -57
- data/site-local/plugins/report-tests-log-factory.html +50 -64
- data/site-local/plugins/report-tests-pretty-stdout.html +7 -21
- data/site-local/plugins/report-tests-raw-output-log.html +6 -20
- data/site-local/plugins/report-tests-teamcity-stdout.html +16 -30
- data/site-local/plugins/sanitize.html +54 -64
- data/site-local/plugins/valgrind.html +46 -56
- data/site-local/project/index.html +6 -20
- data/site-local/project/release-history.html +6 -20
- data/site-local/project/sbom.html +6 -20
- data/site-local/project/upgrade.html +6 -20
- data/site-local/reference/build-directives.html +6 -20
- data/site-local/reference/command-line.html +6 -20
- data/site-local/reference/environment-vars.html +6 -20
- data/site-local/reference/gcov-plugin.html +6 -20
- data/site-local/reference/global-collections.html +6 -20
- data/site-local/reference/index.html +6 -20
- data/site-local/reference/partials-macros.html +6 -20
- data/site-local/reference/project-configuration.html +6 -20
- data/site-local/sitemap.xml.gz +0 -0
- data/site-local/snapshot/examples/cipher_quest/index.html +16 -30
- data/site-local/snapshot/examples/temp_sensor/index.html +8 -22
- data/site-local/snapshot/examples/wondrous_forest/index.html +6 -20
- data/site-local/testing-guide/build-directives.html +14 -28
- data/site-local/testing-guide/conventions.html +13 -27
- data/site-local/testing-guide/frameworks.html +11 -25
- data/site-local/testing-guide/index.html +6 -20
- data/site-local/testing-guide/partials/configuration.html +8 -22
- data/site-local/testing-guide/partials/conventions.html +8 -22
- data/site-local/testing-guide/partials/directives.html +16 -30
- data/site-local/testing-guide/partials/example.html +14 -28
- data/site-local/testing-guide/partials/index.html +6 -20
- data/site-local/testing-guide/partials/overview.html +6 -20
- data/site-local/testing-guide/partials/variables.html +11 -25
- data/site-local/testing-guide/test-cases.html +13 -27
- data/site-local/testing-guide/test-sample.html +12 -26
- data/site-local/testing-guide/test-suite-anatomy.html +6 -20
- data/spec/integration/configurator_pipeline_spec.rb +10 -0
- data/spec/integration/dependencies/dependency_tracker_core_spec.rb +40 -0
- data/spec/integration/rake_task_registry_plugins_spec.rb +95 -0
- data/spec/integration/vendor_copy_transient_io_retry_spec.rb +122 -74
- data/spec/support/integration/configurator_integration_helper.rb +4 -1
- data/spec/units/bin/app_cfg_spec.rb +10 -0
- data/spec/units/bin/cli_handler_spec.rb +2 -1
- data/spec/units/bin/cli_helper_spec.rb +10 -0
- data/spec/units/config/configurator_setup_spec.rb +61 -43
- data/spec/units/config/configurator_spec.rb +10 -1
- data/spec/units/file_wrapper_spec.rb +66 -27
- data/spec/units/rake_app/test_task_name_spec.rb +47 -0
- data/spec/units/test_build_executor_spec.rb +42 -2
- metadata +16 -11
- data/site-local/assets/stylesheets/main.ec1eaa64.min.css +0 -1
- data/site-local/assets/stylesheets/main.ec1eaa64.min.css.map +0 -1
- data/site-local/assets/stylesheets/palette.ab4e12ef.min.css +0 -1
- data/site-local/assets/stylesheets/palette.ab4e12ef.min.css.map +0 -1
|
@@ -5,44 +5,37 @@
|
|
|
5
5
|
# SPDX-License-Identifier: MIT
|
|
6
6
|
# =========================================================================
|
|
7
7
|
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# clears
|
|
8
|
+
# ConfiguratorSetup#vendor_frameworks_and_support_files with a real FileWrapper on a real
|
|
9
|
+
# filesystem. A build brings build/vendor up to date with the gem's frameworks. It writes only
|
|
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). A transient copy
|
|
12
|
+
# fault, such as a path briefly the wrong type (GH #1292), is retried, and one that never
|
|
13
|
+
# clears names the destination.
|
|
14
14
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# (matched by destination path) so the fault is deterministic. Faulting at #cp_r rather
|
|
18
|
-
# than #cp_r_with_retry itself matters here -- cp_r_with_retry's own retry loop (already
|
|
19
|
-
# proven exhaustively in file_wrapper_spec.rb) is exactly the mechanism this spec needs
|
|
20
|
-
# to actually run.
|
|
15
|
+
# Faults are injected at FileWrapper#replace_file, the one write of each file, so the retry
|
|
16
|
+
# loop in FileWrapper#sync_tree is what runs.
|
|
21
17
|
|
|
22
18
|
require 'spec_helper'
|
|
23
19
|
require 'spec_integration_helper'
|
|
24
20
|
require 'ceedling/config/configurator_setup'
|
|
25
21
|
require 'ceedling/constants'
|
|
26
22
|
|
|
27
|
-
describe 'Vendor
|
|
23
|
+
describe 'Vendor copy into build/vendor (integration)' do
|
|
28
24
|
include IntegrationSpecHelpers
|
|
29
25
|
|
|
30
|
-
# A real FileWrapper whose #
|
|
31
|
-
# times
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# classes would otherwise let the second file's definition silently clobber the first's.
|
|
36
|
-
class CpRFaultingFileWrapper < FileWrapper
|
|
37
|
-
def initialize(fault_dest:, raises_left: 1, error: Errno::EISDIR, **kwargs)
|
|
26
|
+
# A real FileWrapper whose #replace_file raises for files beneath `fault_dir` the first
|
|
27
|
+
# `raises_left` times. Named apart from other specs' faulting wrappers, since top-level
|
|
28
|
+
# classes share one RSpec process.
|
|
29
|
+
class ReplaceFaultingFileWrapper < FileWrapper
|
|
30
|
+
def initialize(fault_dir:, raises_left: 1, error: Errno::EISDIR, **kwargs)
|
|
38
31
|
super(**kwargs)
|
|
39
|
-
@
|
|
32
|
+
@fault_dir = fault_dir
|
|
40
33
|
@raises_left = raises_left
|
|
41
34
|
@error = error
|
|
42
35
|
end
|
|
43
36
|
|
|
44
|
-
def
|
|
45
|
-
if destination
|
|
37
|
+
def replace_file(source, destination)
|
|
38
|
+
if destination.start_with?( @fault_dir ) && @raises_left > 0
|
|
46
39
|
@raises_left -= 1
|
|
47
40
|
raise @error, "simulated transient type-mismatch on #{destination}"
|
|
48
41
|
end
|
|
@@ -50,81 +43,136 @@ describe 'Vendor-copy transient I/O retry (integration)' do
|
|
|
50
43
|
end
|
|
51
44
|
end
|
|
52
45
|
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
def file_wrapper(**fault)
|
|
47
|
+
options = { loginator: IntegrationSpecHelpers::NULL, verbosinator: IntegrationSpecHelpers::NULL }
|
|
48
|
+
return fault.empty? ? FileWrapper.new( options ) : ReplaceFaultingFileWrapper.new( **fault, **options )
|
|
49
|
+
end
|
|
50
|
+
|
|
55
51
|
def build_configurator_setup(file_wrapper)
|
|
56
52
|
ConfiguratorSetup.new(
|
|
57
53
|
configurator_builder: IntegrationSpecHelpers::NULL,
|
|
58
54
|
configurator_validator: IntegrationSpecHelpers::NULL,
|
|
59
|
-
loginator:
|
|
60
|
-
reportinator:
|
|
61
|
-
file_wrapper:
|
|
62
|
-
system_wrapper:
|
|
63
|
-
tool_executor:
|
|
55
|
+
loginator: IntegrationSpecHelpers::NULL,
|
|
56
|
+
reportinator: IntegrationSpecHelpers::NULL,
|
|
57
|
+
file_wrapper: file_wrapper,
|
|
58
|
+
system_wrapper: IntegrationSpecHelpers::NULL,
|
|
59
|
+
tool_executor: IntegrationSpecHelpers::NULL
|
|
64
60
|
)
|
|
65
61
|
end
|
|
66
62
|
|
|
67
|
-
# A
|
|
68
|
-
# real file, so a real FileUtils.cp_r has real directory-tree content to walk.
|
|
63
|
+
# A stand-in for the gem's vendor/unity/src, with a nested file to show subdirectories copy
|
|
69
64
|
def flattened_config(dir)
|
|
70
|
-
unity_src = File.join(dir, 'gem_vendor', 'unity', 'src')
|
|
71
|
-
FileUtils.mkdir_p(unity_src)
|
|
72
|
-
File.write(File.join(unity_src, 'unity.c'), "// stand-in for
|
|
65
|
+
unity_src = File.join( dir, 'gem_vendor', 'unity', 'src' )
|
|
66
|
+
FileUtils.mkdir_p( File.join( unity_src, 'internal' ) )
|
|
67
|
+
File.write( File.join( unity_src, 'unity.c' ), "// stand-in for unity.c\n" )
|
|
68
|
+
File.write( File.join( unity_src, 'unity.h' ), "// stand-in for unity.h\n" )
|
|
69
|
+
File.write( File.join( unity_src, 'internal', 'unity_internals.h' ), "// stand-in\n" )
|
|
73
70
|
|
|
74
71
|
{
|
|
75
|
-
# UNITY_LIB_PATH ('unity/src')
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
project_use_partials: false,
|
|
72
|
+
# UNITY_LIB_PATH ('unity/src') joins this vendor root
|
|
73
|
+
unity_vendor_path: File.join( dir, 'gem_vendor' ),
|
|
74
|
+
project_build_vendor_unity_path: File.join( dir, 'build', 'vendor', 'unity', 'src' ),
|
|
75
|
+
project_use_mocks: false,
|
|
76
|
+
project_use_exceptions: false,
|
|
77
|
+
project_use_backtrace: :none,
|
|
78
|
+
project_use_partials: false,
|
|
83
79
|
}
|
|
84
80
|
end
|
|
85
81
|
|
|
86
|
-
|
|
82
|
+
def vendor(file_wrapper, dir, config)
|
|
83
|
+
build_configurator_setup( file_wrapper ).vendor_frameworks_and_support_files( dir, config, test_build: true, release_build: false )
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def snapshot(dest)
|
|
87
|
+
return Dir.glob( File.join( dest, '**', '*' ) ).select { |path| File.file?( path ) }.to_h do |path|
|
|
88
|
+
stat = File.stat( path )
|
|
89
|
+
[path, [stat.ino, stat.mtime]]
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def leftovers(dest)
|
|
94
|
+
return Dir.glob( File.join( dest, '**', '*' ), File::FNM_DOTMATCH ).select { |path| File.basename( path ).include?( '.tmp' ) }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "copies every file, including those in subdirectories, into a fresh destination" do
|
|
87
98
|
with_source_tree({}) do |dir|
|
|
88
|
-
config = flattened_config(dir)
|
|
99
|
+
config = flattened_config( dir )
|
|
89
100
|
dest = config[:project_build_vendor_unity_path]
|
|
90
|
-
# Real usage always runs build_directory_structure (which mkdir_p's every
|
|
91
|
-
# vendor destination) before this copy -- match that precondition here so the
|
|
92
|
-
# only fault in play is the one this spec injects, not a missing parent dir.
|
|
93
|
-
FileUtils.mkdir_p(dest)
|
|
94
|
-
|
|
95
|
-
# Exactly one raise, well within cp_r_with_retry's own retry budget -- the
|
|
96
|
-
# source is otherwise 100% fine, the real copy proceeds either way.
|
|
97
|
-
faulting_fw = CpRFaultingFileWrapper.new(
|
|
98
|
-
fault_dest: dest, raises_left: 1,
|
|
99
|
-
loginator: IntegrationSpecHelpers::NULL, verbosinator: IntegrationSpecHelpers::NULL
|
|
100
|
-
)
|
|
101
|
-
setup = build_configurator_setup(faulting_fw)
|
|
102
101
|
|
|
103
|
-
|
|
104
|
-
setup.vendor_frameworks_and_support_files(dir, config)
|
|
105
|
-
}.not_to raise_error
|
|
102
|
+
vendor( file_wrapper, dir, config )
|
|
106
103
|
|
|
107
|
-
expect(File.
|
|
104
|
+
expect( File.read( File.join( dest, 'unity.h' ) ) ).to eq( "// stand-in for unity.h\n" )
|
|
105
|
+
expect( File.exist?( File.join( dest, 'internal', 'unity_internals.h' ) ) ).to be(true)
|
|
106
|
+
expect( leftovers( dest ) ).to be_empty
|
|
108
107
|
end
|
|
109
108
|
end
|
|
110
109
|
|
|
111
|
-
it "
|
|
110
|
+
it "rewrites nothing when the destination already matches" do
|
|
112
111
|
with_source_tree({}) do |dir|
|
|
113
|
-
config = flattened_config(dir)
|
|
112
|
+
config = flattened_config( dir )
|
|
114
113
|
dest = config[:project_build_vendor_unity_path]
|
|
115
|
-
|
|
114
|
+
vendor( file_wrapper, dir, config )
|
|
115
|
+
before = snapshot( dest )
|
|
116
|
+
sleep( 1.1 ) # Past the coarsest file modification time resolution
|
|
116
117
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
vendor( file_wrapper, dir, config )
|
|
119
|
+
|
|
120
|
+
expect( snapshot( dest ) ).to eq( before )
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it "restores an edited file by replacing it whole, and leaves matching files alone" do
|
|
125
|
+
with_source_tree({}) do |dir|
|
|
126
|
+
config = flattened_config( dir )
|
|
127
|
+
dest = config[:project_build_vendor_unity_path]
|
|
128
|
+
vendor( file_wrapper, dir, config )
|
|
129
|
+
edited = File.join( dest, 'unity.h' )
|
|
130
|
+
File.write( edited, "// an errant edit\n" )
|
|
131
|
+
before = snapshot( dest )
|
|
132
|
+
|
|
133
|
+
vendor( file_wrapper, dir, config )
|
|
134
|
+
|
|
135
|
+
expect( File.read( edited ) ).to eq( "// stand-in for unity.h\n" )
|
|
136
|
+
expect( File.stat( edited ).ino ).not_to eq( before[edited].first ) unless SystemWrapper.windows?
|
|
137
|
+
untouched = File.join( dest, 'unity.c' )
|
|
138
|
+
expect( snapshot( dest )[untouched] ).to eq( before[untouched] )
|
|
139
|
+
expect( leftovers( dest ) ).to be_empty
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "restores a missing file" do
|
|
144
|
+
with_source_tree({}) do |dir|
|
|
145
|
+
config = flattened_config( dir )
|
|
146
|
+
dest = config[:project_build_vendor_unity_path]
|
|
147
|
+
vendor( file_wrapper, dir, config )
|
|
148
|
+
FileUtils.rm( File.join( dest, 'internal', 'unity_internals.h' ) )
|
|
149
|
+
|
|
150
|
+
vendor( file_wrapper, dir, config )
|
|
151
|
+
|
|
152
|
+
expect( File.exist?( File.join( dest, 'internal', 'unity_internals.h' ) ) ).to be(true)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "recovers from a transient copy failure instead of crashing the build" do
|
|
157
|
+
with_source_tree({}) do |dir|
|
|
158
|
+
config = flattened_config( dir )
|
|
159
|
+
dest = config[:project_build_vendor_unity_path]
|
|
160
|
+
|
|
161
|
+
expect { vendor( file_wrapper( fault_dir: dest, raises_left: 1 ), dir, config ) }.not_to raise_error
|
|
162
|
+
|
|
163
|
+
expect( File.exist?( File.join( dest, 'unity.c' ) ) ).to be(true)
|
|
164
|
+
expect( leftovers( dest ) ).to be_empty
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it "raises a clear, path-naming CeedlingException when the fault never clears" do
|
|
169
|
+
with_source_tree({}) do |dir|
|
|
170
|
+
config = flattened_config( dir )
|
|
171
|
+
dest = config[:project_build_vendor_unity_path]
|
|
124
172
|
|
|
125
173
|
expect {
|
|
126
|
-
|
|
127
|
-
}.to raise_error(CeedlingException, /#{Regexp.escape(dest)}/)
|
|
174
|
+
vendor( file_wrapper( fault_dir: dest, raises_left: 999 ), dir, config )
|
|
175
|
+
}.to raise_error( CeedlingException, /#{Regexp.escape( dest )}/ )
|
|
128
176
|
end
|
|
129
177
|
end
|
|
130
178
|
end
|
|
@@ -100,7 +100,8 @@ module ConfiguratorIntegrationHelpers
|
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
# Processes `yaml` as a build's setup would and returns the object graph
|
|
103
|
-
|
|
103
|
+
# `test_build:` stands for the requested tasks building tests, which populates build/vendor
|
|
104
|
+
def configure(yaml, include_test_case: '', exclude_test_case: '', test_build: true, plugins_path: File.join( CEEDLING_ROOT_PATH, 'plugins' ))
|
|
104
105
|
objects = configurator_objects( quiet_loginator )
|
|
105
106
|
objects[:configurator_builder].constants_namespace = constants
|
|
106
107
|
|
|
@@ -121,6 +122,8 @@ module ConfiguratorIntegrationHelpers
|
|
|
121
122
|
include_test_case: include_test_case,
|
|
122
123
|
exclude_test_case: exclude_test_case,
|
|
123
124
|
force_test_rerun: false,
|
|
125
|
+
test_build_tasks?: test_build,
|
|
126
|
+
release_build_tasks?: false,
|
|
124
127
|
log_filepath: nil,
|
|
125
128
|
ceedling_plugins_path: plugins_path,
|
|
126
129
|
ceedling_lib_path: File.join( CEEDLING_ROOT_PATH, 'lib', 'ceedling' ),
|
|
@@ -81,6 +81,16 @@ describe CeedlingAppConfig do
|
|
|
81
81
|
expect(@app_cfg.build_tasks?).to eq( true )
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
+
it '#set_test_build_tasks and #set_release_build_tasks update their flags, which default to false' do
|
|
85
|
+
expect(@app_cfg.test_build_tasks?).to eq( false )
|
|
86
|
+
expect(@app_cfg.release_build_tasks?).to eq( false )
|
|
87
|
+
|
|
88
|
+
@app_cfg.set_test_build_tasks( true )
|
|
89
|
+
@app_cfg.set_release_build_tasks( true )
|
|
90
|
+
expect(@app_cfg.test_build_tasks?).to eq( true )
|
|
91
|
+
expect(@app_cfg.release_build_tasks?).to eq( true )
|
|
92
|
+
end
|
|
93
|
+
|
|
84
94
|
it '#set_tests_graceful_fail updates :tests_graceful_fail? and #tests_graceful_fail?' do
|
|
85
95
|
@app_cfg.set_tests_graceful_fail( true )
|
|
86
96
|
expect(@app_cfg[:tests_graceful_fail?]).to eq( true )
|
|
@@ -109,7 +109,8 @@ describe CliHandler do
|
|
|
109
109
|
)
|
|
110
110
|
|
|
111
111
|
expect(@loginator).to receive(:console) do |message, _label|
|
|
112
|
-
|
|
112
|
+
# The launcher reports the version of the Ceedling running this spec
|
|
113
|
+
expect(message).to include( "Ceedling Launcher => #{Ceedling::Version::GEM}" )
|
|
113
114
|
expect(message).to include( 'Ceedling App => 1.2.0' )
|
|
114
115
|
end
|
|
115
116
|
|
|
@@ -111,6 +111,16 @@ describe CliHelper do
|
|
|
111
111
|
end
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
+
# Whether a command's tasks build a release decides what build/vendor needs (GH #1361)
|
|
115
|
+
describe '#release_task?' do
|
|
116
|
+
it 'is true when any task belongs to a release-building namespace' do
|
|
117
|
+
allow(@rake_task_registry).to receive(:task_is?) { |task, tag| task == 'release' && tag == RakeTaskRegistry::TAG_RELEASE }
|
|
118
|
+
|
|
119
|
+
expect( @cli_helper.release_task?( tasks: ['test:all', 'release'] ) ).to be(true)
|
|
120
|
+
expect( @cli_helper.release_task?( tasks: ['test:all', 'files:test'] ) ).to be(false)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
114
124
|
describe '#which_ceedling?' do
|
|
115
125
|
before(:each) do
|
|
116
126
|
@app_cfg = CeedlingAppConfig.new
|
|
@@ -225,74 +225,93 @@ describe ConfiguratorSetup do
|
|
|
225
225
|
end
|
|
226
226
|
end
|
|
227
227
|
|
|
228
|
+
# Only a build populates build/vendor, so a command that only reads the project, such as
|
|
229
|
+
# files:test or dumpconfig, leaves a concurrent build's files alone (GH #1361)
|
|
228
230
|
context "#vendor_frameworks_and_support_files" do
|
|
229
231
|
def flattened_config
|
|
230
232
|
{
|
|
231
|
-
unity_vendor_path: '/gem/vendor
|
|
233
|
+
unity_vendor_path: '/gem/vendor',
|
|
232
234
|
project_build_vendor_unity_path: '/build/vendor/unity/src',
|
|
233
|
-
cmock_vendor_path: '/gem/vendor
|
|
235
|
+
cmock_vendor_path: '/gem/vendor',
|
|
234
236
|
project_build_vendor_cmock_path: '/build/vendor/cmock/src',
|
|
235
|
-
cexception_vendor_path: '/gem/vendor
|
|
237
|
+
cexception_vendor_path: '/gem/vendor',
|
|
236
238
|
project_build_vendor_cexception_path: '/build/vendor/c_exception/lib',
|
|
239
|
+
project_build_vendor_ceedling_path: '/build/vendor/ceedling',
|
|
240
|
+
project_build_tests_root: '/build/test',
|
|
237
241
|
project_use_mocks: true,
|
|
238
242
|
project_use_exceptions: true,
|
|
239
|
-
project_use_backtrace:
|
|
240
|
-
project_use_partials:
|
|
243
|
+
project_use_backtrace: :gdb,
|
|
244
|
+
project_use_partials: true,
|
|
241
245
|
}
|
|
242
246
|
end
|
|
243
247
|
|
|
244
248
|
before(:each) do
|
|
245
249
|
allow(@file_wrapper).to receive(:exist?).and_return(false)
|
|
246
250
|
allow(@file_wrapper).to receive(:rm_rf)
|
|
247
|
-
allow(@file_wrapper).to receive(:
|
|
251
|
+
allow(@file_wrapper).to receive(:mkdir)
|
|
252
|
+
allow(@file_wrapper).to receive(:sync_tree)
|
|
253
|
+
allow(@file_wrapper).to receive(:sync_file)
|
|
248
254
|
end
|
|
249
255
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
config = flattened_config.merge(
|
|
254
|
-
project_use_backtrace: :gdb, project_build_tests_root: '/build/test',
|
|
255
|
-
project_use_partials: true, project_build_vendor_ceedling_path: '/build/vendor/ceedling'
|
|
256
|
-
)
|
|
256
|
+
def vendor(config = flattened_config, test_build: false, release_build: false)
|
|
257
|
+
@setup.vendor_frameworks_and_support_files( '/gem/lib', config, test_build: test_build, release_build: release_build )
|
|
258
|
+
end
|
|
257
259
|
|
|
258
|
-
|
|
260
|
+
def synced_trees
|
|
261
|
+
synced = []
|
|
262
|
+
allow(@file_wrapper).to receive(:sync_tree) { |source, destination| synced << [source, destination] }
|
|
263
|
+
yield
|
|
264
|
+
return synced
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
it "brings Unity, CMock, CException, the gdb backtrace script and the Partials header up to date for a test build" do
|
|
268
|
+
synced = synced_trees { vendor( test_build: true ) }
|
|
259
269
|
|
|
270
|
+
expect(synced).to eq( [
|
|
271
|
+
['/gem/vendor/unity/src', '/build/vendor/unity/src'],
|
|
272
|
+
['/gem/vendor/cmock/src', '/build/vendor/cmock/src'],
|
|
273
|
+
['/gem/vendor/c_exception/lib', '/build/vendor/c_exception/lib']
|
|
274
|
+
] )
|
|
260
275
|
expect(@file_wrapper).to have_received(:mkdir).with('/build/test')
|
|
261
|
-
expect(@file_wrapper).to have_received(:
|
|
262
|
-
expect(@file_wrapper).to have_received(:
|
|
276
|
+
expect(@file_wrapper).to have_received(:sync_file).with(File.join('/gem/lib', BACKTRACE_GDB_SCRIPT_FILE), '/build/test')
|
|
277
|
+
expect(@file_wrapper).to have_received(:sync_file).with('/gem/lib/ceedling.h', '/build/vendor/ceedling')
|
|
263
278
|
end
|
|
264
279
|
|
|
265
|
-
it "
|
|
266
|
-
|
|
267
|
-
# state a skip-check would trust -- simulate it directly (dest is a real
|
|
268
|
-
# directory, its marker file is a real file, nothing corrupted) to prove there
|
|
269
|
-
# is no such skip.
|
|
270
|
-
allow(@file_wrapper).to receive(:exist?).and_return(true)
|
|
271
|
-
allow(@file_wrapper).to receive(:directory?) do |path|
|
|
272
|
-
!path.end_with?('.c') # marker files (unity.c, cmock.c, CException.c) are plain files; their parent dirs are directories
|
|
273
|
-
end
|
|
280
|
+
it "leaves out CMock, CException, the backtrace script and the Partials header when a test build doesn't use them" do
|
|
281
|
+
config = flattened_config.merge( project_use_mocks: false, project_use_exceptions: false, project_use_backtrace: :simple, project_use_partials: false )
|
|
274
282
|
|
|
275
|
-
|
|
283
|
+
synced = synced_trees { vendor( config, test_build: true ) }
|
|
276
284
|
|
|
277
|
-
expect(
|
|
278
|
-
|
|
279
|
-
expect(@file_wrapper).to have_received(:cp_r_with_retry)
|
|
280
|
-
.with(a_string_including('/gem/vendor/cmock'), '/build/vendor/cmock/src')
|
|
281
|
-
expect(@file_wrapper).to have_received(:cp_r_with_retry)
|
|
282
|
-
.with(a_string_including('/gem/vendor/c_exception'), '/build/vendor/c_exception/lib')
|
|
285
|
+
expect(synced).to eq( [['/gem/vendor/unity/src', '/build/vendor/unity/src']] )
|
|
286
|
+
expect(@file_wrapper).to_not have_received(:sync_file)
|
|
283
287
|
end
|
|
284
288
|
|
|
285
|
-
it "
|
|
286
|
-
|
|
289
|
+
it "brings only CException up to date for a release build" do
|
|
290
|
+
synced = synced_trees { vendor( release_build: true ) }
|
|
291
|
+
|
|
292
|
+
expect(synced).to eq( [['/gem/vendor/c_exception/lib', '/build/vendor/c_exception/lib']] )
|
|
293
|
+
expect(@file_wrapper).to_not have_received(:sync_file)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
it "copies nothing for a release build without exceptions" do
|
|
297
|
+
vendor( flattened_config.merge( project_use_exceptions: false ), release_build: true )
|
|
298
|
+
|
|
299
|
+
expect(@file_wrapper).to_not have_received(:sync_tree)
|
|
300
|
+
expect(@file_wrapper).to_not have_received(:sync_file)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
it "copies each framework once when a command builds both tests and a release" do
|
|
304
|
+
synced = synced_trees { vendor( test_build: true, release_build: true ) }
|
|
305
|
+
|
|
306
|
+
expect(synced.map( &:last )).to eq( ['/build/vendor/unity/src', '/build/vendor/cmock/src', '/build/vendor/c_exception/lib'] )
|
|
307
|
+
end
|
|
287
308
|
|
|
288
|
-
|
|
309
|
+
it "touches nothing beneath build/vendor for a command that builds nothing" do
|
|
310
|
+
vendor
|
|
289
311
|
|
|
290
|
-
expect(@file_wrapper).
|
|
291
|
-
|
|
292
|
-
expect(@file_wrapper).to_not have_received(:
|
|
293
|
-
.with(a_string_including('/gem/vendor/cmock'), anything)
|
|
294
|
-
expect(@file_wrapper).to_not have_received(:cp_r_with_retry)
|
|
295
|
-
.with(a_string_including('/gem/vendor/c_exception'), anything)
|
|
312
|
+
expect(@file_wrapper).to_not have_received(:sync_tree)
|
|
313
|
+
expect(@file_wrapper).to_not have_received(:sync_file)
|
|
314
|
+
expect(@file_wrapper).to_not have_received(:rm_rf)
|
|
296
315
|
end
|
|
297
316
|
|
|
298
317
|
it "heals a corrupted (wrong-typed) vendor destination before copying" do
|
|
@@ -300,13 +319,12 @@ describe ConfiguratorSetup do
|
|
|
300
319
|
allow(@file_wrapper).to receive(:directory?).with('/build/vendor/unity/src').and_return(false)
|
|
301
320
|
allow(@loginator).to receive(:log)
|
|
302
321
|
|
|
303
|
-
|
|
322
|
+
vendor( test_build: true )
|
|
304
323
|
|
|
305
324
|
expect(@file_wrapper).to have_received(:rm_rf).with('/build/vendor/unity/src')
|
|
306
325
|
end
|
|
307
326
|
end
|
|
308
327
|
|
|
309
|
-
# The validations below log each problem they find and report overall validity
|
|
310
328
|
context "configuration validation" do
|
|
311
329
|
before(:each) do
|
|
312
330
|
allow(@loginator).to receive(:log)
|
|
@@ -902,13 +902,22 @@ describe Configurator do
|
|
|
902
902
|
flattened = { flat_a: 1, flat_environment: [ { cc: 'gcc' } ] }
|
|
903
903
|
expect( @configurator_setup ).to have_received(:build_project_config).with( 'lib', 'logs', flattened ).ordered
|
|
904
904
|
expect( @configurator_setup ).to have_received(:build_directory_structure).with( flattened ).ordered
|
|
905
|
-
expect( @configurator_setup ).to have_received(:vendor_frameworks_and_support_files).with( 'lib', flattened ).ordered
|
|
905
|
+
expect( @configurator_setup ).to have_received(:vendor_frameworks_and_support_files).with( 'lib', flattened, test_build: false, release_build: false ).ordered
|
|
906
906
|
expect( @configurator_setup ).to have_received(:build_project_collections).with( flattened ).ordered
|
|
907
907
|
expect( @configurator_setup ).to have_received(:build_constants_and_accessors).with( flattened, anything ).ordered
|
|
908
908
|
expect( @configurator_setup ).to have_received(:build_constants_and_accessors).with( { environment: [ { cc: 'gcc' } ] }, anything ).ordered
|
|
909
909
|
expect( @configurator.project_config_hash ).to eq( flattened )
|
|
910
910
|
end
|
|
911
911
|
|
|
912
|
+
it "copies the vendor files the requested build tasks need" do
|
|
913
|
+
@configurator.test_build_tasks = true
|
|
914
|
+
@configurator.release_build_tasks = true
|
|
915
|
+
|
|
916
|
+
@configurator.build( 'lib', 'logs', { a: 1 } )
|
|
917
|
+
|
|
918
|
+
expect( @configurator_setup ).to have_received(:vendor_frameworks_and_support_files).with( 'lib', { flat_a: 1 }, test_build: true, release_build: true )
|
|
919
|
+
end
|
|
920
|
+
|
|
912
921
|
it "redefines an existing element and its constant" do
|
|
913
922
|
@configurator.build( 'lib', 'logs', { a: 1 } )
|
|
914
923
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
# =========================================================================
|
|
7
7
|
|
|
8
8
|
require 'spec_helper'
|
|
9
|
+
require 'tmpdir'
|
|
9
10
|
require 'ceedling/file_wrapper'
|
|
10
11
|
require 'ceedling/system_wrapper'
|
|
11
12
|
require 'ceedling/exceptions'
|
|
@@ -215,49 +216,87 @@ describe FileWrapper do
|
|
|
215
216
|
# a vendor destination briefly reporting the wrong file/directory type mid-copy).
|
|
216
217
|
# Stub-based, matching #open_with_retry's own spec above -- no real filesystem or
|
|
217
218
|
# timing involved.
|
|
218
|
-
|
|
219
|
+
# A transient error mid-copy (a path briefly the wrong type, a file another process holds
|
|
220
|
+
# open on Windows) is retried; one that never clears names the destination.
|
|
221
|
+
describe '#sync_tree retries' do
|
|
219
222
|
before(:each) do
|
|
220
223
|
allow(@file_wrapper).to receive(:sleep)
|
|
221
224
|
end
|
|
222
225
|
|
|
223
|
-
|
|
224
|
-
|
|
226
|
+
def with_tree
|
|
227
|
+
Dir.mktmpdir do |dir|
|
|
228
|
+
source = File.join( dir, 'src' )
|
|
229
|
+
FileUtils.mkdir_p( source )
|
|
230
|
+
File.write( File.join( source, 'unity.c' ), "int x;\n" )
|
|
231
|
+
yield source, File.join( dir, 'dest' )
|
|
232
|
+
end
|
|
233
|
+
end
|
|
225
234
|
|
|
226
|
-
|
|
235
|
+
it 'copies without retrying when nothing fails' do
|
|
236
|
+
with_tree do |source, dest|
|
|
237
|
+
@file_wrapper.sync_tree( source, dest )
|
|
227
238
|
|
|
228
|
-
|
|
229
|
-
|
|
239
|
+
expect( File.read( File.join( dest, 'unity.c' ) ) ).to eq( "int x;\n" )
|
|
240
|
+
expect(@file_wrapper).not_to have_received(:sleep)
|
|
241
|
+
end
|
|
230
242
|
end
|
|
231
243
|
|
|
232
|
-
it 'retries past a SystemCallError and succeeds once the
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
244
|
+
it 'retries past a SystemCallError and succeeds once the copy stops raising' do
|
|
245
|
+
with_tree do |source, dest|
|
|
246
|
+
calls = 0
|
|
247
|
+
allow(@file_wrapper).to receive(:replace_file).and_wrap_original do |original, *args|
|
|
248
|
+
calls += 1
|
|
249
|
+
raise Errno::EISDIR, 'transient' if calls < 3
|
|
250
|
+
original.call( *args )
|
|
251
|
+
end
|
|
238
252
|
|
|
239
|
-
|
|
253
|
+
@file_wrapper.sync_tree( source, dest )
|
|
240
254
|
|
|
241
|
-
|
|
242
|
-
|
|
255
|
+
expect( calls ).to eq( 3 )
|
|
256
|
+
expect(@file_wrapper).to have_received(:sleep).twice
|
|
257
|
+
expect( File.exist?( File.join( dest, 'unity.c' ) ) ).to be(true)
|
|
258
|
+
end
|
|
243
259
|
end
|
|
244
260
|
|
|
245
|
-
it 'raises a CeedlingException naming the destination
|
|
246
|
-
|
|
261
|
+
it 'raises a CeedlingException naming the destination once every retry is exhausted, and logs it' do
|
|
262
|
+
with_tree do |source, dest|
|
|
263
|
+
allow(@file_wrapper).to receive(:replace_file).and_raise( Errno::ENOTDIR, 'still wrong type' )
|
|
247
264
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
265
|
+
expect { @file_wrapper.sync_tree( source, dest ) }.to raise_error( CeedlingException, /#{Regexp.escape( dest )}/ )
|
|
266
|
+
expect(@loginator).to have_received(:log).with( a_string_including( dest ), Verbosity::ERRORS )
|
|
267
|
+
# Initial attempt plus one retry for each configured delay
|
|
268
|
+
expect(@file_wrapper).to have_received(:replace_file).exactly( FileWrapper::TRANSIENT_IO_RETRY_DELAYS.size + 1 ).times
|
|
269
|
+
end
|
|
252
270
|
end
|
|
253
271
|
|
|
254
|
-
it '
|
|
255
|
-
|
|
272
|
+
it 'propagates an error that is not a SystemCallError without retrying' do
|
|
273
|
+
with_tree do |source, dest|
|
|
274
|
+
allow(@file_wrapper).to receive(:replace_file).and_raise( ArgumentError, 'not transient' )
|
|
256
275
|
|
|
257
|
-
|
|
258
|
-
@file_wrapper.
|
|
259
|
-
|
|
260
|
-
|
|
276
|
+
expect { @file_wrapper.sync_tree( source, dest ) }.to raise_error( ArgumentError, 'not transient' )
|
|
277
|
+
expect(@file_wrapper).not_to have_received(:sleep)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# The temporary copy beside the destination is the longest path replace_file writes
|
|
283
|
+
describe '#replace_file' do
|
|
284
|
+
it 'checks the length of its temporary path, then leaves only the destination' do
|
|
285
|
+
Dir.mktmpdir do |dir|
|
|
286
|
+
source = File.join( dir, 'unity.h' )
|
|
287
|
+
destination = File.join( dir, 'out', 'unity.h' )
|
|
288
|
+
File.write( source, "int x;\n" )
|
|
289
|
+
FileUtils.mkdir_p( File.dirname( destination ) )
|
|
290
|
+
checked = []
|
|
291
|
+
allow(@file_wrapper).to receive(:check_path_length) { |path, **| checked << path }
|
|
292
|
+
|
|
293
|
+
@file_wrapper.replace_file( source, destination )
|
|
294
|
+
|
|
295
|
+
expect( checked.size ).to eq( 1 )
|
|
296
|
+
expect( File.dirname( checked.first ) ).to eq( File.dirname( destination ) )
|
|
297
|
+
expect( checked.first.length ).to be > destination.length
|
|
298
|
+
expect( Dir.children( File.dirname( destination ) ) ).to eq( ['unity.h'] )
|
|
299
|
+
end
|
|
261
300
|
end
|
|
262
301
|
end
|
|
263
302
|
|