Skip to content

Commit 969b734

Browse files
committed
Remove ChildProcess gem dependency
Process.spawn is cross-platform in Ruby 2.7+ and ChildProcess is not actively maintained at the moment. Fixes #11251.
1 parent 81cd1a2 commit 969b734

File tree

9 files changed

+135
-36
lines changed

9 files changed

+135
-36
lines changed

WORKSPACE

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,9 +324,9 @@ pin_browsers()
324324

325325
http_archive(
326326
name = "rules_ruby",
327-
sha256 = "966ef280a3ecb24867b2f67d73bb3ad3c59b4a7beefb67abbcd79b0dff0a5679",
328-
strip_prefix = "rules_ruby_simple-078c24fd29a9e1717990fa2fce15e1cb053f18a3",
329-
url = "https://github.com/p0deje/rules_ruby_simple/archive/078c24fd29a9e1717990fa2fce15e1cb053f18a3.zip",
327+
sha256 = "bb0bffb0285ff8fa9a967fc2580ddf3b511818a6baf269dcd6c5c6076c4921d8",
328+
strip_prefix = "rules_ruby-c3cefa71d0111a04c9ce0672c65d376262d8d975",
329+
url = "https://github.com/p0deje/rules_ruby/archive/c3cefa71d0111a04c9ce0672c65d376262d8d975.zip",
330330
)
331331

332332
load(

rb/lib/selenium/server.rb

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
# specific language governing permissions and limitations
1818
# under the License.
1919

20-
require 'childprocess'
20+
require 'selenium/webdriver/common/child_process'
2121
require 'selenium/webdriver/common/socket_poller'
2222
require 'net/http'
2323

@@ -237,7 +237,7 @@ def stop_process
237237

238238
begin
239239
@process.poll_for_exit(5)
240-
rescue ChildProcess::TimeoutError
240+
rescue WebDriver::Common::ChildProcess::TimeoutError
241241
@process.stop
242242
end
243243
rescue Errno::ECHILD
@@ -252,16 +252,13 @@ def process
252252
properties = @additional_args.dup - @additional_args.delete_if { |arg| arg[/^-D/] }
253253
args = ['-jar', @jar, @role, '--port', @port.to_s]
254254
server_command = ['java'] + properties + args + @additional_args
255-
cp = ChildProcess.build(*server_command)
255+
cp = WebDriver::Common::ChildProcess.build(*server_command)
256256
WebDriver.logger.debug("Executing Process #{server_command}")
257257

258-
io = cp.io
259-
260258
if @log.is_a?(String)
261-
@log_file = File.open(@log, 'w')
262-
io.stdout = io.stderr = @log_file
259+
cp.io = @log
263260
elsif @log
264-
io.inherit!
261+
cp.io = :out
265262
end
266263

267264
cp.detach = @background

rb/lib/selenium/webdriver.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
# specific language governing permissions and limitations
1818
# under the License.
1919

20-
require 'childprocess'
2120
require 'tmpdir'
2221
require 'fileutils'
2322
require 'date'

rb/lib/selenium/webdriver/common.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,4 @@
9696
require 'selenium/webdriver/common/element'
9797
require 'selenium/webdriver/common/shadow_root'
9898
require 'selenium/webdriver/common/websocket_connection'
99+
require 'selenium/webdriver/common/child_process'
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# frozen_string_literal: true
2+
3+
# Licensed to the Software Freedom Conservancy (SFC) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The SFC licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
20+
module Selenium
21+
module WebDriver
22+
module Common
23+
24+
#
25+
# @api private
26+
#
27+
28+
class ChildProcess
29+
TimeoutError = Class.new(StandardError)
30+
31+
SIGTERM = 'TERM'
32+
SIGKILL = 'KILL'
33+
34+
POLL_INTERVAL = 0.1
35+
36+
attr_accessor :detach
37+
attr_writer :io
38+
39+
def self.build(*command)
40+
new(*command)
41+
end
42+
43+
def initialize(*command)
44+
@command = command
45+
@detach = false
46+
@pid = nil
47+
@status = nil
48+
end
49+
50+
def io
51+
@io ||= Platform.null_device
52+
end
53+
54+
def start
55+
options = {%i(out err) => io}
56+
options[:pgroup] = true unless Platform.windows? # NOTE: this is a bug only in Windows 7
57+
58+
WebDriver.logger.debug("Starting process: #{@command} with #{options}")
59+
@pid = Process.spawn(*@command, options)
60+
WebDriver.logger.debug(" -> pid: #{@pid}")
61+
62+
Process.detach(@pid) if detach
63+
end
64+
65+
def stop(timeout = 3)
66+
return unless @pid
67+
return if exited?
68+
69+
WebDriver.logger.debug("Sending TERM to process: #{@pid}")
70+
Process.kill(SIGTERM, @pid)
71+
poll_for_exit(timeout)
72+
73+
WebDriver.logger.debug(" -> stopped #{@pid}")
74+
rescue TimeoutError
75+
WebDriver.logger.debug(" -> sending KILL to process: #{@pid}")
76+
Process.kill(SIGKILL, @pid)
77+
wait
78+
WebDriver.logger.debug(" -> killed #{@pid}")
79+
end
80+
81+
def alive?
82+
@pid && !exited?
83+
end
84+
85+
def exited?
86+
return unless @pid
87+
88+
WebDriver.logger.debug("Checking if #{@pid} is exited")
89+
_, @status = Process.waitpid2(@pid, Process::WNOHANG | Process::WUNTRACED) if @status.nil?
90+
return if @status.nil?
91+
92+
WebDriver.logger.debug(" -> exit code is #{@status.exitstatus}")
93+
@status.exited?
94+
end
95+
96+
def poll_for_exit(timeout)
97+
WebDriver.logger.debug("Polling #{timeout} seconds for exit of #{@pid}")
98+
99+
end_time = Time.now + timeout
100+
sleep POLL_INTERVAL until exited? || Time.now > end_time
101+
102+
raise TimeoutError, " -> #{@pid} still alive after #{timeout} seconds" unless exited?
103+
end
104+
105+
def wait
106+
return if exited?
107+
108+
_, @status = Process.waitpid2(@pid)
109+
end
110+
111+
end # ChildProcess
112+
end # Common
113+
end # WebDriver
114+
end # Selenium

rb/lib/selenium/webdriver/common/service_manager.rb

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,8 @@ def uri
7878

7979
def build_process(*command)
8080
WebDriver.logger.debug("Executing Process #{command}")
81-
@process = ChildProcess.build(*command)
82-
if WebDriver.logger.debug?
83-
@process.io.stdout = @process.io.stderr = WebDriver.logger.io
84-
elsif Platform.jruby?
85-
# Apparently we need to read the output of drivers on JRuby.
86-
@process.io.stdout = @process.io.stderr = File.new(Platform.null_device, 'w')
87-
end
81+
@process = Common::ChildProcess.build(*command)
82+
@process.io = WebDriver.logger.io if WebDriver.logger.debug?
8883

8984
@process
9085
end
@@ -104,16 +99,13 @@ def find_free_port
10499

105100
def start_process
106101
@process = build_process(@executable_path, "--port=#{@port}", *@extra_args)
107-
# NOTE: this is a bug only in Windows 7
108-
@process.leader = true unless Platform.windows?
109102
@process.start
110103
end
111104

112105
def stop_process
113106
return if process_exited?
114107

115108
@process.stop STOP_TIMEOUT
116-
@process.io.stdout.close if Platform.jruby? && !WebDriver.logger.debug?
117109
end
118110

119111
def stop_server

rb/lib/selenium/webdriver/common/socket_lock.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ def release
6666

6767
def can_lock?
6868
@server = TCPServer.new(Platform.localhost, @port)
69-
ChildProcess.close_on_exec @server
70-
69+
@server.close_on_exec = true
7170
true
7271
rescue SocketError, Errno::EADDRINUSE, Errno::EBADF => e
7372
WebDriver.logger.debug("#{self}: #{e.message}")

rb/selenium-webdriver.gemspec

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,10 @@ Gem::Specification.new do |s|
4848
s.bindir = 'bin'
4949
s.require_paths = ['lib']
5050

51-
s.add_runtime_dependency 'childprocess', ['>= 0.5', '< 5.0']
5251
s.add_runtime_dependency 'rexml', ['~> 3.2', '>= 3.2.5']
5352
s.add_runtime_dependency 'rubyzip', ['>= 1.2.2', '< 3.0']
5453
s.add_runtime_dependency 'websocket', ['~> 1.0']
5554

56-
# childprocess requires ffi on windows but doesn't declare it in its dependencies
57-
s.add_development_dependency 'ffi'
5855
s.add_development_dependency 'pry', ['~> 0.14']
5956
s.add_development_dependency 'rack', ['~> 2.0']
6057
s.add_development_dependency 'rake'

rb/spec/unit/selenium/server_spec.rb

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
module Selenium
2424
describe Server do
25-
let(:mock_process) { instance_double(ChildProcess::AbstractProcess).as_null_object }
25+
let(:mock_process) { instance_double(WebDriver::Common::ChildProcess).as_null_object }
2626
let(:mock_poller) { instance_double(WebDriver::SocketPoller, connected?: true, closed?: true) }
2727
let(:repo) { 'https://github.com/api/repos/seleniumhq/selenium/releases' }
2828
let(:example_json) do
@@ -46,7 +46,7 @@ module Selenium
4646

4747
it 'uses the given jar file and port' do
4848
allow(File).to receive(:exist?).with('selenium_server_deploy.jar').and_return(true)
49-
allow(ChildProcess).to receive(:build)
49+
allow(WebDriver::Common::ChildProcess).to receive(:build)
5050
.with('java', '-jar', 'selenium_server_deploy.jar', 'standalone', '--port', '1234')
5151
.and_return(mock_process)
5252

@@ -55,13 +55,13 @@ module Selenium
5555

5656
server.start
5757
expect(File).to have_received(:exist?).with('selenium_server_deploy.jar')
58-
expect(ChildProcess).to have_received(:build)
58+
expect(WebDriver::Common::ChildProcess).to have_received(:build)
5959
.with('java', '-jar', 'selenium_server_deploy.jar', 'standalone', '--port', '1234')
6060
end
6161

6262
it 'waits for the server process by default' do
6363
allow(File).to receive(:exist?).with('selenium_server_deploy.jar').and_return(true)
64-
allow(ChildProcess).to receive(:build)
64+
allow(WebDriver::Common::ChildProcess).to receive(:build)
6565
.with('java', '-jar', 'selenium_server_deploy.jar', 'standalone', '--port', '4444')
6666
.and_return(mock_process)
6767

@@ -71,13 +71,13 @@ module Selenium
7171
expect(mock_process).to receive(:wait)
7272
server.start
7373
expect(File).to have_received(:exist?).with('selenium_server_deploy.jar')
74-
expect(ChildProcess).to have_received(:build)
74+
expect(WebDriver::Common::ChildProcess).to have_received(:build)
7575
.with('java', '-jar', 'selenium_server_deploy.jar', 'standalone', '--port', '4444')
7676
end
7777

7878
it 'adds additional args' do
7979
allow(File).to receive(:exist?).with('selenium_server_deploy.jar').and_return(true)
80-
allow(ChildProcess).to receive(:build)
80+
allow(WebDriver::Common::ChildProcess).to receive(:build)
8181
.with('java', '-jar', 'selenium_server_deploy.jar', 'standalone', '--port', '4444', 'foo', 'bar')
8282
.and_return(mock_process)
8383

@@ -88,14 +88,14 @@ module Selenium
8888

8989
server.start
9090
expect(File).to have_received(:exist?).with('selenium_server_deploy.jar')
91-
expect(ChildProcess).to have_received(:build)
91+
expect(WebDriver::Common::ChildProcess).to have_received(:build)
9292
.with('java', '-jar', 'selenium_server_deploy.jar', 'standalone',
9393
'--port', '4444', 'foo', 'bar')
9494
end
9595

9696
it 'adds additional JAVA options args' do
9797
allow(File).to receive(:exist?).with('selenium_server_deploy.jar').and_return(true)
98-
allow(ChildProcess).to receive(:build)
98+
allow(WebDriver::Common::ChildProcess).to receive(:build)
9999
.with('java',
100100
'-Dwebdriver.chrome.driver=/bin/chromedriver',
101101
'-jar', 'selenium_server_deploy.jar',
@@ -113,7 +113,7 @@ module Selenium
113113

114114
server.start
115115
expect(File).to have_received(:exist?).with('selenium_server_deploy.jar')
116-
expect(ChildProcess).to have_received(:build)
116+
expect(WebDriver::Common::ChildProcess).to have_received(:build)
117117
.with('java',
118118
'-Dwebdriver.chrome.driver=/bin/chromedriver',
119119
'-jar', 'selenium_server_deploy.jar',

0 commit comments

Comments
 (0)