leftright 0.0.1

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,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
+