rwb 0.2.1 → 0.2.2

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.
Files changed (3) hide show
  1. data/lib/rwb.rb +58 -415
  2. data/test/test_rwb.rb +121 -6
  3. metadata +24 -16
data/lib/rwb.rb CHANGED
@@ -1,172 +1,12 @@
1
1
  require 'net/http'
2
2
  require 'uri'
3
+ require 'rwb/url'
4
+ require 'rwb/results'
5
+ require 'rwb/warmup'
6
+ require 'rwb/report'
7
+ require 'open-uri'
3
8
 
4
9
  module RWB
5
-
6
- #
7
- # The Url class holds individual URLs for testing. +Url+s are defined by a
8
- # Fixnum representing the weight and a String representing the URL itself.
9
- # The weights are not required to be percentages.
10
- #
11
- # url = RWB::Url.new(20, "http://www.example.com")
12
- class Url
13
- attr_reader :weight
14
-
15
- def initialize(weight, url)
16
- @weight = weight
17
- @url = url
18
- end
19
-
20
- # to_url returns the URL to be requested as a string
21
- #
22
- # url.to_url # => "http://www.example.com"
23
- def to_url
24
- @url
25
- end
26
-
27
- #
28
- # to_base returns a string suitable for creating a +Regexp+ to match
29
- # against.
30
- #
31
- # url.to_base # => "http://www.example.com "
32
- #
33
- def to_base
34
- @url
35
- end
36
-
37
- def to_s
38
- @weight + " " + @url
39
- end
40
- end
41
-
42
- #
43
- # A UrlGroup is a collection of related URLs, for example the URL for a
44
- # search tool, with several search queries. The group is weighted (and
45
- # reported on) as a whole, but individual requests are made with random
46
- # elements from an array of extensions.
47
- #
48
- # urls = UrlGroup.new(20, "http://www.example.com/search?",
49
- # ["foo", "bar", "baz"])
50
- #
51
- class UrlGroup
52
- attr_reader :weight
53
-
54
- def initialize(weight, base, extension_array)
55
- @weight = weight.to_i
56
- @base = base.to_s
57
- @extension_array = extension_array
58
- end
59
-
60
- #
61
- # to_url returns a complete URL to be requested. It takes an optional
62
- # seed argument, which must be a Fixnum. If given, this will seed the
63
- # random selection of the extension.
64
- #
65
- # urls.to_url(1234) # => "http://www.example.com/search?baz"
66
- #
67
- def to_url(seed = nil)
68
- if seed
69
- srand = seed
70
- end
71
- @base + @extension_array[rand(@extension_array.length)]
72
- end
73
-
74
- #
75
- # to_base returns a String suitable for building a Regex to match
76
- # against. This String will not include any extensions.
77
- #
78
- # urls.to_base # => "http://www.example.com/search?"
79
- #
80
- def to_base
81
- @base
82
- end
83
-
84
- def to_s
85
- @weight + " " + @base
86
- end
87
-
88
- end
89
-
90
- class Result
91
- attr_reader :id, :timestamp, :url
92
- attr_reader :elapsed_time, :response_code
93
-
94
- def initialize(id, timestamp, url, elapsed_time, response_code)
95
- @id = id
96
- @timestamp = timestamp
97
- @url = url
98
- @elapsed_time = elapsed_time
99
- @response_code = response_code
100
- end
101
- end
102
-
103
- class RunResults
104
- attr_reader :results
105
-
106
- def initialize
107
- @results = Array.new
108
- end
109
-
110
- def add_result(result)
111
- @results.push(result)
112
- end
113
-
114
- def each_by_response_code(code)
115
- @results.each do |result|
116
- yield result if result.response_code.to_i == code.to_i
117
- end
118
- end
119
-
120
- def all_by_response_code(code)
121
- coded_results = Array.new
122
- @results.each do |result|
123
- if result.response_code.to_i == code.to_i
124
- coded_results.push(result)
125
- end
126
- end
127
- coded_results
128
- end
129
-
130
- def all_by_url(url)
131
- coded_results = Array.new
132
- @results.each do |result|
133
- if result.url.to_s == url.to_s
134
- coded_results.push(result)
135
- end
136
- end
137
- coded_results
138
- end
139
-
140
- def each_by_url(url)
141
- @results.each do |result|
142
- yield result if result.url.to_s == url.to_s
143
- end
144
- end
145
-
146
- def all_with_id
147
- id_results = Hash.new
148
- @results.each do |result|
149
- id_results[result.id] = result
150
- end
151
- id_results
152
- end
153
-
154
- def times_by_url(url = nil)
155
- coded_results = Array.new
156
- @results.each do |result|
157
- if url
158
- if result.url.to_s == url.to_s
159
- coded_results.push(result.elapsed_time)
160
- end
161
- else
162
- coded_results.push(result.elapsed_time)
163
- end
164
- end
165
- coded_results
166
- end
167
-
168
- end
169
-
170
10
  class Builder
171
11
  attr_reader :urls, :total_weight
172
12
 
@@ -178,14 +18,22 @@ module RWB
178
18
  end
179
19
  end
180
20
 
181
- def add_url(weight, url)
21
+ def add_url(weight, url, method='get')
182
22
  @total_weight += weight.to_i
183
- @urls[@total_weight] = Url.new(weight, url)
23
+ @urls[@total_weight] = Url.new(weight, url, method)
24
+ 7
184
25
  end
185
26
 
186
- def add_url_group(weight, base, extension_array)
27
+ def add_url_group(weight, base, extension_array, method='get')
187
28
  @total_weight += weight.to_i
188
- @urls[@total_weight] = UrlGroup.new(weight, base, extension_array)
29
+ @urls[@total_weight] = UrlGroup.new(weight, base,
30
+ extension_array, method)
31
+ end
32
+
33
+ def add_url_session(weight, session, name, method='get')
34
+ @total_weight += weight.to_i
35
+ @urls[@total_weight] = UrlSession.new(weight, session,
36
+ name, method)
189
37
  end
190
38
 
191
39
  def get_url(seed = nil)
@@ -212,11 +60,8 @@ module RWB
212
60
 
213
61
  def initialize(urls, max_runs=100, max_threads=10)
214
62
  @urls = urls
215
- @current_runs = 0
216
- @current_threads = 0
217
63
  @max_runs = max_runs
218
64
  @max_threads = max_threads
219
- @threads = []
220
65
  @results_mean = nil
221
66
  @results_std_dev = nil
222
67
  @sla_levels = [0.5, 0.9]
@@ -230,264 +75,62 @@ module RWB
230
75
  proxy_pass)
231
76
  end
232
77
 
233
- def warmup(num_runs = 1)
234
- $stderr.puts "warming up with #{num_runs} runs"
235
- for run in 1..num_runs
236
- $stderr.print "#{run} "
237
- @urls.urls.values.each do |url|
238
- url = URI.parse(url.to_url)
239
- @http.start(url.host, url.port) do |http|
240
- http.request( Net::HTTP::Get.new(url) )
241
- end
242
- end
243
- end
244
- $stderr.puts
245
- end
246
-
247
- def rand_warmup(num_requests)
248
- $stderr.puts "warming up with #{num_requests} requests"
249
- checkpoint = num_requests/10
250
- for run in 1..num_requests
251
- if run % checkpoint == 0
252
- $stderr.print "#{run} "
253
- end
254
- url = URI.parse(@urls.get_url.to_url)
255
- @http.start(url.host, url.port) do |http|
256
- http.request( Net::HTTP::Get.new(url) )
257
- end
258
- end
259
- $stderr.puts
260
- end
261
-
262
- def spec_warmup(urls, num_runs=1)
263
- $stderr.puts "warming up with #{num_runs} runs"
264
- for run in 1..num_runs
265
- $stderr.print "#{run} "
266
- urls.each do |url|
267
- url = URI.parse(url)
268
- @http.start(url.host, url.port) do |http|
269
- http.request( Net::HTTP::Get.new(url) )
270
- end
271
- end
272
- end
273
- $stderr.puts
274
- end
275
-
276
78
  def run
277
- printed_runs = Array.new
79
+ threads = []
80
+ @steps = []
81
+ 0.step(@max_runs, @max_runs/10) {|i| @steps.push(i) }
82
+ @requests = (1..@max_runs).to_a
278
83
  total_start_time = Time.now
279
- checkpoint = @max_runs/10
280
- while @current_runs < @max_runs
281
- build_thread
282
- if @current_runs % checkpoint == 0 &&
283
- ! ( printed_runs.include?(@current_runs))
284
- $stderr.puts "completed #{@current_runs} runs"
285
- printed_runs.push(@current_runs)
286
- end
84
+ for t in 1..@max_threads
85
+ threads << Thread.new { run_thread }
287
86
  end
288
-
289
- @threads.each { |th| th.join }
290
-
87
+ threads.each { |th| th.join }
291
88
  total_stop_time = Time.now
292
89
  @total_time = total_stop_time - total_start_time
293
90
  end
294
91
 
295
- def results_mean
296
- if @results_mean
297
- return @results_mean
298
- end
299
- @results_mean = @new_results.times_by_url.inject(0) do |sum, time|
300
- sum += time
301
- end
302
- @results_mean = @results_mean/@new_results.times_by_url.length
303
- make_milli(@results_mean)
304
- end
305
-
306
- def results_std_dev
307
- if @results_std_dev
308
- return @results_std_dev
309
- end
310
- unless @results_mean
311
- results_mean
312
- end
313
- @results_std_dev = @new_results.times_by_url.inject(0) do |std_dev, time|
314
- std_dev += (time - @results_mean) ** 2
315
- end
316
- @results_std_dev =
317
- Math.sqrt(@results_std_dev/@new_results.times_by_url.length)
318
- make_milli(@results_std_dev)
319
- end
320
-
321
- def adjust_scale(scale, max)
322
- if scale/2 > max
323
- scale = scale/2
324
- scale = adjust_scale(scale, max)
325
- end
326
- scale
327
- end
328
-
329
- def results_quartile(times, scale = nil)
330
- times = times.map { |t| make_milli(t) }
331
- size = results_mean + results_std_dev * 2
332
- min = times[0]
333
- first = times[(times.length*0.25).to_int - 1]
334
- second = times[(times.length*0.5).to_int - 1]
335
- third = times[(times.length*0.75).to_int - 1]
336
- max = times[-1]
337
-
338
- len = max.to_s.length
339
- unless scale
340
- scale = (max/((10**len).to_f)).ceil * (10**len)
341
- scale = adjust_scale(scale, max)
342
- end
343
- step = scale/50
344
- step = 1 if step == 0
345
- scale = 50 if scale < 50
346
- line = Array.new
347
- char = ' '
348
- for i in 0..49 do
349
- case i
350
- when max/step
351
- line[i] = ']'
352
- when third/step
353
- line[i] = '|'
354
- char = ' '
355
- when second/step
356
- line[i] = '+'
357
- when first/step
358
- line[i] = '|'
359
- char = '-'
360
- when min/step
361
- line[i] = '['
362
- when 0
363
- line[i] = ':'
364
- when 49
365
- line[i] = ':'
366
- else
367
- line[i] = char
92
+ def run_thread
93
+ while req_num = @requests.shift
94
+ run_test(req_num-1)
95
+ if @steps.include?(req_num)
96
+ puts "completed #{req_num} runs"
368
97
  end
369
98
  end
370
- return '0' + line.join('') + scale.to_s
371
- end
372
-
373
- def report_full(sla_levels = @sla_levels)
374
- report_header
375
- report_overall(sla_levels)
376
- report_urls(sla_levels)
377
- report_by_time(sla_levels)
378
99
  end
379
100
 
380
- def report_overall(sla_levels = @sla_levels)
381
- results = @new_results.times_by_url
101
+ def run_test(id)
102
+ request = @urls.get_url
103
+ # url = URI.parse request.to_url
104
+ base = request.to_base
105
+ time = nil
106
+ response_code = nil
382
107
 
383
- puts "Overall results:"
384
- print_times(results.sort, sla_levels)
385
- end
386
-
387
- def report_urls(sla_levels = @sla_levels)
388
- @urls.urls.keys.sort.each do |key|
389
- url = @urls.urls[key].to_base
390
- results = @new_results.times_by_url(url)
391
- puts "Results for #{url}:"
392
- if results.length > 0
393
- print_times(results.sort, sla_levels)
394
- else
395
- puts "no results for url"
108
+ # @http.start(url.host, url.port) do |http|
109
+ # start_time = Time.now
110
+ # response = http.request( Net::HTTP::Get.new(url) )
111
+ # stop_time = Time.now
112
+ # time = stop_time - start_time
113
+ # response_code = response.code
114
+ # end
115
+
116
+ start_time = Time.now
117
+ begin
118
+ open(request.to_url) do |page|
119
+ stop_time = Time.now
120
+ time = stop_time - start_time
121
+ response_code = page.status
396
122
  end
397
- end
398
- end
399
-
400
- def graph_quartiles_urls(scale = nil)
401
- @urls.urls.keys.sort.each do |key|
402
- url = @urls.urls[key].to_base
403
- results = @new_results.times_by_url(url)
404
- puts "#{url}:\n\t" + results_quartile(results.sort, scale)
405
- end
406
- end
407
-
408
- def graph_quartiles_overall
409
- results = @new_results.times_by_url
410
- puts results_quartile(results.sort)
411
- end
412
-
413
- def report_by_time(sla_levels = @sla_levels, granularity = 0.2)
414
- start = 0.0
415
- stop = granularity
416
- results = @new_results.times_by_url
417
- puts "Results by time:"
418
- while stop <= 1.0
419
- first = (results.length * start).to_i
420
- last = (results.length * stop).to_i
421
- these_results = results.slice(first..last)
422
- puts "results for requests #{first} - #{last}"
423
- print_times(these_results.sort, sla_levels)
424
- start = stop
425
- stop += granularity
426
- end
427
- end
428
-
429
- def print_times(results, levels)
430
- times = get_times(results, levels)
431
- puts "\tShortest time:\t#{times.shift} msecs"
432
- levels.each_with_index do |num, index|
433
- percent = num.to_f * 100.0
434
- puts "\t#{percent}%ile time:\t#{times[index]} msecs"
435
- end
436
- puts "\tLongest time:\t#{times[-1]} msecs"
437
- end
438
-
439
- def get_times(results, levels)
440
- times = Array.new
441
- times.push(make_milli(results[0]))
442
- levels.each do |num|
443
- times.push(make_milli(results[(results.length*num).to_i]))
444
- end
445
- times.push(make_milli(results[-1]))
446
- end
447
-
448
- def make_milli(num)
449
- (num * 1000).to_i
450
- end
451
-
452
- def report_header
453
- print <<EOF
454
- Concurrency Level: #{@max_threads}
455
- Total Requests: #{@max_runs}
456
- Total time for testing: #{@total_time} secs
457
- Requests per second: #{@max_runs/@total_time}
458
- Mean time per request: #{results_mean} msecs
459
- Standard deviation: #{results_std_dev}
460
- EOF
461
- end
462
-
463
- def build_thread
464
- if @current_threads < @max_threads
465
- @current_runs += 1
466
- @threads << Thread.new { run_test(@current_runs)}
467
- @current_threads += 1
468
- end
469
- end
470
-
471
- def run_test(id)
472
- request = @urls.get_url
473
- url = URI.parse request.to_url
474
- base = request.to_base
475
- start_time = Time.now
476
- response_code = nil
477
-
478
- @http.start(url.host, url.port) do |http|
479
- response = http.request( Net::HTTP::Get.new(url) )
480
- response_code = response.code
481
- end
482
-
123
+ rescue
483
124
  stop_time = Time.now
484
125
  time = stop_time - start_time
485
-
486
- result = Result.new(id, Time.now, base, time, response_code)
487
-
488
- @new_results.add_result(result)
489
- @current_threads -= 1
126
+ response_code = 404
490
127
  end
128
+
129
+
130
+ result = Result.new(id, Time.now, base, time, response_code)
131
+
132
+ @new_results.add_result(result)
491
133
  end
492
-
493
134
  end
135
+
136
+ end
@@ -27,6 +27,13 @@ module TestRWB
27
27
  assert_equal(20, @urls.get_url.weight)
28
28
  end
29
29
 
30
+ def test_add_session
31
+ session = ["http://localhost", "http://localhost/foo"]
32
+ @urls.add_url_session(10, session, 'session1')
33
+ assert_equal(1, @urls.urls.length)
34
+ assert_instance_of(RWB::UrlSession, @urls.get_url)
35
+ end
36
+
30
37
  def test_build_urls
31
38
  config = {
32
39
  "http://localhost" => 20,
@@ -39,9 +46,18 @@ module TestRWB
39
46
  @urls.add_url(20, "http://localhost")
40
47
  assert_equal("http://localhost", @urls.get_url.to_url)
41
48
  @urls.add_url(20, "http://localhost/foo")
42
- assert_equal("http://localhost", @urls.get_url(1).to_url)
43
- assert_equal("http://localhost", @urls.get_url(2).to_url)
44
- assert_equal("http://localhost/foo", @urls.get_url(3).to_url)
49
+ # due to a change in the randomization seed handling between
50
+ # Ruby 1.8.2 and 1.8.3, we need to check the RUBY_VERSION
51
+ # and behave accordingly
52
+ if RUBY_VERSION < "1.8.3"
53
+ assert_equal("http://localhost", @urls.get_url(1).to_url)
54
+ assert_equal("http://localhost", @urls.get_url(2).to_url)
55
+ assert_equal("http://localhost/foo", @urls.get_url(3).to_url)
56
+ else # FIX THESE
57
+ assert_equal("http://localhost/foo", @urls.get_url(1).to_url)
58
+ assert_equal("http://localhost", @urls.get_url(2).to_url)
59
+ assert_equal("http://localhost/foo", @urls.get_url(3).to_url)
60
+ end
45
61
  end
46
62
 
47
63
  def test_total_weight
@@ -137,6 +153,19 @@ module TestRWB
137
153
  assert_equal(100, test.adjust_scale(100,75))
138
154
  end
139
155
 
156
+ def test_get_times
157
+ test = RWB::Runner.new(nil)
158
+ assert_equal([100,300,400,500], test.get_times([0.1,0.2,0.3,
159
+ 0.4,0.5],
160
+ [0.5, 0.75]))
161
+ assert_equal([100,900,1000], test.get_times([0.1,0.2,0.3,0.4,
162
+ 0.5,0.6,0.7,0.8,
163
+ 0.9,1],[0.9]))
164
+ assert_equal([100,300,500], test.get_times([0.1,0.2,0.3,
165
+ 0.4,0.5],
166
+ [0.5,0.9]))
167
+ end
168
+
140
169
  def test_print_times
141
170
  #raise NotImplementedError, 'Need to write test_print_times'
142
171
  end
@@ -184,6 +213,27 @@ module TestRWB
184
213
  def test_weight
185
214
  assert_equal(10, @url.weight)
186
215
  end
216
+
217
+ def test_read_method
218
+ assert_equal('GET', @url.read_method())
219
+ assert_equal('GET', @url.read_method('get'))
220
+ assert_equal('GET', @url.read_method('foo'))
221
+ assert_equal('POST', @url.read_method('post'))
222
+ assert_equal('POST', @url.read_method('POST'))
223
+ end
224
+
225
+ def test_method
226
+ assert_equal('GET', @url.method)
227
+ url = RWB::Url.new(10,"http://www.example.com",'Post')
228
+ assert_equal('POST', url.method)
229
+ url = RWB::Url.new(10,"http://www.example.com",'GET')
230
+ assert_equal('GET', url.method)
231
+ end
232
+
233
+ def test_to_s
234
+ assert_equal("10 http://www.example.com", @url.to_s)
235
+ end
236
+
187
237
  end
188
238
 
189
239
  class TestUrlGroup < Test::Unit::TestCase
@@ -197,14 +247,79 @@ module TestRWB
197
247
  end
198
248
 
199
249
  def test_to_url
200
- assert_equal("http://www.example.com/search?foo", @url.to_url(1))
201
- assert_equal("http://www.example.com/search?baz", @url.to_url(2))
202
- assert_equal("http://www.example.com/search?baz", @url.to_url(3))
250
+ # due to changes in random seed handling between Ruby 1.8.2 and 1.8.3,
251
+ # we needdd to check the Ruby version and behave differently
252
+ if RUBY_VERSION < "1.8.3"
253
+ assert_equal("http://www.example.com/search?foo", @url.to_url(1))
254
+ assert_equal("http://www.example.com/search?baz", @url.to_url(2))
255
+ assert_equal("http://www.example.com/search?baz", @url.to_url(3))
256
+ else # FIX THESE
257
+ assert_equal("http://www.example.com/search?bar", @url.to_url(1))
258
+ assert_equal("http://www.example.com/search?foo", @url.to_url(2))
259
+ assert_equal("http://www.example.com/search?foo", @url.to_url(2))
260
+ end
261
+ end
262
+
263
+ def test_weight
264
+ assert_equal(10, @url.weight)
265
+ end
266
+
267
+ def test_method
268
+ assert_equal('GET', @url.method)
269
+ queries = ["foo", "bar", "baz"]
270
+ url = RWB::UrlGroup.new(10, "http://www.example.com/search?",
271
+ queries, 'post')
272
+ assert_equal('POST', url.method)
273
+ end
274
+
275
+ def test_to_s
276
+ assert_equal("10 http://www.example.com/search?", @url.to_s)
277
+ end
278
+
279
+ end
280
+
281
+
282
+ class TestUrlSession < Test::Unit::TestCase
283
+ def setup
284
+ session = ["http://www.example.com",
285
+ "http://www.example.com/browse",
286
+ "http://www.example.com/detail?foo",
287
+ "http://www.example.com/detail?bar",
288
+ "http://www.example.com/browse",
289
+ "http://www.example.com/logout"]
290
+ @url = RWB::UrlSession.new(10,session, 'Session 1')
291
+ end
292
+
293
+ def test_to_url
294
+ assert_equal("http://www.example.com",@url.to_url)
295
+ assert_equal("http://www.example.com/browse",@url.to_url)
296
+ assert_equal("http://www.example.com/detail?foo",@url.to_url)
297
+ assert_equal("http://www.example.com/detail?bar",@url.to_url)
298
+ assert_equal("http://www.example.com/browse",@url.to_url)
299
+ assert_equal("http://www.example.com/logout",@url.to_url)
300
+ assert_equal("http://www.example.com",@url.to_url)
301
+ assert_equal("http://www.example.com/browse",@url.to_url)
302
+ end
303
+
304
+ def test_to_base
305
+ assert_equal('Session 1', @url.to_base)
203
306
  end
204
307
 
205
308
  def test_weight
206
309
  assert_equal(10, @url.weight)
207
310
  end
311
+
312
+ def test_method
313
+ assert_equal('GET', @url.method)
314
+ session = ["foo", "bar", "baz"]
315
+ url = RWB::UrlSession.new(10, session, 'Session 2', 'post')
316
+ assert_equal('POST', url.method)
317
+ end
318
+
319
+ def test_to_s
320
+ assert_equal("10 Session 1", @url.to_s)
321
+ end
322
+
208
323
  end
209
324
 
210
325
  class TestResult < Test::Unit::TestCase
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.10
2
+ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: rwb
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.1
7
- date: 2005-12-02
8
- summary: "Ruby Web Bench, a web performance and load testing framework"
6
+ version: 0.2.2
7
+ date: 2007-01-31 00:00:00 -07:00
8
+ summary: Ruby Web Bench, a web performance and load testing framework
9
9
  require_paths:
10
- - lib
10
+ - lib
11
11
  email: pat.eyler@gmail.com
12
12
  homepage: http://rubyforge.org/projects/rwb
13
13
  rubyforge_project:
@@ -18,24 +18,32 @@ bindir: bin
18
18
  has_rdoc: "true"
19
19
  required_ruby_version: !ruby/object:Gem::Version::Requirement
20
20
  requirements:
21
- -
22
- - ">"
23
- - !ruby/object:Gem::Version
24
- version: 0.0.0
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
25
24
  version:
26
25
  platform: ruby
26
+ signing_key:
27
+ cert_chain:
27
28
  authors:
28
- - Pat Eyler
29
+ - Pat Eyler
29
30
  files:
30
- - NEWS
31
- - README
32
- - TODO
33
- - lib/rwb.rb
34
- - test/test_rwb.rb
31
+ - NEWS
32
+ - README
33
+ - TODO
34
+ - lib/rwb.rb
35
+ - test/test_rwb.rb
35
36
  test_files: []
37
+
36
38
  rdoc_options: []
39
+
37
40
  extra_rdoc_files: []
41
+
38
42
  executables: []
43
+
39
44
  extensions: []
45
+
40
46
  requirements: []
41
- dependencies: []
47
+
48
+ dependencies: []
49
+