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

Reply via email to