rspec-performance 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+