making Element#click_no_wait faster and easier debuggable
---------------------------------------------------------
Key: WTR-449
URL: http://jira.openqa.org/browse/WTR-449
Project: Watir
Issue Type: Improvement
Components: HTML Controls
Affects Versions: 1.6.5
Environment: all environments
Reporter: Jarmo Pertman
Priority: Major
I have been thinking of improving Element#click_no_wait for some time now. I
can see the following problems with current solution:
1) code is not good - method _code_that_copies_readonly_array with funny
comment in main namespace, eval_in_spawned_process with TODO and not-TODO
comments
2) users have a lot of problems with click_no_wait (http://bit.ly/asirep) and
current code doesn't allow easy debugging with monkey-patching or similar
solutions
3) in the spawned process used for click_no_wait, no rubygems is loaded thus
it's possible that users without having RUBYOPT set to -rubygems can't use
click_no_wait and since it's hard to debug (see point #2) then they don't even
know what's the problem
4) click_no_wait is just slow because it loads too many files/libraries in it's
own process - it should just load all the necessary things and nothing else
5) currently, in the unit tests click_no_wait process doesn't use development
libraries
Here is a benchmark:
*click_no_wait_bench.rb:*
{noformat}
require "rubygems"
require "watir"
require "benchmark"
b = Watir::Browser.new
b.goto "http://www.google.com"
times = 5
# almost original watir 1.6.5 code
# * added gsub to avoid bug for some ruby versions
# * using ruby instead of start rubyw to measure time
module Watir
module PageContainer
def eval_in_spawned_process(command)
command.strip!
load_path_code = _code_that_copies_readonly_array($LOAD_PATH,
'$LOAD_PATH')
ruby_code = "require 'watir/ie'; "
# ruby_code = "$HIDE_IE = #{$HIDE_IE};" # This prevents attaching to a
window from setting it visible. However modal dialogs cannot be attached to
when not visible.
ruby_code << "pc = #{attach_command}; " # pc = page container
# IDEA: consider changing this to not use instance_eval (it makes the
code hard to understand)
ruby_code << "pc.instance_eval(#{command.inspect})"
exec_string = "ruby -e #{(load_path_code + '; ' +
ruby_code).inspect}".gsub("\\\"", "'")
system(exec_string)
end
end
end
Benchmark.bmbm do |x|
x.report("watir 1.6.5") {times.times {b.button(:name =>
"btnG").click_no_wait}}
end
=begin
Rehearsal -----------------------------------------------
watir 1.6.5 0.031000 0.015000 0.046000 ( 26.136495)
-------------------------------------- total: 0.046000sec
user system total real
watir 1.6.5 0.000000 0.000000 0.000000 ( 23.600350)
=end
# improved section
# * simplified click_no_wait methods
# * requiring rubygems incase of missing RUBYOPT
# * extracted ruby_execute_command into method for easier monkey-patching for
debugging and testing reasons
module Watir
class Element
def click_no_wait
assert_enabled
highlight(:set)
element = "#{self.class}.new(#...@page_container.attach_command},
:unique_number, #{self.unique_number})"
@page_container.click_no_wait(element)
highlight(:clear)
end
end
module PageContainer
def click_no_wait(element)
ruby_code = "require 'rubygems';" <<
"require 'watir/ie';" <<
"#{element}.click!"
system(spawned_click_no_wait_command(ruby_code))
end
def spawned_click_no_wait_command(command)
"start rubyw -e #{command.inspect}"
end
private :spawned_click_no_wait_command
end
end
Benchmark.bmbm do |x|
x.report("watir improved - ie") {times.times {b.button(:name =>
"btnG").click_no_wait}}
end
=begin
Rehearsal -------------------------------------------------------
watir improved - ie 0.032000 0.047000 0.079000 ( 20.141152)
---------------------------------------------- total: 0.079000sec
user system total real
watir improved - ie 0.047000 0.016000 0.063000 ( 18.931083)
=end
# * loading watir/core.rb to reduce loading of unneeded files/libraries
module Watir
class Element
def click_no_wait
assert_enabled
highlight(:set)
element = "#{self.class}.new(#...@page_container.attach_command},
:unique_number, #{self.unique_number})"
@page_container.click_no_wait(element)
highlight(:clear)
end
end
module PageContainer
def click_no_wait(element)
ruby_code = "require 'rubygems';" <<
"require 'watir/core';" <<
"#{element}.click!"
system(spawned_click_no_wait_command(ruby_code))
end
def spawned_click_no_wait_command(command)
"start rubyw -e #{command.inspect}"
end
private :spawned_click_no_wait_command
end
end
Benchmark.bmbm do |x|
x.report("watir improved - click_no_wait") {times.times {b.button(:name =>
"btnG").click_no_wait}}
end
=begin
Rehearsal ------------------------------------------------------------------
watir improved - click_no_wait 0.062000 0.015000 0.077000 ( 9.729557)
--------------------------------------------------------- total: 0.077000sec
user system total real
watir improved - click_no_wait 0.015000 0.047000 0.062000 ( 9.308532)
=end
b.close
{noformat}
*watir/core.rb:*
{noformat}
require 'watir/win32ole'
require 'logger'
require 'watir/exceptions'
require 'watir/matches'
require 'watir/core_ext'
require 'watir/logger'
require 'watir/container'
require 'watir/locator'
require 'watir/page-container'
require 'watir/ie-class'
require 'watir/element'
require 'watir/element_collections'
require 'watir/form'
require 'watir/non_control_elements'
require 'watir/input_elements'
require 'watir/table'
require 'watir/image'
require 'watir/link'
require 'watir/html_element'
require 'watir/waiter'
require 'watir/module'
{noformat}
As it's possible to see from the results then i got click_no_wait to perform
faster about 2-3 times (of course this depends on the PC and so on, but these
are the results from my machine, which is a quite modest laptop)!
I call this as a quite big improvement if we think about all the Web 2.0
websites where #click_no_wait is often used.
Results:
1) removed all this unnecessary and funny-looking code which also had slight of
a "criminal overengineering" smell on it
(http://coderoom.wordpress.com/2010/06/23/criminal-overengineering/)
2) it is now easy debuggable, just use this monkey-patch:
{noformat}
Watir::PageContainer.class_eval {def spawned_click_no_wait_command(command)
"ruby -e #{command.inspect}" end}
{noformat}
Maybe make it even more easier by using $DEBUG and if-statement or something
similar?
3) requiring rubygems always, just in case, in the spawned process
4) click_no_wait performs 2-3x faster! It might be possible to improve the
performance even more by doing some larger changes with the require statements
in watir/core.rb - unfortunately it is not possible anymore to make changes in
smaller steps
5) all click_no_wait invocations in unit tests use now development libraries
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
http://jira.openqa.org/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira
_______________________________________________
Wtr-development mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/wtr-development