rspec-performance 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|