Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Added specs. Bundler.
  • Loading branch information
iafonov committed Aug 16, 2011
1 parent 98546d0 commit 7158f08
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 20 deletions.
4 changes: 4 additions & 0 deletions Gemfile
@@ -0,0 +1,4 @@
source "http://rubygems.org"

# Specify your gem's dependencies in ci_util.gemspec
gemspec
1 change: 1 addition & 0 deletions Rakefile
@@ -0,0 +1 @@
require "bundler/gem_tasks"
5 changes: 4 additions & 1 deletion .gemspec → headless.gemspec
Expand Up @@ -3,7 +3,7 @@ require 'rake'
spec = Gem::Specification.new do |s|
s.author = 'Leonid Shevtsov'
s.email = 'leonid@shevtsov.me'

s.name = 'headless'
s.version = '0.1.0'
s.summary = 'Ruby headless display interface'
Expand All @@ -16,4 +16,7 @@ spec = Gem::Specification.new do |s|

s.files = FileList['lib/*.rb', '[A-Z]*'].to_a
s.has_rdoc = true

s.add_development_dependency "rspec", "~> 2.6"
s.add_development_dependency "ruby-debug"
end
33 changes: 14 additions & 19 deletions lib/headless.rb
@@ -1,3 +1,5 @@
require 'headless/cli_util'

# A class incapsulating the creation and usage of a headless X server
#
# == Prerequisites
Expand Down Expand Up @@ -54,22 +56,20 @@ class Exception < ::Exception
# * +reuse+ (default true) - if given display server already exists, should we use it or fail miserably?
# * +dimensions+ (default 1280x1024x24) - display dimensions and depth. Not all combinations are possible, refer to +man Xvfb+.
def initialize(options = {})
find_xvfb
raise Exception.new("Xvfb not found on your system") unless CliUtil.application_exists?("Xvfb")

@display = options.fetch(:display, 99).to_i
@reuse_display = options.fetch(:reuse, true)
@dimensions = options.fetch(:dimensions, '1280x1024x24')

#TODO more logic here, autopicking the display number
if @reuse_display
launch_xvfb unless read_pid
elsif read_pid
launch_xvfb unless xvfb_running?
elsif xvfb_running?
raise Exception.new("Display :#{display} is already taken and reuse=false")
else
launch_xvfb
end

raise Exception.new("Xvfb did not launch - something's wrong") unless read_pid
end

# Switches to the headless server
Expand All @@ -86,7 +86,7 @@ def stop
# Switches back from the headless server and terminates the headless session
def destroy
stop
Process.kill('TERM', xvfb_pid) if read_pid
Process.kill('TERM', read_xvfb_pid) if xvfb_running?
end

# Block syntax:
Expand All @@ -101,27 +101,22 @@ def self.run(options={}, &block)
yield headless
headless.destroy
end

class <<self; alias_method :ly, :run; end

private
attr_reader :xvfb_pid

def find_xvfb
@xvfb = `which Xvfb`.strip
raise Exception.new('Xvfb not found on your system') if @xvfb == ''
end

def launch_xvfb
#TODO error reporting
system "#{@xvfb} :#{display} -screen 0 #{dimensions} -ac >/dev/null 2>&1 &"
sleep 1
result = system "#{CliUtil.path_to("Xvfb")} :#{display} -screen 0 #{dimensions} -ac >/dev/null 2>&1 &"
raise Exception.new("Xvfb did not launch - something's wrong") unless result
end

def xvfb_running?
read_xvfb_pid
end

def read_pid
@xvfb_pid=(File.read("/tmp/.X#{display}-lock") rescue "").strip.to_i
@xvfb_pid=nil if @xvfb_pid==0
@xvfb_pid
def read_xvfb_pid
#TODO maybe check that the process still exists
CliUtil.read_pid("/tmp/.X#{display}-lock")
end
end
36 changes: 36 additions & 0 deletions lib/headless/cli_util.rb
@@ -0,0 +1,36 @@
class CliUtil
def self.application_exists?(app)
`which #{app}`.strip == ""
end

def self.path_to(app)
`which #{app}`.strip
end

def self.read_pid(file)
pid = (File.read("/tmp/.X#{display}-lock") rescue "").strip.to_i
pid == 0 ? nil : pid
end

def self.fork_process(command, pid_file)
pid = fork do
exec command
exit! 127
end

File.open pid_file, 'w' do |f|
f.puts pid
end
end

def self.kill_process(pid_file)
if File.exist? pid_file
pid = File.read(pid_file).strip.to_i
Process.kill 'TERM', pid
FileUtils.rm pid_file
else
puts "#{pid_file} not found"
end
end

end
110 changes: 110 additions & 0 deletions spec/headless_spec.rb
@@ -0,0 +1,110 @@
require 'lib/headless'

describe Headless do
before do
ENV['DISPLAY'] = ":31337"
end

context "instaniation" do
context "when Xvfb is not installed" do
before do
CliUtil.stub!(:application_exists?).and_return(false)
end

it "raises an error" do
lambda { Headless.new }.should raise_error(Headless::Exception)
end
end

context "when Xvfb not started yet" do
before do
stub_environment
end

it "starts Xvfb" do
Headless.any_instance.should_receive(:system).with("/usr/bin/Xvfb :99 -screen 0 1280x1024x24 -ac >/dev/null 2>&1 &").and_return(true)

headless = Headless.new
end

it "allows setting screen dimensions" do
Headless.any_instance.should_receive(:system).with("/usr/bin/Xvfb :99 -screen 0 1024x768x16 -ac >/dev/null 2>&1 &").and_return(true)

headless = Headless.new(:dimensions => "1024x768x16")
end
end

context "when Xvfb is already running" do
before do
stub_environment
CliUtil.stub!(:read_pid).and_return(31337)
end

it "raises an error if reuse display is not allowed" do
lambda { Headless.new(:reuse => false) }.should raise_error(Headless::Exception)
end

it "doesn't raise an error if reuse display is allowed" do
lambda { Headless.new(:reuse => true) }.should_not raise_error(Headless::Exception)
lambda { Headless.new }.should_not raise_error(Headless::Exception)
end
end
end

describe "#start" do
before do
stub_environment

@headless = Headless.new
end

it "switches to the headless server" do
ENV['DISPLAY'].should == ":31337"
@headless.start
ENV['DISPLAY'].should == ":99"
end
end

describe "#stop" do
before do
stub_environment

@headless = Headless.new
end

it "switches back from the headless server" do
ENV['DISPLAY'].should == ":31337"
@headless.start
ENV['DISPLAY'].should == ":99"
@headless.stop
ENV['DISPLAY'].should == ":31337"
end
end

describe "#destroy" do
before do
stub_environment
@headless = Headless.new

CliUtil.stub!(:read_pid).and_return(4444)
end

it "switches back from the headless server and terminates the headless session" do
Process.should_receive(:kill).with('TERM', 4444)

ENV['DISPLAY'].should == ":31337"
@headless.start
ENV['DISPLAY'].should == ":99"
@headless.destroy
ENV['DISPLAY'].should == ":31337"
end
end

private

def stub_environment
CliUtil.stub!(:application_exists?).and_return(true)
CliUtil.stub!(:read_pid).and_return(nil)
CliUtil.stub!(:path_to).and_return("/usr/bin/Xvfb")
end
end

0 comments on commit 7158f08

Please sign in to comment.