stella 0.7.0.006 → 0.7.0.012

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/CHANGES.txt CHANGED
@@ -8,6 +8,7 @@ NOTE: Complete rewrite
8
8
  * New internal architecture.
9
9
  * Improved stability and output for high thread loads.
10
10
  * Updated test plan DSL
11
+ * Granular reporting via Benelux
11
12
 
12
13
 
13
14
  #### 0.6.0 (2009-03-15) ###############################
data/bin/stella CHANGED
@@ -59,7 +59,7 @@ class Stella::CLI::Definition
59
59
  about "Run a functional test"
60
60
  usage "stella verify http://stellaaahhhh.com/"
61
61
  usage "stella verify -p path/2/testplan.rb http://stellaaahhhh.com/"
62
- option :b, :benchmark, "Benchmark mode (ignore wait times)"
62
+ option :w, :nowait, "Ignore wait times"
63
63
  option :p, :testplan, String, "Path to testplan"
64
64
  command :verify => Stella::CLI
65
65
 
@@ -68,7 +68,7 @@ class Stella::CLI::Definition
68
68
  usage "stella load http://stellaaahhhh.com:3114/"
69
69
  usage "stella load --clients=10 --repetitions=2 http://stellaaahhhh.com/"
70
70
  usage "stella load -p path/2/testplan.rb -u 100 -r 5 http://stellaaahhhh.com/"
71
- option :b, :benchmark, "Benchmark mode (ignore wait times)"
71
+ option :w, :nowait, "Ignore wait times"
72
72
  option :c, :clients, Integer, "Number of virtual clients"
73
73
  option :r, :repetitions, Integer, "Number of times to repeat the testplan (per vclient)"
74
74
  option :t, :time, String, "Max duration to run test"
@@ -0,0 +1,18 @@
1
+ desc "Maintain Your Cookies"
2
+
3
+ usecase 65, "Simple search" do
4
+
5
+ get "/", "Homepage"
6
+
7
+ get "/search", "Search Results" do
8
+ param :what => random(['Big', 'Beads'])
9
+ param :where => 'Toronto'
10
+ end
11
+
12
+ get "/", "Homepage" do
13
+ response 200 do
14
+ puts doc.css('ul#history').first
15
+ end
16
+ end
17
+
18
+ end
@@ -1,26 +1,165 @@
1
- # 4f61ac3d5607139350b50335ca59a34d04b34ec7
1
+ # Stella - Example Test Plan
2
+ #
3
+ #
4
+ # 1. INTRODUCTION
5
+ #
6
+ # A test plan is a group of one or more user
7
+ # scenarios. This allows you to simulate the
8
+ # kind of traffic your application is exposed
9
+ # to in the wild. Realistic tests are really
10
+ # important because they give you a much more
11
+ # accurate view of how your application is
12
+ # performing.
13
+ #
14
+ # A scenario (or "usecase") is a group a
15
+ # requests that represents a typical path
16
+ # that a real person would take through
17
+ # your site.
18
+ #
19
+ # This plan contains 3 scenarios:
20
+ #
21
+ # - Simple Search (60%)
22
+ # - YAML API (30%)
23
+ # - Self-serve API (10%)
24
+ #
25
+ # The percentages represent the relative amount
26
+ # of traffic to be generated for each scenario.
27
+ # In a test with 100 virtual users, 60 would
28
+ # follow the Simple Search usecase, 30 the YAML
29
+ # API, and 10 would following the Self-Serve API.
30
+ #
31
+ #
32
+ # 2. THE CONFIGURATION LANGUAGE
33
+ #
34
+ # Test plans are defined in a streamlined version
35
+ # of the Ruby programming language. Using a "real"
36
+ # languages gives you a lot of power to specify
37
+ # complex operations.
38
+ #
39
+ #
40
+ # 3. START THE EXAMPLE APPLICATION
41
+ #
42
+ # You need to start the example web application before
43
+ # running this testplan. You can do this in one of the
44
+ # following ways:
45
+ #
46
+ # $ ruby examples/example_webapp.rb
47
+ #
48
+ # OR
49
+ #
50
+ # $ thin -R examples/example_webapp.ru start
51
+ #
52
+ # You can check that it's running by going to:
53
+ # http://127.0.0.1:3000/
54
+ #
55
+ #
56
+ # 4. RUNNING THE TEST PLAN
57
+ #
58
+ # You run this test plan from the command line.
59
+ # First you verify that the plan and application
60
+ # are running correctly:
61
+ #
62
+ # $ stella verify -p examples/essentials/plan.rb http://127.0.0.1:3114/
63
+ #
64
+ # The "verify" command executes the plan with a
65
+ # single user and provides more detailed output.
66
+ #
67
+ # "load" tests are run in a similar way:
68
+ #
69
+ # $ stella load -c 50 -r 10 -p examples/essentials/plan.rb http://127.0.0.1:3114/
70
+ #
71
+ # where "c" is the number of concurrent users and
72
+ # "r" is the number of times to repeat the plan.
73
+ #
74
+ #
75
+ # 5. WRITING A TEST PLAN
76
+ #
77
+ # The following is an example of a working test plan.
78
+ # I put a lot of effort into keeping the syntax simple
79
+ # but feel free to contact me if you have any questions
80
+ # or problems.
81
+ #
82
+ # Happy Testing!
83
+ #
84
+ # Delano (@solutious.com)
85
+ #
2
86
 
3
87
  desc "Business Finder Testplan"
4
88
 
5
89
  usecase 65, "Simple search" do
90
+
91
+ # An important factor in simulating traffic
92
+ # is using real, dynamic data. You can load
93
+ # data from a file using the resource method.
94
+ # Here is an example which reads a list of
95
+ # search terms ("restaurant", "hotel", ...)
96
+ # into an array called :search_terms. The
97
+ # colon is Ruby's way of defining a symbol.
98
+ #
6
99
  resource :search_terms, list('search_terms.csv')
7
100
 
101
+ # Requests are defined with one of the
102
+ # following methods: get, post, head, delete.
103
+ # Here we define a simple get request for the
104
+ # homepage ("/").
105
+ #
8
106
  get "/", "Homepage" do
107
+ # This tells Stella to wait between 1 and 5
108
+ # seconds before moving to the next request.
9
109
  wait 1..5
10
- response 200 do
11
- status # => 200
12
- headers['Content-Type'] # => ['text/html']
13
- body # => <html>...
14
- doc # => Nokigiri::HTML::Document
15
- end
16
110
  end
17
111
 
112
+ # In this request, the user has entered a simple
113
+ # what and where search. You'll notice that we
114
+ # aren't specifying a hostname or port number for
115
+ # these requests. We do that so you use the same
116
+ # test plan to run tests on machines with different
117
+ # hostnames. However, this is optional. You can
118
+ # also specify absolute URIs like this:
119
+ #
120
+ # get "http://example.com:8000/search"
121
+ #
18
122
  get "/search", "Search Results" do
19
123
  wait 2..5
124
+
125
+ # Two URI parameters will be included with this
126
+ # request. Notice that the values for the what
127
+ # and where parameters come from the resources
128
+ # we defined. We've specified for random values
129
+ # to be used, but we could also specify sequential
130
+ # or reverse sequential values (see XML API).
131
+ #
20
132
  param :what => random(:search_terms)
21
- param :where => random(['Toronto', 'Montreal'])
133
+ param :where => random(['Toronto', 'Vancouver', 'Montreal'])
134
+
135
+ # Each request can also include one or more
136
+ # optional response blocks. These blocks determine
137
+ # what should happen when the specified HTTP
138
+ # status code is returned by the web server.
139
+ #
140
+ # For successful responses, we want to parse out
141
+ # some data from the page. For 404 (Not Found)
142
+ # responses, we simply want the virtual user to
143
+ # quit the usecase altogether.
144
+ #
22
145
  response 200 do
146
+
147
+ # If the response contains HTML, it will
148
+ # automatically be parsed using the Ruby
149
+ # library Nokogiri. See the following link
150
+ # for more information:
151
+ # http://nokogiri.rubyforge.org/nokogiri/
152
+ #
153
+ # The important thing to note is that you
154
+ # don't need to write complex regular
155
+ # expressions to grab data from the page.
156
+ #
23
157
  listing = doc.css('div.listing').first
158
+
159
+ # Here we grab the first listing ID on the
160
+ # page and store it in a variable called
161
+ # :lid. This is similar to a resource.
162
+ #
24
163
  set :lid, listing['id'].match(/(\d+)/)[0]
25
164
  end
26
165
  response 404 do
@@ -28,37 +167,66 @@ usecase 65, "Simple search" do
28
167
  end
29
168
  end
30
169
 
31
- get "/listing/:lid" do # URIs can contain variables.
32
- desc "Selected listing" # This one will be replaced by
33
- wait 1..8 # the one stored in the previous
34
- end # request.
170
+ # Notice the special variable in this URI path.
171
+ # Since we have defined a variable with the same
172
+ # name in the previous request, the variable in
173
+ # the request will automatically be replaced with
174
+ # that value. This value is unique for each virtual
175
+ # user.
176
+ #
177
+ get "/listing/:lid" do
178
+ desc "Selected listing"
179
+ wait 1..8
180
+ end
35
181
 
36
182
  end
37
183
 
38
- xusecase 10, "Self-serve" do
39
- post "/listing/add", "Add a listing" do
40
- wait 1..4
41
- param :name => random(8)
42
- param :city => sequential("Montreal", "Toronto", "Vancouver")
43
- param :logo => file('logo.png')
44
- response 302 do
45
- repeat 3
46
- end
47
- end
48
- end
49
-
50
- usecase "Listing API" do
184
+ usecase 25, "YAML API" do
51
185
 
52
186
  get '/listings.yaml', "View All" do
53
187
  response 200 do
54
- # doc contains the parsed YAML object
188
+
189
+ # We showed above how HTML is parsed automatically.
190
+ # Stella can do the same with XML, YAML, and JSON.
191
+ #
192
+ # A variable called "doc" contains the parsed YAML.
193
+ # "collect" is a Ruby method that iterates through
194
+ # all items in an Array and returns a new Array
195
+ # containing the return values from each iteration.
196
+ # Each item item is available in the variable
197
+ # called 'l'.
198
+ #
199
+ # The variable called "listings" will contain all
200
+ # listing ids in the YAML document.
201
+ #
55
202
  listings = doc.collect! { |l|; l[:id]; }
56
- set :current_listing_ids, listings
203
+
204
+ # And here we store that list of ids.
205
+ #
206
+ set :listing_ids, listings
57
207
  end
58
208
  end
59
209
 
60
- get "/listing/:lid.yaml", "Select (sequential)" do
61
- param :lid => rsequential(:current_listing_ids)
210
+ # In the Simple Search usecase we stored a listing
211
+ # id in a variable called :lid. This time we have
212
+ # an Array of listing ids but we only want to use
213
+ # one for each request so we override the automatic
214
+ # variable replacement by using the param method.
215
+ #
216
+ get "/listing/:lid.yaml", "Select Listing" do
217
+
218
+ # Each request will use a new value from the
219
+ # Array and these will be selected sequentially.
220
+ # Note that this is _per virtual user_. Each
221
+ # vuser will have its own copy of the Array and
222
+ # iterate through it independently.
223
+ #
224
+ param :lid => rsequential(:listing_ids)
225
+
226
+ # We can use response blocks to affect behaviour
227
+ # the user. Here we specify that every virtual
228
+ # user should repeat this request 7 times.
229
+ #
62
230
  response 200 do
63
231
  repeat 7
64
232
  end
@@ -66,3 +234,22 @@ usecase "Listing API" do
66
234
 
67
235
  end
68
236
 
237
+ usecase 10, "Self-serve API" do
238
+
239
+ # Here is an example of using a POST request.
240
+ # Notice that the definition is otherwise
241
+ # identical to the ones you've seen above.
242
+ #
243
+ post "/listing/add", "Add a listing" do
244
+ wait 1..4
245
+ param :name => random(8)
246
+ param :city => random(['Toronto', 'Vancouver', 'Montreal'])
247
+ param :logo => file('logo.png')
248
+ response 302 do
249
+ repeat 3
250
+ end
251
+ end
252
+
253
+ end
254
+
255
+ # a5689bb64829d2dc1e9ab8901223cc90c975fe3a
@@ -73,7 +73,11 @@ post '/listing/add' do
73
73
  else
74
74
  @listings = options.listings
75
75
  if find_name(params[:name], @listings).empty?
76
- @listings.shift if @listings.size >= options.max_listings
76
+ # Limit the number of in memory listings, but we want to keep
77
+ # the original listings so we use a slice instead of a shift.
78
+ if @listings.size >= options.max_listings
79
+ @listings.slice!(LISTINGS.size)
80
+ end
77
81
  @listings << { :name => params[:name], :id => rand(100000), :city => params[:city] }
78
82
  if params[:logo].is_a?(Hash) && params[:logo][:tempfile]
79
83
  p "TODO: Fix uploads"
@@ -130,7 +134,8 @@ before do
130
134
  @cookie[:history].unshift params[:what] unless blank?(params[:what])
131
135
  @cookie[:history].pop if @cookie[:history].size > 5
132
136
  @cookie[:location] = params[:where] unless blank?(params[:where])
133
- response.set_cookie "bff-history", :path => '/', :value => @cookie.to_yaml
137
+ response.set_cookie "bff-history",
138
+ :value => @cookie.to_yaml#, :expires => Time.parse('16/02/2012')
134
139
  end
135
140
 
136
141
  helpers do
@@ -177,11 +182,16 @@ __END__
177
182
  @@layout
178
183
  <html>
179
184
  <head>
185
+ <!--
186
+ Param: __stella: <%= params['__stella'] %>
187
+ Header: X-Stella-ID: <%= env['HTTP_X_STELLA_ID'] %>
188
+ -->
180
189
  <title><%= @title %></title>
181
190
  <style>
182
191
  .hilite { background-color: #FEE00B; font-weight: bold; }
183
192
  .footer { color: #ccc; font-weight: lighter; font-size: 80%; margin-top: 30px; }
184
- .footer a { color: #69c;}
193
+ .footer a { color: #69c; }
194
+ body { background: url('http://solutious.com/images/solutious-logo-large.png?1') no-repeat right;}
185
195
  </style>
186
196
  </head>
187
197
  <body>
@@ -194,7 +204,7 @@ __END__
194
204
  </em></p>
195
205
  <%= yield %>
196
206
  <div class="footer">
197
- A <a href="http://solutious.com/projects/stella/">Stella</a> demo by <a href="http://solutious.com/">Solutious</a>.
207
+ A <a href="http://solutious.com/">Solutious Inc</a> production.
198
208
  </div>
199
209
  </body>
200
210
  </html>
@@ -1,3 +1,9 @@
1
+ # Stella Demo Rackup
2
+ #
3
+ # Usage:
4
+ #
5
+ # $ thin -R examples/example_webapp.ru -p 3114 start
6
+
1
7
  dir = ::File.expand_path(::File.dirname(__FILE__))
2
8
  require ::File.join(dir, 'example_webapp.rb')
3
9
  run Sinatra::Application
@@ -3,16 +3,10 @@
3
3
  usecase "Exception Handling" do
4
4
 
5
5
  get "/search", "Search Results" do
6
- wait 2..5
7
- param :what => random()
6
+ param :what => 'No Such Listing'
8
7
  param :where => random('Toronto', 'Montreal', 'Vancouver')
9
- response 200 do
10
- listing = doc.css('div.listing').first
11
- set :lid, listing['id'].match(/(\d+)/)[0]
12
- raise NoListingResultFound if listing.nil?
13
- end
14
8
  response 404 do
15
- raise NoSearchResults
9
+ raise NoSearchResults
16
10
  end
17
11
  end
18
12
 
data/lib/stella/cli.rb CHANGED
@@ -15,7 +15,7 @@ class Stella::CLI < Drydock::Command
15
15
  def verify
16
16
  opts = {}
17
17
  opts[:hosts] = @hosts
18
- opts[:benchmark] = true if @option.benchmark
18
+ opts[:nowait] = true if @option.nowait
19
19
  ret = Stella::Engine::Functional.run @testplan, opts
20
20
  @exit_code = (ret ? 0 : 1)
21
21
  end
@@ -27,7 +27,7 @@ class Stella::CLI < Drydock::Command
27
27
  def load
28
28
  opts = {}
29
29
  opts[:hosts] = @hosts
30
- [:benchmark, :clients, :repetitions, :delay, :time].each do |opt|
30
+ [:nowait, :clients, :repetitions, :delay, :time].each do |opt|
31
31
  opts[opt] = @option.send(opt) unless @option.send(opt).nil?
32
32
  end
33
33
  ret = Stella::Engine::Load.run @testplan, opts
@@ -39,7 +39,10 @@ class Stella::CLI < Drydock::Command
39
39
  unless @option.testplan.nil? || File.exists?(@option.testplan)
40
40
  raise Stella::InvalidOption, "Bad path: #{@option.testplan}"
41
41
  end
42
- @hosts = @argv.collect { |uri|; URI.parse uri; }
42
+ @hosts = @argv.collect { |uri|;
43
+ uri = 'http://' << uri unless uri.match /^http:\/\//i
44
+ URI.parse uri;
45
+ }
43
46
  if @option.testplan
44
47
  @testplan = Stella::Testplan.load_file @option.testplan
45
48
  else
@@ -0,0 +1,49 @@
1
+
2
+
3
+ class Stella::Client
4
+
5
+ class Container
6
+
7
+ attr_accessor :usecase
8
+ attr_accessor :response
9
+ attr_reader :resources
10
+ def initialize(usecase)
11
+ @usecase, @resources = usecase, {}
12
+ @base_path = usecase.base_path
13
+ end
14
+
15
+ # This is used to handle custom exception in usecases.
16
+ # See examples/exceptions/plan.rb
17
+ #
18
+ def self.const_missing(custom_error)
19
+ ResponseError.new custom_error
20
+ end
21
+
22
+ def doc
23
+ # NOTE: It's important to parse the document on every
24
+ # request because this container is available for the
25
+ # entire life of a usecase.
26
+ case @response.header['Content-Type']
27
+ when ['text/html']
28
+ Nokogiri::HTML(body)
29
+ when ['text/yaml']
30
+ YAML.load(body)
31
+ end
32
+ end
33
+
34
+ def resource(n)
35
+ return @usecase.resource(n) if @usecase.resources.has_key? n
36
+ return @resources[n] if @resources.has_key? n
37
+ end
38
+
39
+ def body; @response.body.content; end
40
+ def headers; @response.header; end
41
+ alias_method :header, :headers
42
+ def status; @response.status; end
43
+ def set(n, v); @resources[n] = v; end
44
+ def wait(t); sleep t; end
45
+ def quit(msg=nil); Quit.new(msg); end
46
+ def repeat(t=1); Repeat.new(t); end
47
+ end
48
+
49
+ end
@@ -0,0 +1,19 @@
1
+
2
+
3
+ class Stella::Client
4
+
5
+ class ResponseModifier; end
6
+ class Repeat < ResponseModifier;
7
+ attr_accessor :times
8
+ def initialize(times)
9
+ @times = times
10
+ end
11
+ end
12
+ class Quit < ResponseModifier;
13
+ attr_accessor :message
14
+ def initialize(msg=nil)
15
+ @message = msg
16
+ end
17
+ end
18
+
19
+ end