labs

Keep Your Build Fast with a Timebomb

The virtues of having a fast build have been well extolled. While there are strategies for speeding up an existing slow build, let’s look at a tool that can help us keep us from getting in that position. We will assume you are using a current version of RSpec to test Ruby code.

Before we look at the code, let’s outline the approach. First, we will add a setting to RSpec’s configuration that specifies how long of a build you want to tolerate. Next, we’ll adds hooks that make sure the build will fail if it takes longer than the time you specify.

spec/support/build_bomb.rb



class BuildBomb

  include Singleton

  attr_accessor :start_time, :fail_after

  def self.setup(rspec_config)
    raise 'Your version of RSpec does not allow settings to be added' unless rspec_config.respond_to?(:add_setting)

    self.add_bomb_settings(rspec_config)
    self.add_bomb_hooks(rspec_config)
  end

  def build_started(fail_after)
    self.start_time = Time.now
    self.fail_after = fail_after
  end

  def build_ended(end_time)
    if build_was_too_slow?(start_time, end_time, fail_after)
      raise BuildBomb::BuildTooSlowException.new('Build was too slow!!!')
    end
  end

  private

  def self.add_bomb_settings(config)
    config.add_setting :fail_after
  end

  def self.add_bomb_hooks(config)
    config.before(:suite) do
      BuildBomb.instance.build_started(config.fail_after)
    end

    config.after(:suite) do
      BuildBomb.instance.build_ended(Time.now)
    end
  end

  def build_was_too_slow?(start_time, end_time, max_time)
    (end_time - start_time) > max_time
  end

  class BuildTooSlowException < RuntimeError;
  end
end

BuildBomb.setup(RSpec.configuration)

Then configure the fail_after setting in your spec helper.

spec/spec_helper.rb


...
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }

RSpec.configure do |config|
  ...
  config.fail_after = 60  # seconds to fail you build after
  ...
end


That’s it. With this setting, the build bomb will go off if your build takes longer than 60 seconds.


Hunters-MacBook-Pro:bomber-rails huntergillane$ rspec spec
.

Finished in 60.06 seconds
1 example, 0 failures

Randomized with seed 6736

/Users/huntergillane/workspace/gems/bomber-rails/spec/support/build_bomb.rb:21:in `build_ended': Build was too slow!!! (BuildBomb::BuildTooSlowException)
from /Users/huntergillane/workspace/gems/bomber-rails/spec/support/build_bomb.rb:37:in `block in add_bomb_hooks'
from /Users/huntergillane/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:470:in `instance_eval'
from /Users/huntergillane/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:470:in `instance_eval_with_rescue'
...

Mike Barinek first introduced this on a project I was working on last year and it has since proven to be a useful tool to have. Even if you have an existing slow build, you could use something like this to keep it from getting slower.