leftright 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Jordi Bunster <jordi@bunster.org>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,45 @@
1
+ = leftright
2
+
3
+ leftright is kind of like the redgreen gem. It makes passing tests look
4
+ green, exceptions yellow, and failures red. But then there's more:
5
+
6
+ * It lets you know which TestCase class is being tested
7
+ * It shows you the full text of failures and exceptions as they happen
8
+ * It skips all remaining tests for a TestCase class if one fails
9
+
10
+ == Dependencies
11
+
12
+ Right now this is pretty heavily dependent on Test::Unit, so it won't work
13
+ in Ruby 1.9+ using MiniTest. Support is planned as soon as I find myself
14
+ using the Ruby 1.9 + Rails 3 combo day to day.
15
+
16
+ == Installation instructions
17
+
18
+ From Rubyforge's gem server (might not be there):
19
+
20
+ gem install leftright
21
+
22
+ From Gemcutter:
23
+
24
+ gem install leftright --source http://gemcutter.org
25
+
26
+ Both are the same, and are loaded the same way:
27
+
28
+ require 'leftright'
29
+
30
+ == Example usage
31
+
32
+ require 'leftright'
33
+
34
+ class SomeTest < Test::Unit::TestCase
35
+ def test_that_true_is_indeed_true
36
+ assert_equal true, true
37
+ end
38
+ end
39
+
40
+ Then run the file with ruby. Mind you, it gets a lot more exciting with
41
+ your own tests, specially if they fail. :)
42
+
43
+ == Legal
44
+
45
+ Copyright (c) 2009 Jordi Bunster, released under the MIT license
@@ -0,0 +1,30 @@
1
+ require 'lib/leftright/version'
2
+
3
+ XMLOBJECT_GEMSPEC = Gem::Specification.new do |gem|
4
+ gem.name = 'leftright'
5
+ gem.version = LeftRight::VERSION
6
+
7
+ gem.author, gem.email = 'Jordi Bunster', 'jordi@bunster.org'
8
+
9
+ gem.summary = "Cool replacement for Test::Unit's TestRunner"
10
+ gem.description = %{ leftright is kind of like the redgreen gem. It makes
11
+ passing tests look green, exceptions yellow, and failures red. It also
12
+ has a few features that make your workflow a bit faster (see README).
13
+ }.strip!.gsub! /\s+/, ' '
14
+
15
+ gem.has_rdoc = false
16
+
17
+ gem.date = Date.today
18
+ gem.files = %w[
19
+ MIT-LICENSE
20
+ README.rdoc
21
+ leftright.gemspec
22
+ lib
23
+ lib/leftright.rb
24
+ lib/leftright
25
+ lib/leftright/autorun.rb
26
+ lib/leftright/color.rb
27
+ lib/leftright/runner.rb
28
+ lib/leftright/version.rb
29
+ ]
30
+ end
@@ -0,0 +1,172 @@
1
+ require 'test/unit'
2
+ require 'test/unit/ui/console/testrunner'
3
+
4
+ require 'leftright/version' # to open the module
5
+
6
+ require 'leftright/color'
7
+ require 'leftright/runner'
8
+ require 'leftright/autorun'
9
+
10
+ module LeftRight
11
+ # In counts of ' ':
12
+ MID_SEPARATOR = 1
13
+ RIGHT_MARGIN = 1
14
+ LEFT_MARGIN = 1
15
+
16
+ # This whole thing is fairly gnarly, needing to keep state across multiple
17
+ # parts of the crazyness that is Test::Unit, so we keep it all here.
18
+ #
19
+ def self.state
20
+ @state ||= begin
21
+ fields = [
22
+ :dots, # the number of dots ('.') printed on this line
23
+ :class, # the TestCase-extending class being tested
24
+ :fault, # the current Test::Unit Failure/Error object
25
+ :last_class_printed, # last class printed on the left side
26
+ :previous_failed, # true if the previous test failed/exceptioned
27
+ :skip, # true if the current test was a skip
28
+ :skipped_count # total number of skipped tests so far
29
+ ]
30
+
31
+ state = Struct.new(*fields).new
32
+ state.skipped_count = 0
33
+ state.dots = 0
34
+ state
35
+ end
36
+ end
37
+
38
+ # Gets all descendants of Class that also happen to be descendants of
39
+ # Test::Unit::TestCase that have non-inherited instance methods that
40
+ # begin with the word 'test':
41
+ #
42
+ def self.testcase_classes
43
+ @testcase_classes ||= ObjectSpace.each_object(Class).find_all do |klass|
44
+ Test::Unit::TestCase > klass &&
45
+ klass.instance_methods(false).detect { |m| 'test' == m.to_s[0,4] }
46
+ end
47
+ end
48
+
49
+ # Replaces all instance methods beginning with 'test' in the given class
50
+ # with stubs that skip testing.
51
+ #
52
+ def self.skip_testing_class(klass)
53
+ klass.instance_methods.each do |m|
54
+ if 'test' == m.to_s[0,4]
55
+ klass.send :define_method, m.to_sym do
56
+ ::LeftRight.state.skip = true
57
+ ::LeftRight.state.skipped_count += 1
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ # Formats a class name to display on the left side.
64
+ #
65
+ def self.format_class_name(class_name)
66
+ class_name.chomp 'Test'
67
+ end
68
+
69
+ # Tries to get the terminal width in columns.
70
+ #
71
+ def self.terminal_width
72
+ @terminal_width ||= STDOUT.tty? ? `stty size`.split[-1].to_i : 0 rescue 0
73
+ end
74
+
75
+ # Tries to get the left side width in columns.
76
+ #
77
+ def self.left_side_width
78
+ @left_side_width ||= begin
79
+ testcase_classes.map do |c|
80
+ format_class_name(c.name).size + LEFT_MARGIN
81
+ end.max
82
+ end
83
+ end
84
+
85
+ # Tries to get the right side width in columns.
86
+ #
87
+ def self.right_side_width
88
+ terminal_width - left_side_width
89
+ end
90
+
91
+ # Returns the given string, right-justified onto the left side.
92
+ #
93
+ def self.justify_left_side(str = '')
94
+ str.to_s.rjust(left_side_width) + (' ' * MID_SEPARATOR)
95
+ end
96
+
97
+ # This gets the class name from the 'test_name' method on a
98
+ # Test::Unit Failure or Error. They look like test_method_name(TestCase),
99
+ #
100
+ def self.extract_class_name(test_name)
101
+ test_name.scan(/\(([^(|)]+)\)/x).flatten.last
102
+ end
103
+
104
+ # Wraps the given lines at word boundaries. Ripped right out of
105
+ # http://blog.macromates.com/2006/wrapping-text-with-regular-expressions/
106
+ #
107
+ def self.wrap(line)
108
+ return line unless STDOUT.tty?
109
+ width = right_side_width - MID_SEPARATOR - RIGHT_MARGIN
110
+ line.gsub /(.{1,#{width}})( +|$)\n?|(.{#{width}})/, "\\1\\3\n"
111
+ end
112
+
113
+ # Returns the current fault as a formatted failure message.
114
+ #
115
+ def self.F(color = C.red)
116
+ # First, we wrap each line individually, to keep existing line breaks:
117
+ lines = state.fault.long_display.split("\n")
118
+
119
+ # Drop the redundant "Failure: ", "test: " (shoulda), "test_", etc
120
+ lines.shift if lines.first.match /Failure:|Error:/
121
+ lines.first.sub! /^test[\ |:|_]?/i, ''
122
+
123
+ # Drop the class name in () from the test method name
124
+ lines.first.sub! /\(#{state.class}\)/, ''
125
+
126
+ # shoulda puts '. :' at the end of method names
127
+ lines.first.sub! /\.\ :\s?/, ':'
128
+
129
+ # Wrap lines before coloring, since the wrapping would get confused
130
+ # by non-printables.
131
+ buffer = lines.map { |line| wrap line.strip }.join.strip
132
+
133
+ # We make interesting parts of the failure message bold:
134
+ [ /(`[^']+')/m, # Stuff in `quotes'
135
+ /("[^"]+")/m, # Stuff in "quotes"
136
+ /([^\/|\[]+\.rb:\d+)/, # Filenames with line numbers (without [box])
137
+ /(\s+undefined\s+)/ ].each do |interesting|
138
+ buffer.gsub! interesting, ( C.bold + '\0' + C.reset + color )
139
+ end
140
+
141
+ # These are great for assert_equal and similar:
142
+ buffer.sub! /(<)(.*)(>\s+expected)/,
143
+ '\1' + C.bold + '\2' + C.reset + color + '\3'
144
+ buffer.sub! /(but\s+was\s+<)(.*)(>\.)/,
145
+ '\1' + C.bold + '\2' + C.reset + color + '\3'
146
+
147
+ color + buffer + C.reset + "\n"
148
+ end
149
+
150
+ # Returns the current fault as a formatted error message.
151
+ #
152
+ def self.E
153
+ F C.yellow
154
+ end
155
+
156
+ # Returns a passing dot, aware of how many to print per-line.
157
+ #
158
+ def self.P
159
+ return '.' unless STDOUT.tty?
160
+
161
+ state.dots += 1
162
+
163
+ max_dots = right_side_width - RIGHT_MARGIN - MID_SEPARATOR
164
+
165
+ if state.dots >= max_dots
166
+ state.dots = 1
167
+ "\n" + C.green('.')
168
+ else
169
+ C.green '.'
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,12 @@
1
+ # This is the only monkeypatching we do in LeftRight, since
2
+ # Test::Unit::AutoRunner has no API for changing which runner to use. In
3
+ # fact, it has a hardcoded list of runners.
4
+
5
+ class Test::Unit::AutoRunner
6
+ alias :initialize_without_leftright :initialize
7
+
8
+ def initialize(*args)
9
+ initialize_without_leftright *args
10
+ @runner = lambda { |r| LeftRight::Runner }
11
+ end
12
+ end
@@ -0,0 +1,28 @@
1
+ # This is just here to avoid depending on Term::ANSIColor and such, since
2
+ # we need so little, and need it to transparently do nothing when
3
+ # STDOUT is not a terminal.
4
+
5
+ module LeftRight
6
+ module C
7
+ if STDOUT.tty?
8
+ def self.color(args)
9
+ name, code = args.keys.first, args.values.first
10
+
11
+ eval %' def self.#{name}(string = nil)
12
+ string.nil? ? "\e[#{code}m" : "\e[#{code}m" + string + "\e[0m"
13
+ end ', binding, __FILE__, __LINE__
14
+ end
15
+
16
+ color :red => 31
17
+ color :green => 32
18
+ color :yellow => 33
19
+ color :cyan => 36
20
+ color :reset => 0
21
+ color :bold => 1
22
+ else
23
+ def self.method_missing(color, *args)
24
+ args.first.nil? ? '' : args.first
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,113 @@
1
+ # This is the replacement for Test::Unit::UI::Console::TestRunner
2
+
3
+ module LeftRight
4
+ class Runner < Test::Unit::UI::Console::TestRunner
5
+ # Access to the LeftRight module from the Runner instance. Hopefully to
6
+ # reduce the likelyhood of future name clashes.
7
+ #
8
+ def lr
9
+ LeftRight
10
+ end
11
+
12
+ # We intercept this to be able to set some pertinent state.
13
+ #
14
+ def test_started(test_name)
15
+ name = lr.extract_class_name test_name
16
+ lr.state.class = lr.testcase_classes.detect { |c| c.name == name }
17
+
18
+ super
19
+ end
20
+
21
+ # We intercept this to be able to set some pertinent state, as well as
22
+ # change all remaining test methods in the current class to just skip,
23
+ # since we already failed once at this point.
24
+ #
25
+ def add_fault(fault)
26
+ lr.state.fault = fault
27
+ lr.skip_testing_class lr.state.class
28
+
29
+ super
30
+ end
31
+
32
+ # Test::Unit uses this method to print '.', 'F', 'E', and possibly
33
+ # others. We do most of the work here, using the state saved in
34
+ # 'add_fault' and 'test_finished'.
35
+ #
36
+ def output_single(captured, *etc)
37
+ # Make sure we are printing a test result
38
+ return super unless %w[ . F E ].include? captured
39
+
40
+ # Do nothing if the method was a skipper
41
+ return if lr.state.skip && '.' == captured
42
+
43
+ output = case captured
44
+ when '.' then lr.P
45
+ when 'F' then lr.F
46
+ when 'E' then lr.E
47
+ end
48
+
49
+ if lr.state.last_class_printed != lr.state.class
50
+ # If we're here, we need to print a new class name on the left side
51
+ lr.state.last_class_printed = lr.state.class
52
+ lr.state.dots = 0
53
+ @io.write "\n"
54
+ @io.write lr.justify_left_side(
55
+ lr.format_class_name(lr.state.class.name))
56
+ elsif captured != '.'
57
+ # This handles the edge case when the first test for a class fails
58
+ @io.write "\n"
59
+ @io.write lr.justify_left_side
60
+ end
61
+
62
+ # Justify all lines but first:
63
+ output.gsub! "\n", "\n" + lr.justify_left_side
64
+
65
+ @io.write output
66
+ ensure # reset all of the nasty state stuff
67
+ @io.flush
68
+ lr.state.previous_failed = captured != '.'
69
+ lr.state.skip = false
70
+ end
71
+
72
+ # This prints the final summary at the end of all tests.
73
+ #
74
+ def finished(elapsed_time)
75
+ passed_count = @result.run_count -
76
+ @result.failure_count -
77
+ @result.error_count - lr.state.skipped_count
78
+
79
+ total = { :passed => passed_count,
80
+ :failed => @result.failure_count,
81
+ :errors => @result.error_count,
82
+ :tests => @result.run_count,
83
+ :skipped => lr.state.skipped_count }
84
+
85
+ results = []
86
+
87
+ unless passed_count.zero?
88
+ total[:passed] = 'all' if passed_count == @result.run_count
89
+ results << lr::C.green("#{total[:passed]} passed")
90
+ end
91
+
92
+ unless lr.state.skipped_count.zero?
93
+ results << lr::C.cyan("#{total[:skipped]} skipped")
94
+ end
95
+
96
+ unless @result.failure_count.zero?
97
+ results << lr::C.red("#{total[:failed]} failed")
98
+ end
99
+
100
+ unless @result.error_count.zero?
101
+ plural = @result.error_count > 1 ? 'errors' : 'error'
102
+ results << lr::C.yellow("#{total[:errors]} #{plural}")
103
+ end
104
+
105
+ @io.write "\n"
106
+ @io.write "\n" unless lr.state.previous_failed
107
+ @io.write "#{total[:tests]} test#{'s' if @result.run_count > 1}: "
108
+ @io.write results.join(', ').reverse.sub(',', 'dna ').reverse # :(
109
+ @io.write "\n" + "\n"
110
+ @io.puts "(#{elapsed_time} seconds)"
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,3 @@
1
+ module LeftRight
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: leftright
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jordi Bunster
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-28 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: leftright is kind of like the redgreen gem. It makes passing tests look green, exceptions yellow, and failures red. It also has a few features that make your workflow a bit faster (see README).
17
+ email: jordi@bunster.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - MIT-LICENSE
26
+ - README.rdoc
27
+ - leftright.gemspec
28
+ - lib/leftright.rb
29
+ - lib/leftright/autorun.rb
30
+ - lib/leftright/color.rb
31
+ - lib/leftright/runner.rb
32
+ - lib/leftright/version.rb
33
+ has_rdoc: true
34
+ homepage:
35
+ licenses: []
36
+
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.3.5
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: Cool replacement for Test::Unit's TestRunner
61
+ test_files: []
62
+