Skip to content

Add CI that actually uses the arduino executable #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jan 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
sudo: false
language: ruby

rvm:
# rvm:
# - "2.0.0"
# - "2.1.0"
# - "2.2.0"
# - rbx
- "2.5.0"
# - "2.5.0"

before_install: gem install bundler -v 1.15.4
script:
- bundle exec rubocop --version
- bundle exec rubocop -D .
- bundle exec rspec
- bundle exec ci_system_check.rb
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Added
- `ArduinoInstallation` class for managing lib / executable paths
- `DisplayManager` class for managing Xvfb instance if needed
- `ArduinoCmd` can report on whether a board is installed

### Changed
- `DisplayManger.with_display` doesn't `disable` if the display was enabled prior to starting the block

### Deprecated

Expand Down
7 changes: 5 additions & 2 deletions arduino_ci.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ Gem::Specification.new do |spec|
spec.description = spec.description
spec.homepage = "http://github.com/ifreecarve/arduino_ci"

spec.files = ['README.md', '.yardopts'] + Dir['lib/**/*.*'].reject { |f| f.match(%r{^(test|spec|features)/}) }

spec.bindir = "exe"
rejection_regex = %r{^(test|spec|features)/}
libfiles = Dir['lib/**/*.*'].reject { |f| f.match(rejection_regex) }
binfiles = Dir[File.join(spec.bindir, '/**/*.*')].reject { |f| f.match(rejection_regex) }
spec.files = ['README.md', '.yardopts'] + libfiles + binfiles

spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]

Expand Down
23 changes: 23 additions & 0 deletions exe/ci_system_check.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require 'arduino_ci'

puts "Enabling display with display manager"
ArduinoCI::DisplayManager::instance.enable

puts "Autlocating Arduino command"
arduino_cmd = ArduinoCI::ArduinoCmd.autolocate!

board_tests = {
"arduino:avr:uno" => true,
"eggs:milk:wheat" => false,
}

got_problem = false
board_tests.each do |k, v|
puts "I expect arduino_cmd.board_installed?(#{k}) to be #{v}"
result = arduino_cmd.board_installed?(k)
puts " board_installed?(#{k}) returns #{result}. expected #{v}"
got_problem = true if v != result
end

abort if got_problem
exit(0)
118 changes: 1 addition & 117 deletions lib/arduino_ci.rb
Original file line number Diff line number Diff line change
@@ -1,124 +1,8 @@
require "arduino_ci/version"

require 'singleton'

# Cross-platform way of finding an executable in the $PATH.
# via https://stackoverflow.com/a/5471032/2063546
# which('ruby') #=> /usr/bin/ruby
def which(cmd)
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
exts.each do |ext|
exe = File.join(path, "#{cmd}#{ext}")
return exe if File.executable?(exe) && !File.directory?(exe)
end
end
nil
end
require "arduino_ci/arduino_cmd"

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

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

# create as many ArduinoCmds as you like, but we need one and only one display manager
class DisplayMgr
include Singleton
attr_reader :enabled

def initialize
@existing = existing_display?
@enabled = false
@pid = nil
end

# attempt to determine if the machine is running a graphical display (i.e. not Travis)
def existing_display?
return true if RUBY_PLATFORM.include? "darwin"
return true if ENV["DISPLAY"].nil?
return true if ENV["DISPLAY"].include? ":"
false
end

# enable a virtual display
def enable
return @enabled = true if @existing # silent no-op if built in display
return unless @pid.nil?

@enabled = true
@pid = fork do
puts "Forking Xvfb"
system("Xvfb", ":1", "-ac", "-screen", "0", "1280x1024x16")
puts "Xvfb unexpectedly quit!"
end
sleep(3) # TODO: test a connection to the X server?
end

# disable the virtual display
def disable
return @enabled = false if @existing # silent no-op if built in display
return if @pid.nil?

begin
Process.kill 9, @pid
ensure
Process.wait @pid
@pid = nil
end
puts "Xvfb killed"
end

# Enable a virtual display for the duration of the given block
def with_display
enable
begin
yield environment
ensure
disable
end
end

def environment
return nil unless @existing || @enabled
return {} if @existing
{ DISPLAY => ":1.0" }
end

# On finalize, ensure child process is ended
def self.finalize
disable
end
end

class << self
protected :new

# attempt to find a workable Arduino executable across platforms
def guess_executable_location
osx_place = "/Applications/Arduino.app/Contents/MacOS/Arduino"
places = {
"arduino" => !which("arduino").nil?,
osx_place => (File.exist? osx_place),
}
places.each { |k, v| return k if v }
nil
end

def autolocate
ret = new
ret.path = guess_executable_location
ret
end
end

attr_accessor :path

def initialize
@display_mgr = DisplayMgr::instance
end

end

end
43 changes: 43 additions & 0 deletions lib/arduino_ci/arduino_cmd.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require 'arduino_ci/display_manager'
require 'arduino_ci/arduino_installation'

module ArduinoCI

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

class << self
protected :new

# @return [ArduinoCmd] A command object with a best guess (or nil) for the installation
def autolocate
new(ArduinoInstallation.autolocate)
end

# @return [ArduinoCmd] A command object, installing Arduino if necessary
def autolocate!
new(ArduinoInstallation.autolocate!)
end

end

attr_accessor :installation

# @param installation [ArduinoInstallation] the location of the Arduino program installation
def initialize(installation)
@display_mgr = DisplayManager::instance
@installation = installation
end

# run the arduino command
def run(*args)
full_args = [@installation.cmd_path] + args
@display_mgr.run(*full_args)
end

def board_installed?(board)
run("--board", board)
end

end
end
65 changes: 65 additions & 0 deletions lib/arduino_ci/arduino_installation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
require "arduino_ci/host"

module ArduinoCI

# Manage the OS-specific install location of Arduino
class ArduinoInstallation
attr_accessor :cmd_path
attr_accessor :lib_dir

class << self
def force_install_location
File.join(ENV['HOME'], 'arduino_ci_ide')
end

# attempt to find a workable Arduino executable across platforms
def autolocate
ret = new

osx_place = "/Applications/Arduino.app/Contents/MacOS"
if File.exist? osx_place
ret.cmd_path = File.join(osx_place, "Arduino")
ret.lib_dir = File.join(osx_place, "Libraries")
return ret
end

posix_place = Host.which("arduino")
unless posix_place.nil?
ret.cmd_path = posix_place
ret.lib_dir = File.join(ENV['HOME'], "Sketchbook") # assume linux
# https://learn.adafruit.com/adafruit-all-about-arduino-libraries-install-use/how-to-install-a-library
return ret
end

if File.exist? force_install_location
ret.cmd_path = File.join(force_install_location, "arduino")
ret.lib_dir = File.join(force_install_location, "libraries")
# TODO: "libraries" is what's in the adafruit install.sh script
return ret
end

ret
end

# Attempt to find a workable Arduino executable across platforms, and install it if we don't
def autolocate!
candidate = autolocate
return candidate unless candidate.cmd_path.nil?
# force the install

if force_install
candidate.cmd_path = File.join(force_install_location, "arduino")
candidate.lib_dir = File.join(force_install_location, "libraries")
end
candidate
end

def force_install
system("wget", "https://downloads.arduino.cc/arduino-1.6.5-linux64.tar.xz")
system("tar", "xf", "arduino-1.6.5-linux64.tar.xz")
system("mv", "arduino-1.6.5", force_install_location)
end

end
end
end
Loading