Module: LeftRight

Defined in:
lib/leftright/color.rb,
lib/leftright.rb,
lib/leftright/tty.rb,
lib/leftright/runner.rb,
lib/leftright/version.rb,
lib/leftright/force_tty.rb

Overview

This is the replacement for Test::Unit::UI::Console::TestRunner

Defined Under Namespace

Modules: C Classes: Runner

Constant Summary collapse

MID_SEPARATOR =

In counts of ‘ ’:

1
RIGHT_MARGIN =
1
LEFT_MARGIN =
1
VERSION =
'0.9.1'
LOCATION =

:nodoc:

File.dirname __FILE__

Class Method Summary collapse

Class Method Details

.EObject

Returns the current fault as a formatted error message.



194
195
196
# File 'lib/leftright.rb', line 194

def self.E
  F C.yellow
end

.extract_class_name(test_name) ⇒ Object

This gets the class name from the ‘test_name’ method on a Test::Unit Failure or Error. They look like test_method_name(TestCase),



142
143
144
# File 'lib/leftright.rb', line 142

def self.extract_class_name(test_name)
  test_name.scan(/\(([^(|)]+)\)/x).flatten.last
end

.F(color = C.red) ⇒ Object

Returns the current fault as a formatted failure message.



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/leftright.rb', line 157

def self.F(color = C.red)
  # First, we wrap each line individually, to keep existing line breaks:
  lines = state.fault.long_display.split("\n")

  # Drop the redundant "Failure: ", "test: " (shoulda), "test_", etc
  lines.shift if lines.first.match /Failure:|Error:/
  lines.first.sub! /^test[\ |:|_]?/i,    ''

  # Drop the class name in () from the test method name
  lines.first.sub! /\(#{state.class}\)/, ''

  # shoulda puts '. :' at the end of method names
  lines.first.sub! /\.\ :\s?/, ':'

  # Wrap lines before coloring, since the wrapping would get confused
  # by non-printables.
  buffer = lines.map { |line| wrap line.strip }.join.strip

  # We make interesting parts of the failure message bold:
  [ /(`[^']+')/m,          # Stuff in `quotes'
    /("[^"]+")/m,          # Stuff in "quotes"
    /([^\/|\[]+\.rb:\d+)/, # Filenames with line numbers (without [box])
    /(\s+undefined\s+)/ ].each do |interesting|
    buffer.gsub! interesting, ( C.bold + '\0' + C.reset + color )
  end

  # These are great for assert_equal and similar:
  buffer.sub! /(<)(.*)(>\s+expected)/,
    '\1' + C.bold + '\2' + C.reset + color + '\3'
  buffer.sub! /(but\s+was\s+<)(.*)(>\.)/,
    '\1' + C.bold + '\2' + C.reset + color + '\3'

  color + buffer + C.reset + "\n"
end

.format_class_name(class_name) ⇒ Object

Formats a class name to display on the left side.



77
78
79
# File 'lib/leftright.rb', line 77

def self.format_class_name(class_name)
  class_name.chomp 'Test'
end

.justify_left_side(str = '') ⇒ Object

Returns the given string, right-justified onto the left side.



135
136
137
# File 'lib/leftright.rb', line 135

def self.justify_left_side(str = '')
  str.to_s.rjust(left_side_width) + (' ' * MID_SEPARATOR)
end

.left_side_widthObject

Tries to get the left side width in columns.



119
120
121
122
123
124
125
# File 'lib/leftright.rb', line 119

def self.left_side_width
  @left_side_width ||= begin
    testcase_classes.map do |c|
      format_class_name(c.name).size + LEFT_MARGIN
    end.max
  end
end

.ncurses_terminal_widthObject

Uses ffi-ncurses to determine terminal width



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/leftright.rb', line 100

def self.ncurses_terminal_width
  require 'ffi-ncurses'

  begin
    FFI::NCurses.initscr
    FFI::NCurses.getmaxyx(FFI::NCurses._initscr).reverse.first.to_i
  rescue
    0
  ensure
    FFI::NCurses.endwin
  end
rescue LoadError
  puts %{ Under JRuby, you need to install the 'ffi-ncurses' gem,
          since `stty` is not available. }.strip.gsub(/\s+/, ' ')
  exit 1
end

.PObject

Returns a passing dot, aware of how many to print per-line.



200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/leftright.rb', line 200

def self.P
  return '.' unless tty?

  state.dots += 1

  max_dots = right_side_width - RIGHT_MARGIN - MID_SEPARATOR

  if state.dots >= max_dots
    state.dots = 1
    "\n" + C.green('.')
  else
    C.green '.'
  end
end

.right_side_widthObject

Tries to get the right side width in columns.



129
130
131
# File 'lib/leftright.rb', line 129

def self.right_side_width
  terminal_width - left_side_width
end

.skip_testing_class(klass) ⇒ Object

Replaces all instance methods beginning with ‘test’ in the given class with stubs that skip testing.



64
65
66
67
68
69
70
71
72
73
# File 'lib/leftright.rb', line 64

def self.skip_testing_class(klass)
  klass.instance_methods.each do |m|
    if 'test' == m.to_s[0,4]
      klass.send :define_method, m.to_sym do
        ::LeftRight.state.skip = true
        ::LeftRight.state.skipped_count += 1
      end
    end
  end
end

.stateObject

This whole thing is fairly gnarly, needing to keep state across multiple parts of the crazyness that is Test::Unit, so we keep it all here.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/leftright.rb', line 20

def self.state
  @state ||= begin
    fields = [
      :dots,               # the number of dots ('.') printed on this line
      :class,              # the TestCase-extending class being tested
      :fault,              # the current Test::Unit Failure/Error object
      :last_class_printed, # last class printed on the left side
      :previous_failed,    # true if the previous test failed/exceptioned
      :skip,               # true if the current test was a skip
      :skipped_count       # total number of skipped tests so far
    ]

    state = Struct.new(*fields).new
    state.skipped_count = 0
    state.dots          = 0
    state
  end
end

.stty_terminal_widthObject

Uses stty to determine terminal width



92
93
94
95
96
# File 'lib/leftright.rb', line 92

def self.stty_terminal_width
  `stty size`.split[-1].to_i
rescue
  0
end

.terminal_widthObject

Tries to get the terminal width in columns.



83
84
85
86
87
88
89
# File 'lib/leftright.rb', line 83

def self.terminal_width
  @terminal_width ||= if defined?(RUBY_ENGINE) && RUBY_ENGINE.match('jruby')
    ncurses_terminal_width
  else
    stty_terminal_width
  end
end

.testcase_classesObject

Gets all descendants of Class that also descend from Test::Unit::TestCase



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/leftright.rb', line 41

def self.testcase_classes
  @testcase_classes ||= begin
    found = []

    ObjectSpace.each_object(Class) do |klass|
      found << klass if Test::Unit::TestCase > klass
    end

    # Rails 2.3.5 defines these, and they cramp up my style.
    found.reject! { |k| k.to_s == 'ActiveRecord::TestCase'            }
    found.reject! { |k| k.to_s == 'ActionMailer::TestCase'            }
    found.reject! { |k| k.to_s == 'ActionView::TestCase'              }
    found.reject! { |k| k.to_s == 'ActionController::TestCase'        }
    found.reject! { |k| k.to_s == 'ActionController::IntegrationTest' }
    found.reject! { |k| k.to_s == 'ActiveSupport::TestCase'           }

    found
  end
end

.tty?Boolean

Are we running under a terminal? YES.

Returns:

  • (Boolean)


3
4
5
# File 'lib/leftright/tty.rb', line 3

def self.tty?
  STDOUT.tty?
end

.wrap(line) ⇒ Object

Wraps the given lines at word boundaries. Ripped right out of blog.macromates.com/2006/wrapping-text-with-regular-expressions/



149
150
151
152
153
# File 'lib/leftright.rb', line 149

def self.wrap(line)
  return line unless tty?
  width = right_side_width - MID_SEPARATOR - RIGHT_MARGIN
  line.gsub /(.{1,#{width}})( +|$)\n?|(.{#{width}})/, "\\1\\3\n"
end