rwb 0.2.2 → 0.2.3

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.
@@ -0,0 +1,62 @@
1
+ require 'rubygems'
2
+ require 'rake/clean'
3
+ require 'rake/rdoctask'
4
+ require 'rake/testtask'
5
+ require 'rake/gempackagetask'
6
+ require 'rubygems'
7
+
8
+
9
+ Rake::RDocTask.new do |rd|
10
+ rd.main = "readme"
11
+ rd.rdoc_files.include("readme", "lib/**/*.rb")
12
+ end
13
+
14
+ Rake::TestTask.new(:unittests) do |t|
15
+ t.test_files = FileList['test/test*.rb']
16
+ t.warning = true
17
+ t.verbose = false
18
+ end
19
+
20
+ task :test => [:unittests]
21
+
22
+ spec = Gem::Specification.new do |s|
23
+ s.name = 'rwb'
24
+ s.version = "0.2.3"
25
+ s.author = "Pat Eyler"
26
+ s.email = "pat.eyler@gmail.com"
27
+ s.homepage = "http://rubyforge.org/projects/rwb"
28
+ s.platform = Gem::Platform::RUBY
29
+ s.summary = "Ruby Web Bench, a web performance and load testing framework"
30
+ s.files = Dir.glob("{test,lib,bin}/**/*").delete_if {|item|
31
+ item.include?("CVS") || item.include?("rdoc")}
32
+ s.files += ['NEWS', 'README', 'TODO', 'Rakefile']
33
+ s.require_path = 'lib'
34
+ s.autorequire = 'rwb'
35
+ s.has_rdoc = 'true'
36
+ end
37
+
38
+ if $0==__FILE__
39
+ Gem::manage_gems
40
+ Gem::Builder.new(spec).build
41
+ end
42
+
43
+ Rake::GemPackageTask.new(spec) do |pkg|
44
+ pkg.need_zip = true
45
+ pkg.need_tar = true
46
+ end
47
+
48
+
49
+ desc "Perform a cvs update"
50
+ # relies on ssh keys being set up correctly
51
+ task :update do
52
+ sh "cvs update"
53
+ end
54
+
55
+ desc "Perform a cvs commit after running unit tests and
56
+ updating"
57
+ # relies on ssh keys being set up correctly
58
+ task :commit => [:update, :unittests] do |t|
59
+ msg = "commit from Rake:\n"
60
+ msg << ENV['MSG']
61
+ sh "cvs commit -m '#{msg}'"
62
+ end
@@ -0,0 +1,100 @@
1
+ module RWB
2
+ class Runner
3
+
4
+ def report_full(sla_levels = @sla_levels)
5
+ report_header
6
+ report_overall(sla_levels)
7
+ report_urls(sla_levels)
8
+ report_by_time(sla_levels)
9
+ end
10
+
11
+ def report_overall(sla_levels = @sla_levels)
12
+ results = @new_results.times_by_url
13
+
14
+ puts "Overall results:"
15
+ print_times(results.sort, sla_levels)
16
+ end
17
+
18
+ def report_urls(sla_levels = @sla_levels)
19
+ @urls.urls.keys.sort.each do |key|
20
+ url = @urls.urls[key].to_base
21
+ results = @new_results.times_by_url(url)
22
+ puts "Results for #{url}:"
23
+ if results.length > 0
24
+ print_times(results.sort, sla_levels)
25
+ else
26
+ puts "no results for url"
27
+ end
28
+ end
29
+ end
30
+
31
+ def graph_quartiles_urls(scale = nil)
32
+ @urls.urls.keys.sort.each do |key|
33
+ url = @urls.urls[key].to_base
34
+ results = @new_results.times_by_url(url)
35
+ puts "#{url}:\n\t" + results_quartile(results.sort, scale)
36
+ end
37
+ end
38
+
39
+ def graph_quartiles_overall
40
+ results = @new_results.times_by_url
41
+ puts results_quartile(results.sort)
42
+ end
43
+
44
+ def report_by_time(sla_levels = @sla_levels, granularity = 0.2)
45
+ start = 0.0
46
+ stop = granularity
47
+ results = @new_results.times_by_url
48
+ puts "Results by time:"
49
+ while stop <= 1.0
50
+ first = (results.length * start).to_i
51
+ last = (results.length * stop).to_i
52
+ these_results = results.slice(first..last)
53
+ puts "results for requests #{first} - #{last}"
54
+ print_times(these_results.sort, sla_levels)
55
+ start = stop
56
+ stop += granularity
57
+ end
58
+ end
59
+
60
+ def print_times(results, levels)
61
+ times = get_times(results, levels)
62
+ puts "\tShortest time:\t#{times.shift} msecs"
63
+ levels.each_with_index do |num, index|
64
+ percent = num.to_f * 100.0
65
+ puts "\t#{percent}%ile time:\t#{times[index]} msecs"
66
+ end
67
+ puts "\tLongest time:\t#{times[-1]} msecs"
68
+ end
69
+
70
+ def get_times(results, levels)
71
+ times = Array.new
72
+ times.push(make_milli(results[0]))
73
+ levels.each do |num|
74
+ times.push(make_milli(results[(results.length*num - 1).round]))
75
+ end
76
+ unless times.last == make_milli(results.last)
77
+ times.push(make_milli(results.last))
78
+ end
79
+ times
80
+ end
81
+
82
+ def make_milli(num)
83
+ (num * 1000).to_i
84
+ end
85
+
86
+ def report_header
87
+ print <<EOF
88
+ Concurrency Level: #{@max_threads}
89
+ Total Requests: #{@max_runs}
90
+ Total time for testing: #{@total_time} secs
91
+ Requests per second: #{@max_runs/@total_time}
92
+ Mean time per request: #{results_mean} msecs
93
+ Standard deviation: #{results_std_dev}
94
+ EOF
95
+ end
96
+
97
+
98
+
99
+ end
100
+ end
@@ -0,0 +1,164 @@
1
+ module RWB
2
+
3
+ class Result
4
+ attr_reader :id, :timestamp, :url
5
+ attr_reader :elapsed_time, :response_code
6
+
7
+ def initialize(id, timestamp, url, elapsed_time, response_code)
8
+ @id = id
9
+ @timestamp = timestamp
10
+ @url = url
11
+ @elapsed_time = elapsed_time
12
+ @response_code = response_code
13
+ end
14
+ end
15
+
16
+ class RunResults
17
+ attr_reader :results
18
+
19
+ def initialize
20
+ @results = Array.new
21
+ end
22
+
23
+ def add_result(result)
24
+ @results.push(result)
25
+ end
26
+
27
+ def each_by_response_code(code)
28
+ @results.each do |result|
29
+ yield result if result.response_code.to_i == code.to_i
30
+ end
31
+ end
32
+
33
+ def all_by_response_code(code)
34
+ coded_results = Array.new
35
+ @results.each do |result|
36
+ if result.response_code.to_i == code.to_i
37
+ coded_results.push(result)
38
+ end
39
+ end
40
+ coded_results
41
+ end
42
+
43
+ def all_by_url(url)
44
+ coded_results = Array.new
45
+ @results.each do |result|
46
+ if result.url.to_s == url.to_s
47
+ coded_results.push(result)
48
+ end
49
+ end
50
+ coded_results
51
+ end
52
+
53
+ def each_by_url(url)
54
+ @results.each do |result|
55
+ yield result if result.url.to_s == url.to_s
56
+ end
57
+ end
58
+
59
+ def all_with_id
60
+ id_results = Hash.new
61
+ @results.each do |result|
62
+ id_results[result.id] = result
63
+ end
64
+ id_results
65
+ end
66
+
67
+ def times_by_url(url = nil)
68
+ coded_results = Array.new
69
+ @results.each do |result|
70
+ if url
71
+ if result.url.to_s == url.to_s
72
+ coded_results.push(result.elapsed_time)
73
+ end
74
+ else
75
+ coded_results.push(result.elapsed_time)
76
+ end
77
+ end
78
+ coded_results
79
+ end
80
+
81
+ end
82
+
83
+ class Runner
84
+ def results_mean
85
+ if @results_mean
86
+ return @results_mean
87
+ end
88
+ @results_mean = @new_results.times_by_url.inject(0) do |sum, time|
89
+ sum += time
90
+ end
91
+ @results_mean = @results_mean/@new_results.times_by_url.length
92
+ make_milli(@results_mean)
93
+ end
94
+
95
+ def results_std_dev
96
+ if @results_std_dev
97
+ return @results_std_dev
98
+ end
99
+ unless @results_mean
100
+ results_mean
101
+ end
102
+ @results_std_dev = @new_results.times_by_url.inject(0) do |std_dev, time|
103
+ std_dev += (time - @results_mean) ** 2
104
+ end
105
+ @results_std_dev =
106
+ Math.sqrt(@results_std_dev/@new_results.times_by_url.length)
107
+ make_milli(@results_std_dev)
108
+ end
109
+
110
+ def adjust_scale(scale, max)
111
+ if scale/2 > max
112
+ scale = scale/2
113
+ scale = adjust_scale(scale, max)
114
+ end
115
+ scale
116
+ end
117
+
118
+ def results_quartile(times, scale = nil)
119
+ times = times.map { |t| make_milli(t) }
120
+ size = results_mean + results_std_dev * 2
121
+ min = times[0]
122
+ first = times[(times.length*0.25).to_int - 1]
123
+ second = times[(times.length*0.5).to_int - 1]
124
+ third = times[(times.length*0.75).to_int - 1]
125
+ max = times[-1]
126
+
127
+ len = max.to_s.length
128
+ unless scale
129
+ scale = (max/((10**len).to_f)).ceil * (10**len)
130
+ scale = adjust_scale(scale, max)
131
+ end
132
+ step = scale/50
133
+ step = 1 if step == 0
134
+ scale = 50 if scale < 50
135
+ line = Array.new
136
+ char = ' '
137
+ for i in 0..49 do
138
+ case i
139
+ when max/step
140
+ line[i] = ']'
141
+ when third/step
142
+ line[i] = '|'
143
+ char = ' '
144
+ when second/step
145
+ line[i] = '+'
146
+ when first/step
147
+ line[i] = '|'
148
+ char = '-'
149
+ when min/step
150
+ line[i] = '['
151
+ when 0
152
+ line[i] = ':'
153
+ when 49
154
+ line[i] = ':'
155
+ else
156
+ line[i] = char
157
+ end
158
+ end
159
+ return '0' + line.join('') + scale.to_s
160
+ end
161
+ end
162
+
163
+
164
+ end
@@ -0,0 +1,126 @@
1
+ module RWB
2
+
3
+ #
4
+ # The Url class holds individual URLs for testing. +Url+s are defined by a
5
+ # Fixnum representing the weight and a String representing the URL itself.
6
+ # The weights are not required to be percentages.
7
+ #
8
+ # url = RWB::Url.new(20, "http://www.example.com")
9
+ class Url
10
+ attr_reader :weight, :method
11
+
12
+ def initialize(weight, url, method='GET')
13
+ @weight = weight
14
+ @url = url
15
+ @method = read_method(method)
16
+ end
17
+
18
+ def read_method(method = 'GET')
19
+ method = method.to_s.upcase
20
+ if method != 'GET' && method != 'POST'
21
+ return 'GET'
22
+ else
23
+ return method
24
+ end
25
+ end
26
+
27
+ # to_url returns the URL to be requested as a string
28
+ #
29
+ # url.to_url # => "http://www.example.com"
30
+ def to_url
31
+ @url
32
+ end
33
+
34
+ #
35
+ # to_base returns a string suitable for creating a +Regexp+ to match
36
+ # against.
37
+ #
38
+ # url.to_base # => "http://www.example.com "
39
+ #
40
+ def to_base
41
+ @url
42
+ end
43
+
44
+ def to_s
45
+ @weight.to_s + " " + @url
46
+ end
47
+ end
48
+
49
+ #
50
+ # A UrlGroup is a collection of related URLs, for example the URL for a
51
+ # search tool, with several search queries. The group is weighted (and
52
+ # reported on) as a whole, but individual requests are made with random
53
+ # elements from an array of extensions.
54
+ #
55
+ # urls = UrlGroup.new(20, "http://www.example.com/search?",
56
+ # ["foo", "bar", "baz"])
57
+ #
58
+ class UrlGroup < RWB::Url
59
+
60
+ def initialize(weight, base, extension_array, method='GET')
61
+ @weight = weight.to_i
62
+ @base = base.to_s
63
+ @extension_array = extension_array
64
+ @method = read_method(method)
65
+ end
66
+
67
+
68
+ #
69
+ # to_url returns a complete URL to be requested. It takes an optional
70
+ # seed argument, which must be a Fixnum. If given, this will seed the
71
+ # random selection of the extension.
72
+ #
73
+ # urls.to_url(1234) # => "http://www.example.com/search?baz"
74
+ #
75
+ def to_url(seed = nil)
76
+ if seed
77
+ srand = seed
78
+ end
79
+ @base + @extension_array[rand(@extension_array.length)]
80
+ end
81
+
82
+ #
83
+ # to_base returns a String suitable for building a Regex to match
84
+ # against. This String will not include any extensions.
85
+ #
86
+ # urls.to_base # => "http://www.example.com/search?"
87
+ #
88
+ def to_base
89
+ @base
90
+ end
91
+
92
+ def to_s
93
+ @weight.to_s + " " + @base
94
+ end
95
+
96
+ end
97
+
98
+
99
+ class UrlSession < RWB::Url
100
+ def initialize(weight, session, name, method='GET')
101
+ @weight = weight
102
+ @session = session
103
+ @urls = []
104
+ @name = name
105
+ @method = read_method(method)
106
+ end
107
+
108
+ def to_url
109
+ if @urls == []
110
+ @urls << @session
111
+ @urls.flatten!
112
+ end
113
+ @urls.shift
114
+ end
115
+
116
+ def to_base
117
+ @name
118
+ end
119
+
120
+ def to_s
121
+ @weight.to_s + " " + @name
122
+ end
123
+ end
124
+
125
+
126
+ end
@@ -0,0 +1,48 @@
1
+ module RWB
2
+
3
+ class Runner
4
+ def warmup(num_runs = 1)
5
+ $stderr.puts "warming up with #{num_runs} runs"
6
+ for run in 1..num_runs
7
+ $stderr.print "#{run} "
8
+ @urls.urls.values.each do |url|
9
+ url = URI.parse(url.to_url)
10
+ @http.start(url.host, url.port) do |http|
11
+ http.request( Net::HTTP::Get.new(url) )
12
+ end
13
+ end
14
+ end
15
+ $stderr.puts
16
+ end
17
+
18
+ def rand_warmup(num_requests)
19
+ $stderr.puts "warming up with #{num_requests} requests"
20
+ checkpoint = num_requests/10
21
+ for run in 1..num_requests
22
+ if run % checkpoint == 0
23
+ $stderr.print "#{run} "
24
+ end
25
+ url = URI.parse(@urls.get_url.to_url)
26
+ @http.start(url.host, url.port) do |http|
27
+ http.request( Net::HTTP::Get.new(url) )
28
+ end
29
+ end
30
+ $stderr.puts
31
+ end
32
+
33
+ def spec_warmup(urls, num_runs=1)
34
+ $stderr.puts "warming up with #{num_runs} runs"
35
+ for run in 1..num_runs
36
+ $stderr.print "#{run} "
37
+ urls.each do |url|
38
+ url = URI.parse(url)
39
+ @http.start(url.host, url.port) do |http|
40
+ http.request( Net::HTTP::Get.new(url) )
41
+ end
42
+ end
43
+ end
44
+ $stderr.puts
45
+ end
46
+
47
+ end
48
+ end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: rwb
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.2
7
- date: 2007-01-31 00:00:00 -07:00
6
+ version: 0.2.3
7
+ date: 2007-02-01 00:00:00 -07:00
8
8
  summary: Ruby Web Bench, a web performance and load testing framework
9
9
  require_paths:
10
10
  - lib
@@ -28,11 +28,17 @@ cert_chain:
28
28
  authors:
29
29
  - Pat Eyler
30
30
  files:
31
+ - test/test_rwb.rb
32
+ - lib/rwb
33
+ - lib/rwb.rb
34
+ - lib/rwb/warmup.rb
35
+ - lib/rwb/report.rb
36
+ - lib/rwb/url.rb
37
+ - lib/rwb/results.rb
31
38
  - NEWS
32
39
  - README
33
40
  - TODO
34
- - lib/rwb.rb
35
- - test/test_rwb.rb
41
+ - Rakefile
36
42
  test_files: []
37
43
 
38
44
  rdoc_options: []