From 3cf5dfc496b006e0d38be36883abcb098b65c2e1 Mon Sep 17 00:00:00 2001 From: Dolf Andringa Date: Sat, 27 Jul 2019 16:19:32 +0800 Subject: [PATCH 1/3] #29 include ability to exclude certain directories from compilation --- REFERENCE.md | 8 +++++ SampleProjects/TestSomething/.arduino-ci.yml | 2 ++ .../excludeThis/exclude-this.cpp | 10 +++++++ .../TestSomething/excludeThis/exclude-this.h | 4 +++ exe/arduino_ci_remote.rb | 2 +- lib/arduino_ci/ci_config.rb | 9 ++++++ lib/arduino_ci/cpp_library.rb | 29 +++++++++++++++++-- spec/testsomething_unittests_spec.rb | 4 ++- 8 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 SampleProjects/TestSomething/excludeThis/exclude-this.cpp create mode 100644 SampleProjects/TestSomething/excludeThis/exclude-this.h diff --git a/REFERENCE.md b/REFERENCE.md index ab202fdf..6ddad2d4 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -112,8 +112,16 @@ For your unit tests, in addition to setting specific libraries and platforms, yo Filtering your unit tests may help speed up targeted testing locally, but it is intended primarily as a means to temporarily disable tests between individual commits. +Furthermore, you can filter the files that will be included in the compilation step by specifying `exclude_dirs`. All cpp and header files in those directories will not be included in the compilation step, before the unittests are run. + ```yaml unittest: + + # Exclude these directories from compilation + exclude_dirs: + - someDirectory + - someOtherDirectory + # Perform unit tests with these compilers (these are the binaries that will be called via the shell) compilers: - g++ # default diff --git a/SampleProjects/TestSomething/.arduino-ci.yml b/SampleProjects/TestSomething/.arduino-ci.yml index f9890177..c418bdbe 100644 --- a/SampleProjects/TestSomething/.arduino-ci.yml +++ b/SampleProjects/TestSomething/.arduino-ci.yml @@ -1,4 +1,6 @@ unittest: + exclude_dirs: + - excludeThis platforms: - uno - due diff --git a/SampleProjects/TestSomething/excludeThis/exclude-this.cpp b/SampleProjects/TestSomething/excludeThis/exclude-this.cpp new file mode 100644 index 00000000..639719a7 --- /dev/null +++ b/SampleProjects/TestSomething/excludeThis/exclude-this.cpp @@ -0,0 +1,10 @@ +#include "test-something.h" +int testSomething(void) { + millis(); // this line is only here to test that we're able to refer to the builtins + return 4; +}; + +int* aNullPointer(void) { + int* ret = nullptr; + return ret; +} diff --git a/SampleProjects/TestSomething/excludeThis/exclude-this.h b/SampleProjects/TestSomething/excludeThis/exclude-this.h new file mode 100644 index 00000000..13831ea1 --- /dev/null +++ b/SampleProjects/TestSomething/excludeThis/exclude-this.h @@ -0,0 +1,4 @@ +#pragma once +#include +int testSomething(void); +int *aNullPointer(void); diff --git a/exe/arduino_ci_remote.rb b/exe/arduino_ci_remote.rb index 6ce5da50..fc2f8998 100755 --- a/exe/arduino_ci_remote.rb +++ b/exe/arduino_ci_remote.rb @@ -183,7 +183,7 @@ def perform_unit_tests(file_config) return end config = file_config.with_override_config(@cli_options[:ci_config]) - cpp_library = ArduinoCI::CppLibrary.new(Pathname.new("."), @arduino_cmd.lib_dir) + cpp_library = ArduinoCI::CppLibrary.new(Pathname.new("."), @arduino_cmd.lib_dir, config.exclude_dirs) # check GCC compilers = config.compilers_to_use diff --git a/lib/arduino_ci/ci_config.rb b/lib/arduino_ci/ci_config.rb index c3fa47e9..5f77b7bd 100644 --- a/lib/arduino_ci/ci_config.rb +++ b/lib/arduino_ci/ci_config.rb @@ -28,6 +28,7 @@ compilers: Array, platforms: Array, libraries: Array, + exclude_dirs: Array, testfiles: { select: Array, reject: Array, @@ -256,6 +257,14 @@ def compilers_to_use @unittest_info[:compilers] end + # paths to exclude all files in for building and unitttests + # @return [Array] The directories (relative to base dir) to exclude + def exclude_dirs + return [] if @unittest_info[:exclude_dirs].nil? + + @unittest_info[:exclude_dirs] + end + # platforms to build [the examples on] # @return [Array] The platforms to build def platforms_to_build diff --git a/lib/arduino_ci/cpp_library.rb b/lib/arduino_ci/cpp_library.rb index f241feb4..73974edc 100644 --- a/lib/arduino_ci/cpp_library.rb +++ b/lib/arduino_ci/cpp_library.rb @@ -37,11 +37,14 @@ class CppLibrary # @param base_dir [Pathname] The path to the library being tested # @param arduino_lib_dir [Pathname] The path to the libraries directory - def initialize(base_dir, arduino_lib_dir) + def initialize(base_dir, arduino_lib_dir, exclude_dirs = []) raise ArgumentError, 'base_dir is not a Pathname' unless base_dir.is_a? Pathname raise ArgumentError, 'arduino_lib_dir is not a Pathname' unless arduino_lib_dir.is_a? Pathname + raise ArgumentError, 'exclude_dir is not an array of Pathnames' unless exclude_dirs.is_a?(Array) && + exclude_dirs.each { |p| p.is_a? Pathname } @base_dir = base_dir + @exclude_dirs = exclude_dirs @arduino_lib_dir = arduino_lib_dir.expand_path @artifacts = [] @last_err = "" @@ -115,6 +118,19 @@ def in_tests_dir?(path) false end + # Guess whether a file is part of any @excludes_dir dir (indicating library compilation should ignore it). + # + # @param path [Pathname] The path to check + # @return [bool] + def in_exclude_dir?(path) + # we could do this but some rubies don't return an enumerator for ascend + # path.ascend.any? { |part| tests_dir_aliases.include?(part) } + path.ascend do |part| + return true if exclude_dir.any? { |p| p.realpath == part } + end + false + end + # Check whether libasan (and by extension -fsanitizer=address) is supported # # This requires compilation of a sample program, and will be cached @@ -150,7 +166,7 @@ def cpp_files_in(some_dir) # CPP files that are part of the project library under test # @return [Array] def cpp_files - cpp_files_in(@base_dir).reject { |p| vendor_bundle?(p) || in_tests_dir?(p) } + cpp_files_in(@base_dir).reject { |p| vendor_bundle?(p) || in_tests_dir?(p) || in_exclude_dir?(p) } end # CPP files that are part of the arduino mock library we're providing @@ -172,6 +188,12 @@ def cpp_files_libraries(aux_libraries) arduino_library_src_dirs(aux_libraries).map { |d| cpp_files_in(d) }.flatten.uniq end + # Returns the Pathnames for all paths to exclude from testing and compilation + # @return [Array] + def exclude_dir + @exclude_dirs.map { |p| Pathname.new(@base_dir) + p }.select(&:exist?) + end + # The directory where we expect to find unit test defintions provided by the user # @return [Pathname] def tests_dir @@ -190,7 +212,8 @@ def header_dirs real = @base_dir.realpath all_files = Find.find(real).map { |f| Pathname.new(f) }.reject(&:directory?) unbundled = all_files.reject { |path| vendor_bundle?(path) } - files = unbundled.select { |path| HPP_EXTENSIONS.include?(path.extname.downcase) } + unexcluded = unbundled.reject { |path| in_exclude_dir?(path) } + files = unexcluded.select { |path| HPP_EXTENSIONS.include?(path.extname.downcase) } files.map(&:dirname).uniq end diff --git a/spec/testsomething_unittests_spec.rb b/spec/testsomething_unittests_spec.rb index 97d55694..d4e1ed33 100644 --- a/spec/testsomething_unittests_spec.rb +++ b/spec/testsomething_unittests_spec.rb @@ -13,7 +13,9 @@ def get_relative_dir(sampleprojects_tests_dir) RSpec.describe "TestSomething C++" do next if skip_cpp_tests cpp_lib_path = sampleproj_path + "TestSomething" - cpp_library = ArduinoCI::CppLibrary.new(cpp_lib_path, Pathname.new("my_fake_arduino_lib_dir")) + cpp_library = ArduinoCI::CppLibrary.new(cpp_lib_path, + Pathname.new("my_fake_arduino_lib_dir"), + ["excludeThis"]) context "cpp_files" do it "finds cpp files in directory" do testsomething_cpp_files = [Pathname.new("TestSomething/test-something.cpp")] From f623be411c8c4de4c5f27c768e1411e255cb9a55 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 19 Aug 2019 07:26:14 -0400 Subject: [PATCH 2/3] Fix bugs and other issues in contributed code --- CHANGELOG.md | 1 + .../excludeThis/exclude-this.cpp | 14 +++----- .../TestSomething/excludeThis/exclude-this.h | 10 +++--- exe/arduino_ci_remote.rb | 4 ++- lib/arduino_ci/cpp_library.rb | 7 ++-- spec/ci_config_spec.rb | 3 +- spec/cpp_library_spec.rb | 2 +- spec/testsomething_unittests_spec.rb | 34 ++++++++++++++++++- 8 files changed, 54 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 232856ff..e154ce5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added +- Unit testing configuration now allows `exclude_dirs` to be set, which prevents stray source files from as part of unit testing allows ### Changed diff --git a/SampleProjects/TestSomething/excludeThis/exclude-this.cpp b/SampleProjects/TestSomething/excludeThis/exclude-this.cpp index 639719a7..11b7551e 100644 --- a/SampleProjects/TestSomething/excludeThis/exclude-this.cpp +++ b/SampleProjects/TestSomething/excludeThis/exclude-this.cpp @@ -1,10 +1,6 @@ -#include "test-something.h" -int testSomething(void) { - millis(); // this line is only here to test that we're able to refer to the builtins - return 4; -}; +This file intentionally contains syntactically incorrect code +to break unit test compilation. If arduino_ci is working +properly, it should exclude this file (as per .arduino-ci.yml +configuration) and unit test compilation should succeed. -int* aNullPointer(void) { - int* ret = nullptr; - return ret; -} +~!@#$%^&*() diff --git a/SampleProjects/TestSomething/excludeThis/exclude-this.h b/SampleProjects/TestSomething/excludeThis/exclude-this.h index 13831ea1..11b7551e 100644 --- a/SampleProjects/TestSomething/excludeThis/exclude-this.h +++ b/SampleProjects/TestSomething/excludeThis/exclude-this.h @@ -1,4 +1,6 @@ -#pragma once -#include -int testSomething(void); -int *aNullPointer(void); +This file intentionally contains syntactically incorrect code +to break unit test compilation. If arduino_ci is working +properly, it should exclude this file (as per .arduino-ci.yml +configuration) and unit test compilation should succeed. + +~!@#$%^&*() diff --git a/exe/arduino_ci_remote.rb b/exe/arduino_ci_remote.rb index fc2f8998..12ab224e 100755 --- a/exe/arduino_ci_remote.rb +++ b/exe/arduino_ci_remote.rb @@ -183,7 +183,9 @@ def perform_unit_tests(file_config) return end config = file_config.with_override_config(@cli_options[:ci_config]) - cpp_library = ArduinoCI::CppLibrary.new(Pathname.new("."), @arduino_cmd.lib_dir, config.exclude_dirs) + cpp_library = ArduinoCI::CppLibrary.new(Pathname.new("."), + @arduino_cmd.lib_dir, + config.exclude_dirs.map(&Pathname.method(:new))) # check GCC compilers = config.compilers_to_use diff --git a/lib/arduino_ci/cpp_library.rb b/lib/arduino_ci/cpp_library.rb index 73974edc..78b98f86 100644 --- a/lib/arduino_ci/cpp_library.rb +++ b/lib/arduino_ci/cpp_library.rb @@ -37,11 +37,12 @@ class CppLibrary # @param base_dir [Pathname] The path to the library being tested # @param arduino_lib_dir [Pathname] The path to the libraries directory - def initialize(base_dir, arduino_lib_dir, exclude_dirs = []) + # @param exclude_dirs [Array] Directories that should be excluded from compilation + def initialize(base_dir, arduino_lib_dir, exclude_dirs) raise ArgumentError, 'base_dir is not a Pathname' unless base_dir.is_a? Pathname raise ArgumentError, 'arduino_lib_dir is not a Pathname' unless arduino_lib_dir.is_a? Pathname - raise ArgumentError, 'exclude_dir is not an array of Pathnames' unless exclude_dirs.is_a?(Array) && - exclude_dirs.each { |p| p.is_a? Pathname } + raise ArgumentError, 'exclude_dir is not an array of Pathnames' unless exclude_dirs.is_a?(Array) + raise ArgumentError, 'exclude_dir array contains non-Pathname elements' unless exclude_dirs.all? { |p| p.is_a? Pathname } @base_dir = base_dir @exclude_dirs = exclude_dirs diff --git a/spec/ci_config_spec.rb b/spec/ci_config_spec.rb index bec81f02..23d01e40 100644 --- a/spec/ci_config_spec.rb +++ b/spec/ci_config_spec.rb @@ -157,7 +157,7 @@ context "allowable_unittest_files" do cpp_lib_path = Pathname.new(__dir__) + "fake_library" - cpp_library = ArduinoCI::CppLibrary.new(cpp_lib_path, Pathname.new("my_fake_arduino_lib_dir")) + cpp_library = ArduinoCI::CppLibrary.new(cpp_lib_path, Pathname.new("my_fake_arduino_lib_dir"), []) it "starts with a known set of files" do expect(cpp_library.test_files.map { |f| File.basename(f) }).to match_array([ @@ -177,4 +177,3 @@ end end - diff --git a/spec/cpp_library_spec.rb b/spec/cpp_library_spec.rb index 8044486a..40b2407a 100644 --- a/spec/cpp_library_spec.rb +++ b/spec/cpp_library_spec.rb @@ -13,7 +13,7 @@ def get_relative_dir(sampleprojects_tests_dir) RSpec.describe ArduinoCI::CppLibrary do next if skip_ruby_tests cpp_lib_path = sampleproj_path + "DoSomething" - cpp_library = ArduinoCI::CppLibrary.new(cpp_lib_path, Pathname.new("my_fake_arduino_lib_dir")) + cpp_library = ArduinoCI::CppLibrary.new(cpp_lib_path, Pathname.new("my_fake_arduino_lib_dir"), []) context "cpp_files" do it "finds cpp files in directory" do dosomething_cpp_files = [Pathname.new("DoSomething") + "do-something.cpp"] diff --git a/spec/testsomething_unittests_spec.rb b/spec/testsomething_unittests_spec.rb index d4e1ed33..5e0f9152 100644 --- a/spec/testsomething_unittests_spec.rb +++ b/spec/testsomething_unittests_spec.rb @@ -10,12 +10,44 @@ def get_relative_dir(sampleprojects_tests_dir) sampleprojects_tests_dir.relative_path_from(base_dir) end +RSpec.describe "TestSomething C++ without excludes" do + next if skip_cpp_tests + cpp_lib_path = sampleproj_path + "TestSomething" + cpp_library = ArduinoCI::CppLibrary.new(cpp_lib_path, + Pathname.new("my_fake_arduino_lib_dir"), + []) + context "cpp_files" do + it "finds cpp files in directory" do + testsomething_cpp_files = [ + Pathname.new("TestSomething/test-something.cpp"), + Pathname.new("TestSomething/excludeThis/exclude-this.cpp") + ] + relative_paths = cpp_library.cpp_files.map { |f| get_relative_dir(f) } + expect(relative_paths).to match_array(testsomething_cpp_files) + end + end + + context "unit tests" do + it "can't build due to files that should have been excluded" do + config = ArduinoCI::CIConfig.default.from_example(cpp_lib_path) + path = config.allowable_unittest_files(cpp_library.test_files).first + compiler = config.compilers_to_use.first + result = cpp_library.build_for_test_with_configuration(path, + [], + compiler, + config.gcc_config("uno")) + expect(result).to be nil + end + end + +end + RSpec.describe "TestSomething C++" do next if skip_cpp_tests cpp_lib_path = sampleproj_path + "TestSomething" cpp_library = ArduinoCI::CppLibrary.new(cpp_lib_path, Pathname.new("my_fake_arduino_lib_dir"), - ["excludeThis"]) + ["excludeThis"].map(&Pathname.method(:new))) context "cpp_files" do it "finds cpp files in directory" do testsomething_cpp_files = [Pathname.new("TestSomething/test-something.cpp")] From c875a2a57977d034aba0ab14365fbd961c8b1879 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 19 Aug 2019 12:47:39 -0400 Subject: [PATCH 3/3] allow bundler 2.0 to make travis pass --- arduino_ci.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arduino_ci.gemspec b/arduino_ci.gemspec index 9b13c280..75ccb912 100644 --- a/arduino_ci.gemspec +++ b/arduino_ci.gemspec @@ -28,7 +28,7 @@ Gem::Specification.new do |spec| spec.add_dependency "os", "~> 1.0" spec.add_dependency "rubyzip", "~> 1.2" - spec.add_development_dependency "bundler", "~> 1.15" + spec.add_development_dependency "bundler", "> 1.15" spec.add_development_dependency "keepachangelog_manager", "~> 0.0.2" spec.add_development_dependency "rspec", "~> 3.0" spec.add_development_dependency 'rubocop', '~>0.59.0'