Friday, August 29, 2008

Abstract vs Concrete Parameters (Beck)

Now I can state the general rule: to make software controllable, pass concrete parameters when tests don't need much flexibility and pass abstract parameters when they need more flexibility.
- Kent Beck

A great read

Watir, send_keys and Javascript

I was recently trying to write some tests for some Javascript code that looked something like this (using prototype):

function watch_keys(evt) {
if ((evt.keyCode >= 32) && (evt.keyCode < 127)) {
//other work...
Event.stop(evt);
}
}

Event.observe(window, 'load', function() {
Event.observe($('some_input_id'), 'keypress', watch_keys);
}


I simplified it down to just the basics. This code is attaching a key press handler to an input field. If the key being pressed is a printable character then the handler will do some work on it and then stop its propagation. The work being done isn't to important.

My initial attempts at testing this using Watir (version 1.5.3) used the set method didn't work. The key press event was fired but it never activated the code because regardless of what character was sent the key code was always 0.

No big deal, I decided to use the send_keys method of Watir::IE. Using this method I was able to craft some code that worked some of the time. It turned out that it never worked on our CI system and it was very sensitive to any screen/mouse movement that occurred on the PC that was running the tests. I've experienced the same problems using send_keys in the past and this wasn't going to work for the long term.

Digging into the Watir source and why the set method didn't work revealed this code at the heart of the set method (input_elements.rb):

def doKeyPress(value)
value = limit_to_maxlength(value)
for i in 0 .. value.length - 1
sleep @container.typingspeed
c = value[i,1]
@o.value = @o.value.to_s + c
@o.fireEvent("onKeyDown")
@o.fireEvent("onKeyPress")
@o.fireEvent("onKeyUp")
end
end


Not very hard to see why this doesn't work with any event handlers that are actually checking key codes.... Though the key press event is fired it doesn't have any event information in it. I'm guessing that by default IE will generate an event with a 0 key code if nothing was provided.

I ended up monkey patching IE with a custom implementation of send keys that seems to work for my purposes.

module Watir
class IE
def run_script(js)
ie.Document.parentWindow.execScript(js)
end

def send_some_keys(ele, text)
ele.focus
run_script("var wtr_evt=document.createEventObject();"+
"var wtr_ele=$('#ele.id}');");
text.scan(/./) do |char|
run_script("wtr_evt.keyCode='#{char}'.charCodeAt(0);"+
"wtr_ele.fireEvent('onkeypress', wtr_evt);");
end
end
end


This code injects several pieces of JavaScript into the DOM. It creates new event object which represents the key stroke that is incoming. It then manually fires the event on the element in question.

Note that the element must have a valid ID to work and that the injected JavaScript uses the a Prototype method to locate the element. It shouldn't be hard to customize the code to work with pure JavaScript methods or a different event type.

So far it is working well in development and CI environments.

Wednesday, April 9, 2008

Data Concurrency With a Version Lock

I ran into an interesting bug with using a version lock to control changes against a record in a database.

The system in question uses optimistic concurrency control to ensure that users do not overwrite each others changes. This is implemented as a version column in the database which is an integer column that gets incremented with each update of the row. When a record is updated, the version number of the current (in memory) record is compared against the record in the database if they are not the same then an exception is raised and the update is aborted. All pretty standard stuff.

This all worked well except for one problem. The typical usage pattern looked like this:
  1. User views object with version X - the object is loaded from the DAL and given to the view, the view displays the relevant data from the object and stores the object ID in a hidden form variable.
  2. User updates object by posting the view - the object is loaded from the DAL, updated and saved back to the database via the DAL. During this save, the version numbers are compared to be sure they are the same.
There were test cases that validated this behavior and it seems correct but it isn't. The implementation is only partially correct. The problem is in the second load of the object. Consider this sequence:
  1. User A views obj with ver 1 at 10:00 AM
  2. User B views obj with ver 1 at 10:05 AM
  3. User B saves obj with ver 1 at 10:06 AM
  4. User A saves obj with ver 1 at 10:10 AM -- this should fail
Step 4 should fail and it doesn't. It doesn't fail because the controller simply reloads the object (based on ID) and gets version 2, updates it and saves it. The DAL doesn't see any problem because the version numbers of the object being updated and the record stored in the database match.

What this implementation really checks for is the case where two users update the same record simultaneously between the second read/update cycle. While this can happen, it really isn't the common case and the time span here is likely milliseconds. In other words, the chances of this actually catching a concurrency violation are pretty small.

Fixing the problem is fairly simple, it requires the view also store the current version number along with the ID. Then the controller can request that the DAL load an object with the provided ID and version number. If that object no longer exists, then a concurrency violation has occurred and the user should be notified to resolve the issue. If the object was loaded, then the object can be updated and then saved via the DAL using the same check described above. Having both checks in place should do the trick.

This particular application is a C#/ASP.net application. It occurred to me that the ruby on rails framework has a feature which provides the same kind of concurrency check (lock_version) and I wondered how active record implemented the check.

I fired up a new rails application and generated a simple scaffold. It appears to me that rails is doing exactly the same thing and unless you include the version_lock in your views (or session I guess) it doesn't work. This is a bit confusing because most references to version_lock seem to suggest that just having the column defined in your schema is enough to get the concurrency check and don't really mention the fact that the 'working' version_lock must be available during the update. In fact, the lock_version example given in Agile Web Development with Rails is a good example of the problem I describe at the beginning of the post and only covers the smallest case.

Note, I'm not picking on rails, it just happened to be the first place I looked for an example :)


Monday, April 7, 2008

Cool Visualization

Digg Visualization

Check out the other styles at that same link - fun stuff!

Thursday, April 3, 2008

Automation and the Console

When I'm writing software, I prefer to work with an editor and a set of consoles. Editor for code and consoles for building/testing. Works great in almost any language. Who needs those stinkin IDEs :)

One of the downsides to this is that the test/code cycle can get broken up by continually having to switch windows. With a full blown IDE you just hit some magic key and it all happens. What I wanted was this magic to happen but still keep the flexibility of having the 2 different tools.

Enter AutoHotKey. AutoHotKey allows you to script a whole bunch of windows stuff and it also allows you to setup system wide hot keys. So, I wrote a simple script that looks like this:


^+e::
WinGetActiveTitle, Title
IfWinExist Console
{
WinActivate
SendInput {Up}
SendInput {Enter}
WinActivate, %Title%
}


This script is set to register as the global hot key Control-Shift e. What is does it get/store the current window title, activate the console window, press the up key (to activate the last command), press enter and then reactivate the previous window. Works like a charm. Since I got it working I'm unsure how I lived without it.

Now that I know how to do it, I'm thinking of all kinds of global hot keys that I might be able to use.


Watir Bug (kinda sorta)

I stumbled upon this a while back and it caused me some grief. Today I saw some questions on the Watir NG that seem to be about the same problem.

Consider this irb session:
irb(main):001:0> require 'Watir'
=> true
irb(main):002:0> include Watir
=> Object

irb(main):003:0> ie = IE::start_process('www.google.com')
=> #&lt;Watir::IE:0x2c7d86c ....>
irb(main):006:0> ie.div(:id, 'gbar').id

=> "gbar"
irb(main):007:0> ie.div(:id=>'gbar').id
=> "gbar"
irb(main):008:0> ie.text_field(:name, 'q').id

=> ""
irb(main):009:0> ie.text_field(:name, 'q').name
=> "q"
irb(main):010:0> ie.text_field(:name=>'q').name

TypeError: {:name=>"q"} is not a symbol
irb(main):011:0>

Accessing an input element with a hash fails. Input elements in Watir currently only support a single search item and this is the cause of the confusion.

This patch will solve the problem:

require 'watir'

module Watir::Container
alias :old_locate_input_element :locate_input_element

#form elements do not allow the hash syntax for how/what.

#this change here allows it for single element hashes by
#just converting the hash into a distinct how and what and then
#passing it on
def locate_input_element(how, what, types, value=nil)
how, what = *how.to_a[0] if (how.kind_of? Hash) && (how.length == 1)
old_locate_input_element(how, what, types, value)
end

end

All it does is convert a hash into the multiple args (how and what) that the input element look up requires. It doesn't do anything to allow multiple look up values for input elements.

Update: Turns out this this is a know issue.


Saturday, March 29, 2008

Layout

I'm trying to get the code layout to work well in the major browsers.

Right now it looks fine in Firefox 2, which is what I mostly use. In IE 6 all the line breaks are removed, I know why but I'm not sure how to fix it. I haven't gotten around to testing other browsers so if things look weird, view it in Firefox and hopefully it'll look better :)


Thursday, March 27, 2008

Dynamic Finders with Watir

If you've used rails, then you've probably seen the dynamic finders that active record provides. I thought it'd be interesting to see if that kind of behavior could be added to Watir. It turned out to be a nice little exercise.

I'm not sure how it will play in real test code, but I'm going to try it out over the next couple of days.

Here is an example of what you can do:
require 'watir'
require 'dynamic_finder.rb'

include Watir

ie = IE::start_process('www.google.com')
p ie.div_by_id(/gbar/).id # ie.div(:id => /gbar/)

p ie.span_by_class(/gb1/).text # ie.span(:class => /gb1/)
p ie.span_by_class_and_index(/gb1/, 2).text # ie.span(:class => /gb1/, :index=>2)

p ie.span_by_index(2).text # ie.span(:index => 2)
p ie.text_field_by_index(1).name # ie.text_field(:index,1)

ie.text_field_by_name("q").set('watir')
ie.button_by_value('Google Search').click

2.times do
ie.link_by_text(/Watir Tutorial/).click
end

ie.close

You can find the source files for the change and the example posted above here. Enjoy!


Trying Python

I'm gonna give Python a try!!

I've been using Ruby as my scripting language of choice for about 3 years now. It is a great language and I learned a lot using it. However, there are 2 problems that I have with Ruby.

First, its Windows support isn't great. For basic things it works wonders, but when you get beyond that there are a lot of things that Ruby on Windows doesn't do so well. Some of the more interesting gems do not run well (or at all) on Windows. Even some of the standard libraries that ship with Ruby don't work on Windows and don't get me started with some of the core library methods that don't work. It also seems that everything with Ruby runs slower on windows -- very frustrating.

Second, it seems to me that more and more the Ruby community has an attitude that if you aren't running nix then it doesn't matter. This is a real shame, Windows is a huge market. I use Windows for development, most of my clients use Windows so dropping it for a better OS isn't an option. The community as a whole is struggling with this issue but there doesn't seem to be a good solution right now. Check out these (here, here and here) other blogs for some interesting info (the comments are interesting as well).

So I'm gonna try some options and see what is what.

Wednesday, March 26, 2008

Teaching

"If you would thoroughly know anything teach it to others." -- Tryon Edwards

If you think you know something, try teaching it. If you think you really know something, try teaching it to a 7 year old, a 10 year old and a 13 year old in the same day!!

Blogging Again

I finally got around to start blogging again. I've lost all my old posts so I'll be starting from scratch...