abcrunch 0.0.4
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/.gitignore +6 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +28 -0
- data/README +176 -0
- data/Rakefile +10 -0
- data/ab_honk.rb +104 -0
- data/abcrunch.gemspec +36 -0
- data/lib/abcrunch.rb +11 -0
- data/lib/abcrunch/ab_result.rb +33 -0
- data/lib/abcrunch/ab_runner.rb +36 -0
- data/lib/abcrunch/best_run.rb +22 -0
- data/lib/abcrunch/config.rb +35 -0
- data/lib/abcrunch/log_console_writer.rb +44 -0
- data/lib/abcrunch/logger.rb +13 -0
- data/lib/abcrunch/page.rb +26 -0
- data/lib/abcrunch/page_tester.rb +44 -0
- data/lib/abcrunch/strategy_best_concurrency.rb +48 -0
- data/lib/abcrunch/tasks/default.rake +12 -0
- data/lib/abcrunch/tasks/example.rake +33 -0
- data/lib/abcrunch/tasks/generated.rake +36 -0
- data/lib/abcrunch/tester.rb +56 -0
- data/lib/abcrunch/version.rb +3 -0
- data/spec/helpers/page_helper.rb +62 -0
- data/spec/lib/ab_result_spec.rb +87 -0
- data/spec/lib/ab_runner_spec.rb +47 -0
- data/spec/lib/best_run_spec.rb +56 -0
- data/spec/lib/config_spec.rb +22 -0
- data/spec/lib/log_console_writer_spec.rb +82 -0
- data/spec/lib/logger_spec.rb +12 -0
- data/spec/lib/page_spec.rb +64 -0
- data/spec/lib/page_tester_spec.rb +85 -0
- data/spec/lib/strategy_best_concurrency_spec.rb +124 -0
- data/spec/lib/tester_spec.rb +73 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/tasks/spec.rake +15 -0
- metadata +141 -0
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "AbCrunch::AbResult" do
|
4
|
+
def new_abr(raw_ab_output, ab_options = {})
|
5
|
+
AbCrunch::AbResult.new(raw_ab_output, ab_options)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#initialize" do
|
9
|
+
it "should remember the provided raw ab output and options" do
|
10
|
+
abr = new_abr('foo', {:foo => 'bar'})
|
11
|
+
abr.raw.should == 'foo'
|
12
|
+
abr.ab_options.should == {:foo => 'bar'}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#command" do
|
17
|
+
it "should return the ab command used to produce the result" do
|
18
|
+
ab_options = {:crunch => 'now'}
|
19
|
+
mock(AbCrunch::AbRunner).ab_command(ab_options) { 'tight!' }
|
20
|
+
abr = new_abr('blah', ab_options)
|
21
|
+
abr.command.should == 'tight!'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#avg_response_time" do
|
26
|
+
it "should glean the average response time from raw ab output and return as a float" do
|
27
|
+
abr = new_abr("fleeblesnork\nTime per request: 43.028 [ms] (mean)\n\nblah")
|
28
|
+
abr.avg_response_time.should == 43.028
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "when the raw value is an integer" do
|
32
|
+
it "should still be returned as a float" do
|
33
|
+
abr = new_abr("fleeblesnork\nTime per request: 43 [ms] (mean)\n\nblah")
|
34
|
+
abr.avg_response_time.should == 43.0
|
35
|
+
abr.avg_response_time.class.to_s.should == 'Float'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#queries_per_second" do
|
41
|
+
it "should glean the queries per second from raw ab output and return as a float" do
|
42
|
+
abr = new_abr("fleeblesnork\nRequests per second: 162.68 [#/sec] (mean)\n\nblah")
|
43
|
+
abr.queries_per_second.should == 162.68
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "when the raw value is an integer" do
|
47
|
+
it "should still be returned as a float" do
|
48
|
+
abr = new_abr("fleeblesnork\nRequests per second: 162 [#/sec] (mean)\n\nblah")
|
49
|
+
abr.queries_per_second.should == 162.0
|
50
|
+
abr.queries_per_second.class.to_s.should == 'Float'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#failed_requests" do
|
56
|
+
it "should glean the failed request count from raw ab output and return as a fixnum" do
|
57
|
+
abr = new_abr("fleeblesnork\nFailed requests: 17\n\nblah")
|
58
|
+
abr.failed_requests.should == 17
|
59
|
+
abr.failed_requests.class.to_s.should == 'Fixnum'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#log" do
|
64
|
+
it "should log command, avg response time, queries per second, and failed requests" do
|
65
|
+
ab_options = {:crunch => 'now'}
|
66
|
+
abr = new_abr('blah', ab_options)
|
67
|
+
mock(abr).command { 'tight!' }
|
68
|
+
mock(abr).avg_response_time { "186" }
|
69
|
+
mock(abr).queries_per_second { "50" }
|
70
|
+
mock(abr).failed_requests { '10' }
|
71
|
+
# ...
|
72
|
+
lines = []
|
73
|
+
stub(AbCrunch::Logger).log { |*args|
|
74
|
+
args[0].should == :ab_result
|
75
|
+
lines << args[1]
|
76
|
+
}
|
77
|
+
abr.log
|
78
|
+
lines.should == [
|
79
|
+
'tight!',
|
80
|
+
'Average Response Time: 186',
|
81
|
+
'Queries per Second: 50',
|
82
|
+
'Failed requests: 10'
|
83
|
+
]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "AbRunner" do
|
4
|
+
describe "#validate_options" do
|
5
|
+
it "should throw an error when no url is provided" do
|
6
|
+
lambda {
|
7
|
+
AbCrunch::AbRunner.validate_options({})
|
8
|
+
}.should raise_error "AB Options missing :url"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should return the options merged into the global configuration's ab_options'" do
|
12
|
+
result = AbCrunch::AbRunner.validate_options({:url => 'some url'})
|
13
|
+
result.should == AbCrunch::Config.ab_options.merge({:url => 'some url'})
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#ab_command" do
|
18
|
+
it "should return the ab command line based on given options, forcing the page url to reset" do
|
19
|
+
options = {
|
20
|
+
:concurrency => 'thigh master',
|
21
|
+
:num_requests => 'i can only do 5 today',
|
22
|
+
:url => '5 minute abs'
|
23
|
+
}
|
24
|
+
mock.proxy(AbCrunch::Page).get_url(options, true)
|
25
|
+
|
26
|
+
AbCrunch::AbRunner.ab_command(options).should == "ab -c thigh master -n i can only do 5 today -k -H 'Accept-Encoding: gzip' 5 minute abs"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#ab" do
|
31
|
+
it "should run ab and wrap the results in an ab result" do
|
32
|
+
def Open3.block=(block) ; @block = block ; end
|
33
|
+
def Open3.block(cmd, &block)
|
34
|
+
self.block = block if block_given?
|
35
|
+
block.call(StringIO.new(''), StringIO.new('some fake output'), StringIO.new(''))
|
36
|
+
return(@block)
|
37
|
+
end
|
38
|
+
Open3.block = nil
|
39
|
+
stub(Open3).popen3.implemented_by(Open3.method(:block))
|
40
|
+
|
41
|
+
stub(AbCrunch::AbRunner).ab_command('bar') { 'foo' }
|
42
|
+
mock(AbCrunch::AbResult).new('some fake output', 'bar') { 'six pack' }
|
43
|
+
|
44
|
+
AbCrunch::AbRunner.ab('bar').should == 'six pack'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "BestRun" do
|
4
|
+
before :all do
|
5
|
+
@ab_options = {
|
6
|
+
:url => 'foo',
|
7
|
+
:concurrency => 1,
|
8
|
+
:num_requests => 10
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#of_avg_response_time" do
|
13
|
+
it "should run ab on the ab options N times" do
|
14
|
+
stub(AbCrunch::Logger).log
|
15
|
+
num_runs = 0
|
16
|
+
stub(AbCrunch::AbRunner).ab(@ab_options) do
|
17
|
+
num_runs += 1
|
18
|
+
obj = "foo"
|
19
|
+
|
20
|
+
class << obj
|
21
|
+
def avg_response_time
|
22
|
+
4
|
23
|
+
end
|
24
|
+
end
|
25
|
+
obj
|
26
|
+
end
|
27
|
+
|
28
|
+
AbCrunch::BestRun.of_avg_response_time(7, @ab_options)
|
29
|
+
|
30
|
+
num_runs.should == 7
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should return the ab result for the run with the lowest avg response time" do
|
34
|
+
stub(AbCrunch::Logger).log
|
35
|
+
response_times = [3, 87, 1, 590]
|
36
|
+
|
37
|
+
run_idx = 0
|
38
|
+
stub(AbCrunch::AbRunner).ab(@ab_options) do
|
39
|
+
obj = Object.new
|
40
|
+
response_time = response_times[run_idx]
|
41
|
+
obj.singleton_class.class_eval do
|
42
|
+
define_method(:avg_response_time) do
|
43
|
+
response_time
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
run_idx += 1
|
48
|
+
obj
|
49
|
+
end
|
50
|
+
|
51
|
+
result = AbCrunch::BestRun.of_avg_response_time(4, @ab_options)
|
52
|
+
|
53
|
+
result.avg_response_time.should == 1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "AbCrunch::Config" do
|
4
|
+
it "should have hashes for ab options and max concurrent options" do
|
5
|
+
AbCrunch::Config.best_concurrency_options.class.to_s.should == 'Hash'
|
6
|
+
AbCrunch::Config.ab_options.class.to_s.should == 'Hash'
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should have a hash for the pages to be tested" do
|
10
|
+
AbCrunch::Config.page_sets.class.to_s.should == 'Hash'
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#page_sets=" do
|
14
|
+
it "should remember the new page sets and force new rake tasks to be created" do
|
15
|
+
mock(AbCrunch::Config).load(File.join(AbCrunch.root, "lib/abcrunch/tasks/generated.rake"))
|
16
|
+
|
17
|
+
AbCrunch::Config.page_sets = { :foo => 'bar' }
|
18
|
+
|
19
|
+
AbCrunch::Config.page_sets.should == { :foo => 'bar' }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "AbCrunch::LogConsoleWriter" do
|
4
|
+
describe "#color_for_type" do
|
5
|
+
it "should return the color for the given type" do
|
6
|
+
AbCrunch::LogConsoleWriter.color_for_type(:progress).should == :green
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "when the type is unknown" do
|
10
|
+
it "should return white" do
|
11
|
+
AbCrunch::LogConsoleWriter.color_for_type(:weasel).should == :white
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#prefix_for_type" do
|
17
|
+
it "should return the prefix for the given type" do
|
18
|
+
AbCrunch::LogConsoleWriter.prefix_for_type(:progress).should == ' '
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "when the type is unknown" do
|
22
|
+
it "should return an empty string" do
|
23
|
+
AbCrunch::LogConsoleWriter.prefix_for_type(:weasel).should == ''
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#log" do
|
29
|
+
describe "inline behavior" do
|
30
|
+
before :each do
|
31
|
+
@calls = []
|
32
|
+
@messages = []
|
33
|
+
stub(AbCrunch::LogConsoleWriter).print do |msg|
|
34
|
+
@calls << 'print'
|
35
|
+
@messages << msg
|
36
|
+
end
|
37
|
+
stub(AbCrunch::LogConsoleWriter).puts do |msg|
|
38
|
+
@calls << 'puts'
|
39
|
+
@messages << msg
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "when the message is intended to be inline" do
|
44
|
+
it "should print instead of puts" do
|
45
|
+
AbCrunch::LogConsoleWriter.log(:info, 'foo', {:inline => true})
|
46
|
+
|
47
|
+
@calls.should == ['print']
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "when the message is NOT intended to be inline" do
|
52
|
+
it "should puts instead of print" do
|
53
|
+
AbCrunch::LogConsoleWriter.log(:info, 'foo')
|
54
|
+
|
55
|
+
@calls.should == ['puts']
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "when the previous message was inline, and the current one is not" do
|
60
|
+
it "should prepend a new line" do
|
61
|
+
AbCrunch::LogConsoleWriter.log(:info, 'foo', {:inline => true})
|
62
|
+
AbCrunch::LogConsoleWriter.log(:info, 'foo')
|
63
|
+
|
64
|
+
@messages[1].should include "\n foo"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "if the type has a color" do
|
70
|
+
it "should should invoke the color on the message" do
|
71
|
+
calls = []
|
72
|
+
any_instance_of(String) do |s|
|
73
|
+
stub(s).green { calls << 'green' }
|
74
|
+
end
|
75
|
+
|
76
|
+
AbCrunch::LogConsoleWriter.log(:progress, 'foo')
|
77
|
+
|
78
|
+
calls.should == ['green']
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "AbCrunch::Logger" do
|
4
|
+
describe "#log" do
|
5
|
+
it "should call log on all writers" do
|
6
|
+
AbCrunch::Logger.writers = [AbCrunch::LogConsoleWriter]
|
7
|
+
mock(AbCrunch::LogConsoleWriter).log(:foo, 'bar', {:bizz => 'bazam!'})
|
8
|
+
|
9
|
+
AbCrunch::Logger.log(:foo, 'bar', {:bizz => 'bazam!'})
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "AbCrunch::Page" do
|
4
|
+
describe "#get_url" do
|
5
|
+
describe "when the page url is a string" do
|
6
|
+
it "should return the string" do
|
7
|
+
page = { :url => 'some url' }
|
8
|
+
|
9
|
+
AbCrunch::Page.get_url(page).should == 'some url'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "when the page url is a proc" do
|
14
|
+
it "should call the proc and return the result" do
|
15
|
+
page = { :url => Proc.new { 'a proc url' } }
|
16
|
+
|
17
|
+
AbCrunch::Page.get_url(page).should == 'a proc url'
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "when the page url is a proc returning different results each call" do
|
21
|
+
|
22
|
+
before :each do
|
23
|
+
@call_num = 0
|
24
|
+
@page = { :url => Proc.new { @call_num += 1 ; "changing url #{@call_num}" } }
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "when called repeatedly without force_new" do
|
28
|
+
it "should return the same url each time" do
|
29
|
+
AbCrunch::Page.get_url(@page).should == 'changing url 1'
|
30
|
+
AbCrunch::Page.get_url(@page).should == 'changing url 1'
|
31
|
+
AbCrunch::Page.get_url(@page).should == 'changing url 1'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "when called repeadedly with force_new = True" do
|
36
|
+
it "should return different urls each time" do
|
37
|
+
AbCrunch::Page.get_url(@page, true).should == 'changing url 1'
|
38
|
+
AbCrunch::Page.get_url(@page, true).should == 'changing url 2'
|
39
|
+
AbCrunch::Page.get_url(@page, true).should == 'changing url 3'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#get_display_url" do
|
48
|
+
describe "when the url is a string" do
|
49
|
+
it "should return the string" do
|
50
|
+
page = { :url => 'a text url' }
|
51
|
+
|
52
|
+
AbCrunch::Page.get_display_url(page).should == 'a text url'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "when the url is a proc" do
|
57
|
+
it "should return an example, noting that it's dynamic'" do
|
58
|
+
page = { :url => Proc.new { 'a proc url' } }
|
59
|
+
|
60
|
+
AbCrunch::Page.get_display_url(page).should == 'Dynamic url example: a proc url'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "AbCrunch::PageTester" do
|
4
|
+
describe "#test" do
|
5
|
+
|
6
|
+
def stub_strategies(result = nil)
|
7
|
+
strategy_result = result ? result : AbCrunchSpec.new_result
|
8
|
+
|
9
|
+
@strategies.each do |strategy|
|
10
|
+
stub(strategy).run { strategy_result }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
before :each do
|
15
|
+
@test_page = AbCrunchSpec.new_page
|
16
|
+
@test_result = AbCrunchSpec.new_result
|
17
|
+
@strategies = [AbCrunch::StrategyBestConcurrency]
|
18
|
+
|
19
|
+
stub(AbCrunch::Logger).log
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should show the display url" do
|
23
|
+
stub_strategies
|
24
|
+
|
25
|
+
mock(AbCrunch::Page).get_display_url(@test_page)
|
26
|
+
|
27
|
+
AbCrunch::PageTester.test(@test_page)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should run every load testing strategy on the given page" do
|
31
|
+
@strategies.each do |strategy|
|
32
|
+
mock(strategy).run(@test_page) { @test_result }
|
33
|
+
end
|
34
|
+
|
35
|
+
AbCrunch::PageTester.test(@test_page)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should use any user provided max avg response time as the max latency for all strategies" do
|
39
|
+
stub_strategies
|
40
|
+
page = AbCrunchSpec.new_page({:max_avg_response_time => 139.2})
|
41
|
+
|
42
|
+
AbCrunch::PageTester.test(page)
|
43
|
+
|
44
|
+
page[:max_latency].should == 139.2
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should fail if the avg response time is over the specified maximum" do
|
48
|
+
result = AbCrunchSpec.new_result
|
49
|
+
class << result
|
50
|
+
def avg_response_time
|
51
|
+
200
|
52
|
+
end
|
53
|
+
end
|
54
|
+
page = AbCrunchSpec.new_page({:max_avg_response_time => 117})
|
55
|
+
|
56
|
+
stub_strategies(result)
|
57
|
+
|
58
|
+
passed, qps_result, errors = AbCrunch::PageTester.test(page)
|
59
|
+
|
60
|
+
passed.should == false
|
61
|
+
errors.should == [
|
62
|
+
"some page: Avg response time of 200 must be <= 117"
|
63
|
+
]
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should fail if the queries per second is under the specified minimum" do
|
67
|
+
result = AbCrunchSpec.new_result
|
68
|
+
class << result
|
69
|
+
def queries_per_second
|
70
|
+
5
|
71
|
+
end
|
72
|
+
end
|
73
|
+
page = AbCrunchSpec.new_page({:min_queries_per_second => 117})
|
74
|
+
|
75
|
+
stub_strategies(result)
|
76
|
+
|
77
|
+
passed, qps_result, errors = AbCrunch::PageTester.test(page)
|
78
|
+
|
79
|
+
passed.should == false
|
80
|
+
errors.should == [
|
81
|
+
"some page: QPS of 5 must be >= 117"
|
82
|
+
]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|