grosser-autotest 4.0.3
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.
- data/History.txt +574 -0
- data/README.markdown +74 -0
- data/VERSION.yml +4 -0
- data/bin/autotest +52 -0
- data/bin/unit_diff +37 -0
- data/lib/autotest.rb +661 -0
- data/lib/autotest/autoupdate.rb +26 -0
- data/lib/autotest/camping.rb +37 -0
- data/lib/autotest/cctray.rb +57 -0
- data/lib/autotest/discover.rb +6 -0
- data/lib/autotest/emacs.rb +36 -0
- data/lib/autotest/email_notify.rb +66 -0
- data/lib/autotest/fixtures.rb +12 -0
- data/lib/autotest/growl.rb +29 -0
- data/lib/autotest/heckle.rb +14 -0
- data/lib/autotest/html_report.rb +31 -0
- data/lib/autotest/jabber_notify.rb +111 -0
- data/lib/autotest/kdenotify.rb +14 -0
- data/lib/autotest/menu.rb +51 -0
- data/lib/autotest/migrate.rb +7 -0
- data/lib/autotest/notify.rb +34 -0
- data/lib/autotest/once.rb +9 -0
- data/lib/autotest/pretty.rb +83 -0
- data/lib/autotest/rails.rb +81 -0
- data/lib/autotest/rcov.rb +22 -0
- data/lib/autotest/redgreen.rb +21 -0
- data/lib/autotest/restart.rb +11 -0
- data/lib/autotest/shame.rb +45 -0
- data/lib/autotest/snarl.rb +51 -0
- data/lib/autotest/timestamp.rb +9 -0
- data/lib/unit_diff.rb +258 -0
- data/test/helper.rb +6 -0
- data/test/test_autotest.rb +454 -0
- data/test/test_unit_diff.rb +313 -0
- metadata +89 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
module Autotest::Menu
|
4
|
+
WINDOZE = /win32/ =~ RUBY_PLATFORM unless defined? WINDOZE
|
5
|
+
|
6
|
+
if WINDOZE then
|
7
|
+
require "Win32API"
|
8
|
+
def self.getchar
|
9
|
+
Win32API.new("crtdll", "_getch", [], "L").Call
|
10
|
+
end
|
11
|
+
else
|
12
|
+
STTY_SAVE_STATE=`stty -g`
|
13
|
+
def self.getchar
|
14
|
+
system 'stty raw echo'
|
15
|
+
STDIN.getc
|
16
|
+
ensure
|
17
|
+
system "stty '#{STTY_SAVE_STATE}'"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.menu(choices)
|
22
|
+
result = nil
|
23
|
+
choices.sort.each do |c, desc|
|
24
|
+
puts "#{c.chr}: #{desc}"
|
25
|
+
end
|
26
|
+
until choices[result]
|
27
|
+
print "menu> "
|
28
|
+
result = getchar
|
29
|
+
print " invalid input" unless choices[result]
|
30
|
+
puts
|
31
|
+
end
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
35
|
+
Autotest.add_hook(:interrupt) do |at|
|
36
|
+
$stderr.puts "menu"
|
37
|
+
case menu ?q => "quit", ?c => "continue", ?r => "restart"
|
38
|
+
when ?c
|
39
|
+
true
|
40
|
+
when ?r
|
41
|
+
at.reset
|
42
|
+
true
|
43
|
+
when ?q
|
44
|
+
at.wants_to_quit = true
|
45
|
+
true
|
46
|
+
else
|
47
|
+
false
|
48
|
+
end
|
49
|
+
# puts "you chose #{c.chr}"
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Autotest::Notify
|
2
|
+
def self.notify(title, message, priority='critical')
|
3
|
+
icon = if priority == 'critical'
|
4
|
+
'dialog-error'
|
5
|
+
else
|
6
|
+
'dialog-information'
|
7
|
+
end
|
8
|
+
system "notify-send -u #{priority} -t 10000 -i #{icon} '#{title}' '#{message.inspect}'"
|
9
|
+
end
|
10
|
+
|
11
|
+
Autotest.add_hook :red do |at|
|
12
|
+
tests = 0
|
13
|
+
assertions = 0
|
14
|
+
failures = 0
|
15
|
+
errors = 0
|
16
|
+
at.results.scan(/(\d+) tests, (\d+) assertions, (\d+) failures, (\d+) errors/) do |t, a, f, e|
|
17
|
+
tests += t.to_i
|
18
|
+
assertions += a.to_i
|
19
|
+
failures += f.to_i
|
20
|
+
errors += e.to_i
|
21
|
+
end
|
22
|
+
message = "%d tests, %d assertions, %d failures, %d errors" %
|
23
|
+
[tests, assertions, failures, errors]
|
24
|
+
notify("Tests Failed", message)
|
25
|
+
end
|
26
|
+
|
27
|
+
Autotest.add_hook :green do |at|
|
28
|
+
notify("Tests Passed", "Outstanding tests passed", 'low') if at.tainted
|
29
|
+
end
|
30
|
+
|
31
|
+
Autotest.add_hook :all do |at|_hook
|
32
|
+
notify("autotest", "Tests have fully passed", 'low')
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'osx/cocoa'
|
4
|
+
include Math
|
5
|
+
include OSX
|
6
|
+
|
7
|
+
OSX::NSBundle.bundleWithPath(File.expand_path("~/Library/Frameworks/Aquaterm.framework")).load
|
8
|
+
OSX.ns_import :AQTAdapter
|
9
|
+
|
10
|
+
class Autotest::Pretty
|
11
|
+
BLACK = 0
|
12
|
+
WHITE = 1
|
13
|
+
RED = 2
|
14
|
+
GREEN = 3
|
15
|
+
GRAY = 4
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@past = []
|
19
|
+
|
20
|
+
@adapter = AQTAdapter.alloc.init
|
21
|
+
@adapter.openPlotWithIndex 1
|
22
|
+
@adapter.setPlotSize([122,122])
|
23
|
+
@adapter.setPlotTitle("Autotest Status")
|
24
|
+
|
25
|
+
@adapter.setColormapEntry_red_green_blue(0, 0.0, 0.0, 0.0) # black
|
26
|
+
@adapter.setColormapEntry_red_green_blue(1, 1.0, 1.0, 1.0) # white
|
27
|
+
@adapter.setColormapEntry_red_green_blue(2, 1.0, 0.0, 0.0) # red
|
28
|
+
@adapter.setColormapEntry_red_green_blue(3, 0.0, 1.0, 0.0) # green
|
29
|
+
@adapter.setColormapEntry_red_green_blue(4, 0.7, 0.7, 0.7) # gray
|
30
|
+
|
31
|
+
draw
|
32
|
+
end
|
33
|
+
|
34
|
+
def draw
|
35
|
+
@past.shift if @past.size > 100
|
36
|
+
|
37
|
+
@adapter.takeColorFromColormapEntry(@past.last ? GREEN : RED)
|
38
|
+
@adapter.addFilledRect([0, 0, 122, 122])
|
39
|
+
|
40
|
+
@adapter.takeColorFromColormapEntry(BLACK)
|
41
|
+
@adapter.addFilledRect([10, 10, 102, 102])
|
42
|
+
|
43
|
+
@adapter.takeColorFromColormapEntry(GRAY)
|
44
|
+
@adapter.addFilledRect([11, 11, 100, 100])
|
45
|
+
|
46
|
+
@adapter.takeColorFromColormapEntry(0)
|
47
|
+
|
48
|
+
@past.each_with_index do |passed,i|
|
49
|
+
x = i % 10
|
50
|
+
y = i / 10
|
51
|
+
|
52
|
+
@adapter.takeColorFromColormapEntry(passed ? GREEN : RED)
|
53
|
+
@adapter.addFilledRect([x*10+11, y*10+11, 10, 10])
|
54
|
+
end
|
55
|
+
@adapter.renderPlot
|
56
|
+
end
|
57
|
+
|
58
|
+
def pass
|
59
|
+
@past.push true
|
60
|
+
draw
|
61
|
+
end
|
62
|
+
|
63
|
+
def fail
|
64
|
+
@past.push false
|
65
|
+
draw
|
66
|
+
end
|
67
|
+
|
68
|
+
def close
|
69
|
+
@adapter.closePlot
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
unless $TESTING then
|
74
|
+
board = Autotest::Pretty.new
|
75
|
+
|
76
|
+
Autotest.add_hook :red do |at|
|
77
|
+
board.fail unless $TESTING
|
78
|
+
end
|
79
|
+
|
80
|
+
Autotest.add_hook :green do |at|
|
81
|
+
board.pass unless $TESTING
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'autotest'
|
2
|
+
|
3
|
+
class Autotest::Rails < Autotest
|
4
|
+
|
5
|
+
def initialize # :nodoc:
|
6
|
+
super
|
7
|
+
|
8
|
+
add_exception %r%^\./(?:db|doc|log|public|script|tmp|vendor)%
|
9
|
+
|
10
|
+
clear_mappings
|
11
|
+
|
12
|
+
self.add_mapping(/^lib\/.*\.rb$/) do |filename, _|
|
13
|
+
impl = File.basename(filename, '.rb')
|
14
|
+
files_matching %r%^test/unit/#{impl}_test.rb$%
|
15
|
+
# TODO: (unit|functional|integration) maybe?
|
16
|
+
end
|
17
|
+
|
18
|
+
add_mapping %r%^test/fixtures/(.*)s.yml% do |_, m|
|
19
|
+
["test/unit/#{m[1]}_test.rb",
|
20
|
+
"test/controllers/#{m[1]}_controller_test.rb",
|
21
|
+
"test/views/#{m[1]}_view_test.rb",
|
22
|
+
"test/functional/#{m[1]}_controller_test.rb"]
|
23
|
+
end
|
24
|
+
|
25
|
+
add_mapping %r%^test/(unit|integration|controllers|views|functional)/.*rb$% do |filename, _|
|
26
|
+
filename
|
27
|
+
end
|
28
|
+
|
29
|
+
add_mapping %r%^app/models/(.*)\.rb$% do |_, m|
|
30
|
+
"test/unit/#{m[1]}_test.rb"
|
31
|
+
end
|
32
|
+
|
33
|
+
add_mapping %r%^app/helpers/application_helper.rb% do
|
34
|
+
files_matching %r%^test/(views|functional)/.*_test\.rb$%
|
35
|
+
end
|
36
|
+
|
37
|
+
add_mapping %r%^app/helpers/(.*)_helper.rb% do |_, m|
|
38
|
+
if m[1] == "application" then
|
39
|
+
files_matching %r%^test/(views|functional)/.*_test\.rb$%
|
40
|
+
else
|
41
|
+
["test/views/#{m[1]}_view_test.rb",
|
42
|
+
"test/functional/#{m[1]}_controller_test.rb"]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
add_mapping %r%^app/views/(.*)/% do |_, m|
|
47
|
+
["test/views/#{m[1]}_view_test.rb",
|
48
|
+
"test/functional/#{m[1]}_controller_test.rb"]
|
49
|
+
end
|
50
|
+
|
51
|
+
add_mapping %r%^app/controllers/(.*)\.rb$% do |_, m|
|
52
|
+
if m[1] == "application" then
|
53
|
+
files_matching %r%^test/(controllers|views|functional)/.*_test\.rb$%
|
54
|
+
else
|
55
|
+
["test/controllers/#{m[1]}_test.rb",
|
56
|
+
"test/functional/#{m[1]}_test.rb"]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
add_mapping %r%^app/views/layouts/% do
|
61
|
+
"test/views/layouts_view_test.rb"
|
62
|
+
end
|
63
|
+
|
64
|
+
add_mapping %r%^config/routes.rb$% do # FIX:
|
65
|
+
files_matching %r%^test/(controllers|views|functional)/.*_test\.rb$%
|
66
|
+
end
|
67
|
+
|
68
|
+
add_mapping %r%^test/test_helper.rb|config/((boot|environment(s/test)?).rb|database.yml)% do
|
69
|
+
files_matching %r%^test/(unit|controllers|views|functional)/.*_test\.rb$%
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Convert the pathname s to the name of class.
|
74
|
+
def path_to_classname(s)
|
75
|
+
sep = File::SEPARATOR
|
76
|
+
f = s.sub(/^test#{sep}((unit|functional|integration|views|controllers|helpers)#{sep})?/, '').sub(/\.rb$/, '').split(sep)
|
77
|
+
f = f.map { |path| path.split(/_/).map { |seg| seg.capitalize }.join }
|
78
|
+
f = f.map { |path| path =~ /Test$/ ? path : "#{path}Test" }
|
79
|
+
f.join('::')
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Autotest::RCov
|
2
|
+
@@command, @@pattern = "rcov", "test/*.rb"
|
3
|
+
|
4
|
+
def self.command= o
|
5
|
+
@@command = o
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.pattern= o
|
9
|
+
@@pattern = o
|
10
|
+
end
|
11
|
+
|
12
|
+
Autotest.add_hook :all_good do |at|
|
13
|
+
system "rake #{@@command} PATTERN=#{@@pattern}"
|
14
|
+
end
|
15
|
+
|
16
|
+
Autotest.add_hook :initialize do |at|
|
17
|
+
at.add_exception 'coverage'
|
18
|
+
at.add_exception 'coverage.info'
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
# special thanks to Pat Eyler, Sean Carley, and Rob Sanheim
|
4
|
+
# and to Peter Havens for rspec patches
|
5
|
+
module Autotest::RedGreen
|
6
|
+
BAR = "=" * 78
|
7
|
+
REDCODE = 31
|
8
|
+
GREENCODE = 32
|
9
|
+
|
10
|
+
Autotest.add_hook :ran_command do |at|
|
11
|
+
green = case at.results.last
|
12
|
+
when /^.* (\d+) failures, (\d+) errors$/ # Test::Unit
|
13
|
+
($1 == "0" and $2 == "0")
|
14
|
+
when /^\d+\s+examples?,\s+(\d+)\s+failure/ # RSpec
|
15
|
+
($1 == "0")
|
16
|
+
end
|
17
|
+
|
18
|
+
code = green ? GREENCODE : REDCODE
|
19
|
+
puts "\e[#{ code }m#{ BAR }\e[0m\n\n" unless green.nil?
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'code_statistics'
|
2
|
+
require 'rbosa'
|
3
|
+
|
4
|
+
module Autotest::Shame
|
5
|
+
@@chat_app = :adium
|
6
|
+
|
7
|
+
def self.chat_app= o
|
8
|
+
@@chat_app = o
|
9
|
+
end
|
10
|
+
|
11
|
+
# Until the rails team learns how to write modular code... I must steal :/
|
12
|
+
STATS_DIRECTORIES = [
|
13
|
+
%w(Controllers app/controllers),
|
14
|
+
%w(Helpers app/helpers),
|
15
|
+
%w(Models app/models),
|
16
|
+
%w(Libraries lib/),
|
17
|
+
%w(APIs app/apis),
|
18
|
+
%w(Components components),
|
19
|
+
%w(Integration\ tests test/integration),
|
20
|
+
%w(Functional\ tests test/functional),
|
21
|
+
%w(Unit\ tests test/unit),
|
22
|
+
].select { |name, dir| File.directory?(dir) }
|
23
|
+
|
24
|
+
def self.shame
|
25
|
+
stats = CodeStatistics.new(*STATS_DIRECTORIES)
|
26
|
+
code = stats.send :calculate_code
|
27
|
+
tests = stats.send :calculate_tests
|
28
|
+
msg = "Code To Test Ratio: 1:#{sprintf("%.2f", tests.to_f/code)}"
|
29
|
+
$-w = ! $-w
|
30
|
+
case @@chat_app
|
31
|
+
when :adium then
|
32
|
+
OSA.app('Adium').adium_controller.my_status_message = msg
|
33
|
+
when :ichat then
|
34
|
+
OSA.app('ichat').status_message = msg
|
35
|
+
else
|
36
|
+
raise "huh?"
|
37
|
+
end
|
38
|
+
$-w = ! $-w
|
39
|
+
$stderr.puts "Status set to: #{msg.inspect}"
|
40
|
+
end
|
41
|
+
|
42
|
+
Autotest.add_hook(:all_good) do |autotest|
|
43
|
+
shame
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# special thanks to: Patrick Hurley <phurley@gmail.com>
|
2
|
+
# requires the ruby-snarl gem.
|
3
|
+
|
4
|
+
begin require 'rubygems'; rescue LoadError; end
|
5
|
+
require 'snarl'
|
6
|
+
|
7
|
+
module Autotest::Snarl
|
8
|
+
def self.icon
|
9
|
+
# icons from http://www.famfamfam.com/lab/icons/silk/
|
10
|
+
path = File.join(File.dirname(__FILE__), "/../icons")
|
11
|
+
{
|
12
|
+
:green => "#{path}/accept.png",
|
13
|
+
:red => "#{path}/exclamation.png",
|
14
|
+
:info => "#{path}/information.png"
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.snarl title, msg, ico = nil
|
19
|
+
Snarl.show_message(title, msg, icon[ico])
|
20
|
+
end
|
21
|
+
|
22
|
+
Autotest.add_hook :run do |at|
|
23
|
+
snarl "Run", "Run" unless $TESTING
|
24
|
+
end
|
25
|
+
|
26
|
+
Autotest.add_hook :red do |at|
|
27
|
+
failed_tests = at.files_to_test.inject(0){ |s,a| k,v = a; s + v.size}
|
28
|
+
snarl "Tests Failed", "#{failed_tests} tests failed", :red
|
29
|
+
end
|
30
|
+
|
31
|
+
Autotest.add_hook :green do |at|
|
32
|
+
snarl "Tests Passed", "All tests passed", :green #if at.tainted
|
33
|
+
end
|
34
|
+
|
35
|
+
Autotest.add_hook :run do |at|
|
36
|
+
snarl "autotest", "autotest was started", :info unless $TESTING
|
37
|
+
end
|
38
|
+
|
39
|
+
Autotest.add_hook :interrupt do |at|
|
40
|
+
snarl "autotest", "autotest was reset", :info unless $TESTING
|
41
|
+
end
|
42
|
+
|
43
|
+
Autotest.add_hook :quit do |at|
|
44
|
+
snarl "autotest", "autotest is exiting", :info unless $TESTING
|
45
|
+
end
|
46
|
+
|
47
|
+
Autotest.add_hook :all do |at|_hook
|
48
|
+
snarl "autotest", "Tests have fully passed", :green unless $TESTING
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/lib/unit_diff.rb
ADDED
@@ -0,0 +1,258 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
##
|
4
|
+
# UnitDiff makes reading Test::Unit output easy and fun. Instead of a
|
5
|
+
# confusing jumble of text with nearly unnoticable changes like this:
|
6
|
+
#
|
7
|
+
# 1) Failure:
|
8
|
+
# test_to_gpoints(RouteTest) [test/unit/route_test.rb:29]:
|
9
|
+
# <"new GPolyline([\n new GPoint( 47.00000, -122.00000),\n new GPoint( 46.5000
|
10
|
+
# 0, -122.50000),\n new GPoint( 46.75000, -122.75000),\n new GPoint( 46.00000,
|
11
|
+
# -123.00000)])"> expected but was
|
12
|
+
# <"new Gpolyline([\n new GPoint( 47.00000, -122.00000),\n new GPoint( 46.5000
|
13
|
+
# 0, -122.50000),\n new GPoint( 46.75000, -122.75000),\n new GPoint( 46.00000,
|
14
|
+
# -123.00000)])">.
|
15
|
+
#
|
16
|
+
#
|
17
|
+
# You get an easy-to-read diff output like this:
|
18
|
+
#
|
19
|
+
# 1) Failure:
|
20
|
+
# test_to_gpoints(RouteTest) [test/unit/route_test.rb:29]:
|
21
|
+
# 1c1
|
22
|
+
# < new GPolyline([
|
23
|
+
# ---
|
24
|
+
# > new Gpolyline([
|
25
|
+
#
|
26
|
+
# == Usage
|
27
|
+
#
|
28
|
+
# test.rb | unit_diff [options]
|
29
|
+
# options:
|
30
|
+
# -b ignore whitespace differences
|
31
|
+
# -c contextual diff
|
32
|
+
# -h show usage
|
33
|
+
# -k keep temp diff files around
|
34
|
+
# -l prefix line numbers on the diffs
|
35
|
+
# -u unified diff
|
36
|
+
# -v display version
|
37
|
+
|
38
|
+
class UnitDiff
|
39
|
+
|
40
|
+
WINDOZE = /win32/ =~ RUBY_PLATFORM unless defined? WINDOZE
|
41
|
+
DIFF = if WINDOZE
|
42
|
+
'diff.exe'
|
43
|
+
else
|
44
|
+
if system("gdiff", __FILE__, __FILE__)
|
45
|
+
'gdiff' # solaris and kin suck
|
46
|
+
else
|
47
|
+
'diff'
|
48
|
+
end
|
49
|
+
end unless defined? DIFF
|
50
|
+
|
51
|
+
##
|
52
|
+
# Handy wrapper for UnitDiff#unit_diff.
|
53
|
+
|
54
|
+
def self.unit_diff
|
55
|
+
trap 'INT' do exit 1 end
|
56
|
+
puts UnitDiff.new.unit_diff
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_input(input, output)
|
60
|
+
current = []
|
61
|
+
data = []
|
62
|
+
data << current
|
63
|
+
print_lines = true
|
64
|
+
|
65
|
+
term = "\nFinished".split(//).map { |c| c[0] }
|
66
|
+
term_length = term.size
|
67
|
+
|
68
|
+
old_sync = output.sync
|
69
|
+
output.sync = true
|
70
|
+
while line = input.gets
|
71
|
+
case line
|
72
|
+
when /^(Loaded suite|Started)/ then
|
73
|
+
print_lines = true
|
74
|
+
output.puts line
|
75
|
+
chars = []
|
76
|
+
while c = input.getc do
|
77
|
+
output.putc c
|
78
|
+
chars << c
|
79
|
+
tail = chars[-term_length..-1]
|
80
|
+
break if chars.size >= term_length and tail == term
|
81
|
+
end
|
82
|
+
output.puts input.gets # the rest of "Finished in..."
|
83
|
+
output.puts
|
84
|
+
next
|
85
|
+
when /^\s*$/, /^\(?\s*\d+\) (Failure|Error):/, /^\d+\)/ then
|
86
|
+
print_lines = false
|
87
|
+
current = []
|
88
|
+
data << current
|
89
|
+
when /^Finished in \d/ then
|
90
|
+
print_lines = false
|
91
|
+
end
|
92
|
+
output.puts line if print_lines
|
93
|
+
current << line
|
94
|
+
end
|
95
|
+
output.sync = old_sync
|
96
|
+
data = data.reject { |o| o == ["\n"] or o.empty? }
|
97
|
+
footer = data.pop
|
98
|
+
|
99
|
+
return data, footer
|
100
|
+
end
|
101
|
+
|
102
|
+
# Parses a single diff recording the header and what
|
103
|
+
# was expected, and what was actually obtained.
|
104
|
+
def parse_diff(result)
|
105
|
+
header = []
|
106
|
+
expect = []
|
107
|
+
butwas = []
|
108
|
+
footer = []
|
109
|
+
found = false
|
110
|
+
state = :header
|
111
|
+
|
112
|
+
until result.empty? do
|
113
|
+
case state
|
114
|
+
when :header then
|
115
|
+
header << result.shift
|
116
|
+
state = :expect if result.first =~ /^<|^Expected/
|
117
|
+
when :expect then
|
118
|
+
case result.first
|
119
|
+
when /^Expected (.*?) to equal (.*?):$/ then
|
120
|
+
expect << $1
|
121
|
+
butwas << $2
|
122
|
+
state = :footer
|
123
|
+
result.shift
|
124
|
+
when /^Expected (.*?), not (.*)$/m then
|
125
|
+
expect << $1
|
126
|
+
butwas << $2
|
127
|
+
state = :footer
|
128
|
+
result.shift
|
129
|
+
when /^Expected (.*?)$/ then
|
130
|
+
expect << "#{$1}\n"
|
131
|
+
result.shift
|
132
|
+
when /^to equal / then
|
133
|
+
state = :spec_butwas
|
134
|
+
bw = result.shift.sub(/^to equal (.*):?$/, '\1')
|
135
|
+
butwas << bw
|
136
|
+
else
|
137
|
+
state = :butwas if result.first.sub!(/ expected( but was|, not)/, '')
|
138
|
+
expect << result.shift
|
139
|
+
end
|
140
|
+
when :butwas then
|
141
|
+
butwas = result[0..-1]
|
142
|
+
result.clear
|
143
|
+
when :spec_butwas then
|
144
|
+
if result.first =~ /^\s+\S+ at |^:\s*$/
|
145
|
+
state = :footer
|
146
|
+
else
|
147
|
+
butwas << result.shift
|
148
|
+
end
|
149
|
+
when :footer then
|
150
|
+
butwas.last.sub!(/:$/, '')
|
151
|
+
footer = result.map {|l| l.chomp }
|
152
|
+
result.clear
|
153
|
+
else
|
154
|
+
raise "unknown state #{state}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
return header, expect, nil, footer if butwas.empty?
|
159
|
+
|
160
|
+
expect.last.chomp!
|
161
|
+
expect.first.sub!(/^<\"/, '')
|
162
|
+
expect.last.sub!(/\">$/, '')
|
163
|
+
|
164
|
+
butwas.last.chomp!
|
165
|
+
butwas.last.chop! if butwas.last =~ /\.$/
|
166
|
+
butwas.first.sub!( /^<\"/, '')
|
167
|
+
butwas.last.sub!(/\">$/, '')
|
168
|
+
|
169
|
+
return header, expect, butwas, footer
|
170
|
+
end
|
171
|
+
|
172
|
+
##
|
173
|
+
# Scans Test::Unit output +input+ looking for comparison failures and makes
|
174
|
+
# them easily readable by passing them through diff.
|
175
|
+
|
176
|
+
def unit_diff(input=ARGF, output=$stdout)
|
177
|
+
$b = false unless defined? $b
|
178
|
+
$c = false unless defined? $c
|
179
|
+
$k = false unless defined? $k
|
180
|
+
$u = false unless defined? $u
|
181
|
+
|
182
|
+
data, footer = self.parse_input(input, output)
|
183
|
+
|
184
|
+
output = []
|
185
|
+
|
186
|
+
# Output
|
187
|
+
data.each do |result|
|
188
|
+
first = []
|
189
|
+
second = []
|
190
|
+
|
191
|
+
if result.first =~ /Error/ then
|
192
|
+
output.push result.join('')
|
193
|
+
next
|
194
|
+
end
|
195
|
+
|
196
|
+
prefix, expect, butwas, result_footer = parse_diff(result)
|
197
|
+
|
198
|
+
output.push prefix.compact.map {|line| line.strip}.join("\n")
|
199
|
+
|
200
|
+
if butwas then
|
201
|
+
output.push self.diff(expect, butwas)
|
202
|
+
|
203
|
+
output.push result_footer
|
204
|
+
output.push ''
|
205
|
+
else
|
206
|
+
output.push expect.join('')
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
if footer then
|
211
|
+
footer.shift if footer.first.strip.empty?# unless footer.first.nil?
|
212
|
+
output.push footer.compact.map {|line| line.strip}.join("\n")
|
213
|
+
end
|
214
|
+
|
215
|
+
return output.flatten.join("\n")
|
216
|
+
end
|
217
|
+
|
218
|
+
def diff expect, butwas
|
219
|
+
output = nil
|
220
|
+
|
221
|
+
Tempfile.open("expect") do |a|
|
222
|
+
a.write(massage(expect))
|
223
|
+
a.rewind
|
224
|
+
Tempfile.open("butwas") do |b|
|
225
|
+
b.write(massage(butwas))
|
226
|
+
b.rewind
|
227
|
+
|
228
|
+
diff_flags = $u ? "-u" : $c ? "-c" : ""
|
229
|
+
diff_flags += " -b" if $b
|
230
|
+
|
231
|
+
result = `#{DIFF} #{diff_flags} #{a.path} #{b.path}`
|
232
|
+
output = if result.empty? then
|
233
|
+
"[no difference--suspect ==]"
|
234
|
+
else
|
235
|
+
result.split(/\n/)
|
236
|
+
end
|
237
|
+
|
238
|
+
if $k then
|
239
|
+
warn "moving #{a.path} to #{a.path}.keep"
|
240
|
+
File.rename a.path, a.path + ".keep"
|
241
|
+
warn "moving #{b.path} to #{b.path}.keep"
|
242
|
+
File.rename b.path, b.path + ".keep"
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
output
|
248
|
+
end
|
249
|
+
|
250
|
+
def massage(data)
|
251
|
+
count = 0
|
252
|
+
# unescape newlines, strip <> from entire string
|
253
|
+
data = data.join
|
254
|
+
data = data.gsub(/\\n/, "\n").gsub(/0x[a-f0-9]+/m, '0xXXXXXX') + "\n"
|
255
|
+
data += "\n" unless data[-1] == ?\n
|
256
|
+
data
|
257
|
+
end
|
258
|
+
end
|