Cucumber Performance 1

Posted by jcnnghm Wed, 23 Feb 2011 22:05:00 GMT

Cucumber with celerity running under capybara and celerity was erroring out with Java OutOfMemory exceptions. In looking to fix the problem, I also decided to look at how various jruby invocation switches would alter the performance.

First, to set your jruby invocation options, in your cucumber environment file, add the following:

require 'culerity'
Culerity.jruby_invocation = 'jruby -J-Xmx2500m -J-Xms2500m -J-Xmn512m'

I recommend setting the Xmx and Xms options, the maximum and minimum Java heap sizes, to the same value, tailored to your environment. Xmn controls the generation size for the garbage collector. 20% or so of the total heap size seems to be the sweet spot, and allows the garbage collector to run effectively over this memory.

While increasing the java heap size temporarily alleviated the issue, it didn’t address it entirely, there seemed to be a memory leak. I started poking around, and found this commit, https://github.com/langalex/culerity/commit/5eb7d6e557f86acdeb865af2180629930629bd26, in the culerity repository. After some experimentation, I settled on adding the following to my environment file:

After do
  if page.driver.is_a?(Capybara::Driver::Culerity)
    page.driver.class.server.clear_proxies
  end
end

clearproxies can be replaced without closebrowsers, but it impacts performance more than clear proxies and increases memory usage. The clear_proxies method frees the rjuby remote object proxies, which in turn allows the garbage collector to reclaim the memory that they occupy. This reduced my overall memory usage for the testing suite by over a gigabyte, and had a very minor runtime improvement. At the same time, I also tried both the –fast and the –server options on the rjuby invocation, and found that they didn’t seem to have any significant positive impact on either memory usage or runtime.

jruby -J-Xmx2500m -J-Xms2500m -J-Xmn512m
Mem 1,066,412MB
19m28.724s
with page.driver.class.server.clear_proxies

Mem 2,111,728MB
19m54.007s
without page.driver.class.server.clear_proxies

Mem 1,549,804MB
23m10.708s
with page.driver.class.server.close_browsers


jruby -J-Xmx2500m -J-Xms2500m -J-Xmn512m --fast --server
Mem 1,068,840MB
21m45.931s
with page.driver.class.server.clear_proxies

Mem 2,945,160MB
21m8.971s
without page.driver.class.server.clear_proxies


jruby -J-Xmx2500m -J-Xms2500m -J-Xmn512m --fast
Mem 1,081,064MB
19m57.935s
with page.driver.class.server.clear_proxies

jruby -J-Xmx2500m -J-Xms2500m -J-Xmn512m --server
Mem 1,089,388MB
20m14.662s
with page.driver.class.server.clear_proxies

This wasn’t scientific, but it did stop cucumber from leaking so much memory.

Headless Selenium Screenshots with Cucumber 3

Posted by jcnnghm Wed, 30 Dec 2009 11:24:00 GMT

 I was finally able to get Selenium integrated with Webrat and Cucumber on my Linux development server.  I followed this excellent guide to setting up the selenium server to run headless.  This enables me to run my tests without actually seeing the Firefox window.  This does, unfortunately, make debugging a bit more difficult.

Because of that, I created a Cucumber step to save a screenshot of the webpage in the tmp/screenshots folder.  This includes the entire page, not just the viewable portion.  Not only does this simplify debugging, but it also makes it trivially difficult to capture a large number of screenshots from the testing framework to compile visual reports.  I believe that making sure everything looks right is as much a part of testing as making sure it actually works.

 

Then /^I save a screenshot with filename "([^\"]*)"$/ do |filename|
  selenium.capture_entire_page_screenshot(File.expand_path("tmp/screenshots/#{filename}.png"), "")
end

Set Restriction Info with Facebooker 1

Posted by jcnnghm Tue, 22 Sep 2009 11:03:00 GMT

I’ve been working on a Facebook App and I was trying find out exactly how to go about setting content restrictions with Facebooker. While the documentation for the Admin.setRestricitionInfo API call is pretty clear, the Facebooker documentation is less clear.  It looked to me at first glance that the Facebooker::Admin.set_restriction_info method would have to be called with every request.  This seemed terribly innefecient to me, so I tried some other methods out until I found something that worked. In config/initializers, I created a facebook_permissions.rb file, and inserted the following code:

Facebooker::Admin.new(Facebooker::Session.create).set_restriction_info(‘type’ => ‘alcohol’)

This code will run every time your server starts, updating your application restriction settings.  Run the following command from script/console to double-check that the restrictions are being set correctly:

Facebooker::Admin.new(Facebooker::Session.create).get_restriction_info(‘type’)

Select Random Rows Using Ruby On Rails

Posted by jcnnghm Fri, 07 Aug 2009 00:59:00 GMT

Today I was looking for a way to randomly select rows from a database using Rails. I implemented the following message, which will randomly select a specified number of random rows, or as many rows as are available if the requested number is greater than the available number.

def find_random(random_count, options = {})
  if random_count > count(options)
    find_every(options).sort_by{rand}
  else
    find_every(options.merge({:offset => rand(count(options) - random_count + 1), :limit => random_count})).sort_by{rand}
  end
end

The code first attempts to determine if there are enough records to randomize. After that, it randomly selects an offset and returns the specified number of rows at that offset, after randomly sorting them. The benefit that this has over the other methods, is that it should scale well with very large data sets. The downside is, of course, that the are sequential from the given offset. In my situation, this works fine. Depending on what the data is used for, this may not work well for you. If that is the case, the approach I would take is to randomly generate a number of offsets, and select individual records at each offset. This should perform reasonably quickly as long as only a random items are selected.

Slow Runt 3

Posted by jcnnghm Tue, 03 Jun 2008 12:52:00 GMT

While unit testing some code that I’ve been working on that uses the excellent runt library, I ran into a bit of code that was quite slow. When calling the include? method of the Runt::DIMonth class with a negative value for the week of the month argument (i.e. DIMonth.new(-1,6).include?(date)) it took over 120 seconds to execute my unit tests.

I replaced the method max_day_of_month (Runt::TExprUtils), as listed below:

def max_day_of_month(date)
  result = 1
  next_month = nil
  if(date.mon==12)
    next_month = Date.new(date.year+1,1,1)
  else
    next_month = Date.new(date.year,date.mon+1,1)
  end
  date.step(next_month,1){ |d| result=d.day unless d.day < result }
  result
end

with the following code:

def max_day_of_month(date)
  month = date.month
  year = date.year
  if month == 2
    !year.nil? && (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)) ?  29 : 28
  elsif month <= 7
    month % 2 == 0 ? 30 : 31
  else
     month % 2 == 0 ? 31 : 30
  end
end

The same unit tests can now be performed in under two seconds. I didn’t write the algorithm, it’s taken verbatim from the Rails ActiveSupport::CoreExtensions::Time::Calculations::ClassMethods module daysinmonth method. That method appears to be the cause of the issue.

Testing Authorize.net with ActiveMerchant 6

Posted by jcnnghm Sun, 27 Apr 2008 18:55:00 GMT

I had some trouble testing Authorize.net with the excellent rails credit card processing gem ActiveMerchant. The documentation calls for the code ActiveMerchant::Billing::Base.mode = :test, however, this resulted in the following error: The merchant login ID or password is invalid or the account is inactive.

To solve the problem, a key test with the String value true can be added to the gateway initialization hash, as follows:

if ENV['RAILS_ENV'] != 'production'
  gateway = ActiveMerchant::Billing::Base.gateway(:authorize_net).new(
    :login => "login",
    :password => "transkey", :test => 'true')
else
  gateway = ActiveMerchant::Billing::Base.gateway(:authorize_net).new(
    :login => "login",
    :password => "transkey")
end

Using this code, the account is placed into test mode when not run in production mode.

Multi Bit Shift Launch

Posted by jcnnghm Mon, 25 Feb 2008 12:54:00 GMT

I was finally able to launch my new product, Multi Bit Shift, a Flex multiple file upload applet complete with a Rails plugin to ease the integration. I learned a lot from this project, since this is the first that I have made use of project management software, in particular FogBugz, which I would highly recommend.

In particular, I learned that I grossly underestimated the amount of time required to produce proper documentation, the MBS website, and the product screencasts. Proper documentation is really what separates real, professional products from amateur contributions, so it is absolutely vital to dedicate whatever time is necessary to make sure everything is both well and thoroughly documented.

The website, and to a lesser extent the screencasts were also quite time consuming. I had my company, Compucated Solutions produce the design and integrate it with software that I wrote to manage the site. I am very pleased with the design and presentation at this point, but I must admit that I did spend considerable time writing and formatting content, as well as coding up the necessary software bits. I look at this kind of as an investment though, because when I go to release software in the future I have a very flexible engine that I can now quickly roll products out on.

The screencasts were also quite time consuming. I ultimately settled on the TechSmith’s Camtasia Studio product, as recommended by Jeff Atwood to produce the video. I am quite pleased by the results although I would like to be able to set the background color of the video files, and perhaps tweak the appearance a bit. While the software was a little pricey at $299 considering there are lots of competitors and viable alternatives, I ultimately settled on this particular product because it does what it does so very well. If you need to produce screencasts, seriously consider Camtasia, it is worth the premium. If you don’t need to produce screencasts, I suggest that you reconsider because they are a really excellent way to quickly convey complex information.

Besides just creating the screencasts, I also decided to make use of Amazon’s Simple Storage Service to host them. While I probably have ample bandwidth to host the screencasts on my equipment, I ultimately came to the conclusion that the users would have a better user experience if they were hosted by someone else. I have to say that, at this point, I am extremely impressed by the Amazon S3 service. It’s fast, reliable, flexible, easy to use, and inexpensive. I’m currently using the Firefox S3Fox plugin to manage the storage space. It really doesn’t get much easier.

All said and done, I believe I spent just as much, or even more time, documenting and launching this software than I actually did on the design and coding. In the future, I believe I’ll have to give much more thought in the product planning stages to the actual launch requirements (like a website, screencasts, docs, etc…) and the time required for their production.

Rails Boolean Field Helper

Posted by jcnnghm Thu, 07 Feb 2008 22:10:00 GMT

I wrote this some time ago and I thought it was worth reposting here, primarily so it would be easier for me to find. This details the handling of binary fields from front to back in Ruby on Rails.

Creation:

Active Record migrations are quite good at creating tables. The below migration will add table “table” to the db with a boolean field a_boolean when rake db:migrate is run.

class CreateTable < ActiveRecord::Migration
  def self.up
    create_table :table do |t|
      t.column :a_boolean, :boolean
    end
  end

  def self.down
      drop_table :table
  end
end

Boolean Form Field Select Helper:

The following line creates a select box with true and false represented by Yes and No. The currently selected value (while editing) is automatically selected.

<%= select('table', 'a_boolean', [["Yes",true],["No",false]]) %>

Validation:

This validation will confirm that the field is either true or false.

validates_each :a_boolean do |record, attr, value|
  record.errors.add attr, 'is invalid.' if value != true && value != false
end

state_select and Rails 2.0.2 1

Posted by jcnnghm Tue, 05 Feb 2008 09:23:00 GMT

Because of a change in the workings of Rails 2.0.2, the excellent state_select plugin doesn’t work properly. Fortunately, the fix is really easy. After line 35, enter the following code, value = value(object).

This should give you a new to_state_select_tag, like this:

def to_state_select_tag(country, options, html_options)
  html_options = html_options.stringify_keys
  add_default_name_and_id(html_options)
  value = value(object)
  selected_value = options.has_key?(:selected) ? options[:selected] : value
  content_tag("select", add_options(state_options_for_select(selected_value, country), options, value), html_options)
end

That change got the state_select plugin to work for me under Rails 2.

Update: This has been fixed in the latest version. If you are experiencing this issue, update to the latest version.