stella 0.7.0.006 → 0.7.0.012

Sign up to get free protection for your applications and to get access to all the features.
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