ratch 0.2.2 → 0.2.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.
Files changed (73) hide show
  1. data/COPYING +674 -0
  2. data/README +5 -5
  3. data/bin/lt +11 -11
  4. data/bin/ratch +3 -2
  5. data/demo/README +1 -0
  6. data/demo/WILMA +1 -0
  7. data/demo/doc/rdoc/created.rid +1 -1
  8. data/demo/doc/rdoc/files/README.html +1 -1
  9. data/demo/doc/rdoc/files/lib/foo/foo_rb.html +1 -1
  10. data/demo/doc/rdoc/index.html +3 -3
  11. data/demo/p.rb +9 -0
  12. data/demo/r.rb +6 -0
  13. data/demo/t.rb +3 -0
  14. data/demo/task/config.yaml +2 -0
  15. data/demo/{util → task}/one +0 -0
  16. data/demo/task/rdoc +88 -0
  17. data/demo/task/rdoc-old +182 -0
  18. data/demo/task/simplebuild +15 -0
  19. data/demo/task/stats +6 -0
  20. data/demo/task/task +6 -0
  21. data/demo/{util → task}/tryme +0 -0
  22. data/lib/ratch/argvutils.rb +57 -14
  23. data/lib/ratch/batch.rb +73 -21
  24. data/lib/ratch/batchable.rb +56 -26
  25. data/lib/ratch/batchfile.rb +95 -0
  26. data/lib/ratch/buildable.rb +117 -42
  27. data/lib/ratch/configutils.rb +43 -13
  28. data/lib/ratch/consoleutils.rb +76 -19
  29. data/lib/ratch/emailutils.rb +24 -0
  30. data/lib/ratch/facets/multiglob.rb +160 -0
  31. data/lib/ratch/fileutils.rb +40 -8
  32. data/lib/ratch/options.rb +43 -4
  33. data/lib/ratch/taskable.rb +48 -12
  34. data/{data/ratch/rubyproject → lib/ratch/toolset/ruby}/announce +0 -0
  35. data/lib/ratch/toolset/ruby/crosstest +305 -0
  36. data/{data/ratch/rubyproject → lib/ratch/toolset/ruby}/extest +0 -0
  37. data/{data/ratch/rubyproject → lib/ratch/toolset/ruby}/install +0 -0
  38. data/lib/ratch/toolset/ruby/isotest +293 -0
  39. data/{data/ratch/rubyproject → lib/ratch/toolset/ruby}/load +0 -0
  40. data/lib/ratch/toolset/ruby/loadtest +28 -0
  41. data/{data/ratch/rubyproject → lib/ratch/toolset/ruby}/notes +0 -0
  42. data/{data/ratch/rubyproject → lib/ratch/toolset/ruby}/publish +0 -0
  43. data/lib/ratch/toolset/ruby/rdoc +88 -0
  44. data/{data/ratch/rubyproject → lib/ratch/toolset/ruby}/setup +0 -0
  45. data/{data/ratch/rubyproject → lib/ratch/toolset/ruby}/stats +0 -0
  46. data/lib/ratch/toolset/ruby/syntax +29 -0
  47. data/lib/ratch/toolset/ruby/test +26 -0
  48. data/lib/ratch/uploadutils.rb +25 -4
  49. data/log/history.rd +10 -0
  50. data/log/recent.rd +8 -0
  51. data/log/todo.rd +2 -0
  52. data/man/ratch.man +73 -0
  53. data/meta/MANIFEST +45 -31
  54. data/meta/{RATCH-0.2.2.roll → ratch-0.2.3.roll} +3 -3
  55. data/task/clobber/package +10 -0
  56. data/task/config.yaml +4 -0
  57. data/task/man +14 -0
  58. data/task/publish +1 -1
  59. data/task/rdoc +6 -0
  60. metadata +64 -40
  61. data/LICENSE +0 -344
  62. data/data/ratch/rubyproject/rdoc +0 -42
  63. data/demo/util/conf/rdoc +0 -4
  64. data/demo/util/rdoc +0 -39
  65. data/dev/install +0 -89
  66. data/dev/install.0 +0 -49
  67. data/dev/install.1 +0 -63
  68. data/dev/ludo +0 -25
  69. data/dev/oldtaskable.rb +0 -573
  70. data/dev/taskable-simple.rb +0 -42
  71. data/dev/taskable.rb +0 -120
  72. data/lib/ratch/t.rb +0 -0
  73. data/lib/ratch/taskutils.rb +0 -41
@@ -0,0 +1,160 @@
1
+ # TITLE:
2
+ #
3
+ # Multiglob
4
+ #
5
+ # SUMMARY:
6
+ #
7
+ # Dir extensions for globbing multiple locations at once.
8
+ #
9
+ # COPYING:
10
+ #
11
+ # Copyright (c) 2007 Psi T Corp.
12
+ #
13
+ # This file is part of the ProUtils' Ratch program.
14
+ #
15
+ # Ratch is free software: you can redistribute it and/or modify
16
+ # it under the terms of the GNU General Public License as published by
17
+ # the Free Software Foundation, either version 3 of the License, or
18
+ # (at your option) any later version.
19
+ #
20
+ # Ratch is distributed in the hope that it will be useful,
21
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
22
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
+ # GNU General Public License for more details.
24
+ #
25
+ # You should have received a copy of the GNU General Public License
26
+ # along with Ratch. If not, see <http://www.gnu.org/licenses/>.
27
+
28
+ #
29
+ class Dir
30
+
31
+ # Like +glob+ but can take multiple patterns.
32
+ #
33
+ # Dir.multiglob( '*.rb', '*.py' )
34
+ #
35
+ # Rather then constants for options multiglob accepts a trailing options
36
+ # hash of symbol keys.
37
+ #
38
+ # :noescape File::FNM_NOESCAPE
39
+ # :casefold File::FNM_CASEFOLD
40
+ # :pathname File::FNM_PATHNAME
41
+ # :dotmatch File::FNM_DOTMATCH
42
+ # :strict File::FNM_PATHNAME && File::FNM_DOTMATCH
43
+ #
44
+ # It also has an option for recurse.
45
+ #
46
+ # :recurse Recurively include contents of directories.
47
+ #
48
+ # For example
49
+ #
50
+ # Dir.multiglob( '*', :recurse => true )
51
+ #
52
+ # would have the same result as
53
+ #
54
+ # Dir.multiglob('**/*')
55
+ #
56
+ # Multiglob also accepts '+' and '-' prefixes. Any entry that begins with a '-'
57
+ # is treated as an exclusion glob and will be removed from the final result.
58
+ # For example, to collect all files in the current directory, less ruby scripts:
59
+ #
60
+ # Dir.multiglob( '*', '-*.rb' )
61
+ #
62
+ # This is very useful in collecting files as specificed by a configuration
63
+ # parameter.
64
+
65
+ def self.multiglob( *patterns )
66
+ options = (Hash === patterns.last ? patterns.pop : {})
67
+
68
+ bitflags = 0
69
+ bitflags |= File::FNM_NOESCAPE if options[:noescape]
70
+ bitflags |= File::FNM_CASEFOLD if options[:casefold]
71
+ bitflags |= File::FNM_PATHNAME if options[:pathname] or options[:strict]
72
+ bitflags |= File::FNM_DOTMATCH if options[:dotmatch] or options[:strict]
73
+
74
+ patterns = [patterns].flatten.compact
75
+
76
+ patterns_include = patterns.select{ |f| f !~ /^[-]/ }
77
+ patterns_exclude = patterns.select{ |f| f =~ /^[-]/ }
78
+
79
+ patterns_include.collect!{ |f| f =~ /^[+]/ ? f[1..-1] : f }
80
+ patterns_exclude.collect!{ |f| f =~ /^[-]/ ? f[1..-1] : f }
81
+
82
+ if options[:recurse]
83
+ patterns_include += patterns_include.collect{ |f| File.join(f, '**', '*') }
84
+ patterns_exclude += patterns_exclude.collect{ |f| File.join(f, '**', '*') }
85
+ end
86
+
87
+ files = []
88
+ files += patterns_include.collect{ |pattern| Dir.glob(pattern, bitflags) }.flatten.uniq
89
+ files -= patterns_exclude.collect{ |pattern| Dir.glob(pattern, bitflags) }.flatten.uniq
90
+
91
+ return files
92
+ end
93
+
94
+ # The same as +multiglob+, but recusively includes directories.
95
+ #
96
+ # Dir.multiglob_r( 'folder' )
97
+ #
98
+ # is equivalent to
99
+ #
100
+ # Dir.multiglob( 'folder', :recurse=>true )
101
+ #
102
+ # The effect of which is
103
+ #
104
+ # Dir.multiglob( 'folder', 'folder/**/*' )
105
+
106
+ def self.multiglob_r( *patterns )
107
+ options = (Hash === patterns.last ? patterns.pop : {})
108
+ options[:recurse] = true
109
+ patterns << options
110
+ multiglob(*patterns)
111
+ end
112
+
113
+ # This is just like multiglob but handles a base pattern such that
114
+ # if the +patterns+ list starts with a '+' or '-' entry, then the base
115
+ # will be included in the result, otherwise it will be omitted.
116
+ #
117
+ # Dir.multiglob_with_default('*.yaml', '-*.rb') #=> [ 'foo.yaml' ]
118
+ # Dir.multiglob_with_default('*.yaml', '+*.rb') #=> [ 'foo.yaml', 'foo.rb' ]
119
+ # Dir.multiglob_with_default('*.yaml', '*.rb') #=> [ 'foo.rb' ]
120
+ #
121
+ # This is useful when a configuration option needs to supply a file list
122
+ # that may include files, exclude files or append files to a default list.
123
+ #
124
+ # TODO Deprecate this and replace with :default option on regular multiglob.
125
+
126
+ def self.multiglob_with_default(default, *patterns)
127
+ default = [default].flatten.compact
128
+ patterns = patterns.flatten.compact
129
+
130
+ if patterns.empty?
131
+ patterns = default
132
+ elsif patterns.first =~ /^[+-]/
133
+ patterns = default + patterns
134
+ end
135
+
136
+ multiglob(*patterns)
137
+ end
138
+
139
+ end
140
+
141
+
142
+
143
+ # _____ _
144
+ # |_ _|__ ___| |_
145
+ # | |/ _ \/ __| __|
146
+ # | | __/\__ \ |_
147
+ # |_|\___||___/\__|
148
+ #
149
+
150
+ # TODO Need to mock file system.
151
+
152
+ =begin #no test yet
153
+
154
+ require 'test/unit'
155
+
156
+ class TestDir < Test::Unit::TestCase
157
+
158
+ end
159
+
160
+ =end
@@ -1,4 +1,28 @@
1
+ # TITLE:
2
+ #
3
+ # FileUtils
4
+ #
5
+ # COPYING:
6
+ #
7
+ # Copyright (c) 2007 Psi T Corp.
8
+ #
9
+ # This file is part of the ProUtils' Ratch program.
10
+ #
11
+ # Ratch is free software: you can redistribute it and/or modify
12
+ # it under the terms of the GNU General Public License as published by
13
+ # the Free Software Foundation, either version 3 of the License, or
14
+ # (at your option) any later version.
15
+ #
16
+ # Ratch is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ # GNU General Public License for more details.
20
+ #
21
+ # You should have received a copy of the GNU General Public License
22
+ # along with Ratch. If not, see <http://www.gnu.org/licenses/>.
23
+
1
24
  require 'fileutils'
25
+ require 'ratch/facets/multiglob'
2
26
 
3
27
 
4
28
  module Ratch
@@ -11,6 +35,14 @@ module Ratch
11
35
  Dir.glob(*args, &blk)
12
36
  end
13
37
 
38
+ def multiglob(*args, &blk)
39
+ Dir.multiglob(*args, &blk)
40
+ end
41
+
42
+ def multiglob_r(*args, &blk)
43
+ Dir.multiglob_r(*args, &blk)
44
+ end
45
+
14
46
  # Read file.
15
47
 
16
48
  def file_read(path)
@@ -130,13 +162,13 @@ module Ratch
130
162
  alias_method :exist!, :exists! ; module_function :exist!
131
163
  alias_method :path!, :exists! ; module_function :path!
132
164
 
133
- # Is a file a task?
134
-
135
- def task?(path)
136
- task = File.dirname($0) + "/#{path}"
137
- task.chomp!('!')
138
- task if FileTest.file?(task) && FileTest.executable?(task)
139
- end
165
+ # # Is a file a task?
166
+ #
167
+ # def task?(path)
168
+ # task = File.dirname($0) + "/#{path}"
169
+ # task.chomp!('!')
170
+ # task if FileTest.file?(task) && FileTest.executable?(task)
171
+ # end
140
172
 
141
173
  # Is a file a command executable?
142
174
  #
@@ -152,7 +184,7 @@ module Ratch
152
184
  # This is a support method of #bin?
153
185
 
154
186
  def command_paths
155
- @command_paths ||= ENV['PATH'].split(':')
187
+ @command_paths ||= ENV['PATH'].split(/[:;]/)
156
188
  end
157
189
 
158
190
  # TODO Make more robust.
data/lib/ratch/options.rb CHANGED
@@ -1,3 +1,25 @@
1
+ # TITLE:
2
+ #
3
+ # GeneralOptions
4
+ #
5
+ # COPYING:
6
+ #
7
+ # Copyright (c) 2007 Psi T Corp.
8
+ #
9
+ # This file is part of the ProUtils' Ratch program.
10
+ #
11
+ # Ratch is free software: you can redistribute it and/or modify
12
+ # it under the terms of the GNU General Public License as published by
13
+ # the Free Software Foundation, either version 3 of the License, or
14
+ # (at your option) any later version.
15
+ #
16
+ # Ratch is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ # GNU General Public License for more details.
20
+ #
21
+ # You should have received a copy of the GNU General Public License
22
+ # along with Ratch. If not, see <http://www.gnu.org/licenses/>.
1
23
 
2
24
  module Ratch
3
25
 
@@ -22,7 +44,19 @@ module Ratch
22
44
  # Debug mode.
23
45
 
24
46
  def debug?
25
- @debug
47
+ @debug ||= %w{--debug}.any?{|a| argv.delete(a)}
48
+ end
49
+
50
+ #
51
+
52
+ def verbose?
53
+ @verbose ||= %w{--verbose}.any?{|a| argv.delete(a)}
54
+ end
55
+
56
+ #
57
+
58
+ def verbose!
59
+ @verbose = true
26
60
  end
27
61
 
28
62
  #
@@ -42,16 +76,21 @@ module Ratch
42
76
  def noharm?
43
77
  @noharm ||= %w{--dryrun --dry-run --noharm}.any?{|a| argv.delete(a)}
44
78
  end
45
-
46
79
  alias_method :dryrun?, :noharm? ; module_function :dryrun?
47
80
 
48
- #
49
-
50
81
  def noharm!
51
82
  @noharm = true
52
83
  end
53
84
  alias_method :dryrun!, :noharm! ; module_function :dryrun!
54
85
 
86
+ # Force mode.
87
+
88
+ def force?
89
+ @force ||= %w{--force}.any?{|a| argv.delete(a)}
90
+ end
91
+
92
+ def force! ; @force = true ; end
93
+
55
94
  end
56
95
 
57
96
  end
@@ -1,3 +1,26 @@
1
+ # TITLE:
2
+ #
3
+ # Taskable
4
+ #
5
+ # COPYING:
6
+ #
7
+ # Copyright (c) 2007 Psi T Corp.
8
+ #
9
+ # This file is part of the ProUtils' Ratch program.
10
+ #
11
+ # Ratch is free software: you can redistribute it and/or modify
12
+ # it under the terms of the GNU General Public License as published by
13
+ # the Free Software Foundation, either version 3 of the License, or
14
+ # (at your option) any later version.
15
+ #
16
+ # Ratch is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ # GNU General Public License for more details.
20
+ #
21
+ # You should have received a copy of the GNU General Public License
22
+ # along with Ratch. If not, see <http://www.gnu.org/licenses/>.
23
+
1
24
  module Ratch
2
25
 
3
26
  # = Taskable Mixin
@@ -6,7 +29,7 @@ module Ratch
6
29
 
7
30
  # Reference task manager.
8
31
  def task_manager
9
- @task_manager ||= TaskManager.new
32
+ @task_manager ||= TaskManager.new(self)
10
33
  end
11
34
 
12
35
  # Define a main task.
@@ -46,8 +69,10 @@ module Ratch
46
69
  class TaskManager
47
70
  attr :main
48
71
  attr :tasks
72
+ attr :runspace
49
73
 
50
- def initialize
74
+ def initialize(runspace)
75
+ @runspace = runspace
51
76
  @main = nil
52
77
  @tasks = {}
53
78
  end
@@ -78,14 +103,25 @@ module Ratch
78
103
  if list.include?(name)
79
104
  raise "Circular dependency #{name}."
80
105
  end
81
- task = @tasks[name]
82
- task.preqs.each do |need|
83
- need = need.to_s
84
- next if list.include?(need)
85
- #@tasks[need].plan(need, list)
86
- plan(need, list)
106
+ if task = @tasks[name]
107
+ task.needs.each do |need|
108
+ need = need.to_s
109
+ next if list.include?(need)
110
+ #@tasks[need].plan(need, list)
111
+ plan(need, list)
112
+ end
113
+ list << task.name
114
+ else
115
+ if fname = runspace.batch?(name) # TODO THIS TIES TASKS INTO BATCH, BETTER WAY?
116
+ task = Task.new(name) do
117
+ runspace.batch(fname)
118
+ end
119
+ @tasks[name] = task
120
+ list << task.name
121
+ else
122
+ abort "no task -- #{name}"
123
+ end
87
124
  end
88
- list << task.name
89
125
  return list
90
126
  end
91
127
 
@@ -96,13 +132,13 @@ module Ratch
96
132
  class Task
97
133
 
98
134
  attr :name
99
- attr :preqs
135
+ attr :needs
100
136
  attr :action
101
137
 
102
138
  #
103
- def initialize(name, *preqs, &action)
139
+ def initialize(name, *needs, &action)
104
140
  @name = name.to_s
105
- @preqs = preqs
141
+ @needs = needs
106
142
  @action = action
107
143
  end
108
144
 
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env ratch
2
+
3
+ # run cross comparison tests
4
+ #
5
+ # This tool runs unit tests in pairs to make
6
+ # sure there is cross library compatibility.
7
+
8
+ require 'facets/hash/rekey'
9
+ require 'facets/string/tabs'
10
+ require 'facets/progressbar'
11
+
12
+ require 'test/unit/ui/testrunnermediator' #require 'test/unit'
13
+ ::Test::Unit.run = true # Don't autorun tests!
14
+
15
+
16
+ main :crosstest do
17
+ run_crosstests
18
+ end
19
+
20
+ # Run unit-tests. Each test is run in a separate interpretor
21
+ # to prevent script clash. This makes for a more robust test
22
+ # facility and prevents potential conflicts between test scripts.
23
+ #
24
+ # tests Test files (eg. test/tc_**/*.rb) [test/**/*]
25
+ # libs Directories to include in load path.
26
+ # ('./lib' is always included)
27
+ # live Deactive use of local libs and test against install.
28
+ # reqs List of files to require prior to running tests.
29
+ # extract Extract embedded tests first? [false]
30
+ #
31
+ # To isolate tests this tool marshals test results across a
32
+ # stdout->stdin shell pipe. This prevents interfence of one
33
+ # script's tests on another. But consequently it is not always
34
+ # possible to send debug info to stdout in the tests themselves
35
+ # (eg. #p and #puts).
36
+
37
+ def run_crosstests
38
+ info = configuration['test']
39
+
40
+ tests = info['files'] || 'test/unit/**/test_*.rb'
41
+ libs = info['libpath']
42
+ reqs = info['require']
43
+ live = info['live']
44
+ #extract = info['extract']
45
+
46
+ tests = [ tests ].flatten
47
+ libs = [ libs ].flatten
48
+
49
+ results = TestResults.new #(style)
50
+
51
+ # get test files
52
+ test_libs = libs.join(':')
53
+ test_files = Dir.multiglob( *tests )
54
+ if test_files.empty?
55
+ puts "Tests [NONE]"
56
+ return
57
+ end
58
+
59
+ # run tests
60
+
61
+ if trace?
62
+ pbar = nil
63
+ size = test_files.collect{|f| f.size}.max * 2 + 5
64
+ dots = '.' * size
65
+ else
66
+ pbar = Console::ProgressBar.new('Testing', test_files.size ** 2)
67
+ pbar.inc
68
+ end
69
+
70
+ test_files.each do |test_file|
71
+ next unless File.file?(test_file)
72
+
73
+ test_files.each do |test_file_x|
74
+ next unless File.file?(test_file_x)
75
+ next if test_file == test_file_x
76
+
77
+ pbar.inc if pbar
78
+ unless pbar
79
+ out = test_file +' : ' + test_file_x
80
+ print out + dots[out.size..-1]
81
+ $stdout.flush
82
+ end
83
+
84
+ r = fork_test( test_file, test_file_x, :libs=>libs, :reqs=>reqs, :live=>live )
85
+
86
+ unless pbar
87
+ puts r.passed? ? "[PASS]" : "[FAIL]"
88
+ end
89
+
90
+ results << r
91
+ end
92
+ end
93
+
94
+ pbar.finish if pbar
95
+
96
+ # display results
97
+ puts results
98
+
99
+ fails, errrs = results.failure_count, results.error_count
100
+ #CHECKLIST << :test if fails > 0
101
+ #CHECKLIST << :test if errrs > 0
102
+ return (fails + errrs > 0)
103
+ end
104
+
105
+ # Run a test in a separate process.
106
+ #
107
+ # Currently send program output to null device.
108
+ # Could send to a logger in future version.
109
+ #
110
+ # Key parameters are libs, reqs, live.
111
+
112
+ def fork_test( file, xfile, keys )
113
+ keys = keys.rekey(:to_s)
114
+
115
+ libs = keys['lib'] || []
116
+ reqs = keys['req'] || []
117
+ live = keys['live']
118
+
119
+ src = ''
120
+
121
+ unless live
122
+ l = File.join( Dir.pwd, 'lib' )
123
+ if File.directory?( l )
124
+ src << %{$:.unshift('#{l}')\n}
125
+ end
126
+ libs.each { |r| src << %{$:.unshift('#{r}')\n} }
127
+ end
128
+
129
+ src << %{
130
+ #require 'test/unit'
131
+ require 'test/unit/collector'
132
+ require 'test/unit/collector/objectspace'
133
+ require 'test/unit/ui/testrunnermediator'
134
+ }
135
+
136
+ reqs.each do |fix|
137
+ src << %Q{
138
+ require '#{fix}'
139
+ }
140
+ end
141
+
142
+ src << %{
143
+ def warn(*null); end # silence warnings
144
+
145
+ output = STDOUT.dup
146
+ STDOUT.reopen( PLATFORM =~ /mswin/ ? "NUL" : "/dev/null" )
147
+
148
+ load('#{file}')
149
+ load('#{xfile}')
150
+
151
+ tests = Test::Unit::Collector::ObjectSpace.new.collect
152
+ runner = Test::Unit::UI::TestRunnerMediator.new( tests )
153
+ result = runner.run_suite
154
+
155
+ begin
156
+ marshalled = Marshal.dump(result)
157
+ rescue TypeError => e
158
+ $stderr << "MARSHAL ERROR\n"
159
+ $stderr << "TEST: #{file}\n"
160
+ $stderr << "XTEST: #{xfile}\n"
161
+ $stderr << "DATA:" << result.inspect
162
+ exit -1
163
+ end
164
+ output << marshalled
165
+
166
+ STDOUT.reopen(output)
167
+ output.close
168
+ }
169
+
170
+ result = IO.popen("ruby","w+") do |ruby|
171
+ ruby.puts src
172
+ ruby.close_write
173
+ ruby.read
174
+ end
175
+
176
+ begin
177
+ marsh = Marshal.load(result)
178
+ rescue ArgumentError
179
+ $stderr << "\nCannot load marshalled test data.\n"
180
+ $stderr << result << "\n"
181
+ exit -1
182
+ end
183
+
184
+ return marsh
185
+ end
186
+
187
+ #
188
+ # Support class for collecting test results.
189
+ #
190
+
191
+ class TestResults
192
+ attr_reader :assertion_count,
193
+ :run_count,
194
+ :failure_count,
195
+ :error_count
196
+
197
+ attr_accessor :style
198
+
199
+ def initialize( style=nil )
200
+ @style = style
201
+
202
+ @results = []
203
+
204
+ @assertion_count = 0
205
+ @run_count = 0
206
+ @failure_count = 0
207
+ @error_count = 0
208
+ end
209
+
210
+ # Add a result to the results collection.
211
+
212
+ def <<( result )
213
+ @results << result
214
+
215
+ @assertion_count += result.assertion_count
216
+ @run_count += result.run_count
217
+ @failure_count += result.failure_count
218
+ @error_count += result.error_count
219
+ end
220
+
221
+ #
222
+
223
+ def errors
224
+ errors = []
225
+ @results.each do |r|
226
+ unless r.passed?
227
+ errors << r.instance_variable_get('@errors')
228
+ end
229
+ end
230
+ errors.reject! { |e| e == [] }
231
+ errors
232
+ end
233
+
234
+ #
235
+
236
+ def failures
237
+ failures = []
238
+ @results.each do |r|
239
+ unless r.passed?
240
+ failures << r.instance_variable_get('@failures')
241
+ end
242
+ end
243
+ failures.reject! { |e| e == [] }
244
+ failures
245
+ end
246
+
247
+ # Output format for test results.
248
+
249
+ def to_s
250
+ return @results.to_s if style == 'pease'
251
+
252
+ s = []
253
+ # Display failures
254
+ unless failures.empty?
255
+ s << ''
256
+ s << "FAILURES:"
257
+ failures.reverse.each do |fails|
258
+ fails.reverse.each do |failure|
259
+ #puts
260
+ s << %{ - test : #{failure.test_name}}
261
+ s << %{ location : #{failure.location}}
262
+ if failure.message.index("\n")
263
+ s << %{ message : >}
264
+ s << failure.message.tabto(6)
265
+ else
266
+ s << %{ message : #{failure.message}}
267
+ end
268
+ end
269
+ end
270
+ end
271
+
272
+ # Display errors
273
+ unless errors.empty?
274
+ s << ''
275
+ s << "ERRORS:"
276
+ errors.reverse.each do |errs|
277
+ errs.reverse.each do |err|
278
+ s << ''
279
+ s << %{ - test : #{err.test_name}}
280
+ s << %{ message : #{err.exception.message}}
281
+ s << %{ backtrace :}
282
+ err.exception.backtrace[0...-1].each { |bt| s << %Q{ - #{bt}} }
283
+ end
284
+ end
285
+ end
286
+
287
+ # Display final results
288
+ s << ''
289
+ s << "Summary:"
290
+ s << " Tests : #{@run_count}"
291
+ s << " Assertions : #{@assertion_count}"
292
+ s << " Failures : #{@failure_count}"
293
+ s << " Errors : #{@error_count}"
294
+ s << ''
295
+ s << ''
296
+
297
+ return s.join("\n")
298
+ end
299
+
300
+ # Delegate missing call to the results array.
301
+
302
+ def method_missing(*a,&b)
303
+ @results.send(*a,&b)
304
+ end
305
+ end