Skip to content

Commit e6c19f9

Browse files
authored
Merge pull request #84 from ianfixes/2019-01-14_command_line_filtering
Command-line test selection/rejection
2 parents d3df720 + 4fbb472 commit e6c19f9

File tree

9 files changed

+199
-9
lines changed

9 files changed

+199
-9
lines changed

.github/issue_template.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## System
22

3-
> Feel free to delete this section if you're submitting a feature request
3+
<!-- Feel free to delete this section if you're submitting a feature request -->
44

55
- OS: _(Travis/OSX/Linux/Windows)_
66
- `ruby -v`:

.github/pull_request_template.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
* See CHANGELOG.md for more
44

5-
5+
<!--
66
## Issues Fixed
77
88
* Fixes #3000
9+
-->

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77
## [Unreleased]
88
### Added
99
- Provide an `itoa` function. It is present in Arduino's runtime environment but not on most (all?) host systems because itoa is not a portable standard function.
10+
- `to_h` and `to_s` functions for `ci_config.rb`
11+
- `CIConfig::clone`
12+
- Ability to override `CIConfig` from a hash instead of just a file
13+
- `arduino_ci_remote.rb` now supports command line switches `--testfile-select=GLOB` and `--testfile-reject=GLOB` (which can both be repeated)
1014

1115
### Changed
1216
- Simplified the use of `Array.each` with a return statement; it's now simply `Array.find`
@@ -19,6 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1923

2024
### Fixed
2125
- Determining a working OSX launch command no longer breaks on non-English installations
26+
- `arduino_ci_remote.rb` now honors selected and rejected test files
2227

2328
### Security
2429

README.md

+19
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,25 @@ source 'https://rubygems.org'
3434
gem 'arduino_ci'
3535
```
3636

37+
### Testing Locally
38+
39+
First, pull in the `arduino_ci` library as a dependency.
40+
41+
```
42+
$ bundle install
43+
```
44+
45+
46+
With that installed, just the following shell command each time you want the tests to execute:
47+
48+
```
49+
$ bundle exec arduino_ci_remote.rb
50+
```
51+
52+
53+
54+
### Testing with remote CI
55+
3756
> **Note:** `arduino_ci_remote.rb` expects to be run from the root directory of your Arduino project library.
3857
3958

REFERENCE.md

+22
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,28 @@ These defaults are specified in [misc/default.yml](misc/default.yml). You are f
99

1010
## Overriding default build behavior
1111

12+
### From the command line
13+
14+
The following options are currently available in the `arduino_ci_remote.rb` test runner.
15+
16+
```
17+
Usage: arduino_ci_remote.rb [options]
18+
--testfile-select=GLOB Unit test file (or glob) to select
19+
--testfile-reject=GLOB Unit test file (or glob) to reject
20+
-h, --help Prints this help
21+
```
22+
23+
#### `--testfile-select` option
24+
25+
This allows a file (or glob) pattern to be executed in your tests directory, creating a whitelist of files to test. E.g. `--testfile-select=test_animal_*.cpp` would match `test_animal_cat.cpp` and `test_animal_dog.cpp` (testing only those) and not `test_plant_rose.cpp`.
26+
27+
#### `--testfile-reject` option
28+
29+
This allows a file (or glob) pattern to be executed in your tests directory, creating a blacklist of files to skip. E.g. `--testfile-reject=test_animal_*.cpp` would match `test_animal_cat.cpp` and `test_animal_dog.cpp` (skipping those) and test only `test_plant_rose.cpp`, `test_plant_daisy.cpp`, etc.
30+
31+
32+
### From configuration
33+
1234
`.arduino-ci.yml` files will override the default behavior. There are 3 places you can put them:
1335

1436
1. the root of your library

exe/arduino_ci_remote.rb

+47-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,49 @@
22
require 'arduino_ci'
33
require 'set'
44
require 'pathname'
5+
require 'optparse'
56

67
WIDTH = 80
78
FIND_FILES_INDENT = 4
89

910
@failure_count = 0
1011
@passfail = proc { |result| result ? "✓" : "✗" }
1112

13+
# Use some basic parsing to allow command-line overrides of config
14+
class Parser
15+
def self.parse(options)
16+
parsed_config = {}
17+
parsed_config["unittest"] = {}
18+
19+
opt_parser = OptionParser.new do |opts|
20+
opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
21+
22+
opts.on("--testfile-select=GLOB", "Unit test file (or glob) to select") do |p|
23+
parsed_config["unittest"]["testfiles"] ||= {}
24+
parsed_config["unittest"]["testfiles"]["select"] ||= []
25+
parsed_config["unittest"]["testfiles"]["select"] << p
26+
end
27+
28+
opts.on("--testfile-reject=GLOB", "Unit test file (or glob) to reject") do |p|
29+
parsed_config["unittest"]["testfiles"] ||= {}
30+
parsed_config["unittest"]["testfiles"]["reject"] ||= []
31+
parsed_config["unittest"]["testfiles"]["reject"] << p
32+
end
33+
34+
opts.on("-h", "--help", "Prints this help") do
35+
puts opts
36+
exit
37+
end
38+
end
39+
40+
opt_parser.parse!(options)
41+
parsed_config
42+
end
43+
end
44+
45+
# Read in command line options and make them read-only
46+
@cli_options = (Parser.parse ARGV).freeze
47+
1248
# terminate after printing any debug info. TODO: capture debug info
1349
def terminate(final = nil)
1450
puts "Failures: #{@failure_count}"
@@ -28,6 +64,12 @@ def terminate(final = nil)
2864
# without altering the signature because it only leaves space
2965
# for the checkmark _after_ the multiline, it doesn't know how
3066
# to make that conditionally the body
67+
# @param message String the text of the progress indicator
68+
# @param multiline boolean whether multiline output is expected
69+
# @param mark_fn block (string) -> string that says how to describe the result
70+
# @param on_fail_msg String custom message for failure
71+
# @param tally_on_fail boolean whether to increment @failure_count
72+
# @param abort_on_fail boolean whether to abort immediately on failure (i.e. if this is a fatal error)
3173
def perform_action(message, multiline, mark_fn, on_fail_msg, tally_on_fail, abort_on_fail)
3274
line = "#{message}... "
3375
endline = "...#{message} "
@@ -111,7 +153,10 @@ def display_files(pathname)
111153
non_hidden.each { |p| puts "#{margin}#{p}" }
112154
end
113155

114-
def perform_unit_tests(config)
156+
def perform_unit_tests(file_config)
157+
puts file_config.to_h[:unittest].to_s
158+
config = file_config.with_override_config(@cli_options)
159+
puts config.to_h[:unittest].to_s
115160
cpp_library = ArduinoCI::CppLibrary.new(Pathname.new("."), @arduino_cmd.lib_dir)
116161

117162
# check GCC
@@ -149,7 +194,7 @@ def perform_unit_tests(config)
149194
inform("Skipping unit tests") { "no platforms were requested" }
150195
else
151196
config.platforms_to_unittest.each do |p|
152-
cpp_library.test_files.each do |unittest_path|
197+
config.allowable_unittest_files(cpp_library.test_files).each do |unittest_path|
153198
unittest_name = unittest_path.basename.to_s
154199
compilers.each do |gcc_binary|
155200
attempt_multiline("Unit testing #{unittest_name} with #{gcc_binary}") do

lib/arduino_ci/ci_config.rb

+43-5
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,21 @@ def initialize
7272
@unittest_info = {}
7373
end
7474

75+
# @return [Hash] config data as a hash
76+
def to_h
77+
{
78+
packages: @package_info,
79+
platforms: @platform_info,
80+
compile: @compile_info,
81+
unittest: @unittest_info
82+
}
83+
end
84+
85+
# @return [String] config data as a string
86+
def to_s
87+
to_h.to_s
88+
end
89+
7590
# Deep-clone a hash
7691
# @param hash [Hash] the source data
7792
# @return [Hash] a copy
@@ -112,6 +127,13 @@ def load_yaml(path)
112127
yml = YAML.load_file(path)
113128
raise ConfigurationError, "The YAML file at #{path} failed to load" unless yml
114129

130+
apply_configuration(yml)
131+
end
132+
133+
# Load configuration from a hash
134+
# @param yml [Hash] the source data
135+
# @return [ArduinoCI::CIConfig] a reference to self
136+
def apply_configuration(yml)
115137
if yml.include?("packages")
116138
yml["packages"].each do |k, v|
117139
valid_data = validate_data("packages", v, PACKAGE_SCHEMA)
@@ -139,19 +161,35 @@ def load_yaml(path)
139161
self
140162
end
141163

164+
# Create a clone of this configuration and return it
165+
# @return [ArduinoCI::CIConfig] the new settings object
166+
def clone
167+
cloned_config = self.class.new
168+
cloned_config.package_info = deep_clone(@package_info)
169+
cloned_config.platform_info = deep_clone(@platform_info)
170+
cloned_config.compile_info = deep_clone(@compile_info)
171+
cloned_config.unittest_info = deep_clone(@unittest_info)
172+
cloned_config
173+
end
174+
142175
# Override these settings with settings from another file
143176
# @param path [String] the path to the settings yaml file
144177
# @return [ArduinoCI::CIConfig] the new settings object
145178
def with_override(path)
146-
overridden_config = self.class.new
147-
overridden_config.package_info = deep_clone(@package_info)
148-
overridden_config.platform_info = deep_clone(@platform_info)
149-
overridden_config.compile_info = deep_clone(@compile_info)
150-
overridden_config.unittest_info = deep_clone(@unittest_info)
179+
overridden_config = clone
151180
overridden_config.load_yaml(path)
152181
overridden_config
153182
end
154183

184+
# Override these settings with settings from a hash
185+
# @param config_hash [Hash] A configuration hash
186+
# @return [ArduinoCI::CIConfig] the new settings object
187+
def with_override_config(config_hash)
188+
overridden_config = clone
189+
overridden_config.apply_configuration(config_hash)
190+
overridden_config
191+
end
192+
155193
# Get the config file at a given path, if it exists, and pass that to a block.
156194
# Many config files may exist, but only the first match is used
157195
# @param base_dir [String] The directory in which to search for a config file

spec/ci_config_spec.rb

+40
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,46 @@
3939
end
4040
end
4141

42+
context "clone" do
43+
it "creates a copy" do
44+
base = ArduinoCI::CIConfig.new
45+
base.load_yaml(File.join(File.dirname(__FILE__), "yaml", "o2.yaml"))
46+
47+
expect(base.to_h).to eq(
48+
packages: {},
49+
platforms: {
50+
"bogo"=> {
51+
board: "fakeduino:beep:bogo"
52+
},
53+
},
54+
compile: {
55+
libraries: ["zip"],
56+
platforms: ["bogo"]
57+
},
58+
unittest: {
59+
testfiles: {
60+
select: ["*-*.*"],
61+
reject: ["sam-squamsh.*"]
62+
},
63+
libraries: ["def456"],
64+
platforms: ["bogo"]
65+
}
66+
)
67+
end
68+
end
69+
70+
context "clone" do
71+
it "creates a copy" do
72+
base = ArduinoCI::CIConfig.default
73+
orig = base.to_h
74+
clone1 = orig.clone.to_h
75+
clone2 = orig.clone.to_h
76+
77+
expect(orig).to eq(clone1)
78+
expect(clone1).to eq(clone2)
79+
end
80+
end
81+
4282
context "with_override" do
4383
it "loads from yaml" do
4484
override_file = File.join(File.dirname(__FILE__), "yaml", "o1.yaml")

spec/yaml/o2.yaml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
platforms:
2+
bogo:
3+
board: fakeduino:beep:bogo
4+
5+
compile:
6+
libraries:
7+
- "zip"
8+
platforms:
9+
- bogo
10+
11+
unittest:
12+
testfiles:
13+
select:
14+
- "*-*.*"
15+
reject:
16+
- "sam-squamsh.*"
17+
libraries:
18+
- "def456"
19+
platforms:
20+
- bogo

0 commit comments

Comments
 (0)