test-unit-ext 0.1.0

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.
@@ -0,0 +1,28 @@
1
+ require "test/unit/ui/testrunnermediator"
2
+
3
+ module Test
4
+ module Unit
5
+ module UI
6
+ class TestRunnerMediator
7
+ alias_method :original_run_suite, :run_suite
8
+ def run_suite
9
+ @notified_finished = false
10
+ begin_time = Time.now
11
+ original_run_suite
12
+ rescue Interrupt
13
+ unless @notified_finished
14
+ end_time = Time.now
15
+ elapsed_time = end_time - begin_time
16
+ notify_listeners(FINISHED, elapsed_time)
17
+ end
18
+ raise
19
+ end
20
+
21
+ def notify_listeners(channel_name, *arguments)
22
+ @notified_finished = true if channel_name == FINISHED
23
+ super
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ require 'test/unit/util/backtracefilter'
2
+
3
+ module Test
4
+ module Unit
5
+ module Util
6
+ module BacktraceFilter
7
+ TEST_UNIT_EXT_PREFIX = File.dirname(__FILE__)
8
+
9
+ alias_method :original_filter_backtrace, :filter_backtrace
10
+ def filter_backtrace(backtrace, prefix=nil)
11
+ original_result = original_filter_backtrace(backtrace, prefix)
12
+ original_filter_backtrace(original_result, TEST_UNIT_EXT_PREFIX)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,59 @@
1
+ module Test
2
+ class Color
3
+ NAMES = ["black", "red", "green", "yellow",
4
+ "blue", "magenta", "cyan", "white"]
5
+ def initialize(name, options={})
6
+ @name = name
7
+ @foreground = options[:foreground]
8
+ @foreground = true if @foreground.nil?
9
+ @intensity = options[:intensity]
10
+ @bold = options[:bold]
11
+ @italic = options[:italic]
12
+ @underline = options[:underline]
13
+ end
14
+
15
+ def sequence
16
+ sequence = []
17
+ if @name == "none"
18
+ elsif @name == "reset"
19
+ sequence << "0"
20
+ else
21
+ foreground_parameter = @foreground ? 3 : 4
22
+ foreground_parameter += 6 if @intensity
23
+ sequence << "#{foreground_parameter}#{NAMES.index(@name)}"
24
+ end
25
+ sequence << "1" if @bold
26
+ sequence << "3" if @italic
27
+ sequence << "4" if @underline
28
+ sequence
29
+ end
30
+
31
+ def escape_sequence
32
+ "\e[#{sequence.join(';')}m"
33
+ end
34
+
35
+ def +(other)
36
+ MixColor.new([self, other])
37
+ end
38
+ end
39
+
40
+ class MixColor
41
+ def initialize(colors)
42
+ @colors = colors
43
+ end
44
+
45
+ def sequence
46
+ @colors.inject([]) do |result, color|
47
+ result + color.sequence
48
+ end
49
+ end
50
+
51
+ def escape_sequence
52
+ "\e[#{sequence.join(';')}m"
53
+ end
54
+
55
+ def +(other)
56
+ self.class.new([self, other])
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,101 @@
1
+ require "test/unit/ui/console/testrunner"
2
+
3
+ module Test
4
+ module Unit
5
+ module UI
6
+ module Console
7
+ class ColorizedTestRunner < TestRunner
8
+ extend TestRunnerUtilities
9
+
10
+ SCHEMES = {
11
+ :default => {
12
+ "success" => Color.new("green", :bold => true),
13
+ "failure" => Color.new("red", :bold => true),
14
+ "error" => Color.new("yellow", :bold => true),
15
+ },
16
+ }
17
+
18
+ def initialize(suite, output_level=NORMAL, io=STDOUT)
19
+ super
20
+ @use_color = guess_color_availability
21
+ @color_scheme = SCHEMES[:default]
22
+ @reset_color = Color.new("reset")
23
+ end
24
+
25
+ private
26
+ def add_fault(fault)
27
+ @faults << fault
28
+ output_single_with_color(fault.single_character_display,
29
+ fault_color(fault),
30
+ PROGRESS_ONLY)
31
+ @already_outputted = true
32
+ end
33
+
34
+ def test_finished(name)
35
+ unless @already_outputted
36
+ output_single_with_color(".",
37
+ @color_scheme["success"],
38
+ PROGRESS_ONLY)
39
+ end
40
+ nl(VERBOSE)
41
+ @already_outputted = false
42
+ end
43
+
44
+ def finished(elapsed_time)
45
+ nl
46
+ output("Finished in #{elapsed_time} seconds.")
47
+ @faults.each_with_index do |fault, index|
48
+ nl
49
+ output_single("%3d) " % (index + 1))
50
+ output_with_color(fault.long_display, fault_color(fault))
51
+ end
52
+ nl
53
+ output_with_color(@result.to_s, result_color)
54
+ end
55
+
56
+ def fault_color(fault)
57
+ @color_scheme[fault.class.name.split(/::/).last.downcase]
58
+ end
59
+
60
+ def result_color
61
+ if @result.passed?
62
+ @color_scheme["success"]
63
+ elsif @result.error_count > 0
64
+ @color_scheme["error"]
65
+ elsif @result.failure_count > 0
66
+ @color_scheme["failure"]
67
+ end
68
+ end
69
+
70
+ def output_with_color(message, color=nil, level=NORMAL)
71
+ return unless output?(level)
72
+ output_single_with_color(message, color, level)
73
+ @io.puts
74
+ end
75
+
76
+ def output_single_with_color(message, color=nil, level=NORMAL)
77
+ return unless output?(level)
78
+ if @use_color and color
79
+ message = "%s%s%s" % [color.escape_sequence,
80
+ message,
81
+ @reset_color.escape_sequence]
82
+ end
83
+ @io.write(message)
84
+ @io.flush
85
+ end
86
+
87
+ def guess_color_availability
88
+ term = ENV["TERM"]
89
+ return true if term and (/term\z/ =~ term or term == "screen")
90
+ return true if ENV["EMACS"] == "t"
91
+ false
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ AutoRunner::RUNNERS[:console] = Proc.new do
98
+ Test::Unit::UI::Console::ColorizedTestRunner
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,187 @@
1
+ # port of ndiff in Python's difflib.
2
+
3
+ module Test
4
+ module Diff
5
+ class SequenceMatcher
6
+ def initialize(from, to)
7
+ @from = from
8
+ @to = to
9
+ update_to_indexes
10
+ end
11
+
12
+ def longest_match(from_start, from_end, to_start, to_end)
13
+ best_from, best_to, best_size = from_start, to_start, 0
14
+ lengths = {}
15
+ from_start.upto(from_end) do |i|
16
+ new_lengths = {}
17
+ (@to_indexes[@from[i]] || []).each do |j|
18
+ next if j < to_start
19
+ break if j >= to_end
20
+ k = new_lengths[j] = (lengths[j - 1] || 0) + 1
21
+ if k > best_size
22
+ best_from, best_to, best_size = i - k + 1, j - k + 1, k
23
+ end
24
+ end
25
+ lengths = new_lengths
26
+ end
27
+
28
+ while best_from > from_start and best_to > to_start and
29
+ @from[best_from - 1] == @to[best_to - 1]
30
+ best_from -= 1
31
+ best_to -= 1
32
+ best_size += 1
33
+ end
34
+
35
+ while best_from + best_size <= from_end and
36
+ best_to + best_size <= to_end and
37
+ @from[best_from + best_size] == @to[best_to + best_size]
38
+ best_size += 1
39
+ end
40
+
41
+ [best_from, best_to, best_size]
42
+ end
43
+
44
+ def matching_blocks
45
+ queue = [[0, @from.size - 1, 0, @to.size - 1]]
46
+ blocks = []
47
+ until queue.empty?
48
+ from_start, from_end, to_start, to_end = queue.pop
49
+ match_info = longest_match(from_start, from_end, to_start, to_end)
50
+ match_from_index, match_to_index, size = match_info
51
+ unless size.zero?
52
+ blocks << match_info
53
+ if from_start < match_from_index and to_start < match_to_index
54
+ queue.push([from_start, match_from_index,
55
+ to_start, match_to_index])
56
+ end
57
+ if match_from_index + size < from_end and
58
+ match_to_index + size < to_end
59
+ queue.push([match_from_index + size, from_end,
60
+ match_to_index + size, to_end])
61
+ end
62
+ end
63
+ end
64
+
65
+ non_adjacent = []
66
+ prev_from_index = prev_to_index = prev_size = 0
67
+ blocks.sort.each do |from_index, to_index, size|
68
+ if prev_from_index + prev_size == from_index and
69
+ prev_to_index + prev_size == to_index
70
+ prev_size += size
71
+ else
72
+ unless prev_size.zero?
73
+ non_adjacent << [prev_from_index, prev_to_index, prev_size]
74
+ end
75
+ prev_from_index, prev_to_index, prev_size =
76
+ from_index, to_index, size
77
+ end
78
+ end
79
+ unless prev_size.zero?
80
+ non_adjacent << [prev_from_index, prev_to_index, prev_size]
81
+ end
82
+
83
+ non_adjacent << [@from.size, @to.size, 0]
84
+ non_adjacent
85
+ end
86
+
87
+ def operations
88
+ from_index = to_index = 0
89
+ operations = []
90
+ matching_blocks.each do |match_from_index, match_to_index, size|
91
+ tag = nil
92
+ if from_index < match_from_index and to_index < match_to_index
93
+ tag = :replace
94
+ elsif from_index < match_from_index
95
+ tag = :delete
96
+ elsif to_index < match_to_index
97
+ tag = :insert
98
+ end
99
+
100
+ if tag
101
+ operations << [tag,
102
+ from_index, match_from_index,
103
+ to_index, match_to_index]
104
+ end
105
+
106
+ from_index, to_index = match_from_index + size, match_to_index + size
107
+ if size > 0
108
+ operations << [:equal,
109
+ match_from_index, from_index,
110
+ match_to_index, to_index]
111
+ end
112
+ end
113
+
114
+ operations
115
+ end
116
+
117
+ private
118
+ def update_to_indexes
119
+ @to_indexes = {}
120
+ @to.each_with_index do |line, i|
121
+ @to_indexes[line] ||= []
122
+ @to_indexes[line] << i
123
+ end
124
+ end
125
+ end
126
+
127
+ class Differ
128
+ def initialize(from, to)
129
+ @from = from
130
+ @to = to
131
+ end
132
+
133
+ def compare
134
+ result = []
135
+ matcher = SequenceMatcher.new(@from, @to)
136
+ matcher.operations.each do |args|
137
+ tag, from_start, from_end, to_start, to_end = args
138
+ case tag
139
+ when :replace
140
+ result.concat(fancy_replace(from_start, from_end, to_start, to_end))
141
+ when :delete
142
+ result.concat(tagging('-', @from[from_start..from_end]))
143
+ when :insert
144
+ result.concat(tagging('+', @to[to_start..to_end]))
145
+ when :equal
146
+ result.concat(tagging(' ', @from[from_start..from_end]))
147
+ else
148
+ raise "unknown tag: #{tag}"
149
+ end
150
+ end
151
+ result
152
+ end
153
+
154
+ private
155
+ def tagging(tag, contents)
156
+ contents.collect {|content| "#{tag} #{content}"}
157
+ end
158
+
159
+ def format_diff_point(from_line, to_line, from_tags, to_tags)
160
+ common = [n_leading_characters(from_line, ?\t),
161
+ n_leading_characters(to_line, ?\t)].min
162
+ common = [common, n_leading_characters(from_tags[0, common], " "[0])].min
163
+ from_tags = from_tags[common..-1].rstrip
164
+ to_tags = to_tags[common..-1].rstrip
165
+
166
+ result = ["- #{from_line}"]
167
+ result << "? #{"\t" * common}#{from_tags}" unless from_tags.empty?
168
+ result << "+ #{to_line}"
169
+ result << "? #{"\t" * common}#{to_tags}" unless to_tags.empty?
170
+ result
171
+ end
172
+
173
+ def n_leading_characters(string, character)
174
+ n = 0
175
+ while string[n] == character
176
+ n += 1
177
+ end
178
+ n
179
+ end
180
+ end
181
+
182
+ module_function
183
+ def ndiff(from, to)
184
+ Differ.new(from, to).compare.join("\n")
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,25 @@
1
+ require 'test/unit/failure'
2
+ require 'test/unit/error'
3
+
4
+ module Test
5
+ module Unit
6
+ BACKTRACE_INFO_RE = /.+:\d+:in `.+?'/
7
+ class Failure
8
+ alias_method :original_long_display, :long_display
9
+ def long_display
10
+ extract_backtraces_re =
11
+ /^ \[(#{BACKTRACE_INFO_RE}(?:\n #{BACKTRACE_INFO_RE})+)\]:$/
12
+ original_long_display.gsub(extract_backtraces_re) do |backtraces|
13
+ $1.gsub(/^ (#{BACKTRACE_INFO_RE})/, '\1') + ':'
14
+ end
15
+ end
16
+ end
17
+
18
+ class Error
19
+ alias_method :original_long_display, :long_display
20
+ def long_display
21
+ original_long_display.gsub(/^ (#{BACKTRACE_INFO_RE})/, '\1')
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,111 @@
1
+ require "test/unit"
2
+
3
+ module Test
4
+ module Unit
5
+ class TestCase
6
+ class << self
7
+ alias_method :method_added_without_metadata, :method_added
8
+ def method_added(name)
9
+ method_added_without_metadata(name)
10
+ if defined?(@current_metadata)
11
+ set_metadata(name, @current_metadata)
12
+ @current_metadata = {}
13
+ end
14
+ end
15
+
16
+ def metadata(name, value, *tests)
17
+ @current_metadata ||= {}
18
+ if tests.empty?
19
+ @current_metadata[name] = value
20
+ else
21
+ tests.each do |test|
22
+ set_metadata(test, {name => value})
23
+ end
24
+ end
25
+ end
26
+
27
+ def bug(value, *tests)
28
+ metadata(:bug, value, *tests)
29
+ end
30
+
31
+ def set_metadata(test_name, metadata)
32
+ return if metadata.empty?
33
+ test_name = normalize_test_name(test_name)
34
+ @metadata ||= {}
35
+ @metadata[test_name] ||= {}
36
+ @metadata[test_name] = @metadata[test_name].merge(metadata)
37
+ end
38
+
39
+ def get_metadata(test_name)
40
+ test_name = normalize_test_name(test_name)
41
+ @metadata ||= {}
42
+ @metadata[test_name]
43
+ end
44
+ end
45
+
46
+ alias_method :run_without_metadata, :run
47
+ def run(result, &block)
48
+ run_without_metadata(TestResultMetadataSupport.new(result, self), &block)
49
+ end
50
+
51
+ def metadata
52
+ self.class.get_metadata(@method_name) || {}
53
+ end
54
+ end
55
+
56
+ class TestResultMetadataSupport
57
+ def initialize(result, test)
58
+ @result = result
59
+ @test = test
60
+ end
61
+
62
+ def add_failure(failure)
63
+ failure.metadata = @test.metadata
64
+ method_missing(:add_failure, failure)
65
+ end
66
+
67
+ def add_error(error)
68
+ error.metadata = @test.metadata
69
+ method_missing(:add_error, error)
70
+ end
71
+
72
+ def method_missing(name, *args, &block)
73
+ @result.send(name, *args, &block)
74
+ end
75
+ end
76
+
77
+ module MetadataFormatter
78
+ private
79
+ def format_metadata
80
+ return '' if metadata.empty?
81
+ metadata.collect do |key, value|
82
+ " #{key}: #{value}"
83
+ end.join("\n") + "\n"
84
+ end
85
+ end
86
+
87
+ class Failure
88
+ include MetadataFormatter
89
+
90
+ attr_accessor :metadata
91
+
92
+ alias_method :long_display_without_metadata, :long_display
93
+ def long_display
94
+ long_display_without_metadata.sub(/(^#{Regexp.escape(@test_name)}.*\n)/,
95
+ "\\1#{format_metadata}")
96
+ end
97
+ end
98
+
99
+ class Error
100
+ include MetadataFormatter
101
+
102
+ attr_accessor :metadata
103
+
104
+ alias_method :long_display_without_metadata, :long_display
105
+ def long_display
106
+ long_display_without_metadata.sub(/(^#{Regexp.escape(@test_name)}:\n)/,
107
+ "\\1#{format_metadata}")
108
+ end
109
+ end
110
+ end
111
+ end