stella 0.8.2.003 → 0.8.3.001

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.txt CHANGED
@@ -11,6 +11,25 @@ STELLA, CHANGES
11
11
  * TODO: process templates for calls to set in get blocks
12
12
 
13
13
 
14
+ #### 0.8.3 (2010-03-15) ###############################
15
+
16
+ * FIXED: Testrun digest handling
17
+ * FIXED: Testrun.id will be converted to a Gibbler::Digest if it's a String.
18
+ * FIXED: Reconstituting Testrun from a hash (samples and stats)
19
+ * CHANGE: Removed dashes from CLI params: notemplate, nostats, nowait, withheader, withparam
20
+ * CHANGE: Engine is now called via Testrun#run
21
+ * CHANGE: Use the ID attribute (e.g. Testplan.id) instead of digest or digest_cache (which only
22
+ be used once to generate an ID
23
+ * ADDED: Testrun#client_options
24
+ * ADDED: Reconstituting Stella::Testrun and Stella::Testplan objects from JSON/Hash
25
+ * ADDED: Testrun objects are now frozen after the run
26
+ * ADDED: Testrun#userid, Testrun#runinfo
27
+ * ADDED: Default Testrun status is "new"
28
+ * ADDED: Testrun#run now accepts an options hash which is passed to Engine#run
29
+ * ADDED: Engine option: with_content (populate Testrun#log during a load test)
30
+ * ADDED: First batch of requests store header content (for load tests)
31
+
32
+
14
33
  #### 0.8.2 (2010-03-05) ###############################
15
34
 
16
35
  * FIXED: Misc issues with usage via module (Stella::Engine.load)
data/Rakefile CHANGED
@@ -27,9 +27,9 @@ begin
27
27
  gem.authors = ["Delano Mandelbaum"]
28
28
  gem.add_dependency("gibbler", ">= 0.7.4")
29
29
  gem.add_dependency("drydock", ">= 0.6.9")
30
- gem.add_dependency("benelux", ">= 0.5.8")
30
+ gem.add_dependency("benelux", ">= 0.5.11")
31
31
  gem.add_dependency('sysinfo', '>= 0.7.3')
32
- gem.add_dependency('storable', '>= 0.6.3')
32
+ gem.add_dependency('storable', '>= 0.6.4')
33
33
  gem.add_dependency("nokogiri")
34
34
 
35
35
  #gem.add_development_dependency("rspec", ">= 1.2.9")
data/VERSION.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
2
  :MAJOR: 0
3
3
  :MINOR: 8
4
- :PATCH: 2
5
- :BUILD: '003'
4
+ :PATCH: 3
5
+ :BUILD: '001'
data/bin/stella CHANGED
@@ -43,9 +43,8 @@ class Stella::CLI::Definition
43
43
  Stella.enable_debug
44
44
  end
45
45
  global :W, :wait, Integer, "Seconds to wait before starting test"
46
- global :'with-header', "Include X-Stella-ID request header"
47
- global :'with-param', "Include __stella query parameter header"
48
- global :R, :remote
46
+ global :H, :withheader, "Include X-Stella-ID request header"
47
+ global :P, :withparam, "Include __stella query parameter header"
49
48
  global :o, :output, String, "Write output to the given file" do |v|
50
49
  String.disable_color
51
50
  Stella.log.output = v
@@ -61,10 +60,10 @@ class Stella::CLI::Definition
61
60
  global :v, :verbose, "Increase verbosity of output (e.g. -v or -vv or -vvv)" do
62
61
  Stella.stdout.lev += 1
63
62
  end
64
- global :'no-stats', "Disable stat collection" do
63
+ global :nostats, "Disable stat collection" do
65
64
  true
66
65
  end
67
- global :'no-templates', "Disable template parsing" do
66
+ global :notemplates, "Disable template parsing" do
68
67
  true
69
68
  end
70
69
  global :V, :version, "Display version number" do
data/lib/proc_source.rb CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  require 'stringio'
2
3
  require 'irb/ruby-lex'
3
4
  #SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
data/lib/stella/cli.rb CHANGED
@@ -14,45 +14,34 @@ class Stella::CLI < Drydock::Command
14
14
  def verify_valid?
15
15
  create_testplan
16
16
  end
17
-
18
- def verify
19
- opts = {}
20
- opts[:hosts] = @hosts
21
- opts[:nowait] = true if @option.nowait
22
-
23
- connect_service if @global.remote
24
-
25
- [:'no-templates', :'no-stats', :'with-header', :'with-param'].each do |opt|
26
- opts[opt] = @global.send(opt) unless @global.send(opt).nil?
27
- end
28
-
29
- engine = Stella::Engine::Functional.new opts
30
- testrun = engine.run @testplan
31
- @exit_code = (testrun.stats[:summary][:failed].n == 0 ? 0 : 1)
32
- end
33
-
17
+
34
18
  def generate_valid?
35
19
  create_testplan
36
20
  end
37
21
 
38
- def generate
39
- opts = {}
22
+ def run(mode)
23
+ opts = { :mode => mode }
40
24
  opts[:hosts] = @hosts
41
25
  [:nowait, :clients, :repetitions, :duration, :arrival].each do |opt|
42
26
  opts[opt] = @option.send(opt) unless @option.send(opt).nil?
43
27
  end
44
-
45
- [:'no-templates', :'no-stats', :'no-header', :'no-param'].each do |opt|
28
+ [:'notemplates', :'nostats', :'noheader', :'noparam'].each do |opt|
46
29
  opts[opt] = @global.send(opt) unless @global.send(opt).nil?
47
30
  end
48
-
49
- connect_service if @global.remote
50
-
51
- engine = Stella::Engine::Load.new opts
52
- testrun = engine.run @testplan
53
- @exit_code = (testrun.stats[:summary][:failed].n == 0 ? 0 : 1)
31
+ testrun = Stella::Testrun.new @testplan, opts
32
+ testrun.run
33
+ testrun
54
34
  end
55
35
 
36
+ def generate
37
+ testrun = run :generate
38
+ @exit_code = (testrun.stats['summary']['failed'].n == 0 ? 0 : 1)
39
+ end
40
+ def verify
41
+ testrun = run :verify
42
+ @exit_code = (testrun.stats['summary']['failed'].n == 0 ? 0 : 1)
43
+ end
44
+
56
45
  def example
57
46
  base_path = File.expand_path(File.join(STELLA_LIB_HOME, '..'))
58
47
  thin_path = File.join(base_path, 'support', 'sample_webapp', 'config.ru')
@@ -88,27 +77,21 @@ class Stella::CLI < Drydock::Command
88
77
  end
89
78
 
90
79
  private
91
- def connect_service
92
- if @global.remote
93
- s = Stella::Service.new
94
- Stella::Engine.service = s
95
- end
96
- end
97
80
 
98
81
  def create_testplan
99
82
  unless @option.testplan.nil? || File.exists?(@option.testplan)
100
83
  raise Stella::InvalidOption, "Bad path: #{@option.testplan}"
101
84
  end
102
- @hosts = @argv.collect { |uri|;
103
- uri = 'http://' << uri unless uri.match /^https?:\/\//i
104
- URI.parse uri;
105
- }
106
85
  (@global.var || []).each do |var|
107
86
  n, v = *var.split('=')
108
87
  raise "Bad variable format: #{var}" if n.nil? || !n.match(/[a-z]+/i)
109
88
  Stella::Testplan.global(n.to_sym, v)
110
89
  end
111
90
  if @option.testplan
91
+ @hosts = @argv.collect { |uri|;
92
+ uri = 'http://' << uri unless uri.match /^https?:\/\//i
93
+ URI.parse uri;
94
+ }
112
95
  @testplan = Stella::Testplan.load_file @option.testplan
113
96
  else
114
97
  opts = {}
data/lib/stella/client.rb CHANGED
@@ -19,25 +19,33 @@ module Stella
19
19
  attr_accessor :created
20
20
 
21
21
  gibbler :opts, :index, :base_uri, :proxy, :nowait, :created
22
-
23
- def initialize(base_uri=nil, index=1, opts={})
22
+ @@client_index = 0
23
+ def initialize(base_uri=nil, opts={})
24
+ @index = @@client_index += 1
24
25
  @created = Time.now.to_f
25
26
  opts = {
26
- :'no-templates' => false
27
+ :nowait => false,
28
+ :withparam => false,
29
+ :withheader => false,
30
+ :notemplates => false,
31
+ :wait => 0
27
32
  }.merge! opts
28
33
  @opts = opts
29
34
  @base_uri, @index = base_uri, index
30
35
  #@cookie_file = File.new("cookies-#{index}", 'w')
31
36
  @proxy = OpenStruct.new
32
37
  end
38
+ def id
39
+ @id || self.digest
40
+ end
33
41
  def execute(usecase, &stat_collector)
34
42
  # Gibbler.enable_debug
35
43
  # We need to make sure the digest cache has a value
36
- self.digest if self.digest_cache.nil?
44
+ @id = self.digest if self.digest_cache.nil?
37
45
  Gibbler.disable_debug
38
46
  http_client = create_http_client
39
47
  stats = {}
40
- container = Container.new(self.digest_cache, usecase)
48
+ container = Container.new(self.id, usecase)
41
49
  counter = 0
42
50
  usecase.requests.each do |req|
43
51
  counter += 1
@@ -58,10 +66,11 @@ module Stella
58
66
  container.params, container.headers = params, headers
59
67
 
60
68
  uri = build_request_uri req.uri, params, container
61
-
69
+
62
70
  if http_auth = req.http_auth || usecase.http_auth
63
71
  # TODO: The first arg is domain and can include a URI path.
64
72
  # Are there cases where this is important?
73
+
65
74
  domain = http_auth.domain
66
75
  # When req.uri is a fully qualified URI, domain will be
67
76
  # set to an incorrect value like, http://domain1/http://domain2.
@@ -83,16 +92,16 @@ module Stella
83
92
  Stella.ld "TIMEOUT " << http_client.receive_timeout.to_s
84
93
 
85
94
  raise NoHostDefined, req.uri if uri.host.nil? || uri.host.empty?
86
- stella_id = [Time.now.to_f, self.digest_cache, req.digest_cache, params, headers, counter].digest
95
+ stella_id = [Time.now.to_f, self.id, req.id, params, headers, counter].digest
87
96
 
88
- Benelux.add_thread_tags :request => req.digest_cache
97
+ Benelux.add_thread_tags :request => req.id
89
98
  Benelux.add_thread_tags :retry => counter
90
99
  Benelux.add_thread_tags :stella_id => stella_id
91
100
 
92
101
  container.unique_id = stella_id
93
102
 
94
- params['__stella'] = container.unique_id.short if @opts[:'with-param']
95
- headers['X-Stella-ID'] = container.unique_id.short if @opts[:'with-header']
103
+ params['__stella'] = container.unique_id.short if @opts[:'withparam']
104
+ headers['X-Stella-ID'] = container.unique_id.short if @opts[:'withheader']
96
105
 
97
106
  meth = req.http_method.to_s.downcase
98
107
  Stella.ld "#{req.http_method}: " << "#{req.uri} " << params.inspect
@@ -104,9 +113,14 @@ module Stella
104
113
  break
105
114
  end
106
115
 
116
+
117
+
107
118
  begin
119
+
108
120
  send_request http_client, usecase, meth, uri, req, params, headers, container, counter
121
+
109
122
  update(:receive_response, usecase, uri, req, params, headers, counter, container)
123
+
110
124
  Benelux.add_thread_tags :status => container.status
111
125
  res = container.response
112
126
  [
@@ -117,6 +131,8 @@ module Stella
117
131
  ].each do |att|
118
132
  Benelux.thread_timeline.add_count att[0], att[1]
119
133
  end
134
+
135
+
120
136
  ret = execute_response_handler container, req
121
137
 
122
138
  asset_start = Time.now
@@ -138,7 +154,8 @@ module Stella
138
154
  next
139
155
  end
140
156
 
141
- run_sleeper(req.wait, asset_duration) if req.wait != 0 && !nowait?
157
+ wait = (req.wait > 0) ? req.wait : @opts[:wait]
158
+ run_sleeper(wait, asset_duration) if (wait > 0) && !nowait?
142
159
 
143
160
  # TODO: consider throw/catch
144
161
  case ret.class.to_s
@@ -175,9 +192,9 @@ module Stella
175
192
  stats
176
193
  end
177
194
 
178
- def enable_nowait_mode; @nowait = true; end
179
- def disable_nowait_mode; @nowait = false; end
180
- def nowait?; @nowait == true; end
195
+ def enable_nowait_mode; @opts[:'nowait'] = true; end
196
+ def disable_nowait_mode; @opts[:'nowait'] = false; end
197
+ def nowait?; @opts[:'nowait'] == true; end
181
198
 
182
199
  private
183
200
  # We use a method so we can time it with Benelux
@@ -191,7 +208,7 @@ module Stella
191
208
  end
192
209
 
193
210
  def update(kind, *args)
194
- changed and notify_observers(kind, self.digest_cache, *args)
211
+ changed and notify_observers(kind, self.id, *args)
195
212
  end
196
213
 
197
214
  def run_sleeper(wait, already_waited=0)
@@ -233,7 +250,7 @@ module Stella
233
250
  newh = {}
234
251
  #Stella.ld "PREPARE HEADERS: #{headers}"
235
252
  hashobj.each_pair do |n,v|
236
- unless @opts[:'no-templates']
253
+ unless @opts[:'notemplates']
237
254
  v = container.parse_template v
238
255
  end
239
256
  v = extra.call(v) unless extra.nil?
@@ -270,13 +287,14 @@ module Stella
270
287
  raise NoHostDefined, uri
271
288
  end
272
289
 
290
+ base_uri = URI.parse(self.base_uri || '')
273
291
  uri.scheme = base_uri.scheme if uri.scheme.nil?
274
292
  uri.host = base_uri.host if uri.host.nil?
275
293
  uri.port = base_uri.port if uri.port.nil?
276
294
 
277
295
  # Support for specifying default path prefix:
278
296
  # $ stella verify -p plan.rb http://localhost/basicauth
279
- if base_uri.path
297
+ if URI::Generic === base_uri && base_uri.path
280
298
  if uri.path.nil? || uri.path.empty?
281
299
  uri.path = base_uri.path
282
300
  else
data/lib/stella/common.rb CHANGED
@@ -3,6 +3,79 @@
3
3
  $KCODE = "u" if RUBY_VERSION =~ /^1.8/
4
4
 
5
5
 
6
+
7
+ # A hash with indifferent access and magic predicates.
8
+ #
9
+ # hash = Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true
10
+ #
11
+ # hash[:foo] #=> 'bar'
12
+ # hash['foo'] #=> 'bar'
13
+ # hash.foo? #=> true
14
+ #
15
+ class HashWithIndifferentAccess < ::Hash #:nodoc:
16
+
17
+ def initialize(hash={})
18
+ super()
19
+ hash.each do |key, value|
20
+ self[convert_key(key)] = value
21
+ end
22
+ end
23
+
24
+ def [](key)
25
+ super(convert_key(key))
26
+ end
27
+
28
+ def []=(key, value)
29
+ super(convert_key(key), value)
30
+ end
31
+
32
+ def delete(key)
33
+ super(convert_key(key))
34
+ end
35
+
36
+ def values_at(*indices)
37
+ indices.collect { |key| self[convert_key(key)] }
38
+ end
39
+
40
+ def merge(other)
41
+ dup.merge!(other)
42
+ end
43
+
44
+ def merge!(other)
45
+ other.each do |key, value|
46
+ self[convert_key(key)] = value
47
+ end
48
+ self
49
+ end
50
+
51
+ protected
52
+
53
+ def convert_key(key)
54
+ key.is_a?(Symbol) ? key.to_s : key
55
+ end
56
+
57
+ # Magic predicates. For instance:
58
+ #
59
+ # options.force? # => !!options['force']
60
+ # options.shebang # => "/usr/lib/local/ruby"
61
+ # options.test_framework?(:rspec) # => options[:test_framework] == :rspec
62
+ #
63
+ def method_missing(method, *args, &block)
64
+ method = method.to_s
65
+ if method =~ /^(\w+)\?$/
66
+ if args.empty?
67
+ !!self[$1]
68
+ else
69
+ self[$1] == args.first
70
+ end
71
+ else
72
+ self[method]
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+
6
79
  # Assumes Time::Units and Numeric mixins are available.
7
80
 
8
81
  class String
@@ -235,11 +308,7 @@ class Stella::Config < Storable
235
308
  USER_PATH = File.join(Stella.sysinfo.home, DIR_NAME, 'config')
236
309
  end
237
310
  PROJECT_PATH = Stella::Config.find_project_config
238
- DEFAULT_CONFIG = <<CONF
239
- apikey: ''
240
- secret: ''
241
- source: ''
242
- CONF
311
+ DEFAULT_CONFIG = ""
243
312
  DEFAULT_CONFIG_HASH = YAML.load(DEFAULT_CONFIG).gibbler
244
313
  end
245
314
 
@@ -5,7 +5,16 @@ module Stella::Data::HTTP
5
5
  include Gibbler::Complex
6
6
  include Stella::Data::Helpers
7
7
 
8
- field :id, &gibbler_id_processor
8
+ field :id => String, &gibbler_id_processor
9
+
10
+ # A hash containing blocks to be executed depending on the HTTP response status.
11
+ # The hash keys are numeric HTTP Status Codes.
12
+ #
13
+ # 200 => { ... }
14
+ # 304 => { ... }
15
+ # 500 => { ... }
16
+ #
17
+ field :response_handler => Hash, &hash_proc_processor
9
18
 
10
19
  # Store the description in the attic so
11
20
  # it's not used in the gibbler digest.
@@ -24,21 +33,17 @@ module Stella::Data::HTTP
24
33
  field :timeout
25
34
 
26
35
  field :autofollow # boolean. Was this an auto generated follow request.
27
-
28
- # A hash containing blocks to be executed depending on the HTTP response status.
29
- # The hash keys are numeric HTTP Status Codes.
30
- #
31
- # 200 => { ... }
32
- # 304 => { ... }
33
- # 500 => { ... }
34
- #
35
- field :response_handler, &hash_proc_processor
36
+
36
37
 
37
38
  def has_body?
38
39
  !@body.nil?
39
40
  end
40
41
 
41
- def initialize (method, uri_str, version="1.1", &definition)
42
+ def id
43
+ Gibbler::Digest.new @id || self.digest
44
+ end
45
+
46
+ def initialize (method=nil, uri_str=nil, version="1.1", &definition)
42
47
  @uri = uri_str.to_s
43
48
  @http_method, @http_version = method, version
44
49
  @headers, @params, @response_handler = {}, {}, {}
@@ -53,6 +58,16 @@ module Stella::Data::HTTP
53
58
  @autofollow = true
54
59
  end
55
60
 
61
+ def self.from_hash(hash={})
62
+ me = super(hash)
63
+ me.response_handler.keys.each do |status|
64
+ proc_str = me.response_handler[status]
65
+ me.response_handler[status] = eval "Proc.new #{proc_str}"
66
+ me.response_handler[status].source = proc_str
67
+ end
68
+ me
69
+ end
70
+
56
71
  def auth(user=nil, pass=nil, domain=nil)
57
72
  @http_auth ||= Stella::Testplan::Usecase::Auth.new
58
73
  @http_auth.user, @http_auth.pass, @http_auth.domain = user, pass, domain
@@ -143,6 +158,7 @@ module Stella::Data::HTTP
143
158
  end
144
159
 
145
160
  def freeze
161
+ return if frozen?
146
162
  @params = convert_values_to_templates @params
147
163
  @headers = convert_values_to_templates @headers
148
164
  super
@@ -2,27 +2,12 @@
2
2
  module Stella::Engine
3
3
  class Functional < Stella::Engine::Base
4
4
 
5
- def run(plan)
6
- opts = process_options! plan, @opts
7
-
8
- Stella.stdout.info2 "Hosts: " << opts[:hosts].join(', ') if !opts[:hosts].empty?
9
-
10
- client = Stella::Client.new opts[:hosts].first, 1, opts
5
+ def run(testrun)
6
+ client = Stella::Client.new testrun.hosts.first, testrun.client_options
11
7
  client.add_observer(self)
12
-
13
- client.enable_nowait_mode if opts[:nowait]
14
-
15
- @testrun = Stella::Testrun.new plan, [:response_time, :failed], opts
16
- @testrun.mode = 'f'
17
-
18
- if Stella::Engine.service
19
- Stella::Engine.service.testplan_sync plan
20
- Stella::Engine.service.testrun_create @testrun
21
- Stella::Engine.service.client_create client.digest, :index => client.index
22
- Stella.stdout.info "Testrun: #{@testrun.remote_digest}"
23
- end
24
8
 
25
9
  Stella.stdout.info2 $/, "Starting test...", $/
10
+ testrun.start_time = Time.now.utc.to_i
26
11
 
27
12
  start_time = Time.now.utc
28
13
 
@@ -30,12 +15,12 @@ module Stella::Engine
30
15
  # Identify this thread to Benelux
31
16
  Benelux.current_track :functional
32
17
 
33
- dig = Stella.stdout.lev > 1 ? plan.digest_cache : plan.digest_cache.shorter
34
- Stella.stdout.info " %-65s ".att(:reverse) % ["#{plan.desc} (#{dig})"]
35
- plan.usecases.each_with_index do |uc,i|
18
+ dig = Stella.stdout.lev > 1 ? testrun.plan.id : testrun.plan.id.shorter
19
+ Stella.stdout.info " %-65s ".att(:reverse) % ["#{testrun.plan.desc} (#{dig})"]
20
+ testrun.plan.usecases.each_with_index do |uc,i|
36
21
  desc = (uc.desc || "Usecase ##{i+1}")
37
- Benelux.add_thread_tags :usecase => uc.digest_cache
38
- dig = Stella.stdout.lev > 1 ? uc.digest_cache : uc.digest_cache.shorter
22
+ Benelux.add_thread_tags :usecase => uc.id
23
+ dig = Stella.stdout.lev > 1 ? uc.id : uc.id.shorter
39
24
  Stella.stdout.info ' %-65s '.att(:reverse).bright % ["#{desc} (#{dig}) "]
40
25
  Stella.rescue { client.execute uc }
41
26
  end
@@ -45,25 +30,17 @@ module Stella::Engine
45
30
 
46
31
  test_time = Time.now.utc - start_time
47
32
 
48
- #Benelux.update_global_timeline
49
-
50
33
  # Need to use thread timeline b/c the clients are running in the
51
34
  # main thread which Benelux.update_global_timeline does not touch.
52
35
  tt = thread.timeline
53
36
 
54
- if Stella::Engine.service
55
- data = tt.messages.filter(:kind => :log).to_json
56
- Stella::Engine.service.client_log client.digest, data
57
- @testrun.add_sample 1, 1, tt
58
- end
59
-
60
- @testrun
37
+ testrun
61
38
  end
62
39
 
63
40
 
64
41
  def update_prepare_request(client_id, usecase, req, counter)
65
42
  notice = "repeat: #{counter-1}" if counter > 1
66
- dig = Stella.stdout.lev > 1 ? req.digest_cache : req.digest_cache.shorter
43
+ dig = Stella.stdout.lev > 1 ? req.id : req.id.shorter
67
44
  desc = "#{req.desc} (#{dig}) "
68
45
  Stella.stdout.info2 " %-46s %16s ".bright % [desc, notice]
69
46
  end
@@ -72,7 +49,7 @@ module Stella::Engine
72
49
  def update_receive_response(client_id, usecase, uri, req, params, headers, counter, container)
73
50
  log = Stella::Engine::Log.new Time.now.to_f, container.unique_id, client_id,
74
51
  'testplanid',
75
- usecase.digest, req.digest,
52
+ usecase.id, req.id,
76
53
  req.http_method, container.status, uri,
77
54
  params, container.response.request.header.dump,
78
55
  container.response.header.dump,
@@ -175,12 +152,3 @@ module Stella::Engine
175
152
 
176
153
  end
177
154
  end
178
-
179
- __END__
180
-
181
-
182
- $ stella verify -p examples/basic/plan.rb http://localhost:3114
183
- $ stella load -p examples/basic/plan.rb http://localhost:3114
184
- $ stella remote-load -p examples/basic/plan.rb http://localhost:3114
185
- $ stella remote-verify -p examples/basic/plan.rb http://localhost:3114
186
-