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 +4 -4
- data/CHANGELOG +6 -0
- data/README.md +6 -1
- data/headless.gemspec +1 -1
- data/lib/headless.rb +26 -12
- data/lib/headless/cli_util.rb +15 -14
- data/spec/headless_spec.rb +34 -4
- metadata +2 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d30b463017cd61d3b19e47c94a04e9aa4061acc7
|
4
|
+
data.tar.gz: d8b07cc50c12a80ad547a8214a530cffa55db8f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
|
data/headless.gemspec
CHANGED
data/lib/headless.rb
CHANGED
@@ -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
|
-
#
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
210
|
+
(pid = read_xvfb_pid) && CliUtil.process_running?(pid)
|
197
211
|
end
|
198
212
|
|
199
213
|
def pid_filename
|
data/lib/headless/cli_util.rb
CHANGED
@@ -14,20 +14,21 @@ class Headless
|
|
14
14
|
`which #{app}`.strip
|
15
15
|
end
|
16
16
|
|
17
|
-
def self.
|
18
|
-
|
19
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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 =
|
47
|
+
if pid = read_pid(pid_filename)
|
47
48
|
begin
|
48
49
|
Process.kill 'TERM', pid
|
49
50
|
Process.wait pid if options[:wait]
|
data/spec/headless_spec.rb
CHANGED
@@ -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')
|
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
|
90
|
-
expect
|
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.
|
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
|
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:
|