headless 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8dc1c06f85049924c12fc40b4558040fd514c01a
4
- data.tar.gz: a5e904086bdccf72798574e2450ce76b64fda0a7
3
+ metadata.gz: d30b463017cd61d3b19e47c94a04e9aa4061acc7
4
+ data.tar.gz: d8b07cc50c12a80ad547a8214a530cffa55db8f0
5
5
  SHA512:
6
- metadata.gz: d6ad825721817e789fde8d9786bac8fec26ef097e5fb4af74f8c685a9895dfa3b5850161f3bf8f809b5024a7827c08f35dfd131d843e2f0cc71b274b4197ddea
7
- data.tar.gz: af0c4aea4a57b14f56868315a1874bf28a09e7ab8a4a91355d0b864e09e939794aa9eedcf9ced54692c79dc428ae5a30003ea43afaa097edaaf75b06ee85fd5f
6
+ metadata.gz: fadb8ac4e38bb5ce3dc9f2f8245d3a13055e5d384d8aafb6ef87aecfaed732505d0017054a0d7d84c58c9854456aa01015bd83e3fdfb0e047a0ef40217a13dab
7
+ data.tar.gz: 5574ded55ca457df6835da69994911da9be288fb1ab1f943e0ff3215d52d1618596876f042325b08c9e1ecc7d299de0f4542a001f654f1b81a1180cc86bfe098
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ ## 2.2.0 (2015-07-05)
2
+
3
+ * Allow reuse of displays started by other user (from @marxarelli)
4
+ * Add support for graphicsmagick instead of ImageMagick (from @BlakeMesdag)
5
+ * Wait for Xvfb to finish when destroying it, to avoid creating zombie processes (from @samnissen)
6
+
1
7
  ## 2.1.0 (2015-05-10)
2
8
 
3
9
  * Allow path to video recorder binary to be customized (from @briandamaged)
data/README.md CHANGED
@@ -83,8 +83,13 @@ Headless.new(display: 100, reuse: true, destroy_at_exit: false).start
83
83
  # reap_headless.rb
84
84
  headless = Headless.new(display: 100, reuse: true)
85
85
  headless.destroy
86
+
87
+ # kill_headless_without_waiting.rb
88
+ headless = Headless.new
89
+ headless.destroy_without_sync
86
90
  ```
87
-
91
+
92
+ There's also a different approach that creates a new virtual display for every parallel test process - see [this implementation](https://gist.github.com/rosskevin/5937888) by @rosskevin.
88
93
 
89
94
  ## Cucumber with wkhtmltopdf
90
95
 
@@ -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 = '2.1.0'
6
+ s.version = '2.2.0'
7
7
  s.summary = 'Ruby headless display interface'
8
8
 
9
9
  s.description = <<-EOF
@@ -101,15 +101,24 @@ class Headless
101
101
  end
102
102
 
103
103
  # Switches back from the headless server and terminates the headless session
104
+ # while waiting for Xvfb process to terminate.
104
105
  def destroy
105
106
  stop
106
- CliUtil.kill_process(pid_filename, preserve_pid_file: true)
107
+ CliUtil.kill_process(pid_filename, preserve_pid_file: true, wait: true)
107
108
  end
108
109
 
109
- # Same as destroy, but waits for Xvfb process to terminate
110
+ # Deprecated.
111
+ # Same as destroy.
112
+ # Kept for backward compatibility in June 2015.
110
113
  def destroy_sync
114
+ destroy
115
+ end
116
+
117
+ # Same as the old destroy function -- doesn't wait for Xvfb to die.
118
+ # Can cause zombies: http://stackoverflow.com/a/31003621/1651458
119
+ def destroy_without_sync
111
120
  stop
112
- CliUtil.kill_process(pid_filename, preserve_pid_file: true, wait: true)
121
+ CliUtil.kill_process(pid_filename, preserve_pid_file: true)
113
122
  end
114
123
 
115
124
  # Block syntax:
@@ -133,12 +142,16 @@ class Headless
133
142
 
134
143
  def take_screenshot(file_path, options={})
135
144
  using = options.fetch(:using, :imagemagick)
136
- if using == :imagemagick
145
+ case using
146
+ when :imagemagick
137
147
  CliUtil.ensure_application_exists!('import', "imagemagick is not found on your system. Please install it using sudo apt-get install imagemagick")
138
148
  system "#{CliUtil.path_to('import')} -display localhost:#{display} -window root #{file_path}"
139
- elsif using == :xwd
149
+ when :xwd
140
150
  CliUtil.ensure_application_exists!('xwd', "xwd is not found on your system. Please install it using sudo apt-get install X11-apps")
141
151
  system "#{CliUtil.path_to('xwd')} -display localhost:#{display} -silent -root -out #{file_path}"
152
+ when :graphicsmagick, :gm
153
+ CliUtil.ensure_application_exists!('gm', "graphicsmagick is not found on your system. Please install it.")
154
+ system "#{CliUtil.path_to('gm')} import -display localhost:#{display} -window root #{file_path}"
142
155
  else
143
156
  raise Headless::Exception.new('Unknown :using option value')
144
157
  end
@@ -154,12 +167,9 @@ private
154
167
  def pick_available_display(display_set, can_reuse)
155
168
  display_set.each do |display_number|
156
169
  @display = display_number
157
- begin
158
- return true if xvfb_running? && can_reuse
159
- return true if !xvfb_running? && launch_xvfb
160
- rescue Errno::EPERM # display not accessible
161
- next
162
- end
170
+
171
+ return true if xvfb_running? && can_reuse && (xvfb_mine? || !@autopick_display)
172
+ return true if !xvfb_running? && launch_xvfb
163
173
  end
164
174
  raise Headless::Exception.new("Could not find an available display")
165
175
  end
@@ -192,8 +202,12 @@ private
192
202
  end while !xvfb_running?
193
203
  end
194
204
 
205
+ def xvfb_mine?
206
+ CliUtil.process_mine?(read_xvfb_pid)
207
+ end
208
+
195
209
  def xvfb_running?
196
- !!read_xvfb_pid
210
+ (pid = read_xvfb_pid) && CliUtil.process_running?(pid)
197
211
  end
198
212
 
199
213
  def pid_filename
@@ -14,20 +14,21 @@ class Headless
14
14
  `which #{app}`.strip
15
15
  end
16
16
 
17
- def self.read_pid(pid_filename)
18
- pid = (File.read(pid_filename) rescue "").strip.to_i
19
- pid = nil if pid.zero?
17
+ def self.process_mine?(pid)
18
+ Process.kill(0, pid) && true
19
+ rescue Errno::EPERM, Errno::ESRCH
20
+ false
21
+ end
20
22
 
21
- if pid
22
- begin
23
- Process.kill(0, pid)
24
- pid
25
- rescue Errno::ESRCH
26
- nil
27
- end
28
- else
29
- nil
30
- end
23
+ def self.process_running?(pid)
24
+ Process.getpgid(pid) && true
25
+ rescue Errno::ESRCH
26
+ false
27
+ end
28
+
29
+ def self.read_pid(pid_filename)
30
+ pid = (File.read(pid_filename) rescue "").strip
31
+ pid.empty? ? nil : pid.to_i
31
32
  end
32
33
 
33
34
  def self.fork_process(command, pid_filename, log_filename='/dev/null')
@@ -43,7 +44,7 @@ class Headless
43
44
  end
44
45
 
45
46
  def self.kill_process(pid_filename, options={})
46
- if pid = self.read_pid(pid_filename)
47
+ if pid = read_pid(pid_filename)
47
48
  begin
48
49
  Process.kill 'TERM', pid
49
50
  Process.wait pid if options[:wait]
@@ -38,9 +38,12 @@ describe Headless do
38
38
  end
39
39
  end
40
40
 
41
- context "when Xvfb is already running" do
41
+ context "when Xvfb is already running and was started by this user" do
42
42
  before do
43
43
  allow(Headless::CliUtil).to receive(:read_pid).with('/tmp/.X99-lock').and_return(31337)
44
+ allow(Headless::CliUtil).to receive(:process_running?).with(31337).and_return(true)
45
+ allow(Headless::CliUtil).to receive(:process_mine?).with(31337).and_return(true)
46
+
44
47
  allow(Headless::CliUtil).to receive(:read_pid).with('/tmp/.X100-lock').and_return(nil)
45
48
  end
46
49
 
@@ -79,15 +82,18 @@ describe Headless do
79
82
 
80
83
  context 'when Xvfb is started, but by another user' do
81
84
  before do
82
- allow(Headless::CliUtil).to receive(:read_pid).with('/tmp/.X99-lock') { raise Errno::EPERM }
85
+ allow(Headless::CliUtil).to receive(:read_pid).with('/tmp/.X99-lock').and_return(31337)
86
+ allow(Headless::CliUtil).to receive(:process_running?).with(31337).and_return(true)
87
+ allow(Headless::CliUtil).to receive(:process_mine?).with(31337).and_return(false)
88
+
83
89
  allow(Headless::CliUtil).to receive(:read_pid).with('/tmp/.X100-lock').and_return(nil)
84
90
  end
85
91
 
86
92
  context "and display autopicking is not allowed" do
87
93
  let(:options) { {:autopick => false} }
88
94
 
89
- it "should fail with and exception" do
90
- expect { Headless.new(options) }.to raise_error(Headless::Exception)
95
+ it "should reuse the display" do
96
+ expect(Headless.new(options).display).to eq 99
91
97
  end
92
98
  end
93
99
 
@@ -176,6 +182,18 @@ describe Headless do
176
182
  expect { headless.take_screenshot('a.png', :using => :xwd) }.to raise_error(Headless::Exception)
177
183
  end
178
184
 
185
+ it "raises an error if gm is not installed with using: :graphicsmagick" do
186
+ allow(Headless::CliUtil).to receive(:application_exists?).with('gm').and_return(false)
187
+
188
+ expect { headless.take_screenshot('a.png', :using => :graphicsmagick) }.to raise_error(Headless::Exception)
189
+ end
190
+
191
+ it "raises an error if gm is not installed with using: :gm" do
192
+ allow(Headless::CliUtil).to receive(:application_exists?).with('gm').and_return(false)
193
+
194
+ expect { headless.take_screenshot('a.png', :using => :gm) }.to raise_error(Headless::Exception)
195
+ end
196
+
179
197
  it "issues command to take screenshot, with default options" do
180
198
  allow(Headless::CliUtil).to receive(:path_to).with('import').and_return('path/import')
181
199
  expect(headless).to receive(:system).with("path/import -display localhost:99 -window root /tmp/image.png")
@@ -193,6 +211,18 @@ describe Headless do
193
211
  expect(headless).to receive(:system).with("path/xwd -display localhost:99 -silent -root -out /tmp/image.png")
194
212
  headless.take_screenshot("/tmp/image.png", :using => :xwd)
195
213
  end
214
+
215
+ it "issues command to take screenshot, with using: :graphicsmagick" do
216
+ allow(Headless::CliUtil).to receive(:path_to).with('gm').and_return('path/gm')
217
+ expect(headless).to receive(:system).with("path/gm import -display localhost:99 -window root /tmp/image.png")
218
+ headless.take_screenshot("/tmp/image.png", :using => :graphicsmagick)
219
+ end
220
+
221
+ it "issues command to take screenshot, with using: :gm" do
222
+ allow(Headless::CliUtil).to receive(:path_to).with('gm').and_return('path/gm')
223
+ expect(headless).to receive(:system).with("path/gm import -display localhost:99 -window root /tmp/image.png")
224
+ headless.take_screenshot("/tmp/image.png", :using => :gm)
225
+ end
196
226
  end
197
227
  end
198
228
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: headless
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leonid Shevtsov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-10 00:00:00.000000000 Z
11
+ date: 2015-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -98,4 +98,3 @@ signing_key:
98
98
  specification_version: 4
99
99
  summary: Ruby headless display interface
100
100
  test_files: []
101
- has_rdoc: