Skip to content

Commit ab47c85

Browse files
committed
split some files and add installer functionality
1 parent 8d41392 commit ab47c85

File tree

8 files changed

+226
-128
lines changed

8 files changed

+226
-128
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
66

77
## [Unreleased]
88
### Added
9+
- `ArduinoInstallation` class for managing lib / executable paths
10+
- `DisplayManager` class for managing Xvfb instance if needed
911

1012
### Changed
1113

lib/arduino_ci.rb

Lines changed: 1 addition & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,8 @@
11
require "arduino_ci/version"
2-
3-
require 'singleton'
4-
5-
# Cross-platform way of finding an executable in the $PATH.
6-
# via https://stackoverflow.com/a/5471032/2063546
7-
# which('ruby') #=> /usr/bin/ruby
8-
def which(cmd)
9-
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
10-
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
11-
exts.each do |ext|
12-
exe = File.join(path, "#{cmd}#{ext}")
13-
return exe if File.executable?(exe) && !File.directory?(exe)
14-
end
15-
end
16-
nil
17-
end
2+
require "arduino_ci/arduino_cmd"
183

194
# ArduinoCI contains classes for automated testing of Arduino code on the command line
205
# @author Ian Katz <ifreecarve@gmail.com>
216
module ArduinoCI
227

23-
# Wrap the Arduino executable. This requires, in some cases, a faked display.
24-
class ArduinoCmd
25-
26-
# create as many ArduinoCmds as you like, but we need one and only one display manager
27-
class DisplayMgr
28-
include Singleton
29-
attr_reader :enabled
30-
31-
def initialize
32-
@existing = existing_display?
33-
@enabled = false
34-
@pid = nil
35-
end
36-
37-
# attempt to determine if the machine is running a graphical display (i.e. not Travis)
38-
def existing_display?
39-
return true if RUBY_PLATFORM.include? "darwin"
40-
return true if ENV["DISPLAY"].nil?
41-
return true if ENV["DISPLAY"].include? ":"
42-
false
43-
end
44-
45-
# enable a virtual display
46-
def enable
47-
return @enabled = true if @existing # silent no-op if built in display
48-
return unless @pid.nil?
49-
50-
@enabled = true
51-
@pid = fork do
52-
puts "Forking Xvfb"
53-
system("Xvfb", ":1", "-ac", "-screen", "0", "1280x1024x16")
54-
puts "Xvfb unexpectedly quit!"
55-
end
56-
sleep(3) # TODO: test a connection to the X server?
57-
end
58-
59-
# disable the virtual display
60-
def disable
61-
return @enabled = false if @existing # silent no-op if built in display
62-
return if @pid.nil?
63-
64-
begin
65-
Process.kill 9, @pid
66-
ensure
67-
Process.wait @pid
68-
@pid = nil
69-
end
70-
puts "Xvfb killed"
71-
end
72-
73-
# Enable a virtual display for the duration of the given block
74-
def with_display
75-
enable
76-
begin
77-
yield environment
78-
ensure
79-
disable
80-
end
81-
end
82-
83-
def environment
84-
return nil unless @existing || @enabled
85-
return {} if @existing
86-
{ DISPLAY => ":1.0" }
87-
end
88-
89-
# On finalize, ensure child process is ended
90-
def self.finalize
91-
disable
92-
end
93-
end
94-
95-
class << self
96-
protected :new
97-
98-
# attempt to find a workable Arduino executable across platforms
99-
def guess_executable_location
100-
osx_place = "/Applications/Arduino.app/Contents/MacOS/Arduino"
101-
places = {
102-
"arduino" => !which("arduino").nil?,
103-
osx_place => (File.exist? osx_place),
104-
}
105-
places.each { |k, v| return k if v }
106-
nil
107-
end
108-
109-
def autolocate
110-
ret = new
111-
ret.path = guess_executable_location
112-
ret
113-
end
114-
end
115-
116-
attr_accessor :path
117-
118-
def initialize
119-
@display_mgr = DisplayMgr::instance
120-
end
121-
122-
end
123-
1248
end

lib/arduino_ci/arduino_cmd.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
require 'arduino_ci/display_manager'
2+
require 'arduino_ci/arduino_installation'
3+
4+
module ArduinoCI
5+
6+
# Wrap the Arduino executable. This requires, in some cases, a faked display.
7+
class ArduinoCmd
8+
9+
class << self
10+
protected :new
11+
12+
def autolocate
13+
new(ArduinoInstallation.autolocate)
14+
end
15+
16+
def autolocate!
17+
new(ArduinoInstallation.autolocate!)
18+
end
19+
20+
end
21+
22+
attr_accessor :installation
23+
24+
def initialize(installation)
25+
@display_mgr = DisplayManager::instance
26+
@installation = installation
27+
end
28+
29+
def board_installed?(board)
30+
system(@installation, "--board", board)
31+
end
32+
33+
end
34+
end
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Cross-platform way of finding an executable in the $PATH.
2+
# via https://stackoverflow.com/a/5471032/2063546
3+
# which('ruby') #=> /usr/bin/ruby
4+
def which(cmd)
5+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
6+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
7+
exts.each do |ext|
8+
exe = File.join(path, "#{cmd}#{ext}")
9+
return exe if File.executable?(exe) && !File.directory?(exe)
10+
end
11+
end
12+
nil
13+
end
14+
15+
module ArduinoCI
16+
17+
# Manage the OS-specific install location of Arduino
18+
class ArduinoInstallation
19+
attr_accessor :cmd_path
20+
attr_accessor :lib_dir
21+
22+
class << self
23+
def force_install_location
24+
File.join(ENV['HOME'], 'arduino_ci_ide')
25+
end
26+
27+
# attempt to find a workable Arduino executable across platforms
28+
def autolocate
29+
ret = new
30+
31+
osx_place = "/Applications/Arduino.app/Contents/MacOS/Arduino"
32+
if File.exist? osx_place
33+
ret.cmd_path = File.join(osx_place, "arduino")
34+
ret.lib_dir = File.join(osx_place, "Libraries")
35+
return ret
36+
end
37+
38+
posix_place = which("arduino")
39+
unless posix_place.nil?
40+
ret.cmd_path = posix_place
41+
ret.lib_dir = File.join(ENV['HOME'], "Sketchbook") # assume linux
42+
# https://learn.adafruit.com/adafruit-all-about-arduino-libraries-install-use/how-to-install-a-library
43+
return ret
44+
end
45+
46+
if File.exist? force_install_location
47+
ret.cmd_path = File.join(force_install_location, "arduino")
48+
ret.lib_dir = File.join(force_install_location, "libraries")
49+
# TODO: "libraries" is what's in the adafruit install.sh script
50+
return ret
51+
end
52+
53+
ret
54+
end
55+
56+
# Attempt to find a workable Arduino executable across platforms, and install it if we don't
57+
def autolocate!
58+
candidate = autolocate
59+
return candidate unless candidate.cmd_path.nil?
60+
# force the install
61+
62+
if force_install
63+
candidate.cmd_path = File.join(force_install_location, "arduino")
64+
candidate.lib_dir = File.join(force_install_location, "libraries")
65+
end
66+
candidate
67+
end
68+
69+
def force_install
70+
system("wget", "https://downloads.arduino.cc/arduino-1.6.5-linux64.tar.xz")
71+
system("tar", "xf", "arduino-1.6.5-linux64.tar.xz")
72+
system("mv", "arduino-1.6.5", force_install_location)
73+
end
74+
75+
end
76+
end
77+
end

lib/arduino_ci/display_manager.rb

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
require 'singleton'
2+
3+
module ArduinoCI
4+
5+
# When arduino commands run, they need a graphical display.
6+
# This class handles the setup of that display, if needed.
7+
class DisplayManager
8+
include Singleton
9+
attr_reader :enabled
10+
11+
def initialize
12+
@existing = existing_display?
13+
@enabled = false
14+
@pid = nil
15+
end
16+
17+
# attempt to determine if the machine is running a graphical display (i.e. not Travis)
18+
def existing_display?
19+
return true if RUBY_PLATFORM.include? "darwin"
20+
return true if ENV["DISPLAY"].nil?
21+
return true if ENV["DISPLAY"].include? ":"
22+
false
23+
end
24+
25+
# enable a virtual display
26+
def enable
27+
return @enabled = true if @existing # silent no-op if built in display
28+
return unless @pid.nil?
29+
30+
@enabled = true
31+
@pid = fork do
32+
puts "Forking Xvfb"
33+
system("Xvfb", ":1", "-ac", "-screen", "0", "1280x1024x16")
34+
puts "Xvfb unexpectedly quit!"
35+
end
36+
sleep(3) # TODO: test a connection to the X server?
37+
end
38+
39+
# disable the virtual display
40+
def disable
41+
return @enabled = false if @existing # silent no-op if built in display
42+
return if @pid.nil?
43+
44+
begin
45+
Process.kill 9, @pid
46+
ensure
47+
Process.wait @pid
48+
@pid = nil
49+
end
50+
puts "Xvfb killed"
51+
end
52+
53+
# Enable a virtual display for the duration of the given block
54+
def with_display
55+
enable
56+
begin
57+
yield environment
58+
ensure
59+
disable
60+
end
61+
end
62+
63+
def environment
64+
return nil unless @existing || @enabled
65+
return {} if @existing
66+
{ DISPLAY => ":1.0" }
67+
end
68+
69+
# On finalize, ensure child process is ended
70+
def self.finalize
71+
disable
72+
end
73+
end
74+
end

spec/arduino_cmd_spec.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
require "spec_helper"
2+
3+
RSpec.describe ArduinoCI::ArduinoCmd do
4+
it "Finds the Arduino executable" do
5+
arduino_cmd = ArduinoCI::ArduinoCmd.autolocate
6+
# expect(arduino_cmd.path).not_to be nil
7+
end
8+
end
9+

spec/arduino_installation_spec.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require "spec_helper"
2+
3+
RSpec.describe ArduinoCI::ArduinoInstallation do
4+
context "force_install_location" do
5+
it "is resolvable" do
6+
expect(ArduinoCI::ArduinoInstallation.force_install_location).not_to be nil
7+
end
8+
end
9+
10+
context "autolocate" do
11+
it "doesn't fail" do
12+
ArduinoCI::ArduinoInstallation.autolocate
13+
end
14+
end
15+
16+
context "autolocate!" do
17+
it "doesn't fail" do
18+
installation = ArduinoCI::ArduinoInstallation.autolocate!
19+
expect(installation.cmd_path).not_to be nil
20+
expect(installation.lib_dir).not_to be nil
21+
end
22+
end
23+
24+
end
25+

spec/arduino_exec_spec.rb renamed to spec/display_manager_spec.rb

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,20 @@
11
require "spec_helper"
22

3-
RSpec.describe ArduinoCI::ArduinoCmd do
4-
it "Finds the Arduino executable" do
5-
arduino_cmd = ArduinoCI::ArduinoCmd.autolocate
6-
# expect(arduino_cmd.path).not_to be nil
7-
end
8-
end
9-
10-
RSpec.describe ArduinoCI::ArduinoCmd::DisplayMgr do
3+
RSpec.describe ArduinoCI::DisplayManager do
114
context "singleton ::instance" do
125
it "produces an instance" do
13-
expect(ArduinoCI::ArduinoCmd::DisplayMgr::instance).not_to be_nil
6+
expect(ArduinoCI::DisplayManager::instance).not_to be_nil
147
end
158
end
169

1710
context "with_display" do
1811
it "Properly enables and disables" do
19-
manager = ArduinoCI::ArduinoCmd::DisplayMgr::instance
12+
manager = ArduinoCI::DisplayManager::instance
2013
expect(manager.enabled).to be false
2114
manager.with_display do |environment|
2215
expect(manager.enabled).to be true
2316
expect(environment.class).to eq(Hash)
24-
also_manager = ArduinoCI::ArduinoCmd::DisplayMgr::instance
17+
also_manager = ArduinoCI::DisplayManager::instance
2518
expect(also_manager.enabled).to be true
2619
end
2720
expect(manager.enabled).to be false

0 commit comments

Comments
 (0)