tryouts 0.8.8 → 2.0.0
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/.gitignore +1 -0
- data/LICENSE.txt +1 -1
- data/README.rdoc +64 -228
- data/Rakefile +42 -65
- data/VERSION.yml +5 -0
- data/bin/try +26 -0
- data/lib/sysinfo.rb +278 -0
- data/lib/tryouts.rb +405 -320
- data/try/step1_try.rb +51 -0
- data/try/step2_try.rb +59 -0
- data/try/step3_try.rb +19 -0
- data/try/step4_try.rb +6 -0
- metadata +20 -75
- data/CHANGES.txt +0 -202
- data/bin/mockout +0 -54
- data/bin/sergeant +0 -66
- data/lib/tryouts/cli/run.rb +0 -229
- data/lib/tryouts/cli.rb +0 -15
- data/lib/tryouts/drill/context.rb +0 -33
- data/lib/tryouts/drill/dream.rb +0 -57
- data/lib/tryouts/drill/reality.rb +0 -49
- data/lib/tryouts/drill/response.rb +0 -117
- data/lib/tryouts/drill/sergeant/api.rb +0 -42
- data/lib/tryouts/drill/sergeant/benchmark.rb +0 -76
- data/lib/tryouts/drill/sergeant/cli.rb +0 -66
- data/lib/tryouts/drill/sergeant/rbenchmark.rb +0 -132
- data/lib/tryouts/drill.rb +0 -224
- data/lib/tryouts/mixins.rb +0 -37
- data/lib/tryouts/orderedhash.rb +0 -199
- data/lib/tryouts/stats.rb +0 -96
- data/lib/tryouts/tryout.rb +0 -176
- data/tryouts/01_mixins_tryouts.rb +0 -23
- data/tryouts/10_syntax_tryouts.rb +0 -44
- data/tryouts/14_set_tryouts.rb +0 -26
- data/tryouts/15_dreams_tryouts.rb +0 -54
- data/tryouts/20_cli_tryouts.rb +0 -40
- data/tryouts/30_benchmark_tryouts.rb +0 -27
- data/tryouts/50_class_context_tryouts.rb +0 -33
- data/tryouts/X1_new_api_syntax.rb +0 -78
- data/tryouts/X2_new_cli_syntax.rb +0 -36
- data/tryouts/standalone_test.rb +0 -39
- data/tryouts.gemspec +0 -84
data/lib/tryouts.rb
CHANGED
@@ -1,349 +1,434 @@
|
|
1
|
+
#require 'pathname'
|
2
|
+
#p Pathname(caller.last.split(':').first)
|
3
|
+
require 'ostruct'
|
1
4
|
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
require 'attic'
|
6
|
-
require 'sysinfo'
|
7
|
-
require 'yaml'
|
5
|
+
unless defined?(TRYOUTS_LIB_HOME)
|
6
|
+
TRYOUTS_LIB_HOME = File.expand_path File.dirname(__FILE__)
|
7
|
+
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
class Tryouts
|
10
|
+
module VERSION
|
11
|
+
def self.to_s
|
12
|
+
load_config
|
13
|
+
[@version[:MAJOR], @version[:MINOR], @version[:PATCH]].join('.')
|
14
|
+
end
|
15
|
+
def self.inspect
|
16
|
+
load_config
|
17
|
+
[@version[:MAJOR], @version[:MINOR], @version[:PATCH], @version[:BUILD]].join('.')
|
18
|
+
end
|
19
|
+
def self.load_config
|
20
|
+
require 'yaml'
|
21
|
+
@version ||= YAML.load_file(File.join(TRYOUTS_LIB_HOME, '..', 'VERSION.yml'))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
12
25
|
|
13
|
-
begin; require 'json'; rescue LoadError; end # json may not be installed
|
14
26
|
|
15
|
-
|
16
|
-
|
27
|
+
class Tryouts
|
28
|
+
@debug = false
|
29
|
+
@container = Class.new
|
30
|
+
@cases = []
|
31
|
+
@sysinfo = nil
|
32
|
+
class << self
|
33
|
+
attr_accessor :debug, :container
|
34
|
+
attr_reader :cases
|
35
|
+
|
36
|
+
def sysinfo
|
37
|
+
require 'sysinfo'
|
38
|
+
@sysinfo ||= SysInfo.new
|
39
|
+
@sysinfo
|
40
|
+
end
|
41
|
+
|
42
|
+
def debug?() @debug == true end
|
43
|
+
|
44
|
+
def run_all *paths
|
45
|
+
batches = paths.collect do |path|
|
46
|
+
run path
|
47
|
+
end
|
48
|
+
|
49
|
+
all, skipped_tests, failed_tests = 0, 0, 0
|
50
|
+
skipped_batches, failed_batches = 0, 0
|
51
|
+
|
52
|
+
msg 'Ruby %s @ %-40s' % [RUBY_VERSION, Time.now], $/
|
53
|
+
|
54
|
+
batches.each do |batch|
|
55
|
+
if !batch.run?
|
56
|
+
skipped_batches += 1
|
57
|
+
status = "SKIP"
|
58
|
+
elsif batch.failed?
|
59
|
+
failed_batches += 1
|
60
|
+
status = Console.color(:red, "FAIL").bright
|
61
|
+
else
|
62
|
+
status = Console.color(:green, "PASS").bright
|
63
|
+
end
|
64
|
+
|
65
|
+
path = batch.path.gsub(/#{Dir.pwd}\/?/, '')
|
66
|
+
|
67
|
+
msg '%-60s %s' % [path, status]
|
68
|
+
batch.each do |t|
|
69
|
+
if t.failed? && failed_tests == 0
|
70
|
+
#msg Console.reverse(" %-60s" % 'Errors')
|
71
|
+
end
|
72
|
+
|
73
|
+
all += 1
|
74
|
+
skipped_tests += 1 unless t.run?
|
17
75
|
|
76
|
+
if t.failed?
|
77
|
+
msg if (failed_tests += 1) == 1
|
78
|
+
msg Console.reverse(' %-58s ' % [t.desc.to_s])
|
79
|
+
msg t.test.inspect, t.exps.inspect
|
80
|
+
msg Console.color(:red, t.failed.join($/)), $/
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
msg
|
85
|
+
if all > 0
|
86
|
+
suffix = 'tests passed'
|
87
|
+
suffix << " (#{skipped_tests} skipped)" if skipped_tests > 0
|
88
|
+
msg cformat(all-failed_tests-skipped_tests, all-skipped_tests, suffix) if all-skipped_tests > 0
|
89
|
+
end
|
90
|
+
if batches.size > 1
|
91
|
+
if batches.size-skipped_batches > 0
|
92
|
+
suffix = "batches passed"
|
93
|
+
suffix << " (#{skipped_batches} skipped)" if skipped_batches > 0
|
94
|
+
msg cformat(batches.size-skipped_batches-failed_batches, batches.size-skipped_batches, suffix)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
failed_tests # 0 means success
|
99
|
+
end
|
100
|
+
|
101
|
+
def cformat(*args)
|
102
|
+
Console.bright '%3d of %d %s' % args
|
103
|
+
end
|
104
|
+
|
105
|
+
def run path
|
106
|
+
batch = parse path
|
107
|
+
batch.run
|
108
|
+
batch
|
109
|
+
end
|
110
|
+
|
111
|
+
def parse path
|
112
|
+
#debug "Loading #{path}"
|
113
|
+
lines = File.readlines path
|
114
|
+
skip_ahead = 0
|
115
|
+
batch = TestBatch.new path, lines
|
116
|
+
lines.size.times do |idx|
|
117
|
+
skip_ahead -= 1 and next if skip_ahead > 0
|
118
|
+
line = lines[idx].chomp
|
119
|
+
#debug('%-4d %s' % [idx, line])
|
120
|
+
if expectation? line
|
121
|
+
offset = 0
|
122
|
+
exps = Section.new(path, idx+1)
|
123
|
+
exps << line.chomp
|
124
|
+
while (idx+offset < lines.size)
|
125
|
+
offset += 1
|
126
|
+
this_line = lines[idx+offset]
|
127
|
+
break if ignore?(this_line)
|
128
|
+
if expectation?(this_line)
|
129
|
+
exps << this_line.chomp
|
130
|
+
skip_ahead += 1
|
131
|
+
end
|
132
|
+
exps.last += 1
|
133
|
+
end
|
134
|
+
|
135
|
+
offset = 0
|
136
|
+
buffer, desc = Section.new(path), Section.new(path)
|
137
|
+
test = Section.new(path, idx) # test start the line before the exp.
|
138
|
+
blank_buffer = Section.new(path)
|
139
|
+
while (idx-offset >= 0)
|
140
|
+
offset += 1
|
141
|
+
this_line = lines[idx-offset].chomp
|
142
|
+
buffer.unshift this_line if ignore?(this_line)
|
143
|
+
if comment?(this_line)
|
144
|
+
buffer.unshift this_line
|
145
|
+
end
|
146
|
+
if test?(this_line)
|
147
|
+
test.unshift(*buffer) && buffer.clear
|
148
|
+
test.unshift this_line
|
149
|
+
end
|
150
|
+
if test_begin?(this_line)
|
151
|
+
while test_begin?(lines[idx-(offset+1)].chomp)
|
152
|
+
offset += 1
|
153
|
+
buffer.unshift lines[idx-offset].chomp
|
154
|
+
end
|
155
|
+
end
|
156
|
+
if test_begin?(this_line) || idx-offset == 0 || expectation?(this_line)
|
157
|
+
adjust = expectation?(this_line) ? 2 : 1
|
158
|
+
test.first = idx-offset+buffer.size+adjust
|
159
|
+
desc.unshift *buffer
|
160
|
+
desc.last = test.first-1
|
161
|
+
desc.first = desc.last-desc.size+1
|
162
|
+
# remove empty lines between the description
|
163
|
+
# and the previous expectation
|
164
|
+
while !desc.empty? && desc[0].empty?
|
165
|
+
desc.shift
|
166
|
+
desc.first += 1
|
167
|
+
end
|
168
|
+
break
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
batch << TestCase.new(desc, test, exps)
|
173
|
+
end
|
174
|
+
end
|
18
175
|
|
19
|
-
|
20
|
-
#
|
21
|
-
# This class has three purposes:
|
22
|
-
# * It represents the Tryouts object which is a group of Tryout objects.
|
23
|
-
# * The tryouts and dreams DSLs are executed within its namespace. In general the
|
24
|
-
# class methods are the handlers for the DSL syntax (some instance getter methods
|
25
|
-
# are modified to support DSL syntax by acting like setters when given arguments)
|
26
|
-
# * It stores all known instances of Tryouts objects in a class variable @@instances.
|
27
|
-
#
|
28
|
-
# ==== Are you ready to run some drills?
|
29
|
-
#
|
30
|
-
# May all your dreams come true!
|
31
|
-
#
|
32
|
-
class Tryouts
|
33
|
-
# = Exception
|
34
|
-
# A generic exception which all other Tryouts exceptions inherit from.
|
35
|
-
class Exception < RuntimeError; end
|
36
|
-
# = BadDreams
|
37
|
-
# Raised when there is a problem loading or parsing a Tryouts::Drill::Dream object
|
38
|
-
class BadDream < Tryouts::Exception; end
|
39
|
-
class TooManyArgs < Tryouts::Exception; end
|
40
|
-
class NoDrillType < Tryouts::Exception
|
41
|
-
attr_accessor :tname
|
42
|
-
def initialize(t); @tname = t; end
|
43
|
-
def message
|
44
|
-
vdt = Tryouts::Drill.valid_dtypes
|
45
|
-
"Tryout '#{@tname}' has no drill type. Should be: #{vdt.join(', ')}"
|
176
|
+
batch
|
46
177
|
end
|
47
|
-
end
|
48
178
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
require 'tryouts/tryout'
|
53
|
-
require 'tryouts/drill'
|
54
|
-
|
55
|
-
autoload :Stats, 'tryouts/stats'
|
56
|
-
|
57
|
-
unless defined?(HASH_TYPE)
|
58
|
-
if RUBY_VERSION =~ /1.8/
|
59
|
-
require 'tryouts/orderedhash'
|
60
|
-
HASH_TYPE = Tryouts::OrderedHash
|
61
|
-
else
|
62
|
-
HASH_TYPE = Hash
|
179
|
+
def print str
|
180
|
+
STDOUT.print str
|
181
|
+
STDOUT.flush
|
63
182
|
end
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
@@debug = false
|
74
|
-
@@verbose = 0
|
75
|
-
# This will be true if any error occurred during any of the drills or parsing.
|
76
|
-
@@failed = false
|
77
|
-
|
78
|
-
def self.debug?; @@debug; end
|
79
|
-
def self.enable_debug; @@debug = true; end
|
80
|
-
def self.disable_debug; @@debug = false; end
|
81
|
-
|
82
|
-
def self.verbose; @@verbose; end
|
83
|
-
def self.verbose=(v); @@verbose = (v == true) ? 1 : v; end
|
84
|
-
|
85
|
-
def self.failed?; @@failed; end
|
86
|
-
def self.failed=(v); @@failed = v; end
|
87
|
-
|
88
|
-
# Returns +@@instances+
|
89
|
-
def self.instances; @@instances; end
|
90
|
-
# Returns +@@sysinfo+
|
91
|
-
def self.sysinfo
|
92
|
-
@@sysinfo = SysInfo.new if @@sysinfo.nil?
|
93
|
-
@@sysinfo
|
94
|
-
end
|
95
|
-
|
96
|
-
# The name of this group of Tryout objects
|
97
|
-
attr_accessor :group
|
98
|
-
# A Symbol representing the default drill type. One of: :cli, :api
|
99
|
-
attr_accessor :dtype
|
100
|
-
# An Array of file paths which populated this instance of Tryouts
|
101
|
-
attr_accessor :paths
|
102
|
-
# An Array of Tryout objects
|
103
|
-
attr_accessor :tryouts
|
104
|
-
# A Symbol representing the command taking part in the tryouts. For @dtype :cli only.
|
105
|
-
attr_accessor :command
|
106
|
-
# A Symbol representing the name of the library taking part in the tryouts. For @dtype :api only.
|
107
|
-
attr_accessor :library
|
108
|
-
# An Array of exceptions that were raised during the tryouts that were not captured by a drill.
|
109
|
-
attr_reader :errors
|
110
|
-
|
111
|
-
def initialize(group=nil)
|
112
|
-
@group = group || "Default Group"
|
113
|
-
@tryouts = HASH_TYPE.new
|
114
|
-
@paths, @errors = [], []
|
115
|
-
@command = nil
|
116
|
-
end
|
117
|
-
|
118
|
-
# Populate this Tryouts from a block. The block should contain calls to
|
119
|
-
# the external DSL methods: tryout, command, library, group
|
120
|
-
def from_block(b, &inline)
|
121
|
-
instance_eval &b
|
122
|
-
end
|
123
|
-
|
124
|
-
# Execute Tryout#report for each Tryout in +@tryouts+
|
125
|
-
def report
|
126
|
-
successes = []
|
127
|
-
@tryouts.each_pair { |n,to| successes << to.report }
|
128
|
-
puts $/, "All your dreams came true" unless successes.member?(false)
|
129
|
-
end
|
130
|
-
|
131
|
-
# Execute Tryout#run for each Tryout in +@tryouts+
|
132
|
-
def run; @tryouts.each_pair { |n,to| to.run }; end
|
133
|
-
|
134
|
-
# Add a shell command to Rye::Cmd and save the command name
|
135
|
-
# in @@commands so it can be used as the default for drills
|
136
|
-
def command(name=nil, path=nil)
|
137
|
-
return @command if name.nil?
|
138
|
-
require 'rye'
|
139
|
-
@command = name.to_sym
|
140
|
-
@dtype = :cli
|
141
|
-
Rye::Cmd.module_eval do
|
142
|
-
define_method(name) do |*args|
|
143
|
-
cmd(path || name, *args)
|
183
|
+
|
184
|
+
def msg *msg
|
185
|
+
STDOUT.puts *msg
|
186
|
+
end
|
187
|
+
|
188
|
+
def err *msg
|
189
|
+
msg.each do |line|
|
190
|
+
STDERR.puts Console.color :red, line
|
144
191
|
end
|
145
192
|
end
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
# in specified in multiple arguments they are joined and expanded.
|
160
|
-
#
|
161
|
-
# library '/an/absolute/path'
|
162
|
-
# library __FILE__, '..', 'lib'
|
163
|
-
#
|
164
|
-
def library(name=nil, *path)
|
165
|
-
return @library if name.nil?
|
166
|
-
@library, @dtype = name.to_sym, :api
|
167
|
-
path = File.expand_path(File.join(*path))
|
168
|
-
$LOAD_PATH.unshift path unless path.nil?
|
169
|
-
begin
|
170
|
-
require @library.to_s
|
171
|
-
rescue LoadError => ex
|
172
|
-
newex = Tryouts::Exception.new(ex.message)
|
173
|
-
trace = ex.backtrace
|
174
|
-
trace.unshift @paths.last
|
175
|
-
newex.set_backtrace trace
|
176
|
-
@errors << newex
|
177
|
-
Tryouts.failed = true
|
178
|
-
rescue SyntaxError, Exception, TypeError,
|
179
|
-
RuntimeError, NoMethodError, NameError => ex
|
180
|
-
@errors << ex
|
181
|
-
Tryouts.failed = true
|
193
|
+
|
194
|
+
def debug *msg
|
195
|
+
STDERR.puts *msg if @debug
|
196
|
+
end
|
197
|
+
|
198
|
+
def eval(str, path, line)
|
199
|
+
begin
|
200
|
+
Kernel.eval str, @container.send(:binding), path, line
|
201
|
+
rescue SyntaxError, LoadError => ex
|
202
|
+
Tryouts.err Console.color(:red, ex.message),
|
203
|
+
Console.color(:red, ex.backtrace.first)
|
204
|
+
nil
|
205
|
+
end
|
182
206
|
end
|
183
|
-
end
|
184
|
-
# Calls Tryouts#library on the current instance of Tryouts
|
185
|
-
#
|
186
|
-
# NOTE: this is a standalone DSL-syntax method.
|
187
|
-
def self.library(*args)
|
188
|
-
@@instances.last.library(*args)
|
189
|
-
end
|
190
|
-
|
191
|
-
def group(name=nil)
|
192
|
-
return @group if name.nil?
|
193
|
-
@group = name unless name.nil?
|
194
|
-
@group
|
195
|
-
end
|
196
|
-
# Raises a Tryouts::Exception. +group+ is not support in the standalone syntax
|
197
|
-
# because the group name is taken from the name of the class. See inherited.
|
198
|
-
#
|
199
|
-
# NOTE: this is a standalone DSL-syntax method.
|
200
|
-
def self.group(*args)
|
201
|
-
raise "Group is already set: #{@@instances.last.group}"
|
202
|
-
end
|
203
|
-
|
204
|
-
# Create a new Tryout object and add it to the list for this Tryouts class.
|
205
|
-
# * +name+ is the name of the Tryout
|
206
|
-
# * +dtype+ is the default drill type for the Tryout.
|
207
|
-
# * +command+ when type is :cli, this is the name of the Rye::Box method that we're testing. Otherwise ignored.
|
208
|
-
# * +b+ is a block definition for the Tryout. See Tryout#from_block
|
209
|
-
#
|
210
|
-
# NOTE: This is a DSL-only method and is not intended for OO use.
|
211
|
-
def tryout(name, dtype=nil, command=nil, &block)
|
212
|
-
return if name.nil?
|
213
|
-
dtype ||= @dtype
|
214
|
-
command ||= @command if dtype == :cli
|
215
207
|
|
216
|
-
|
208
|
+
private
|
217
209
|
|
218
|
-
|
219
|
-
|
220
|
-
to = Tryouts::Tryout.new(name, dtype, command)
|
221
|
-
@tryouts[name] = to
|
210
|
+
def expectation? str
|
211
|
+
!ignore?(str) && str.strip.match(/^\#\s*=>/)
|
222
212
|
end
|
223
213
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
214
|
+
def comment? str
|
215
|
+
!str.strip.match(/^\#+/).nil? && !expectation?(str)
|
216
|
+
end
|
217
|
+
|
218
|
+
def test? str
|
219
|
+
!ignore?(str) && !expectation?(str) && !comment?(str)
|
220
|
+
end
|
221
|
+
|
222
|
+
def ignore? str
|
223
|
+
str.to_s.strip.chomp.empty?
|
224
|
+
end
|
225
|
+
|
226
|
+
def test_begin? str
|
227
|
+
ret = !str.strip.match(/^\#+\s*TEST/i).nil? ||
|
228
|
+
!str.strip.match(/^\#\#+[\s\w]+/i).nil?
|
229
|
+
ret
|
230
|
+
end
|
240
231
|
|
241
|
-
|
242
|
-
# +dtype+ if specified. Returns a Tryout object or nil.
|
243
|
-
def find_tryout(name, dtype=nil)
|
244
|
-
by_name = @tryouts.values.select { |t| t.name == name }
|
245
|
-
by_name = by_name.select { |t| t.dtype == dtype } if dtype
|
246
|
-
by_name.first # by_name is an Array. We just want the Object.
|
247
|
-
end
|
248
|
-
|
249
|
-
# This method does nothing. It provides a quick way to disable a tryout.
|
250
|
-
#
|
251
|
-
# NOTE: This is a DSL-only method and is not intended for OO use.
|
252
|
-
def xtryout(*args, &block); end
|
253
|
-
# This method does nothing. It provides a quick way to disable a tryout.
|
254
|
-
#
|
255
|
-
# NOTE: this is a standalone DSL-syntax method.
|
256
|
-
def self.xtryout(*args, &block); end
|
257
|
-
|
258
|
-
# Returns +@tryouts+.
|
259
|
-
#
|
260
|
-
# Also acts as a stub for Tryouts#tryout in case someone
|
261
|
-
# specifies "tryouts 'name' do ..." in the DSL.
|
262
|
-
def tryouts(*args, &block)
|
263
|
-
return tryout(*args, &block) unless args.empty?
|
264
|
-
@tryouts
|
265
|
-
end
|
266
|
-
# An alias for Tryouts.tryout.
|
267
|
-
def self.tryouts(*args, &block)
|
268
|
-
tryout(args, &block)
|
232
|
+
|
269
233
|
end
|
270
234
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
# This method does nothing. It provides a quick way to disable a tryout.
|
276
|
-
#
|
277
|
-
# NOTE: this is a standalone DSL-syntax method.
|
278
|
-
def self.xtryouts(*args, &block); end
|
279
|
-
|
280
|
-
|
281
|
-
# Parse a +_tryouts.rb+ file. See Tryouts::CLI::Run for an example.
|
282
|
-
#
|
283
|
-
# NOTE: this is an OO syntax method
|
284
|
-
def self.parse_file(fpath)
|
285
|
-
raise "No such file: #{fpath}" unless File.exists?(fpath)
|
286
|
-
file_content = File.read(fpath)
|
287
|
-
to = Tryouts.new
|
288
|
-
begin
|
289
|
-
to.paths << fpath
|
290
|
-
to.instance_eval file_content, fpath
|
291
|
-
# After parsing the DSL, we'll know the group name.
|
292
|
-
# If a Tryouts object already exists for that group
|
293
|
-
# we'll use that instead and re-parse the DSL.
|
294
|
-
if @@instances.has_key? to.group
|
295
|
-
to = @@instances[to.group]
|
296
|
-
to.instance_eval file_content, fpath
|
235
|
+
class TestBatch < Array
|
236
|
+
class Container
|
237
|
+
def metaclass
|
238
|
+
class << self; end
|
297
239
|
end
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
240
|
+
end
|
241
|
+
attr_reader :path
|
242
|
+
attr_reader :failed
|
243
|
+
attr_reader :lines
|
244
|
+
def initialize(p,l)
|
245
|
+
@path, @lines = p, l
|
246
|
+
@container = Container.new.metaclass
|
247
|
+
@run = false
|
248
|
+
end
|
249
|
+
def run
|
250
|
+
return if empty?
|
251
|
+
setup
|
252
|
+
ret = self.select { |tc| !tc.run } # select failed
|
253
|
+
@failed = ret.size
|
254
|
+
@run = true
|
255
|
+
clean
|
256
|
+
!failed?
|
257
|
+
end
|
258
|
+
def failed?
|
259
|
+
!@failed.nil? && @failed > 0
|
260
|
+
end
|
261
|
+
def setup
|
262
|
+
return if empty?
|
263
|
+
start = first.desc.nil? ? first.test.first : first.desc.first-1
|
264
|
+
Tryouts.eval lines[0..start-1].join, path, 0 if start > 0
|
265
|
+
end
|
266
|
+
def clean
|
267
|
+
return if empty?
|
268
|
+
last = first.exps.last+1
|
269
|
+
if last < lines.size
|
270
|
+
Tryouts.eval lines[last..-1].join, path, last
|
307
271
|
end
|
308
272
|
end
|
309
|
-
|
310
|
-
|
273
|
+
def run?
|
274
|
+
@run
|
275
|
+
end
|
311
276
|
end
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
277
|
+
class TestCase
|
278
|
+
attr_reader :desc, :test, :exps, :failed, :passed
|
279
|
+
def initialize(d,t,e)
|
280
|
+
@desc, @test, @exps, @path = d,t,e
|
281
|
+
end
|
282
|
+
def inspect
|
283
|
+
[@desc.inspect, @test.inspect, @exps.inspect].join
|
284
|
+
end
|
285
|
+
def to_s
|
286
|
+
[@desc.to_s, @test.to_s, @exps.to_s].join
|
287
|
+
end
|
288
|
+
def run
|
289
|
+
Tryouts.debug '%s:%d' % [@test.path, @test.first]
|
290
|
+
Tryouts.debug inspect, $/
|
291
|
+
test_value = Tryouts.eval @test.to_s, @test.path, @test.first
|
292
|
+
@passed, @failed = [], []
|
293
|
+
exps.each_with_index { |exp,idx|
|
294
|
+
exp =~ /\#+\s*=>\s*(.+)$/
|
295
|
+
exp_value = Tryouts.eval($1, @exps.path, @exps.first+idx)
|
296
|
+
ret = test_value == exp_value
|
297
|
+
if ret
|
298
|
+
@passed << ' %s == %s' % [test_value.inspect, exp_value.inspect]
|
299
|
+
else
|
300
|
+
@failed << ' %s != %s' % [test_value.inspect, exp_value.inspect]
|
301
|
+
end
|
302
|
+
ret
|
303
|
+
}
|
304
|
+
Tryouts.debug
|
305
|
+
@failed.empty?
|
306
|
+
end
|
307
|
+
def run?
|
308
|
+
!@failed.nil?
|
309
|
+
end
|
310
|
+
def failed?
|
311
|
+
!@failed.nil? && !@failed.empty?
|
312
|
+
end
|
313
|
+
private
|
314
|
+
def create_proc str, path, line
|
315
|
+
eval("Proc.new {\n #{str}\n}", binding, path, line)
|
322
316
|
end
|
323
317
|
end
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
318
|
+
class Section < Array
|
319
|
+
attr_accessor :path, :first, :last
|
320
|
+
def initialize path, start=0
|
321
|
+
@path = path
|
322
|
+
@first, @last = start, start
|
323
|
+
end
|
324
|
+
def range
|
325
|
+
@first..@last
|
326
|
+
end
|
327
|
+
def inspect
|
328
|
+
range.to_a.zip(self).collect do |line|
|
329
|
+
"%-4d %s\n" % line
|
330
|
+
end.join
|
331
|
+
end
|
332
|
+
def to_s
|
333
|
+
self.join($/)
|
334
|
+
end
|
336
335
|
end
|
336
|
+
|
337
337
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
338
|
+
module Console
|
339
|
+
|
340
|
+
# ANSI escape sequence numbers for text attributes
|
341
|
+
ATTRIBUTES = {
|
342
|
+
:normal => 0,
|
343
|
+
:bright => 1,
|
344
|
+
:dim => 2,
|
345
|
+
:underline => 4,
|
346
|
+
:blink => 5,
|
347
|
+
:reverse => 7,
|
348
|
+
:hidden => 8,
|
349
|
+
:default => 0,
|
350
|
+
}.freeze unless defined? ATTRIBUTES
|
351
|
+
|
352
|
+
# ANSI escape sequence numbers for text colours
|
353
|
+
COLOURS = {
|
354
|
+
:black => 30,
|
355
|
+
:red => 31,
|
356
|
+
:green => 32,
|
357
|
+
:yellow => 33,
|
358
|
+
:blue => 34,
|
359
|
+
:magenta => 35,
|
360
|
+
:cyan => 36,
|
361
|
+
:white => 37,
|
362
|
+
:default => 39,
|
363
|
+
:random => 30 + rand(10).to_i
|
364
|
+
}.freeze unless defined? COLOURS
|
365
|
+
|
366
|
+
# ANSI escape sequence numbers for background colours
|
367
|
+
BGCOLOURS = {
|
368
|
+
:black => 40,
|
369
|
+
:red => 41,
|
370
|
+
:green => 42,
|
371
|
+
:yellow => 43,
|
372
|
+
:blue => 44,
|
373
|
+
:magenta => 45,
|
374
|
+
:cyan => 46,
|
375
|
+
:white => 47,
|
376
|
+
:default => 49,
|
377
|
+
:random => 40 + rand(10).to_i
|
378
|
+
}.freeze unless defined? BGCOLOURS
|
379
|
+
|
380
|
+
module InstanceMethods
|
381
|
+
def bright
|
382
|
+
Console.bright(self)
|
383
|
+
end
|
384
|
+
def reverse
|
385
|
+
Console.reverse(self)
|
386
|
+
end
|
387
|
+
def color(col)
|
388
|
+
Console.color(col, self)
|
389
|
+
end
|
390
|
+
def att(col)
|
391
|
+
Console.att(col, self)
|
392
|
+
end
|
393
|
+
def bgcolor(col)
|
394
|
+
Console.bgcolor(col, self)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
def self.bright(str)
|
399
|
+
str = [style(ATTRIBUTES[:bright]), str, default_style].join
|
400
|
+
str.extend Console::InstanceMethods
|
401
|
+
str
|
402
|
+
end
|
403
|
+
def self.reverse(str)
|
404
|
+
str = [style(ATTRIBUTES[:reverse]), str, default_style].join
|
405
|
+
str.extend Console::InstanceMethods
|
406
|
+
str
|
407
|
+
end
|
408
|
+
def self.color(col, str)
|
409
|
+
str = [style(COLOURS[col]), str, default_style].join
|
410
|
+
str.extend Console::InstanceMethods
|
411
|
+
str
|
412
|
+
end
|
413
|
+
def self.att(name, str)
|
414
|
+
str = [style(ATTRIBUTES[name]), str, default_style].join
|
415
|
+
str.extend Console::InstanceMethods
|
416
|
+
str
|
417
|
+
end
|
418
|
+
def self.bgcolor(col, str)
|
419
|
+
str = [style(ATTRIBUTES[col]), str, default_style].join
|
420
|
+
str.extend Console::InstanceMethods
|
421
|
+
str
|
422
|
+
end
|
423
|
+
private
|
424
|
+
def self.style(*att)
|
425
|
+
# => \e[8;34;42m
|
426
|
+
"\e[%sm" % att.join(';')
|
427
|
+
end
|
428
|
+
def self.default_style
|
429
|
+
style(ATTRIBUTES[:default], ATTRIBUTES[:COLOURS], ATTRIBUTES[:BGCOLOURS])
|
430
|
+
end
|
431
|
+
end
|
348
432
|
|
349
433
|
end
|
434
|
+
|