From ab310abb7ff6a9f0652c7c5843a323cfc4fef85a Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 21 Jan 2021 11:08:21 -0500 Subject: [PATCH 01/37] Avoid error when no examples directory exists --- CHANGELOG.md | 1 + lib/arduino_ci/cpp_library.rb | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16ee831b..24c41012 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Apply "rule of three" to Client copy constructor and copy assignment operator - Run Windows tests on Windows not Ubuntu - Properly report error in building shared library +- A missing `examples` directory no longer causes a crash in `cpp_library.rb` ### Security diff --git a/lib/arduino_ci/cpp_library.rb b/lib/arduino_ci/cpp_library.rb index ed43f72c..5ca7eb74 100644 --- a/lib/arduino_ci/cpp_library.rb +++ b/lib/arduino_ci/cpp_library.rb @@ -135,7 +135,8 @@ def info # @param installed_library_path [String] The library to query # @return [Array] Example sketch files def example_sketches - reported_dirs = info["library"]["examples"].map(&Pathname::method(:new)) + examples = info["library"].fetch("examples", []) + reported_dirs = examples.map(&Pathname::method(:new)) reported_dirs.map { |e| e + e.basename.sub_ext(".ino") }.select(&:exist?).sort_by(&:to_s) end From 2f3aee6d8f9b498b5bcaf9ec085eba2f32a8d382 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 21 Jan 2021 11:08:37 -0500 Subject: [PATCH 02/37] Fix documentation about CI --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8ac9750..ae677bd1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ ArduinoCI uses a very standard GitHub workflow. * If you are submitting code, use `master` as the base branch * If you are submitting broken unit tests (illustrating a bug that should be fixed), use `tdd` as the base branch. -Pull requests will trigger a Travis CI job. The following two commands will be expected to pass (so you may want to run them locally before opening the pull request): +Pull requests will trigger a CI job. The following two commands will be expected to pass (so you may want to run them locally before opening the pull request): * `bundle exec rubocop -D .` - code style tests * `bundle exec rspec` - functional tests From 8ac8090782e6858a1337796629903478791877b7 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 12 Dec 2022 11:36:12 -0500 Subject: [PATCH 03/37] fix naming of github actions --- .github/workflows/linux.yaml | 10 +++++----- .github/workflows/macos.yaml | 10 +++++----- .github/workflows/windows.yaml | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/linux.yaml b/.github/workflows/linux.yaml index a095c5b2..d088b02a 100644 --- a/.github/workflows/linux.yaml +++ b/.github/workflows/linux.yaml @@ -11,7 +11,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: Check style, functionality, and usage + - name: Check style run: | g++ -v bundle install @@ -25,7 +25,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: Check style, functionality, and usage + - name: Check functionality run: | g++ -v bundle install @@ -38,7 +38,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: TestSomething + - name: Check usage - TestSomething run: | g++ -v bundle install @@ -53,7 +53,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: Test NetworkLib from scratch + - name: Check usage - Test NetworkLib from scratch run: | g++ -v cd SampleProjects/NetworkLib @@ -69,5 +69,5 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: Test SharedLibrary should fail + - name: Check usage - Test SharedLibrary should fail run: ./SampleProjects/SharedLibrary/test.sh diff --git a/.github/workflows/macos.yaml b/.github/workflows/macos.yaml index 5019f96f..869daa91 100644 --- a/.github/workflows/macos.yaml +++ b/.github/workflows/macos.yaml @@ -11,7 +11,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: Check style, functionality, and usage + - name: Check style run: | g++ -v bundle install @@ -25,7 +25,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: Check style, functionality, and usage + - name: Check functionality run: | g++ -v bundle install @@ -38,7 +38,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: TestSomething + - name: Check usage - TestSomething run: | g++ -v bundle install @@ -53,7 +53,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: Test NetworkLib from scratch + - name: Check usage - Test NetworkLib from scratch run: | g++ -v cd SampleProjects/NetworkLib @@ -69,5 +69,5 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: Test SharedLibrary should fail + - name: Check usage - Test SharedLibrary should fail run: ./SampleProjects/SharedLibrary/test.sh diff --git a/.github/workflows/windows.yaml b/.github/workflows/windows.yaml index 3169ea44..724684ff 100644 --- a/.github/workflows/windows.yaml +++ b/.github/workflows/windows.yaml @@ -11,7 +11,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: Check style, functionality, and usage + - name: Check style and functionality run: | g++ -v bundle install @@ -27,7 +27,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: TestSomething + - name: Check usage - TestSomething run: | g++ -v bundle install @@ -42,7 +42,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: Test NetworkLib from scratch + - name: Check usage - Test NetworkLib from scratch run: | g++ -v cd SampleProjects/NetworkLib @@ -58,5 +58,5 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: Test SharedLibrary should fail + - name: Check usage - Test SharedLibrary should fail run: ./SampleProjects/SharedLibrary/test.sh From 448b7d13bcbab6dcbadfe2c26a7f7dfa9629b0c1 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 17 Dec 2022 16:47:10 -0500 Subject: [PATCH 04/37] Fix rubocop on Windows #315 --- .github/workflows/windows.yaml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/windows.yaml b/.github/workflows/windows.yaml index 724684ff..1decd120 100644 --- a/.github/workflows/windows.yaml +++ b/.github/workflows/windows.yaml @@ -4,23 +4,34 @@ name: windows on: [push, pull_request] jobs: - "rubocop_and_rspec": + "rubocop": runs-on: windows-latest steps: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 - - name: Check style and functionality + - name: Check style run: | g++ -v bundle install bundle exec rubocop --version - bundle exec rubocop -D . - echo "done with Rubocop (See https://github.com/Arduino-CI/arduino_ci/issues/315)" + bundle exec rubocop -D . --except Layout/EndOfLine + + "rspec": + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.6 + - name: Check functionality + run: | + g++ -v + bundle install bundle exec rspec --backtrace - "TestSomething": + TestSomething: runs-on: windows-latest steps: - uses: actions/checkout@v2 From ff240bc79aacfc53e7a3c57bb63f9bce8216a5af Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 21 Jan 2021 15:36:11 -0500 Subject: [PATCH 05/37] note that this project is for libraries --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d8c84294..b4b78ee4 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Gitter](https://badges.gitter.im/Arduino-CI/arduino_ci.svg)](https://gitter.im/Arduino-CI/arduino_ci?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![GitHub Marketplace](https://img.shields.io/badge/Get_it-on_Marketplace-informational.svg)](https://github.com/marketplace/actions/arduino_ci) -Arduino CI was created to enable better collaboration among Arduino library maintainers and contributors, by enabling automated code checks to be performed as part of a pull request process. +Arduino CI tests [Arduino libraries](https://arduino.github.io/arduino-cli/library-specification/); it was created to enable better collaboration among Arduino library maintainers and contributors, by enabling automated code checks to be performed as part of a pull request process. * enables running unit tests against the library **without hardware present** * provides a system of mocks that allow fine-grained control over the hardware inputs, including the system's clock From 4cc00b97f77e3ad45967637a8d15e0442a810290 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 21 Jan 2021 15:37:12 -0500 Subject: [PATCH 06/37] remove now-incorrect message about library.properties tests in README --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index b4b78ee4..b5c3119f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ Arduino CI tests [Arduino libraries](https://arduino.github.io/arduino-cli/libra * provides a system of mocks that allow fine-grained control over the hardware inputs, including the system's clock * verifies compilation of any example sketches included in the library * can test a wide range of arduino boards with different hardware options available -* compares entries in `library.properties` to the contents of the library and reports mismatches * can be run both locally and as part of CI (GitHub Actions, TravisCI, Appveyor, etc.) * runs on multiple platforms -- any platform that supports the Arduino IDE * provides detailed analysis of segfaults in compilers that support such debugging features From cf47379af5edef63c863c89dd7dd8e78de44acc8 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 12 Dec 2022 10:57:16 -0500 Subject: [PATCH 07/37] Restore build_shared_library and fix its bug --- exe/arduino_ci.rb | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/exe/arduino_ci.rb b/exe/arduino_ci.rb index 175cdb2c..f5bccbc4 100755 --- a/exe/arduino_ci.rb +++ b/exe/arduino_ci.rb @@ -423,22 +423,7 @@ def perform_unit_tests(cpp_library, file_config) puts compilers.each do |gcc_binary| # before compiling the tests, build a shared library of everything except the test code - got_shared_library = true - attempt_multiline("Build shared library with #{gcc_binary} for #{p}") do - exe = cpp_library.build_shared_library( - config.aux_libraries_for_unittest, - gcc_binary, - config.gcc_config(p) - ) - unless exe - puts "Last command: #{cpp_library.last_cmd}" - puts cpp_library.last_out - puts cpp_library.last_err - got_shared_library = false - end - next got_shared_library - end - next unless got_shared_library + next unless build_shared_library(gcc_binary, p, config, cpp_library) # now build and run each test using the shared library build above config.allowable_unittest_files(cpp_library.test_files).each do |unittest_path| @@ -460,6 +445,23 @@ def perform_unit_tests(cpp_library, file_config) end end +def build_shared_library(gcc_binary, platform, config, cpp_library) + attempt_multiline("Build shared library with #{gcc_binary} for #{platform}") do + exe = cpp_library.build_shared_library( + config.aux_libraries_for_unittest, + gcc_binary, + config.gcc_config(platform) + ) + puts + unless exe + puts "Last command: #{cpp_library.last_cmd}" + puts cpp_library.last_out + puts cpp_library.last_err + end + exe + end +end + def perform_example_compilation_tests(cpp_library, config) phase("Compilation of example sketches") if @cli_options[:skip_compilation] From 6da7797e663438eb196c0b086e11170fa1ae2ead Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 12 Dec 2022 11:21:09 -0500 Subject: [PATCH 08/37] factor out print_backend_logs --- exe/arduino_ci.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/exe/arduino_ci.rb b/exe/arduino_ci.rb index f5bccbc4..04558fbf 100755 --- a/exe/arduino_ci.rb +++ b/exe/arduino_ci.rb @@ -76,17 +76,19 @@ def self.parse(options) # Read in command line options and make them read-only @cli_options = (Parser.parse ARGV).freeze +def print_backend_logs + puts "========== Last backend command (if relevant):" + puts @backend.last_msg.to_s + puts "========== Backend Stdout:" + puts @backend.last_out + puts "========== Backend Stderr:" + puts @backend.last_err +end + # terminate after printing any debug info. TODO: capture debug info def terminate(final = nil) puts "Failures: #{@failure_count}" - unless @failure_count.zero? || final || @backend.nil? - puts "========== Last backend command (if relevant):" - puts @backend.last_msg.to_s - puts "========== Backend Stdout:" - puts @backend.last_out - puts "========== Backend Stderr:" - puts @backend.last_err - end + print_backend_logs unless @failure_count.zero? || final || @backend.nil? retcode = @failure_count.zero? ? 0 : 1 exit(retcode) end From 360a87f2d69228255c45807b180c57c78bd1945a Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 12 Dec 2022 11:24:58 -0500 Subject: [PATCH 09/37] Support for checking space usage in compiled sketches via command line --- CHANGELOG.md | 1 + exe/arduino_ci.rb | 25 +++++++++++-------------- lib/arduino_ci/arduino_backend.rb | 16 ++++++++++++++++ spec/arduino_backend_spec.rb | 8 ++++++++ 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24c41012..a582d64f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Support for `dtostrf()` - Added a CI workflow to lint the code base - Added a CI workflow to check for spelling errors +- Extraction of byes usage in a compiled sketch is now calculated in a method: `ArduinoBackend.last_bytes_usage` ### Changed - We now compile a shared library to be used for each test. diff --git a/exe/arduino_ci.rb b/exe/arduino_ci.rb index 04558fbf..6abf2285 100755 --- a/exe/arduino_ci.rb +++ b/exe/arduino_ci.rb @@ -24,7 +24,7 @@ def self.parse(options) ci_config: { "unittest" => unit_config }, - min_free_space: 0, + min_free_space: nil, } opt_parser = OptionParser.new do |opts| @@ -50,7 +50,7 @@ def self.parse(options) unit_config["testfiles"]["reject"] << p end - opts.on("--min-free-space=VALUE", "Minimum free SRAM memory for stack/heap") do |p| + opts.on("--min-free-space=VALUE", "Minimum free SRAM memory for stack/heap, in bytes") do |p| output_options[:min_free_space] = p.to_i end @@ -502,23 +502,20 @@ def perform_example_compilation_tests(cpp_library, config) board = ovr_config.platform_info[p][:board] attempt("Compiling #{example_name} for #{board}") do ret = @backend.compile_sketch(example_path, board) - puts - if ret - output = @backend.last_msg - puts output - i = output.index("leaving") - free_space = output[i + 8..-1].to_i - min_free_space = @cli_options[:min_free_space] - if free_space < min_free_space - puts "Free space of #{free_space} is less than minimum of #{min_free_space}" - ret = false - end - else + unless ret puts "Last command: #{@backend.last_msg}" puts @backend.last_err end ret end + + next if @cli_options[:min_free_space].nil? + + usage = @backend.last_bytes_usage + min_free_space = @cli_options[:min_free_space] + attempt("Checking that free space of #{usage[:free]} is less than desired minimum #{min_free_space}") do + min_free_space <= usage[:free] + end end end end diff --git a/lib/arduino_ci/arduino_backend.rb b/lib/arduino_ci/arduino_backend.rb index e67710bf..e8c7ec1d 100644 --- a/lib/arduino_ci/arduino_backend.rb +++ b/lib/arduino_ci/arduino_backend.rb @@ -235,5 +235,21 @@ def install_local_library(path) Host.symlink(src_path, destination_path) cpp_library end + + # extract the "Free space remaining" amount from the last run + # @return [Hash] the usage, as a hash with keys :free, :max, and :globals + def last_bytes_usage + # Free-spacing syntax for regexes is not working today, not sure why. Make a string and convert to regex. + re_str = [ + 'Global variables use (?\d+) bytes', + '\(\d+%\) of dynamic memory,', + 'leaving (?\d+) bytes for local variables.', + 'Maximum is (?\d+) bytes.' + ].join(" ") + mem_info = Regexp.new(re_str).match(@last_msg) + return {} if mem_info.nil? + + Hash[mem_info.names.map(&:to_sym).zip(mem_info.captures.map(&:to_i))] + end end end diff --git a/spec/arduino_backend_spec.rb b/spec/arduino_backend_spec.rb index 5b654d5b..1b6c6ee0 100644 --- a/spec/arduino_backend_spec.rb +++ b/spec/arduino_backend_spec.rb @@ -103,5 +103,13 @@ def get_sketch(dir, file) it "Passes a simple INO sketch at #{sketch_path_ino}" do expect(backend.compile_sketch(sketch_path_ino, "arduino:avr:uno")).to be true end + + it "Detects the bytes usage after compiling a sketch" do + expect(backend.compile_sketch(sketch_path_ino, "arduino:avr:uno")).to be true + the_bytes = backend.last_bytes_usage + expect(the_bytes[:globals]).to eq 9 + expect(the_bytes[:free]).to eq 2039 + expect(the_bytes[:max]).to eq 2048 + end end end From 21c3c206b91808d5ef1e66353c1d45a2f803cc6a Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 17 Dec 2022 15:06:55 -0500 Subject: [PATCH 10/37] Explicit handling and future-proofing for arduino-cli#753 - config path --- CHANGELOG.md | 1 + lib/arduino_ci/arduino_backend.rb | 58 +++++++++++++++++++++-- spec/arduino_backend_spec.rb | 4 +- spec/arduino_installation_spec.rb | 78 +++++++++++++++++++++++++++++++ spec/fake_lib_dir.rb | 4 +- 5 files changed, 135 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a582d64f..faafd672 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Update .gitattributes so we have consistent line endings - Change 266 files from CRLF to LF. - Run tests on push as well as on a pull request so developers can see impact +- `ArduinoBackend` now exposes `config_file_path` instead of `config_dir` so that we can be explicit about [strange behavior in `arduino-cli` that isn't going to change anytime soon](https://github.com/arduino/arduino-cli/issues/753) ### Deprecated diff --git a/lib/arduino_ci/arduino_backend.rb b/lib/arduino_ci/arduino_backend.rb index e8c7ec1d..0489581a 100644 --- a/lib/arduino_ci/arduino_backend.rb +++ b/lib/arduino_ci/arduino_backend.rb @@ -17,15 +17,17 @@ class ArduinoBackend # @return [String] the only allowable name for the arduino-cli config file. CONFIG_FILE_NAME = "arduino-cli.yaml".freeze + # Unfortunately we need error messaging around this quirk + # @return [String] The text to use for user apologies regarding the config file + CONFIG_FILE_APOLOGY = "Sorry this is weird, see https://github.com/arduino/arduino-cli/issues/753".freeze + # the actual path to the executable on this platform # @return [Pathname] attr_accessor :binary_path - # If a custom config is deired (i.e. for testing), specify it here. - # Note https://github.com/arduino/arduino-cli/issues/753 : the --config-file option - # is really the director that contains the file + # The directory that contains the config file # @return [Pathname] - attr_accessor :config_dir + attr_reader :config_dir # @return [String] STDOUT of the most recently-run command attr_reader :last_out @@ -53,7 +55,7 @@ def _wrap_run(work_fn, *args, **kwargs) has_env = !args.empty? && args[0].instance_of?(Hash) env_vars = has_env ? args[0] : {} actual_args = has_env ? args[1..-1] : args # need to shift over if we extracted args - custom_config = @config_dir.nil? ? [] : ["--config-file", @config_dir.to_s] + custom_config = @config_dir.nil? ? [] : ["--config-file", config_file_cli_param.to_s] full_args = [binary_path.to_s, "--format", "json"] + custom_config + actual_args full_cmd = env_vars.empty? ? full_args : [env_vars] + full_args @@ -62,6 +64,52 @@ def _wrap_run(work_fn, *args, **kwargs) work_fn.call(*full_cmd, **kwargs) end + # The config file name to be passed on the command line + # + # Note https://github.com/arduino/arduino-cli/issues/753 : the --config-file option + # is really the directory that contains the file + # + # @return [Pathname] + def config_file_path + @config_dir + CONFIG_FILE_NAME + end + + # The config file name to be passed on the command line + # + # Note https://github.com/arduino/arduino-cli/issues/753 : the --config-file option + # is really the directory that contains the file + # + # @param val [Pathname] The config file that will be used + # @return [Pathname] + def config_file_path=(rhs) + path_rhs = Pathname(rhs) + err_text = "Config file basename must be '#{CONFIG_FILE_NAME}'. #{CONFIG_FILE_APOLOGY}" + raise ArgumentError, err_text unless path_rhs.basename.to_s == CONFIG_FILE_NAME + + @config_dir = path_rhs.dirname + end + + # The config file to be used as a CLI param + # + # Apparently Linux wants the whole path, and OSX wants just the directory as of 0.29.0, + # it's all very annoying. See unit tests. + # + # @return [Pathname] the path to use for a given OS + def config_file_cli_param + OS.linux? ? config_file_path : @config_dir + end + + # Get an acceptable filename for use as a config file + # + # Note https://github.com/arduino/arduino-cli/issues/753 : the --config-file option + # is really the directory that contains the file + # + # @param dir [Pathname] the desired directory + # @return [Pathname] + def self.config_file_path_from_dir(dir) + Pathname(dir) + CONFIG_FILE_NAME + end + # build and run the arduino command def run_and_output(*args, **kwargs) _wrap_run((proc { |*a, **k| Host.run_and_output(*a, **k) }), *args, **kwargs) diff --git a/spec/arduino_backend_spec.rb b/spec/arduino_backend_spec.rb index 1b6c6ee0..2a494231 100644 --- a/spec/arduino_backend_spec.rb +++ b/spec/arduino_backend_spec.rb @@ -1,11 +1,9 @@ require "spec_helper" -require 'pathname' def get_sketch(dir, file) - File.join(File.dirname(__FILE__), dir, file) + Pathname.new(__FILE__).parent + dir + file end - RSpec.describe ArduinoCI::ArduinoBackend do next if skip_ruby_tests diff --git a/spec/arduino_installation_spec.rb b/spec/arduino_installation_spec.rb index e53ea523..10bd0da5 100644 --- a/spec/arduino_installation_spec.rb +++ b/spec/arduino_installation_spec.rb @@ -1,4 +1,38 @@ require "spec_helper" +require "pathname" +require "tmpdir" +require "os" + +# test function for below, to avoid long lines +def bug_753_cmd(backend, config_file) + [ + backend.binary_path.to_s, + "--config-file", + config_file.to_s, + "--verbose", + "config", + "dump" + ] +end + +def with_tmp_file(desired_filename = nil) + Dir.mktmpdir do |tdir| + config_dir = Pathname(tdir) + config_file = config_dir + (desired_filename || ArduinoCI::ArduinoBackend::CONFIG_FILE_NAME) + File.open(config_file, "w") { |f| f.write("") } + yield(config_dir, config_file) + end +end + +def config_success_msg(config_file) + config_file_str = config_file.to_s + config_file_str = config_file_str.gsub('/', '\\') if OS.windows? + "Using config file: #{config_file}" +end + +def config_fail_msg + "Config file not found, using default values" +end RSpec.describe ArduinoCI::ArduinoInstallation do next if skip_ruby_tests @@ -35,4 +69,48 @@ end end + context "installed version-specific quirks" do + backend = ArduinoCI::ArduinoInstallation.autolocate! + + # https://github.com/arduino/arduino-cli/issues/753 + + it "suffers from arduino-cli bug 753 - nonstandard filename" do + # foo.yml won't be accepted as a filename + with_tmp_file("foo.yml") do |config_dir, config_file| + expect(config_dir).to exist + expect(config_file).to exist + ret = ArduinoCI::Host.run_and_capture(*bug_753_cmd(backend, config_file)) + if OS.linux? + expect(ret[:out].lines[0]).to include(config_success_msg(config_file)) + else + expect(ret[:out].lines[0]).to include(config_fail_msg) + end + end + end + + it "obeys arduino-cli bug 753 workaround" do + # the standard filename will work + with_tmp_file do |config_dir, config_file| + expect(config_dir).to exist + expect(config_file).to exist + ret = ArduinoCI::Host.run_and_capture(*bug_753_cmd(backend, config_file)) + expect(ret[:out].lines[0]).to include(config_success_msg(config_file)) + end + end + + it "obeys arduino-cli bug 753" do + # the directory alone will work if there is a file with the right name + with_tmp_file do |config_dir, config_file| + expect(config_dir).to exist + expect(config_file).to exist + ret = ArduinoCI::Host.run_and_capture(*bug_753_cmd(backend, config_dir)) + if OS.linux? + expect(ret[:out].lines[0]).to include(config_fail_msg) + else + expect(ret[:out].lines[0]).to include(config_success_msg(config_file)) + end + end + end + end + end diff --git a/spec/fake_lib_dir.rb b/spec/fake_lib_dir.rb index 2c613213..027450ef 100644 --- a/spec/fake_lib_dir.rb +++ b/spec/fake_lib_dir.rb @@ -11,9 +11,9 @@ class FakeLibDir def initialize # we will need to install some dummy libraries into a fake location, so do that on demand @config_dir = Pathname.new(Dir.pwd).realpath - @config_file = @config_dir + ArduinoCI::ArduinoBackend::CONFIG_FILE_NAME + @config_file = ArduinoCI::ArduinoBackend.config_file_path_from_dir(@config_dir) @backend = ArduinoCI::ArduinoInstallation.autolocate! - @backend.config_dir = @config_dir + @backend.config_file_path = @config_file end # designed to be called by rspec's "around" function From 5f6615156f30706469ce46a19ac8a299492d3cb4 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 17 Dec 2022 16:48:46 -0500 Subject: [PATCH 11/37] fixup config path --- lib/arduino_ci/arduino_backend.rb | 2 +- spec/arduino_installation_spec.rb | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/arduino_ci/arduino_backend.rb b/lib/arduino_ci/arduino_backend.rb index 0489581a..167348dd 100644 --- a/lib/arduino_ci/arduino_backend.rb +++ b/lib/arduino_ci/arduino_backend.rb @@ -96,7 +96,7 @@ def config_file_path=(rhs) # # @return [Pathname] the path to use for a given OS def config_file_cli_param - OS.linux? ? config_file_path : @config_dir + OS.osx? ? @config_dir : config_file_path end # Get an acceptable filename for use as a config file diff --git a/spec/arduino_installation_spec.rb b/spec/arduino_installation_spec.rb index 10bd0da5..fe559220 100644 --- a/spec/arduino_installation_spec.rb +++ b/spec/arduino_installation_spec.rb @@ -80,10 +80,10 @@ def config_fail_msg expect(config_dir).to exist expect(config_file).to exist ret = ArduinoCI::Host.run_and_capture(*bug_753_cmd(backend, config_file)) - if OS.linux? - expect(ret[:out].lines[0]).to include(config_success_msg(config_file)) - else + if OS.osx? expect(ret[:out].lines[0]).to include(config_fail_msg) + else + expect(ret[:out].lines[0]).to include(config_success_msg(config_file)) end end end @@ -104,10 +104,10 @@ def config_fail_msg expect(config_dir).to exist expect(config_file).to exist ret = ArduinoCI::Host.run_and_capture(*bug_753_cmd(backend, config_dir)) - if OS.linux? - expect(ret[:out].lines[0]).to include(config_fail_msg) - else + if OS.osx? expect(ret[:out].lines[0]).to include(config_success_msg(config_file)) + else + expect(ret[:out].lines[0]).to include(config_fail_msg) end end end From d4c3fea4396ea0fdcd529c7dcb3695bcfe26aa65 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 17 Dec 2022 15:07:35 -0500 Subject: [PATCH 12/37] Update arduino-cli to 0.29.0 --- CHANGELOG.md | 1 + lib/arduino_ci/arduino_backend.rb | 2 +- lib/arduino_ci/arduino_installation.rb | 2 +- spec/arduino_installation_spec.rb | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faafd672..07412997 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Change 266 files from CRLF to LF. - Run tests on push as well as on a pull request so developers can see impact - `ArduinoBackend` now exposes `config_file_path` instead of `config_dir` so that we can be explicit about [strange behavior in `arduino-cli` that isn't going to change anytime soon](https://github.com/arduino/arduino-cli/issues/753) +- Use `arduino-cli` version `0.29.0` as the backend ### Deprecated diff --git a/lib/arduino_ci/arduino_backend.rb b/lib/arduino_ci/arduino_backend.rb index 167348dd..0df1de3e 100644 --- a/lib/arduino_ci/arduino_backend.rb +++ b/lib/arduino_ci/arduino_backend.rb @@ -211,7 +211,7 @@ def compile_sketch(path, boardname) @last_msg = "Can't compile Sketch at nonexistent path '#{path}'!" return false end - ret = run_and_capture("compile", "--fqbn", boardname, "--warnings", "all", "--dry-run", path.to_s) + ret = run_and_capture("compile", "--fqbn", boardname, "--warnings", "all", path.to_s) @last_msg = ret[:out] ret[:success] end diff --git a/lib/arduino_ci/arduino_installation.rb b/lib/arduino_ci/arduino_installation.rb index b422fac0..5d605704 100644 --- a/lib/arduino_ci/arduino_installation.rb +++ b/lib/arduino_ci/arduino_installation.rb @@ -12,7 +12,7 @@ class ArduinoInstallationError < StandardError; end # Manage the OS-specific install location of Arduino class ArduinoInstallation - DESIRED_ARDUINO_CLI_VERSION = "0.13.0".freeze + DESIRED_ARDUINO_CLI_VERSION = "0.29.0".freeze class << self diff --git a/spec/arduino_installation_spec.rb b/spec/arduino_installation_spec.rb index fe559220..b66937fb 100644 --- a/spec/arduino_installation_spec.rb +++ b/spec/arduino_installation_spec.rb @@ -39,7 +39,7 @@ def config_fail_msg context "constants" do it "Exposes desired backend version" do - expect(ArduinoCI::ArduinoInstallation::DESIRED_ARDUINO_CLI_VERSION).to eq("0.13.0") + expect(ArduinoCI::ArduinoInstallation::DESIRED_ARDUINO_CLI_VERSION).to eq("0.29.0") end end From 83fd308afc252c1d6086b2e137e5c1d9e7fe8f5f Mon Sep 17 00:00:00 2001 From: Helmi Date: Sat, 5 Mar 2022 17:28:20 +0700 Subject: [PATCH 13/37] Remove flag `--no-dry-run` for `arduino-ci >= 0.14.0` --- lib/arduino_ci/arduino_backend.rb | 13 ++++++++- spec/arduino_backend_spec.rb | 46 +++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/lib/arduino_ci/arduino_backend.rb b/lib/arduino_ci/arduino_backend.rb index 0df1de3e..be55985a 100644 --- a/lib/arduino_ci/arduino_backend.rb +++ b/lib/arduino_ci/arduino_backend.rb @@ -211,7 +211,12 @@ def compile_sketch(path, boardname) @last_msg = "Can't compile Sketch at nonexistent path '#{path}'!" return false end - ret = run_and_capture("compile", "--fqbn", boardname, "--warnings", "all", path.to_s) + use_dry_run = should_use_dry_run? + if use_dry_run + ret = run_and_capture("compile", "--fqbn", boardname, "--warnings", "all", "--dry-run", path.to_s) + else + ret = run_and_capture("compile", "--fqbn", boardname, "--warnings", "all", path.to_s) + end @last_msg = ret[:out] ret[:success] end @@ -299,5 +304,11 @@ def last_bytes_usage Hash[mem_info.names.map(&:to_sym).zip(mem_info.captures.map(&:to_i))] end + + def should_use_dry_run? + ret = capture_json("version") + version = ret[:json]["VersionString"] + Gem::Version.new(version) < Gem::Version.new('0.14') + end end end diff --git a/spec/arduino_backend_spec.rb b/spec/arduino_backend_spec.rb index 2a494231..9407268e 100644 --- a/spec/arduino_backend_spec.rb +++ b/spec/arduino_backend_spec.rb @@ -110,4 +110,50 @@ def get_sketch(dir, file) expect(the_bytes[:max]).to eq 2048 end end + + context "--dry-run flags" do + + sketch_path_ino = get_sketch("FakeSketch", "FakeSketch.ino") + + before { allow(backend).to receive(:run_and_capture).and_call_original } + + it "Uses --dry-run flag for arduino-cli version < 0.14.0" do + parsed_stdout = JSON.parse('{ "VersionString": "0.13.6" }') + cli_version_output = { + json: parsed_stdout + } + allow(backend).to receive(:capture_json).and_return cli_version_output + + backend.compile_sketch(sketch_path_ino, "arduino:avr:uno") + + expect(backend).to have_received(:run_and_capture).with( + "compile", + "--fqbn", + "arduino:avr:uno", + "--warnings", + "all", + "--dry-run", + sketch_path_ino.to_s + ) + end + + it "Does not use --dry-run flag for arduino-cli version >= 0.14.0" do + parsed_stdout = JSON.parse('{ "VersionString": "0.14.0" }') + cli_version_output = { + json: parsed_stdout + } + allow(backend).to receive(:capture_json).and_return cli_version_output + + backend.compile_sketch(sketch_path_ino, "arduino:avr:uno") + + expect(backend).to have_received(:run_and_capture).with( + "compile", + "--fqbn", + "arduino:avr:uno", + "--warnings", + "all", + sketch_path_ino.to_s + ) + end + end end From 1f312ac6e37ca66576c9c1e2f3ad6f9ed6714615 Mon Sep 17 00:00:00 2001 From: Ian Date: Fri, 11 Mar 2022 09:48:40 -0500 Subject: [PATCH 14/37] Add comments, avoid unnecessary assignments --- lib/arduino_ci/arduino_backend.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/arduino_ci/arduino_backend.rb b/lib/arduino_ci/arduino_backend.rb index be55985a..ed45f1cf 100644 --- a/lib/arduino_ci/arduino_backend.rb +++ b/lib/arduino_ci/arduino_backend.rb @@ -211,11 +211,11 @@ def compile_sketch(path, boardname) @last_msg = "Can't compile Sketch at nonexistent path '#{path}'!" return false end - use_dry_run = should_use_dry_run? - if use_dry_run - ret = run_and_capture("compile", "--fqbn", boardname, "--warnings", "all", "--dry-run", path.to_s) + + ret = if should_use_dry_run? + run_and_capture("compile", "--fqbn", boardname, "--warnings", "all", "--dry-run", path.to_s) else - ret = run_and_capture("compile", "--fqbn", boardname, "--warnings", "all", path.to_s) + run_and_capture("compile", "--fqbn", boardname, "--warnings", "all", path.to_s) end @last_msg = ret[:out] ret[:success] @@ -305,6 +305,10 @@ def last_bytes_usage Hash[mem_info.names.map(&:to_sym).zip(mem_info.captures.map(&:to_i))] end + private + + # Since the dry-run behavior became default in arduino-cli 0.14, the command line flag was removed + # @return [Bool] whether the --dry-run flag is available for this arduino-cli version def should_use_dry_run? ret = capture_json("version") version = ret[:json]["VersionString"] From 8e40c59ae9d5f78bd742fa7cbeebda45578df17f Mon Sep 17 00:00:00 2001 From: Midas Date: Tue, 16 Feb 2021 09:11:28 +0100 Subject: [PATCH 15/37] added arduino nano every config --- misc/default.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/misc/default.yml b/misc/default.yml index befbfa6f..793936e6 100644 --- a/misc/default.yml +++ b/misc/default.yml @@ -9,6 +9,8 @@ packages: url: https://downloads.arduino.cc/packages/package_index.json arduino:samd: url: https://downloads.arduino.cc/packages/package_index.json + arduino:megaavr: + url: https://downloads.arduino.cc/packages/package_index.json esp8266:esp8266: url: http://arduino.esp8266.com/stable/package_esp8266com_index.json adafruit:avr: @@ -57,6 +59,17 @@ platforms: - NUM_SERIAL_PORTS=2 warnings: flags: + nano_every: + board: arduino:megaavr:nona4809 + package: arduino:megaavr + gcc: + features: + defines: + - MILLIS_USE_TIMERB3 + - NO_EXTERNAL_I2C_PULLUP + - AVR_NANO_4809_328MODE + warnings: + flags: esp32: board: esp32:esp32:featheresp32:FlashFreq=80 package: esp32:esp32 @@ -181,6 +194,7 @@ compile: - esp32 - esp8266 - mega2560 + - nano_every unittest: compilers: From 15ebe8667a8111d8db0fa539cc0a60b1ea2824f8 Mon Sep 17 00:00:00 2001 From: Midas Date: Tue, 16 Feb 2021 09:25:04 +0100 Subject: [PATCH 16/37] fixed formatting error --- misc/default.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/misc/default.yml b/misc/default.yml index 793936e6..edfefd2a 100644 --- a/misc/default.yml +++ b/misc/default.yml @@ -60,16 +60,16 @@ platforms: warnings: flags: nano_every: - board: arduino:megaavr:nona4809 - package: arduino:megaavr - gcc: - features: - defines: - - MILLIS_USE_TIMERB3 - - NO_EXTERNAL_I2C_PULLUP - - AVR_NANO_4809_328MODE - warnings: - flags: + board: arduino:megaavr:nona4809 + package: arduino:megaavr + gcc: + features: + defines: + - MILLIS_USE_TIMERB3 + - NO_EXTERNAL_I2C_PULLUP + - AVR_NANO_4809_328MODE + warnings: + flags: esp32: board: esp32:esp32:featheresp32:FlashFreq=80 package: esp32:esp32 From 0d245e5f373d8b8484cb901d14087a4aa71730fa Mon Sep 17 00:00:00 2001 From: Midas Date: Tue, 16 Feb 2021 15:20:00 +0100 Subject: [PATCH 17/37] added changelog message --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07412997..5767de8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added a CI workflow to lint the code base - Added a CI workflow to check for spelling errors - Extraction of byes usage in a compiled sketch is now calculated in a method: `ArduinoBackend.last_bytes_usage` - +- Added ```nano_every``` platform to represent ```arduino:megaavr``` architecture ### Changed - We now compile a shared library to be used for each test. - Put build artifacts in a separate directory to reduce clutter. From 4114f7efc1bd129048673c08cc87aaa85132a9d7 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 17 Dec 2022 15:51:04 -0500 Subject: [PATCH 18/37] fix test case for nano every --- spec/ci_config_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/ci_config_spec.rb b/spec/ci_config_spec.rb index 4db68968..c838a6e1 100644 --- a/spec/ci_config_spec.rb +++ b/spec/ci_config_spec.rb @@ -31,7 +31,7 @@ expect(default_config.package_url("adafruit:avr")).to eq("https://adafruit.github.io/arduino-board-index/package_adafruit_index.json") expect(default_config.package_url("adafruit:samd")).to eq("https://adafruit.github.io/arduino-board-index/package_adafruit_index.json") expect(default_config.package_url("esp32:esp32")).to eq("https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json") - expect(default_config.platforms_to_build).to match(["uno", "due", "zero", "leonardo", "m4", "esp32", "esp8266", "mega2560"]) + expect(default_config.platforms_to_build).to match(["uno", "due", "zero", "leonardo", "m4", "esp32", "esp8266", "mega2560", "nano_every"]) expect(default_config.platforms_to_unittest).to match(["uno", "due", "zero", "leonardo"]) expect(default_config.aux_libraries_for_build).to match([]) expect(default_config.aux_libraries_for_unittest).to match([]) From ffc428237fb267469470c5565bd4cd76fa2ac828 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 22 Dec 2022 11:10:20 -0500 Subject: [PATCH 19/37] Add guard for library properties path nonexistence --- lib/arduino_ci/library_properties.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/arduino_ci/library_properties.rb b/lib/arduino_ci/library_properties.rb index f1fb98d9..1403b6ca 100644 --- a/lib/arduino_ci/library_properties.rb +++ b/lib/arduino_ci/library_properties.rb @@ -11,6 +11,8 @@ class LibraryProperties # @param path [Pathname] The path to the library.properties file def initialize(path) @fields = {} + raise ArgumentError, "Library properties at '#{path}' doesn't exist" unless path.exist? + File.foreach(path) do |line_with_delim| line = line_with_delim.chomp parts = line.split("=", 2) From 9cb84af8254d061aded0467035cf658f27e11a80 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 22 Dec 2022 11:10:39 -0500 Subject: [PATCH 20/37] Convert FileUtils to block style --- spec/fake_lib_dir.rb | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/spec/fake_lib_dir.rb b/spec/fake_lib_dir.rb index 027450ef..08702363 100644 --- a/spec/fake_lib_dir.rb +++ b/spec/fake_lib_dir.rb @@ -1,5 +1,11 @@ require "arduino_ci" +# This class is meant for the :around behavior of RSpec test cases so that +# we can make temporary directories in test cases. Note that since test cases +# are evaluated on load, any temp directories that were created will not exist +# by the time the test runs. So this class handles all of the particulars +# around creating a fake library directory on time and configuring the backend +# to properly use it. class FakeLibDir attr_reader :config_dir @@ -18,16 +24,18 @@ def initialize # designed to be called by rspec's "around" function def in_pristine_fake_libraries_dir(example) - d = Dir.mktmpdir - begin + # we will make a dummy directory to contain the libraries directory, + # and use that directory in a dummy config which we will pass to the backend. + # then we can run the test case + Dir.mktmpdir do |d| # write a yaml file containing the current directory dummy_config = { "directories" => { "user" => d.to_s } } @arduino_dir = Pathname.new(d) @libraries_dir = @arduino_dir + "libraries" Dir.mkdir(@libraries_dir) - f = File.open(@config_file, "w") - begin + # with the config file, enforce a structure similar to a temp file -- delete after use + File.open(@config_file, "w") do |f| f.write dummy_config.to_yaml f.close example.run @@ -39,6 +47,7 @@ def in_pristine_fake_libraries_dir(example) end end ensure + # the tmp dir will be cleaned up automatically, but if we did our own symlink hack then here is the place to clean it up if ArduinoCI::Host.needs_symlink_hack? stdout, stderr, exitstatus = Open3.capture3('cmd.exe', "/c rmdir /s /q #{ArduinoCI::Host.pathname_to_windows(d)}") unless exitstatus.success? @@ -46,8 +55,6 @@ def in_pristine_fake_libraries_dir(example) puts stdout puts stderr end - else - FileUtils.remove_entry(d) end end end From deb0f4e1a462d0b26b41f475adabe825812cc008 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 15 Feb 2021 11:14:54 -0500 Subject: [PATCH 21/37] Better communication about undefined platforms in config --- CHANGELOG.md | 1 + exe/arduino_ci.rb | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5767de8c..7b97feb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Run Windows tests on Windows not Ubuntu - Properly report error in building shared library - A missing `examples` directory no longer causes a crash in `cpp_library.rb` +- Referring to an undefined platform no longer causes a crash; it's now a helpful error message ### Security diff --git a/exe/arduino_ci.rb b/exe/arduino_ci.rb index 6abf2285..73d88fb0 100755 --- a/exe/arduino_ci.rb +++ b/exe/arduino_ci.rb @@ -419,6 +419,11 @@ def perform_unit_tests(cpp_library, file_config) end end + # having undefined platforms is a config error + platforms.select { |p| config.platform_info[p].nil? }.each do |p| + assure("Platform '#{p}' is defined in configuration files") { false } + end + install_arduino_library_dependencies(config.aux_libraries_for_unittest, "") platforms.each do |p| @@ -486,6 +491,7 @@ def perform_example_compilation_tests(cpp_library, config) ovr_config = config.from_example(example_path) platforms = choose_platform_set(ovr_config, "library example", ovr_config.platforms_to_build, cpp_library.library_properties) + # having no platforms defined is probably an error if platforms.empty? explain_and_exercise_envvar(VAR_EXPECT_EXAMPLES, "examples compilation", "platforms and architectures") do puts " Configured platforms: #{ovr_config.platforms_to_build}" @@ -495,11 +501,16 @@ def perform_example_compilation_tests(cpp_library, config) end end + # having undefined platforms is a config error + platforms.select { |p| ovr_config.platform_info[p].nil? }.each do |p| + assure("Platform '#{p}' is defined in configuration files") { false } + end + install_all_packages(platforms, ovr_config) install_arduino_library_dependencies(ovr_config.aux_libraries_for_build, "") platforms.each do |p| - board = ovr_config.platform_info[p][:board] + board = ovr_config.platform_info[p][:board] # assured to exist, above attempt("Compiling #{example_name} for #{board}") do ret = @backend.compile_sketch(example_path, board) unless ret From adf4dff4dd4ffbd95fb68bef53ad1eb3c29194c6 Mon Sep 17 00:00:00 2001 From: James Foster Date: Mon, 15 Feb 2021 10:47:58 -0800 Subject: [PATCH 22/37] Set all text files to have endings --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 6ada3e30..70242330 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,7 +13,7 @@ # the Git repository itself. # Set the default behavior, in case people don't have core.autocrlf set. -* text=auto +* text=auto eol=lf # Explicitly declare text files you want to always be normalized and converted # to native line endings on checkout. Git would likely get these right, but From 858c171ff8f192f5b60669931875a76c18751142 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sun, 4 Apr 2021 15:22:57 -0400 Subject: [PATCH 23/37] print working directory in test output --- CHANGELOG.md | 2 ++ exe/arduino_ci.rb | 1 + 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b97feb8..914e1f50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added a CI workflow to check for spelling errors - Extraction of byes usage in a compiled sketch is now calculated in a method: `ArduinoBackend.last_bytes_usage` - Added ```nano_every``` platform to represent ```arduino:megaavr``` architecture +- Working directory is now printed in test runner output + ### Changed - We now compile a shared library to be used for each test. - Put build artifacts in a separate directory to reduce clutter. diff --git a/exe/arduino_ci.rb b/exe/arduino_ci.rb index 73d88fb0..4b9d4572 100755 --- a/exe/arduino_ci.rb +++ b/exe/arduino_ci.rb @@ -533,6 +533,7 @@ def perform_example_compilation_tests(cpp_library, config) banner inform("Host OS") { ArduinoCI::Host.os } +inform("Working directory") { Dir.pwd } # initialize command and config config = ArduinoCI::CIConfig.default.from_project_library From 76947778fef6ba7ad173e3644015b411845f4b21 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sun, 4 Apr 2021 15:29:51 -0400 Subject: [PATCH 24/37] Explicitly include irb via rubygems --- CHANGELOG.md | 1 + Gemfile | 1 + SampleProjects/TestSomething/Gemfile | 1 + 3 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 914e1f50..4d1dfaca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Extraction of byes usage in a compiled sketch is now calculated in a method: `ArduinoBackend.last_bytes_usage` - Added ```nano_every``` platform to represent ```arduino:megaavr``` architecture - Working directory is now printed in test runner output +- Explicitly include `irb` via rubygems ### Changed - We now compile a shared library to be used for each test. diff --git a/Gemfile b/Gemfile index d3ff73d9..ff624287 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,7 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } gemspec gem "bundler", "> 1.15", require: false, group: :test +gem "irb", "~> 1.3.5", require: false gem "keepachangelog_manager", "~> 0.0.2", require: false, group: :test gem "rspec", "~> 3.0", require: false, group: :test gem 'rubocop', '~>1.5.0', require: false, group: :test diff --git a/SampleProjects/TestSomething/Gemfile b/SampleProjects/TestSomething/Gemfile index b2b3b1fd..56c06235 100644 --- a/SampleProjects/TestSomething/Gemfile +++ b/SampleProjects/TestSomething/Gemfile @@ -1,2 +1,3 @@ source 'https://rubygems.org' gem 'arduino_ci', path: '../../' +gem "irb", "~> 1.3.5", require: false From c1eae2644d48bbe1ce7f2637e7c1a48dbb245c3c Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sun, 4 Apr 2021 20:28:40 -0400 Subject: [PATCH 25/37] wrap library spec tests in a context --- spec/cpp_library_spec.rb | 281 ++++++++++++++++++++------------------- 1 file changed, 142 insertions(+), 139 deletions(-) diff --git a/spec/cpp_library_spec.rb b/spec/cpp_library_spec.rb index ec03f055..a0edc535 100644 --- a/spec/cpp_library_spec.rb +++ b/spec/cpp_library_spec.rb @@ -75,170 +75,173 @@ def verified_install(backend, path) RSpec.describe ArduinoCI::CppLibrary do next if skip_ruby_tests - answers = { - DoSomething: { - one_five: false, - library_properties: true, - cpp_files: [Pathname.new("DoSomething") + "do-something.cpp"], - cpp_files_libraries: [], - header_dirs: [Pathname.new("DoSomething")], - arduino_library_src_dirs: [], - test_files: [ - "DoSomething/test/bad-errormessages.cpp", - "DoSomething/test/bad-null.cpp", - "DoSomething/test/good-assert.cpp", - "DoSomething/test/good-library.cpp", - "DoSomething/test/good-null.cpp", - ].map { |f| Pathname.new(f) } - }, - OnePointOhDummy: { - one_five: false, - library_properties: false, - cpp_files: [ - "OnePointOhDummy/YesBase.cpp", - "OnePointOhDummy/utility/YesUtil.cpp", - ].map { |f| Pathname.new(f) }, - cpp_files_libraries: [], - header_dirs: [ - "OnePointOhDummy", - "OnePointOhDummy/utility" - ].map { |f| Pathname.new(f) }, - arduino_library_src_dirs: [], - test_files: [ - "OnePointOhDummy/test/null.cpp", - ].map { |f| Pathname.new(f) } - }, - OnePointFiveMalformed: { - one_five: false, - library_properties: false, - cpp_files: [ - "OnePointFiveMalformed/YesBase.cpp", - "OnePointFiveMalformed/utility/YesUtil.cpp", - ].map { |f| Pathname.new(f) }, - cpp_files_libraries: [], - header_dirs: [ - "OnePointFiveMalformed", - "OnePointFiveMalformed/utility" - ].map { |f| Pathname.new(f) }, - arduino_library_src_dirs: [], - test_files: [] - }, - OnePointFiveDummy: { + context "arduino-library-specification detection" do + + answers = { + DoSomething: { + one_five: false, + library_properties: true, + cpp_files: [Pathname.new("DoSomething") + "do-something.cpp"], + cpp_files_libraries: [], + header_dirs: [Pathname.new("DoSomething")], + arduino_library_src_dirs: [], + test_files: [ + "DoSomething/test/bad-errormessages.cpp", + "DoSomething/test/bad-null.cpp", + "DoSomething/test/good-assert.cpp", + "DoSomething/test/good-library.cpp", + "DoSomething/test/good-null.cpp", + ].map { |f| Pathname.new(f) } + }, + OnePointOhDummy: { + one_five: false, + library_properties: false, + cpp_files: [ + "OnePointOhDummy/YesBase.cpp", + "OnePointOhDummy/utility/YesUtil.cpp", + ].map { |f| Pathname.new(f) }, + cpp_files_libraries: [], + header_dirs: [ + "OnePointOhDummy", + "OnePointOhDummy/utility" + ].map { |f| Pathname.new(f) }, + arduino_library_src_dirs: [], + test_files: [ + "OnePointOhDummy/test/null.cpp", + ].map { |f| Pathname.new(f) } + }, + OnePointFiveMalformed: { + one_five: false, + library_properties: false, + cpp_files: [ + "OnePointFiveMalformed/YesBase.cpp", + "OnePointFiveMalformed/utility/YesUtil.cpp", + ].map { |f| Pathname.new(f) }, + cpp_files_libraries: [], + header_dirs: [ + "OnePointFiveMalformed", + "OnePointFiveMalformed/utility" + ].map { |f| Pathname.new(f) }, + arduino_library_src_dirs: [], + test_files: [] + }, + OnePointFiveDummy: { + one_five: true, + library_properties: true, + cpp_files: [ + "OnePointFiveDummy/src/YesSrc.cpp", + "OnePointFiveDummy/src/subdir/YesSubdir.cpp", + ].map { |f| Pathname.new(f) }, + cpp_files_libraries: [], + header_dirs: [ + "OnePointFiveDummy/src", + "OnePointFiveDummy/src/subdir", + ].map { |f| Pathname.new(f) }, + arduino_library_src_dirs: [], + test_files: [ + "OnePointFiveDummy/test/null.cpp", + ].map { |f| Pathname.new(f) } + } + } + + # easier to construct this one from the other test cases + answers[:DependOnSomething] = { one_five: true, library_properties: true, - cpp_files: [ - "OnePointFiveDummy/src/YesSrc.cpp", - "OnePointFiveDummy/src/subdir/YesSubdir.cpp", - ].map { |f| Pathname.new(f) }, - cpp_files_libraries: [], - header_dirs: [ - "OnePointFiveDummy/src", - "OnePointFiveDummy/src/subdir", - ].map { |f| Pathname.new(f) }, - arduino_library_src_dirs: [], + cpp_files: ["DependOnSomething/src/YesDeps.cpp"].map { |f| Pathname.new(f) }, + cpp_files_libraries: answers[:OnePointOhDummy][:cpp_files] + answers[:OnePointFiveDummy][:cpp_files], + header_dirs: ["DependOnSomething/src"].map { |f| Pathname.new(f) }, # this is not recursive! + arduino_library_src_dirs: answers[:OnePointOhDummy][:header_dirs] + answers[:OnePointFiveDummy][:header_dirs], test_files: [ - "OnePointFiveDummy/test/null.cpp", - ].map { |f| Pathname.new(f) } + "DependOnSomething/test/null.cpp", + ].map { |f| Pathname.new(f) } } - } - - # easier to construct this one from the other test cases - answers[:DependOnSomething] = { - one_five: true, - library_properties: true, - cpp_files: ["DependOnSomething/src/YesDeps.cpp"].map { |f| Pathname.new(f) }, - cpp_files_libraries: answers[:OnePointOhDummy][:cpp_files] + answers[:OnePointFiveDummy][:cpp_files], - header_dirs: ["DependOnSomething/src"].map { |f| Pathname.new(f) }, # this is not recursive! - arduino_library_src_dirs: answers[:OnePointOhDummy][:header_dirs] + answers[:OnePointFiveDummy][:header_dirs], - test_files: [ - "DependOnSomething/test/null.cpp", - ].map { |f| Pathname.new(f) } - } - answers.freeze + answers.freeze - answers.each do |sampleproject, expected| + answers.each do |sampleproject, expected| - # we will need to install some dummy libraries into a fake location, so do that on demand - fld = FakeLibDir.new - backend = fld.backend + # we will need to install some dummy libraries into a fake location, so do that on demand + fld = FakeLibDir.new + backend = fld.backend - context "#{sampleproject}" do - cpp_lib_path = sampleproj_path + sampleproject.to_s - around(:example) { |example| fld.in_pristine_fake_libraries_dir(example) } - before(:each) do - @base_dir = fld.libraries_dir - @cpp_library = verified_install(backend, cpp_lib_path) - end + context "#{sampleproject}" do + cpp_lib_path = sampleproj_path + sampleproject.to_s + around(:example) { |example| fld.in_pristine_fake_libraries_dir(example) } + before(:each) do + @base_dir = fld.libraries_dir + @cpp_library = verified_install(backend, cpp_lib_path) + end - it "is a sane test env" do - expect(sampleproject.to_s).to eq(@cpp_library.name) - end + it "is a sane test env" do + expect(sampleproject.to_s).to eq(@cpp_library.name) + end - it "detects 1.5 format" do - expect(@cpp_library.one_point_five?).to eq(expected[:one_five]) - end + it "detects 1.5 format" do + expect(@cpp_library.one_point_five?).to eq(expected[:one_five]) + end - it "detects library.properties" do - expect(@cpp_library.library_properties?).to eq(expected[:library_properties]) - end + it "detects library.properties" do + expect(@cpp_library.library_properties?).to eq(expected[:library_properties]) + end - context "cpp_files" do - it "finds cpp files in directory" do - relative_paths = @cpp_library.cpp_files.map { |f| f.relative_path_from(@base_dir) } - expect(relative_paths.map(&:to_s)).to match_array(expected[:cpp_files].map(&:to_s)) + context "cpp_files" do + it "finds cpp files in directory" do + relative_paths = @cpp_library.cpp_files.map { |f| f.relative_path_from(@base_dir) } + expect(relative_paths.map(&:to_s)).to match_array(expected[:cpp_files].map(&:to_s)) + end end - end - context "cpp_files_libraries" do - it "finds cpp files in directories of dependencies" do - @cpp_library.all_arduino_library_dependencies! # side effect: installs them - dependencies = @cpp_library.arduino_library_dependencies.nil? ? [] : @cpp_library.arduino_library_dependencies - dependencies.each { |d| verified_install(backend, sampleproj_path + d) } - relative_paths = @cpp_library.cpp_files_libraries(dependencies).map { |f| f.relative_path_from(@base_dir) } - expect(relative_paths.map(&:to_s)).to match_array(expected[:cpp_files_libraries].map(&:to_s)) + context "cpp_files_libraries" do + it "finds cpp files in directories of dependencies" do + @cpp_library.all_arduino_library_dependencies! # side effect: installs them + dependencies = @cpp_library.arduino_library_dependencies.nil? ? [] : @cpp_library.arduino_library_dependencies + dependencies.each { |d| verified_install(backend, sampleproj_path + d) } + relative_paths = @cpp_library.cpp_files_libraries(dependencies).map { |f| f.relative_path_from(@base_dir) } + expect(relative_paths.map(&:to_s)).to match_array(expected[:cpp_files_libraries].map(&:to_s)) + end end - end - context "header_dirs" do - it "finds directories containing h files" do - relative_paths = @cpp_library.header_dirs.map { |f| f.relative_path_from(@base_dir) } - expect(relative_paths.map(&:to_s)).to match_array(expected[:header_dirs].map(&:to_s)) + context "header_dirs" do + it "finds directories containing h files" do + relative_paths = @cpp_library.header_dirs.map { |f| f.relative_path_from(@base_dir) } + expect(relative_paths.map(&:to_s)).to match_array(expected[:header_dirs].map(&:to_s)) + end end - end - context "tests_dir" do - it "locates the tests directory" do - # since we don't know where the CI system will install this stuff, - # we need to go looking for a relative path to the SampleProjects directory - # just to get our "expected" value - relative_path = @cpp_library.tests_dir.relative_path_from(@base_dir) - expect(relative_path.to_s).to eq("#{sampleproject}/test") + context "tests_dir" do + it "locates the tests directory" do + # since we don't know where the CI system will install this stuff, + # we need to go looking for a relative path to the SampleProjects directory + # just to get our "expected" value + relative_path = @cpp_library.tests_dir.relative_path_from(@base_dir) + expect(relative_path.to_s).to eq("#{sampleproject}/test") + end end - end - context "examples_dir" do - it "locates the examples directory" do - relative_path = @cpp_library.examples_dir.relative_path_from(@base_dir) - expect(relative_path.to_s).to eq("#{sampleproject}/examples") + context "examples_dir" do + it "locates the examples directory" do + relative_path = @cpp_library.examples_dir.relative_path_from(@base_dir) + expect(relative_path.to_s).to eq("#{sampleproject}/examples") + end end - end - context "test_files" do - it "finds cpp files in directory" do - relative_paths = @cpp_library.test_files.map { |f| f.relative_path_from(@base_dir) } - expect(relative_paths.map(&:to_s)).to match_array(expected[:test_files].map(&:to_s)) + context "test_files" do + it "finds cpp files in directory" do + relative_paths = @cpp_library.test_files.map { |f| f.relative_path_from(@base_dir) } + expect(relative_paths.map(&:to_s)).to match_array(expected[:test_files].map(&:to_s)) + end end - end - context "arduino_library_src_dirs" do - it "finds src dirs from dependent libraries" do - # we explicitly feed in the internal dependencies - dependencies = @cpp_library.arduino_library_dependencies.nil? ? [] : @cpp_library.arduino_library_dependencies - dependencies.each { |d| verified_install(backend, sampleproj_path + d) } - relative_paths = @cpp_library.arduino_library_src_dirs(dependencies).map { |f| f.relative_path_from(@base_dir) } - expect(relative_paths.map(&:to_s)).to match_array(expected[:arduino_library_src_dirs].map(&:to_s)) + context "arduino_library_src_dirs" do + it "finds src dirs from dependent libraries" do + # we explicitly feed in the internal dependencies + dependencies = @cpp_library.arduino_library_dependencies.nil? ? [] : @cpp_library.arduino_library_dependencies + dependencies.each { |d| verified_install(backend, sampleproj_path + d) } + relative_paths = @cpp_library.arduino_library_src_dirs(dependencies).map { |f| f.relative_path_from(@base_dir) } + expect(relative_paths.map(&:to_s)).to match_array(expected[:arduino_library_src_dirs].map(&:to_s)) + end end end end From 9c19f86466624e54238390751fcf1dc6a96fc7bb Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sun, 4 Apr 2021 20:29:19 -0400 Subject: [PATCH 26/37] Fix typo in compiler flag generation --- CHANGELOG.md | 1 + spec/cpp_library_spec.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d1dfaca..804f59e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Properly report error in building shared library - A missing `examples` directory no longer causes a crash in `cpp_library.rb` - Referring to an undefined platform no longer causes a crash; it's now a helpful error message +- A copy/paste error that prevented compiler warning flags from being supplied has been fixed, via jgfoster ### Security diff --git a/spec/cpp_library_spec.rb b/spec/cpp_library_spec.rb index a0edc535..ef01a637 100644 --- a/spec/cpp_library_spec.rb +++ b/spec/cpp_library_spec.rb @@ -75,6 +75,32 @@ def verified_install(backend, path) RSpec.describe ArduinoCI::CppLibrary do next if skip_ruby_tests + context "compiler flags" do + config = ArduinoCI::CIConfig.new + config.load_yaml(File.join(File.dirname(__FILE__), "yaml", "o1.yaml")) + bogo_config = config.gcc_config("bogo") + fld = FakeLibDir.new + backend = fld.backend + cpp_lib_path = sampleproj_path + "DoSomething" + cpp_library = verified_install(backend, cpp_lib_path) + + # the keys are the methods of cpp_library to call + # the results are what we expect to see based on the config we loaded + methods_and_results = { + feature_args: ["-fa", "-fb"], + warning_args: ["-We", "-Wf"], + define_args: ["-Dc", "-Dd"], + flag_args: ["g", "h"] + } + + methods_and_results.each do |m, expected| + it "Creates #{m} from config" do + expect(expected).to eq(cpp_library.send(m, bogo_config)) + end + end + + end + context "arduino-library-specification detection" do answers = { From 3e8a6cb36511dd624ea268097902115a0bf3c419 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sun, 4 Apr 2021 21:01:56 -0400 Subject: [PATCH 27/37] allow flexibility in console width --- CHANGELOG.md | 1 + exe/arduino_ci.rb | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 804f59e1..f404db50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Run tests on push as well as on a pull request so developers can see impact - `ArduinoBackend` now exposes `config_file_path` instead of `config_dir` so that we can be explicit about [strange behavior in `arduino-cli` that isn't going to change anytime soon](https://github.com/arduino/arduino-cli/issues/753) - Use `arduino-cli` version `0.29.0` as the backend +- Test runner detects console width if possible, allowing variable width from 80-132 chars ### Deprecated diff --git a/exe/arduino_ci.rb b/exe/arduino_ci.rb index 4b9d4572..99d9ab06 100755 --- a/exe/arduino_ci.rb +++ b/exe/arduino_ci.rb @@ -3,8 +3,14 @@ require 'set' require 'pathname' require 'optparse' +require 'io/console' -WIDTH = 80 +# be flexible between 80 and 132 cols of output +WIDTH = begin + [132, [80, IO::console.winsize[1] - 2].max].min +rescue NoMethodError + 80 +end VAR_CUSTOM_INIT_SCRIPT = "CUSTOM_INIT_SCRIPT".freeze VAR_USE_SUBDIR = "USE_SUBDIR".freeze VAR_EXPECT_EXAMPLES = "EXPECT_EXAMPLES".freeze From b6e6d3ca19328ef2448cab61b1d80406e096d812 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sun, 4 Apr 2021 22:43:00 -0400 Subject: [PATCH 28/37] Fix compile errors reporting in rspec --- CHANGELOG.md | 1 + spec/testsomething_unittests_spec.rb | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f404db50..4fbf75d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - A missing `examples` directory no longer causes a crash in `cpp_library.rb` - Referring to an undefined platform no longer causes a crash; it's now a helpful error message - A copy/paste error that prevented compiler warning flags from being supplied has been fixed, via jgfoster +- RSpec was not communicating compile errors from unit test executables that failed to build. Now it does, via jgfoster ### Security diff --git a/spec/testsomething_unittests_spec.rb b/spec/testsomething_unittests_spec.rb index d7eae266..abc12160 100644 --- a/spec/testsomething_unittests_spec.rb +++ b/spec/testsomething_unittests_spec.rb @@ -89,11 +89,8 @@ end end - it "#{tfn} builds successfully" do + it "#{tfn} builds successfully and passes tests" do expect(@exe).not_to be nil - end - it "#{tfn} passes tests" do - skip "Can't run the test program because it failed to build" if @exe.nil? expect(@cpp_library.run_test_file(@exe)).to_not be_falsey end end From e76c4029757dc7dfe7329d876ddd3d5d25f93cb3 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sun, 4 Apr 2021 23:11:20 -0400 Subject: [PATCH 29/37] Avoid ingesting windows pathnames with backslashes -- convert to forward slashes --- CHANGELOG.md | 1 + lib/arduino_ci/arduino_backend.rb | 4 +++- lib/arduino_ci/arduino_downloader.rb | 2 +- lib/arduino_ci/arduino_downloader_linux.rb | 6 ------ lib/arduino_ci/arduino_downloader_osx.rb | 6 ------ lib/arduino_ci/arduino_downloader_windows.rb | 12 ++++++------ lib/arduino_ci/host.rb | 9 +++++---- spec/arduino_downloader_spec.rb | 1 - spec/host_spec.rb | 2 +- 9 files changed, 17 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fbf75d2..c218bd41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Referring to an undefined platform no longer causes a crash; it's now a helpful error message - A copy/paste error that prevented compiler warning flags from being supplied has been fixed, via jgfoster - RSpec was not communicating compile errors from unit test executables that failed to build. Now it does, via jgfoster +- Windows paths now avoid picking up backslashes, for proper equality comparisons ### Security diff --git a/lib/arduino_ci/arduino_backend.rb b/lib/arduino_ci/arduino_backend.rb index ed45f1cf..2827c446 100644 --- a/lib/arduino_ci/arduino_backend.rb +++ b/lib/arduino_ci/arduino_backend.rb @@ -138,7 +138,9 @@ def config_dump # @return [String] the path to the Arduino libraries directory def lib_dir - Pathname.new(config_dump["directories"]["user"]) + "libraries" + user_dir_raw = config_dump["directories"]["user"] + user_dir = OS.windows? ? Host.windows_to_pathname(user_dir_raw) : user_dir_raw + Pathname.new(user_dir) + "libraries" end # Board manager URLs diff --git a/lib/arduino_ci/arduino_downloader.rb b/lib/arduino_ci/arduino_downloader.rb index 0e8fdbdb..ad62f703 100644 --- a/lib/arduino_ci/arduino_downloader.rb +++ b/lib/arduino_ci/arduino_downloader.rb @@ -43,7 +43,7 @@ def self.autolocated_executable # The executable Arduino file in an existing installation, or nil # @return [Pathname] def self.existing_executable - self.must_implement(__method__) + Host.which("arduino-cli") end # The local file (dir) name of the desired IDE package (zip/tar/etc) diff --git a/lib/arduino_ci/arduino_downloader_linux.rb b/lib/arduino_ci/arduino_downloader_linux.rb index 7ca4c0ff..ddb49eee 100644 --- a/lib/arduino_ci/arduino_downloader_linux.rb +++ b/lib/arduino_ci/arduino_downloader_linux.rb @@ -17,12 +17,6 @@ def self.extracted_file "arduino-cli" end - # The executable Arduino file in an existing installation, or nil - # @return [string] - def self.existing_executable - Host.which("arduino-cli") - end - # Make any preparations or run any checks prior to making changes # @return [string] Error message, or nil if success def prepare diff --git a/lib/arduino_ci/arduino_downloader_osx.rb b/lib/arduino_ci/arduino_downloader_osx.rb index f4aad08a..f23bb8eb 100644 --- a/lib/arduino_ci/arduino_downloader_osx.rb +++ b/lib/arduino_ci/arduino_downloader_osx.rb @@ -17,12 +17,6 @@ def self.extracted_file "arduino-cli" end - # The executable Arduino file in an existing installation, or nil - # @return [string] - def self.existing_executable - Host.which("arduino-cli") - end - # Make any preparations or run any checks prior to making changes # @return [string] Error message, or nil if success def prepare diff --git a/lib/arduino_ci/arduino_downloader_windows.rb b/lib/arduino_ci/arduino_downloader_windows.rb index f3b3964b..2d3da667 100644 --- a/lib/arduino_ci/arduino_downloader_windows.rb +++ b/lib/arduino_ci/arduino_downloader_windows.rb @@ -28,12 +28,6 @@ def package_file "arduino-cli_#{@desired_version}_Windows_64bit.zip" end - # The executable Arduino file in an existing installation, or nil - # @return [string] - def self.existing_executable - Host.which("arduino-cli") - end - # The technology that will be used to extract the download # (for logging purposes) # @return [string] @@ -57,5 +51,11 @@ def self.extracted_file "arduino-cli.exe" end + # The executable Arduino file in a forced installation, or nil + # @return [Pathname] + def self.force_installed_executable + Pathname.new(Host.windows_to_pathname(ENV['HOME'])) + self.extracted_file + end + end end diff --git a/lib/arduino_ci/host.rb b/lib/arduino_ci/host.rb index dc8e2911..4cd5e243 100644 --- a/lib/arduino_ci/host.rb +++ b/lib/arduino_ci/host.rb @@ -17,13 +17,14 @@ class Host # via https://stackoverflow.com/a/5471032/2063546 # which('ruby') #=> /usr/bin/ruby # @param cmd [String] the command to search for - # @return [String] the full path to the command if it exists + # @return [Pathname] the full path to the command if it exists def self.which(cmd) exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] - ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| + ENV['PATH'].split(File::PATH_SEPARATOR).each do |string_path| + path = OS.windows? ? windows_to_pathname(string_path) : Pathname.new(string_path) exts.each do |ext| - exe = File.join(path, "#{cmd}#{ext}") - return exe if File.executable?(exe) && !File.directory?(exe) + exe = path.join("#{cmd}#{ext}") + return exe if exe.executable? && !exe.directory? end end nil diff --git a/spec/arduino_downloader_spec.rb b/spec/arduino_downloader_spec.rb index 6b1498b4..f711326b 100644 --- a/spec/arduino_downloader_spec.rb +++ b/spec/arduino_downloader_spec.rb @@ -7,7 +7,6 @@ it "has correct class properties" do ad = ArduinoCI::ArduinoDownloader - expect{ad.existing_executable}.to raise_error(NotImplementedError) expect{ad.extracted_file}.to raise_error(NotImplementedError) expect{ad.extractor}.to raise_error(NotImplementedError) expect{ad.extract("foo")}.to raise_error(NotImplementedError) diff --git a/spec/host_spec.rb b/spec/host_spec.rb index ac40f4e0..f9b94f4b 100644 --- a/spec/host_spec.rb +++ b/spec/host_spec.rb @@ -54,7 +54,7 @@ def with_tmpdir(path) it "can find things with which" do ruby_path = ArduinoCI::Host.which("ruby") expect(ruby_path).not_to be nil - expect(ruby_path.include? "ruby").to be true + expect(ruby_path.to_s.include? "ruby").to be true end end From afce5efcf860695bc5871796a3c1b8028a2c9500 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 22 Dec 2022 11:58:27 -0500 Subject: [PATCH 30/37] Clean up ruby style --- lib/arduino_ci/cpp_library.rb | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/arduino_ci/cpp_library.rb b/lib/arduino_ci/cpp_library.rb index 5ca7eb74..e1ed851b 100644 --- a/lib/arduino_ci/cpp_library.rb +++ b/lib/arduino_ci/cpp_library.rb @@ -3,6 +3,7 @@ require 'pathname' require 'shellwords' require 'os' +require 'fileutils' HPP_EXTENSIONS = [".hpp", ".hh", ".h", ".hxx", ".h++"].freeze CPP_EXTENSIONS = [".cpp", ".cc", ".c", ".cxx", ".c++"].freeze @@ -512,6 +513,15 @@ def build_for_test(test_file, gcc_binary) executable end + # Add build dir to path + def add_build_dirs_to_env + ENV["LD_LIBRARY_PATH"] = BUILD_DIR unless OS.windows? + paths = ENV["PATH"].split(File::PATH_SEPARATOR) + return if paths.include?(BUILD_DIR) + + ENV["PATH"] = BUILD_DIR + File::PATH_SEPARATOR + ENV["PATH"] if OS.windows? + end + # build a shared library to be used by each test # # The dependent libraries configuration is appended with data from library.properties internal to the library under test @@ -521,15 +531,9 @@ def build_for_test(test_file, gcc_binary) # @param ci_gcc_config [Hash] The GCC config object # @return [Pathname] path to the compiled test executable def build_shared_library(aux_libraries, gcc_binary, ci_gcc_config) - Dir.mkdir BUILD_DIR unless File.exist?(BUILD_DIR) - if OS.windows? - flag = ENV["PATH"].include? ";" - ENV["PATH"] = BUILD_DIR + (flag ? ";" : ":") + ENV["PATH"] unless ENV["PATH"].include? BUILD_DIR - suffix = "dll" - else - ENV["LD_LIBRARY_PATH"] = BUILD_DIR - suffix = "so" - end + FileUtils.mkdir_p(BUILD_DIR) + add_build_dirs_to_env + suffix = OS.windows? ? "dll" : "so" full_lib_name = "#{BUILD_DIR}/lib#{LIBRARY_NAME}.#{suffix}" executable = Pathname.new(full_lib_name).expand_path File.delete(executable) if File.exist?(executable) From cf0dbcdb9164cdb1975a332d936ffbbaa9797f9f Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 24 Dec 2022 00:19:38 -0500 Subject: [PATCH 31/37] Ensure that lib installation during tests only happens in pristine dir --- spec/cpp_library_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/cpp_library_spec.rb b/spec/cpp_library_spec.rb index ef01a637..06b067dd 100644 --- a/spec/cpp_library_spec.rb +++ b/spec/cpp_library_spec.rb @@ -82,7 +82,9 @@ def verified_install(backend, path) fld = FakeLibDir.new backend = fld.backend cpp_lib_path = sampleproj_path + "DoSomething" - cpp_library = verified_install(backend, cpp_lib_path) + + around(:example) { |example| fld.in_pristine_fake_libraries_dir(example) } + before(:each) { @cpp_library = verified_install(backend, cpp_lib_path) } # the keys are the methods of cpp_library to call # the results are what we expect to see based on the config we loaded @@ -95,7 +97,7 @@ def verified_install(backend, path) methods_and_results.each do |m, expected| it "Creates #{m} from config" do - expect(expected).to eq(cpp_library.send(m, bogo_config)) + expect(expected).to eq(@cpp_library.send(m, bogo_config)) end end From cd889d79f8ef934f3659ef6eb21605db1a208e1a Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 24 Dec 2022 00:20:08 -0500 Subject: [PATCH 32/37] Explicit tests for windows-posix path translation --- spec/host_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/host_spec.rb b/spec/host_spec.rb index f9b94f4b..b1883461 100644 --- a/spec/host_spec.rb +++ b/spec/host_spec.rb @@ -58,4 +58,17 @@ def with_tmpdir(path) end end + context "path mangling" do + win_path = "D:\\a\\_temp\\d20221224-4508-11w7f4\\foo.yml" + posix_pathname = Pathname.new("D:/a/_temp/d20221224-4508-11w7f4/foo.yml") + + it "converts windows paths to pathnames" do + expect(ArduinoCI::Host.pathname_to_windows(posix_pathname)).to eq(win_path) + end + + it "converts pathnames to windows paths" do + expect(ArduinoCI::Host.windows_to_pathname(win_path)).to eq(posix_pathname) + end + end + end From 4c6f8954f3055284017796470af917b84244a293 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 24 Dec 2022 01:34:50 -0500 Subject: [PATCH 33/37] Make config yaml path version-aware --- lib/arduino_ci/arduino_backend.rb | 32 +++++++++++++++++++++++-------- spec/arduino_installation_spec.rb | 9 ++++----- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/lib/arduino_ci/arduino_backend.rb b/lib/arduino_ci/arduino_backend.rb index 2827c446..96fca45b 100644 --- a/lib/arduino_ci/arduino_backend.rb +++ b/lib/arduino_ci/arduino_backend.rb @@ -48,6 +48,7 @@ def initialize(binary_path) @last_out = "" @last_err = "" @last_msg = "" + @config_dir_hack = false end def _wrap_run(work_fn, *args, **kwargs) @@ -55,7 +56,8 @@ def _wrap_run(work_fn, *args, **kwargs) has_env = !args.empty? && args[0].instance_of?(Hash) env_vars = has_env ? args[0] : {} actual_args = has_env ? args[1..-1] : args # need to shift over if we extracted args - custom_config = @config_dir.nil? ? [] : ["--config-file", config_file_cli_param.to_s] + custom_config = [] + custom_config += ["--config-file", config_file_cli_param.to_s] unless @config_dir_hack || @config_dir.nil? full_args = [binary_path.to_s, "--format", "json"] + custom_config + actual_args full_cmd = env_vars.empty? ? full_args : [env_vars] + full_args @@ -91,12 +93,11 @@ def config_file_path=(rhs) # The config file to be used as a CLI param # - # Apparently Linux wants the whole path, and OSX wants just the directory as of 0.29.0, - # it's all very annoying. See unit tests. + # This format changes based on version, which is very annoying. See unit tests. # # @return [Pathname] the path to use for a given OS def config_file_cli_param - OS.osx? ? @config_dir : config_file_path + should_use_config_dir? ? @config_dir : config_file_path end # Get an acceptable filename for use as a config file @@ -307,14 +308,29 @@ def last_bytes_usage Hash[mem_info.names.map(&:to_sym).zip(mem_info.captures.map(&:to_i))] end - private + # @return [String] the arduino-cli version that the backend is using, as String + def version_str + capture_json("version")[:json]["VersionString"] + end + + # @return [Gem::Version] the arduino-cli version that the backend is using, as a semver object + def version + Gem::Version.new(version_str) + end # Since the dry-run behavior became default in arduino-cli 0.14, the command line flag was removed # @return [Bool] whether the --dry-run flag is available for this arduino-cli version def should_use_dry_run? - ret = capture_json("version") - version = ret[:json]["VersionString"] - Gem::Version.new(version) < Gem::Version.new('0.14') + version < Gem::Version.new('0.14') + end + + # Since the config dir behavior has changed from a directory to a file (At some point??) + # @return [Bool] whether to specify configuration by directory or filename + def should_use_config_dir? + @config_dir_hack = true # prevent an infinite loop when trying to run the command + version < Gem::Version.new('0.14') + ensure + @config_dir_hack = false end end end diff --git a/spec/arduino_installation_spec.rb b/spec/arduino_installation_spec.rb index b66937fb..097016c3 100644 --- a/spec/arduino_installation_spec.rb +++ b/spec/arduino_installation_spec.rb @@ -25,9 +25,8 @@ def with_tmp_file(desired_filename = nil) end def config_success_msg(config_file) - config_file_str = config_file.to_s - config_file_str = config_file_str.gsub('/', '\\') if OS.windows? - "Using config file: #{config_file}" + config_file_str = OS.windows? ? ArduinoCI::Host.pathname_to_windows(config_file) : config_file + "Using config file: #{config_file_str}" end def config_fail_msg @@ -80,7 +79,7 @@ def config_fail_msg expect(config_dir).to exist expect(config_file).to exist ret = ArduinoCI::Host.run_and_capture(*bug_753_cmd(backend, config_file)) - if OS.osx? + if backend.should_use_config_dir? expect(ret[:out].lines[0]).to include(config_fail_msg) else expect(ret[:out].lines[0]).to include(config_success_msg(config_file)) @@ -104,7 +103,7 @@ def config_fail_msg expect(config_dir).to exist expect(config_file).to exist ret = ArduinoCI::Host.run_and_capture(*bug_753_cmd(backend, config_dir)) - if OS.osx? + if backend.should_use_config_dir? expect(ret[:out].lines[0]).to include(config_success_msg(config_file)) else expect(ret[:out].lines[0]).to include(config_fail_msg) From 8419a6c26588082529495764c7cc8b0e9342041a Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 24 Dec 2022 01:35:05 -0500 Subject: [PATCH 34/37] remove unnecessary cleanup --- spec/fake_lib_dir.rb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/spec/fake_lib_dir.rb b/spec/fake_lib_dir.rb index 08702363..422ebfab 100644 --- a/spec/fake_lib_dir.rb +++ b/spec/fake_lib_dir.rb @@ -46,18 +46,6 @@ def in_pristine_fake_libraries_dir(example) # cool, already done end end - ensure - # the tmp dir will be cleaned up automatically, but if we did our own symlink hack then here is the place to clean it up - if ArduinoCI::Host.needs_symlink_hack? - stdout, stderr, exitstatus = Open3.capture3('cmd.exe', "/c rmdir /s /q #{ArduinoCI::Host.pathname_to_windows(d)}") - unless exitstatus.success? - puts "====== rmdir of #{d} failed" - puts stdout - puts stderr - end - end end end - - end From 788ca9b66091989751a34f37987e7d9cdba80c51 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 24 Dec 2022 01:36:46 -0500 Subject: [PATCH 35/37] Consider libraries installed if their path is a symlink --- CHANGELOG.md | 1 + lib/arduino_ci/cpp_library.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c218bd41..dd4509b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - A copy/paste error that prevented compiler warning flags from being supplied has been fixed, via jgfoster - RSpec was not communicating compile errors from unit test executables that failed to build. Now it does, via jgfoster - Windows paths now avoid picking up backslashes, for proper equality comparisons +- Libraries are now considered installed if their entry is a symlink (for which `exist?` would return `false`) ### Security diff --git a/lib/arduino_ci/cpp_library.rb b/lib/arduino_ci/cpp_library.rb index e1ed851b..8ee111ea 100644 --- a/lib/arduino_ci/cpp_library.rb +++ b/lib/arduino_ci/cpp_library.rb @@ -98,7 +98,7 @@ def examples_dir # # @return [bool] def installed? - path.exist? + path.exist? || path.symlink? end # install a library by name From 8b36a14d9409337e5646d0bbfc27ebc132888dd5 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 24 Dec 2022 01:48:25 -0500 Subject: [PATCH 36/37] Add OS names to tests, stop printing CI backtrace --- .github/workflows/linux.yaml | 4 ++-- .github/workflows/macos.yaml | 4 ++-- .github/workflows/windows.yaml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/linux.yaml b/.github/workflows/linux.yaml index d088b02a..1b29e833 100644 --- a/.github/workflows/linux.yaml +++ b/.github/workflows/linux.yaml @@ -18,7 +18,7 @@ jobs: bundle exec rubocop --version bundle exec rubocop -D . - "rspec": + "rspec-linux": runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -29,7 +29,7 @@ jobs: run: | g++ -v bundle install - bundle exec rspec --backtrace + bundle exec rspec "TestSomething": runs-on: ubuntu-latest diff --git a/.github/workflows/macos.yaml b/.github/workflows/macos.yaml index 869daa91..6d8eaa2c 100644 --- a/.github/workflows/macos.yaml +++ b/.github/workflows/macos.yaml @@ -18,7 +18,7 @@ jobs: bundle exec rubocop --version bundle exec rubocop -D . - "rspec": + "rspec-macos": runs-on: macos-latest steps: - uses: actions/checkout@v2 @@ -29,7 +29,7 @@ jobs: run: | g++ -v bundle install - bundle exec rspec --backtrace + bundle exec rspec "TestSomething": runs-on: macos-latest diff --git a/.github/workflows/windows.yaml b/.github/workflows/windows.yaml index 1decd120..3ebf158b 100644 --- a/.github/workflows/windows.yaml +++ b/.github/workflows/windows.yaml @@ -18,7 +18,7 @@ jobs: bundle exec rubocop --version bundle exec rubocop -D . --except Layout/EndOfLine - "rspec": + "rspec-windows": runs-on: windows-latest steps: - uses: actions/checkout@v2 @@ -29,7 +29,7 @@ jobs: run: | g++ -v bundle install - bundle exec rspec --backtrace + bundle exec rspec TestSomething: runs-on: windows-latest From ba0e4e2b850d4a28e3764c62e802e1cc17a60d58 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Sat, 24 Dec 2022 22:12:24 -0500 Subject: [PATCH 37/37] Pin the spelling action version, to match what's enabled in project settings This matches https://github.com/Arduino-CI/arduino_ci/settings/actions --- .github/workflows/spelling.yaml | 2 +- exe/arduino_ci.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/spelling.yaml b/.github/workflows/spelling.yaml index ad182e4d..3450600c 100644 --- a/.github/workflows/spelling.yaml +++ b/.github/workflows/spelling.yaml @@ -21,6 +21,6 @@ jobs: fetch-depth: 0 - name: Check Spelling - uses: codespell-project/actions-codespell@master + uses: codespell-project/actions-codespell@v1.0 with: check_filenames: true diff --git a/exe/arduino_ci.rb b/exe/arduino_ci.rb index 99d9ab06..c73522dd 100755 --- a/exe/arduino_ci.rb +++ b/exe/arduino_ci.rb @@ -100,7 +100,7 @@ def terminate(final = nil) end # make a nice status line for an action and react to the action -# TODO / note to self: inform_multline is tougher to write +# TODO / note to self: inform_multiline is tougher to write # without altering the signature because it only leaves space # for the checkmark _after_ the multiline, it doesn't know how # to make that conditionally the body @@ -121,7 +121,7 @@ def perform_action(message, multiline, mark_fn, on_fail_msg, tally_on_fail, abor $stdout.flush result = yield mark = mark_fn.nil? ? "" : mark_fn.call(result) - # if multline, put checkmark at full width + # if multiline, put checkmark at full width print endline if multiline puts mark.to_s.rjust(WIDTH - line.length, " ") unless result