boomerang-trample 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 James Golick
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,57 @@
1
+ = trample
2
+
3
+ Trample is a flexible load simulation tool.
4
+
5
+ Most load sim tools make requests to a static list of urls. They spawn n threads and make requests to the urls on the list in succession, in each thread. Unfortunately, though, if your applicaition makes use of any kind of caching (including your database's internal caching facilities), this kind of load simulation is unrealistic.
6
+
7
+ The data required to serve a single set of urls is likely to fit nicely in the database server's cache (even on a small server). So, having a single user hammer a finite set of pages will make your application look much faster than it really is.
8
+
9
+ Trample allows you to provide parameter values inside of a block (a lambda function, for non-rubyists). The block is executed each time the parameters are needed. So, if you use randomization in the block, you should (theoretically) get different values every time.
10
+
11
+ That way, you can have trample log in as several different users and request different data in each session.
12
+
13
+ = How To
14
+
15
+ Trample uses a ruby DSL for configuration.
16
+
17
+ Trample.configure do
18
+ concurrency 5
19
+ iterations 10
20
+
21
+ #
22
+ # GETs & POSTs that follow are executed sequentially inside a thread.
23
+ # Results are stored in thread local storage in Thread.current[:result]
24
+ # Any intermediate results you want to use also need to go into thread local storage!
25
+ #
26
+ # YOU HAVE BEEN WARNED!
27
+ #
28
+
29
+ login do
30
+ post "http://yoursite.com/login" do
31
+ {:username => User.random.username, :password => "swordfish"}
32
+ end
33
+ end
34
+ get "http://yoursite.com/somewhere"
35
+ post "http://yoursite.com/something" do
36
+ {:a_parameter => "a value"}
37
+ end
38
+ get "http://yoursite.com/someresources/:id" do
39
+ {:id => SomeResource.random.id}
40
+ end
41
+
42
+ # By default, get will accept 'application/xml', so you may run into
43
+ # odd behavior with rails respond_to blocks. This will allow you
44
+ # to send a request that Rails will handle and return a 200 instead of
45
+ # a 406 status message.
46
+ get "http://yoursite.com/path/to/resource", :headers => {:accept => 'text/html'}
47
+ end
48
+
49
+ The above configuration will start 5 (concurrency) sessions by logging in as a random user at the url in the login block. Then, it'll hit the three urls underneath it 10 (iterations) times during each session.
50
+
51
+ To run trample, save your configuration somewhere and run:
52
+
53
+ trample start /path/to/your/trample/config.rb
54
+
55
+ == Copyright
56
+
57
+ Copyright (c) 2009 James Golick. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "boomerang-trample"
8
+ gem.summary = %Q{A simple command line tool for stress testing remote resources.}
9
+ gem.email = "makesson@csc.com"
10
+ gem.homepage = "http://github.com/boomerang/trample"
11
+ gem.authors = ["James Golick", 'Jeremy Friesen']
12
+
13
+ gem.add_dependency "rest-client"
14
+ gem.add_dependency "thor"
15
+ gem.add_dependency "log4r"
16
+ gem.add_development_dependency "rake"
17
+ gem.add_development_dependency "shoulda"
18
+ gem.add_development_dependency "rr"
19
+ gem.add_development_dependency "jeweler"
20
+
21
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
22
+ end
23
+ rescue LoadError
24
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
25
+ end
26
+
27
+ require 'rake/rdoctask'
28
+ Rake::RDocTask.new do |rdoc|
29
+ rdoc.rdoc_dir = 'rdoc'
30
+ rdoc.title = 'trample'
31
+ rdoc.options << '--line-numbers' << '--inline-source'
32
+ rdoc.rdoc_files.include('README*')
33
+ rdoc.rdoc_files.include('lib/**/*.rb')
34
+ end
35
+
36
+ require 'rake/testtask'
37
+ Rake::TestTask.new(:test) do |test|
38
+ test.libs << 'lib' << 'test'
39
+ test.pattern = 'test/**/*_test.rb'
40
+ test.verbose = false
41
+ end
42
+
43
+ begin
44
+ require 'rcov/rcovtask'
45
+ Rcov::RcovTask.new do |test|
46
+ test.libs << 'test'
47
+ test.pattern = 'test/**/*_test.rb'
48
+ test.verbose = true
49
+ end
50
+ rescue LoadError
51
+ task :rcov do
52
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
53
+ end
54
+ end
55
+
56
+
57
+ task :default => :test
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 1
data/bin/trample ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require File.dirname(__FILE__) + '/../lib/trample'
5
+
6
+ Trample::Cli.start
7
+
data/lib/trample.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'log4r'
2
+ require 'rest_client'
3
+
4
+ module Trample
5
+ autoload :Configuration, File.dirname(__FILE__) + "/trample/configuration"
6
+ autoload :Page, File.dirname(__FILE__) + "/trample/page"
7
+ autoload :Session, File.dirname(__FILE__) + "/trample/session"
8
+ autoload :Runner, File.dirname(__FILE__) + "/trample/runner"
9
+ autoload :Cli, File.dirname(__FILE__) + "/trample/cli"
10
+ autoload :Logging, File.dirname(__FILE__) + "/trample/logging"
11
+ autoload :Timer, File.dirname(__FILE__) + "/trample/timer"
12
+
13
+ class << self
14
+ attr_reader :current_configuration
15
+
16
+ def configure(&block)
17
+ @current_configuration = Configuration.new(&block)
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,12 @@
1
+ require 'thor'
2
+
3
+ module Trample
4
+ class Cli < Thor
5
+ desc "start path/to/config/file", "Start trampling"
6
+ def start(config)
7
+ load(config)
8
+ Runner.new(Trample.current_configuration).trample
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,48 @@
1
+ module Trample
2
+ class Configuration
3
+ attr_reader :pages
4
+
5
+ def initialize(&block)
6
+ @pages = []
7
+ instance_eval(&block)
8
+ end
9
+
10
+ def concurrency(*value)
11
+ @concurrency = value.first unless value.empty?
12
+ @concurrency
13
+ end
14
+
15
+ def iterations(*value)
16
+ @iterations = value.first unless value.empty?
17
+ @iterations
18
+ end
19
+
20
+ def get(url, *args, &block)
21
+ args << block if block_given?
22
+
23
+ @pages << Page.new(:get, url, *args)
24
+ end
25
+
26
+ def post(url, *args, &block)
27
+ args << block if block_given?
28
+
29
+ @pages << Page.new(:post, url, *args)
30
+ end
31
+
32
+ def login
33
+ if block_given?
34
+ yield
35
+ @login = pages.pop
36
+ end
37
+
38
+ @login
39
+ end
40
+
41
+ def ==(other)
42
+ other.is_a?(Configuration) &&
43
+ other.pages == pages &&
44
+ other.concurrency == concurrency &&
45
+ other.iterations == iterations
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,14 @@
1
+ module Trample
2
+ module Logging
3
+ def logger
4
+ init_logger if Log4r::Logger['main'].nil?
5
+ Log4r::Logger['main']
6
+ end
7
+
8
+ protected
9
+ def init_logger
10
+ logger = Log4r::Logger.new('main')
11
+ logger.outputters = Log4r::Outputter.stdout
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,49 @@
1
+ module Trample
2
+ class Page
3
+ attr_reader :request_method, :headers
4
+
5
+ def initialize(request_method, url, *args)
6
+ @request_method = request_method
7
+ @url = url
8
+
9
+ parameters = args.pop
10
+ if parameters.respond_to?(:call)
11
+ if options = args.shift
12
+ @headers = options.delete(:headers) || options.delete('headers')
13
+ end
14
+ elsif parameters
15
+ @headers = parameters.delete(:headers) || parameters.delete('headers') || {}
16
+ end
17
+ @headers ||= {}
18
+ @parameters = parameters
19
+ end
20
+
21
+ def parameters
22
+ proc_params? ? @parameters.call : @parameters
23
+ end
24
+
25
+ def ==(other)
26
+ other.is_a?(Page) &&
27
+ other.request_method == request_method &&
28
+ other.url == url
29
+ end
30
+
31
+ def url
32
+ proc_params? ? interpolated_url : @url
33
+ end
34
+
35
+ protected
36
+ def proc_params?
37
+ @parameters.respond_to?(:call)
38
+ end
39
+
40
+ def interpolated_url
41
+ params = parameters # cache called proc
42
+ url = @url.dup
43
+ url.scan(/\:[a-zA-Z]\w+/).each do |m|
44
+ url.gsub!(m, params[m.gsub(/:/, '').to_sym].to_s)
45
+ end
46
+ url
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,29 @@
1
+ module Trample
2
+ class Runner
3
+ include Logging
4
+
5
+ attr_reader :config, :threads
6
+
7
+ def initialize(config)
8
+ @config = config
9
+ @threads = []
10
+ end
11
+
12
+ def trample
13
+ start_time = Time.now
14
+ logger.info "Starting trample at #{start_time}..."
15
+
16
+ config.concurrency.times do
17
+ thread = Thread.new(@config) do |c|
18
+ Session.new(c).trample
19
+ end
20
+ threads << thread
21
+ end
22
+
23
+ threads.each { |t| t.join }
24
+
25
+ logger.info "Trample completed at #{Time.now}, duration #{Time.now.to_f - start_time.to_f}s ..."
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,49 @@
1
+ module Trample
2
+ class Session
3
+ include Logging
4
+ include Timer
5
+
6
+ attr_reader :config, :response_times, :cookies, :last_response
7
+
8
+ def initialize(config)
9
+ @config = config
10
+ @response_times = []
11
+ @cookies = {}
12
+ end
13
+
14
+ def trample
15
+ start_time = Time.now
16
+ hit @config.login unless @config.login.nil?
17
+ @config.iterations.times do
18
+ @config.pages.each do |p|
19
+ hit p
20
+ end
21
+ end
22
+ logger.info "#{Thread.current} duration: #{Time.now.to_f - start_time.to_f}s"
23
+ end
24
+
25
+ protected
26
+ def hit(page)
27
+ response_times << request(page)
28
+ # this is ugly, but it's the only way that I could get the test to pass
29
+ # because rr keeps a reference to the arguments, not a copy. ah well.
30
+ @cookies = cookies.merge(last_response.cookies)
31
+ logger.info "#{page.request_method.to_s.upcase} #{page.url} #{response_times.last}s #{last_response.code}"
32
+ Thread.current[:result] = @last_response
33
+ end
34
+
35
+ def request(page)
36
+ time do
37
+ @last_response = send(page.request_method, page)
38
+ end
39
+ end
40
+
41
+ def get(page)
42
+ RestClient.get(page.url, page.headers.merge(:cookies => cookies))
43
+ end
44
+
45
+ def post(page)
46
+ RestClient.post(page.url, page.parameters,page.headers.merge(:cookies => cookies))
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,9 @@
1
+ module Trample
2
+ module Timer
3
+ def time
4
+ start = Time.now
5
+ yield
6
+ Time.now.to_f - start.to_f
7
+ end
8
+ end
9
+ end
data/test/cli_test.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'test_helper'
2
+
3
+ class CliTest < Test::Unit::TestCase
4
+ context "Running the CLI" do
5
+ setup do
6
+ Trample::Cli.new.start('test/fixtures/basic_config.rb')
7
+ end
8
+
9
+ before_should "start a trample with the current_config" do
10
+ load('test/fixtures/basic_config.rb')
11
+ stub.proxy(Trample::Runner).new(Trample.current_configuration) do |r|
12
+ mock(r).trample
13
+ r
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,103 @@
1
+ require 'test_helper'
2
+
3
+ class ConfigurationTest < Test::Unit::TestCase
4
+ context "Configuring trample" do
5
+ setup do
6
+ @params_proc = lambda { { :q => "the meaning of life" } }
7
+ @config = Trample::Configuration.new do
8
+ concurrency 2
9
+ iterations 1
10
+ get "http://google.com/" do
11
+ {:a => 'b'}
12
+ end
13
+ post "http://google.com/", {:q => "something"}
14
+ post "http://google.com/", &@params_proc
15
+ login do
16
+ post "http://google.com/login" do
17
+ { :q => "whatever" }
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ should "set concurrency" do
24
+ assert_equal 2, @config.concurrency
25
+ end
26
+
27
+ should "set iterations" do
28
+ assert_equal 1, @config.iterations
29
+ end
30
+
31
+ should "add get requests to an array of pages" do
32
+ assert_equal Trample::Page.new(:get, "http://google.com/"), @config.pages.first
33
+ end
34
+
35
+ should "add get params to the array of pages" do
36
+ assert_equal({:a => "b"}, @config.pages.first.parameters)
37
+ end
38
+
39
+ should "add post requests to the array of pages, including their params" do
40
+ expected = Trample::Page.new(:post, "http://google.com/", {:q => "something"})
41
+ assert_equal expected, @config.pages[1]
42
+ end
43
+
44
+ should "add post requests to the array of pages, including their block-based params" do
45
+ expected = Trample::Page.new(:post, "http://google.com/", @params_proc)
46
+ assert_equal expected, @config.pages[2]
47
+ end
48
+
49
+ should "support a login parameter which contains a page to hit with params" do
50
+ assert_equal "http://google.com/login", @config.login.url
51
+ assert_equal({:q => "whatever"}, @config.login.parameters)
52
+ end
53
+
54
+ should "be equal if all the objects are the same" do
55
+ identical_config = Trample::Configuration.new do
56
+ concurrency 2
57
+ iterations 1
58
+ get "http://google.com/"
59
+ post "http://google.com/", {:q => "something"}
60
+ post "http://google.com/", &@params_proc
61
+ end
62
+ assert_equal identical_config, @config
63
+ end
64
+
65
+ should "not be equal if any of the objects are different" do
66
+ non_identical_config = Trample::Configuration.new do
67
+ concurrency 3
68
+ iterations 1
69
+ get "http://google.com/"
70
+ end
71
+ assert_not_equal non_identical_config, @config
72
+ end
73
+ end
74
+ context 'with headers and explicit parameters' do
75
+ setup do
76
+ @config = Trample::Configuration.new do
77
+ get 'http://google.com/:id', :headers => {:accept => 'text/html'} do
78
+ {:id => 5}
79
+ end
80
+ post 'http://google.com/:id', :headers => {:accept => 'text/html'} do
81
+ {:id => 2}
82
+ end
83
+ end
84
+ end
85
+
86
+ should "set the headers for :get" do
87
+ assert_equal({:accept => 'text/html'}, @config.pages.first.headers)
88
+ end
89
+
90
+ should "set the headers for :post" do
91
+ assert_equal({:accept => 'text/html'}, @config.pages.last.headers)
92
+ end
93
+
94
+ should "allow headers and parameters to be passed for :get" do
95
+ assert_equal('http://google.com/5', @config.pages.first.url)
96
+ end
97
+
98
+ should "allow headers and parameters to be passed for :post" do
99
+ assert_equal('http://google.com/2', @config.pages.last.url)
100
+ end
101
+ end
102
+ end
103
+
@@ -0,0 +1,5 @@
1
+ Trample.configure do |t|
2
+ concurrency 2
3
+ iterations 1
4
+ get "http://google.com"
5
+ end
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+
3
+ class TrampleASingleUrlTest < Test::Unit::TestCase
4
+ def test_trampling_a_single_url
5
+ mock_get("http://google.com", :times => 2)
6
+ trample "test/fixtures/basic_config.rb"
7
+ end
8
+ end
9
+
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+
3
+ class LoggingTest < Test::Unit::TestCase
4
+ context "The logging module" do
5
+ setup do
6
+ @object = Class.new do
7
+ include Trample::Logging
8
+ end.new
9
+ @object.send(:init_logger)
10
+ end
11
+
12
+ should "return Log4r::Logger['main'] for #logger" do
13
+ assert_equal Log4r::Logger['main'], @object.logger
14
+ end
15
+ end
16
+ end
data/test/page_test.rb ADDED
@@ -0,0 +1,96 @@
1
+ require 'test_helper'
2
+
3
+ class PageTest < Test::Unit::TestCase
4
+ context "A page" do
5
+ setup do
6
+ @page = Trample::Page.new(:get, "http://google.com/", :username => "joetheuser")
7
+ end
8
+
9
+ should "have a request_method" do
10
+ assert_equal :get, @page.request_method
11
+ end
12
+
13
+ should "have a url" do
14
+ assert_equal "http://google.com/", @page.url
15
+ end
16
+
17
+ should "have request parameters" do
18
+ assert_equal({:username => "joetheuser"}, @page.parameters)
19
+ end
20
+
21
+ should "be equal with the same request_method and url" do
22
+ assert_equal Trample::Page.new(:get, "http://google.com"), Trample::Page.new(:get, "http://google.com")
23
+ end
24
+
25
+ should "not be equal with a different request_method or url" do
26
+ assert_not_equal Trample::Page.new(:get, "http://google.com"), Trample::Page.new(:get, "http://google.com/asdf")
27
+ end
28
+ end
29
+
30
+ context "with headers and explicit parameters" do
31
+ setup do
32
+ @page = Trample::Page.new(:get, 'http://google.com/:id', :headers => {:accepts => 'text/html'}, :id => 5)
33
+ end
34
+ should 'extract headers from parameters' do
35
+ assert_equal({:accepts => 'text/html'}, @page.headers)
36
+ end
37
+
38
+ should 'omit the headers from the parameters' do
39
+ assert_equal({:id => 5}, @page.parameters)
40
+ end
41
+ end
42
+
43
+ context 'with headers and interpoloated parameters' do
44
+ setup do
45
+ @page = Trample::Page.new(:get, 'http://google.com/:id', {:headers => {:accepts => 'text/html'}}, lambda { {:id => 5}})
46
+ end
47
+
48
+ should 'extract headers from parameters' do
49
+ assert_equal( {:accepts => 'text/html'}, @page.headers)
50
+ end
51
+
52
+ should 'interpolate parameters' do
53
+ assert_equal( {:id => 5}, @page.parameters)
54
+ end
55
+ end
56
+
57
+ context "Block-based request parameters for POST requests" do
58
+ setup do
59
+ @page = Trample::Page.new(:post, "http://google.com/:id", lambda { { :id => 1, :username => "joetheuser" } })
60
+ end
61
+
62
+ should "be resolved at call time" do
63
+ assert_equal({:username => "joetheuser", :id => 1}, @page.parameters)
64
+ end
65
+
66
+ should "interpolate parameters into the url" do
67
+ assert_equal "http://google.com/1", @page.url
68
+ end
69
+ end
70
+
71
+ context "Block based parameters for GET requests" do
72
+ setup do
73
+ @page = Trample::Page.new(:get, "http://mysite.com/somethings/:id", lambda { {:id => 5} })
74
+ end
75
+
76
+ should "interpolate those parameters with the url string" do
77
+ assert_equal "http://mysite.com/somethings/5", @page.url
78
+ end
79
+
80
+ should "interpolate a different parameter each time" do
81
+ page = Trample::Page.new(:get, "http://mysite.com/somethings/:id", lambda { {:id => rand(1000)} })
82
+ assert_not_equal page.url, page.url
83
+ end
84
+ end
85
+
86
+ context "A page with port number and block based parameters" do
87
+ setup do
88
+ @page = Trample::Page.new(:get, "http://localhost:3000/somethings/:id", lambda { {:id => 1} })
89
+ end
90
+
91
+ should "not interpolate the port" do
92
+ assert_equal "http://localhost:3000/somethings/1", @page.url
93
+ end
94
+ end
95
+ end
96
+
@@ -0,0 +1,35 @@
1
+ require 'test_helper'
2
+
3
+ class RunnerTest < Test::Unit::TestCase
4
+ def setup
5
+ @config = Trample::Configuration.new do |t|
6
+ t.concurrency 2
7
+ t.iterations 5
8
+ end
9
+ end
10
+
11
+ context "Running a trample" do
12
+ setup do
13
+ @runner = Trample::Runner.new(@config)
14
+ @runner.trample
15
+ end
16
+
17
+ before_should "spawn <concurrency> threads" do
18
+ mock.proxy(Thread).new(@config).times(2)
19
+ end
20
+
21
+ before_should "start <concurrency> sessions trampling" do
22
+ stub.proxy(Trample::Session).new(@config).times(2) do |s|
23
+ mock(s).trample
24
+ s
25
+ end
26
+ end
27
+
28
+ before_should "block until all the threads terminate" do
29
+ stub.proxy(Thread).new(@config) do |t|
30
+ mock(t).join
31
+ t
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,114 @@
1
+ require 'test_helper'
2
+
3
+ class SessionTest < Test::Unit::TestCase
4
+ def setup
5
+ @config = Trample::Configuration.new do
6
+ iterations 2
7
+ get "http://google.com/"
8
+ get "http://amazon.com/"
9
+ post "http://google.com/"
10
+ end
11
+ @session = Trample::Session.new(@config)
12
+ end
13
+
14
+ context "Starting a trample session" do
15
+ setup do
16
+ @session.trample
17
+ end
18
+
19
+ before_should "visit the pages iterations times each" do
20
+ mock_get("http://google.com/", :times => 2)
21
+ mock_get("http://amazon.com/", :times => 2)
22
+ mock_post("http://google.com/", :times => 2)
23
+ end
24
+ end
25
+
26
+ context "Visiting a page" do
27
+ setup do
28
+ stub(@session).time { 1.4 }
29
+ stub(@session).last_response do
30
+ response = RestClient::Response.new("", stub!)
31
+ stub(response).cookies { {} }
32
+ stub(response).code { 200 }
33
+ end
34
+ @session.trample
35
+ end
36
+
37
+ should "record the length of time it took to visit that page" do
38
+ assert_equal [1.4, 1.4, 1.4, 1.4, 1.4, 1.4], @session.response_times
39
+ end
40
+ end
41
+
42
+ context "If a page responds with a cookie" do
43
+ should "pass that cookie on to the next page" do
44
+ stub(RestClient).get(anything, anything) do
45
+ response = RestClient::Response.new("", stub!)
46
+ stub(response).cookies { {"xyz" => "abc"} }
47
+ stub(response).code { 200 }
48
+ end
49
+
50
+ @config = Trample::Configuration.new do
51
+ iterations 2
52
+ get "http://amazon.com/"
53
+ end
54
+
55
+ @session = Trample::Session.new(@config)
56
+ @session.trample
57
+
58
+ assert_received(RestClient) { |c| c.get("http://amazon.com/", :cookies => {"xyz" => "abc"}) }
59
+ end
60
+ end
61
+
62
+ context "A session with login" do
63
+ # TODO: the order of the requests isn't being tested here. not
64
+ # sure if it's possible with rr
65
+ should "hit the login once at the beginning of the session" do
66
+ @config = Trample::Configuration.new do
67
+ iterations 2
68
+ login do
69
+ post "http://google.com/login" do
70
+ {:user => "xyz", :password => "swordfish"}
71
+ end
72
+ end
73
+ get "http://google.com/"
74
+ end
75
+ stub_get(anything, :times => 2)
76
+ mock_post("http://google.com/login", :payload => {:user => "xyz", :password => "swordfish"}, :times => 1)
77
+ Trample::Session.new(@config).trample
78
+ end
79
+ end
80
+
81
+ should 'should pass headers on get' do
82
+ @config = Trample::Configuration.new do
83
+ iterations 2
84
+ get "http://google.com", :headers => {:accept => 'text/html'}
85
+ end
86
+ mock_get('http://google.com', :accept => 'text/html', :times => 2)
87
+
88
+ @session = Trample::Session.new(@config)
89
+ @session.trample
90
+
91
+ assert_equal 200, @session.last_response.code
92
+ end
93
+
94
+ should 'should pass headers on post' do
95
+ @config = Trample::Configuration.new do
96
+ iterations 1
97
+ login do
98
+ post "http://google.com/login", :headers => {:accept => 'text/html'} do
99
+ {:user => "xyz", :password => "swordfish"}
100
+ end
101
+ end
102
+ end
103
+ mock_post('http://google.com/login', :payload => {:user => "xyz", :password => "swordfish"}, :accept => 'text/html', :times => 1)
104
+
105
+ @session = Trample::Session.new(@config)
106
+ @session.trample
107
+
108
+ assert_equal 200, @session.last_response.code
109
+ end
110
+
111
+
112
+
113
+ end
114
+
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'rr'
5
+
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ require 'trample'
9
+
10
+ class Test::Unit::TestCase
11
+ include RR::Adapters::TestUnit unless include?(RR::Adapters::TestUnit)
12
+
13
+ protected
14
+ def trample(config)
15
+ Trample::Cli.new.start(config)
16
+ end
17
+
18
+ def mock_get(url, opts={})
19
+ return_cookies = opts.delete(:return_cookies) || {}
20
+ opt_times = opts.delete(:times) || 1
21
+ opts[:cookies] ||= {}
22
+
23
+ mock(RestClient).get(url, opts).times(opt_times) do
24
+ response = RestClient::Response.new("", stub!)
25
+ stub(response).cookies { return_cookies }
26
+ stub(response).code { 200 }
27
+ end
28
+ end
29
+
30
+ def mock_post(url, opts={})
31
+ payload = opts.delete(:payload)
32
+ return_cookies = opts.delete(:return_cookies) || {}
33
+ opt_times = opts.delete(:times) || 1
34
+ opts[:cookies] ||= {}
35
+
36
+ mock(RestClient).post(url, payload, opts).times(opt_times) do
37
+ response = RestClient::Response.new("", stub!)
38
+ stub(response).cookies { return_cookies }
39
+ stub(response).code { 200 }
40
+ end
41
+ end
42
+
43
+ def stub_get(url, opts = {})
44
+ return_cookies = opts.delete(:return_cookies) || {}
45
+ opt_times = opts.delete(:times) || 1
46
+ opts[:cookies] ||= {}
47
+
48
+ stub(RestClient).get(url, opts).times(opt_times) do
49
+ response = RestClient::Response.new("", stub!)
50
+ stub(response).cookies { return_cookies }
51
+ stub(response).code { 200 }
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,23 @@
1
+ require 'test_helper'
2
+
3
+ class TimerTest < Test::Unit::TestCase
4
+ context "Timing the execution of a block" do
5
+ setup do
6
+ now = Time.now
7
+ invocations = 0
8
+ stub(Time).now do
9
+ invocations += 1
10
+ # not sure why we have to start at 1,
11
+ # but this seems to get called somewhere
12
+ invocations == 2 ? now : (now + 1.5)
13
+ end
14
+
15
+ @obj = Class.new { include Trample::Timer }.new
16
+ end
17
+
18
+ should "return the amount of time elapsed while the block was run" do
19
+ assert_equal(1.5, @obj.time {})
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+
3
+ class TrampleTest < Test::Unit::TestCase
4
+ context "Configuring Trample" do
5
+ setup do
6
+ Trample.configure do |t|
7
+ t.concurrency 5
8
+ t.iterations 1
9
+ end
10
+ end
11
+
12
+ should "assign the configuration to Trample.current_configuration" do
13
+ assert_equal 5, Trample.current_configuration.concurrency
14
+ end
15
+ end
16
+ end
data/trample.gemspec ADDED
@@ -0,0 +1,98 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{boomerang-trample}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["James Golick", "Jeremy Friesen"]
12
+ s.date = %q{2010-04-08}
13
+ s.default_executable = %q{trample}
14
+ s.email = %q{makesson@csc.com}
15
+ s.executables = ["trample"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION.yml",
26
+ "bin/trample",
27
+ "lib/trample.rb",
28
+ "lib/trample/cli.rb",
29
+ "lib/trample/configuration.rb",
30
+ "lib/trample/logging.rb",
31
+ "lib/trample/page.rb",
32
+ "lib/trample/runner.rb",
33
+ "lib/trample/session.rb",
34
+ "lib/trample/timer.rb",
35
+ "test/cli_test.rb",
36
+ "test/configuration_test.rb",
37
+ "test/fixtures/basic_config.rb",
38
+ "test/integration/trample_a_single_url_test.rb",
39
+ "test/logging_test.rb",
40
+ "test/page_test.rb",
41
+ "test/runner_test.rb",
42
+ "test/session_test.rb",
43
+ "test/test_helper.rb",
44
+ "test/timer_test.rb",
45
+ "test/trample_test.rb",
46
+ "trample.gemspec"
47
+ ]
48
+ s.homepage = %q{http://github.com/boomerang/trample}
49
+ s.rdoc_options = ["--charset=UTF-8"]
50
+ s.require_paths = ["lib"]
51
+ s.rubygems_version = %q{1.3.6}
52
+ s.summary = %q{A simple command line tool for stress testing remote resources.}
53
+ s.test_files = [
54
+ "test/cli_test.rb",
55
+ "test/configuration_test.rb",
56
+ "test/fixtures/basic_config.rb",
57
+ "test/integration/trample_a_single_url_test.rb",
58
+ "test/logging_test.rb",
59
+ "test/page_test.rb",
60
+ "test/runner_test.rb",
61
+ "test/session_test.rb",
62
+ "test/test_helper.rb",
63
+ "test/timer_test.rb",
64
+ "test/trample_test.rb"
65
+ ]
66
+
67
+ if s.respond_to? :specification_version then
68
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
69
+ s.specification_version = 3
70
+
71
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
72
+ s.add_runtime_dependency(%q<rest-client>, [">= 0"])
73
+ s.add_runtime_dependency(%q<thor>, [">= 0"])
74
+ s.add_runtime_dependency(%q<log4r>, [">= 0"])
75
+ s.add_development_dependency(%q<rake>, [">= 0"])
76
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
77
+ s.add_development_dependency(%q<rr>, [">= 0"])
78
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
79
+ else
80
+ s.add_dependency(%q<rest-client>, [">= 0"])
81
+ s.add_dependency(%q<thor>, [">= 0"])
82
+ s.add_dependency(%q<log4r>, [">= 0"])
83
+ s.add_dependency(%q<rake>, [">= 0"])
84
+ s.add_dependency(%q<shoulda>, [">= 0"])
85
+ s.add_dependency(%q<rr>, [">= 0"])
86
+ s.add_dependency(%q<jeweler>, [">= 0"])
87
+ end
88
+ else
89
+ s.add_dependency(%q<rest-client>, [">= 0"])
90
+ s.add_dependency(%q<thor>, [">= 0"])
91
+ s.add_dependency(%q<log4r>, [">= 0"])
92
+ s.add_dependency(%q<rake>, [">= 0"])
93
+ s.add_dependency(%q<shoulda>, [">= 0"])
94
+ s.add_dependency(%q<rr>, [">= 0"])
95
+ s.add_dependency(%q<jeweler>, [">= 0"])
96
+ end
97
+ end
98
+
metadata ADDED
@@ -0,0 +1,182 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: boomerang-trample
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
+ platform: ruby
11
+ authors:
12
+ - James Golick
13
+ - Jeremy Friesen
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-04-08 00:00:00 +02:00
19
+ default_executable: trample
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rest-client
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: thor
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ version: "0"
43
+ type: :runtime
44
+ version_requirements: *id002
45
+ - !ruby/object:Gem::Dependency
46
+ name: log4r
47
+ prerelease: false
48
+ requirement: &id003 !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ segments:
53
+ - 0
54
+ version: "0"
55
+ type: :runtime
56
+ version_requirements: *id003
57
+ - !ruby/object:Gem::Dependency
58
+ name: rake
59
+ prerelease: false
60
+ requirement: &id004 !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ type: :development
68
+ version_requirements: *id004
69
+ - !ruby/object:Gem::Dependency
70
+ name: shoulda
71
+ prerelease: false
72
+ requirement: &id005 !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ type: :development
80
+ version_requirements: *id005
81
+ - !ruby/object:Gem::Dependency
82
+ name: rr
83
+ prerelease: false
84
+ requirement: &id006 !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ type: :development
92
+ version_requirements: *id006
93
+ - !ruby/object:Gem::Dependency
94
+ name: jeweler
95
+ prerelease: false
96
+ requirement: &id007 !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ type: :development
104
+ version_requirements: *id007
105
+ description:
106
+ email: makesson@csc.com
107
+ executables:
108
+ - trample
109
+ extensions: []
110
+
111
+ extra_rdoc_files:
112
+ - LICENSE
113
+ - README.rdoc
114
+ files:
115
+ - .gitignore
116
+ - LICENSE
117
+ - README.rdoc
118
+ - Rakefile
119
+ - VERSION.yml
120
+ - bin/trample
121
+ - lib/trample.rb
122
+ - lib/trample/cli.rb
123
+ - lib/trample/configuration.rb
124
+ - lib/trample/logging.rb
125
+ - lib/trample/page.rb
126
+ - lib/trample/runner.rb
127
+ - lib/trample/session.rb
128
+ - lib/trample/timer.rb
129
+ - test/cli_test.rb
130
+ - test/configuration_test.rb
131
+ - test/fixtures/basic_config.rb
132
+ - test/integration/trample_a_single_url_test.rb
133
+ - test/logging_test.rb
134
+ - test/page_test.rb
135
+ - test/runner_test.rb
136
+ - test/session_test.rb
137
+ - test/test_helper.rb
138
+ - test/timer_test.rb
139
+ - test/trample_test.rb
140
+ - trample.gemspec
141
+ has_rdoc: true
142
+ homepage: http://github.com/boomerang/trample
143
+ licenses: []
144
+
145
+ post_install_message:
146
+ rdoc_options:
147
+ - --charset=UTF-8
148
+ require_paths:
149
+ - lib
150
+ required_ruby_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ segments:
155
+ - 0
156
+ version: "0"
157
+ required_rubygems_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ segments:
162
+ - 0
163
+ version: "0"
164
+ requirements: []
165
+
166
+ rubyforge_project:
167
+ rubygems_version: 1.3.6
168
+ signing_key:
169
+ specification_version: 3
170
+ summary: A simple command line tool for stress testing remote resources.
171
+ test_files:
172
+ - test/cli_test.rb
173
+ - test/configuration_test.rb
174
+ - test/fixtures/basic_config.rb
175
+ - test/integration/trample_a_single_url_test.rb
176
+ - test/logging_test.rb
177
+ - test/page_test.rb
178
+ - test/runner_test.rb
179
+ - test/session_test.rb
180
+ - test/test_helper.rb
181
+ - test/timer_test.rb
182
+ - test/trample_test.rb