Skip to content

Commit 3555117

Browse files
authored
Merge pull request #5 from ifreecarve/2018-01-17_binaries
Allow non-GUI binaries
2 parents dac0c75 + cef20cf commit 3555117

17 files changed

+472
-170
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ vendor
1111

1212
# rspec failure tracking
1313
.rspec_status
14+
15+
# C++ stuff
16+
arduino_ci_built.bin

SampleProjects/DoSomething/do-something.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <Arduino.h>
12
#include <do-something.h>
23
int doSomething(void) {
34
millis(); // this line is only here to test that we're able to refer to the builtins

arduino_ci.gemspec

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Gem::Specification.new do |spec|
2323
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
2424
spec.require_paths = ["lib"]
2525

26+
spec.add_dependency "os", "~> 1.0"
27+
2628
spec.add_development_dependency "bundler", "~> 1.15"
2729
spec.add_development_dependency "rspec", "~> 3.0"
2830
spec.add_development_dependency 'rubocop', '~>0.49.0'

cpp/Arduino.h

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
Mock Arduino.h library.
3+
4+
Where possible, variable names from the Arduino library are used to avoid conflicts
5+
6+
*/
7+
8+
9+
#ifndef ARDUINO_CI_ARDUINO
10+
11+
#include "math.h"
12+
#define ARDUINO_CI_ARDUINO
13+
14+
struct unit_test_state {
15+
unsigned long micros;
16+
};
17+
18+
struct unit_test_state godmode {
19+
0, // micros
20+
};
21+
22+
unsigned long millis() {
23+
return godmode.micros / 1000;
24+
}
25+
26+
unsigned long micros() {
27+
return godmode.micros;
28+
}
29+
30+
#endif

cpp/math.h

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//abs
2+
long abs(long x) { return x > 0 ? x : -x; }
3+
double fabs(double x) { return x > 0 ? x : -x; }
4+
5+
//max
6+
long max(long a, long b) { return a > b ? a : b; }
7+
double fmax(double a, double b) { return a > b ? a : b; }
8+
9+
//min
10+
long min(long a, long b) { return a < b ? a : b; }
11+
double fmin(double a, double b) { return a < b ? a : b; }
12+
13+
//constrain
14+
long constrain(long x, long a, long b) { return max(a, min(b, x)); }
15+
double constrain(double x, double a, double b) { return max(a, min(b, x)); }
16+
17+
//map
18+
long map(long x, long in_min, long in_max, long out_min, long out_max)
19+
{
20+
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
21+
}
22+
23+
double map(double x, double in_min, double in_max, double out_min, double out_max)
24+
{
25+
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
26+
}
27+
28+
//sq
29+
long sq(long x) { return x * x; }
30+
double sq(double x) { return x * x; }
31+
32+
// ??? too lazy to sort these now
33+
//pow
34+
//sqrt
35+
36+
// http://www.ganssle.com/approx.htm
37+
// http://www.ganssle.com/approx/sincos.cpp
38+
//cos
39+
//sin
40+
//tan
41+

exe/ci_system_check.rb

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
require 'arduino_ci'
22

3-
puts "Enabling display with display manager"
4-
ArduinoCI::DisplayManager::instance.enable
5-
63
puts "Autlocating Arduino command"
7-
arduino_cmd = ArduinoCI::ArduinoCmd.autolocate!
4+
arduino_cmd = ArduinoCI::ArduinoInstallation.autolocate!
85

96
board_tests = {
107
"arduino:avr:uno" => true,
@@ -50,6 +47,11 @@
5047
got_problem = true unless arduino_cmd.verify_sketch(simple_sketch)
5148

5249
library_path = File.join(File.dirname(File.dirname(__FILE__)), "SampleProjects", "DoSomething")
50+
51+
puts "verify a library with arduino mocks"
52+
cpp_library = ArduinoCI::CppLibrary.new(library_path)
53+
got_problem = true unless cpp_library.build(arduino_cmd)
54+
5355
puts "verify the examples of a library (#{library_path})..."
5456
puts " - Install the library"
5557
installed_library_path = arduino_cmd.install_local_library(library_path)

lib/arduino_ci.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require "arduino_ci/version"
2-
require "arduino_ci/arduino_cmd"
2+
require "arduino_ci/arduino_installation"
3+
require "arduino_ci/cpp_library"
34

45
# ArduinoCI contains classes for automated testing of Arduino code on the command line
56
# @author Ian Katz <[email protected]>

lib/arduino_ci/arduino_cmd.rb

+68-76
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,43 @@
11
require 'fileutils'
2-
require 'arduino_ci/display_manager'
3-
require 'arduino_ci/arduino_installation'
42

53
module ArduinoCI
64

75
# Wrap the Arduino executable. This requires, in some cases, a faked display.
86
class ArduinoCmd
97

10-
class << self
11-
protected :new
12-
13-
# @return [ArduinoCmd] A command object with a best guess (or nil) for the installation
14-
def autolocate
15-
new(ArduinoInstallation.autolocate)
16-
end
17-
18-
# @return [ArduinoCmd] A command object, installing Arduino if necessary
19-
def autolocate!
20-
new(ArduinoInstallation.autolocate!)
21-
end
22-
8+
# Enable a shortcut syntax for command line flags
9+
# @param name [String] What the flag will be called (prefixed with 'flag_')
10+
# @return [void]
11+
# @macro [attach] flag
12+
# @!attribute [r] flag_$1
13+
# @return String $2 the text of the command line flag
14+
def self.flag(name, text = nil)
15+
text = "(flag #{name} not defined)" if text.nil?
16+
self.class_eval("def flag_#{name};\"#{text}\";end")
2317
end
2418

2519
attr_accessor :installation
26-
attr_reader :prefs_response_time
20+
attr_accessor :base_cmd
21+
attr_accessor :gcc_cmd
22+
2723
attr_reader :library_is_indexed
2824

29-
# @param installation [ArduinoInstallation] the location of the Arduino program installation
30-
def initialize(installation)
31-
@display_mgr = DisplayManager::instance
32-
@installation = installation
33-
@prefs_response_time = nil
25+
# set the command line flags (undefined for now).
26+
# These vary between gui/cli
27+
flag :get_pref
28+
flag :set_pref
29+
flag :save_prefs
30+
flag :use_board
31+
flag :install_boards
32+
flag :install_library
33+
flag :verify
34+
35+
def initialize
3436
@prefs_cache = nil
3537
@library_is_indexed = false
3638
end
3739

38-
def _parse_pref_string(arduino_output)
40+
def parse_pref_string(arduino_output)
3941
lines = arduino_output.split("\n").select { |l| l.include? "=" }
4042
ret = lines.each_with_object({}) do |e, acc|
4143
parts = e.split("=", 2)
@@ -45,26 +47,21 @@ def _parse_pref_string(arduino_output)
4547
ret
4648
end
4749

48-
# fetch preferences to a hash
49-
def _prefs
50-
resp = nil
51-
if @installation.requires_x
52-
@display_mgr.with_display do
53-
start = Time.now
54-
resp = run_and_capture("--get-pref")
55-
@prefs_response_time = Time.now - start
56-
end
57-
else
58-
start = Time.now
59-
resp = run_and_capture("--get-pref")
60-
@prefs_response_time = Time.now - start
61-
end
50+
def _lib_dir
51+
"<lib dir not defined>"
52+
end
53+
54+
# fetch preferences to a string
55+
def _prefs_raw
56+
resp = run_and_capture(flag_get_pref)
6257
return nil unless resp[:success]
63-
_parse_pref_string(resp[:out])
58+
resp[:out]
6459
end
6560

6661
def prefs
67-
@prefs_cache = _prefs if @prefs_cache.nil?
62+
prefs_raw = _prefs_raw if @prefs_cache.nil?
63+
return nil if prefs_raw.nil?
64+
@prefs_cache = parse_pref_string(prefs_raw)
6865
@prefs_cache.clone
6966
end
7067

@@ -74,42 +71,38 @@ def get_pref(key)
7471
data[key]
7572
end
7673

77-
# set a preference key/value pair
74+
# underlying preference-setter.
75+
# @return [bool] whether the command succeeded
76+
def _set_pref(key, value)
77+
run_and_capture(flag_set_pref, "#{key}=#{value}", flag_save_prefs)[:success]
78+
end
79+
80+
# set a preference key/value pair, and update the cache.
7881
# @param key [String] the preference key
7982
# @param value [String] the preference value
8083
# @return [bool] whether the command succeeded
8184
def set_pref(key, value)
82-
success = run_with_gui_guess(" about preferences", "--pref", "#{key}=#{value}", "--save-prefs")
85+
success = _set_pref(key, value)
8386
@prefs_cache[key] = value if success
8487
success
8588
end
8689

8790
# run the arduino command
88-
def run(*args, **kwargs)
89-
full_args = @installation.base_cmd + args
90-
if @installation.requires_x
91-
@display_mgr.run(*full_args, **kwargs)
92-
else
93-
Host.run(*full_args, **kwargs)
94-
end
91+
def _run(*args, **kwargs)
92+
raise "Ian needs to implement this in a subclass #{args} #{kwargs}"
9593
end
9694

97-
def run_with_gui_guess(message, *args, **kwargs)
98-
# On Travis CI, we get an error message in the GUI instead of on STDERR
99-
# so, assume that if we don't get a rapid reply that things are not installed
100-
101-
# if we don't need X, we can skip this whole thing
102-
return run_and_capture(*args, **kwargs)[:success] unless @installation.requires_x
95+
# build and run the arduino command
96+
def run(*args, **kwargs)
97+
# TODO: detect env!!
98+
full_args = @base_cmd + args
99+
_run(*full_args, **kwargs)
100+
end
103101

104-
prefs if @prefs_response_time.nil?
105-
x3 = @prefs_response_time * 3
106-
Timeout.timeout(x3) do
107-
result = run_and_capture(*args, **kwargs)
108-
result[:success]
109-
end
110-
rescue Timeout::Error
111-
puts "No response in #{x3} seconds. Assuming graphical modal error message#{message}."
112-
false
102+
def run_gcc(*args, **kwargs)
103+
# TODO: detect env!!
104+
full_args = @gcc_cmd + args
105+
_run(*full_args, **kwargs)
113106
end
114107

115108
# run a command and capture its output
@@ -140,30 +133,29 @@ def run_wrap(*args, **kwargs)
140133
# we do this by just selecting a board.
141134
# the arduino binary will error if unrecognized and do a successful no-op if it's installed
142135
def board_installed?(boardname)
143-
run_with_gui_guess(" about board not installed", "--board", boardname)
136+
run_and_capture(flag_use_board, boardname)[:success]
144137
end
145138

146139
# install a board by name
147140
# @param name [String] the board name
148141
# @return [bool] whether the command succeeded
149142
def install_board(boardname)
150143
# TODO: find out why IO.pipe fails but File::NULL succeeds :(
151-
run_and_capture("--install-boards", boardname, out: File::NULL)[:success]
144+
run_and_capture(flag_install_boards, boardname, out: File::NULL)[:success]
152145
end
153146

154147
# install a library by name
155148
# @param name [String] the library name
156149
# @return [bool] whether the command succeeded
157150
def install_library(library_name)
158-
result = run_and_capture("--install-library", library_name)
151+
result = run_and_capture(flag_install_library, library_name)
159152
@library_is_indexed = true if result[:success]
160153
result[:success]
161154
end
162155

163156
# generate the (very likely) path of a library given its name
164157
def library_path(library_name)
165-
sketchbook = get_pref("sketchbook.path")
166-
File.join(sketchbook, library_name)
158+
File.join(_lib_dir, library_name)
167159
end
168160

169161
# update the library index
@@ -175,7 +167,7 @@ def update_library_index
175167

176168
# use a particular board for compilation
177169
def use_board(boardname)
178-
run_with_gui_guess(" about board not installed", "--board", boardname, "--save-prefs")
170+
run_and_capture(flag_use_board, boardname, flag_save_prefs)[:success]
179171
end
180172

181173
# use a particular board for compilation, installing it if necessary
@@ -197,25 +189,25 @@ def verify_sketch(path)
197189
puts "Can't verify nonexistent Sketch at '#{path}'!"
198190
return false
199191
end
200-
run("--verify", path, err: :out)
192+
run(flag_verify, path, err: :out)
201193
end
202194

203195
# ensure that the given library is installed, or symlinked as appropriate
204196
# return the path of the prepared library, or nil
205-
def install_local_library(library_path)
206-
library_name = File.basename(library_path)
207-
destination_path = File.join(@installation.lib_dir, library_name)
197+
def install_local_library(path)
198+
library_name = File.basename(path)
199+
destination_path = library_path(library_name)
208200

209201
# things get weird if the sketchbook contains the library.
210202
# check that first
211203
if File.exist? destination_path
212204
uhoh = "There is already a library '#{library_name}' in the library directory"
213-
return destination_path if destination_path == library_path
205+
return destination_path if destination_path == path
214206

215207
# maybe it's a symlink? that would be OK
216208
if File.symlink?(destination_path)
217-
return destination_path if File.readlink(destination_path) == library_path
218-
puts "#{uhoh} and it's not symlinked to #{library_path}"
209+
return destination_path if File.readlink(destination_path) == path
210+
puts "#{uhoh} and it's not symlinked to #{path}"
219211
return nil
220212
end
221213

@@ -224,7 +216,7 @@ def install_local_library(library_path)
224216
end
225217

226218
# install the library
227-
FileUtils.ln_s(library_path, destination_path)
219+
FileUtils.ln_s(path, destination_path)
228220
destination_path
229221
end
230222

0 commit comments

Comments
 (0)