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 +0 -0
- data/Rakefile +27 -0
- data/lib/rspec-performance.rb +4 -0
- data/lib/spec/performance/client/http_client.rb +88 -0
- data/lib/spec/performance/client/response.rb +25 -0
- data/lib/spec/performance/client.rb +1 -0
- data/lib/spec/performance/configuration.rb +60 -0
- data/lib/spec/performance/example/performance_example_group.rb +18 -0
- data/lib/spec/performance/example/performance_example_group_methods.rb +19 -0
- data/lib/spec/performance/example/performance_execution_builder.rb +71 -0
- data/lib/spec/performance/version.rb +11 -0
- data/spec/helpers/integration_server.rb +81 -0
- data/spec/lib/spec/performance/client/http_client_spec.rb +112 -0
- data/spec/lib/spec/performance/configuration_spec.rb +39 -0
- data/spec/lib/spec/performance/example/performance_example_group_methods_spec.rb +98 -0
- data/spec/lib/spec/performance/example/performance_example_group_spec.rb +48 -0
- data/spec/lib/spec/performance/example/performance_execution_builder_spec.rb +122 -0
- data/spec/spec_helper.rb +69 -0
- metadata +72 -0
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,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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
+
|