Skip to content

Commit 2a6cb31

Browse files
committed
Refactor test runner for clarity
1 parent 7f81d59 commit 2a6cb31

File tree

1 file changed

+99
-96
lines changed

1 file changed

+99
-96
lines changed

exe/arduino_ci.rb

+99-96
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def perform_action(message, multiline, mark_fn, on_fail_msg, tally_on_fail, abor
9999
else
100100
print line
101101
end
102-
STDOUT.flush
102+
$stdout.flush
103103
result = yield
104104
mark = mark_fn.nil? ? "" : mark_fn.call(result)
105105
# if multline, put checkmark at full width
@@ -125,7 +125,7 @@ def attempt_multiline(message, &block)
125125
end
126126

127127
# Make a nice status for something that kills the script immediately on failure
128-
FAILED_ASSURANCE_MESSAGE = "This may indicate a problem with ArduinoCI, or your configuration".freeze
128+
FAILED_ASSURANCE_MESSAGE = "This may indicate a problem with ArduinoCI, or your configuration; halting here".freeze
129129
def assure(message, &block)
130130
perform_action(message, false, @passfail, FAILED_ASSURANCE_MESSAGE, true, true, &block)
131131
end
@@ -145,9 +145,7 @@ def inform_multiline(message, &block)
145145
# Assure that a platform exists and return its definition
146146
def assured_platform(purpose, name, config)
147147
platform_definition = config.platform_definition(name)
148-
assure("Requested #{purpose} platform '#{name}' is defined in 'platforms' YML") do
149-
!platform_definition.nil?
150-
end
148+
assure("Requested #{purpose} platform '#{name}' is defined in 'platforms' YML") { !platform_definition.nil? }
151149
platform_definition
152150
end
153151

@@ -177,10 +175,8 @@ def display_files(pathname)
177175
# @return [Array<String>] The list of installed libraries
178176
def install_arduino_library_dependencies(library_names, on_behalf_of, already_installed = [])
179177
installed = already_installed.clone
180-
library_names.map { |n| @backend.library_of_name(n) }.each do |l|
181-
if installed.include?(l)
182-
# do nothing
183-
elsif l.installed?
178+
(library_names.map { |n| @backend.library_of_name(n) } - installed).each do |l|
179+
if l.installed?
184180
inform("Using pre-existing dependency of #{on_behalf_of}") { l.name }
185181
else
186182
assure("Installing dependency of #{on_behalf_of}: '#{l.name}'") do
@@ -195,30 +191,69 @@ def install_arduino_library_dependencies(library_names, on_behalf_of, already_in
195191
installed
196192
end
197193

198-
def handle_expectation_of_files(expectation_envvar, operation, filegroup_name, dir_description, dir)
194+
# @param example_platform_info [Hash] mapping of platform name to package information
195+
# @param board_package_url [Hash] mapping of package name to URL
196+
def install_all_packages(example_platform_info, board_package_url)
197+
# with all platform info, we can extract unique packages and their urls
198+
# do that, set the URLs, and download the packages
199+
all_packages = example_platform_info.values.map { |v| v[:package] }.uniq.reject(&:nil?)
200+
201+
# make sure any non-builtin package has a URL defined
202+
all_packages.each { |p| assure("Board package #{p} has a defined URL") { board_package_url[p] } }
203+
204+
# set up all the board manager URLs.
205+
# we can safely reject nils now, they would be for the builtins
206+
all_urls = all_packages.map { |p| board_package_url[p] }.uniq.reject(&:nil?)
207+
assure("Setting board manager URLs") { @backend.board_manager_urls = all_urls } unless all_urls.empty?
208+
all_packages.each { |p| assure("Installing board package #{p}") { @backend.install_boards(p) } }
209+
end
210+
211+
# @param expectation_envvar [String] the name of the env var to check
212+
# @param operation [String] a description of what operation we might be skipping
213+
# @param filegroup_name [String] a description of the set of files without which we effectively skip the operation
214+
# @param dir_description [String] a description of the directory where we looked for the files
215+
# @param dir [Pathname] the directory where we looked for the files
216+
def handle_expectation_of_files(expectation_envvar, operation, filegroup_name, dir_description, dir_path)
217+
# alert future me about running the script from the wrong directory, instead of doing the huge file dump
218+
# otherwise, assume that the user might be running the script on a library with no actual unit tests
219+
if Pathname.new(__dir__).parent == Pathname.new(Dir.pwd)
220+
inform_multiline("arduino_ci seems to be trying to test itself") do
221+
[
222+
"arduino_ci (the ruby gem) isn't an arduino project itself, so running the CI test script against",
223+
"the core library isn't really a valid thing to do... but it's easy for a developer (including the",
224+
"owner) to mistakenly do just that. Hello future me, you probably meant to run this against one of",
225+
"the sample projects in SampleProjects/ ... if not, please submit a bug report; what a wild case!"
226+
].each { |l| puts " #{l}" }
227+
false
228+
end
229+
exit(1)
230+
end
231+
232+
# either the directory is empty, or it doesn't exist at all. message accordingly.
233+
(problem, dir_desc, dir) = if dir.exist?
234+
["No #{filegroup_name} were found in #{dir}", dir_description, dir_path]
235+
else
236+
["No #{dir_description} at #{dir}", "base directory", dir_path.parent]
237+
end
238+
239+
inform("#{problem}, value of #{expectation_envvar} is") { ENV[expectation_envvar] }
199240
if ENV[expectation_envvar].nil?
200-
inform_multiline("Skipping #{operation}; no #{filegroup_name} were found in #{dir}") do
201-
puts " In case that's an error, this is what was found in the #{dir_description}:"
241+
inform_multiline("Skipping #{operation}") do
242+
puts " In case that's an error, this is what was found in the #{dir_desc}:"
202243
display_files(dir)
203244
puts "To force an error in this case, set the environment variable #{expectation_envvar}"
204245
true
205246
end
206247
else
207-
assure_multiline("No #{filegroup_name} were found in #{dir} and #{expectation_envvar} was set") do
208-
puts " This is what was found in the #{dir_description}:"
248+
assure_multiline("Dumping project's #{dir_desc} before exit") do
209249
display_files(dir)
210250
false
211251
end
212252
end
213253
end
214254

215-
def perform_unit_tests(cpp_library, file_config)
216-
if @cli_options[:skip_unittests]
217-
inform("Skipping unit tests") { "as requested via command line" }
218-
return
219-
end
220-
config = file_config.with_override_config(@cli_options[:ci_config])
221-
255+
# report and return the set of compilers
256+
def get_annotated_compilers(config, cpp_library)
222257
# check GCC
223258
compilers = config.compilers_to_use
224259
assure("The set of compilers (#{compilers.length}) isn't empty") { !compilers.empty? }
@@ -232,62 +267,54 @@ def perform_unit_tests(cpp_library, file_config)
232267
end
233268
inform("libasan availability for #{gcc_binary}") { cpp_library.libasan?(gcc_binary) }
234269
end
270+
compilers
271+
end
235272

236-
# Ensure platforms exist for unit test, and save their info in all_platform_info keyed by name
237-
all_platform_info = {}
238-
config.platforms_to_unittest.each { |p| all_platform_info[p] = assured_platform("unittest", p, config) }
273+
def perform_unit_tests(cpp_library, file_config)
274+
if @cli_options[:skip_unittests]
275+
inform("Skipping unit tests") { "as requested via command line" }
276+
return
277+
end
278+
279+
config = file_config.with_override_config(@cli_options[:ci_config])
280+
compilers = get_annotated_compilers(config, cpp_library)
281+
config.platforms_to_unittest.each_with_object({}) { |p, acc| acc[p] = assured_platform("unittest", p, config) }
239282

240283
inform("Library conforms to Arduino library specification") { cpp_library.one_point_five? ? "1.5" : "1.0" }
241284

242-
# iterate boards / tests
243-
if !cpp_library.tests_dir.exist?
244-
# alert future me about running the script from the wrong directory, instead of doing the huge file dump
245-
# otherwise, assume that the user might be running the script on a library with no actual unit tests
246-
if Pathname.new(__dir__).parent == Pathname.new(Dir.pwd)
247-
inform_multiline("arduino_ci seems to be trying to test itself") do
248-
[
249-
"arduino_ci (the ruby gem) isn't an arduino project itself, so running the CI test script against",
250-
"the core library isn't really a valid thing to do... but it's easy for a developer (including the",
251-
"owner) to mistakenly do just that. Hello future me, you probably meant to run this against one of",
252-
"the sample projects in SampleProjects/ ... if not, please submit a bug report; what a wild case!"
253-
].each { |l| puts " #{l}" }
254-
false
255-
end
256-
exit(1)
257-
else
258-
inform_multiline("Skipping unit tests; no tests dir at #{cpp_library.tests_dir}") do
259-
puts " In case that's an error, this is what was found in the library:"
260-
display_files(cpp_library.tests_dir.parent)
261-
true
262-
end
263-
end
264-
elsif cpp_library.test_files.empty?
285+
# Handle lack of test files
286+
if cpp_library.test_files.empty?
265287
handle_expectation_of_files(VAR_EXPECT_UNITTESTS, "unit tests", "test files", "tests directory", cpp_library.tests_dir)
266-
elsif config.platforms_to_unittest.empty?
288+
return
289+
end
290+
291+
# Handle lack of platforms
292+
if config.platforms_to_unittest.empty?
267293
inform("Skipping unit tests") { "no platforms were requested" }
268-
else
269-
install_arduino_library_dependencies(config.aux_libraries_for_unittest, "<unittest/libraries>")
270-
271-
config.platforms_to_unittest.each do |p|
272-
config.allowable_unittest_files(cpp_library.test_files).each do |unittest_path|
273-
unittest_name = unittest_path.basename.to_s
274-
compilers.each do |gcc_binary|
275-
attempt_multiline("Unit testing #{unittest_name} with #{gcc_binary} for #{p}") do
276-
exe = cpp_library.build_for_test_with_configuration(
277-
unittest_path,
278-
config.aux_libraries_for_unittest,
279-
gcc_binary,
280-
config.gcc_config(p)
281-
)
282-
puts
283-
unless exe
284-
puts "Last command: #{cpp_library.last_cmd}"
285-
puts cpp_library.last_out
286-
puts cpp_library.last_err
287-
next false
288-
end
289-
cpp_library.run_test_file(exe)
294+
return
295+
end
296+
297+
install_arduino_library_dependencies(config.aux_libraries_for_unittest, "<unittest/libraries>")
298+
299+
config.platforms_to_unittest.each do |p|
300+
config.allowable_unittest_files(cpp_library.test_files).each do |unittest_path|
301+
unittest_name = unittest_path.basename.to_s
302+
compilers.each do |gcc_binary|
303+
attempt_multiline("Unit testing #{unittest_name} with #{gcc_binary} for #{p}") do
304+
exe = cpp_library.build_for_test_with_configuration(
305+
unittest_path,
306+
config.aux_libraries_for_unittest,
307+
gcc_binary,
308+
config.gcc_config(p)
309+
)
310+
puts
311+
unless exe
312+
puts "Last command: #{cpp_library.last_cmd}"
313+
puts cpp_library.last_out
314+
puts cpp_library.last_err
315+
next false
290316
end
317+
cpp_library.run_test_file(exe)
291318
end
292319
end
293320
end
@@ -325,38 +352,14 @@ def perform_example_compilation_tests(cpp_library, config)
325352
aux_libraries.merge(ovr_config.aux_libraries_for_build)
326353
end
327354

328-
# with all platform info, we can extract unique packages and their urls
329-
# do that, set the URLs, and download the packages
330-
all_packages = example_platform_info.values.map { |v| v[:package] }.uniq.reject(&:nil?)
331-
332-
# make sure any non-builtin package has a URL defined
333-
all_packages.each do |p|
334-
assure("Board package #{p} has a defined URL") { board_package_url[p] }
335-
end
336-
337-
# set up all the board manager URLs.
338-
# we can safely reject nils now, they would be for the builtins
339-
all_urls = all_packages.map { |p| board_package_url[p] }.uniq.reject(&:nil?)
340-
341-
unless all_urls.empty?
342-
assure("Setting board manager URLs") do
343-
@backend.board_manager_urls = all_urls
344-
end
345-
end
346-
347-
all_packages.each do |p|
348-
assure("Installing board package #{p}") do
349-
@backend.install_boards(p)
350-
end
351-
end
352-
355+
install_all_packages(example_platform_info, board_package_url)
353356
install_arduino_library_dependencies(aux_libraries, "<compile/libraries>")
354357

355358
if config.platforms_to_build.empty?
356359
inform("Skipping builds") { "no platforms were requested" }
357360
return
358361
elsif library_examples.empty?
359-
handle_expectation_of_files(VAR_EXPECT_EXAMPLES, "builds", "examples", "the library directory", installed_library_path)
362+
handle_expectation_of_files(VAR_EXPECT_EXAMPLES, "builds", "examples", "the examples directory", cpp_library.examples_dir)
360363
return
361364
end
362365

0 commit comments

Comments
 (0)