test-unit 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -83,6 +83,14 @@ module Test
83
83
  collector.collect(*auto_runner.to_run)
84
84
  end
85
85
 
86
+ # JUST TEST!
87
+ # register_collector(:xml) do |auto_runner|
88
+ # require 'test/unit/collector/xml'
89
+ # collector = Collector::XML.new
90
+ # collector.filter = auto_runner.filters
91
+ # collector.collect(auto_runner.to_run[0])
92
+ # end
93
+
86
94
  # deprecated
87
95
  register_collector(:object_space) do |auto_runner|
88
96
  require 'test/unit/collector/objectspace'
@@ -152,13 +160,18 @@ module Test
152
160
  o.banner = "Test::Unit automatic runner."
153
161
  o.banner << "\nUsage: #{$0} [options] [-- untouched arguments]"
154
162
 
155
- o.on
156
163
  o.on('-r', '--runner=RUNNER', RUNNERS,
157
164
  "Use the given RUNNER.",
158
165
  "(" + keyword_display(RUNNERS) + ")") do |r|
159
166
  @runner = r
160
167
  end
161
168
 
169
+ o.on('--collector=COLLECTOR', COLLECTORS,
170
+ "Use the given COLLECTOR.",
171
+ "(" + keyword_display(COLLECTORS) + ")") do |collector|
172
+ @collector = collector
173
+ end
174
+
162
175
  if (@standalone)
163
176
  o.on('-b', '--basedir=DIR', "Base directory of test suites.") do |b|
164
177
  @base = b
@@ -397,3 +410,4 @@ end
397
410
  require 'test/unit/runner/console'
398
411
  require 'test/unit/runner/emacs'
399
412
  require 'test/unit/runner/tap'
413
+ require 'test/unit/runner/xml'
@@ -0,0 +1,250 @@
1
+ #--
2
+ #
3
+ # Author:: Kouhei Sutou
4
+ # Copyright::
5
+ # * Copyright (c) 2011 Kouhei Sutou <kou@clear-code.com>
6
+ # License:: Ruby license.
7
+
8
+ # just test!!! don't use it yet!!!
9
+
10
+ require 'test/unit/collector'
11
+
12
+ require 'rexml/document'
13
+ require 'rexml/streamlistener'
14
+
15
+ module Test
16
+ module Unit
17
+ module Collector
18
+ class XML
19
+ include Collector
20
+
21
+ def collect(xml_log_path)
22
+ listener = Listener.new
23
+ File.open(xml_log_path) do |xml_log|
24
+ parser = REXML::Parsers::StreamParser.new(xml_log, listener)
25
+ parser.parse
26
+ end
27
+ suite = TestSuite.new("tests in #{xml_log_path}")
28
+ suites = listener.test_suites
29
+ sort(suites).each {|s| add_suite(suite, s)}
30
+ suite
31
+ end
32
+
33
+ class Listener
34
+ include REXML::StreamListener
35
+
36
+ attr_reader :test_suites
37
+ def initialize
38
+ @ns_stack = [{"xml" => :xml}]
39
+ @tag_stack = [["", :root]]
40
+ @text_stack = ['']
41
+ @state_stack = [:root]
42
+ @values = {}
43
+ @test_suites = []
44
+ end
45
+
46
+ def tag_start(name, attributes)
47
+ @text_stack.push('')
48
+
49
+ ns = @ns_stack.last.dup
50
+ attrs = {}
51
+ attributes.each do |n, v|
52
+ if /\Axmlns(?:\z|:)/ =~ n
53
+ ns[$POSTMATCH] = v
54
+ else
55
+ attrs[n] = v
56
+ end
57
+ end
58
+ @ns_stack.push(ns)
59
+
60
+ _parent_tag = parent_tag
61
+ prefix, local = split_name(name)
62
+ uri = _ns(ns, prefix)
63
+ @tag_stack.push([uri, local])
64
+
65
+ state = next_state(@state_stack.last, uri, local)
66
+ @state_stack.push(state)
67
+ case state
68
+ when :test_suite, :test_case
69
+ @values = {}
70
+ when :test
71
+ @values = {}
72
+ @n_pass_assertions = 0 if _parent_tag == "start-test"
73
+ when :backtrace
74
+ @backtrace = []
75
+ @values_backup = @values
76
+ @values = {}
77
+ end
78
+ end
79
+
80
+ def tag_end(name)
81
+ state = @state_stack.pop
82
+ text = @text_stack.pop
83
+ uri, local = @tag_stack.pop
84
+ no_action_states = [:root, :stream]
85
+ case state
86
+ when *no_action_states
87
+ # do nothing
88
+ when :test_suite
89
+ test_suite_end
90
+ when :complete_test_case
91
+ @test_suites.last << @test_case.suite
92
+ when :test_case
93
+ test_case_end
94
+ when :result
95
+ @result = @values
96
+ when :test
97
+ test_end
98
+ when :pass_assertion
99
+ @n_pass_assertions += 1
100
+ when :backtrace
101
+ @values = @values_backup
102
+ @values["backtrace"] = @backtrace
103
+ when :entry
104
+ file = @values['file']
105
+ line = @values['line']
106
+ info = @values['info']
107
+ @backtrace << "#{file}:#{line}: #{info}"
108
+ @values = {}
109
+ else
110
+ local = normalize_local(local)
111
+ @values[local] = text
112
+ end
113
+ @ns_stack.pop
114
+ end
115
+
116
+ def text(data)
117
+ @text_stack.last << data
118
+ end
119
+
120
+ private
121
+ def _ns(ns, prefix)
122
+ ns.fetch(prefix, "")
123
+ end
124
+
125
+ NAME_SPLIT = /^(?:([\w:][-\w\d.]*):)?([\w:][-\w\d.]*)/
126
+ def split_name(name)
127
+ name =~ NAME_SPLIT
128
+ [$1 || '', $2]
129
+ end
130
+
131
+ STATE_TABLE = {
132
+ :root => [:stream],
133
+ :stream => [:ready_test_suite,
134
+ :start_test_suite,
135
+ :ready_test_case,
136
+ :start_test_case,
137
+ :start_test,
138
+ :pass_assertion,
139
+ :test_result,
140
+ :complete_test,
141
+ :complete_test_case,
142
+ :complete_test_suite,
143
+ :success],
144
+ :ready_test_suite => [:n_tests],
145
+ :start_test_suite => [:test_suite],
146
+ :ready_test_case => [:test_case,
147
+ :n_tests],
148
+ :start_test_case => [:test_case],
149
+ :start_test => [:test],
150
+ :pass_assertion => [:test],
151
+ :complete_test => [:test, :success],
152
+ :complete_test_case => [:test_case,
153
+ :elapsed,
154
+ :success],
155
+ :complete_test_suite => [:test_suite,
156
+ :success],
157
+ :test_suite => [:start_time,
158
+ :elapsed],
159
+ :test_case => [:name,
160
+ :start_time,
161
+ :elapsed],
162
+ :test => [:name,
163
+ :start_time,
164
+ :elapsed],
165
+ :test_result => [:test,
166
+ :result],
167
+ :result => [:test_case,
168
+ :test,
169
+ :status,
170
+ :backtrace,
171
+ :detail],
172
+ :backtrace => [:entry],
173
+ :entry => [:file,
174
+ :line,
175
+ :info],
176
+ }
177
+ def next_state(current_state, uri, local)
178
+ local = normalize_local(local)
179
+ valid_elements = STATE_TABLE[current_state]
180
+ if valid_elements.nil?
181
+ raise "unexpected element: #{current_path}"
182
+ end
183
+ next_state = local.to_sym
184
+ unless valid_elements.include?(next_state)
185
+ raise "unexpected element: #{current_path}"
186
+ end
187
+ next_state
188
+ end
189
+
190
+ def current_path
191
+ locals = @tag_stack.collect do |uri, local|
192
+ local
193
+ end
194
+ ["", *locals].join("/")
195
+ end
196
+
197
+ def normalize_local(local)
198
+ local.gsub(/-/, "_")
199
+ end
200
+
201
+ def parent_tag
202
+ @tag_stack.last[1]
203
+ end
204
+
205
+ def test_suite_end
206
+ return unless parent_tag == "start-test-suite"
207
+ suite = TestSuite.new
208
+ ["start_time", "elapsed_time", "n_tests"].each do |key|
209
+ if @values.has_key?(key)
210
+ suite.instance_variable_set("@#{key}", @values[key])
211
+ end
212
+ end
213
+ @test_suites << suite
214
+ end
215
+
216
+ def test_case_end
217
+ return unless parent_tag == "start-test-case"
218
+ name = @values["name"]
219
+ @test_case = Class.new(TestCase) do
220
+ define_method(:name) do
221
+ name
222
+ end
223
+ end
224
+ end
225
+
226
+ def test_end
227
+ return unless parent_tag == "complete-test"
228
+ name = @values["name"]
229
+ n_pass_assertions = @n_pass_assertions
230
+ result = @result
231
+ @test_case.module_eval do
232
+ test
233
+ define_method(name) do
234
+ n_pass_assertions.times do
235
+ add_assertion
236
+ end
237
+ case result["status"]
238
+ when "omission"
239
+ add_omission(Omission.new(name,
240
+ result["backtrace"],
241
+ result["detail"]))
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,80 @@
1
+ module Test
2
+ module Unit
3
+ module Data
4
+ class << self
5
+ def included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+ end
9
+
10
+ module ClassMethods
11
+ # TODO: WRITE ME.
12
+ def data(*arguments, &block)
13
+ n_arguments = arguments.size
14
+ case n_arguments
15
+ when 0
16
+ data_set = block
17
+ when 1
18
+ data_set = arguments[0]
19
+ when 2
20
+ data_set = {arguments[0] => arguments[1]}
21
+ else
22
+ message= "wrong number arguments(#{n_arguments} for 1..2)"
23
+ raise ArgumentError, message
24
+ end
25
+ current_data = current_attribute(:data)[:value] || []
26
+ attribute(:data, current_data + [data_set])
27
+ end
28
+
29
+ # TODO: WRITE ME.
30
+ def load_data(file_name)
31
+ case file_name
32
+ when /\.csv/i
33
+ loader = CSVDataLoader.new(self)
34
+ loader.load(file_name)
35
+ else
36
+ raise ArgumentError, "unsupported file format: <#{file_name}>"
37
+ end
38
+ end
39
+
40
+ class CSVDataLoader
41
+ def initialize(test_case)
42
+ @test_case = test_case
43
+ end
44
+
45
+ def load(file_name)
46
+ require 'csv'
47
+ header = nil
48
+ CSV.foreach(file_name) do |row|
49
+ if header.nil?
50
+ header = row
51
+ next
52
+ end
53
+ label = nil
54
+ data = {}
55
+ header.each_with_index do |key, i|
56
+ if key == "label"
57
+ label = row[i]
58
+ else
59
+ data[key] = normalize_value(row[i])
60
+ end
61
+ end
62
+ @test_case.data(label, data)
63
+ end
64
+ end
65
+
66
+ private
67
+ def normalize_value(value)
68
+ Integer(value)
69
+ rescue ArgumentError
70
+ begin
71
+ Float(value)
72
+ rescue ArgumentError
73
+ value
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -48,13 +48,14 @@ module Test
48
48
 
49
49
  # Returns a verbose version of the error description.
50
50
  def long_display
51
- backtrace_display = backtrace.join("\n ")
51
+ backtrace_display = location.join("\n ")
52
52
  "#{label}:\n#@test_name:\n#{message}\n #{backtrace_display}"
53
53
  end
54
54
 
55
- def backtrace
56
- filter_backtrace(@exception.backtrace)
55
+ def location
56
+ @location ||= filter_backtrace(@exception.backtrace)
57
57
  end
58
+ alias_method :backtrace, :location # Deprecated
58
59
 
59
60
  # Overridden to return long_display.
60
61
  def to_s
@@ -5,7 +5,7 @@ module Test
5
5
  def included(base)
6
6
  base.extend(ClassMethods)
7
7
 
8
- [:setup, :teardown].each do |fixture|
8
+ [:setup, :cleanup, :teardown].each do |fixture|
9
9
  observer = Proc.new do |test_case, _, _, value, method_name|
10
10
  if value.nil?
11
11
  test_case.send("unregister_#{fixture}_method", method_name)
@@ -28,6 +28,14 @@ module Test
28
28
  unregister_fixture(:setup, *method_names)
29
29
  end
30
30
 
31
+ def cleanup(*method_names)
32
+ register_fixture(:cleanup, *method_names)
33
+ end
34
+
35
+ def unregister_cleanup(*method_names)
36
+ unregister_fixture(:cleanup, *method_names)
37
+ end
38
+
31
39
  def teardown(*method_names)
32
40
  register_fixture(:teardown, *method_names)
33
41
  end
@@ -44,6 +52,15 @@ module Test
44
52
  unregister_fixture_method(:setup, method_name)
45
53
  end
46
54
 
55
+ def register_cleanup_method(method_name, options)
56
+ register_fixture_method(:cleanup, method_name, options,
57
+ :before, :prepend)
58
+ end
59
+
60
+ def unregister_cleanup_method(method_name)
61
+ unregister_fixture_method(:cleanup, method_name)
62
+ end
63
+
47
64
  def register_teardown_method(method_name, options)
48
65
  register_fixture_method(:teardown, method_name, options,
49
66
  :before, :prepend)
@@ -61,6 +78,14 @@ module Test
61
78
  collect_fixture_methods(:setup, :after)
62
79
  end
63
80
 
81
+ def before_cleanup_methods
82
+ collect_fixture_methods(:cleanup, :before)
83
+ end
84
+
85
+ def after_cleanup_methods
86
+ collect_fixture_methods(:cleanup, :after)
87
+ end
88
+
64
89
  def before_teardown_methods
65
90
  collect_fixture_methods(:teardown, :before)
66
91
  end
@@ -177,6 +202,10 @@ module Test
177
202
  run_fixture(:setup)
178
203
  end
179
204
 
205
+ def run_cleanup
206
+ run_fixture(:cleanup)
207
+ end
208
+
180
209
  def run_teardown
181
210
  run_fixture(:teardown, :handle_exception => true)
182
211
  end