Permalink
Browse files

display number autopicking, and handling non-accessible Xvfb pidfile,…

… as per #22
  • Loading branch information...
1 parent 6bdebc2 commit 10249fecbf2ca2f2d3e410f75901a18b5f3b00f6 @leonid-shevtsov committed Mar 29, 2012
Showing with 92 additions and 18 deletions.
  1. +38 −11 lib/headless.rb
  2. +54 −7 spec/headless_spec.rb
View
@@ -41,6 +41,9 @@
#++
class Headless
+ DEFAULT_DISPLAY_NUMBER = 99
+ DEFAULT_DISPLAY_DIMENSIONS = '1280x1024x24'
+
class Exception < RuntimeError
end
@@ -54,26 +57,21 @@ class Exception < RuntimeError
#
# List of available options:
# * +display+ (default 99) - what display number to listen to;
- # * +reuse+ (default true) - if given display server already exists, should we use it or fail miserably?
+ # * +reuse+ (default true) - if given display server already exists, should we use it or try another?
+ # * +autopick+ (default true is display number isn't explicitly set) - if Headless should automatically pick a display, or fail if the given one is not available.
# * +dimensions+ (default 1280x1024x24) - display dimensions and depth. Not all combinations are possible, refer to +man Xvfb+.
# * +destroy_at_exit+ (default true) - if a display is started but not stopped, should it be destroyed when the script finishes?
def initialize(options = {})
CliUtil.ensure_application_exists!('Xvfb', 'Xvfb not found on your system')
- @display = options.fetch(:display, 99).to_i
+ @display = options.fetch(:display, DEFAULT_DISPLAY_NUMBER).to_i
+ @autopick_display = options.fetch(:autopick, !options.key?(:display))
@reuse_display = options.fetch(:reuse, true)
- @dimensions = options.fetch(:dimensions, '1280x1024x24')
+ @dimensions = options.fetch(:dimensions, DEFAULT_DISPLAY_DIMENSIONS)
@video_capture_options = options.fetch(:video, {})
@destroy_at_exit = options.fetch(:destroy_at_exit, true)
- #TODO more logic here, autopicking the display number
- if @reuse_display
- launch_xvfb unless xvfb_running?
- elsif xvfb_running?
- raise Headless::Exception.new("Display :#{display} is already taken and reuse=false")
- else
- launch_xvfb
- end
+ attach_xvfb
end
# Switches to the headless server
@@ -120,6 +118,35 @@ def take_screenshot(file_path)
private
+ def attach_xvfb
+ # TODO this loop isn't elegant enough
+ success = false
+ while !success && @display<10000
+ begin
+ if !xvfb_running?
+ launch_xvfb
+ success=true
+ else
+ success = @reuse_display
+ end
+ rescue Errno::EPERM
+ # No permission to read pid file
+ success = false
+ end
+
+ # TODO this is crufty
+ if @autopick_display
+ @display += 1 unless success
+ else
+ break
+ end
+ end
+
+ unless success
+ raise Headless::Exception.new("Display :#{display} is already taken and reuse=false")
+ end
+ end
+
def launch_xvfb
#TODO error reporting
result = system "#{CliUtil.path_to("Xvfb")} :#{display} -screen 0 #{dimensions} -ac >/dev/null 2>&1 &"
View
@@ -6,7 +6,7 @@
stub_environment
end
- context "instaniation" do
+ context "instantiation" do
context "when Xvfb is not installed" do
before do
Headless::CliUtil.stub!(:application_exists?).and_return(false)
@@ -33,16 +33,63 @@
context "when Xvfb is already running" do
before do
- Headless::CliUtil.stub!(:read_pid).and_return(31337)
+ Headless::CliUtil.stub!(:read_pid).with('/tmp/.X99-lock').and_return(31337)
+ Headless::CliUtil.stub!(:read_pid).with('/tmp/.X100-lock').and_return(nil)
end
- it "raises an error if reuse display is not allowed" do
- lambda { Headless.new(:reuse => false) }.should raise_error(Headless::Exception)
+ context "and display reuse is allowed" do
+ let(:options) { {:reuse => true} }
+
+ it "should reuse the existing Xvfb" do
+ Headless.new(options).display.should == 99
+ end
+ end
+
+ context "and display reuse is not allowed" do
+ let(:options) { {:reuse => false} }
+
+ it "should pick the next available display number" do
+ Headless.new(options).display.should == 100
+ end
+
+ context "and display number is explicitly set" do
+ let(:options) { {:reuse => false, :display => 99} }
+
+ it "should fail with an exception" do
+ lambda { Headless.new(options) }.should raise_error(Headless::Exception)
+ end
+
+ context "and autopicking is allowed" do
+ let(:options) { {:reuse => false, :display => 99, :autopick => true} }
+
+ it "should pick the next available display number" do
+ Headless.new(options).display.should == 100
+ end
+ end
+ end
+ end
+ end
+
+ context 'when Xvfb is started, but by another user' do
+ before do
+ Headless::CliUtil.stub!(:read_pid).with('/tmp/.X99-lock') { raise Errno::EPERM }
+ Headless::CliUtil.stub!(:read_pid).with('/tmp/.X100-lock').and_return(nil)
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)
+ context "and display autopicking is not allowed" do
+ let(:options) { {:autopick => false} }
+
+ it "should fail with and exception" do
+ lambda { Headless.new(options) }.should raise_error(Headless::Exception)
+ end
+ end
+
+ context "and display autopicking is allowed" do
+ let(:options) { {:autopick => true} }
+
+ it "should pick the next display number" do
+ Headless.new(options).display.should == 100
+ end
end
end
end

0 comments on commit 10249fe

Please sign in to comment.