dbrady-tourbus 0.0.7 → 0.0.8

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.
@@ -6,6 +6,7 @@ Flexible and scalable website testing tool.
6
6
 
7
7
  * David Brady -- david.brady@leadmediapartners.com
8
8
  * Tim Harper -- tim.harper@leadmediapartners.com
9
+ * James Britt -- http://github.com/jamesbritt
9
10
 
10
11
  == General Info
11
12
 
@@ -54,22 +55,48 @@ better name, these are called Tours.
54
55
 
55
56
  === Example TourBus Run
56
57
 
57
- tourbus -c 2 -n 3 simple
58
+ You want to invoke +tourbus+ from the parent directory of the @tours/@ folder.
58
59
 
59
- This will create 2 concurrent Tour runners, each of which will run all
60
+ For example, if you have this project tree ...
61
+
62
+ `-- contact_app
63
+ |-- README.rdoc
64
+ |-- contact_app.rb
65
+ `-- tours
66
+ |-- simple.rb
67
+ `-- tourbus.yml
68
+
69
+ ... then you execute +tourbus+ from the +contact_app/+ directory.
70
+
71
+ tourbus -c 2 -n 3 simple
72
+
73
+ That will run the +simple.rb+ tour file.
74
+
75
+ It will create 2 concurrent Tour runners, each of which will run all
60
76
  of the methods in Simple three times.
61
77
 
62
78
  * You can specify multiple tours.
63
79
 
80
+ tourbus -c 2 -n 3 simple1 simple2 simple3
81
+
64
82
  * If you don't specify a tour, all tours in ./tours will be run.
65
83
 
66
84
  * tourbus --help will give you more information.
67
85
 
86
+ * You can run tours and filter given tests.
87
+
88
+ tourbus -c 2 -n 3 simple -t test_login,test_logout
89
+
90
+ Note that if you specify multiple tours and filter tests, the filtered
91
+ tests will be run on all tours specified. If you do not specify a
92
+ tour, the filtered tests will be run on all tours found in the
93
+ +./tours+ folder.
94
+
68
95
  === Example TourWatch Run
69
96
 
70
97
  On the webserver, you can type
71
98
 
72
- tourwatch -c 4
99
+ tourwatch -c 4
73
100
 
74
101
  To begin running tourwatch. It's basically a stripped-down version of
75
102
  top with cheesy text graphs. (TourWatch's development cycles were
@@ -82,7 +109,7 @@ included in the 2 days for TourBus.)
82
109
 
83
110
  * You can choose which processes to watch by passing a csv to -p:
84
111
 
85
- tourwatch -p ruby,mongrel
112
+ tourwatch -p ruby,mongrel
86
113
 
87
114
  Each process name is a partial regexp, so the above would match
88
115
  mongrel AND mongrel_rails, etc.
@@ -110,7 +137,8 @@ duplications, oversights, and kludges.
110
137
  didn't put tests on it. I haven't put tests on it yet because I'm
111
138
  not sure how to go about it. Instead of exercising a web app with a
112
139
  test browser, we need to exercise a test browser with... um... a web
113
- app?
140
+ app? (dbrady notes: Now that we have a contact_app, we could try
141
+ writing some specs and features to run tourbus against it.)
114
142
 
115
143
  * Web-Sickle is another internal app, written by Tim Harper, that
116
144
  works "well enough". Until I open-sourced this project, it was a
@@ -118,12 +146,31 @@ duplications, oversights, and kludges.
118
146
  from WebSickle itself, so there's a lot of code in Runner that
119
147
  really belongs in WebSickle.
120
148
 
121
- * Documentation is horrible.
149
+ * Documentation is <strike>horrible</strike> merely quite bad.
122
150
 
123
151
  * There's not much in the way of examples, either. When I removed all
124
152
  the LMP-specific code, all of the examples went with it. Sorry about
125
153
  that. Coming soon.
126
154
 
155
+ == Feature Requests (How You Can Help!)
156
+
157
+ * I'd like to beef up the example contact app to show more of TourBus
158
+ than the simplest possible path. Adding in another page or two, and
159
+ then adding an additional tour or two would make it more apparent to
160
+ new users that you can do things like run multiple tours at once.
161
+ Also, having more than one test_ method in simple.rb would let us
162
+ demonstrate test filtering as well. (Be aware that at present
163
+ [2009-04-17], webrat still does not play well with Sinatra sessions
164
+ so there would be complications. dbrady's fork of webrat combines
165
+ jferris' fix with the latest webrat, and will be maintained until
166
+ main webrat includes the feature. That fork is at
167
+ http://github.com/dbrady/webrat)
168
+
169
+ * I'd like to remove WebSickle and replace it with Webrat. There is a
170
+ webrat branch on the main fork (http://github.com/dbrady/tourbus)
171
+ that is 90% complete. Once that's done we can start massaging the
172
+ API to be a little more friendly.
173
+
127
174
  == Credits
128
175
 
129
176
  * Tim Harper camped at my place for a day fixing bugs in WebSickle as
@@ -133,6 +180,9 @@ duplications, oversights, and kludges.
133
180
  source it. How much do they rock? All the way to 11, that's how much
134
181
  they rock.
135
182
 
183
+ * James Britt jumped on this and revived it as it was gathering dust.
184
+ Thanks!
185
+
136
186
  == License
137
187
 
138
188
  MIT. See the license file.
data/bin/tourbus CHANGED
@@ -7,7 +7,7 @@ require_all_files_in_folder 'tours'
7
7
  config_file = ["./tourbus.yml", "./tours/tourbus.yml", "./config/tourbus.yml", "~/tourbus.yml"].map {|p| File.expand_path(p)}.find {|p| File.exists? p}
8
8
  config = config_file ? YAML::load_file(config_file).symbolize_keys : {}
9
9
 
10
- config_map = { :host => :to_s, :concurrency => :to_i, :number => :to_i, :rand => :to_i }
10
+ config_map = { :host => :to_s, :concurrency => :to_i, :number => :to_i, :rand => :to_i, :tests => :to_s }
11
11
  config_map.each {|key,conv| config[key] = config[key].send(conv) if config.key? key }
12
12
 
13
13
  # defaults
@@ -22,10 +22,11 @@ opts = Trollop.options do
22
22
  opt :number, "Number of times to run the tour (in each concurrent step, so -c 10 -n 10 will run the tour 100 times)", :type => :integer, :default => config[:number]
23
23
  opt :list, "List tours and runs available. If tours or runs are included, filters the list", :type => :boolean, :default => nil
24
24
  opt :rand, "Random seed", :type => :integer, :default => config[:rand]
25
+ opt :tests, "Test name(s) filter. The name of the test to run (use --list to see the test names). Use commas, no spaces, for mulitple names", :type => :string, :default => nil
25
26
  end
26
27
 
27
28
  tours = if ARGV.empty?
28
- Dir[File.join('.', 'tours', '*.rb')].map {|f| File.basename(f, ".rb")}
29
+ Tour.tours
29
30
  else
30
31
  ARGV
31
32
  end
@@ -38,6 +39,8 @@ if opts[:list]
38
39
  puts Tour.tests(tour).map {|test| " #{test}"}
39
40
  end
40
41
  else
41
- TourBus.new(opts[:host], opts[:concurrency], opts[:number], tours).run
42
+ opts[:tests] = opts[:tests].split(',')
43
+
44
+ TourBus.new(opts[:host], opts[:concurrency], opts[:number], tours, opts[:tests]).run
42
45
  end
43
46
 
data/lib/runner.rb CHANGED
@@ -4,23 +4,31 @@ require 'common'
4
4
  class Runner
5
5
  attr_reader :host, :tours, :number, :runner_type, :runner_id
6
6
 
7
- def initialize(host, tours, number, runner_id)
8
- @host, @tours, @number, @runner_id = host, tours, number, runner_id
7
+ def initialize(host, tours, number, runner_id, test_list)
8
+ @host, @tours, @number, @runner_id, @test_list = host, tours, number, runner_id, test_list
9
9
  @runner_type = self.send(:class).to_s
10
10
  log("Ready to run #{@runner_type}")
11
11
  end
12
12
 
13
13
  # Dispatches to subclass run method
14
- def run_tours
15
- runs,passes,fails,errors = 0,0,0,0
14
+ def run_tours test_list = []
15
+ log "Filtering on tests #{test_list.join(', ')}" unless test_list.to_a.empty?
16
+ tours,tests,passes,fails,errors = 0,0,0,0,0
16
17
  1.upto(number) do |num|
17
18
  log("Starting #{@runner_type} run #{num}/#{number}")
18
- @tours.each do |tour|
19
- runs += 1
20
- tour = Tour.make_tour(tour,@host,@tours,@number,@runner_id)
19
+ @tours.each do |tour_name|
20
+
21
+ log("Starting run #{number} of Tour #{tour_name}")
22
+ tours += 1
23
+ tour = Tour.make_tour(tour_name,@host,@tours,@number,@runner_id)
21
24
  tour.tests.each do |test|
25
+
26
+ next if !test_list.empty? && !test_list.include?(test.to_s)
27
+
22
28
  begin
29
+ tests += 1
23
30
  tour.run_test test
31
+ passes += 1
24
32
  rescue TourBusException, WebsickleException => e
25
33
  log("********** FAILURE IN RUN! **********")
26
34
  log e.message
@@ -38,14 +46,13 @@ class Runner
38
46
  end
39
47
  errors += 1
40
48
  end
41
-
49
+ log("Finished run #{number} of Tour #{tour_name}")
42
50
  end
43
51
  end
44
- passes += 1
45
52
  log("Finished #{@runner_type} run #{num}/#{number}")
46
53
  end
47
- log("Finished all #{@runner_type} runs.")
48
- [runs,passes,fails,errors]
54
+ log("Finished all #{@runner_type} tours.")
55
+ [tours,tests,passes,fails,errors]
49
56
  end
50
57
 
51
58
  protected
data/lib/tour.rb CHANGED
@@ -117,5 +117,22 @@ class Tour
117
117
  end
118
118
  log "Page body ok (matches #{pattern})"
119
119
  end
120
+
121
+
122
+
123
+ # True if page does not contain (or match) the given string (or regexp)
124
+ #
125
+ # TODO: Refactor me--these were separated out back when Websickle
126
+ # was a shared submodule and we couldn't pollute it. Now that it's
127
+ # frozen these probably belong there.
128
+ def assert_page_body_does_not_contain(pattern)
129
+ case pattern
130
+ when String:
131
+ raise WebsickleException, "Expected page body to not contain String '#{pattern}' but it did. It was #{page.body}" if page.body.to_s.index(pattern)
132
+ when Regexp:
133
+ raise WebsickleException, "Expected page body to not match Regexp '#{pattern}' but it did. It was #{page.body}" if page.body.to_s =~ pattern
134
+ end
135
+ log "Page body ok (does not match #{pattern})"
136
+ end
120
137
  end
121
138
 
data/lib/tour_bus.rb CHANGED
@@ -1,12 +1,12 @@
1
1
  require 'benchmark'
2
2
 
3
3
  class TourBus < Monitor
4
- attr_reader :host, :concurrency, :number, :tours, :runs, :passes, :fails, :errors, :benchmarks
4
+ attr_reader :host, :concurrency, :number, :tours, :runs, :tests, :passes, :fails, :errors, :benchmarks
5
5
 
6
- def initialize(host="localhost", concurrency=1, number=1, tours=[])
7
- @host, @concurrency, @number, @tours = host, concurrency, number, tours
6
+ def initialize(host="localhost", concurrency=1, number=1, tours=[], test_list=nil)
7
+ @host, @concurrency, @number, @tours, @test_list = host, concurrency, number, tours, test_list
8
8
  @runner_id = 0
9
- @runs, @passes, @fails, @errors = 0,0,0,0
9
+ @runs, @tests, @passes, @fails, @errors = 0,0,0,0,0
10
10
  super()
11
11
  end
12
12
 
@@ -16,9 +16,10 @@ class TourBus < Monitor
16
16
  end
17
17
  end
18
18
 
19
- def update_stats(runs,passes,fails,errors)
19
+ def update_stats(runs,tests,passes,fails,errors)
20
20
  synchronize do
21
21
  @runs += runs
22
+ @tests += tests
22
23
  @passes += passes
23
24
  @fails += fails
24
25
  @errors += errors
@@ -48,11 +49,11 @@ class TourBus < Monitor
48
49
  log "Starting #{tour_name}"
49
50
  threads << Thread.new do
50
51
  runner_id = next_runner_id
51
- runs,passes,fails,errors,start = 0,0,0,0,Time.now.to_f
52
+ runs,tests,passes,fails,errors,start = 0,0,0,0,0,Time.now.to_f
52
53
  bm = Benchmark.measure do
53
- runner = Runner.new(@host, @tours, @number, runner_id)
54
- runs,passes,fails,errors = runner.run_tours
55
- update_stats runs, passes, fails, errors
54
+ runner = Runner.new(@host, @tours, @number, runner_id, @test_list)
55
+ runs,tests,passes,fails,errors = runner.run_tours(@test_list)
56
+ update_stats runs, tests, passes, fails, errors
56
57
  end
57
58
  log "Runner Finished!"
58
59
  log "Runner finished in %0.3f seconds" % (Time.now.to_f - start)
@@ -66,12 +67,13 @@ class TourBus < Monitor
66
67
  log '-' * 80
67
68
  log tour_name
68
69
  log "All Runners finished."
69
- log "Total Runs: #{@runs}"
70
+ log "Total Tours: #{@runs}"
71
+ log "Total Tests: #{@tests}"
70
72
  log "Total Passes: #{@passes}"
71
73
  log "Total Fails: #{@fails}"
72
74
  log "Total Errors: #{@errors}"
73
75
  log "Elapsed Time: #{finished - started}"
74
- log "Speed: %5.3f v/s" % (@runs / (finished-started))
76
+ log "Speed: %5.3f tours/sec" % (@runs / (finished-started))
75
77
  log '-' * 80
76
78
  if @fails > 0 || @errors > 0
77
79
  log '********************************************************************************'
metadata CHANGED
@@ -1,15 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dbrady-tourbus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Brady
8
+ - James Britt
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
12
 
12
- date: 2009-01-10 00:00:00 -08:00
13
+ date: 2009-04-17 00:00:00 -07:00
13
14
  default_executable:
14
15
  dependencies:
15
16
  - !ruby/object:Gem::Dependency
@@ -52,7 +53,7 @@ dependencies:
52
53
  - !ruby/object:Gem::Version
53
54
  version: "0"
54
55
  version:
55
- description: TourBus web stress-testing tool
56
+ description: TourBus, a web stress-testing tool that combines complex 'tour' definitions with scalable concurrent testing
56
57
  email: github@shinybit.com
57
58
  executables:
58
59
  - tourbus
@@ -60,7 +61,7 @@ executables:
60
61
  extensions: []
61
62
 
62
63
  extra_rdoc_files:
63
- - README.txt
64
+ - README.rdoc
64
65
  - MIT-LICENSE
65
66
  - examples/contact_app/README.rdoc
66
67
  files:
@@ -86,16 +87,16 @@ files:
86
87
  - lib/web-sickle/spec/spec_helper.rb
87
88
  - lib/web-sickle/spec/spec_helpers/mechanize_mock_helper.rb
88
89
  - lib/web-sickle/spec/web_sickle_spec.rb
89
- - README.txt
90
+ - README.rdoc
90
91
  - MIT-LICENSE
91
92
  has_rdoc: true
92
- homepage: http://github.com/dbrady/tourbus
93
+ homepage: http://github.com/jamesbritt/tourbus/
93
94
  post_install_message:
94
95
  rdoc_options:
95
96
  - --line-numbers
96
97
  - --inline-source
97
98
  - --main
98
- - README.txt
99
+ - README.rdoc
99
100
  - --title
100
101
  - Tourbus - Web Stress Testing in Ruby
101
102
  require_paths: