tarantula 0.0.5

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 (118) hide show
  1. data/CHANGELOG +2 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +106 -0
  4. data/Rakefile +80 -0
  5. data/init.rb +1 -0
  6. data/install.rb +1 -0
  7. data/laf/images/background.jpg +0 -0
  8. data/laf/images/relevance-os-logo.gif +0 -0
  9. data/laf/images/tab.png +0 -0
  10. data/laf/images/table-sort.gif +0 -0
  11. data/laf/images/tarantula-sprites.png +0 -0
  12. data/laf/javascripts/jquery-1.2.3.js +3408 -0
  13. data/laf/javascripts/jquery-ui-tabs.js +890 -0
  14. data/laf/javascripts/jquery.tablesorter.js +861 -0
  15. data/laf/javascripts/tarantula.js +10 -0
  16. data/laf/stylesheets/tarantula.css +638 -0
  17. data/laf/stylesheets/ui.tabs.css +113 -0
  18. data/lib/relevance/core_extensions/ellipsize.rb +34 -0
  19. data/lib/relevance/core_extensions/file.rb +9 -0
  20. data/lib/relevance/core_extensions/response.rb +9 -0
  21. data/lib/relevance/core_extensions/test_case.rb +12 -0
  22. data/lib/relevance/tarantula.rb +63 -0
  23. data/lib/relevance/tarantula/attack.rb +15 -0
  24. data/lib/relevance/tarantula/attack_form_submission.rb +75 -0
  25. data/lib/relevance/tarantula/attack_handler.rb +37 -0
  26. data/lib/relevance/tarantula/crawler.rb +240 -0
  27. data/lib/relevance/tarantula/detail.html.erb +77 -0
  28. data/lib/relevance/tarantula/form.rb +21 -0
  29. data/lib/relevance/tarantula/form_submission.rb +70 -0
  30. data/lib/relevance/tarantula/html_document_handler.rb +36 -0
  31. data/lib/relevance/tarantula/html_report_helper.rb +56 -0
  32. data/lib/relevance/tarantula/html_reporter.rb +105 -0
  33. data/lib/relevance/tarantula/index.html.erb +48 -0
  34. data/lib/relevance/tarantula/invalid_html_handler.rb +18 -0
  35. data/lib/relevance/tarantula/io_reporter.rb +34 -0
  36. data/lib/relevance/tarantula/link.rb +56 -0
  37. data/lib/relevance/tarantula/log_grabber.rb +16 -0
  38. data/lib/relevance/tarantula/rails_integration_proxy.rb +70 -0
  39. data/lib/relevance/tarantula/recording.rb +12 -0
  40. data/lib/relevance/tarantula/response.rb +13 -0
  41. data/lib/relevance/tarantula/result.rb +66 -0
  42. data/lib/relevance/tarantula/test_report.html.erb +34 -0
  43. data/lib/relevance/tarantula/tidy_handler.rb +32 -0
  44. data/lib/relevance/tarantula/transform.rb +17 -0
  45. data/manifest.txt +117 -0
  46. data/rails/init.rb +1 -0
  47. data/tarantula.gemspec +48 -0
  48. data/tasks/tarantula_tasks.rake +34 -0
  49. data/template/tarantula_test.rb +12 -0
  50. data/test/relevance/core_extensions/ellipsize_test.rb +19 -0
  51. data/test/relevance/core_extensions/file_test.rb +8 -0
  52. data/test/relevance/core_extensions/response_test.rb +29 -0
  53. data/test/relevance/core_extensions/test_case_test.rb +16 -0
  54. data/test/relevance/tarantula/attack_form_submission_test.rb +79 -0
  55. data/test/relevance/tarantula/attack_handler_test.rb +29 -0
  56. data/test/relevance/tarantula/crawler_test.rb +296 -0
  57. data/test/relevance/tarantula/form_submission_test.rb +71 -0
  58. data/test/relevance/tarantula/form_test.rb +50 -0
  59. data/test/relevance/tarantula/html_document_handler_test.rb +43 -0
  60. data/test/relevance/tarantula/html_report_helper_test.rb +47 -0
  61. data/test/relevance/tarantula/html_reporter_test.rb +82 -0
  62. data/test/relevance/tarantula/invalid_html_handler_test.rb +33 -0
  63. data/test/relevance/tarantula/io_reporter_test.rb +11 -0
  64. data/test/relevance/tarantula/link_test.rb +61 -0
  65. data/test/relevance/tarantula/log_grabber_test.rb +26 -0
  66. data/test/relevance/tarantula/rails_integration_proxy_test.rb +94 -0
  67. data/test/relevance/tarantula/result_test.rb +85 -0
  68. data/test/relevance/tarantula/tidy_handler_test.rb +58 -0
  69. data/test/relevance/tarantula/transform_test.rb +21 -0
  70. data/test/relevance/tarantula_test.rb +23 -0
  71. data/test/test_helper.rb +34 -0
  72. data/tmp/test_output/images/background.jpg +0 -0
  73. data/tmp/test_output/images/relevance-os-logo.gif +0 -0
  74. data/tmp/test_output/images/tab.png +0 -0
  75. data/tmp/test_output/images/table-sort.gif +0 -0
  76. data/tmp/test_output/images/tarantula-sprites.png +0 -0
  77. data/tmp/test_output/index.html +255 -0
  78. data/tmp/test_output/javascripts/jquery-1.2.3.js +3408 -0
  79. data/tmp/test_output/javascripts/jquery-ui-tabs.js +890 -0
  80. data/tmp/test_output/javascripts/jquery.tablesorter.js +861 -0
  81. data/tmp/test_output/javascripts/tarantula.js +10 -0
  82. data/tmp/test_output/stylesheets/tarantula.css +638 -0
  83. data/tmp/test_output/stylesheets/ui.tabs.css +113 -0
  84. data/tmp/test_output/test_user_pages/1.html +71 -0
  85. data/tmp/test_output/test_user_pages/10.html +71 -0
  86. data/tmp/test_output/test_user_pages/11.html +71 -0
  87. data/tmp/test_output/test_user_pages/12.html +71 -0
  88. data/tmp/test_output/test_user_pages/13.html +71 -0
  89. data/tmp/test_output/test_user_pages/14.html +71 -0
  90. data/tmp/test_output/test_user_pages/15.html +71 -0
  91. data/tmp/test_output/test_user_pages/16.html +71 -0
  92. data/tmp/test_output/test_user_pages/17.html +71 -0
  93. data/tmp/test_output/test_user_pages/18.html +71 -0
  94. data/tmp/test_output/test_user_pages/19.html +71 -0
  95. data/tmp/test_output/test_user_pages/2.html +71 -0
  96. data/tmp/test_output/test_user_pages/20.html +71 -0
  97. data/tmp/test_output/test_user_pages/3.html +71 -0
  98. data/tmp/test_output/test_user_pages/4.html +71 -0
  99. data/tmp/test_output/test_user_pages/5.html +71 -0
  100. data/tmp/test_output/test_user_pages/6.html +71 -0
  101. data/tmp/test_output/test_user_pages/7.html +71 -0
  102. data/tmp/test_output/test_user_pages/8.html +71 -0
  103. data/tmp/test_output/test_user_pages/9.html +71 -0
  104. data/uninstall.rb +1 -0
  105. data/vendor/xss-shield/MIT-LICENSE +20 -0
  106. data/vendor/xss-shield/README +76 -0
  107. data/vendor/xss-shield/init.rb +16 -0
  108. data/vendor/xss-shield/lib/xss_shield.rb +6 -0
  109. data/vendor/xss-shield/lib/xss_shield/erb_hacks.rb +111 -0
  110. data/vendor/xss-shield/lib/xss_shield/haml_hacks.rb +42 -0
  111. data/vendor/xss-shield/lib/xss_shield/safe_string.rb +47 -0
  112. data/vendor/xss-shield/lib/xss_shield/secure_helpers.rb +40 -0
  113. data/vendor/xss-shield/test/test_actionview_integration.rb +40 -0
  114. data/vendor/xss-shield/test/test_erb.rb +44 -0
  115. data/vendor/xss-shield/test/test_haml.rb +43 -0
  116. data/vendor/xss-shield/test/test_helpers.rb +25 -0
  117. data/vendor/xss-shield/test/test_safe_string.rb +55 -0
  118. metadata +283 -0
@@ -0,0 +1,77 @@
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>
5
+ <head>
6
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
7
+ <title>Tarantula report detail <%= short_description %></title>
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
+ <link type="text/css" media="screen" rel="stylesheet" href="../stylesheets/tarantula.css"/>
13
+ <link type="text/css" media="screen" rel="stylesheet" href="../stylesheets/ui.tabs.css"/>
14
+
15
+ <!--[if lte IE 7]>
16
+ <link rel="stylesheet" href="jqeury.tabs-ie.css" type="text/css" media="projection, screen" />
17
+ <![endif]-->
18
+
19
+ </head>
20
+
21
+ <body id="top">
22
+
23
+ <div id="container">
24
+
25
+ <div id="header">
26
+ <hr class="top"/>
27
+ <h1><span>Tarantula : Eight Legs, Two Fangs, and an Attitude</span></h1>
28
+ </div>
29
+
30
+ <div id="page-container">
31
+ <hr class="top"/>
32
+ <div id="page">
33
+ <h1>Tarantula report detail <%= short_description %></h1>
34
+ <p>Generated on <%= Time.now %> (<a href="../index.html">Back</a>)</p>
35
+ <p>Visit <a href="<%= full_url %>"><%= full_url %></a></p>
36
+ <p>Response: <%= code %></p>
37
+ <p>Referrer: <%= referrer || "" %></p>
38
+ <div id="tabs-container">
39
+ <ul>
40
+ <li><a href="#fragment-1"><span>Data</span></a></li>
41
+ <li><a href="#fragment-2"><span>Body</span></a></li>
42
+ <li><a href="#fragment-3"><span>Log</span></a></li>
43
+ </ul>
44
+ <div id="fragment-1">
45
+ <% if data %>
46
+ <%= wrap_in_line_number_table(data) %>
47
+ <% else %>
48
+ <p>No Data</p>
49
+ <% end %>
50
+ </div>
51
+ <div id="fragment-2">
52
+ <% if body %>
53
+ <%= wrap_in_line_number_table(body) %>
54
+ <% else %>
55
+ <p>No Body</p>
56
+ <% end %>
57
+ </div>
58
+ <div id="fragment-3">
59
+ <% if log %>
60
+ <%= wrap_in_line_number_table(log) {|line| wrap_stack_trace_line(line)} %>
61
+ <% else %>
62
+ <p>No Log</p>
63
+ <% end %>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ <div id="sidebar">
69
+ <h3><span>Tarantula</span></h3>
70
+ <p>Tarantula is an open-source tool for testing Rails web applications. Tarantula is developed by <a href="http://thinkrelevance.com">Relevance, Inc.</a> and lives at <a href="http://opensource.thinkrelevance.com">http://opensource.thinkrelevance.com</a>.</p>
71
+ </div>
72
+ </div>
73
+ </body>
74
+
75
+ </html>
76
+
77
+
@@ -0,0 +1,21 @@
1
+ class Relevance::Tarantula::Form
2
+ extend Forwardable
3
+ def_delegators("@tag", :search)
4
+
5
+ def initialize(tag)
6
+ @tag = tag
7
+ end
8
+
9
+ def action
10
+ @tag['action'].downcase
11
+ end
12
+
13
+ def method
14
+ (rails_method_hack or @tag['method'] or 'get').downcase
15
+ end
16
+
17
+ def rails_method_hack
18
+ (tag = @tag.at('input[@name="_method"]')) && tag["value"]
19
+ end
20
+
21
+ end
@@ -0,0 +1,70 @@
1
+ class Relevance::Tarantula::FormSubmission
2
+ attr_accessor :method, :action, :data
3
+ def initialize(form)
4
+ @method = form.method
5
+ @action = form.action
6
+ @data = mutate_selects(form).merge(mutate_text_areas(form)).merge(mutate_inputs(form))
7
+ end
8
+
9
+ def self.mutate(form)
10
+ [self.new(form)]
11
+ end
12
+
13
+ def to_s
14
+ "#{action} #{method} #{data.inspect}"
15
+ end
16
+
17
+ # a form's signature is what makes it unique (e.g. action + fields)
18
+ # used to keep track of which forms we have submitted already
19
+ def signature
20
+ [action, data.keys.sort]
21
+ end
22
+
23
+ def create_random_data_for(form, tag_selector)
24
+ form.search(tag_selector).inject({}) do |form_args, input|
25
+ # TODO: test
26
+ form_args[input['name']] = random_data(input) if input['name']
27
+ form_args
28
+ end
29
+ end
30
+
31
+ def mutate_inputs(form)
32
+ create_random_data_for(form, 'input')
33
+ end
34
+
35
+ def mutate_text_areas(form)
36
+ create_random_data_for(form, 'textarea')
37
+ end
38
+
39
+ def mutate_selects(form)
40
+ form.search('select').inject({}) do |form_args, select|
41
+ options = select.search('option')
42
+ option = options.rand
43
+ form_args[select['name']] = option['value']
44
+ form_args
45
+ end
46
+ end
47
+
48
+ def random_data(input)
49
+ case input['name']
50
+ when /amount/ : random_int
51
+ when /_id$/ : random_whole_number
52
+ when /uploaded_data/ : nil
53
+ when /^_method$/ : input['value']
54
+ when nil : input['value']
55
+ else random_int
56
+ end
57
+ end
58
+
59
+ def big_number
60
+ 10000 # arbitrary
61
+ end
62
+
63
+ def random_int
64
+ rand(big_number) - (big_number/2)
65
+ end
66
+
67
+ def random_whole_number
68
+ rand(big_number)
69
+ end
70
+ 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,56 @@
1
+ require "erb"
2
+ module Relevance::Tarantula::HtmlReportHelper
3
+ include ERB::Util
4
+ include Relevance::Tarantula
5
+ def wrap_in_line_number_table(text, &blk)
6
+ x = Builder::XmlMarkup.new
7
+ x.table(:class => "grid tablesorter") do
8
+ x.thead do
9
+ x.tr do
10
+ x.th(:class => "sort asc") do
11
+ x.span("Line \#")
12
+ x.span(:class => "sort") do
13
+ x.em do
14
+ x << '&#8613;'
15
+ end
16
+ end
17
+ end
18
+ x.th(:class => "sort left") do
19
+ x.span("Line")
20
+ x.span(:class => "sort") do
21
+ x.em do
22
+ x << '&#8613;'
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ text.split("\n").each_with_index do |line, index|
29
+ x.tr do
30
+ x.td(index+1)
31
+ if block_given?
32
+ x.td {x << yield(line)}
33
+ else
34
+ x.td(line)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ x.target!
40
+ end
41
+
42
+ def textmate_url(file, line_no)
43
+ "txmt://open?url=file://#{File.expand_path(File.join(rails_root,file))}&line_no=#{line_no}"
44
+ end
45
+
46
+ def wrap_stack_trace_line(text)
47
+ if text =~ %r{^\s*(/[^:]+):(\d+):([^:]+)$}
48
+ file = h($1) # .to_s_xss_protected
49
+ line_number = $2
50
+ message = h($3) # .to_s_xss_protected
51
+ "<a href='#{textmate_url(file, line_number)}'>#{file}:#{line_number}</a>:#{message}" # .mark_as_xss_protected
52
+ else
53
+ h(text) # .to_s_xss_protected
54
+ end
55
+ end
56
+ 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 "#tabs-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,48 @@
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>
5
+ <head>
6
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
7
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
8
+ <meta name="Author" content="Erik Yowell; erik[at]thinkrelevance.com"/>
9
+ <title>Tarantula report</title>
10
+ <script type="text/javascript" src="javascripts/jquery-1.2.3.js"></script>
11
+ <script type="text/javascript" src="javascripts/jquery.tablesorter.js"></script>
12
+ <script type="text/javascript" src="javascripts/jquery-ui-tabs.js"></script>
13
+ <script type="text/javascript" src="javascripts/tarantula.js"></script>
14
+ <link type="text/css" media="screen" rel="stylesheet" href="stylesheets/tarantula.css"/>
15
+ <link type="text/css" media="screen" rel="stylesheet" href="stylesheets/ui.tabs.css"/>
16
+ </head>
17
+
18
+ <body id="top">
19
+
20
+ <div id="container">
21
+
22
+ <div id="header">
23
+ <hr class="top"/>
24
+ <h1><span>Tarantula : Eight Legs, Two Fangs, and an Attitude</span></h1>
25
+ <ul class="navigation">
26
+ <li><a href="#" class="result-all"><span>All</span> <em><%= successes.size + failures.size %></em></a></li>
27
+ <li><a href="#" class="result-failure"><span>Failures</span> <em><%= failures.size %></em></a></li>
28
+ <li><a href="#" class="result-success"><span>Successful</span> <em><%= successes.size %></em></a></li>
29
+ </ul>
30
+ </div>
31
+ </div>
32
+ <div id="page-container">
33
+ <hr class="top"/>
34
+ <div id="page">
35
+ <p>Generated on <%= Time.now %></p>
36
+ <div id="tabs-container">
37
+ <ul>
38
+ </ul>
39
+ </div>
40
+ </div>
41
+ <div id="sidebar">
42
+ <h3><span>Tarantula</span></h3>
43
+ <p>Tarantula is an open-source tool for testing Rails web applications. Tarantula is developed by <a href="http://thinkrelevance.com">Relevance, Inc.</a> and lives at <a href="http://opensource.thinkrelevance.com">http://opensource.thinkrelevance.com</a>.</p>
44
+ </div>
45
+ </div>
46
+ </body>
47
+
48
+ </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