headless 0.3.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG CHANGED
@@ -1,3 +1,8 @@
1
+ ## 1.0.0 (2013-01-28)
2
+
3
+ * bugfix release
4
+ * version number compliant to the [semantic versioning system](http://semver.org)
5
+
1
6
  ## 0.3.1 (2012-03-29)
2
7
 
3
8
  * added autopicking of display number, if the requested one is already taken
data/README.md CHANGED
@@ -10,6 +10,8 @@ Documentation is available at [rdoc.info](http://rdoc.info/projects/leonid-shevt
10
10
 
11
11
  [Changelog](https://github.com/leonid-shevtsov/headless/blob/master/CHANGELOG)
12
12
 
13
+ **Note: Headless will NOT hide most applications on OS X. [Here is a detailed explanation](https://github.com/leonid-shevtsov/headless/issues/31#issuecomment-8933108)**
14
+
13
15
  ## Installation
14
16
 
15
17
  On Debian/Ubuntu:
@@ -59,6 +61,27 @@ Running cucumber headless is now as simple as adding a before and after hook in
59
61
  headless.start
60
62
  end
61
63
 
64
+ ## Running tests in parallel
65
+
66
+ If you have multiple threads running acceptance tests in parallel, you want to spawn Headless before forking, and then reuse that instance with `destroy_at_exit: false`.
67
+ You can even spawn a Headless instance in one ruby script, and then reuse the same instance in other scripts by specifying the same display number and `reuse: true`.
68
+
69
+ # spawn_headless.rb
70
+ Headless.new(display: 100, destroy_at_exit: false).start
71
+
72
+
73
+ # test_suite_that_could_be_ran_multiple_times.rb
74
+ headless = Headless.new(display: 100, reuse: true, destroy_at_exit: false)
75
+ # Xvfb is already started by the first script
76
+
77
+ # reap_headless.rb
78
+ headless = Headless.new(display: 100, reuse: true)
79
+ headless.destroy
80
+
81
+
82
+
83
+
84
+
62
85
  ## Cucumber with wkhtmltopdf
63
86
 
64
87
  _Note: this is true for other programs which may use headless at the same time as cucumber is running_
@@ -3,7 +3,7 @@ spec = Gem::Specification.new do |s|
3
3
  s.email = 'leonid@shevtsov.me'
4
4
 
5
5
  s.name = 'headless'
6
- s.version = '0.3.1'
6
+ s.version = '1.0.0'
7
7
  s.summary = 'Ruby headless display interface'
8
8
 
9
9
  s.description = <<-EOF
@@ -14,5 +14,6 @@ spec = Gem::Specification.new do |s|
14
14
 
15
15
  s.files = `git ls-files`.split("\n")
16
16
 
17
+ s.add_development_dependency 'rake'
17
18
  s.add_development_dependency "rspec", "~> 2.6"
18
19
  end
@@ -15,11 +15,11 @@ require 'headless/video/video_recorder'
15
15
  # require 'rubygems'
16
16
  # require 'headless'
17
17
  # require 'selenium-webdriver'
18
- #
18
+ #
19
19
  # Headless.ly do
20
20
  # driver = Selenium::WebDriver.for :firefox
21
21
  # driver.navigate.to 'http://google.com'
22
- # puts driver.title
22
+ # puts driver.title
23
23
  # end
24
24
  #
25
25
  # Object mode:
@@ -33,7 +33,7 @@ require 'headless/video/video_recorder'
33
33
  #
34
34
  # driver = Selenium::WebDriver.for :firefox
35
35
  # driver.navigate.to 'http://google.com'
36
- # puts driver.title
36
+ # puts driver.title
37
37
  #
38
38
  # headless.destroy
39
39
  #--
@@ -42,6 +42,7 @@ require 'headless/video/video_recorder'
42
42
  class Headless
43
43
 
44
44
  DEFAULT_DISPLAY_NUMBER = 99
45
+ MAX_DISPLAY_NUMBER = 10_000
45
46
  DEFAULT_DISPLAY_DIMENSIONS = '1280x1024x24'
46
47
 
47
48
  class Exception < RuntimeError
@@ -102,6 +103,7 @@ class Headless
102
103
  headless = Headless.new(options)
103
104
  headless.start
104
105
  yield headless
106
+ ensure
105
107
  headless.destroy
106
108
  end
107
109
  class <<self; alias_method :ly, :run; end
@@ -119,38 +121,28 @@ class Headless
119
121
  private
120
122
 
121
123
  def attach_xvfb
122
- # TODO this loop isn't elegant enough
123
- success = false
124
- while !success && @display<10000
125
- begin
126
- if !xvfb_running?
127
- launch_xvfb
128
- success=true
129
- else
130
- success = @reuse_display
131
- end
132
- rescue Errno::EPERM
133
- # No permission to read pid file
134
- success = false
135
- end
124
+ possible_display_set = @autopick_display ? @display..MAX_DISPLAY_NUMBER : Array(@display)
125
+ pick_available_display(possible_display_set, @reuse_display)
126
+ end
136
127
 
137
- # TODO this is crufty
138
- if @autopick_display
139
- @display += 1 unless success
140
- else
141
- break
128
+ def pick_available_display(display_set, can_reuse)
129
+ display_set.each do |display_number|
130
+ @display = display_number
131
+ begin
132
+ return true if xvfb_running? && can_reuse
133
+ return true if !xvfb_running? && launch_xvfb
134
+ rescue Errno::EPERM # display not accessible
135
+ next
142
136
  end
143
137
  end
144
-
145
- unless success
146
- raise Headless::Exception.new("Display :#{display} is already taken and reuse=false")
147
- end
138
+ raise Headless::Exception.new("Could not find an available display")
148
139
  end
149
140
 
150
141
  def launch_xvfb
151
142
  #TODO error reporting
152
143
  result = system "#{CliUtil.path_to("Xvfb")} :#{display} -screen 0 #{dimensions} -ac >/dev/null 2>&1 &"
153
144
  raise Headless::Exception.new("Xvfb did not launch - something's wrong") unless result
145
+ return true
154
146
  end
155
147
 
156
148
  def xvfb_running?
@@ -164,7 +156,7 @@ private
164
156
  def read_xvfb_pid
165
157
  CliUtil.read_pid(pid_filename)
166
158
  end
167
-
159
+
168
160
  def hook_at_exit
169
161
  unless @at_exit_hook_installed
170
162
  @at_exit_hook_installed = true
@@ -3,7 +3,7 @@ class Headless
3
3
  def self.application_exists?(app)
4
4
  `which #{app}`.strip != ""
5
5
  end
6
-
6
+
7
7
  def self.ensure_application_exists!(app, error_message)
8
8
  if !self.application_exists?(app)
9
9
  raise Headless::Exception.new(error_message)
@@ -36,7 +36,6 @@ class Headless
36
36
  exec command
37
37
  exit! 127 # safeguard in case exec fails
38
38
  end
39
- Process.detach(pid)
40
39
 
41
40
  File.open pid_filename, 'w' do |f|
42
41
  f.puts pid
@@ -14,6 +14,7 @@ class Headless
14
14
  @tmp_file_path = options.fetch(:tmp_file_path, "/tmp/.headless_ffmpeg_#{@display}.mov")
15
15
  @log_file_path = options.fetch(:log_file_path, "/dev/null")
16
16
  @codec = options.fetch(:codec, "qtrle")
17
+ @frame_rate = options.fetch(:frame_rate, 30)
17
18
  end
18
19
 
19
20
  def capture_running?
@@ -21,7 +22,7 @@ class Headless
21
22
  end
22
23
 
23
24
  def start_capture
24
- CliUtil.fork_process("#{CliUtil.path_to('ffmpeg')} -y -r 30 -g 600 -s #{@dimensions} -f x11grab -i :#{@display} -vcodec #{@codec} #{@tmp_file_path}", @pid_file_path, @log_file_path)
25
+ CliUtil.fork_process("#{CliUtil.path_to('ffmpeg')} -y -r #{@frame_rate} -g 600 -s #{@dimensions} -f x11grab -i :#{@display} -vcodec #{@codec} #{@tmp_file_path}", @pid_file_path, @log_file_path)
25
26
  at_exit do
26
27
  exit_status = $!.status if $!.is_a?(SystemExit)
27
28
  stop_and_discard
@@ -44,10 +44,10 @@ describe Headless do
44
44
  Headless.new(options).display.should == 99
45
45
  end
46
46
  end
47
-
47
+
48
48
  context "and display reuse is not allowed" do
49
49
  let(:options) { {:reuse => false} }
50
-
50
+
51
51
  it "should pick the next available display number" do
52
52
  Headless.new(options).display.should == 100
53
53
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: headless
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,27 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-29 00:00:00.000000000 Z
12
+ date: 2013-01-28 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
14
30
  - !ruby/object:Gem::Dependency
15
31
  name: rspec
16
- requirement: &70170211681540 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
17
33
  none: false
18
34
  requirements:
19
35
  - - ~>
@@ -21,7 +37,12 @@ dependencies:
21
37
  version: '2.6'
22
38
  type: :development
23
39
  prerelease: false
24
- version_requirements: *70170211681540
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '2.6'
25
46
  description: ! ' Headless is a Ruby interface for Xvfb. It allows you to create
26
47
  a headless display straight from Ruby code, hiding some low-level action.
27
48
 
@@ -56,16 +77,22 @@ required_ruby_version: !ruby/object:Gem::Requirement
56
77
  - - ! '>='
57
78
  - !ruby/object:Gem::Version
58
79
  version: '0'
80
+ segments:
81
+ - 0
82
+ hash: 2986386947499728446
59
83
  required_rubygems_version: !ruby/object:Gem::Requirement
60
84
  none: false
61
85
  requirements:
62
86
  - - ! '>='
63
87
  - !ruby/object:Gem::Version
64
88
  version: '0'
89
+ segments:
90
+ - 0
91
+ hash: 2986386947499728446
65
92
  requirements:
66
93
  - Xvfb
67
94
  rubyforge_project:
68
- rubygems_version: 1.8.17
95
+ rubygems_version: 1.8.24
69
96
  signing_key:
70
97
  specification_version: 3
71
98
  summary: Ruby headless display interface