arduino_ci 0.1.20 → 0.4.0
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/README.md +21 -19
- data/REFERENCE.md +625 -0
- data/cpp/arduino/Arduino.h +1 -2
- data/cpp/arduino/AvrMath.h +117 -17
- data/cpp/arduino/Client.h +26 -0
- data/cpp/arduino/EEPROM.h +64 -0
- data/cpp/arduino/Godmode.cpp +38 -19
- data/cpp/arduino/Godmode.h +88 -22
- data/cpp/arduino/HardwareSerial.h +9 -28
- data/cpp/arduino/IPAddress.h +59 -0
- data/cpp/arduino/MockEventQueue.h +86 -0
- data/cpp/arduino/PinHistory.h +64 -24
- data/cpp/arduino/Print.h +9 -12
- data/cpp/arduino/Printable.h +8 -0
- data/cpp/arduino/SPI.h +11 -3
- data/cpp/arduino/Server.h +5 -0
- data/cpp/arduino/Udp.h +27 -0
- data/cpp/arduino/Wire.h +234 -0
- data/cpp/arduino/avr/io.h +10 -1
- data/cpp/arduino/avr/pgmspace.h +76 -46
- data/cpp/arduino/ci/StreamTape.h +36 -0
- data/cpp/unittest/ArduinoUnitTests.h +1 -0
- data/cpp/unittest/Compare.h +91 -897
- data/cpp/unittest/OstreamHelpers.h +9 -0
- data/exe/arduino_ci.rb +401 -0
- data/exe/arduino_ci_remote.rb +2 -385
- data/lib/arduino_ci.rb +1 -0
- data/lib/arduino_ci/arduino_cmd.rb +13 -9
- data/lib/arduino_ci/arduino_downloader.rb +5 -4
- data/lib/arduino_ci/arduino_installation.rb +5 -5
- data/lib/arduino_ci/ci_config.rb +12 -0
- data/lib/arduino_ci/cpp_library.rb +152 -25
- data/lib/arduino_ci/installed_cpp_library.rb +0 -0
- data/lib/arduino_ci/library_properties.rb +86 -0
- data/lib/arduino_ci/version.rb +1 -1
- data/misc/default.yml +50 -3
- metadata +23 -13
- data/cpp/arduino/Arduino.h.orig +0 -143
- data/cpp/arduino/Nullptr.h +0 -7
- data/cpp/arduino/ci/Queue.h +0 -73
- data/exe/libasan.rb +0 -29
data/lib/arduino_ci.rb
CHANGED
@@ -2,6 +2,7 @@ require "arduino_ci/version"
|
|
2
2
|
require "arduino_ci/arduino_installation"
|
3
3
|
require "arduino_ci/cpp_library"
|
4
4
|
require "arduino_ci/ci_config"
|
5
|
+
require "arduino_ci/library_properties"
|
5
6
|
|
6
7
|
# ArduinoCI contains classes for automated testing of Arduino code on the command line
|
7
8
|
# @author Ian Katz <ianfixes@gmail.com>
|
@@ -6,6 +6,9 @@ WORKAROUND_LIB = "USBHost".freeze
|
|
6
6
|
|
7
7
|
module ArduinoCI
|
8
8
|
|
9
|
+
# To report errors that we can't resolve or possibly even explain
|
10
|
+
class ArduinoExecutionError < StandardError; end
|
11
|
+
|
9
12
|
# Wrap the Arduino executable. This requires, in some cases, a faked display.
|
10
13
|
class ArduinoCmd
|
11
14
|
|
@@ -42,14 +45,14 @@ module ArduinoCI
|
|
42
45
|
attr_reader :last_msg
|
43
46
|
|
44
47
|
# set the command line flags (undefined for now).
|
45
|
-
# These vary between gui/cli
|
46
|
-
flag :get_pref
|
47
|
-
flag :set_pref
|
48
|
-
flag :save_prefs
|
49
|
-
flag :use_board
|
50
|
-
flag :install_boards
|
51
|
-
flag :install_library
|
52
|
-
flag :verify
|
48
|
+
# These vary between gui/cli. Inline comments added for greppability
|
49
|
+
flag :get_pref # flag_get_pref
|
50
|
+
flag :set_pref # flag_set_pref
|
51
|
+
flag :save_prefs # flag_save_prefs
|
52
|
+
flag :use_board # flag_use_board
|
53
|
+
flag :install_boards # flag_install_boards
|
54
|
+
flag :install_library # flag_install_library
|
55
|
+
flag :verify # flag_verify
|
53
56
|
|
54
57
|
def initialize
|
55
58
|
@prefs_cache = {}
|
@@ -82,7 +85,8 @@ module ArduinoCI
|
|
82
85
|
# @return [String] Preferences as a set of lines
|
83
86
|
def _prefs_raw
|
84
87
|
resp = run_and_capture(flag_get_pref)
|
85
|
-
|
88
|
+
fail_msg = "Arduino binary failed to operate as expected; you will have to troubleshoot it manually"
|
89
|
+
raise ArduinoExecutionError, "#{fail_msg}. The command was #{@last_msg}" unless resp[:success]
|
86
90
|
|
87
91
|
@prefs_fetched = true
|
88
92
|
resp[:out]
|
@@ -161,16 +161,17 @@ module ArduinoCI
|
|
161
161
|
return false
|
162
162
|
end
|
163
163
|
|
164
|
+
arduino_package = "Arduino #{@desired_ide_version} package"
|
164
165
|
attempts = 0
|
165
166
|
|
166
167
|
loop do
|
167
168
|
if File.exist? package_file
|
168
|
-
@output.puts "
|
169
|
+
@output.puts "#{arduino_package} seems to have been downloaded already" if attempts.zero?
|
169
170
|
break
|
170
171
|
elsif attempts >= DOWNLOAD_ATTEMPTS
|
171
172
|
break @output.puts "After #{DOWNLOAD_ATTEMPTS} attempts, failed to download #{package_url}"
|
172
173
|
else
|
173
|
-
@output.print "Attempting to download
|
174
|
+
@output.print "Attempting to download #{arduino_package} with #{downloader}"
|
174
175
|
download
|
175
176
|
@output.puts
|
176
177
|
end
|
@@ -178,7 +179,7 @@ module ArduinoCI
|
|
178
179
|
end
|
179
180
|
|
180
181
|
if File.exist? extracted_file
|
181
|
-
@output.puts "
|
182
|
+
@output.puts "#{arduino_package} seems to have been extracted already"
|
182
183
|
elsif File.exist? package_file
|
183
184
|
@output.print "Extracting archive with #{extracter}"
|
184
185
|
extract
|
@@ -186,7 +187,7 @@ module ArduinoCI
|
|
186
187
|
end
|
187
188
|
|
188
189
|
if File.exist? self.class.force_install_location
|
189
|
-
@output.puts "
|
190
|
+
@output.puts "#{arduino_package} seems to have been installed already"
|
190
191
|
elsif File.exist? extracted_file
|
191
192
|
install
|
192
193
|
else
|
@@ -110,11 +110,11 @@ module ArduinoCI
|
|
110
110
|
# Forcibly install Arduino from the web
|
111
111
|
# @return [bool] Whether the command succeeded
|
112
112
|
def force_install(output = $stdout, version = DESIRED_ARDUINO_IDE_VERSION)
|
113
|
-
worker_class =
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
113
|
+
worker_class = case Host.os
|
114
|
+
when :osx then ArduinoDownloaderOSX
|
115
|
+
when :windows then ArduinoDownloaderWindows
|
116
|
+
when :linux then ArduinoDownloaderLinux
|
117
|
+
end
|
118
118
|
worker = worker_class.new(version, output)
|
119
119
|
worker.execute
|
120
120
|
end
|
data/lib/arduino_ci/ci_config.rb
CHANGED
@@ -28,6 +28,7 @@ UNITTEST_SCHEMA = {
|
|
28
28
|
compilers: Array,
|
29
29
|
platforms: Array,
|
30
30
|
libraries: Array,
|
31
|
+
exclude_dirs: Array,
|
31
32
|
testfiles: {
|
32
33
|
select: Array,
|
33
34
|
reject: Array,
|
@@ -256,6 +257,14 @@ module ArduinoCI
|
|
256
257
|
@unittest_info[:compilers]
|
257
258
|
end
|
258
259
|
|
260
|
+
# paths to exclude all files in for building and unitttests
|
261
|
+
# @return [Array<String>] The directories (relative to base dir) to exclude
|
262
|
+
def exclude_dirs
|
263
|
+
return [] if @unittest_info[:exclude_dirs].nil?
|
264
|
+
|
265
|
+
@unittest_info[:exclude_dirs]
|
266
|
+
end
|
267
|
+
|
259
268
|
# platforms to build [the examples on]
|
260
269
|
# @return [Array<String>] The platforms to build
|
261
270
|
def platforms_to_build
|
@@ -293,9 +302,12 @@ module ArduinoCI
|
|
293
302
|
return paths if @unittest_info[:testfiles].nil?
|
294
303
|
|
295
304
|
ret = paths
|
305
|
+
# Check for array emptiness, otherwise nothing will be selected!
|
296
306
|
unless @unittest_info[:testfiles][:select].nil? || @unittest_info[:testfiles][:select].empty?
|
297
307
|
ret.select! { |p| unittest_info[:testfiles][:select].any? { |glob| p.basename.fnmatch(glob) } }
|
298
308
|
end
|
309
|
+
|
310
|
+
# It's OK for the :reject array to be empty, that means nothing will be rejected by default
|
299
311
|
unless @unittest_info[:testfiles][:reject].nil?
|
300
312
|
ret.reject! { |p| unittest_info[:testfiles][:reject].any? { |glob| p.basename.fnmatch(glob) } }
|
301
313
|
end
|
@@ -37,11 +37,15 @@ module ArduinoCI
|
|
37
37
|
|
38
38
|
# @param base_dir [Pathname] The path to the library being tested
|
39
39
|
# @param arduino_lib_dir [Pathname] The path to the libraries directory
|
40
|
-
|
40
|
+
# @param exclude_dirs [Array<Pathname>] Directories that should be excluded from compilation
|
41
|
+
def initialize(base_dir, arduino_lib_dir, exclude_dirs)
|
41
42
|
raise ArgumentError, 'base_dir is not a Pathname' unless base_dir.is_a? Pathname
|
42
43
|
raise ArgumentError, 'arduino_lib_dir is not a Pathname' unless arduino_lib_dir.is_a? Pathname
|
44
|
+
raise ArgumentError, 'exclude_dir is not an array of Pathnames' unless exclude_dirs.is_a?(Array)
|
45
|
+
raise ArgumentError, 'exclude_dir array contains non-Pathname elements' unless exclude_dirs.all? { |p| p.is_a? Pathname }
|
43
46
|
|
44
47
|
@base_dir = base_dir
|
48
|
+
@exclude_dirs = exclude_dirs
|
45
49
|
@arduino_lib_dir = arduino_lib_dir.expand_path
|
46
50
|
@artifacts = []
|
47
51
|
@last_err = ""
|
@@ -51,6 +55,32 @@ module ArduinoCI
|
|
51
55
|
@vendor_bundle_cache = nil
|
52
56
|
end
|
53
57
|
|
58
|
+
# The expected path to the library.properties file (i.e. even if it does not exist)
|
59
|
+
# @return [Pathname]
|
60
|
+
def library_properties_path
|
61
|
+
@base_dir + "library.properties"
|
62
|
+
end
|
63
|
+
|
64
|
+
# Whether library.properties definitions for this library exist
|
65
|
+
# @return [bool]
|
66
|
+
def library_properties?
|
67
|
+
lib_props = library_properties_path
|
68
|
+
lib_props.exist? && lib_props.file?
|
69
|
+
end
|
70
|
+
|
71
|
+
# Decide whether this is a 1.5-compatible library
|
72
|
+
#
|
73
|
+
# according to https://arduino.github.io/arduino-cli/latest/library-specification
|
74
|
+
#
|
75
|
+
# Should match logic from https://github.com/arduino/arduino-cli/blob/master/arduino/libraries/loader.go
|
76
|
+
# @return [bool]
|
77
|
+
def one_point_five?
|
78
|
+
return false unless library_properties?
|
79
|
+
|
80
|
+
src_dir = (@base_dir + "src")
|
81
|
+
src_dir.exist? && src_dir.directory?
|
82
|
+
end
|
83
|
+
|
54
84
|
# Guess whether a file is part of the vendor bundle (indicating we should ignore it).
|
55
85
|
#
|
56
86
|
# A safe way to do this seems to be to check whether any of the installed gems
|
@@ -106,6 +136,8 @@ module ArduinoCI
|
|
106
136
|
# @param path [Pathname] The path to check
|
107
137
|
# @return [bool]
|
108
138
|
def in_tests_dir?(path)
|
139
|
+
return false unless tests_dir.exist?
|
140
|
+
|
109
141
|
tests_dir_aliases = [tests_dir, tests_dir.realpath]
|
110
142
|
# we could do this but some rubies don't return an enumerator for ascend
|
111
143
|
# path.ascend.any? { |part| tests_dir_aliases.include?(part) }
|
@@ -115,6 +147,19 @@ module ArduinoCI
|
|
115
147
|
false
|
116
148
|
end
|
117
149
|
|
150
|
+
# Guess whether a file is part of any @excludes_dir dir (indicating library compilation should ignore it).
|
151
|
+
#
|
152
|
+
# @param path [Pathname] The path to check
|
153
|
+
# @return [bool]
|
154
|
+
def in_exclude_dir?(path)
|
155
|
+
# we could do this but some rubies don't return an enumerator for ascend
|
156
|
+
# path.ascend.any? { |part| tests_dir_aliases.include?(part) }
|
157
|
+
path.ascend do |part|
|
158
|
+
return true if exclude_dir.any? { |p| p.realpath == part }
|
159
|
+
end
|
160
|
+
false
|
161
|
+
end
|
162
|
+
|
118
163
|
# Check whether libasan (and by extension -fsanitizer=address) is supported
|
119
164
|
#
|
120
165
|
# This requires compilation of a sample program, and will be cached
|
@@ -133,43 +178,98 @@ module ArduinoCI
|
|
133
178
|
@has_libasan_cache[gcc_binary]
|
134
179
|
end
|
135
180
|
|
181
|
+
# Library properties
|
182
|
+
def library_properties
|
183
|
+
return nil unless library_properties?
|
184
|
+
|
185
|
+
LibraryProperties.new(library_properties_path)
|
186
|
+
end
|
187
|
+
|
188
|
+
# Get a list of all dependencies as defined in library.properties
|
189
|
+
# @return [Array<String>] The library names of the dependencies (not the paths)
|
190
|
+
def arduino_library_dependencies
|
191
|
+
return nil unless library_properties?
|
192
|
+
|
193
|
+
library_properties.depends
|
194
|
+
end
|
195
|
+
|
136
196
|
# Get a list of all CPP source files in a directory and its subdirectories
|
137
197
|
# @param some_dir [Pathname] The directory in which to begin the search
|
198
|
+
# @param extensions [Array<Sring>] The set of allowable file extensions
|
138
199
|
# @return [Array<Pathname>] The paths of the found files
|
139
|
-
def
|
200
|
+
def code_files_in(some_dir, extensions)
|
140
201
|
raise ArgumentError, 'some_dir is not a Pathname' unless some_dir.is_a? Pathname
|
141
202
|
return [] unless some_dir.exist? && some_dir.directory?
|
142
203
|
|
143
|
-
|
144
|
-
|
145
|
-
cpp = files.select { |path| CPP_EXTENSIONS.include?(path.extname.downcase) }
|
204
|
+
files = some_dir.realpath.children.reject(&:directory?)
|
205
|
+
cpp = files.select { |path| extensions.include?(path.extname.downcase) }
|
146
206
|
not_hidden = cpp.reject { |path| path.basename.to_s.start_with?(".") }
|
147
207
|
not_hidden.sort_by(&:to_s)
|
148
208
|
end
|
149
209
|
|
210
|
+
# Get a list of all CPP source files in a directory and its subdirectories
|
211
|
+
# @param some_dir [Pathname] The directory in which to begin the search
|
212
|
+
# @param extensions [Array<Sring>] The set of allowable file extensions
|
213
|
+
# @return [Array<Pathname>] The paths of the found files
|
214
|
+
def code_files_in_recursive(some_dir, extensions)
|
215
|
+
raise ArgumentError, 'some_dir is not a Pathname' unless some_dir.is_a? Pathname
|
216
|
+
return [] unless some_dir.exist? && some_dir.directory?
|
217
|
+
|
218
|
+
real = some_dir.realpath
|
219
|
+
Find.find(real).map { |p| Pathname.new(p) }.select(&:directory?).map { |d| code_files_in(d, extensions) }.flatten
|
220
|
+
end
|
221
|
+
|
222
|
+
# Header files that are part of the project library under test
|
223
|
+
# @return [Array<Pathname>]
|
224
|
+
def header_files
|
225
|
+
ret = if one_point_five?
|
226
|
+
code_files_in_recursive(@base_dir + "src", HPP_EXTENSIONS)
|
227
|
+
else
|
228
|
+
[@base_dir, @base_dir + "utility"].map { |d| code_files_in(d, HPP_EXTENSIONS) }.flatten
|
229
|
+
end
|
230
|
+
|
231
|
+
# note to future troubleshooter: some of these tests may not be relevant, but at the moment at
|
232
|
+
# least some of them are tied to existing features
|
233
|
+
ret.reject { |p| vendor_bundle?(p) || in_tests_dir?(p) || in_exclude_dir?(p) }
|
234
|
+
end
|
235
|
+
|
150
236
|
# CPP files that are part of the project library under test
|
151
237
|
# @return [Array<Pathname>]
|
152
238
|
def cpp_files
|
153
|
-
|
239
|
+
ret = if one_point_five?
|
240
|
+
code_files_in_recursive(@base_dir + "src", CPP_EXTENSIONS)
|
241
|
+
else
|
242
|
+
[@base_dir, @base_dir + "utility"].map { |d| code_files_in(d, CPP_EXTENSIONS) }.flatten
|
243
|
+
end
|
244
|
+
|
245
|
+
# note to future troubleshooter: some of these tests may not be relevant, but at the moment at
|
246
|
+
# least some of them are tied to existing features
|
247
|
+
ret.reject { |p| vendor_bundle?(p) || in_tests_dir?(p) || in_exclude_dir?(p) }
|
154
248
|
end
|
155
249
|
|
156
250
|
# CPP files that are part of the arduino mock library we're providing
|
157
251
|
# @return [Array<Pathname>]
|
158
252
|
def cpp_files_arduino
|
159
|
-
|
253
|
+
code_files_in(ARDUINO_HEADER_DIR, CPP_EXTENSIONS)
|
160
254
|
end
|
161
255
|
|
162
256
|
# CPP files that are part of the unit test library we're providing
|
163
257
|
# @return [Array<Pathname>]
|
164
258
|
def cpp_files_unittest
|
165
|
-
|
259
|
+
code_files_in(UNITTEST_HEADER_DIR, CPP_EXTENSIONS)
|
166
260
|
end
|
167
261
|
|
168
262
|
# CPP files that are part of the 3rd-party libraries we're including
|
169
263
|
# @param [Array<String>] aux_libraries
|
170
264
|
# @return [Array<Pathname>]
|
171
265
|
def cpp_files_libraries(aux_libraries)
|
172
|
-
arduino_library_src_dirs(aux_libraries).map { |d|
|
266
|
+
arduino_library_src_dirs(aux_libraries).map { |d| code_files_in(d, CPP_EXTENSIONS) }.flatten.uniq
|
267
|
+
end
|
268
|
+
|
269
|
+
# Returns the Pathnames for all paths to exclude from testing and compilation
|
270
|
+
# @return [Array<Pathname>]
|
271
|
+
def exclude_dir
|
272
|
+
@exclude_dirs.map { |p| Pathname.new(@base_dir) + p }.select(&:exist?)
|
173
273
|
end
|
174
274
|
|
175
275
|
# The directory where we expect to find unit test defintions provided by the user
|
@@ -181,16 +281,15 @@ module ArduinoCI
|
|
181
281
|
# The files provided by the user that contain unit tests
|
182
282
|
# @return [Array<Pathname>]
|
183
283
|
def test_files
|
184
|
-
|
284
|
+
code_files_in(tests_dir, CPP_EXTENSIONS)
|
185
285
|
end
|
186
286
|
|
187
287
|
# Find all directories in the project library that include C++ header files
|
188
288
|
# @return [Array<Pathname>]
|
189
289
|
def header_dirs
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
files = unbundled.select { |path| HPP_EXTENSIONS.include?(path.extname.downcase) }
|
290
|
+
unbundled = header_files.reject { |path| vendor_bundle?(path) }
|
291
|
+
unexcluded = unbundled.reject { |path| in_exclude_dir?(path) }
|
292
|
+
files = unexcluded.select { |path| HPP_EXTENSIONS.include?(path.extname.downcase) }
|
194
293
|
files.map(&:dirname).uniq
|
195
294
|
end
|
196
295
|
|
@@ -212,20 +311,24 @@ module ArduinoCI
|
|
212
311
|
@last_err
|
213
312
|
end
|
214
313
|
|
215
|
-
# Arduino library directories containing sources
|
314
|
+
# Arduino library directories containing sources -- only those of the dependencies
|
216
315
|
# @return [Array<Pathname>]
|
217
316
|
def arduino_library_src_dirs(aux_libraries)
|
218
317
|
# Pull in all possible places that headers could live, according to the spec:
|
219
318
|
# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5:-Library-specification
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
319
|
+
|
320
|
+
aux_libraries.map do |d|
|
321
|
+
# library manager coerces spaces in package names to underscores
|
322
|
+
# see https://github.com/ianfixes/arduino_ci/issues/132#issuecomment-518857059
|
323
|
+
legal_dir = d.tr(" ", "_")
|
324
|
+
self.class.new(@arduino_lib_dir + legal_dir, @arduino_lib_dir, @exclude_dirs).header_dirs
|
325
|
+
end.flatten.uniq
|
226
326
|
end
|
227
327
|
|
228
328
|
# GCC command line arguments for including aux libraries
|
329
|
+
#
|
330
|
+
# This function recursively collects the library directores of the dependencies
|
331
|
+
#
|
229
332
|
# @param aux_libraries [Array<Pathname>] The external Arduino libraries required by this project
|
230
333
|
# @return [Array<String>] The GCC command-line flags necessary to include those libraries
|
231
334
|
def include_args(aux_libraries)
|
@@ -288,6 +391,9 @@ module ArduinoCI
|
|
288
391
|
end
|
289
392
|
|
290
393
|
# build a file for running a test of the given unit test file
|
394
|
+
#
|
395
|
+
# The dependent libraries configuration is appended with data from library.properties internal to the library under test
|
396
|
+
#
|
291
397
|
# @param test_file [Pathname] The path to the file containing the unit tests
|
292
398
|
# @param aux_libraries [Array<Pathname>] The external Arduino libraries required by this project
|
293
399
|
# @param ci_gcc_config [Hash] The GCC config object
|
@@ -306,8 +412,12 @@ module ArduinoCI
|
|
306
412
|
"-fsanitize=address"
|
307
413
|
]
|
308
414
|
end
|
309
|
-
|
310
|
-
|
415
|
+
|
416
|
+
# combine library.properties defs (if existing) with config file.
|
417
|
+
# TODO: as much as I'd like to rely only on the properties file(s), I think that would prevent testing 1.0-spec libs
|
418
|
+
full_aux_libraries = arduino_library_dependencies.nil? ? aux_libraries : aux_libraries + arduino_library_dependencies
|
419
|
+
arg_sets << test_args(full_aux_libraries, ci_gcc_config)
|
420
|
+
arg_sets << cpp_files_libraries(full_aux_libraries).map(&:to_s)
|
311
421
|
arg_sets << [test_file.to_s]
|
312
422
|
args = arg_sets.flatten(1)
|
313
423
|
return nil unless run_gcc(gcc_binary, *args)
|
@@ -316,14 +426,31 @@ module ArduinoCI
|
|
316
426
|
executable
|
317
427
|
end
|
318
428
|
|
429
|
+
# print any found stack dumps
|
430
|
+
# @param executable [Pathname] the path to the test file
|
431
|
+
def print_stack_dump(executable)
|
432
|
+
possible_dumpfiles = [
|
433
|
+
executable.sub_ext(executable.extname + ".stackdump")
|
434
|
+
]
|
435
|
+
possible_dumpfiles.select(&:exist?).each do |dump|
|
436
|
+
puts "========== Stack dump from #{dump}:"
|
437
|
+
File.foreach(dump) { |line| print " #{line}" }
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
319
441
|
# run a test file
|
320
|
-
# @param [Pathname] the path to the test file
|
442
|
+
# @param executable [Pathname] the path to the test file
|
321
443
|
# @return [bool] whether all tests were successful
|
322
444
|
def run_test_file(executable)
|
323
445
|
@last_cmd = executable
|
324
446
|
@last_out = ""
|
325
447
|
@last_err = ""
|
326
|
-
Host.run_and_output(executable.to_s.shellescape)
|
448
|
+
ret = Host.run_and_output(executable.to_s.shellescape)
|
449
|
+
|
450
|
+
# print any stack traces found during a failure
|
451
|
+
print_stack_dump(executable) unless ret
|
452
|
+
|
453
|
+
ret
|
327
454
|
end
|
328
455
|
|
329
456
|
end
|
File without changes
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module ArduinoCI
|
2
|
+
|
3
|
+
# Information about an Arduino library package, as specified by the library.properties file
|
4
|
+
#
|
5
|
+
# See https://arduino.github.io/arduino-cli/library-specification/#libraryproperties-file-format
|
6
|
+
class LibraryProperties
|
7
|
+
|
8
|
+
# @return [Hash] The properties file parsed as a hash
|
9
|
+
attr_reader :fields
|
10
|
+
|
11
|
+
# @param path [Pathname] The path to the library.properties file
|
12
|
+
def initialize(path)
|
13
|
+
@fields = {}
|
14
|
+
File.foreach(path) do |line|
|
15
|
+
parts = line.split("=", 2)
|
16
|
+
@fields[parts[0]] = parts[1].chomp unless parts.empty?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Enable a shortcut syntax for library property accessors, in the style of `attr_accessor` metaprogramming.
|
21
|
+
# This is used to create a named field pointing to a specific property in the file, optionally applying
|
22
|
+
# a specific formatting function.
|
23
|
+
#
|
24
|
+
# The formatting function MUST be a static method on this class. This is a limitation caused by the desire
|
25
|
+
# to both (1) expose the formatters outside this class, and (2) use them for metaprogramming without the
|
26
|
+
# having to name the entire function. field_reader is a static method, so if not for the fact that
|
27
|
+
# `self.class.methods.include? formatter` fails to work for class methods in this context (unlike
|
28
|
+
# `self.methods.include?`, which properly finds instance methods), I would allow either one and just
|
29
|
+
# conditionally `define_method` the proper definition
|
30
|
+
#
|
31
|
+
# @param name [String] What the accessor will be called
|
32
|
+
# @param field_num [Integer] The name of the key of the property
|
33
|
+
# @param formatter [Symbol] The symbol for the formatting function to apply to the field (optional)
|
34
|
+
# @return [void]
|
35
|
+
# @macro [attach] field_reader
|
36
|
+
# @!attribute [r] $1
|
37
|
+
# @return property $2 of the library.properties file, formatted with the function {$3}
|
38
|
+
def self.field_reader(name, formatter = nil)
|
39
|
+
key = name.to_s
|
40
|
+
if formatter.nil?
|
41
|
+
define_method(name) { @fields[key] }
|
42
|
+
else
|
43
|
+
define_method(name) { @fields.key?(key) ? self.class.send(formatter.to_sym, @fields[key]) : nil }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Parse a value as a comma-separated array
|
48
|
+
# @param input [String]
|
49
|
+
# @return [Array<String>] The individual values
|
50
|
+
def self._csv(input)
|
51
|
+
input.split(",").map(&:strip)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Parse a value as a boolean
|
55
|
+
# @param input [String]
|
56
|
+
# @return [Array<String>] The individual values
|
57
|
+
def self._bool(input)
|
58
|
+
input == "true" # no indication given in the docs that anything but lowercase "true" indicates boolean true.
|
59
|
+
end
|
60
|
+
|
61
|
+
field_reader :name
|
62
|
+
field_reader :version
|
63
|
+
field_reader :author, :_csv
|
64
|
+
field_reader :maintainer
|
65
|
+
field_reader :sentence
|
66
|
+
field_reader :paragraph
|
67
|
+
field_reader :category
|
68
|
+
field_reader :url
|
69
|
+
field_reader :architectures, :_csv
|
70
|
+
field_reader :depends, :_csv
|
71
|
+
field_reader :dot_a_linkage, :_bool
|
72
|
+
field_reader :includes, :_csv
|
73
|
+
field_reader :precompiled, :_bool
|
74
|
+
field_reader :ldflags, :_csv
|
75
|
+
|
76
|
+
# The value of sentence always will be prepended, so you should start by writing the second sentence here
|
77
|
+
#
|
78
|
+
# (according to the docs)
|
79
|
+
# @return [String] the sentence and paragraph together
|
80
|
+
def full_paragraph
|
81
|
+
[sentence, paragraph].join(" ")
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|