Skip to content

Commit

Permalink
Calculate time using Process::CLOCK_MONOTONIC rather than Time.now
Browse files Browse the repository at this point in the history
Some gems (Timecop in particular) mock Time class causing failures for
our timeout-like loops. With this change, we now use more low-level API
introduced in MRI 2.1 to get monotonic clock. This API gives a bit
different results on POSIX vs Windows, but consistent enough for our
needs. JRuby also supports this for years.
  • Loading branch information
p0deje committed Mar 9, 2019
1 parent b4a0af5 commit cca8329
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 21 deletions.
8 changes: 6 additions & 2 deletions rb/lib/selenium/webdriver/common/socket_lock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,19 @@ def locked
private

def lock
max_time = Time.now + @timeout
max_time = current_time + @timeout

sleep 0.1 until can_lock? || Time.now >= max_time
sleep 0.1 until can_lock? || current_time >= max_time

return if did_lock?

raise Error::WebDriverError, "unable to bind to locking port #{@port} within #{@timeout} seconds"
end

def current_time
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end

def release
@server&.close
end
Expand Down
17 changes: 6 additions & 11 deletions rb/lib/selenium/webdriver/common/socket_poller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,24 +96,19 @@ def conn_completed?(sock)
end

def with_timeout
max_time = time_now + @timeout
max_time = current_time + @timeout

(
until current_time > max_time
return true if yield

wait
) until time_now > max_time
sleep @interval
end

false
end

def wait
sleep @interval
end

# for testability
def time_now
Time.now
def current_time
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
end # SocketPoller
end # WebDriver
Expand Down
10 changes: 8 additions & 2 deletions rb/lib/selenium/webdriver/common/wait.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ def initialize(opts = {})
#

def until
end_time = Time.now + @timeout
end_time = current_time + @timeout
last_error = nil

until Time.now > end_time
until current_time > end_time
begin
result = yield
return result if result
Expand All @@ -72,6 +72,12 @@ def until

raise Error::TimeOutError, msg
end

private

def current_time
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
end # Wait
end # WebDriver
end # Selenium
11 changes: 5 additions & 6 deletions rb/spec/unit/selenium/webdriver/socket_poller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def setup_connect(*states)
wait = Time.parse('2010-01-01 00:00:04')
stop = Time.parse('2010-01-01 00:00:06')

allow(Time).to receive(:now).and_return(start, wait, stop)
expect(Process).to receive(:clock_gettime).and_return(start, wait, stop)
expect(poller).not_to be_connected
end
end
Expand All @@ -61,12 +61,11 @@ def setup_connect(*states)
it 'returns false if the socket is still listening after the given timeout' do
setup_connect true

start = Time.parse('2010-01-01 00:00:00')
wait = Time.parse('2010-01-01 00:00:04')
stop = Time.parse('2010-01-01 00:00:06')
start = Time.parse('2010-01-01 00:00:00').to_f
wait = Time.parse('2010-01-01 00:00:04').to_f
stop = Time.parse('2010-01-01 00:00:06').to_f

# on rbx, we can't add expectations to Time.now since it will be called by the kernel code.
allow(poller).to receive(:time_now).and_return(start, wait, stop)
expect(Process).to receive(:clock_gettime).and_return(start, wait, stop)
expect(poller).not_to be_closed
end
end
Expand Down

0 comments on commit cca8329

Please sign in to comment.