rspec-performance 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require "rubygems"
2
+ require "rake"
3
+ require "rake/gempackagetask"
4
+ require "spec/rake/spectask"
5
+
6
+ load("rspec-performance.gemspec")
7
+
8
+ task :default => :spec do
9
+ end
10
+
11
+ desc "Runs the rspec-performance ruby specs."
12
+ Spec::Rake::SpecTask.new(:spec) do |t|
13
+ spec_options = File.readlines("spec/spec.opts").map {|line| line.chomp }
14
+
15
+ t.libs << 'lib'
16
+ t.libs << File.dirname(__FILE__)
17
+ t.spec_opts = spec_options
18
+ t.spec_files = FileList['spec/**/*_spec.rb']
19
+ end
20
+
21
+ desc "Publish the gem via gem cutter"
22
+ task :publish do
23
+ system "gem build rspec-performance.gemspec"
24
+ system "gem push rspec-performance-#{Spec::Performance::VERSION::STRING}.gem"
25
+ end
26
+
27
+ # Gem packaging tasks
@@ -0,0 +1,4 @@
1
+ require "spec/performance/version"
2
+ require "spec/performance/client"
3
+ require "spec/performance/configuration"
4
+ require "spec/performance/example/performance_example_group"
@@ -0,0 +1,88 @@
1
+ require "net/http"
2
+ require "cgi"
3
+ require "spec/performance/client/response"
4
+
5
+ module Spec
6
+ module Performance
7
+ module Client
8
+ class HttpClient
9
+ attr_writer :recording, :cookies
10
+ attr_reader :base_uri, :cookies
11
+
12
+ def initialize(base_uri)
13
+ @base_uri = base_uri
14
+ @cookies = {}
15
+ @recording = true
16
+ end
17
+
18
+ def post(uri, params = {})
19
+ request = Net::HTTP::Post.new(uri.path, headers)
20
+ request.form_data = params
21
+ request.basic_auth uri.user, uri.password if uri.user
22
+ response = Net::HTTP.new(uri.host, uri.port).start do |http|
23
+ http.request(request)
24
+ end
25
+ capture(response) if recording?
26
+ create_http_client_response(response)
27
+ end
28
+
29
+ def get(uri, params = {})
30
+ if params && params.size > 0
31
+ query = params2query(params)
32
+ uri = URI.parse("#{uri}?#{query}")
33
+ end
34
+
35
+ http = Net::HTTP.start(uri.host, uri.port)
36
+ response = http.get(uri.request_uri, { "Cookie" => browser_cookies })
37
+ http.finish
38
+
39
+ create_http_client_response(response)
40
+ end
41
+
42
+ def recording?
43
+ @recording
44
+ end
45
+
46
+ private
47
+
48
+ def params2query(hash)
49
+ q = hash.inject([]) do |acc, (k, v)|
50
+ acc << CGI::escape(k.to_s) + "=" + CGI::escape(v.to_s)
51
+ end.join("&")
52
+ end
53
+
54
+ def capture(response)
55
+ if raw_cookies = response.get_fields("Set-Cookie")
56
+ cookie_jar = raw_cookies.inject({}) do |parsed_cookies, raw_cookie_string|
57
+ CGI::Cookie.parse(raw_cookie_string).each do |name, cookie|
58
+ parsed_cookies[name.to_sym] = cookie
59
+ end
60
+ parsed_cookies
61
+ end
62
+ @cookies.merge!(cookie_jar)
63
+ end
64
+ end
65
+
66
+ def headers
67
+ @cookies.values.inject({}) do |acc, cookie|
68
+ acc["Cookie"] = cookie.to_s
69
+ acc
70
+ end
71
+ end
72
+
73
+ def browser_cookies
74
+ cookies_to_send = @cookies.values.reject {|cookie| cookie.name == "path" || cookie.name == "domain" }
75
+ cookies_to_send.map do |cookie|
76
+ CGI::escape(cookie.name) + "=" + CGI::escape(cookie.value.first)
77
+ end.join("&")
78
+ end
79
+
80
+ def create_http_client_response(net_http_response)
81
+ attributes = { :code => net_http_response.code,
82
+ :body => net_http_response.body }
83
+ Response.new(attributes)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,25 @@
1
+
2
+ module Spec
3
+ module Performance
4
+ module Client
5
+
6
+ class Response
7
+ attr_reader :code, :body, :cookies
8
+
9
+ def initialize(attributes = {})
10
+ @code = attributes[:code].to_i
11
+ @body = attributes[:body]
12
+ end
13
+
14
+ def success?
15
+ @code == 200
16
+ end
17
+
18
+ def redirect?
19
+ @code == 301 || @code == 302
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1 @@
1
+ require "spec/performance/client/http_client"
@@ -0,0 +1,60 @@
1
+ module Spec
2
+ module Performance
3
+ class Configuration
4
+ attr_reader :options
5
+
6
+ def initialize(options)
7
+ @options = options
8
+ end
9
+
10
+ def concurrency=(value)
11
+ @options[:concurrency] = value
12
+ end
13
+
14
+ def iterations=(value)
15
+ @options[:iterations] = value
16
+ end
17
+
18
+ def iterations_per_second=(value)
19
+ @options[:iterations_per_second] = value
20
+ end
21
+
22
+ def maximum_iteration_time=(value)
23
+ @options[:maximum_iteration_time] = value
24
+ end
25
+
26
+ def performance_driver_class=(value)
27
+ @options[:performance_driver_class] = value
28
+ end
29
+
30
+ def performance_driver_base_uri=(value)
31
+ @options[:performance_driver_base_uri] = value
32
+ end
33
+
34
+ class << self
35
+ def instance
36
+ @@instance ||= new(default_options)
37
+ end
38
+
39
+ def configure(&block)
40
+ yield(instance)
41
+ end
42
+
43
+ def configured_options
44
+ instance.options
45
+ end
46
+
47
+ def default_options
48
+ {
49
+ :concurrency => 1,
50
+ :iterations => 20,
51
+ :iterations_per_second => nil,
52
+ :maximum_iteration_time => nil,
53
+ :performance_driver_class => Spec::Performance::Client::HttpClient,
54
+ :performance_driver_base_uri => "http://localhost/"
55
+ }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,18 @@
1
+ require "spec/performance/example/performance_example_group_methods"
2
+
3
+ module Spec
4
+ module Performance
5
+ module Example
6
+ class PerformanceExampleGroup < Spec::Example::ExampleGroup
7
+
8
+ attr_reader :performance_driver
9
+ before(:each) do
10
+ options = Spec::Performance::Configuration.configured_options
11
+ @performance_driver = options[:performance_driver_class].new(options[:performance_driver_base_uri])
12
+ end
13
+
14
+ extend PerformanceExampleGroupMethods
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ require "spec/performance/example/performance_execution_builder"
2
+
3
+ module Spec
4
+ module Performance
5
+ module Example
6
+
7
+ module PerformanceExampleGroupMethods
8
+
9
+ def perform(description, options = {}, backtrace = nil, &implementation)
10
+ options = Spec::Performance::Configuration.configured_options.merge(options)
11
+
12
+ builder = PerformanceExecutionBuilder.new(options, &implementation)
13
+ example(description, options, backtrace, &builder.performance_proc)
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,71 @@
1
+ module Spec
2
+ module Performance
3
+ module Example
4
+
5
+ class PerformanceExecutionBuilder
6
+ attr_reader :options
7
+
8
+ EXAMPLE_RUN_TIME_LABEL = "Performance Example Run Time"
9
+ ITERATION_RUN_TIME_LABEL = "Performance Iteration Run Time"
10
+
11
+ def initialize(options, &implementation)
12
+ @options = options
13
+ @impl = implementation
14
+ end
15
+
16
+ def performance_proc
17
+ iterations_per_slice = options[:iterations] / options[:concurrency]
18
+
19
+ # NOTE: Block execution only works if this is a local variable.
20
+ implementation = @impl
21
+
22
+ Proc.new do
23
+ extend(PerformanceHelpers)
24
+
25
+ example_run_time, maximum_iteration_time = _run_performance_loop(iterations_per_slice, &implementation)
26
+ _assert_iterations_per_second(example_run_time, iterations_per_slice, options[:iterations_per_second]) if options[:iterations_per_second]
27
+ _assert_maximum_iteration_time(maximum_iteration_time, options[:maximum_iteration_time]) if options[:maximum_iteration_time]
28
+ end
29
+ end
30
+
31
+ module PerformanceHelpers
32
+ private
33
+
34
+ def _run_performance_loop(iterations_per_slice, &implementation)
35
+ maximum_iteration_time = 0.0
36
+ example_run_time = _timed_operation EXAMPLE_RUN_TIME_LABEL do
37
+ (1..iterations_per_slice).each do |current_iteration|
38
+ iteration_run_time = _timed_operation ITERATION_RUN_TIME_LABEL do
39
+ instance_eval(&implementation)
40
+ end
41
+ maximum_iteration_time = _calculate_average(current_iteration - 1, maximum_iteration_time, iteration_run_time)
42
+ end
43
+ end
44
+ [example_run_time, maximum_iteration_time]
45
+ end
46
+
47
+ def _assert_iterations_per_second(example_run_time, iterations_per_slice, acceptable_iterations_per_second)
48
+ estimated_iteration_run_time = example_run_time / iterations_per_slice
49
+ acceptable_estimated_iteration_run_time = 1.0 / acceptable_iterations_per_second
50
+ estimated_iteration_run_time.should <= acceptable_estimated_iteration_run_time
51
+ end
52
+
53
+ def _assert_maximum_iteration_time(maximum_iteration_time, acceptable_maximum_iteration_time)
54
+ maximum_iteration_time.should <= acceptable_maximum_iteration_time
55
+ end
56
+
57
+ def _calculate_average(sample_size, current_average, new_value)
58
+ (sample_size * current_average + new_value) / (sample_size + 1)
59
+ end
60
+
61
+ def _timed_operation(label, &block)
62
+ tic = Time.now.to_f
63
+ yield
64
+ Time.now.to_f - tic
65
+ end
66
+ end
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,11 @@
1
+ module Spec
2
+ module Performance
3
+ module VERSION
4
+ MAJOR = 0
5
+ MINOR = 0
6
+ TINY = 1
7
+
8
+ STRING = [MAJOR, MINOR, TINY].join(".")
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,81 @@
1
+ require "rubygems"
2
+ require "thin"
3
+
4
+ Thread.abort_on_exception = true
5
+
6
+ class IntegrationServer
7
+ def initialize(port)
8
+ @server_thread = nil
9
+ @port = port
10
+ @running = false
11
+ end
12
+
13
+ def self.base_url
14
+ "http://localhost:#{@@port}/"
15
+ end
16
+
17
+ def self.instance
18
+ @@instance ||= new(@@port)
19
+ end
20
+
21
+ def self.start
22
+ instance.start
23
+ end
24
+
25
+ def self.kill
26
+ @@instance.kill
27
+ end
28
+
29
+ def self.port=(value)
30
+ @@port = value
31
+ end
32
+
33
+ def running?
34
+ @running
35
+ end
36
+
37
+ def start
38
+ return if running?
39
+
40
+ Thin::Logging.silent = true
41
+ @server_thread = Thread.new do
42
+ @server = Thin::Server.start('0.0.0.0', @port) do
43
+ map "/hello" do
44
+ run HelloAdapter.new
45
+ end
46
+
47
+ map "/cookie_echo" do
48
+ run CookieEchoAdapter.new
49
+ end
50
+ end
51
+ end
52
+ sleep 0.010 unless @server && @server.running?
53
+ @running = true
54
+ end
55
+
56
+ def kill
57
+ @server_thread.kill
58
+ @running = false
59
+ end
60
+
61
+ private
62
+
63
+ class HelloAdapter
64
+ def call(env)
65
+ body = ["<html><head><title>hi!</title></head><body>hello</body</html>"]
66
+ [ 200, { 'Content-Type' => 'text/html' }, body ]
67
+ end
68
+ end
69
+
70
+ class CookieEchoAdapter
71
+ def call(env)
72
+ cookies = env["rack.input"].read.split(/&/).inject([]) do |acc, pair|
73
+ name, value = pair.split(/=/).map {|p| CGI::unescape(p) }
74
+ acc << CGI::Cookie.new(name, value).to_s
75
+ acc
76
+ end
77
+ cookie_string = cookies.join("\n")
78
+ [200, { "Set-Cookie" => cookie_string }, ["echo"]]
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,112 @@
1
+ require "#{File.dirname(__FILE__)}/../../../../spec_helper"
2
+
3
+ describe Spec::Performance::Client::HttpClient do
4
+ attr_reader :client
5
+
6
+ before do
7
+ @client = Spec::Performance::Client::HttpClient.new("http://localhost")
8
+ end
9
+
10
+ describe "#initialize" do
11
+ it "initializes the cookie jar" do
12
+ client.cookies.should be_a(Hash)
13
+ end
14
+ end
15
+
16
+ describe "#post" do
17
+ attr_reader :uri, :params
18
+ before do
19
+ @uri = URI.join(IntegrationServer.base_url, "hello")
20
+ @params = { :foo => "bar", :baz => "quux" }
21
+ end
22
+
23
+ it "makes an HTTP post" do
24
+ mock.instance_of(Net::HTTP).request(anything) do |request|
25
+ request.should be_a(Net::HTTP::Post)
26
+ stub(Net::HTTPResponse.new(1.1, 200, nil)).body { "stubbed response body" }
27
+ end
28
+ client.post(uri, params).should be_success
29
+ end
30
+
31
+ it "returns a response object" do
32
+ client.post(uri, params).should be_a(Spec::Performance::Client::Response)
33
+ end
34
+
35
+ describe "when the client is recording" do
36
+ before do
37
+ @uri = URI.join(IntegrationServer.base_url, "cookie_echo")
38
+ client.should be_recording
39
+ end
40
+
41
+ it "captures the cookie from the response" do
42
+ client.post(uri, params).should be_success
43
+ client.cookies[:foo].value.first.should == "bar"
44
+ client.cookies[:baz].value.first.should == "quux"
45
+ end
46
+ end
47
+
48
+ describe "when the client is not recording" do
49
+ before do
50
+ @uri = URI.join(IntegrationServer.base_url, "cookie_echo")
51
+ client.recording = false
52
+ client.should_not be_recording
53
+ end
54
+
55
+ it "does not capture cookies" do
56
+ client.post(uri, params).should be_success
57
+ client.cookies.should_not have_key(:foo)
58
+ client.cookies.should_not have_key(:baz)
59
+ end
60
+ end
61
+ end
62
+
63
+ describe "#get" do
64
+ attr_reader :uri, :params
65
+ before do
66
+ @params = { :monster_truck => "Truckasaurus", :us_president => "Grover Cleveland" }
67
+ @uri = URI.join(IntegrationServer.base_url, "hello")
68
+ end
69
+
70
+ it "sends an HTTP get request, sending the current cookies" do
71
+ expected_cookie = CGI::Cookie.new("cookie-name", "cookie-value")
72
+ expected_headers = { "Cookie" => "cookie-name=cookie-value" }
73
+ client.cookies = { "cookie-name".to_sym => expected_cookie }
74
+
75
+ mock_http = Object.new
76
+ mock(Net::HTTP).start("localhost", 8888) { mock_http }
77
+ mock(mock_http).get(anything, expected_headers) do |request_uri, headers|
78
+ request_uri.index("/hello?").should == 0
79
+ request_uri.should include("monster_truck=Truckasaurus")
80
+ request_uri.should include("us_president=Grover+Cleveland")
81
+ request_uri.should include("&")
82
+
83
+ stub(Net::HTTPResponse.new(1.1, 200, nil)).body { "stubbed response body" }
84
+ end
85
+ mock(mock_http).finish
86
+
87
+ response = client.get(uri, params)
88
+ end
89
+
90
+ it "returns a response object" do
91
+ response = client.get(uri, params)
92
+ response.should be_a(Spec::Performance::Client::Response)
93
+ response.should be_success
94
+ end
95
+
96
+ describe "when the client is recording" do
97
+ it "captures the cookie from the response" do
98
+ end
99
+ end
100
+
101
+ describe "when the client is not recording" do
102
+ it "does not capture" do
103
+ end
104
+ end
105
+ end
106
+
107
+ describe "#restore" do
108
+ it "resets after each run" do
109
+ end
110
+ end
111
+
112
+ end
@@ -0,0 +1,39 @@
1
+ require "#{File.dirname(__FILE__)}/../../../spec_helper"
2
+
3
+ describe Spec::Performance::Configuration do
4
+ describe ".default_options" do
5
+ it "has correct configuration keys" do
6
+ options = Spec::Performance::Configuration.default_options
7
+ options.has_key?(:concurrency)
8
+ options.has_key?(:iterations)
9
+ options.has_key?(:iterations_per_second)
10
+ options.has_key?(:maximum_iteration_time)
11
+ options.has_key?(:performance_driver_class)
12
+ options.has_key?(:performance_driver_base_uri)
13
+ end
14
+ end
15
+
16
+ describe ".configure" do
17
+ it "sets a the options of Configuration singleton" do
18
+ Spec::Performance::Configuration.configure do |config|
19
+ config.concurrency = 10000
20
+ config.iterations_per_second = 20000
21
+ end
22
+
23
+ default_options = Spec::Performance::Configuration.default_options
24
+ configured_options = Spec::Performance::Configuration.instance.options
25
+ configured_options[:concurrency].should == 10000
26
+ configured_options[:iterations_per_second].should == 20000
27
+ configured_options[:maximum_iteration_time].should == default_options[:maximum_iteration_time]
28
+ configured_options[:iterations].should == default_options[:iterations]
29
+ configured_options[:performance_driver_class].should == default_options[:performance_driver_class]
30
+ configured_options[:performance_driver_base_uri].should == default_options[:performance_driver_base_uri]
31
+ end
32
+ end
33
+
34
+ describe ".configured_options" do
35
+ it "returns the configured options" do
36
+ Spec::Performance::Configuration.instance.options.should == Spec::Performance::Configuration.configured_options
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,98 @@
1
+ require "#{File.dirname(__FILE__)}/../../../../spec_helper"
2
+
3
+ describe Spec::Performance::Example::PerformanceExampleGroupMethods do
4
+ with_sandboxed_options do
5
+ attr_reader :example_group, :fake_run_options
6
+ before do
7
+ @example_group = Class.new(Spec::Performance::Example::PerformanceExampleGroup)
8
+ @fake_run_options = ::Spec::Runner::Options.new(StringIO.new, StringIO.new)
9
+ Spec::Performance::Runner::TestReporter.new(fake_run_options)
10
+ end
11
+
12
+ describe ".perform" do
13
+ context "configuration options" do
14
+ attr_reader :configured_options
15
+ before do
16
+ @configured_options = Spec::Performance::Configuration.configured_options
17
+ end
18
+
19
+ it "uses the global configuration options" do
20
+ mock(example_group).example(anything, configured_options, anything)
21
+ example_group.perform("do something") {}
22
+ end
23
+
24
+ describe "when options are passed on a per test level" do
25
+ attr_reader :spec_options, :expected_options
26
+ before do
27
+ @spec_options = { :concurrency => 100, :iterations => 200 }
28
+ @expected_options = configured_options.merge(spec_options)
29
+ end
30
+
31
+ it "overrides the global configuration options" do
32
+ mock(example_group).example(anything, expected_options, anything)
33
+ example_group.perform("do something", spec_options) {}
34
+ end
35
+ end
36
+ end
37
+
38
+ context "execution context" do
39
+ attr_reader :spec_options
40
+ before do
41
+ @spec_options = { :concurrency => 2, :iterations => 10, :iterations_per_second => nil, :maximum_iteration_time => nil }
42
+ end
43
+
44
+ it "executes the performance loop in the same context as the other examples" do
45
+ example_group.before do
46
+ @scope = self
47
+ @foo = "bar"
48
+ end
49
+
50
+ executed = false
51
+ example_group.perform "do performance loop", spec_options.merge(:iterations => 1, :concurrency => 1) do
52
+ (@scope == self).should be_true
53
+ @foo.should == "bar"
54
+ executed = true
55
+ end
56
+
57
+ example_group.run(fake_run_options)
58
+
59
+ @scope.should be_nil
60
+ @foo.should_not == "bar"
61
+ executed.should be_true
62
+ end
63
+ end
64
+
65
+ context "reporting" do
66
+ describe "when a performance test assertion fails" do
67
+ it "reports the failure" do
68
+ example_group.perform("do something") { 1.should == 0 }
69
+ example_group.run(fake_run_options)
70
+
71
+ fake_run_options.reporter.example_successes.should be_empty
72
+ fake_run_options.reporter.example_failures.should_not be_empty
73
+ error = fake_run_options.reporter.example_failures.first[:error]
74
+ error.should be_a(Spec::Expectations::ExpectationNotMetError)
75
+ end
76
+ end
77
+
78
+ describe "when a performance test passes" do
79
+ attr_reader :spec_options
80
+ before do
81
+ @spec_options = { :iterations => 1, :concurrency => 1, :maximum_iteration_time => 100000 }
82
+ end
83
+
84
+ it "reports the success" do
85
+ example_group.perform("reports the success", spec_options) { 1.should == 1 }
86
+ example_group.run(fake_run_options)
87
+
88
+ fake_run_options.reporter.example_failures.should be_empty
89
+ example = fake_run_options.reporter.example_successes.first
90
+ example.description.should == "reports the success"
91
+ end
92
+ end
93
+ end
94
+
95
+ end
96
+ end
97
+ end
98
+
@@ -0,0 +1,48 @@
1
+ require "#{File.dirname(__FILE__)}/../../../../spec_helper"
2
+
3
+ describe Spec::Performance::Example::PerformanceExampleGroup do
4
+ with_sandboxed_options do
5
+
6
+ attr_reader :example_group, :fake_run_options, :spec_options
7
+ before do
8
+ @spec_options = { :iterations => 1, :concurrency => 1, :maximum_iteration_time => 1, :iterations_per_second => 1 }
9
+ @example_group = Class.new(Spec::Performance::Example::PerformanceExampleGroup)
10
+ @fake_run_options = ::Spec::Runner::Options.new(StringIO.new, StringIO.new)
11
+ Spec::Performance::Runner::TestReporter.new(fake_run_options)
12
+
13
+ fake_run_options.reporter.reset
14
+ end
15
+
16
+ it "sets a configured performance_driver attribute to an instance of the performance_driver_class before running each spec" do
17
+ driver_instance_in_before_block = nil
18
+ driver_instance_in_first_perform_block = nil
19
+ driver_instance_in_second_perform_block = nil
20
+
21
+ example_group.before do
22
+ driver_instance_in_before_block = performance_driver
23
+ end
24
+
25
+ example_group.perform "some description", spec_options do
26
+ driver_instance_in_first_perform_block = performance_driver
27
+ end
28
+
29
+ example_group.perform "some other description", spec_options do
30
+ driver_instance_in_second_perform_block = performance_driver
31
+ end
32
+
33
+ example_group.run(fake_run_options)
34
+ fake_run_options.reporter.example_failures.should be_empty
35
+
36
+ driver_instance_in_first_perform_block.should be_a(Spec::Performance::Client::HttpClient)
37
+ driver_instance_in_before_block.base_uri.should == "http://localhost/"
38
+
39
+ driver_instance_in_second_perform_block.should be_a(Spec::Performance::Client::HttpClient)
40
+ driver_instance_in_second_perform_block.base_uri.should == "http://localhost/"
41
+
42
+ driver_instance_in_first_perform_block.eql?(driver_instance_in_second_perform_block).should be_false
43
+
44
+ # the performance_driver from the before block retains the last value it was set to
45
+ driver_instance_in_before_block.eql?(driver_instance_in_second_perform_block).should be_true
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,122 @@
1
+ require "#{File.dirname(__FILE__)}/../../../../spec_helper"
2
+ require "spec/performance/example/performance_execution_builder"
3
+
4
+ describe Spec::Performance::Example::PerformanceExecutionBuilder do
5
+ attr_reader :spec_options
6
+ with_sandboxed_options do
7
+
8
+ describe ".performance_proc" do
9
+ context "performance loop" do
10
+ before do
11
+ @spec_options = { :concurrency => 2, :iterations => 10, :iterations_per_second => nil, :maximum_iteration_time => nil }
12
+ end
13
+
14
+ it "runs the performance loop N times where N is equal to iterations divided by concurrency" do
15
+ call_count = 0
16
+ expected_call_count = spec_options[:iterations] / spec_options[:concurrency]
17
+
18
+ builder = Spec::Performance::Example::PerformanceExecutionBuilder.new(spec_options) { call_count += 1 }
19
+ builder.performance_proc.call
20
+
21
+ call_count.should == expected_call_count
22
+ end
23
+
24
+ it "extends the ExampleGroup prior to test execution" do
25
+ builder = Spec::Performance::Example::PerformanceExecutionBuilder.new(spec_options) {}
26
+ mock(Spec::Performance::Example::PerformanceExecutionBuilder::PerformanceHelpers).extended(builder) {}
27
+ builder.performance_proc.call
28
+ end
29
+ end
30
+
31
+ context "extended tests" do
32
+ describe "when the spec should fail" do
33
+ attr_reader :builder
34
+
35
+ before do
36
+ @spec_options = { :concurrency => 1, :iterations => 2, :iterations_per_second => nil, :maximum_iteration_time => nil }
37
+ end
38
+
39
+ describe "when the iterations_per_second configuration option is set" do
40
+ before do
41
+ spec_options.merge!({ :iterations_per_second => 3 })
42
+ @builder = Spec::Performance::Example::PerformanceExecutionBuilder.new(spec_options)
43
+ end
44
+
45
+ it "fails when it does not match on iterations per second" do
46
+ stub(builder)._timed_operation(Spec::Performance::Example::PerformanceExecutionBuilder::EXAMPLE_RUN_TIME_LABEL) { 1.0 }
47
+
48
+ lambda do
49
+ builder.performance_proc.call
50
+ end.should raise_error(Spec::Expectations::ExpectationNotMetError)
51
+ end
52
+ end
53
+
54
+ describe "when the maximum_iteration_time configuration option is set" do
55
+ before do
56
+ spec_options.merge!({ :iterations => 5, :maximum_iteration_time => 0.100 })
57
+ @builder = Spec::Performance::Example::PerformanceExecutionBuilder.new(spec_options)
58
+ end
59
+
60
+ def create_stub_impl
61
+ lambda do |args, block|
62
+ label = Spec::VERSION::MAJOR == 1 && Spec::VERSION::MINOR > 2 ? args.first : args
63
+ return 0.2 if label == Spec::Performance::Example::PerformanceExecutionBuilder::ITERATION_RUN_TIME_LABEL
64
+ block.call
65
+ 1.0
66
+ end
67
+ end
68
+
69
+ it "fails when the maximum_iteration_time falls below the configured value" do
70
+ stub(builder)._timed_operation(anything, &create_stub_impl)
71
+
72
+ lambda do
73
+ builder.performance_proc.call
74
+ end.should raise_error(Spec::Expectations::ExpectationNotMetError)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ describe Spec::Performance::Example::PerformanceExecutionBuilder::PerformanceHelpers do
82
+ attr_reader :helper_instance
83
+ before do
84
+ @helper_instance = Class.new do
85
+ include Spec::Performance::Example::PerformanceExecutionBuilder::PerformanceHelpers
86
+ end.new
87
+ end
88
+
89
+ describe ".calculate_average" do
90
+ attr_reader :sample, :expected_average, :current_average
91
+ before do
92
+ @sample = [3, 3, 5, 7]
93
+ @expected_average = (sample.sum + 7) / (sample.size + 1)
94
+ expected_average.should == 5
95
+
96
+ @current_average = sample.sum / sample.size
97
+ end
98
+
99
+ it "calculates averages correctly" do
100
+ helper_instance.send(:_calculate_average, sample.size, current_average, 7).should == expected_average
101
+ end
102
+ end
103
+
104
+ describe ".timed_operation" do
105
+ before do
106
+ called = 0
107
+ stub(Time).now do
108
+ return_value = called > 0 ? 1 : 0
109
+ called += 1
110
+ return_value
111
+ end
112
+ end
113
+
114
+ it "returns the elapsed time of execution for a block" do
115
+ elapsed_time = helper_instance.send(:_timed_operation, "foo") {}
116
+ elapsed_time.should be_a(Float)
117
+ elapsed_time.should == 1.0
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,69 @@
1
+ begin
2
+ require "spec"
3
+ rescue LoadError
4
+ require "rubygems" unless ENV["NO_RUBYGEMS"]
5
+ gem "rspec"
6
+ require "spec"
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + "/../lib")
10
+ dir = File.dirname(__FILE__)
11
+
12
+ require "rspec-performance"
13
+ require "#{dir}/helpers/integration_server"
14
+
15
+ IntegrationServer.port = 8888
16
+ IntegrationServer.start
17
+
18
+ Spec::Runner.configure do |config|
19
+ config.mock_with :rr
20
+ end
21
+
22
+ module Spec::Performance::Runner
23
+ class TestReporter < Spec::Runner::Reporter
24
+ attr_reader :example_failures, :example_successes
25
+
26
+ def initialize(options)
27
+ super(options)
28
+ reset
29
+ end
30
+
31
+ def example_failed(example, error)
32
+ @example_failures << { :example => example, :error => error }
33
+ end
34
+
35
+ def example_passed(example)
36
+ @example_successes << example
37
+ end
38
+
39
+ def reset
40
+ @example_failures = []
41
+ @example_successes = []
42
+ end
43
+ end
44
+ end
45
+
46
+ def with_sandboxed_options
47
+ attr_reader :options
48
+
49
+ before(:each) do
50
+ @original_rspec_options = ::Spec::Runner.options
51
+ ::Spec::Runner.use(@options = ::Spec::Runner::Options.new(StringIO.new, StringIO.new))
52
+ end
53
+
54
+ after(:each) do
55
+ ::Spec::Runner.use(@original_rspec_options)
56
+ end
57
+
58
+ yield
59
+ end
60
+
61
+ class Array
62
+ def sum(&block)
63
+ inject(0.0) do |acc, x|
64
+ acc += block_given? ? yield(x) : x
65
+ acc
66
+ end
67
+ end
68
+ end
69
+
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-performance
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Bob Remeika
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-01 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: rspec-performance adds a couple of utility methods for unit testing performance
17
+ email: bob@grockit.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/rspec-performance.rb
26
+ - lib/spec/performance/client/http_client.rb
27
+ - lib/spec/performance/client/response.rb
28
+ - lib/spec/performance/client.rb
29
+ - lib/spec/performance/configuration.rb
30
+ - lib/spec/performance/example/performance_example_group.rb
31
+ - lib/spec/performance/example/performance_example_group_methods.rb
32
+ - lib/spec/performance/example/performance_execution_builder.rb
33
+ - lib/spec/performance/version.rb
34
+ - spec/helpers/integration_server.rb
35
+ - spec/lib/spec/performance/client/http_client_spec.rb
36
+ - spec/lib/spec/performance/configuration_spec.rb
37
+ - spec/lib/spec/performance/example/performance_example_group_methods_spec.rb
38
+ - spec/lib/spec/performance/example/performance_example_group_spec.rb
39
+ - spec/lib/spec/performance/example/performance_execution_builder_spec.rb
40
+ - spec/spec_helper.rb
41
+ - Rakefile
42
+ - README
43
+ has_rdoc: true
44
+ homepage: http://grockit.com
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options: []
49
+
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ requirements:
65
+ - rspec-1.2.6
66
+ rubyforge_project: rspec-performance
67
+ rubygems_version: 1.3.5
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Ruby based make-like utility.
71
+ test_files: []
72
+