stella 0.8.0.001 → 0.8.1.001

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.txt CHANGED
@@ -5,11 +5,23 @@ STELLA, CHANGES
5
5
  * TODO: Force response block content type.
6
6
  * TODO: Add assert methods to response blocks (reconsider)
7
7
  * TODO: Add option to dump full request/response output to a file by client
8
- * TODO: review cooking handling. Not always sent automatically.
8
+ * TODO: review cookie handling. Not always sent automatically.
9
9
  * TODO: review :variables in URI elements
10
- * TODO: Stella::Testrun (no local analog for runid so it's set by the service)
11
10
  * TODO: global responses
12
11
  * TODO: request block conditions.
12
+ * TODO: process templates for calls to set in get blocks
13
+
14
+
15
+ #### 0.8.1 (2010-02-10) ###############################
16
+
17
+ * FIXED: Handling of non ASCII characters with Nokogiri parsing in Ruby 1.9
18
+ * FIXED: warning in Ruby 1.9
19
+ * FIXED: Setting of domain for HTTP authentication when the request URI
20
+ is a fully qualified URI.
21
+ * CHANGE: Don't attempt to convert parameters or headers to ERB templates.
22
+ * CHANGE: Randomize order of usecase when generating load
23
+ * CHANGE: noparams and noheader now default to off. Must use with-param and with-header.
24
+ * ADDED: Stella.get
13
25
 
14
26
 
15
27
  #### 0.8.0 (2010-01-16) ###############################
data/bin/stella CHANGED
@@ -43,12 +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 :'no-header', "Do not include X-Stella-ID request header" do
47
- true # params starting with 'no-' return false??
48
- end
49
- global :'no-param', "Do not include __stella query parameter header" do
50
- true
51
- end
46
+ global :'with-header', "Include X-Stella-ID request header"
47
+ global :'with-param', "Include __stella query parameter header"
52
48
  global :R, :remote
53
49
  global :E, :engine, String, "Specify a load engine (experimental)"
54
50
  global :o, :output, String, "Write output to the given file" do |v|
@@ -156,7 +156,7 @@ usecase 65, "Simple search" do
156
156
  set :lid, listing['id'].match(/(\d+)/)[0]
157
157
  end
158
158
  response 404 do
159
- fail "No results"
159
+ quit "No results"
160
160
  end
161
161
  end
162
162
 
@@ -28,6 +28,9 @@ usecase "Form Example" do
28
28
  get "$uri/search" do
29
29
  param :what => resource(:globalvar)
30
30
  response do
31
+ if resource(:globalvar).nil?
32
+ abort "Usage: stella --var globalvar=smoked verify -p examples/variables/plan.rb"
33
+ end
31
34
  puts " Global variable: " << resource(:globalvar)
32
35
  puts " Usecase variable: " << resource(:uri)
33
36
  puts " Usecase copy of global: " << resource(:apple)
data/lib/stella/cli.rb CHANGED
@@ -20,13 +20,9 @@ class Stella::CLI < Drydock::Command
20
20
  opts[:hosts] = @hosts
21
21
  opts[:nowait] = true if @option.nowait
22
22
 
23
- if @global.remote
24
- require 'bone'
25
- s = Stella::Service.new Bone['STELLA_SOURCE'], Bone['STELLA_TOKEN']
26
- Stella::Engine.service = s
27
- end
23
+ connect_service if @global.remote
28
24
 
29
- [:'no-templates', :'no-stats', :'no-header', :'no-param'].each do |opt|
25
+ [:'no-templates', :'no-stats', :'with-header', :'with-param'].each do |opt|
30
26
  opts[opt] = @global.send(opt) unless @global.send(opt).nil?
31
27
  end
32
28
 
@@ -49,6 +45,8 @@ class Stella::CLI < Drydock::Command
49
45
  opts[opt] = @global.send(opt) unless @global.send(opt).nil?
50
46
  end
51
47
 
48
+ connect_service if @global.remote
49
+
52
50
  case @global.engine
53
51
  when "package"
54
52
  ret = Stella::Engine::LoadPackage.run @testplan, opts
@@ -99,6 +97,13 @@ class Stella::CLI < Drydock::Command
99
97
  end
100
98
 
101
99
  private
100
+ def connect_service
101
+ if @global.remote
102
+ s = Stella::Service.new
103
+ Stella::Engine.service = s
104
+ end
105
+ end
106
+
102
107
  def create_testplan
103
108
  unless @option.testplan.nil? || File.exists?(@option.testplan)
104
109
  raise Stella::InvalidOption, "Bad path: #{@option.testplan}"
@@ -95,18 +95,19 @@ class Stella::Client
95
95
  def doc
96
96
  return @doc unless @doc.nil?
97
97
  return nil if body.nil? || body.empty?
98
+ str = RUBY_VERSION >= "1.9.0" ? body.force_encoding("UTF-8") : body
98
99
  # NOTE: It's important to parse the document on every
99
100
  # request because this container is available for the
100
101
  # entire life of a usecase.
101
102
  @doc = case (@response.header['Content-Type'] || []).first
102
103
  when /text\/html/
103
- Nokogiri::HTML(body)
104
+ Nokogiri::HTML(str)
104
105
  when /text\/xml/
105
- Nokogiri::XML(body)
106
+ Nokogiri::XML(str)
106
107
  when /text\/yaml/
107
- YAML.load(body)
108
+ YAML.load(str)
108
109
  when /application\/json/
109
- JSON.load(body)
110
+ JSON.load(str)
110
111
  end
111
112
  end
112
113
 
data/lib/stella/client.rb CHANGED
@@ -63,7 +63,11 @@ module Stella
63
63
  # TODO: The first arg is domain and can include a URI path.
64
64
  # Are there cases where this is important?
65
65
  domain = http_auth.domain
66
- domain ||= '%s://%s:%d%s' % [uri.scheme, uri.host, uri.port, req.uri]
66
+ # When req.uri is a fully qualified URI, domain will be
67
+ # set to an incorrect value like, http://domain1/http://domain2.
68
+ # So we parse it and if that fails we'll set it to the value given.
69
+ uri_tmp = URI.parse(req.uri).uri rescue req.uri
70
+ domain ||= '%s://%s:%d%s' % [uri.scheme, uri.host, uri.port, uri_tmp]
67
71
  domain = container.instance_eval &domain if Proc === domain
68
72
  Stella.ld "DOMAIN " << domain
69
73
  user, pass = http_auth.user, http_auth.pass
@@ -87,8 +91,8 @@ module Stella
87
91
 
88
92
  container.unique_id = stella_id
89
93
 
90
- params['__stella'] = container.unique_id unless @opts[:'no-param']
91
- headers['X-Stella-ID'] = container.unique_id unless @opts[:'no-header']
94
+ params['__stella'] = container.unique_id.short if @opts[:'with-param']
95
+ headers['X-Stella-ID'] = container.unique_id.short if @opts[:'with-header']
92
96
 
93
97
  meth = req.http_method.to_s.downcase
94
98
  Stella.ld "#{req.http_method}: " << "#{req.uri} " << params.inspect
data/lib/stella/common.rb CHANGED
@@ -250,15 +250,18 @@ module Stella
250
250
  @force_stop = true
251
251
  @dthread.join
252
252
  @routine.call
253
+ @finally.call unless @finally.nil?
253
254
  end
254
255
  def stop?() @force_stop == true end
255
-
256
+
257
+ def stopped?() @stopped end
258
+
256
259
  # Execute yield every FREQUENCY seconds.
257
260
  def start
258
261
  @dthread ||= Thread.new do
259
262
  prev_ptime = Time.now
260
263
  loop do
261
- break if Stella.abort? || stop?
264
+ @stopped = true and break if Stella.abort? || stop?
262
265
  if (Time.now - prev_ptime).to_i >= @freq
263
266
  @routine.call
264
267
  prev_ptime = Time.now
@@ -268,7 +271,13 @@ module Stella
268
271
  end
269
272
  @dthread.abort_on_exception = true
270
273
  end
274
+
275
+ def finally(&blk)
276
+ @finally = blk
277
+ end
271
278
  end
279
+
280
+
272
281
  end
273
282
 
274
283
 
data/lib/stella/data.rb CHANGED
@@ -49,7 +49,7 @@ module Stella::Data
49
49
  end
50
50
 
51
51
  def to_templ(meth, *args)
52
- Stella::Template.to_templ("#{meth}(#{args_to_str(*args)})")
52
+ "<%= #{meth}(#{args_to_str(*args)}) %>"
53
53
  end
54
54
 
55
55
  end
@@ -15,46 +15,51 @@ module Stella::Engine
15
15
 
16
16
  client.enable_nowait_mode if opts[:nowait]
17
17
 
18
+ @testrun = Stella::Testrun.new plan, [:do_request, :failed], opts
19
+ @testrun.mode = 'f'
20
+
18
21
  if Stella::Engine.service
19
22
  Stella::Engine.service.testplan_sync plan
20
- Stella::Engine.service.testrun_create opts
23
+ Stella::Engine.service.testrun_create @testrun
21
24
  Stella::Engine.service.client_create client.digest, :index => client.index
22
25
  end
23
26
 
24
27
  Stella.stdout.info2 $/, "Starting test...", $/
25
28
 
26
- # Identify this thread to Benelux
27
- Benelux.current_track :functional
28
-
29
29
  start_time = Time.now.utc
30
30
 
31
- dig = Stella.stdout.lev > 1 ? plan.digest_cache : plan.digest_cache.shorter
32
- Stella.stdout.info " %-65s ".att(:reverse) % ["#{plan.desc} (#{dig})"]
33
- plan.usecases.each_with_index do |uc,i|
34
- desc = (uc.desc || "Usecase ##{i+1}")
35
- dig = Stella.stdout.lev > 1 ? uc.digest_cache : uc.digest_cache.shorter
36
- Stella.stdout.info ' %-65s '.att(:reverse).bright % ["#{desc} (#{dig}) "]
37
- Stella.rescue { client.execute uc }
31
+ thread = Thread.new do
32
+ # Identify this thread to Benelux
33
+ Benelux.current_track :functional
34
+
35
+ dig = Stella.stdout.lev > 1 ? plan.digest_cache : plan.digest_cache.shorter
36
+ Stella.stdout.info " %-65s ".att(:reverse) % ["#{plan.desc} (#{dig})"]
37
+ plan.usecases.each_with_index do |uc,i|
38
+ desc = (uc.desc || "Usecase ##{i+1}")
39
+ Benelux.add_thread_tags :usecase => uc.digest_cache
40
+ dig = Stella.stdout.lev > 1 ? uc.digest_cache : uc.digest_cache.shorter
41
+ Stella.stdout.info ' %-65s '.att(:reverse).bright % ["#{desc} (#{dig}) "]
42
+ Stella.rescue { client.execute uc }
43
+ end
38
44
  end
39
45
 
46
+ thread.join
47
+
40
48
  test_time = Time.now.utc - start_time
41
49
 
50
+ #Benelux.update_global_timeline
51
+
42
52
  # Need to use thread timeline b/c the clients are running in the
43
53
  # main thread which Benelux.update_global_timeline does not touch.
44
- tt = Benelux.thread_timeline
45
-
46
- failed = tt.stats.group(:failed).merge
47
- total = tt.stats.group(:do_request).merge
54
+ tt = thread.timeline
48
55
 
49
56
  if Stella::Engine.service
50
57
  data = tt.messages.filter(:kind => :log).to_json
51
58
  Stella::Engine.service.client_log client.digest, data
52
- Stella::Engine.service.testrun_summary :successful => (total.n-failed.n),
53
- :failed => failed.n,
54
- :duration => test_time
59
+ @testrun.add_sample 1, 1, tt
55
60
  end
56
61
 
57
- failed == 0
62
+ @testrun.summary[:summary][:failed].n == 0
58
63
  end
59
64
 
60
65
 
@@ -78,7 +83,7 @@ module Stella::Engine
78
83
  Benelux.thread_timeline.add_message log, :status => container.status, :kind => :log
79
84
 
80
85
  msg = ' %-6s %-53s ' % [req.http_method, uri]
81
- msg << container.status.to_s if Stella.stdout.lev == 1
86
+ msg << container.status.to_s if Stella.stdout.lev <= 2
82
87
  Stella.stdout.info msg
83
88
 
84
89
  Stella.stdout.info2 $/, " Params:"
@@ -143,6 +148,7 @@ module Stella::Engine
143
148
  end
144
149
 
145
150
  def update_usecase_quit client_id, msg, req, container
151
+ Benelux.thread_timeline.add_count :failed, 1
146
152
  Stella.stdout.info " QUIT %s" % [msg]
147
153
  end
148
154
 
@@ -152,6 +158,7 @@ module Stella::Engine
152
158
  end
153
159
 
154
160
  def update_request_error client_id, msg, req, container
161
+ Benelux.thread_timeline.add_count :failed, 1
155
162
  Stella.stdout.info " ERROR %s" % [msg]
156
163
  end
157
164
 
@@ -164,6 +171,7 @@ module Stella::Engine
164
171
  end
165
172
 
166
173
  def update_request_timeout(client_id, usecase, uri, req, params, headers, counter, container)
174
+ Benelux.thread_timeline.add_count :failed, 1
167
175
  Stella.stdout.info " TIMEOUT %-53s" % [uri]
168
176
  end
169
177
 
@@ -13,11 +13,13 @@ module Stella::Engine
13
13
 
14
14
  time_started = Time.now
15
15
 
16
+
16
17
  (1..reps).to_a.each { |rep|
17
18
  @real_reps += 1 # Increments when duration is specified.
18
19
  Stella.stdout.info3 "*** REPETITION #{@real_reps} of #{reps} ***"
19
20
  packages.each { |package|
20
21
  if running_threads.size <= packages.size
22
+ Stella::Engine.service.client_create package.client.digest, :index => package.client.index
21
23
  @threads << Thread.new do
22
24
  c, uc = package.client, package.usecase
23
25
  msg = "THREAD START: client %s: " % [c.digest.short]
@@ -40,6 +42,7 @@ module Stella::Engine
40
42
  Stella.sleep CREATE_THREAD_SLEEPx
41
43
  end
42
44
 
45
+ @current_clients = running_threads.size
43
46
  if running_threads.size > @max_clients
44
47
  @max_clients = running_threads.size
45
48
  end
@@ -73,8 +73,9 @@ module Stella::Engine
73
73
 
74
74
  unless arrival.nil?
75
75
  # Create 1 second / users per second
76
- args = [1/arrival, @threads.size, packages.size]
77
- Stella.stdout.info2 $/, "======== ARRIVAL (%s): %s of %s" % args
76
+ args = [@threads.size, packages.size]
77
+ Stella.stdout.print 2, '+'
78
+ Stella.stdout.info3 $/, "-> NEW CLIENT: %s of %s" % args
78
79
  sleep 1/arrival
79
80
  end
80
81
  }
@@ -26,7 +26,7 @@ module Stella::Engine
26
26
  File.unlink latest if File.exists? latest
27
27
  FileUtils.ln_sf File.basename(@logdir), latest
28
28
  end
29
-
29
+
30
30
  @reqlog = Stella::Logger.new log_path(plan, 'requests')
31
31
  @failog = Stella::Logger.new log_path(plan, 'requests-exceptions')
32
32
  @sumlog = Stella::Logger.new log_path(plan, 'summary')
@@ -48,14 +48,23 @@ module Stella::Engine
48
48
  @optlog.add_template :head, '%10s: %s'
49
49
  @failog.add_template :request, '%s %s'
50
50
 
51
- @dumper = prepare_dumper(plan, opts)
52
-
53
51
  if Stella.stdout.lev > 2
54
52
  Load.timers += [:query, :connect, :socket_gets_first_byte, :get_body]
55
53
  Load.counts = [:request_header_size, :request_content_size]
56
54
  Load.counts += [:response_headers_size, :response_content_size]
57
55
  end
58
56
 
57
+ events = [Load.timers, Load.counts, :failed].flatten
58
+ @testrun = Stella::Testrun.new plan, events, opts
59
+ @testrun.mode = 'l'
60
+
61
+ if Stella::Engine.service
62
+ Stella::Engine.service.testplan_sync plan
63
+ Stella::Engine.service.testrun_create @testrun
64
+ end
65
+
66
+ @dumper = prepare_dumper(plan, opts)
67
+
59
68
  counts = calculate_usecase_clients plan, opts
60
69
 
61
70
  @optlog.head 'RUNID', runid(plan)
@@ -79,7 +88,6 @@ module Stella::Engine
79
88
  @optlog.head "START", Time.now.to_s
80
89
  Stella.stdout.status "Running"
81
90
  execute_test_plan packages, opts[:repetitions], opts[:duration], opts[:arrival]
82
- Stella.stdout.info $/, "Done"
83
91
  rescue Interrupt
84
92
  Stella.stdout.info $/, "Stopping test"
85
93
  Stella.abort!
@@ -89,47 +97,27 @@ module Stella::Engine
89
97
  STDERR.puts ex.backtrace if Stella.debug? || Stella.stdout.lev >= 3
90
98
  end
91
99
 
100
+ Stella.stdout.status "Processing"
101
+
92
102
  @optlog.head "END", Time.now.to_s
93
103
  @optlog.flush
94
104
 
95
105
  @dumper.stop
96
106
 
97
- Stella.stdout.status "Processing"
98
-
99
- Benelux.update_global_timeline
100
-
101
107
  bt = Benelux.timeline
102
108
  tt = Benelux.thread_timeline
103
109
 
110
+
111
+ # TODO: don't get test time from benelux.
104
112
  test_time = tt.stats.group(:execute_test_plan).mean
105
113
  generate_report @sumlog, plan, test_time
106
- report_time = tt.stats.group(:generate_report).mean
107
-
108
- # Here is the calcualtion for the number of
109
- # Benelux assets created for each request:
110
- #
111
- # [5*2*REQ+6, 5*1*REQ+3, 13*REQ]
112
- #
113
-
114
- failed = bt.stats.group(:failed).merge
115
- total = bt.stats.group(:do_request).merge
116
-
117
- @sumlog.info $/, "Summary: "
118
- @sumlog.dsummary 'successful req', total.n
119
- @sumlog.dsummary "failed req", failed.n
120
- @sumlog.dsummary "max clients", @max_clients
121
- @sumlog.dsummary "repetitions", @real_reps
122
- @sumlog.fsummary "test time", test_time
123
- @sumlog.fsummary "reporting time", report_time
124
- @sumlog.flush
114
+ #report_time = tt.stats.group(:generate_report).mean
125
115
 
126
116
  Stella.stdout.info File.read(@sumlog.path)
127
- # DNE:
128
- #p [@real_reps, total.n]
129
117
 
130
118
  Stella.stdout.info $/, "Log dir: #{@logdir}"
131
119
 
132
- failed.n == 0
120
+ @testrun.summary[:summary][:failed].n == 0
133
121
  end
134
122
 
135
123
  protected
@@ -143,84 +131,38 @@ module Stella::Engine
143
131
  end
144
132
 
145
133
  def prepare_dumper(plan, opts)
146
- Stella::Hand.new(15.seconds, 2.seconds) do
134
+ hand = Stella::Hand.new(LoadQueue::ROTATE_TIMELINE, 2.seconds) do
147
135
  Benelux.update_global_timeline
136
+ # @threads contains only stella clients
137
+ concurrency = @threads.select { |t| !t.status.nil? }.size
138
+ batch, timeline = Benelux.timeline_updates, Benelux.timeline_chunk
139
+ @testrun.add_sample batch, concurrency, timeline
140
+
148
141
  #reqlog.info [Time.now, Benelux.timeline.size].inspect
149
142
  @reqlog.info Benelux.timeline.messages.filter(:kind => :request)
150
143
  @failog.info Benelux.timeline.messages.filter(:kind => :exception)
151
144
  @failog.info Benelux.timeline.messages.filter(:kind => :timeout)
152
145
  @authlog.info Benelux.timeline.messages.filter(:kind => :authentication)
153
146
  @reqlog.clear and @failog.clear and @authlog.clear
147
+
154
148
  Benelux.timeline.clear if opts[:"no-stats"]
149
+
155
150
  end
156
-
157
- end
158
-
159
- def calculate_usecase_clients(plan, opts)
160
- counts = { :total => 0 }
161
- plan.usecases.each_with_index do |usecase,i|
162
- count = case opts[:clients]
163
- when 0..9
164
- if (opts[:clients] % plan.usecases.size > 0)
165
- msg = "Client count does not evenly match usecase count"
166
- raise Stella::WackyRatio, msg
167
- else
168
- (opts[:clients] / plan.usecases.size)
169
- end
170
- else
171
- (opts[:clients] * usecase.ratio).to_i
172
- end
173
- counts[usecase.digest_cache] = count
174
- counts[:total] += count
175
- end
176
- counts
177
- end
178
-
179
- def build_thread_package(plan, opts, counts)
180
- packages, pointer = Array.new(counts[:total]), 0
181
- plan.usecases.each do |usecase|
182
- count = counts[usecase.digest_cache]
183
- Stella.ld "THREAD PACKAGE: #{usecase.desc} (#{pointer} + #{count})"
184
- # Fill the thread_package with the contents of the block
185
- packages.fill(pointer, count) do |index|
186
- client = Stella::Client.new opts[:hosts].first, index+1, opts
187
- client.add_observer(self)
188
- client.enable_nowait_mode if opts[:nowait]
189
- Stella.stdout.info4 "Created client #{client.digest.short}"
190
- ThreadPackage.new(index+1, client, usecase)
191
- end
192
- pointer += count
151
+ hand.finally do
152
+ #total = @testrun.stats[:summary][:do_request].n
153
+ #failed = @testrun.stats[:summary][:failed].n
154
+ #@sumlog.info $/, "Summary: "
155
+ #@sumlog.dsummary 'successful req', total-failed
156
+ #@sumlog.dsummary "failed req", failed
157
+ #@sumlog.dsummary "max clients", @max_clients
158
+ #@sumlog.dsummary "repetitions", @real_reps
159
+ ##@sumlog.fsummary "test time", test_time
160
+ ##@sumlog.fsummary "reporting time", report_time
161
+ #@sumlog.flush
193
162
  end
194
- packages.compact # TODO: Why one nil element sometimes?
163
+ hand
195
164
  end
196
-
197
- def execute_test_plan(*args)
198
- raise "Override execute_test_plan method in #{self}"
199
- end
200
-
201
- def running_threads
202
- @threads.select { |t| t.status } # non-false status are still running
203
- end
204
-
205
- def generate_runtime_report(plan)
206
- gt = Benelux.timeline
207
- gstats = gt.stats.group(:do_request).merge
208
-
209
- plan.usecases.uniq.each_with_index do |uc,i|
210
- uc.requests.each do |req|
211
- filter = [uc.digest_cache, req.digest_cache]
212
165
 
213
- Load.timers.each do |sname|
214
- stats = gt.stats.group(sname)[filter].merge
215
- #Stella.stdout.info stats.inspect
216
- puts [sname, stats.min, stats.mean, stats.max, stats.sd, stats.n].join('; ')
217
- end
218
-
219
- end
220
- end
221
-
222
- end
223
-
224
166
  def generate_report(sumlog,plan,test_time)
225
167
  global_timeline = Benelux.timeline
226
168
  global_stats = global_timeline.stats.group(:do_request).merge
@@ -229,8 +171,6 @@ module Stella::Engine
229
171
  return
230
172
  end
231
173
 
232
-
233
-
234
174
  @sumlog.info " %-72s ".att(:reverse) % ["#{plan.desc} (#{plan.digest_cache.shorter})"]
235
175
  plan.usecases.uniq.each_with_index do |uc,i|
236
176
 
@@ -242,18 +182,19 @@ module Stella::Engine
242
182
  desc = uc.desc || "Usecase ##{i+1} "
243
183
  desc << " (#{uc.digest_cache.shorter}) "
244
184
  str = ' ' << " %-66s %s %d%% ".bright.att(:reverse)
245
- @sumlog.info str % [desc, '', uc.ratio_pretty]
246
-
185
+ @sumlog.info str % [desc, '', uc.ratio_pretty]
247
186
  uc.requests.each do |req|
248
187
  filter = [uc.digest_cache, req.digest_cache]
249
188
  desc = req.desc
250
189
  @sumlog.info " %-72s ".bright % ["#{req.desc} (#{req.digest_cache.shorter})"]
251
190
  @sumlog.info " %s" % [req.to_s]
191
+
252
192
  Load.timers.each do |sname|
253
193
  stats = global_timeline.stats.group(sname)[filter].merge
254
194
  # Stella.stdout.info stats.inspect
255
195
  str = ' %-30s %.3f <= ' << '%.3fs' << ' >= %.3f; %.3f(SD) %d(N)'
256
- @sumlog.info str % [sname, stats.min, stats.mean, stats.max, stats.sd, stats.n]
196
+ msg = str % [sname, stats.min, stats.mean, stats.max, stats.sd, stats.n]
197
+ @sumlog.info msg
257
198
  @sumlog.flush
258
199
  end
259
200
  @sumlog.info $/
@@ -320,6 +261,75 @@ module Stella::Engine
320
261
  end
321
262
 
322
263
 
264
+ def calculate_usecase_clients(plan, opts)
265
+ counts = { :total => 0 }
266
+ plan.usecases.each_with_index do |usecase,i|
267
+ count = case opts[:clients]
268
+ when 0..9
269
+ if (opts[:clients] % plan.usecases.size > 0)
270
+ msg = "Client count does not evenly match usecase count"
271
+ raise Stella::WackyRatio, msg
272
+ else
273
+ (opts[:clients] / plan.usecases.size)
274
+ end
275
+ else
276
+ (opts[:clients] * usecase.ratio).to_i
277
+ end
278
+ counts[usecase.digest_cache] = count
279
+ counts[:total] += count
280
+ end
281
+ counts
282
+ end
283
+
284
+
285
+ def build_thread_package(plan, opts, counts)
286
+ packages, pointer = Array.new(counts[:total]), 0
287
+ plan.usecases.each do |usecase|
288
+ count = counts[usecase.digest_cache]
289
+ Stella.ld "THREAD PACKAGE: #{usecase.desc} (#{pointer} + #{count})"
290
+ # Fill the thread_package with the contents of the block
291
+ packages.fill(pointer, count) do |index|
292
+ client = Stella::Client.new opts[:hosts].first, index+1, opts
293
+ client.add_observer(self)
294
+ client.enable_nowait_mode if opts[:nowait]
295
+ Stella.stdout.info4 "Created client #{client.digest.short}"
296
+ ThreadPackage.new(index+1, client, usecase)
297
+ end
298
+ pointer += count
299
+ end
300
+ packages.compact # TODO: Why one nil element sometimes?
301
+ # Randomize so when ramping up load
302
+ # we get a mix of usecases.
303
+ packages.sort_by {rand}
304
+ end
305
+
306
+ def execute_test_plan(*args)
307
+ raise "Override execute_test_plan method in #{self}"
308
+ end
309
+
310
+ def running_threads
311
+ @threads.select { |t| t.status } # non-false status are still running
312
+ end
313
+
314
+ def generate_runtime_report(plan)
315
+ gt = Benelux.timeline
316
+ gstats = gt.stats.group(:do_request).merge
317
+
318
+ plan.usecases.uniq.each_with_index do |uc,i|
319
+ uc.requests.each do |req|
320
+ filter = [uc.digest_cache, req.digest_cache]
321
+
322
+ Load.timers.each do |sname|
323
+ stats = gt.stats.group(sname)[filter].merge
324
+ #Stella.stdout.info stats.inspect
325
+ puts [sname, stats.min, stats.mean, stats.max, stats.sd, stats.n].join('; ')
326
+ end
327
+
328
+ end
329
+ end
330
+
331
+ end
332
+
323
333
  def update_prepare_request(client_id, usecase, req, counter)
324
334
 
325
335
  end
@@ -367,7 +377,7 @@ module Stella::Engine
367
377
  Stella.ld ex.backtrace
368
378
  end
369
379
  end
370
-
380
+
371
381
  def update_usecase_quit client_id, msg, req, container
372
382
  args = [Time.now.to_f, Stella.sysinfo.hostname, client_id.short]
373
383
  Benelux.thread_timeline.add_count :quit, 1
@@ -393,7 +403,7 @@ module Stella::Engine
393
403
  Stella.le ' Client-%s %-45s %s' % [client_id.shorter, desc, ex.message]
394
404
  end
395
405
  end
396
-
406
+
397
407
  def update_request_repeat client_id, counter, total, req, container
398
408
  Stella.stdout.info3 " Client-%s REPEAT %d of %d" % [client_id.shorter, counter, total]
399
409
  end
data/lib/stella/engine.rb CHANGED
@@ -4,6 +4,7 @@ module Stella::Engine
4
4
  class << self
5
5
  attr_accessor :service
6
6
  end
7
+ # See functional.rb
7
8
  class Log < Storable
8
9
  include Selectable::Object
9
10
  field :stamp
@@ -23,6 +24,8 @@ module Stella::Engine
23
24
  module Base
24
25
  extend self
25
26
 
27
+ @testrun = nil
28
+
26
29
  @@client_limit = 1000
27
30
 
28
31
  def update(*args)
@@ -84,6 +87,7 @@ module Stella::Engine
84
87
  opts
85
88
  end
86
89
 
90
+
87
91
  def run; raise; end
88
92
  def update_usecase_quit(client_id, msg) raise end
89
93
  def update_request_repeat(client_id, counter, total) raise end
@@ -119,3 +123,98 @@ module Stella::Engine
119
123
 
120
124
  end
121
125
 
126
+ class Stella::Testrun < Storable
127
+ extend Attic
128
+ attic :remote_digest
129
+ field :samples => Array
130
+ field :plan
131
+ field :summary
132
+ field :hosts
133
+ field :events
134
+ field :mode # (f)unctional or (l)oad
135
+ field :clients => Integer
136
+ field :duration => Integer
137
+ field :arrival => Float
138
+ field :repetitions => Integer
139
+ field :nowait => Integer
140
+ def initialize(plan, events, opts={})
141
+ @plan, @events = plan, events
142
+ @samples, @summary = nil, nil
143
+ opts.each_pair do |n,v|
144
+ self.send("#{n}=", v) if has_field? n
145
+ end
146
+ reset
147
+ end
148
+
149
+ def reset
150
+ @samples = []
151
+ @summary = { :summary => {} }
152
+ @plan.usecases.each do |uc|
153
+ @events.each do |event|
154
+ @summary[:summary][event] = Benelux::Stats::Calculator.new
155
+ @summary[uc.digest] ||= { :summary => {} }
156
+ @summary[uc.digest][:summary][event] = Benelux::Stats::Calculator.new
157
+ uc.requests.each do |req|
158
+ @summary[uc.digest][req.digest] ||= {}
159
+ @summary[uc.digest][req.digest][event] = Benelux::Stats::Calculator.new
160
+ end
161
+ end
162
+ end
163
+ end
164
+
165
+ def add_sample batch, concurrency, tl
166
+
167
+ opts = {
168
+ :batch => batch,
169
+ :duration => tl.duration,
170
+ :stamp => Time.now.utc.to_i,
171
+ :concurrency => concurrency
172
+ }
173
+
174
+ sam = Stella::Testrun::Sample.new opts
175
+
176
+ @plan.usecases.uniq.each_with_index do |uc,i|
177
+ sam.stats[uc.digest] ||= { }
178
+ uc.requests.each do |req|
179
+ sam.stats[uc.digest][req.digest] ||= {}
180
+ filter = [uc.digest, req.digest]
181
+ @events.each_with_index do |event,idx| # do_request, etc...
182
+ stats = tl.stats.group(event)[filter].merge
183
+ sam.stats[uc.digest][req.digest][event] = stats
184
+ # Tally request, usecase and total summaries at the same time.
185
+ @summary[uc.digest][req.digest][event] += stats
186
+ @summary[uc.digest][:summary][event] += stats
187
+ @summary[:summary][event] += stats
188
+ end
189
+ end
190
+ end
191
+
192
+ @samples << sam
193
+
194
+ begin
195
+ if Stella::Engine.service
196
+ Stella::Engine.service.testrun_stats @summary, @samples
197
+ end
198
+ rescue => ex
199
+ Stella.stdout.info "Error syncing to #{Stella::Engine.service.source}"
200
+ Stella.stdout.info ex.message, ex.backtrace if Stella.debug?
201
+ end
202
+
203
+ end
204
+
205
+
206
+ class Sample < Storable
207
+ field :batch
208
+ field :concurrency
209
+ field :stamp
210
+ field :duration
211
+ field :stats => Hash
212
+ #gibbler :batch, :concurrency, :stamp, :duration
213
+ def initialize(opts={})
214
+ opts.each_pair do |n,v|
215
+ self.send("#{n}=", v) if has_field? n
216
+ end
217
+ @stats = { }
218
+ end
219
+ end
220
+ end
data/lib/stella/logger.rb CHANGED
@@ -1,8 +1,10 @@
1
1
 
2
2
 
3
3
  module Stella
4
+
4
5
 
5
6
  class Logger
7
+
6
8
  @@disable = false
7
9
  def self.disable!() @@disable = true end
8
10
  def self.disabled?() @@disable == true end
@@ -1,6 +1,77 @@
1
1
  Stella::Utils.require_vendor "httpclient", '2.1.5.2'
2
2
 
3
+ class Hash
4
+
5
+ # Courtesy of Julien Genestoux
6
+ def flatten
7
+ params = {}
8
+ stack = []
9
+
10
+ each do |k, v|
11
+ if v.is_a?(Hash)
12
+ stack << [k,v]
13
+ elsif v.is_a?(Array)
14
+ stack << [k,Hash.from_array(v)]
15
+ else
16
+ params[k] = v
17
+ end
18
+ end
19
+
20
+ stack.each do |parent, hash|
21
+ hash.each do |k, v|
22
+ if v.is_a?(Hash)
23
+ stack << ["#{parent}[#{k}]", v]
24
+ else
25
+ params["#{parent}[#{k}]"] = v
26
+ end
27
+ end
28
+ end
29
+
30
+ params
31
+ end
32
+
33
+ # Courtesy of Julien Genestoux
34
+ # See: http://stackoverflow.com/questions/798710/how-to-turn-a-ruby-hash-into-http-params
35
+ def to_params
36
+ params = ''
37
+ stack = []
38
+
39
+ each do |k, v|
40
+ if v.is_a?(Hash)
41
+ stack << [k,v]
42
+ elsif v.is_a?(Array)
43
+ stack << [k,Hash.from_array(v)]
44
+ else
45
+ params << "#{k}=#{v}&"
46
+ end
47
+ end
48
+
49
+ stack.each do |parent, hash|
50
+ hash.each do |k, v|
51
+ if v.is_a?(Hash)
52
+ stack << ["#{parent}[#{k}]", v]
53
+ else
54
+ params << "#{parent}[#{k}]=#{v}&"
55
+ end
56
+ end
57
+ end
58
+
59
+ params.chop!
60
+ params
61
+ end
62
+ def self.from_array(array = [])
63
+ h = Hash.new
64
+ array.size.times do |t|
65
+ h[t] = array[t]
66
+ end
67
+ h
68
+ end
69
+
70
+ end
71
+
72
+
3
73
  class Stella::Service
74
+ attr_accessor :runid, :tid, :uid, :rtid
4
75
  class Problem < Stella::Error
5
76
  def res() @obj end
6
77
  end
@@ -60,27 +131,53 @@ class Stella::Service
60
131
  obj = JSON.parse res.content
61
132
  @rtid = obj['digest']
62
133
  end
63
- def testrun_create(opts={})
134
+ def testrun_create(trun)
64
135
  raise NoTestplanSelected unless @tid
65
136
  req = uri('v1', 'testrun', "create.json")
66
137
  params = {
67
138
  :tid => @tid,
68
- :start_time => Stella::START_TIME
69
- }.merge! opts
139
+ :v => Stella::VERSION.to_s,
140
+ :start_at => Stella::START_TIME.to_i,
141
+ :mode => trun.mode,
142
+ :clients => trun.clients,
143
+ :duration => trun.duration,
144
+ :arrival => trun.arrival,
145
+ :repetitions => trun.repetitions,
146
+ :nowait => trun.nowait,
147
+ :hosts => trun.hosts,
148
+ :status => 'created'
149
+ }
70
150
  res = send_request :post, req, params
71
151
  obj = JSON.parse res.content
72
152
  Stella.ld "CREATED TRUN: #{obj.inspect}"
153
+ trun.remote_digest = obj['digest']
73
154
  @runid = obj['digest']
74
155
  end
75
- def testrun_summary(opts={})
156
+ def testrun_stats(summary, samples)
157
+ raise NoTestrunSelected, "no testrun: #{runid}" unless @runid
158
+ req = uri('v1', 'testrun', "stats.json")
159
+ params = {
160
+ :runid => @runid,
161
+ :status => 'running',
162
+ :summary => summary.to_json,
163
+ :samples => samples.to_json
164
+ }
165
+ res = send_request :post, req, params
166
+ obj = JSON.parse res.content
167
+ Stella.ld "TESTRUN STATS: #{obj.inspect}"
168
+ @runid
169
+ end
170
+ def testrun_finalize(duration)
76
171
  raise NoTestrunSelected unless @runid
77
- req = uri('v1', 'testrun', 'summary', "create.json")
172
+ req = uri('v1', 'testrun', "finalize.json")
78
173
  params = {
79
- :runid => @runid
80
- }.merge! opts
174
+ :runid => @runid,
175
+ :duration => duration,
176
+ :status => 'complete'
177
+ }
81
178
  res = send_request :post, req, params
82
179
  obj = JSON.parse res.content
83
- Stella.ld "CREATED SUMMARY: #{obj.inspect}"
180
+ Stella.ld "FINALIZE TESTRUN: #{obj.inspect}"
84
181
  @runid
85
182
  end
86
183
  def client_create(clientid, opts={})
@@ -148,6 +245,14 @@ class Stella::Service
148
245
  attr_accessor :tid, :uid, :rtid, :runid
149
246
 
150
247
  def initialize(source=nil,apikey=nil)
248
+ begin
249
+ require 'bone'
250
+ rescue LoadError
251
+ end
252
+ if defined?(Bone)
253
+ source ||= Bone['STELLA_SOURCE'] rescue nil
254
+ apikey ||= Bone['STELLA_TOKEN'] rescue nil
255
+ end
151
256
  source ||= ENV['STELLA_SOURCE']
152
257
  apikey ||= ENV['STELLA_TOKEN']
153
258
  @source, @apikey = source, apikey
@@ -166,7 +271,9 @@ class Stella::Service
166
271
 
167
272
  def send_request(meth, uri, params={}, headers={})
168
273
  headers['X-TOKEN'] ||= @apikey
274
+
169
275
  params = process_params(params)
276
+
170
277
  if meth == "delete"
171
278
  args = [meth, uri, headers]
172
279
  else
@@ -175,7 +282,6 @@ class Stella::Service
175
282
  res = @http_client.send(*args) # booya!
176
283
 
177
284
  if res.status > 200
178
- puts res.content
179
285
  raise Problem.new(res)
180
286
  end
181
287
 
data/lib/stella.rb CHANGED
@@ -22,7 +22,7 @@ module Stella
22
22
  unless defined?(MAJOR)
23
23
  MAJOR = 0.freeze
24
24
  MINOR = 8.freeze
25
- TINY = 0.freeze
25
+ TINY = 1.freeze
26
26
  PATCH = '001'.freeze
27
27
  end
28
28
  def self.to_s; [MAJOR, MINOR, TINY].join('.'); end
@@ -50,17 +50,12 @@ class Stella::Template
50
50
  attr_reader :src
51
51
  def initialize(src)
52
52
  src = src.to_s
53
- @src, @template = src, ERB.new(Stella::Template.to_templ(src))
53
+ @src, @template = src, ERB.new(src)
54
54
  end
55
55
  def result(binding)
56
56
  @template.result(binding)
57
57
  end
58
58
  def to_s() src end
59
- private
60
- def self.to_templ(str)
61
- return str if str.match(/\A<%.+%>\z/)
62
- "<%= #{str} %>"
63
- end
64
59
  end
65
60
 
66
61
  module Stella
@@ -83,7 +78,7 @@ module Stella
83
78
  class << self
84
79
  attr_accessor :log, :stdout
85
80
  end
86
-
81
+
87
82
  def le(*msg); stdout.info " " << msg.join("#{$/} ").color(:red); end
88
83
  def ld(*msg)
89
84
  return unless Stella.debug?
@@ -129,6 +124,16 @@ module Stella
129
124
  autoload :Client, 'stella/client'
130
125
  autoload :Service, 'stella/service'
131
126
 
127
+ def get(uri, query={})
128
+ require 'stella/client'
129
+ http_client = HTTPClient.new :agent_name => "Opera/9.51 (Windows NT 5.1; U; en)"
130
+ http_client.get(uri, query).body.content
131
+ rescue => ex
132
+ STDERR.puts ex.message
133
+ STDERR.puts ex.backtrace if Stella.debug?
134
+ nil
135
+ end
136
+
132
137
  end
133
138
 
134
139
  Stella.stdout.lev = Stella.quiet? ? 0 : 1
data/stella.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  @spec = Gem::Specification.new do |s|
2
2
  s.name = "stella"
3
3
  s.rubyforge_project = 'stella'
4
- s.version = "0.8.0.001"
4
+ s.version = "0.8.1.001"
5
5
  s.summary = "Blame Stella for breaking your web application!"
6
6
  s.description = s.summary
7
7
  s.author = "Delano Mandelbaum"
@@ -14,8 +14,8 @@
14
14
  s.require_paths = %w[lib]
15
15
 
16
16
  s.executables = %w[stella]
17
-
18
- s.add_dependency 'benelux', '>= 0.5.3'
17
+
18
+ s.add_dependency 'benelux', '>= 0.5.6'
19
19
  s.add_dependency 'drydock', '>= 0.6.8'
20
20
  s.add_dependency 'gibbler', '>= 0.7.3'
21
21
  s.add_dependency 'sysinfo', '>= 0.7.1'
@@ -364,7 +364,12 @@ module HTTP
364
364
  set('Last-Modified', @body_date.httpdate)
365
365
  end
366
366
  if self['Content-Type'].empty?
367
- set('Content-Type', "#{ @body_type || 'text/html' }; charset=#{ charset_label(@body_charset || $KCODE) }")
367
+ if RUBY_VERSION < "1.9"
368
+ charset = charset_label(@body_charset || $KCODE)
369
+ else
370
+ charset = charset_label(@body_charset)
371
+ end
372
+ set('Content-Type', "#{ @body_type || 'text/html' }; charset=#{ charset }")
368
373
  end
369
374
  end
370
375
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stella
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0.001
4
+ version: 0.8.1.001
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-26 00:00:00 -05:00
12
+ date: 2010-02-10 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -20,7 +20,7 @@ dependencies:
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 0.5.3
23
+ version: 0.5.6
24
24
  version:
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: drydock