Skip to content

Instantly share code, notes, and snippets.

@jacott
Created June 3, 2012 10:32
Show Gist options
  • Save jacott/2862964 to your computer and use it in GitHub Desktop.
Save jacott/2862964 to your computer and use it in GitHub Desktop.
Single threading Capybara/Selenium for using transactional fixtures
require 'thread'
class MyBlockCaller
def initialize
@block = nil
@lock = Mutex.new
@result = nil
@call_done = ConditionVariable.new
@ready_to_call = ConditionVariable.new
@exception = nil
end
def pass_block(&block)
@lock.synchronize do
@block=block
@exception=nil
@ready_to_call.broadcast
@call_done.wait(@lock)
result=@result
@result=nil # garbage collect
raise @exception if @exception
result
end
end
def call_block
@lock.synchronize do
looper_result=nil
looper_exception=nil
looper_running=true
looper_thread = Thread.new do
begin
looper_result= yield
unless looper_result
sleep 0.05
end
rescue Exception => looper_exception
ensure
@lock.synchronize do
looper_running=false
@ready_to_call.broadcast
end
end
end
while looper_running
if !@block && looper_running
@ready_to_call.wait(@lock)
end
if @block
begin
@[email protected]
rescue Exception => @exception
ensure
@block=nil
@call_done.signal
end
end
nil
end
looper_thread.join
raise looper_exception if looper_exception
looper_result
end
end
end
$my_block_caller=MyBlockCaller.new
require 'action_dispatch/middleware/request_id'
ActionDispatch::RequestId.class_eval do
def call_with_single_thread(env)
$my_block_caller.pass_block do
call_without_single_thread(env)
end
end
alias_method_chain :call, :single_thread
end
require 'selenium/webdriver'
module Selenium
module WebDriver
module Remote
class Bridge
def execute(*args)
$my_block_caller.call_block {raw_execute(*args)['value']}
end
end
end
end
end
require 'capybara/util/timeout'
module Capybara
class << self
##
# Provides timeout similar to standard library Timeout, but avoids threads
#
def timeout(seconds = 1, driver = nil, error_message = nil, &block)
start_time = Time.now
result = nil
until result
result=$my_block_caller.call_block(&block)
return result if result
delay = seconds - (Time.now - start_time)
if delay <= 0
raise TimeoutError, error_message || "timed out #{[seconds, delay, start_time].inspect}"
end
driver && driver.wait_until(delay)
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment