arduino_ci 0.1.16 → 0.1.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +21 -2
- data/cpp/arduino/stdlib.cpp +61 -0
- data/cpp/arduino/stdlib.h +14 -0
- data/exe/arduino_ci_remote.rb +47 -2
- data/lib/arduino_ci/arduino_downloader.rb +3 -15
- data/lib/arduino_ci/arduino_downloader_osx.rb +3 -7
- data/lib/arduino_ci/arduino_downloader_windows.rb +3 -6
- data/lib/arduino_ci/arduino_installation.rb +11 -2
- data/lib/arduino_ci/ci_config.rb +46 -6
- data/lib/arduino_ci/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a089a7486b4295c2ed49a832a0dd148d6e31561a
|
4
|
+
data.tar.gz: 0c13b415b9b585c42ec2a8501c5e6ef606137c30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74819da0c7bbaf23fbd5ceaf1952e43442d0bdd4789ecf1beb4c3a110038350797b9b6629698a3348bc9803afb444344badc2f0bdebbc930bf42835bb07b3109
|
7
|
+
data.tar.gz: 794e75954f74a2e9420d6ec4e884683b86f2a04d2ac0b0a3cf13776126e8db40c29724ebe397fb2d9bd062c5e3044cea206ed078aa5958dee825cd902d38e5a9
|
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
|
2
|
-
# ArduinoCI Ruby gem (`arduino_ci`) [![Gem Version](https://badge.fury.io/rb/arduino_ci.svg)](https://rubygems.org/gems/arduino_ci) [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/arduino_ci/0.1.
|
2
|
+
# ArduinoCI Ruby gem (`arduino_ci`) [![Gem Version](https://badge.fury.io/rb/arduino_ci.svg)](https://rubygems.org/gems/arduino_ci) [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/arduino_ci/0.1.17)
|
3
3
|
|
4
4
|
You want your Arduino library to be automatically built and tested every time someone contributes code to your project on GitHub, but the Arduino IDE lacks the ability to run unit tests. [Arduino CI](https://github.com/ianfixes/arduino_ci) provides that ability.
|
5
5
|
|
6
|
-
You want to run tests on your Arduino library without hardware present, but the IDE doesn't support that. Arduino CI
|
6
|
+
You want to run tests on your Arduino library without hardware present, but the IDE doesn't support that. Arduino CI provides that ability.
|
7
7
|
|
8
8
|
You want to precisely replicate certain software states in your library, but you don't have sub-millisecond reflexes for physically faking the inputs, outputs, and serial port. Arduino CI fakes 100% of the physical input and output of an Arduino board, including the clock.
|
9
9
|
|
@@ -34,6 +34,25 @@ source 'https://rubygems.org'
|
|
34
34
|
gem 'arduino_ci'
|
35
35
|
```
|
36
36
|
|
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
|
+
|
37
56
|
> **Note:** `arduino_ci_remote.rb` expects to be run from the root directory of your Arduino project library.
|
38
57
|
|
39
58
|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
|
2
|
+
#if 1 // This code is copied from https://people.cs.umu.se/isak/snippets/ltoa.c and then converted from ltoa to itoa.
|
3
|
+
/*
|
4
|
+
** LTOA.C
|
5
|
+
**
|
6
|
+
** Converts a integer to a string.
|
7
|
+
**
|
8
|
+
** Copyright 1988-90 by Robert B. Stout dba MicroFirm
|
9
|
+
**
|
10
|
+
** Released to public domain, 1991
|
11
|
+
**
|
12
|
+
** Parameters: 1 - number to be converted
|
13
|
+
** 2 - buffer in which to build the converted string
|
14
|
+
** 3 - number base to use for conversion
|
15
|
+
**
|
16
|
+
** Returns: A character pointer to the converted string if
|
17
|
+
** successful, a NULL pointer if the number base specified
|
18
|
+
** is out of range.
|
19
|
+
*/
|
20
|
+
|
21
|
+
#include <stdlib.h>
|
22
|
+
#include <string.h>
|
23
|
+
|
24
|
+
#define BUFSIZE (sizeof(int) * 8 + 1)
|
25
|
+
|
26
|
+
char *itoa(int N, char *str, int base)
|
27
|
+
{
|
28
|
+
int i = 2;
|
29
|
+
int uarg;
|
30
|
+
char *tail, *head = str, buf[BUFSIZE];
|
31
|
+
|
32
|
+
if (36 < base || 2 > base)
|
33
|
+
base = 10; /* can only use 0-9, A-Z */
|
34
|
+
tail = &buf[BUFSIZE - 1]; /* last character position */
|
35
|
+
*tail-- = '\0';
|
36
|
+
|
37
|
+
if (10 == base && N < 0L)
|
38
|
+
{
|
39
|
+
*head++ = '-';
|
40
|
+
uarg = -N;
|
41
|
+
}
|
42
|
+
else uarg = N;
|
43
|
+
|
44
|
+
if (uarg)
|
45
|
+
{
|
46
|
+
for (i = 1; uarg; ++i)
|
47
|
+
{
|
48
|
+
ldiv_t r;
|
49
|
+
|
50
|
+
r = ldiv(uarg, base);
|
51
|
+
*tail-- = (char)(r.rem + ((9L < r.rem) ?
|
52
|
+
('A' - 10L) : '0'));
|
53
|
+
uarg = r.quot;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
else *tail-- = '0';
|
57
|
+
|
58
|
+
memcpy(head, ++tail, i);
|
59
|
+
return str;
|
60
|
+
}
|
61
|
+
#endif
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
// Header file to compensate for differences between
|
4
|
+
// arduino-1.x.x/hardware/tools/avr/avr/include/stdlib.h and /usr/include/stdlib.h.
|
5
|
+
|
6
|
+
#include_next <stdlib.h>
|
7
|
+
|
8
|
+
/*
|
9
|
+
* Arduino stdlib.h includes a prototype for itoa which is not a standard function,
|
10
|
+
* and is not available in /usr/include/stdlib.h. Provide one here.
|
11
|
+
* http://www.cplusplus.com/reference/cstdlib/itoa/
|
12
|
+
* https://stackoverflow.com/questions/190229/where-is-the-itoa-function-in-linux
|
13
|
+
*/
|
14
|
+
char *itoa(int val, char *s, int radix);
|
data/exe/arduino_ci_remote.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
require 'arduino_ci'
|
3
3
|
require 'set'
|
4
4
|
require 'pathname'
|
5
|
+
require 'optparse'
|
5
6
|
|
6
7
|
WIDTH = 80
|
7
8
|
FIND_FILES_INDENT = 4
|
@@ -9,6 +10,41 @@ FIND_FILES_INDENT = 4
|
|
9
10
|
@failure_count = 0
|
10
11
|
@passfail = proc { |result| result ? "✓" : "✗" }
|
11
12
|
|
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
|
+
|
12
48
|
# terminate after printing any debug info. TODO: capture debug info
|
13
49
|
def terminate(final = nil)
|
14
50
|
puts "Failures: #{@failure_count}"
|
@@ -28,6 +64,12 @@ end
|
|
28
64
|
# without altering the signature because it only leaves space
|
29
65
|
# for the checkmark _after_ the multiline, it doesn't know how
|
30
66
|
# 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)
|
31
73
|
def perform_action(message, multiline, mark_fn, on_fail_msg, tally_on_fail, abort_on_fail)
|
32
74
|
line = "#{message}... "
|
33
75
|
endline = "...#{message} "
|
@@ -111,7 +153,10 @@ def display_files(pathname)
|
|
111
153
|
non_hidden.each { |p| puts "#{margin}#{p}" }
|
112
154
|
end
|
113
155
|
|
114
|
-
def perform_unit_tests(
|
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
|
115
160
|
cpp_library = ArduinoCI::CppLibrary.new(Pathname.new("."), @arduino_cmd.lib_dir)
|
116
161
|
|
117
162
|
# check GCC
|
@@ -149,7 +194,7 @@ def perform_unit_tests(config)
|
|
149
194
|
inform("Skipping unit tests") { "no platforms were requested" }
|
150
195
|
else
|
151
196
|
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|
|
153
198
|
unittest_name = unittest_path.basename.to_s
|
154
199
|
compilers.each do |gcc_binary|
|
155
200
|
attempt_multiline("Unit testing #{unittest_name} with #{gcc_binary}") do
|
@@ -32,13 +32,7 @@ module ArduinoCI
|
|
32
32
|
# if it exists. I'm not sure why we would have both, but if we did
|
33
33
|
# a force install then let's make sure we actually use it.
|
34
34
|
locations = [self.force_installed_executable, self.existing_executable]
|
35
|
-
locations.
|
36
|
-
next if loc.nil?
|
37
|
-
next unless File.exist? loc
|
38
|
-
|
39
|
-
return loc
|
40
|
-
end
|
41
|
-
nil
|
35
|
+
locations.find { |loc| !loc.nil? && File.exist?(loc) }
|
42
36
|
end
|
43
37
|
|
44
38
|
# The autolocated directory of the installation
|
@@ -49,13 +43,7 @@ module ArduinoCI
|
|
49
43
|
# if it exists. I'm not sure why we would have both, but if we did
|
50
44
|
# a force install then let's make sure we actually use it.
|
51
45
|
locations = [self.force_install_location, self.existing_installation]
|
52
|
-
locations.
|
53
|
-
next if loc.nil?
|
54
|
-
next unless File.exist? loc
|
55
|
-
|
56
|
-
return loc
|
57
|
-
end
|
58
|
-
nil
|
46
|
+
locations.find { |loc| !loc.nil? && File.exist?(loc) }
|
59
47
|
end
|
60
48
|
|
61
49
|
# The path to the directory of an existing installation, or nil
|
@@ -198,7 +186,7 @@ module ArduinoCI
|
|
198
186
|
elsif File.exist? extracted_file
|
199
187
|
install
|
200
188
|
else
|
201
|
-
puts "
|
189
|
+
puts "Could not find extracted archive (tried #{extracted_file})"
|
202
190
|
end
|
203
191
|
|
204
192
|
File.exist? self.class.force_install_location
|
@@ -27,21 +27,17 @@ module ArduinoCI
|
|
27
27
|
# @param Array<string> a list of places to look
|
28
28
|
# @return [string]
|
29
29
|
def self.find_existing_arduino_dir(paths)
|
30
|
-
paths.
|
31
|
-
return path if File.exist? path
|
32
|
-
end
|
33
|
-
nil
|
30
|
+
paths.find(&File.method(:exist?))
|
34
31
|
end
|
35
32
|
|
36
33
|
# An existing Arduino file in one of the given directories, or nil
|
37
34
|
# @param Array<string> a list of places to look for the executable
|
38
35
|
# @return [string]
|
39
36
|
def self.find_existing_arduino_exe(paths)
|
40
|
-
paths.
|
37
|
+
paths.find do |path|
|
41
38
|
exe = File.join(path, "MacOS", "Arduino")
|
42
|
-
|
39
|
+
File.exist? exe
|
43
40
|
end
|
44
|
-
nil
|
45
41
|
end
|
46
42
|
|
47
43
|
# The path to the directory of an existing installation, or nil
|
@@ -81,10 +81,10 @@ module ArduinoCI
|
|
81
81
|
# @return [string]
|
82
82
|
def self.existing_executable
|
83
83
|
arduino_reg = 'SOFTWARE\WOW6432Node\Arduino'
|
84
|
-
Win32::Registry::HKEY_LOCAL_MACHINE.open(arduino_reg) do |reg|
|
84
|
+
Win32::Registry::HKEY_LOCAL_MACHINE.open(arduino_reg).find do |reg|
|
85
85
|
path = reg.read_s('Install_Dir')
|
86
86
|
exe = File.join(path, "arduino_debug.exe")
|
87
|
-
|
87
|
+
File.exist? exe
|
88
88
|
end
|
89
89
|
rescue
|
90
90
|
nil
|
@@ -93,10 +93,7 @@ module ArduinoCI
|
|
93
93
|
# The executable Arduino file in a forced installation, or nil
|
94
94
|
# @return [string]
|
95
95
|
def self.force_installed_executable
|
96
|
-
|
97
|
-
return nil if exe.nil?
|
98
|
-
|
99
|
-
exe
|
96
|
+
File.join(self.force_install_location, "arduino_debug.exe")
|
100
97
|
end
|
101
98
|
|
102
99
|
end
|
@@ -13,6 +13,8 @@ DESIRED_ARDUINO_IDE_VERSION = "1.8.6".freeze
|
|
13
13
|
|
14
14
|
module ArduinoCI
|
15
15
|
|
16
|
+
class ArduinoInstallationError < StandardError; end
|
17
|
+
|
16
18
|
# Manage the OS-specific install location of Arduino
|
17
19
|
class ArduinoInstallation
|
18
20
|
|
@@ -78,7 +80,13 @@ module ArduinoCI
|
|
78
80
|
# don't want to see is a java error.
|
79
81
|
args = launcher + ["--bogus-option"]
|
80
82
|
result = Host.run_and_capture(*args)
|
81
|
-
|
83
|
+
|
84
|
+
# NOTE: Was originally searching for "Error: unknown option: --bogus-option"
|
85
|
+
# but also need to find "Erreur: option inconnue : --bogus-option"
|
86
|
+
# and who knows how many other languages.
|
87
|
+
# For now, just search for the end of the error and hope that the java-style
|
88
|
+
# launch of this won't include a similar string in it
|
89
|
+
next unless result[:err].include? ": --bogus-option"
|
82
90
|
|
83
91
|
ret.base_cmd = launcher
|
84
92
|
ret.binary_path = Pathname.new(osx_root)
|
@@ -94,7 +102,8 @@ module ArduinoCI
|
|
94
102
|
return candidate unless candidate.nil?
|
95
103
|
|
96
104
|
# force the install
|
97
|
-
force_install
|
105
|
+
raise ArduinoInstallationError, "Failed to force-install Arduino" unless force_install
|
106
|
+
|
98
107
|
autolocate
|
99
108
|
end
|
100
109
|
|
data/lib/arduino_ci/ci_config.rb
CHANGED
@@ -35,6 +35,8 @@ UNITTEST_SCHEMA = {
|
|
35
35
|
}.freeze
|
36
36
|
module ArduinoCI
|
37
37
|
|
38
|
+
class ConfigurationError < StandardError; end
|
39
|
+
|
38
40
|
# The filename controlling (overriding) the defaults for testing.
|
39
41
|
# Files with this name can be used in the root directory of the Arduino library and in any/all of the example directories
|
40
42
|
CONFIG_FILENAMES = [
|
@@ -70,6 +72,21 @@ module ArduinoCI
|
|
70
72
|
@unittest_info = {}
|
71
73
|
end
|
72
74
|
|
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
|
+
|
73
90
|
# Deep-clone a hash
|
74
91
|
# @param hash [Hash] the source data
|
75
92
|
# @return [Hash] a copy
|
@@ -108,8 +125,15 @@ module ArduinoCI
|
|
108
125
|
# @return [ArduinoCI::CIConfig] a reference to self
|
109
126
|
def load_yaml(path)
|
110
127
|
yml = YAML.load_file(path)
|
111
|
-
raise "The YAML file at #{path} failed to load" unless yml
|
128
|
+
raise ConfigurationError, "The YAML file at #{path} failed to load" unless yml
|
129
|
+
|
130
|
+
apply_configuration(yml)
|
131
|
+
end
|
112
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)
|
113
137
|
if yml.include?("packages")
|
114
138
|
yml["packages"].each do |k, v|
|
115
139
|
valid_data = validate_data("packages", v, PACKAGE_SCHEMA)
|
@@ -137,19 +161,35 @@ module ArduinoCI
|
|
137
161
|
self
|
138
162
|
end
|
139
163
|
|
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
|
+
|
140
175
|
# Override these settings with settings from another file
|
141
176
|
# @param path [String] the path to the settings yaml file
|
142
177
|
# @return [ArduinoCI::CIConfig] the new settings object
|
143
178
|
def with_override(path)
|
144
|
-
overridden_config =
|
145
|
-
overridden_config.package_info = deep_clone(@package_info)
|
146
|
-
overridden_config.platform_info = deep_clone(@platform_info)
|
147
|
-
overridden_config.compile_info = deep_clone(@compile_info)
|
148
|
-
overridden_config.unittest_info = deep_clone(@unittest_info)
|
179
|
+
overridden_config = clone
|
149
180
|
overridden_config.load_yaml(path)
|
150
181
|
overridden_config
|
151
182
|
end
|
152
183
|
|
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
|
+
|
153
193
|
# Get the config file at a given path, if it exists, and pass that to a block.
|
154
194
|
# Many config files may exist, but only the first match is used
|
155
195
|
# @param base_dir [String] The directory in which to search for a config file
|
data/lib/arduino_ci/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arduino_ci
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.17
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ian Katz
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-01-
|
11
|
+
date: 2019-01-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: os
|
@@ -401,6 +401,8 @@ files:
|
|
401
401
|
- cpp/arduino/ci/Queue.h
|
402
402
|
- cpp/arduino/ci/README.md
|
403
403
|
- cpp/arduino/ci/Table.h
|
404
|
+
- cpp/arduino/stdlib.cpp
|
405
|
+
- cpp/arduino/stdlib.h
|
404
406
|
- cpp/unittest/ArduinoUnitTests.cpp
|
405
407
|
- cpp/unittest/ArduinoUnitTests.h
|
406
408
|
- cpp/unittest/Assertion.h
|