hansel 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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Paul Mylchreest
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,22 @@
1
+ = hansel
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == References
16
+
17
+ "HTTP Perforance Testing with httperf":http://agiletesting.blogspot.com/2005/04/http-performance-testing-with-httperf.html
18
+ "Perforance vs Load Testing":http://agiletesting.blogspot.com/2005/04/more-on-performance-vs-load-testing.html
19
+
20
+ == Copyright
21
+
22
+ Copyright (c) 2010 Paul Mylchreest. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "hansel"
8
+ gem.summary = %Q{Ruby driver for httperf - automated load and performance testing}
9
+ gem.description = %Q{Ruby driver for httperf - automated load and performance testing}
10
+ gem.email = "paul.mylchreest@mac.com"
11
+ gem.homepage = "http://github.com/xlymian/hansel"
12
+ gem.authors = ["Paul Mylchreest"]
13
+ gem.add_dependency("mutter", ">= 0.5.2")
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/bin/hansel ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'optparse'
5
+ require 'ostruct'
6
+
7
+ class File
8
+ def self.here(string)
9
+ expand_path(dirname(__FILE__)) + string
10
+ end
11
+ end
12
+
13
+ require File.here '/../lib/arg_parser'
14
+ require File.here '/../lib/mutter'
15
+ require File.here '/../lib/config'
16
+ require File.here '/../lib/hansel'
17
+
18
+ include Config
19
+ include Mutter
20
+
21
+ hansel = Hansel.new(options)
22
+ puts hansel.run
data/hansel.gemspec ADDED
@@ -0,0 +1,58 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{hansel}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Paul Mylchreest"]
12
+ s.date = %q{2010-01-24}
13
+ s.default_executable = %q{hansel}
14
+ s.description = %q{Ruby driver for httperf - automated load and performance testing}
15
+ s.email = %q{paul.mylchreest@mac.com}
16
+ s.executables = ["hansel"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".gitignore",
24
+ "LICENSE",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "bin/hansel",
29
+ "hansel.gemspec",
30
+ "hansel.rb",
31
+ "lib/arg_parser.rb",
32
+ "lib/config.rb",
33
+ "lib/csv_formatter.rb",
34
+ "lib/hansel.rb",
35
+ "lib/mutter.rb",
36
+ "lib/octave_formatter.rb",
37
+ "lib/yaml_formatter.rb"
38
+ ]
39
+ s.homepage = %q{http://github.com/xlymian/hansel}
40
+ s.rdoc_options = ["--charset=UTF-8"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = %q{1.3.5}
43
+ s.summary = %q{Ruby driver for httperf - automated load and performance testing}
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
+ s.add_runtime_dependency(%q<mutter>, [">= 0.5.2"])
51
+ else
52
+ s.add_dependency(%q<mutter>, [">= 0.5.2"])
53
+ end
54
+ else
55
+ s.add_dependency(%q<mutter>, [">= 0.5.2"])
56
+ end
57
+ end
58
+
data/hansel.rb ADDED
@@ -0,0 +1 @@
1
+
data/lib/arg_parser.rb ADDED
@@ -0,0 +1,100 @@
1
+ class ArgParser
2
+ # http://www.ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html
3
+ # Return a structure describing the options.
4
+ def self.parse(args)
5
+ # The options specified on the command line will be collected in *options*.
6
+ # We set default values here.
7
+ options = OpenStruct.new
8
+ options.verbose = false
9
+ options.server = 'localhost'
10
+ options.port = '80'
11
+ options.uri = '/'
12
+ options.num_conns = 1
13
+ options.rate = 1
14
+ options.cookie = nil
15
+ options.low_rate = 1
16
+ options.high_rate = 2
17
+ options.rate_step = 1
18
+ options.output_format = :yaml
19
+ options.output = nil
20
+
21
+ opts = OptionParser.new do |opts|
22
+ opts.banner = "Usage: hansel [options]"
23
+
24
+ opts.separator ""
25
+ opts.separator "Specific options:"
26
+
27
+ # Mandatory argument.
28
+ opts.on("-s", "--server=S",
29
+ "Specifies the IP hostname of the server.") do |opt|
30
+ options.server = opt
31
+ end
32
+
33
+ opts.on("-p", "--port=N",
34
+ "This option specifies the port number N on which the web server is listening for HTTP requests.") do |opt|
35
+ options.port = opt
36
+ end
37
+
38
+ opts.on("-u", "--uri=S",
39
+ "Specifies that URI S should be accessed on the server.") do |opt|
40
+ options.uri = opt
41
+ end
42
+
43
+ opts.on("-n", "--num_conns=N",
44
+ "Specifies the total number of connections to create.") do |opt|
45
+ options.num_conns = opt.to_i
46
+ end
47
+
48
+ opts.on("-l", "--low_rate=S",
49
+ "Specifies the starting fixed rate at which connections are created.") do |opt|
50
+ options.low_rate = opt.to_i
51
+ end
52
+
53
+ opts.on("-l", "--high_rate=S",
54
+ "Specifies the ending fixed rate at which connections are created.") do |opt|
55
+ options.high_rate = opt.to_i
56
+ end
57
+
58
+ opts.on("-l", "--rate_step=S",
59
+ "Specifies the fixed rate step at which connections are created.") do |opt|
60
+ options.rate_step = opt.to_i
61
+ end
62
+
63
+ opts.on("-c", "--cookie=C", "Specify a cookie.") do |opt|
64
+ options.cookie = opt
65
+ end
66
+
67
+ opts.on("-f", "--format=FILE [yaml|csv]", "Specify an output format.") do |opt|
68
+ options.output_format = opt.to_sym
69
+ end
70
+
71
+ opts.on("-o", "--output=FILE", "Specify an output file.") do |opt|
72
+ options.output = opt
73
+ end
74
+
75
+ opts.separator ""
76
+ opts.separator "Common options:"
77
+
78
+ # Boolean switch.
79
+ opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
80
+ options.verbose = v
81
+ end
82
+
83
+ # No argument, shows at tail. This will print an options summary.
84
+ # Try it and see!
85
+ opts.on_tail("-h", "--help", "Show this message") do
86
+ puts opts
87
+ exit
88
+ end
89
+
90
+ # Another typical switch to print the version.
91
+ opts.on_tail("--version", "Show version") do
92
+ puts OptionParser::Version.join('.')
93
+ exit
94
+ end
95
+ end
96
+
97
+ opts.parse!(args)
98
+ options
99
+ end
100
+ end
data/lib/config.rb ADDED
@@ -0,0 +1,27 @@
1
+ module Config
2
+ def config_path
3
+ config_path ||= File.join [ENV['HOME'], '.hansel']
4
+ end
5
+
6
+ def options_path
7
+ options_path ||= File.join config_path, 'options'
8
+ end
9
+
10
+ def read_config_file
11
+ args = []
12
+ FileUtils.mkdir_p config_path unless File.exists? config_path
13
+ if File.exists? options_path
14
+ File.open options_path do |file|
15
+ file.read.split("\n").each do |line|
16
+ next if line =~ /#+/
17
+ args += line.split(' ')
18
+ end
19
+ end
20
+ end
21
+ args
22
+ end
23
+
24
+ def options
25
+ @opt ||= ArgParser.parse(read_config_file + ARGV)
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ require 'csv'
2
+
3
+ class CsvFormatter
4
+ def initialize(data)
5
+ @data = data
6
+ end
7
+
8
+ def format
9
+ result = ""
10
+ return result if @data.empty?
11
+ keys = @data[@data.keys.first].keys
12
+ result << CSV.generate_line(keys.map(&:to_s))
13
+ result << "\n"
14
+ @data.each_pair do |key, value|
15
+ result << CSV.generate_line(keys.collect{|k| value[k]})
16
+ result << "\n"
17
+ end
18
+ result
19
+ end
20
+ end
data/lib/hansel.rb ADDED
@@ -0,0 +1,83 @@
1
+ class Hansel
2
+ def initialize(options = {})
3
+ @options = options
4
+ @results = {}
5
+ end
6
+
7
+ def verbose?
8
+ @options.verbose
9
+ end
10
+
11
+ def benchmark(rate)
12
+ result = {:output => ""}
13
+ httperf_cmd = [
14
+ "httperf --hog",
15
+ "--server=#{@options.server}",
16
+ "--port=#{@options.port}",
17
+ "--uri=#{@options.uri}",
18
+ "--num-conns=#{@options.num_conns}",
19
+ "--rate=#{rate}",
20
+ @options.cookie && "--add-header='Cookie: #{@options.cookie}\\n'"
21
+ ].compact.join ' '
22
+
23
+ IO.popen("#{httperf_cmd} 2>&1") do |pipe|
24
+ puts "\n#{httperf_cmd}"
25
+ while line = pipe.gets
26
+ case line
27
+ when /^Total: .*replies (\d+)/ then result[:replies] = $1
28
+ when /^Connection rate: (\d+\.\d)/ then result[:connection_rate] = $1
29
+ when /^Request rate: (\d+\.\d)/ then result[:request_rate] = $1
30
+ when /^Reply time .* response (\d+\.\d)/ then result[:reply_time] = $1
31
+ when /^Net I\/O: (\d+\.\d)/ then result[:net_io] = $1
32
+ when /^Errors: total (\d+)/ then result[:errors] = $1
33
+ when /^Reply rate .*min (\d+\.\d) avg (\d+\.\d) max (\d+\.\d) stddev (\d+\.\d)/ then
34
+ result[:reply_rate_min] = $1
35
+ result[:reply_rate_avg] = $2
36
+ result[:reply_rate_max] = $3
37
+ result[:reply_rate_stddev] = $4
38
+ when /^Reply status: 1xx=\d+ 2xx=\d+ 3xx=\d+ 4xx=\d+ 5xx=(\d+)/ then result[:status] = $1
39
+ end
40
+ end
41
+ end
42
+ result
43
+ end
44
+
45
+ def output
46
+ if @options.output
47
+ File.open(@options.output, "w+") do |f|
48
+ formatter = case @options.output_format
49
+ when :yaml
50
+ load File.here '/../lib/yaml_formatter.rb'
51
+ YamlFormatter.new(@results)
52
+ when :csv
53
+ load File.here '/../lib/csv_formatter.rb'
54
+ CsvFormatter.new(@results)
55
+ when :octave
56
+ load File.here '/../lib/octave_formatter.rb'
57
+ OctaveFormatter.new(@results)
58
+ end
59
+ f.puts formatter.format
60
+ end
61
+ end
62
+ end
63
+
64
+ def run
65
+ puts "starting run..." if verbose?
66
+ (@options.low_rate..@options.high_rate).step(@options.rate_step) do |rate|
67
+ puts "benchmarking at rate: #{rate}" if verbose?
68
+ @results[:server] = @options.server
69
+ @results[:port] = @options.port
70
+ @results[:uri] = @options.uri
71
+ @results[:num_conns] = @options.num_conns
72
+ @results[rate] = benchmark(rate)
73
+ end
74
+ puts "ending run..." if verbose?
75
+ output
76
+ @results
77
+ end
78
+
79
+ trap("INT") {
80
+ puts "Terminating tests."
81
+ Process.exit
82
+ }
83
+ end
data/lib/mutter.rb ADDED
@@ -0,0 +1,16 @@
1
+ module Mutter
2
+ begin
3
+ require 'mutter'
4
+ rescue LoadError
5
+ STDERR.puts "mutter gem wasn't found, using default output."
6
+ end
7
+
8
+ def mutter
9
+ mutter ||= Mutter.new({
10
+ :underline => %w'( )', :yellow => %w'[ ]',
11
+ :bold => %w'< >'
12
+ }).clear(:default)
13
+ rescue
14
+ nil
15
+ end
16
+ end
@@ -0,0 +1,47 @@
1
+ class OctaveFormatter
2
+ def initialize(data)
3
+ @data = data
4
+ end
5
+
6
+ def format
7
+ rate = @data.keys.reject{|x| x.instance_of? Symbol}.map(&:to_i).sort
8
+ request_rate = rate.collect{|x| @data[x][:request_rate]}
9
+ connection_rate = rate.collect{|x| @data[x][:connection_rate]}
10
+ reply_rate_avg = rate.collect{|x| @data[x][:reply_rate_avg]}
11
+ reply_rate_max = rate.collect{|x| @data[x][:reply_rate_max]}
12
+ reply_rate_stddev = rate.collect{|x| @data[x][:reply_rate_stddev]}
13
+ errors = rate.collect{|x| @data[x][:errors]}
14
+
15
+ result = <<-EOS
16
+ rate = [#{rate.join(',')}];
17
+ request_rate = [#{request_rate.join(',')}];
18
+ connection_rate = [#{connection_rate.join(',')}];
19
+ reply_rate_avg = [#{reply_rate_avg.join(',')}];
20
+ reply_rate_max = [#{reply_rate_max.join(',')}];
21
+ reply_rate_stddev = [#{reply_rate_stddev.join(',')}];
22
+ errors = [#{errors.join(',')}];
23
+
24
+ plot(rate, request_rate, '-k*');
25
+ hold on;
26
+ plot(rate, connection_rate, '-kd');
27
+ hold on;
28
+ plot(rate, reply_rate_max, '-kp');
29
+ hold on;
30
+ plot(rate, reply_rate_max, '-k+');
31
+ hold on;
32
+ plot(rate, reply_rate_stddev, '-kh');
33
+ hold on;
34
+ plot(rate, errors, '-r<');
35
+
36
+ grid on;
37
+
38
+ axis([0 #{rate.last} 0 #{rate.last}]);
39
+ title('Hansel report for #{@data[:server]}:#{@data[:port]}#{@data[:uri]} (#{@data[:num_conns]} connections per run)')
40
+ xlabel('Demanded Request Rate');
41
+ legend('Request Rate', 'Connection Rate', 'Avg. reply rate', 'Max. reply rate', 'Reply rate StdDev', 'Errors');
42
+ print('stats.png', '-dpng')
43
+ EOS
44
+ result = result.gsub ' ', ''
45
+ result
46
+ end
47
+ end
@@ -0,0 +1,9 @@
1
+ class YamlFormatter
2
+ def initialize(data)
3
+ @data = data
4
+ end
5
+
6
+ def format
7
+ @data.to_yaml
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hansel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Paul Mylchreest
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-24 00:00:00 -05:00
13
+ default_executable: hansel
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: mutter
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.5.2
24
+ version:
25
+ description: Ruby driver for httperf - automated load and performance testing
26
+ email: paul.mylchreest@mac.com
27
+ executables:
28
+ - hansel
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.rdoc
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - LICENSE
38
+ - README.rdoc
39
+ - Rakefile
40
+ - VERSION
41
+ - bin/hansel
42
+ - hansel.gemspec
43
+ - hansel.rb
44
+ - lib/arg_parser.rb
45
+ - lib/config.rb
46
+ - lib/csv_formatter.rb
47
+ - lib/hansel.rb
48
+ - lib/mutter.rb
49
+ - lib/octave_formatter.rb
50
+ - lib/yaml_formatter.rb
51
+ has_rdoc: true
52
+ homepage: http://github.com/xlymian/hansel
53
+ licenses: []
54
+
55
+ post_install_message:
56
+ rdoc_options:
57
+ - --charset=UTF-8
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.5
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Ruby driver for httperf - automated load and performance testing
79
+ test_files: []
80
+