tarantula-rails3 0.3.3
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/CHANGELOG +49 -0
- data/LICENSE +20 -0
- data/README.rdoc +161 -0
- data/Rakefile +83 -0
- data/VERSION.yml +4 -0
- data/examples/example_helper.rb +57 -0
- data/examples/relevance/core_extensions/ellipsize_example.rb +19 -0
- data/examples/relevance/core_extensions/file_example.rb +8 -0
- data/examples/relevance/core_extensions/response_example.rb +29 -0
- data/examples/relevance/core_extensions/test_case_example.rb +20 -0
- data/examples/relevance/tarantula/attack_handler_example.rb +29 -0
- data/examples/relevance/tarantula/basic_attack_example.rb +12 -0
- data/examples/relevance/tarantula/crawler_example.rb +375 -0
- data/examples/relevance/tarantula/form_example.rb +50 -0
- data/examples/relevance/tarantula/form_submission_example.rb +171 -0
- data/examples/relevance/tarantula/html_document_handler_example.rb +43 -0
- data/examples/relevance/tarantula/html_report_helper_example.rb +46 -0
- data/examples/relevance/tarantula/html_reporter_example.rb +82 -0
- data/examples/relevance/tarantula/invalid_html_handler_example.rb +33 -0
- data/examples/relevance/tarantula/io_reporter_example.rb +11 -0
- data/examples/relevance/tarantula/link_example.rb +84 -0
- data/examples/relevance/tarantula/log_grabber_example.rb +26 -0
- data/examples/relevance/tarantula/rails_integration_proxy_example.rb +88 -0
- data/examples/relevance/tarantula/result_example.rb +85 -0
- data/examples/relevance/tarantula/tidy_handler_example.rb +58 -0
- data/examples/relevance/tarantula/transform_example.rb +20 -0
- data/examples/relevance/tarantula_example.rb +23 -0
- data/laf/images/header_bg.jpg +0 -0
- data/laf/images/logo.png +0 -0
- data/laf/images/tagline.png +0 -0
- data/laf/javascripts/jquery-1.2.3.js +3408 -0
- data/laf/javascripts/jquery-ui-tabs.js +890 -0
- data/laf/javascripts/jquery.tablesorter.js +861 -0
- data/laf/javascripts/tarantula.js +10 -0
- data/laf/stylesheets/tarantula.css +346 -0
- data/lib/relevance/core_extensions/ellipsize.rb +34 -0
- data/lib/relevance/core_extensions/file.rb +9 -0
- data/lib/relevance/core_extensions/metaclass.rb +78 -0
- data/lib/relevance/core_extensions/response.rb +9 -0
- data/lib/relevance/core_extensions/string_chars_fix.rb +11 -0
- data/lib/relevance/core_extensions/test_case.rb +19 -0
- data/lib/relevance/tarantula.rb +58 -0
- data/lib/relevance/tarantula/attack.rb +18 -0
- data/lib/relevance/tarantula/attack_handler.rb +37 -0
- data/lib/relevance/tarantula/basic_attack.rb +40 -0
- data/lib/relevance/tarantula/crawler.rb +254 -0
- data/lib/relevance/tarantula/detail.html.erb +81 -0
- data/lib/relevance/tarantula/form.rb +23 -0
- data/lib/relevance/tarantula/form_submission.rb +88 -0
- data/lib/relevance/tarantula/html_document_handler.rb +36 -0
- data/lib/relevance/tarantula/html_report_helper.rb +39 -0
- data/lib/relevance/tarantula/html_reporter.rb +105 -0
- data/lib/relevance/tarantula/index.html.erb +37 -0
- data/lib/relevance/tarantula/invalid_html_handler.rb +18 -0
- data/lib/relevance/tarantula/io_reporter.rb +34 -0
- data/lib/relevance/tarantula/link.rb +94 -0
- data/lib/relevance/tarantula/log_grabber.rb +16 -0
- data/lib/relevance/tarantula/rails_integration_proxy.rb +68 -0
- data/lib/relevance/tarantula/recording.rb +12 -0
- data/lib/relevance/tarantula/response.rb +13 -0
- data/lib/relevance/tarantula/result.rb +77 -0
- data/lib/relevance/tarantula/test_report.html.erb +32 -0
- data/lib/relevance/tarantula/tidy_handler.rb +32 -0
- data/lib/relevance/tarantula/transform.rb +17 -0
- data/lib/relevance/tasks/tarantula_tasks.rake +42 -0
- data/lib/tarantula-rails3.rb +9 -0
- data/template/tarantula_test.rb +22 -0
- metadata +164 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
3
|
+
|
4
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
5
|
+
<head>
|
6
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
7
|
+
<link rel="stylesheet" href="../stylesheets/tarantula.css" type="text/css" media="screen" title="no title" charset="utf-8" />
|
8
|
+
<title>Detail</title>
|
9
|
+
</head>
|
10
|
+
<body>
|
11
|
+
<div id="container">
|
12
|
+
<div id="header">
|
13
|
+
<h1>Tarantula by Relevance</h1>
|
14
|
+
<h2>Eight legs, two fangs ... and an attitude</h2>
|
15
|
+
<p>Tarantula is an open source tool for testing Rails web
|
16
|
+
applications. Tarantula is developed by <a href="http://thinkrelevance.com">Relevance, Inc.</a>
|
17
|
+
and lives at <a href="http://github.com/relevance/tarantula">http://github.com/relevance/tarantula</a>.</p>
|
18
|
+
<hr/>
|
19
|
+
</div>
|
20
|
+
<div id="page">
|
21
|
+
<ul class="tabs">
|
22
|
+
<li><a href="../index.html">« Back</a></li>
|
23
|
+
<li><a href="#fragment-1" class="active">Body</a></li>
|
24
|
+
<li><a href="#fragment-2" class="active">Log</a></li>
|
25
|
+
</ul>
|
26
|
+
|
27
|
+
<div id="report">
|
28
|
+
<h3>Detail of <%= result.short_description %> <em>Generated on <%= Time.now %></em></h3>
|
29
|
+
<p><b>Resource</b> <a href="<%= result.full_url %>"><%= result.full_url %></a></p>
|
30
|
+
<p><b>Response</b> <span class="r<%= result.code.first %>"><%= result.code %></span></p>
|
31
|
+
<p><b>Referrer</b> <%= result.referrer || "" %></p>
|
32
|
+
|
33
|
+
<table class="output">
|
34
|
+
<tbody>
|
35
|
+
<tr>
|
36
|
+
<th colspan="2"># Data</th>
|
37
|
+
</tr>
|
38
|
+
<% if result.data %>
|
39
|
+
<%= result.wrap_in_line_number_table_row(result.data) %>
|
40
|
+
<% else %>
|
41
|
+
<tr>
|
42
|
+
<td colspan="2">No Data</td>
|
43
|
+
</tr>
|
44
|
+
<% end %>
|
45
|
+
</tbody>
|
46
|
+
</table>
|
47
|
+
|
48
|
+
<table class="output" id="fragment-1">
|
49
|
+
<tbody>
|
50
|
+
<tr>
|
51
|
+
<th colspan="2"># Body</th>
|
52
|
+
</tr>
|
53
|
+
<% if result.body %>
|
54
|
+
<%= result.wrap_in_line_number_table_row(result.body) %>
|
55
|
+
<% else %>
|
56
|
+
<tr>
|
57
|
+
<td colspan="2">No Body</td>
|
58
|
+
</tr>
|
59
|
+
<% end %>
|
60
|
+
</tbody>
|
61
|
+
</table>
|
62
|
+
|
63
|
+
<table class="output" id="fragment-2">
|
64
|
+
<tbody>
|
65
|
+
<tr>
|
66
|
+
<th colspan="2"># Log</th>
|
67
|
+
</tr>
|
68
|
+
<% if result.log %>
|
69
|
+
<%= result.wrap_in_line_number_table_row(result.log) {|line| wrap_stack_trace_line(line)} %>
|
70
|
+
<% else %>
|
71
|
+
<tr>
|
72
|
+
<td colspan="2">No Log</td>
|
73
|
+
</tr>
|
74
|
+
<% end %>
|
75
|
+
</tbody>
|
76
|
+
</table>
|
77
|
+
</div>
|
78
|
+
</div>
|
79
|
+
</div>
|
80
|
+
</body>
|
81
|
+
</html>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Relevance::Tarantula::Form
|
2
|
+
extend Forwardable
|
3
|
+
def_delegators("@tag", :search)
|
4
|
+
|
5
|
+
attr_accessor :crawler, :referrer
|
6
|
+
|
7
|
+
def initialize(tag, crawler, referrer)
|
8
|
+
@tag, @crawler, @referrer = tag, crawler, referrer
|
9
|
+
end
|
10
|
+
|
11
|
+
def action
|
12
|
+
@tag['action'].downcase
|
13
|
+
end
|
14
|
+
|
15
|
+
def method
|
16
|
+
(rails_method_hack or @tag['method'] or 'get').downcase
|
17
|
+
end
|
18
|
+
|
19
|
+
def rails_method_hack
|
20
|
+
(tag = @tag.at('input[@name="_method"]')) && tag["value"]
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
class Relevance::Tarantula::FormSubmission
|
2
|
+
include Relevance::Tarantula
|
3
|
+
attr_accessor :method, :action, :data, :attack, :form
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def attacks
|
7
|
+
# normalize from hash input to Attack
|
8
|
+
@attacks = @attacks.map do |val|
|
9
|
+
Hash === val ? Relevance::Tarantula::Attack.new(val) : val
|
10
|
+
end
|
11
|
+
@attacks
|
12
|
+
end
|
13
|
+
def attacks=(atts)
|
14
|
+
# normalize from hash input to Attack
|
15
|
+
@attacks = atts.map do |val|
|
16
|
+
Hash === val ? Relevance::Tarantula::Attack.new(val) : val
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
@attacks = [Relevance::Tarantula::BasicAttack.new]
|
21
|
+
|
22
|
+
def initialize(form, attack = Relevance::Tarantula::BasicAttack.new)
|
23
|
+
@form = form
|
24
|
+
@method = form.method
|
25
|
+
@action = form.action
|
26
|
+
@attack = attack
|
27
|
+
@data = mutate_selects(form).merge(mutate_text_areas(form)).merge(mutate_inputs(form))
|
28
|
+
end
|
29
|
+
|
30
|
+
def crawl
|
31
|
+
begin
|
32
|
+
response = form.crawler.submit(method, action, data)
|
33
|
+
log "Response #{response.code} for #{self}"
|
34
|
+
rescue ActiveRecord::RecordNotFound => e
|
35
|
+
log "Skipping #{action}, presumed ok that record is missing"
|
36
|
+
response = Relevance::Tarantula::Response.new(:code => "404", :body => e.message, :content_type => "text/plain")
|
37
|
+
end
|
38
|
+
form.crawler.handle_form_results(self, response)
|
39
|
+
response
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.mutate(form)
|
43
|
+
attacks.map{|attack| new(form, attack)} if attacks
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
"#{action} #{method} #{data.inspect} #{attack.inspect}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# a form's signature is what makes it unique (e.g. action + fields)
|
51
|
+
# used to keep track of which forms we have submitted already
|
52
|
+
def signature
|
53
|
+
[action, data.keys.sort, attack.name]
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_random_data_for(form, tag_selector)
|
57
|
+
form.search(tag_selector).inject({}) do |form_args, input|
|
58
|
+
# TODO: test
|
59
|
+
form_args[input['name']] = random_data(input) if input['name']
|
60
|
+
form_args
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def mutate_inputs(form)
|
65
|
+
create_random_data_for(form, 'input')
|
66
|
+
end
|
67
|
+
|
68
|
+
def mutate_text_areas(form)
|
69
|
+
create_random_data_for(form, 'textarea')
|
70
|
+
end
|
71
|
+
|
72
|
+
def mutate_selects(form)
|
73
|
+
form.search('select').inject({}) do |form_args, select|
|
74
|
+
options = select.search('option')
|
75
|
+
option = options.rand
|
76
|
+
form_args[select['name']] = option['value']
|
77
|
+
form_args
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def random_data(input)
|
82
|
+
case input['name']
|
83
|
+
when /^_method$/ then input['value']
|
84
|
+
else
|
85
|
+
attack.input(input)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'hpricot'
|
2
|
+
|
3
|
+
class Relevance::Tarantula::HtmlDocumentHandler
|
4
|
+
extend Forwardable
|
5
|
+
def_delegators("@crawler", :queue_link, :queue_form)
|
6
|
+
|
7
|
+
def initialize(crawler)
|
8
|
+
@crawler = crawler
|
9
|
+
end
|
10
|
+
# HTML::Document shouts to stderr when it sees ugly HTML
|
11
|
+
# We don't want this -- the InvalidHtmlHandler will deal with it
|
12
|
+
def html_doc_without_stderr_noise(html)
|
13
|
+
body = nil
|
14
|
+
Recording.stderr do
|
15
|
+
body = Hpricot html
|
16
|
+
end
|
17
|
+
body
|
18
|
+
end
|
19
|
+
def handle(result)
|
20
|
+
response = result.response
|
21
|
+
url = result.url
|
22
|
+
return unless response.html?
|
23
|
+
body = html_doc_without_stderr_noise(response.body)
|
24
|
+
body.search('a').each do |tag|
|
25
|
+
queue_link(tag, url)
|
26
|
+
end
|
27
|
+
body.search('link').each do |tag|
|
28
|
+
queue_link(tag, url)
|
29
|
+
end
|
30
|
+
body.search('form').each do |form|
|
31
|
+
form['action'] = url unless form['action']
|
32
|
+
queue_form(form, url)
|
33
|
+
end
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "erb"
|
2
|
+
module Relevance::Tarantula::HtmlReportHelper
|
3
|
+
include ERB::Util
|
4
|
+
include Relevance::Tarantula
|
5
|
+
def wrap_in_line_number_table_row(text, &blk)
|
6
|
+
x = Builder::XmlMarkup.new
|
7
|
+
|
8
|
+
x.tr do
|
9
|
+
lines = text.split("\n")
|
10
|
+
x.td(:class => "numbers") do
|
11
|
+
lines.size.times do |index|
|
12
|
+
x.span(index+1, :class => "line number")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
x.td(:class => "lines") do
|
16
|
+
lines.each do |line|
|
17
|
+
x.span(line, :class => "line")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
x.target!
|
23
|
+
end
|
24
|
+
|
25
|
+
def textmate_url(file, line_no)
|
26
|
+
"txmt://open?url=file://#{File.expand_path(File.join(rails_root,file))}&line_no=#{line_no}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def wrap_stack_trace_line(text)
|
30
|
+
if text =~ %r{^\s*(/[^:]+):(\d+):([^:]+)$}
|
31
|
+
file = h($1) # .to_s_xss_protected
|
32
|
+
line_number = $2
|
33
|
+
message = h($3) # .to_s_xss_protected
|
34
|
+
"<a href='#{textmate_url(file, line_number)}'>#{file}:#{line_number}</a>:#{message}" # .mark_as_xss_protected
|
35
|
+
else
|
36
|
+
h(text) # .to_s_xss_protected
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
class Relevance::Tarantula::HtmlReporter
|
2
|
+
|
3
|
+
include Relevance::Tarantula
|
4
|
+
attr_accessor :basedir, :results
|
5
|
+
delegate :successes, :failures, :to => :results
|
6
|
+
|
7
|
+
HtmlResultOverview = Struct.new(:code, :url, :description, :method, :referrer, :file_name)
|
8
|
+
|
9
|
+
def initialize(basedir)
|
10
|
+
@basedir = basedir
|
11
|
+
@results = Struct.new(:successes, :failures).new([], [])
|
12
|
+
FileUtils.mkdir_p(@basedir)
|
13
|
+
end
|
14
|
+
|
15
|
+
def report(result)
|
16
|
+
return if result.nil?
|
17
|
+
|
18
|
+
create_detail_report(result)
|
19
|
+
|
20
|
+
collection = result.success ? results.successes : results.failures
|
21
|
+
collection << HtmlResultOverview.new(
|
22
|
+
result.code, result.url, result.description, result.method, result.referrer, result.file_name
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def finish_report(test_name)
|
27
|
+
puts "Writing results to #{basedir}"
|
28
|
+
copy_styles unless styles_exist?
|
29
|
+
create_index unless index_exists?
|
30
|
+
update_index(test_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_detail_report(result)
|
34
|
+
template = ERB.new(template("detail.html.erb"))
|
35
|
+
output(result.file_name, template.result(result.send(:binding)), result.test_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def copy_styles
|
39
|
+
# not using cp_r because it picks up .svn crap
|
40
|
+
FileUtils.mkdir_p(File.join(basedir, "stylesheets"))
|
41
|
+
Dir.glob("#{tarantula_home}/laf/stylesheets/*.css").each do |file|
|
42
|
+
FileUtils.cp(file, File.join(basedir, "stylesheets"))
|
43
|
+
end
|
44
|
+
FileUtils.mkdir_p(File.join(basedir, "images"))
|
45
|
+
Dir.glob("#{tarantula_home}/laf/images/*.{jpg,gif,png}").each do |file|
|
46
|
+
FileUtils.cp(file, File.join(basedir, "images"))
|
47
|
+
end
|
48
|
+
FileUtils.mkdir_p(File.join(basedir, "javascripts"))
|
49
|
+
Dir.glob("#{tarantula_home}/laf/javascripts/*.js").each do |file|
|
50
|
+
FileUtils.cp(file, File.join(basedir, "javascripts"))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_index
|
55
|
+
template = ERB.new(template("index.html.erb"))
|
56
|
+
output("index.html", template.result(binding))
|
57
|
+
end
|
58
|
+
|
59
|
+
def update_index(test_name)
|
60
|
+
File.open(File.join(basedir, "index.html"), "r+") do |file|
|
61
|
+
doc = Hpricot file.read
|
62
|
+
tabs_container = doc.search "#tabs-container ul"
|
63
|
+
results_container = doc.search "#results-container"
|
64
|
+
tabs_container.append tab_html(test_name)
|
65
|
+
results_container.append results_html(test_name)
|
66
|
+
file.rewind
|
67
|
+
file.write doc.to_s
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def index_exists?
|
72
|
+
File.exists?(File.join(basedir, "index.html"))
|
73
|
+
end
|
74
|
+
|
75
|
+
def styles_exist?
|
76
|
+
File.exists?(File.join(basedir, "stylesheets", "tarantula.css"))
|
77
|
+
end
|
78
|
+
|
79
|
+
def tab_html(test_name)
|
80
|
+
"<li><a href='##{test_name}'><span>#{test_name}</span></a></li>"
|
81
|
+
end
|
82
|
+
|
83
|
+
def results_html(test_name)
|
84
|
+
template = ERB.new(template("test_report.html.erb"))
|
85
|
+
template.result(binding)
|
86
|
+
end
|
87
|
+
|
88
|
+
def template(name)
|
89
|
+
File.read(File.join(File.dirname(__FILE__), name))
|
90
|
+
end
|
91
|
+
|
92
|
+
def output(name, body, subdir = '')
|
93
|
+
FileUtils.mkdir_p(File.join(basedir, subdir)) unless subdir.empty?
|
94
|
+
File.open(File.join(basedir, subdir, name), "w") do |file|
|
95
|
+
file.write body
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# CSS class for HTML status codes
|
100
|
+
def class_for_code(code)
|
101
|
+
"r#{Integer(code)/100}"
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
3
|
+
|
4
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
5
|
+
<head>
|
6
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
7
|
+
<link rel="stylesheet" href="stylesheets/tarantula.css" type="text/css" media="screen" title="no title" charset="utf-8">
|
8
|
+
<script type="text/javascript" src="javascripts/jquery-1.2.3.js"></script>
|
9
|
+
<script type="text/javascript" src="javascripts/jquery.tablesorter.js"></script>
|
10
|
+
<script type="text/javascript" src="javascripts/jquery-ui-tabs.js"></script>
|
11
|
+
<script type="text/javascript" src="javascripts/tarantula.js"></script>
|
12
|
+
|
13
|
+
<title>Tarantula</title>
|
14
|
+
</head>
|
15
|
+
|
16
|
+
<body>
|
17
|
+
<div id="container">
|
18
|
+
<div id="header">
|
19
|
+
<h1>Tarantula by Relevance</h1>
|
20
|
+
<h2>Eight legs, two fangs ... and an attitude</h2>
|
21
|
+
<p>Tarantula is an open source tool for testing Rails web
|
22
|
+
applications. Tarantula is developed by <a href="http://thinkrelevance.com">Relevance, Inc.</a>
|
23
|
+
and lives at <a href="http://github.com/relevance/tarantula">http://github.com/relevance/tarantula</a>.</p>
|
24
|
+
<hr/>
|
25
|
+
</div>
|
26
|
+
<div id="page">
|
27
|
+
<div id="tabs-container">
|
28
|
+
<ul class="tabs"> </ul>
|
29
|
+
</div>
|
30
|
+
|
31
|
+
<div id="results-container">
|
32
|
+
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
</body>
|
37
|
+
</html>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Relevance::Tarantula::InvalidHtmlHandler
|
2
|
+
include Relevance::Tarantula
|
3
|
+
def handle(result)
|
4
|
+
response = result.response
|
5
|
+
return unless response.html?
|
6
|
+
begin
|
7
|
+
body = HTML::Document.new(response.body, true)
|
8
|
+
rescue Exception => e
|
9
|
+
error_result = result.dup
|
10
|
+
error_result.success = false
|
11
|
+
error_result.description = "Bad HTML (Scanner)"
|
12
|
+
error_result.data = e.message
|
13
|
+
error_result
|
14
|
+
else
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Relevance::Tarantula::IOReporter
|
2
|
+
|
3
|
+
include Relevance::Tarantula
|
4
|
+
attr_accessor :io, :results
|
5
|
+
delegate :successes, :failures, :to => :results
|
6
|
+
|
7
|
+
IOResultOverview = Struct.new(:code, :url)
|
8
|
+
|
9
|
+
def initialize(io)
|
10
|
+
@io = io
|
11
|
+
@results = Struct.new(:successes, :failures).new([], [])
|
12
|
+
end
|
13
|
+
|
14
|
+
def report(result)
|
15
|
+
return if result.nil?
|
16
|
+
|
17
|
+
unless result.success # collection = result.success ? results.successes : results.failures
|
18
|
+
results.failures << IOResultOverview.new(
|
19
|
+
result.code, result.url
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def finish_report(test_name)
|
25
|
+
unless (failures).empty?
|
26
|
+
io.puts "****** FAILURES"
|
27
|
+
failures.each do |failure|
|
28
|
+
io.puts "#{failure.code}: #{failure.url}"
|
29
|
+
end
|
30
|
+
raise "#{failures.size} failures"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|