== Performance Testing Built into Rails ==

As of June 20, 2008 edge rails has had a new type of Unit test geared towards profiling. Of course like most great things, getting it working takes  bit of work. The test relies on statistics gathered from the Garbage Collection that isn't readily available from standard compiled ruby. There is a patch located at http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch[http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch]

Also the test requires a new version of Ruby-Prof version of 0.6.1. It is not readily available at the moment and can most easily be found as a tarball on github. It's repository is located at git://github.com/jeremy/ruby-prof.git.

What follows is a description of how to set up an alternative ruby install to use these features

=== Compiling the Interpreter ===


[source, shell]
----------------------------------------------------------------------------
[User ~]$ mkdir rubygc
[User ~]$ wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p111.tar.gz
[User ~]$ tar -xzvf ruby-1.8.6-p111.tar.gz
[User ~]$ cd ruby-1.8.6-p111
[User ruby-1.8.6-p111]$ curl http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch | patch -p0

#I like putting my alternative ruby builds in an opt directory, set the prefix to where ever you feel is most comfortable.

[User ruby-1.8.6-p111]$ ./configure --prefix=/opt/rubygc
[User ruby-1.8.6-p111]$ sudo make && make install
----------------------------------------------------------------------------

Add the following lines in your \~/.profile or \~/.bash\_login for convenience.

----------------------------------------------------------------------------
alias gcruby='/opt/rubygc/rubygc/bin/ruby'
alias gcrake='/opt/rubygc/rubygc/bin/rake'
alias gcgem='/opt/rubygc/rubygc/bin/gem'
alias gcirb=/opt/rubygc/rubygc/bin/irb'
alias gcrails='/opt/rubygc/rubygc/bin/rails'
----------------------------------------------------------------------------

=== Installing RubyGems ===

Next we need to install rubygems and rails so that we can use the interpreter properly. 


[source, shell]
----------------------------------------------------------------------------
[User ~]$ wget http://rubyforge.org/frs/download.php/38646/rubygems-1.2.0.tgz
[User ~]$ tar -xzvf rubygems-1.2.0.tgz 
[User ~]$ cd rubygems-1.2.0
[User rubygems-1.2.0]$ gcruby setup.rb
[User rubygems-1.2.0]$ cd ~
[User ~]$ gcgem install rake
[User ~]$ gcgem install mysql
[User ~]$ gcgem install rails
----------------------------------------------------------------------------

If installing mysql gem fails ( like it did for me ), you will have to manually install it :

[source, shell]
----------------------------------------------------------------------------
[User ~]$ cd /Users/lifo/rubygc/lib/ruby/gems/1.8/gems/mysql-2.7/
[User mysql-2.7]$ gcruby extconf.rb --with-mysql-config
[User mysql-2.7]$ make && make install
----------------------------------------------------------------------------

=== Installing Jeremy Kemper's ruby-prof ===

We are in the home stretch. All we need now is ruby-proff 0.6.1


[source, shell]
----------------------------------------------------------------------------
[User ~]$ git clone git://github.com/jeremy/ruby-prof.git
[User ~]$ cd ruby-prof/
[User ruby-prof (master)]$ gcrake gem
[User ruby-prof (master)]$ gcgem install pkg/ruby-prof-0.6.1.gem
----------------------------------------------------------------------------

Finished, go get yourself a power drink!

=== Ok so I lied, a few more things we need to do ===

You have everything we need to start profiling through rails Unit Testing. Unfortunately we are still missing a few files. I'm going to do the next step on a fresh Rails app, but it will work just as well on developmental 2.1 rails application. 

==== The Rails App ====

First I need to generate a rail app

[source, shell]
----------------------------------------------------------------------------
[User ~]$ gcrails profiling_tester -d mysql
[User ~]$ cd profiling_tester
[User profiling_tester]$ script/generate scaffold item name:string
[User profiling_tester]$ gcrake db:create:all
[User profiling_tester]$ gcrake db:migrate
[User profiling_tester (master)]$ rm public/index.html
----------------------------------------------------------------------------

Now I'm going to init it as a git repository and add edge rails as a submodule to it. 

[source, shell]
----------------------------------------------------------------------------
[User profiling_tester]$ git init
[User profiling_tester (master)]$ git submodule add git://github.com/rails/rails.git vendor/rails
----------------------------------------------------------------------------

Finally we want to  change config.cache_classes to true in our environment.rb

----------------------------------------------------------------------------
config.cache_classes = true
----------------------------------------------------------------------------

If we don't cache classes, then the time Rails spends reloading and compiling our models and controllers will confound our results.  Obviously we will try to make our test setup as similar as possible to our production environment.

=== Generating and Fixing the tests ===

Ok next we need to generate the test script. 

[source, shell]
----------------------------------------------------------------------------
[User profiling_tester (master)]$ script/generate performance_test homepage
----------------------------------------------------------------------------

This will generate _test/performance/homepage_test.rb_ for you. However, as I have generated the project using Rails 2.1 gem, we'll need to manually generate one more file before we can go ahead.

We need to put the following inside _test/performance/test_helper.rb


[source, ruby]
----------------------------------------------------------------------------
require 'test_helper'
require 'performance_test_help'
----------------------------------------------------------------------------

Though this depends where you run your tests from and your system config. I myself run my tests from the Application root directory 

so instead of 

[source, ruby]
----------------------------------------------------------------------------
require 'test_helper'

#I have

require 'test/test_helper'
----------------------------------------------------------------------------

Also I needed to change homepage_test.rb to reflect this also

[source, ruby]
----------------------------------------------------------------------------
require 'test/performance/test_helper.rb'
----------------------------------------------------------------------------

=== Testing ===

#TODO is there some way to compare multiple request at once like ruby_analyze

Now, if we look at the generated performance test ( one we generated using _script/generate performance_test_ ), it'll look something like :

[source, ruby]
----------------------------------------------------------------------------
.require 'performance/test_helper'

class HomepageTest < ActionController::PerformanceTest
  # Replace this with your real tests.
  def test_homepage
    get '/'
  end
end
----------------------------------------------------------------------------


The format looks very similar to that of an integration test. And guess what, that's what it is. But that doesn't stop you from testing your Model methods. You could very well write something like :

[source, ruby]
----------------------------------------------------------------------------
require 'performance/test_helper'

class UserModelTest < ActionController::PerformanceTest
  # Replace this with your real tests.
  def test_slow_find
    User.this_takes_shlong_to_run
  end
end
----------------------------------------------------------------------------


Which is very useful way to profile individual processes. 
