stella 0.3.2
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.txt +135 -0
- data/Rakefile +100 -0
- data/bin/stella +12 -0
- data/lib/stella.rb +58 -0
- data/lib/stella/adapter/ab.rb +303 -0
- data/lib/stella/adapter/base.rb +87 -0
- data/lib/stella/adapter/httperf.rb +296 -0
- data/lib/stella/adapter/siege.rb +321 -0
- data/lib/stella/cli.rb +291 -0
- data/lib/stella/cli/agents.rb +73 -0
- data/lib/stella/cli/base.rb +19 -0
- data/lib/stella/cli/language.rb +18 -0
- data/lib/stella/cli/localtest.rb +80 -0
- data/lib/stella/command/base.rb +111 -0
- data/lib/stella/command/localtest.rb +339 -0
- data/lib/stella/logger.rb +63 -0
- data/lib/stella/response.rb +82 -0
- data/lib/stella/storable.rb +116 -0
- data/lib/stella/support.rb +106 -0
- data/lib/stella/test/base.rb +34 -0
- data/lib/stella/test/definition.rb +79 -0
- data/lib/stella/test/run/summary.rb +50 -0
- data/lib/stella/test/summary.rb +82 -0
- data/lib/stella/text.rb +64 -0
- data/lib/stella/text/resource.rb +39 -0
- data/lib/utils/crypto-key.rb +84 -0
- data/lib/utils/escape.rb +302 -0
- data/lib/utils/fileutil.rb +59 -0
- data/lib/utils/httputil.rb +210 -0
- data/lib/utils/mathutil.rb +78 -0
- data/lib/utils/textgraph.rb +267 -0
- data/lib/utils/timerutil.rb +58 -0
- data/support/text/en.yaml +54 -0
- data/support/text/nl.yaml +1 -0
- data/support/useragents.txt +75 -0
- data/vendor/useragent/MIT-LICENSE +20 -0
- data/vendor/useragent/README +21 -0
- data/vendor/useragent/init.rb +1 -0
- data/vendor/useragent/lib/user_agent.rb +83 -0
- data/vendor/useragent/lib/user_agent/browsers.rb +24 -0
- data/vendor/useragent/lib/user_agent/browsers/all.rb +69 -0
- data/vendor/useragent/lib/user_agent/browsers/gecko.rb +43 -0
- data/vendor/useragent/lib/user_agent/browsers/internet_explorer.rb +40 -0
- data/vendor/useragent/lib/user_agent/browsers/opera.rb +49 -0
- data/vendor/useragent/lib/user_agent/browsers/webkit.rb +94 -0
- data/vendor/useragent/lib/user_agent/comparable.rb +25 -0
- data/vendor/useragent/lib/user_agent/operating_systems.rb +19 -0
- data/vendor/useragent/spec/browsers/gecko_user_agent_spec.rb +209 -0
- data/vendor/useragent/spec/browsers/internet_explorer_user_agent_spec.rb +99 -0
- data/vendor/useragent/spec/browsers/opera_user_agent_spec.rb +59 -0
- data/vendor/useragent/spec/browsers/other_user_agent_spec.rb +19 -0
- data/vendor/useragent/spec/browsers/webkit_user_agent_spec.rb +373 -0
- data/vendor/useragent/spec/spec_helper.rb +1 -0
- data/vendor/useragent/spec/user_agent_spec.rb +331 -0
- data/vendor/useragent/useragent.gemspec +12 -0
- metadata +139 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Stella
|
4
|
+
class Logger
|
5
|
+
attr_accessor :debug
|
6
|
+
|
7
|
+
# +args+ is a hash of initialization arguments
|
8
|
+
# * <tt>:info_logger</tt> The IO class for info level logging. Default: STDOUT
|
9
|
+
# * <tt>:error_logger</tt> The IO class for error level logging. Default: STDERR
|
10
|
+
# * <tt>:debug_logger</tt> The IO class for error level logging. Default: STDERR
|
11
|
+
# * <tt>:debug</tt> Log debugging output, true or false (default)
|
12
|
+
def initialize(args={})
|
13
|
+
@debug = args[:debug] || false
|
14
|
+
@info_logger = args[:info_logger] || STDOUT
|
15
|
+
@error_logger = args[:error_logger] || STDERR
|
16
|
+
@debug_logger = args[:debug_logger] || STDERR
|
17
|
+
end
|
18
|
+
|
19
|
+
# +msgs+ is an array which can contain a list of messages or a symbol and a list of values
|
20
|
+
# If the first element is a symbol, this will return the output of Stella::Text.msg(msgs[0],msgs[1..-1])
|
21
|
+
def info(*msgs)
|
22
|
+
return if !msgs || msgs.empty?
|
23
|
+
if msgs[0].is_a? Symbol
|
24
|
+
txtsym = msgs.shift
|
25
|
+
@info_logger.puts Stella::TEXT.msg(txtsym, msgs)
|
26
|
+
else
|
27
|
+
msgs.each do |m|
|
28
|
+
@info_logger.puts m
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@info_logger.flush
|
32
|
+
end
|
33
|
+
|
34
|
+
# Print all messages on a single line.
|
35
|
+
def info_print(*msgs)
|
36
|
+
msgs.each do |m|
|
37
|
+
@info_logger.print m
|
38
|
+
end
|
39
|
+
@info_logger.flush
|
40
|
+
end
|
41
|
+
|
42
|
+
# Print all messages on a single line.
|
43
|
+
def info_printf(pattern, *vals)
|
44
|
+
@info_logger.printf(pattern, *vals)
|
45
|
+
@info_logger.flush
|
46
|
+
end
|
47
|
+
|
48
|
+
def debug(*msgs)
|
49
|
+
return unless @debug
|
50
|
+
msgs.each do |m|
|
51
|
+
@debug_logger.puts "DEBUG: #{m}"
|
52
|
+
end
|
53
|
+
@debug_logger.flush
|
54
|
+
end
|
55
|
+
def error(ex, prefix="ERR: ")
|
56
|
+
@error_logger.puts "#{prefix}#{ex.message}"
|
57
|
+
return unless @debug
|
58
|
+
@error_logger.puts("#{prefix}------------------------------------------")
|
59
|
+
@error_logger.puts("#{prefix}#{ex.backtrace.join("\n")}")
|
60
|
+
@error_logger.puts("#{prefix}------------------------------------------")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module Stella
|
5
|
+
|
6
|
+
# An object for HTTP response content
|
7
|
+
#
|
8
|
+
class Response < Storable
|
9
|
+
attr_accessor :errors, :content, :messages
|
10
|
+
attr_writer :success
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@success = false
|
14
|
+
@errors = []
|
15
|
+
@messages = []
|
16
|
+
@content = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def success?
|
20
|
+
@success
|
21
|
+
end
|
22
|
+
|
23
|
+
def add(key, value)
|
24
|
+
@content[key] = value
|
25
|
+
end
|
26
|
+
|
27
|
+
def get(key)
|
28
|
+
@content[key] if @content.has_key? key
|
29
|
+
end
|
30
|
+
|
31
|
+
def message(msg)
|
32
|
+
@messages.push(msg)
|
33
|
+
end
|
34
|
+
|
35
|
+
def error(msg)
|
36
|
+
@errors.push(msg)
|
37
|
+
end
|
38
|
+
|
39
|
+
def output(format='yaml')
|
40
|
+
format = 'yaml' unless self.respond_to? "output_#{format}"
|
41
|
+
#STDERR.puts "OUTPUT: #{format}"
|
42
|
+
self.send("output_#{format}")
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_hash
|
46
|
+
h = {}
|
47
|
+
h[:version] = API_VERSION
|
48
|
+
h[:errors] = @errors unless @errors.empty?
|
49
|
+
h[:messages] = @messages unless @messages.empty?
|
50
|
+
h[:content] = @content || {}
|
51
|
+
h[:success] = @success || false
|
52
|
+
h
|
53
|
+
end
|
54
|
+
|
55
|
+
def output_zip
|
56
|
+
output = @content
|
57
|
+
end
|
58
|
+
|
59
|
+
def output_yaml
|
60
|
+
to_hash.to_yaml
|
61
|
+
end
|
62
|
+
|
63
|
+
# http://evang.eli.st/blog/2007/2/22/my-rails-gotcha-custom-to_xml-in-a-hash-or-array
|
64
|
+
# http://api.rubyonrails.org/classes/ActiveRecord/XmlSerialization.html#M000910
|
65
|
+
def output_xml
|
66
|
+
output = "<StellaResponse success=\":[\">\n"
|
67
|
+
output << "<todo>implement XML</todo>\n"
|
68
|
+
output << "</StellaResponse>\n"
|
69
|
+
end
|
70
|
+
|
71
|
+
def output_json
|
72
|
+
to_hash.to_json
|
73
|
+
end
|
74
|
+
|
75
|
+
def output_html
|
76
|
+
"hello!"
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
@@ -0,0 +1,116 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Stella
|
4
|
+
class Storable
|
5
|
+
|
6
|
+
SupportedFormats= {
|
7
|
+
'yaml' => 'yml', # format name => file extension
|
8
|
+
'yml' => 'yml',
|
9
|
+
'csv' => 'csv',
|
10
|
+
'tsv' => 'tsv',
|
11
|
+
'json' => 'json'
|
12
|
+
}.freeze unless defined? SupportedFormats
|
13
|
+
|
14
|
+
attr_reader :format
|
15
|
+
|
16
|
+
def format=(v)
|
17
|
+
raise "Unsupported format: #{v}" unless SupportedFormats.has_key?(v)
|
18
|
+
@format = v
|
19
|
+
end
|
20
|
+
|
21
|
+
def field_names
|
22
|
+
raise "You need to override field_names (#{self.class})"
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.undump(format, file=[])
|
26
|
+
#raise "Format not defined (#{@format})" unless self.method_defined?("to_#{@format}")
|
27
|
+
#puts "LOAD: from_#{format}"
|
28
|
+
send("from_#{format}", file)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def dump(format="yaml", with_titles=true)
|
33
|
+
#raise "Format not defined (#{@format})" unless self.method_defined?("to_#{@format}")
|
34
|
+
#puts "DUMP: to_#{format}"
|
35
|
+
self.send("to_#{format}", with_titles)
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_hash(with_titles=true)
|
39
|
+
tmp = {}
|
40
|
+
|
41
|
+
field_names.each do |fname|
|
42
|
+
tmp[fname] = self.send(fname.to_s)
|
43
|
+
end
|
44
|
+
|
45
|
+
tmp
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_yaml(with_titles=true)
|
49
|
+
require 'yaml'
|
50
|
+
to_hash.to_yaml
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_delimited(with_titles=false, delim=',')
|
54
|
+
values = []
|
55
|
+
field_names.each do |fname|
|
56
|
+
values << self.send(fname.to_s) # TODO: escape values
|
57
|
+
end
|
58
|
+
output = values.join(delim)
|
59
|
+
output = field_names.join(delim) << $/ << output if with_titles
|
60
|
+
output
|
61
|
+
end
|
62
|
+
def to_tsv(with_titles=false)
|
63
|
+
to_delimited(with_titles, "\t")
|
64
|
+
end
|
65
|
+
def to_csv(with_titles=false)
|
66
|
+
to_delimited(with_titles, ',')
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def self.from_delimited(from=[],delim=',')
|
71
|
+
return if from.empty?
|
72
|
+
# We grab an instance of the class so we can
|
73
|
+
hash = {}
|
74
|
+
|
75
|
+
fnames = values = []
|
76
|
+
if (from.size > 1 && !from[1].empty?)
|
77
|
+
fnames = from[0].chomp.split(delim)
|
78
|
+
values = from[1].chomp.split(delim)
|
79
|
+
else
|
80
|
+
fnames = self.new.field_names
|
81
|
+
values = from[0].chomp.split(delim)
|
82
|
+
end
|
83
|
+
|
84
|
+
fnames.each_with_index do |key,index|
|
85
|
+
next unless values[index]
|
86
|
+
number_or_string = (values[index].match(/[\d\.]+/)) ? values[index].to_f : values[index]
|
87
|
+
hash[key.to_sym] = number_or_string
|
88
|
+
end
|
89
|
+
hash
|
90
|
+
end
|
91
|
+
def self.from_tsv(from=[])
|
92
|
+
self.from_delimited(from, "\t")
|
93
|
+
end
|
94
|
+
def self.from_csv(from=[])
|
95
|
+
self.from_delimited(from, ',')
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.from_hash(from={})
|
99
|
+
return if from.empty?
|
100
|
+
me = self.new
|
101
|
+
fnames = me.to_hash.keys
|
102
|
+
fnames.each do |key|
|
103
|
+
# NOTE: this will skip generated values b/c they don't have a setter method
|
104
|
+
me.send("#{key}=", from[key]) if self.method_defined?("#{key}=")
|
105
|
+
end
|
106
|
+
me
|
107
|
+
end
|
108
|
+
def self.from_yaml(from=[])
|
109
|
+
require 'yaml'
|
110
|
+
# from is an array of strings
|
111
|
+
from_str = from.join('')
|
112
|
+
YAML::load(from_str)
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module Stella
|
5
|
+
|
6
|
+
class InvalidArgument < RuntimeError
|
7
|
+
attr_accessor :name
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
def message
|
12
|
+
Stella::TEXT.err(:error_invalid_argument, @name)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class UnavailableAdapter < RuntimeError
|
17
|
+
attr_accessor :name
|
18
|
+
def initialize(name)
|
19
|
+
@name = name
|
20
|
+
end
|
21
|
+
def message
|
22
|
+
Stella::TEXT.err(:error_unavailable_adapter, @name)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class UnknownValue < RuntimeError
|
27
|
+
attr_accessor :value
|
28
|
+
def initialize(value)
|
29
|
+
@value = value.to_s
|
30
|
+
end
|
31
|
+
def message
|
32
|
+
Stella::TEXT.err(:error_unknown_value, @value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class UnsupportedLanguage < RuntimeError
|
37
|
+
end
|
38
|
+
|
39
|
+
class Util
|
40
|
+
|
41
|
+
# process_useragents
|
42
|
+
#
|
43
|
+
# We read the useragents.txt file into a hash index which
|
44
|
+
# useful for refering to specific useragents on the command-line
|
45
|
+
# and other places.
|
46
|
+
#
|
47
|
+
# Examples:
|
48
|
+
# --agent=ie-5
|
49
|
+
# --agent=ff-2.0.0.2-linux
|
50
|
+
# --agent=chrome-windows
|
51
|
+
# --agent=safari-3.0-osx
|
52
|
+
#
|
53
|
+
def self.process_useragents(ua_strs=[])
|
54
|
+
agents_index = {}
|
55
|
+
return agents_index if ua_strs.empty?
|
56
|
+
|
57
|
+
ua_strs.each do |ua_str|
|
58
|
+
ua_str.chomp! # remove trailing line separator
|
59
|
+
|
60
|
+
ua = UserAgent.parse(ua_str)
|
61
|
+
|
62
|
+
# Standardize the index values
|
63
|
+
# i.e. firefox-3-windows
|
64
|
+
name = ua.browser.downcase.tr(' ', '')
|
65
|
+
version = ua.version.to_i
|
66
|
+
os = ua.platform.downcase.tr(' ', '')
|
67
|
+
|
68
|
+
# Non-windows operating systems have the OS string inside of "os"
|
69
|
+
# rather than "platform". We look there for the value and then
|
70
|
+
# standardize the values.
|
71
|
+
# i.e. firefox-3-osx
|
72
|
+
if os != 'windows'
|
73
|
+
os = ua.os.downcase
|
74
|
+
os = 'linux' if os.match(/^linux/)
|
75
|
+
os = 'osx' if os.match(/mac os x/)
|
76
|
+
os = 'bsd' if os.match(/bsd/)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Make sure all arrays exist before we populate them
|
80
|
+
agents_index[name] ||= []
|
81
|
+
agents_index["#{name}-#{version}"] ||= []
|
82
|
+
agents_index["#{name}-#{version}-#{os}"] ||= []
|
83
|
+
agents_index["#{name}-#{os}"] ||= [] # We use this one for failover
|
84
|
+
|
85
|
+
# Populate each list.
|
86
|
+
agents_index[name] << ua
|
87
|
+
agents_index["#{name}-#{version}"] << ua
|
88
|
+
agents_index["#{name}-#{version}-#{os}"] << ua
|
89
|
+
agents_index["#{name}-#{os}"] << ua
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
agents_index
|
94
|
+
end
|
95
|
+
|
96
|
+
# expand_str
|
97
|
+
#
|
98
|
+
# Turns a string like ff-4-freebsd into ["ff","4","freebsd"]
|
99
|
+
# We use this for command-line values liek agent and rampup
|
100
|
+
def self.expand_str(str)
|
101
|
+
str.split(/\s*[,\-]\s*/) # remove extra spaces at the same time.
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
module Stella::Test
|
3
|
+
|
4
|
+
module Base
|
5
|
+
|
6
|
+
attr_reader :message
|
7
|
+
attr_reader :elapsed_time_avg, :transaction_rate_avg, :vusers_avg, :response_time_avg
|
8
|
+
attr_reader :elapsed_time_sdev, :transaction_rate_sdev, :vusers_sdev, :response_time_sdev
|
9
|
+
attr_accessor :transactions_total, :headers_transferred_total, :data_transferred_total
|
10
|
+
attr_accessor :successful_total, :failed_total, :elapsed_time_total, :throughput_avg, :throughput_sdev
|
11
|
+
|
12
|
+
def availability
|
13
|
+
return 0 if @successful_total == 0
|
14
|
+
(@transactions_total / @successful_total).to_f * 100
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
# Defines the fields the are output by to_hash and to_csv.
|
19
|
+
# For to_csv, this also determines the field order
|
20
|
+
def field_names
|
21
|
+
[
|
22
|
+
:message,
|
23
|
+
:elapsed_time_avg, :transaction_rate_avg, :vusers_avg, :response_time_avg,
|
24
|
+
:elapsed_time_sdev, :transaction_rate_sdev, :vusers_sdev, :response_time_sdev,
|
25
|
+
|
26
|
+
:transactions_total, :successful_total, :failed_total,
|
27
|
+
:data_transferred_total, :headers_transferred_total,
|
28
|
+
|
29
|
+
:elapsed_time_total, :availability, :throughput_avg, :throughput_sdev
|
30
|
+
]
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Stella
|
4
|
+
|
5
|
+
|
6
|
+
module Test
|
7
|
+
|
8
|
+
# Stella::Test::Definition
|
9
|
+
#
|
10
|
+
# This class defines the properties of load test. These are "generic" properties
|
11
|
+
# in that they don't relate to a specific tool.
|
12
|
+
class Definition
|
13
|
+
|
14
|
+
# Stella::Test::Definition::Rampup
|
15
|
+
#
|
16
|
+
# This class holds the values for a rampup: interval and ceiling.
|
17
|
+
class Rampup
|
18
|
+
attr_accessor :interval
|
19
|
+
attr_accessor :ceiling
|
20
|
+
def initialize(interval, ceiling)
|
21
|
+
@interval = interval
|
22
|
+
@ceiling = ceiling
|
23
|
+
end
|
24
|
+
def to_s
|
25
|
+
to_a.join(',')
|
26
|
+
end
|
27
|
+
def to_a
|
28
|
+
[interval,ceiling]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Number of virtual users to create or to begin with if rampup is specific.
|
33
|
+
attr_accessor :vusers
|
34
|
+
# The total number of requests per test
|
35
|
+
attr_accessor :requests
|
36
|
+
# The number of requests per virtual user
|
37
|
+
attr_accessor :request_ratio
|
38
|
+
|
39
|
+
# Number of times to repeat the test run. Integer.
|
40
|
+
attr_reader :repetitions
|
41
|
+
# The amount of time to pause between test runs
|
42
|
+
attr_accessor :sleep
|
43
|
+
# Warmup factor (0.1 - 1) for a single test run before the actual test.
|
44
|
+
# A warmup factor of 0.5 means run a test run at 50% strength.
|
45
|
+
attr_accessor :warmup
|
46
|
+
# Contains an interval and maximum threshold to increase virtual users.
|
47
|
+
# Rampup object, [R,M] where R is the interval and M is the maximum.
|
48
|
+
attr_accessor :rampup
|
49
|
+
# An array of string appropriate for a User-Agent HTTP header
|
50
|
+
attr_accessor :agents
|
51
|
+
# A short reminder to yourself what you're testing
|
52
|
+
attr_accessor :message
|
53
|
+
|
54
|
+
def initialize
|
55
|
+
@repetitions = 3
|
56
|
+
end
|
57
|
+
|
58
|
+
def repetitions=(v)
|
59
|
+
return unless v && v > 0
|
60
|
+
@repetitions = v
|
61
|
+
end
|
62
|
+
|
63
|
+
def rampup=(v)
|
64
|
+
return unless v
|
65
|
+
|
66
|
+
if (v.is_a? Rampup)
|
67
|
+
@rampup = v
|
68
|
+
elsif (v.is_a?(Array) && v.size == 2)
|
69
|
+
@rampup = Rampup.new(v[0], v[1])
|
70
|
+
else
|
71
|
+
raise UnknownValue.new(v.to_s)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|