標籤:begin com 計算 是什麼 發送 and safari source bug
rescue in receive
由於寫ruby的時候感覺混身上下都拽起來了,所以比較喜歡用ruby寫代碼。
今天遇到了一個webdriver timeout的問題,問題本身還是由於我對webdriver不瞭解以及破文檔導致的。首先我們把問題簡化一下:
driver = Selenium::WebDriver.for :safari
driver.navigate.to "http://www.faraway.com"
wait = Selenium::WebDriver::Wait.new(:timeout => 1000) # seconds
wait.until { driver.find_element(:css, ‘input[name="username"]‘) }
由於考慮到網站太遠了,速度比較慢,於是timeout設的值比較大:timeout => 1000
這裡我們訪問:faraway這個網站,然後等待出現輸入框輸入username。
得到了例如以下錯誤資訊:
/Users/twer/.rvm/gems/[email protected]/gems/selenium-webdriver-2.42.0/lib/
selenium/webdriver/safari/server.rb:41:in`rescue in receive‘: timed out waiting for Safari to respond (Selenium::WebDriver::Error::TimeOutError)
from /Users/twer/.rvm/gems/[email protected]/gems/selenium-webdriver-2.42.0/lib/selenium/webdriver/safari/server.rb:36:in `receive‘
from /Users/twer/.rvm/gems/[email protected]/gems/selenium-webdriver-2.42.0/lib/selenium/webdriver/safari/bridge.rb:68:in `raw_execute‘
from /Users/twer/.rvm/gems/[email protected]/gems/selenium-webdriver-2.42.0/lib/selenium/webdriver/remote/bridge.rb:612:in `execute‘
from /Users/twer/.rvm/gems/[email protected]/gems/selenium-webdriver-2.42.0/lib/selenium/webdriver/remote/bridge.rb:110:in `get‘
from /Users/twer/.rvm/gems/[email protected]/gems/selenium-webdriver-2.42.0/lib/selenium/webdriver/common/navigation.rb:14:in `to‘
from
faraway.rb:26:in `<main>‘
看了出錯的地方就是我wait的地方調用的。我心想我這不是設了:timeout => 1000。wait的等待時間為1000秒嗎,怎麼還搞不定啊?
Monkey Patch來解救我了
搞不定啊,上面有人在吹啊,咋辦嘛。暴力解決。
我找到了上面紅色部分標明的檔案/selenium/webdriver/safari/server.rb。(https://code.google.com/p/selenium/source/browse/rb/lib/selenium/webdriver/safari/server.rb)
找到了出錯的地方41行,是在receive函數裡:
def receive
@frame ||= WebSocket::Frame::Incoming::Server.new(:version => @version)
until msg = @frame.next
end_time = Time.now + @command_timeout
begin
data = @ws.read_nonblock(1)
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
now = Time.now
if now >= end_time
raise Error::TimeOutError, "timed out waiting for Safari to respond" #第41行
end
IO.select([@ws], nil, nil, end_time - now)
retry
end
@frame << data
end
puts "<<< #{msg}" if $DEBUG
WebDriver.json_load msg.to_s
end
能夠看出,是now >= end_time用來計算是否逾時。在看前面end_time是由Time.now + @command_timeout得到的,然後進入begin/rescue/retry的。奇怪,我的:timeout => 1000不應該是work的嗎?
第一個monkey patch
我把上面那部分代碼直接拷到了我的源檔案faraway.rb中,做了例如以下改動:
if now >= end_time
puts @command_timeout
raise Error::TimeOutError, "timed out waiting for Safari to respond"
end
發現@command_timeout不是1000。而是60,也就是一分鐘
第二個monkey patch
until msg = @frame.next
end_time = Time.now + 1000
這樣強制讓@command_timeout設為1000
這樣執行,沒有問題了
探個到底
儘管monkey patch能夠解決我串連faraway的問題。可是,作為高速實驗得到反饋還行。真這樣用還是不太愉快。
於是我就開始讀源碼,webdriver本身的代碼還是挺簡單的。非常easy看懂。詳細怎麼看我就不講了。分分鐘發現了問題事實上是在這裡:
driver = Selenium::WebDriver.for :safari,timeout: 1000
driver.navigate.to "http://www.faraway.com"
紅色的部分在文檔上是沒有交代的,那這裡的timeout是什麼意思呢?我的wait不是已經有timeout了嗎。怎麼在Driver的建立時還須要呢?
原因是這是兩種不同的timeout。wait的timeout表示的是。我們的driver會等待多久知道這個元素出現。而我們出錯的地方是driver接收server返回資訊的timeout。
還不清楚?
這樣說,在wait的時候,driver會周期輪詢的去運行until中的代碼。看看until中的情況是否得到了滿足。
每次啟動並執行時候Driver對會發一個請求。這個請求事實上也是有timeout時間的。所以我們最開始遇到的問題並非wait本身timeout了。
而是driver發送命令後接收的時候timeout了。
明了了吧,都是faraway.com搞得怪,想重現這個問題。把command timeout改為1試試
假設我再次不幸。。
。
我一僅僅攪得自己是運氣比較背的那種人。
只是解決這個問題的過程還是非常開心。ruby這樣的動態語言,能夠打monkey patch,對於高速定位問題,解決這個問題還是非常有協助的。開心。
一個有意思的Ruby Webdriver逾時問題的解決過程