lenc 1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.txt +2 -0
- data/README.txt +90 -0
- data/bin/lencrypt +5 -0
- data/lib/lenc.rb +1 -0
- data/lib/lenc/aes.rb +523 -0
- data/lib/lenc/config_file.rb +108 -0
- data/lib/lenc/lencrypt.rb +70 -0
- data/lib/lenc/repo.rb +1024 -0
- data/lib/lenc/tools.rb +632 -0
- data/lib/lenc/trollop.rb +782 -0
- data/test/test.rb +377 -0
- metadata +61 -0
data/lib/lenc/tools.rb
ADDED
@@ -0,0 +1,632 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
# Various utility and debug convenience functions.
|
5
|
+
#
|
6
|
+
|
7
|
+
# Convenience method to perform 'require_relative' on a set of files
|
8
|
+
#
|
9
|
+
# @param fileListStr space-delimited file/path items, without .rb extensions
|
10
|
+
# @param subdir optional path to files relative to this file
|
11
|
+
#
|
12
|
+
def req(fileListStr,subdir = nil)
|
13
|
+
fileListStr.split(' ').each do |x|
|
14
|
+
if subdir
|
15
|
+
x = File.join(subdir,x)
|
16
|
+
end
|
17
|
+
x += '.rb'
|
18
|
+
require_relative(x)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Shorthand for printf(...)
|
23
|
+
# @param args passed to printf
|
24
|
+
def pr(*args)
|
25
|
+
printf(*args)
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# Convert an object to a human-readable string,
|
30
|
+
# or <nil>
|
31
|
+
#
|
32
|
+
def d(arg)
|
33
|
+
arg.nil? ? "<nil>" : arg.inspect
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
# Convert an object to a human-readable string,
|
38
|
+
# by calling a type-appropriate function: da, dh, or just d.
|
39
|
+
# @param arg object
|
40
|
+
# @param indent optional indentation for pretty printing; if result
|
41
|
+
# spans multiple lines, each line should be indented by this amount
|
42
|
+
#
|
43
|
+
def d2(arg, indent = 0)
|
44
|
+
return da(arg, indent) if arg.is_a? Array
|
45
|
+
return dh(arg, indent) if arg.is_a? Hash
|
46
|
+
return df(arg) if arg.class == FalseClass || arg.class == TrueClass
|
47
|
+
return d(arg)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Convert an object to a human-readable string, prefixed with its type
|
51
|
+
#
|
52
|
+
def dt(arg)
|
53
|
+
if arg.nil?
|
54
|
+
return "<nil>"
|
55
|
+
end
|
56
|
+
s = arg.class.to_s
|
57
|
+
s << ':'
|
58
|
+
s << arg.inspect
|
59
|
+
s
|
60
|
+
end
|
61
|
+
|
62
|
+
# Append a particular number of spaces to a string
|
63
|
+
def add_sp(s, indent = 0)
|
64
|
+
s << ' ' * indent
|
65
|
+
end
|
66
|
+
|
67
|
+
# Pretty-print an array,
|
68
|
+
# one element to a line
|
69
|
+
# @param indent indentation of each line, in spaces
|
70
|
+
def da(array, indent = 0)
|
71
|
+
return d(array) if !array
|
72
|
+
s = 'Array ['
|
73
|
+
indent += 2
|
74
|
+
array.each do |x|
|
75
|
+
s << "\n"
|
76
|
+
add_sp(s,indent)
|
77
|
+
s2 = d2(x, indent + 2)
|
78
|
+
s << s2
|
79
|
+
end
|
80
|
+
s << " ]"
|
81
|
+
s
|
82
|
+
end
|
83
|
+
|
84
|
+
# Pretty-print a hash,
|
85
|
+
# one element to a line
|
86
|
+
# @param indent indentation of each line, in spaces
|
87
|
+
def dh(hash, indent = 0)
|
88
|
+
return d(hash) if !hash
|
89
|
+
s = 'Hash {'
|
90
|
+
indent += 2
|
91
|
+
hash.each_pair do |key,val|
|
92
|
+
s2 = d(key)
|
93
|
+
s3 = d2(val, indent + 4)
|
94
|
+
s << "\n "
|
95
|
+
add_sp(s,indent)
|
96
|
+
s << s2.chomp << " => " << s3.chomp
|
97
|
+
end
|
98
|
+
s << " }"
|
99
|
+
s
|
100
|
+
end
|
101
|
+
|
102
|
+
# Generate debug description of a boolean value
|
103
|
+
# @param flag value to interpret as a boolean; prints 'T' iff not nil
|
104
|
+
# @param label optional label
|
105
|
+
def df(flag, label=nil)
|
106
|
+
s = ''
|
107
|
+
if label
|
108
|
+
s << label << ':'
|
109
|
+
end
|
110
|
+
s << (flag ? "T" : "F")
|
111
|
+
s << ' '
|
112
|
+
s
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
# Assert that a value is true. Should be considered a
|
118
|
+
# very temporary, debug-only option; it is slow and
|
119
|
+
# generates a warning that it is being called.
|
120
|
+
# @param cond condition
|
121
|
+
# @param msg generates additional message using printf(), if these arguments exist
|
122
|
+
def assert!(cond, *msg)
|
123
|
+
one_time_alert("warning",0,"Checking assertion")
|
124
|
+
if not cond
|
125
|
+
str = (msg.size == 0) ? "assertion error" : sprintf(*msg)
|
126
|
+
raise Exception, str
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
## Set test directory. If nil, sets to home directory + "__test__"
|
132
|
+
#def setTestDir(d = nil)
|
133
|
+
# $testDir = d || File.join(Dir.home,"__test__")
|
134
|
+
#end
|
135
|
+
|
136
|
+
## Get a path within the test directory;
|
137
|
+
## create test directory if it doesn't exist.
|
138
|
+
##
|
139
|
+
## relPath : if nil, returns the test directory; else
|
140
|
+
## returns the test directory joined to this one
|
141
|
+
##
|
142
|
+
#def withinTestDir(relPath = nil)
|
143
|
+
# if !$testDir
|
144
|
+
# raise IllegalStateException, "No test directory has been defined"
|
145
|
+
# end
|
146
|
+
# if !File.directory?($testDir)
|
147
|
+
# Dir::mkdir($testDir)
|
148
|
+
# end
|
149
|
+
# relPath ? File.join($testDir,relPath) : $testDir
|
150
|
+
#end
|
151
|
+
|
152
|
+
## Convert a .dot file (string) to a PDF file "__mygraph__nnn.pdf"
|
153
|
+
## in the test directory.
|
154
|
+
##
|
155
|
+
## It does this by making a system call to the 'dot' utility.
|
156
|
+
##
|
157
|
+
#def dotToPDF(dotFile, name = "")
|
158
|
+
# gr = dotFile
|
159
|
+
# dotPath = withinTestDir(".__mygraph__.dot")
|
160
|
+
# write_text_file(dotPath,gr)
|
161
|
+
# destName = withinTestDir( "__mygraph__"+name+".pdf")
|
162
|
+
# system("dot -Tpdf "+dotPath+" -o "+destName)
|
163
|
+
#end
|
164
|
+
|
165
|
+
## Extensions to the Enumerable module
|
166
|
+
##
|
167
|
+
#module Enumerable
|
168
|
+
# # Calculate a value for each item, and return the item with the
|
169
|
+
# # highest value, its index, and the value.
|
170
|
+
# # @yieldparam function to calculate value of an object, given that object as a parameter
|
171
|
+
# # @return the triple [object, index, value] reflecting the maximum value, or
|
172
|
+
# # nil if there were no items
|
173
|
+
# def max_with_index
|
174
|
+
#
|
175
|
+
# best = nil
|
176
|
+
#
|
177
|
+
# each_with_index do |obj,ind|
|
178
|
+
# sc = yield(obj)
|
179
|
+
# if !best || best[2] < sc
|
180
|
+
# best = [obj,ind,sc]
|
181
|
+
# end
|
182
|
+
# end
|
183
|
+
# best
|
184
|
+
# end
|
185
|
+
#end
|
186
|
+
|
187
|
+
|
188
|
+
# Get a nice, concise description of the file and line
|
189
|
+
# of some caller within the stack.
|
190
|
+
#
|
191
|
+
# @param nSkip the number of items deep in the call stack to look
|
192
|
+
#
|
193
|
+
def get_caller_location(nSkip = 2)
|
194
|
+
|
195
|
+
filename = nil
|
196
|
+
linenumber = nil
|
197
|
+
|
198
|
+
if nSkip >= 0 && nSkip < caller.size
|
199
|
+
fi = caller[nSkip]
|
200
|
+
|
201
|
+
i = fi.index(':')
|
202
|
+
j = nil
|
203
|
+
if i
|
204
|
+
j = fi.index(':',i+1)
|
205
|
+
end
|
206
|
+
if j
|
207
|
+
pth = fi[0,i].split('/')
|
208
|
+
if pth.size
|
209
|
+
filename = pth[-1]
|
210
|
+
end
|
211
|
+
linenumber = fi[i+1,j-i-1]
|
212
|
+
end
|
213
|
+
end
|
214
|
+
if filename && linenumber
|
215
|
+
loc = filename + " ("+linenumber+")"
|
216
|
+
else
|
217
|
+
loc = "(UNKNOWN LOCATION)"
|
218
|
+
end
|
219
|
+
loc
|
220
|
+
end
|
221
|
+
|
222
|
+
# Set of alert strings that have already been reported
|
223
|
+
# (to avoid printing anything on subsequent invocations)
|
224
|
+
#
|
225
|
+
$AlertStrings = Set.new
|
226
|
+
|
227
|
+
# Print a message if it hasn't yet been printed,
|
228
|
+
# which includes the caller's location
|
229
|
+
#
|
230
|
+
# @param typeString e.g., "warning", "unimplemented"
|
231
|
+
# @param nSkip the number of levels deep that the caller is in the stack
|
232
|
+
# @param args if present, calls sprintf(...) with these to append to the message
|
233
|
+
#
|
234
|
+
def one_time_alert(typeString, nSkip, *args)
|
235
|
+
loc = get_caller_location(nSkip + 2)
|
236
|
+
s = "*** "+typeString+" " + loc
|
237
|
+
if args && args.size
|
238
|
+
s2 = sprintf(args[0], *args[1..-1])
|
239
|
+
msg = s + ": " + s2
|
240
|
+
else
|
241
|
+
msg = s
|
242
|
+
end
|
243
|
+
|
244
|
+
if $AlertStrings.add?(msg)
|
245
|
+
puts msg
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# Print a 'warning' alert, one time only
|
250
|
+
# @param args if present, calls printf() with these
|
251
|
+
def warn(*args)
|
252
|
+
one_time_alert("warning",0, *args)
|
253
|
+
end
|
254
|
+
|
255
|
+
# Print an 'unimplemented' alert, one time only
|
256
|
+
# @param args if present, calls printf() with these
|
257
|
+
def unimp(*args)
|
258
|
+
one_time_alert("unimplemented", 0, *args)
|
259
|
+
end
|
260
|
+
|
261
|
+
# Write a string to a text file
|
262
|
+
#
|
263
|
+
def write_text_file(path, contents)
|
264
|
+
File.open(path, "wb") {|f| f.write(contents) }
|
265
|
+
end
|
266
|
+
|
267
|
+
# Read a file's contents, return as a string
|
268
|
+
#
|
269
|
+
def read_text_file(path)
|
270
|
+
contents = nil
|
271
|
+
File.open(path,"rb") {|f| contents = f.read }
|
272
|
+
contents
|
273
|
+
end
|
274
|
+
|
275
|
+
# Method that takes a code block as an argument to
|
276
|
+
# achieve the same functionality as Java/C++'s
|
277
|
+
# do {
|
278
|
+
# ...
|
279
|
+
# ... possibly with 'break' to jump to the end ...
|
280
|
+
# } while (false);
|
281
|
+
#
|
282
|
+
def block
|
283
|
+
yield
|
284
|
+
end
|
285
|
+
|
286
|
+
# Exception class for objects in illegal states
|
287
|
+
#
|
288
|
+
class IllegalStateException < Exception
|
289
|
+
end
|
290
|
+
|
291
|
+
|
292
|
+
def to_hex(value, num_digits=4)
|
293
|
+
s = sprintf("%x", value)
|
294
|
+
s.rjust(num_digits,'0')
|
295
|
+
end
|
296
|
+
|
297
|
+
def hex_dump(byte_array_or_string, title=nil, offset=0, length= -1, bytes_per_row=16, with_text=true)
|
298
|
+
ss = hex_dump_to_string(byte_array_or_string, title, offset, length, bytes_per_row, with_text)
|
299
|
+
puts ss
|
300
|
+
end
|
301
|
+
|
302
|
+
def hex_dump_to_string(byte_array_or_string, title=nil, offset=0, length= -1, bytes_per_row=16, with_text=true)
|
303
|
+
|
304
|
+
byte_array = byte_array_or_string
|
305
|
+
if byte_array.is_a? String
|
306
|
+
byte_array = byte_array.bytes.to_a
|
307
|
+
end
|
308
|
+
|
309
|
+
ss = ''
|
310
|
+
|
311
|
+
if title
|
312
|
+
ss << title << ":\n"
|
313
|
+
end
|
314
|
+
|
315
|
+
if length < 0
|
316
|
+
length = byte_array.size - offset
|
317
|
+
end
|
318
|
+
|
319
|
+
length = [length, byte_array.size - offset].min
|
320
|
+
|
321
|
+
max_addr = offset + length - 1
|
322
|
+
num_digits = 4
|
323
|
+
while (1 << (4 * num_digits)) <= max_addr
|
324
|
+
num_digits += 1
|
325
|
+
end
|
326
|
+
|
327
|
+
while true
|
328
|
+
ss << to_hex(offset, num_digits)
|
329
|
+
ss << ': '
|
330
|
+
|
331
|
+
chunk = [length, bytes_per_row].min
|
332
|
+
bytes_per_row.times do |i|
|
333
|
+
if i % 4 == 0
|
334
|
+
ss << ' '
|
335
|
+
end
|
336
|
+
|
337
|
+
if i < chunk
|
338
|
+
v = byte_array[offset + i]
|
339
|
+
ss << ((v != 0) ? to_hex(v,2) : '..')
|
340
|
+
ss << ' '
|
341
|
+
else
|
342
|
+
ss << ' '
|
343
|
+
end
|
344
|
+
|
345
|
+
end
|
346
|
+
|
347
|
+
|
348
|
+
if with_text
|
349
|
+
ss << ' |'
|
350
|
+
bytes_per_row.times do |i|
|
351
|
+
if i < chunk
|
352
|
+
v = byte_array[offset + i]
|
353
|
+
ss << ((v >= 32 && v < 127) ? v : '_')
|
354
|
+
end
|
355
|
+
end
|
356
|
+
ss << '|'
|
357
|
+
end
|
358
|
+
ss << "\n"
|
359
|
+
|
360
|
+
length -= chunk
|
361
|
+
offset += chunk
|
362
|
+
break if length <= 0
|
363
|
+
end
|
364
|
+
|
365
|
+
ss << "\n"
|
366
|
+
ss
|
367
|
+
end
|
368
|
+
|
369
|
+
$prevTime = nil
|
370
|
+
|
371
|
+
# Calculate time elapsed, in seconds, from last call to this function;
|
372
|
+
# if it's never been called, returns zero
|
373
|
+
def elapsed
|
374
|
+
curr = Time.now.to_f
|
375
|
+
elap = 0
|
376
|
+
if $prevTime
|
377
|
+
elap = curr - $prevTime
|
378
|
+
end
|
379
|
+
$prevTime = curr
|
380
|
+
elap
|
381
|
+
end
|
382
|
+
|
383
|
+
# Construct a string from an array of bytes
|
384
|
+
# @param byte_array array of bytes, or string (in which case it
|
385
|
+
# returns it unchanged)
|
386
|
+
#
|
387
|
+
def bytes_to_str(byte_array)
|
388
|
+
return byte_array if byte_array.is_a? String
|
389
|
+
|
390
|
+
byte_array.pack('C*')
|
391
|
+
end
|
392
|
+
|
393
|
+
# Construct an array of bytes from a string
|
394
|
+
# @param str string, or array of bytes (in which case it
|
395
|
+
# returns it unchanged)
|
396
|
+
#
|
397
|
+
def str_to_bytes(str)
|
398
|
+
return str if str.is_a? Array
|
399
|
+
str.bytes
|
400
|
+
end
|
401
|
+
|
402
|
+
# Get directory entries, excluding '.' and '..'
|
403
|
+
#
|
404
|
+
def dir_entries(path)
|
405
|
+
ents = Dir.entries(path)
|
406
|
+
ents.reject!{|entry| entry == '.' || entry == '..'}
|
407
|
+
end
|
408
|
+
|
409
|
+
# Convenience method for setting 'db' true within methods,
|
410
|
+
# and to print a one-time warning if so.
|
411
|
+
# @param val value to set db to; it is convenient to disable
|
412
|
+
# debug printing quickly by adding a zero, e.g., 'warndb 0'
|
413
|
+
#
|
414
|
+
def warndb(val = true)
|
415
|
+
if !val || val == 0
|
416
|
+
return false
|
417
|
+
end
|
418
|
+
one_time_alert("warning",1, "Debug printing enabled")
|
419
|
+
true
|
420
|
+
end
|
421
|
+
|
422
|
+
|
423
|
+
def int_to_bytes(x)
|
424
|
+
[(x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff]
|
425
|
+
end
|
426
|
+
|
427
|
+
def short_to_bytes(x)
|
428
|
+
[(x >> 8) & 0xff, x & 0xff]
|
429
|
+
end
|
430
|
+
|
431
|
+
# Decode a short from an array of bytes (big-endian).
|
432
|
+
# @param ba array of bytes
|
433
|
+
# @param offset offset of first (most significant) byte
|
434
|
+
#
|
435
|
+
def short_from_bytes(ba, offset=0)
|
436
|
+
(ba[offset] << 8) | ba[offset + 1]
|
437
|
+
end
|
438
|
+
|
439
|
+
# Decode an int from an array of bytes (big-endian).
|
440
|
+
# @param ba array of bytes
|
441
|
+
# @param offset offset of first (most significant) byte
|
442
|
+
#
|
443
|
+
def int_from_bytes(ba, offset=0)
|
444
|
+
(((((ba[offset] << 8) | ba[offset + 1]) << 8) | \
|
445
|
+
ba[offset + 2]) << 8) | ba[offset + 3]
|
446
|
+
end
|
447
|
+
|
448
|
+
|
449
|
+
# Delete a file or directory, if it exists.
|
450
|
+
# Caution! If directory, deletes all files and subdirectories.
|
451
|
+
def remove_file_or_dir(pth)
|
452
|
+
if File.directory?(pth)
|
453
|
+
FileUtils.remove_dir(pth)
|
454
|
+
elsif File.file?(pth)
|
455
|
+
FileUtils.remove_file(pth)
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
# Transform string to 8-bit ASCII (i.e., just treat each byte as-is)
|
460
|
+
#
|
461
|
+
def to_ascii8(str)
|
462
|
+
str.force_encoding("ASCII-8BIT")
|
463
|
+
end
|
464
|
+
|
465
|
+
# Verify that a string is encoded as ASCII-8BIT
|
466
|
+
def simple_str(s)
|
467
|
+
if s.encoding.name != 'ASCII-8BIT' && s.encoding.name != 'UTF-8'
|
468
|
+
pr("string [%s]\n encoding is %s,\n expected ASCII-8BIT\n",s,s.encoding.name)
|
469
|
+
assert!(false)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
# Truncate or pad string so it has a particular size
|
474
|
+
#
|
475
|
+
# @param s input string
|
476
|
+
# @param size
|
477
|
+
# @param pad padding character to use if string needs to grow
|
478
|
+
# @return modified string
|
479
|
+
#
|
480
|
+
def str_sized(s, size, pad="\0")
|
481
|
+
s[0...size].ljust(size,pad)
|
482
|
+
end
|
483
|
+
|
484
|
+
# Determine if running on the Windows operating system.
|
485
|
+
# Note: there is some debate about the best way to do this.
|
486
|
+
#
|
487
|
+
def windows?
|
488
|
+
if !defined? $__windows__
|
489
|
+
$__windows__ = (RUBY_PLATFORM =~ /mswin/)
|
490
|
+
end
|
491
|
+
$__windows__
|
492
|
+
end
|
493
|
+
|
494
|
+
# I didn't end up needing this:
|
495
|
+
#
|
496
|
+
#module Kernel
|
497
|
+
# def suppress_warnings
|
498
|
+
# original_verbosity = $VERBOSE
|
499
|
+
# $VERBOSE = nil
|
500
|
+
# result = yield
|
501
|
+
# $VERBOSE = original_verbosity
|
502
|
+
# return result
|
503
|
+
# end
|
504
|
+
#end
|
505
|
+
|
506
|
+
# Convenience method to detect if a script is being run
|
507
|
+
# e.g. as a 'main' method (for debug purposes only).
|
508
|
+
# If so, it changes the current directory to the
|
509
|
+
# directory containing the script.
|
510
|
+
#
|
511
|
+
# @param file pass __FILE__ in here
|
512
|
+
# @return true if so
|
513
|
+
#
|
514
|
+
def main?(file)
|
515
|
+
|
516
|
+
scr = $0
|
517
|
+
|
518
|
+
# The test/unit framework seems to be adding a suffix ": xxx#xxx.."
|
519
|
+
# to the .rb filename, so adjust in this case
|
520
|
+
i = scr.index(".rb: ")
|
521
|
+
if i
|
522
|
+
scr = scr[0...i+3]
|
523
|
+
end
|
524
|
+
|
525
|
+
if (ret = (file == scr))
|
526
|
+
Dir.chdir(File.dirname(file))
|
527
|
+
end
|
528
|
+
ret
|
529
|
+
end
|
530
|
+
|
531
|
+
if defined? Test::Unit
|
532
|
+
|
533
|
+
# Framework for suite testing
|
534
|
+
#
|
535
|
+
# If test suite functionality is desired within a script,
|
536
|
+
# then require 'test/unit' before requiring 'tools.rb'.
|
537
|
+
# This will cause the following class, MyTestSuite, to be defined.
|
538
|
+
#
|
539
|
+
# The user's test script can define subclasses of this,
|
540
|
+
# and declare test methods with the name 'test_xxxx', where
|
541
|
+
# xxxx is lexicographically between 01 and zz.
|
542
|
+
#
|
543
|
+
# There are two levels of setup/teardown called : suite level, and
|
544
|
+
# method level. For example, if the user's test class performs two tests:
|
545
|
+
#
|
546
|
+
# def test_b ... end
|
547
|
+
# def test_c ... end
|
548
|
+
#
|
549
|
+
# Then the test framework will make these calls:
|
550
|
+
#
|
551
|
+
# suite_setup
|
552
|
+
#
|
553
|
+
# method_setup
|
554
|
+
# test_b
|
555
|
+
# method_teardown
|
556
|
+
#
|
557
|
+
# method_setup
|
558
|
+
# test_c
|
559
|
+
# method_teardown
|
560
|
+
#
|
561
|
+
# suite_teardown
|
562
|
+
#
|
563
|
+
# Notes
|
564
|
+
# -----
|
565
|
+
# 1) The usual setup / teardown methods should NOT be overridden; instead,
|
566
|
+
# use the method_xxx alternatives.
|
567
|
+
#
|
568
|
+
# 2) The base class implementations of method_/suite_xxx do nothing.
|
569
|
+
#
|
570
|
+
# 3) The number of test cases reported may be higher than you expect, since
|
571
|
+
# there are test methods defined by the TestSuite class to acheive this
|
572
|
+
# functionality.
|
573
|
+
#
|
574
|
+
# 4) Avoid naming test methods that fall outside of test_01 ... test_zz.
|
575
|
+
#
|
576
|
+
class MyTestSuite < Test::Unit::TestCase
|
577
|
+
|
578
|
+
# This is named to be the FIRST test called. It
|
579
|
+
# will do suite-level setup, and nothing else.
|
580
|
+
def test_00_setup
|
581
|
+
@@suiteSetup = true
|
582
|
+
suite_setup()
|
583
|
+
end
|
584
|
+
|
585
|
+
# This is named to be the LAST test called. It
|
586
|
+
# will do suite-level teardown, and nothing else.
|
587
|
+
def test_zzzzzz_teardown
|
588
|
+
suite_teardown()
|
589
|
+
@@suiteSetup = false
|
590
|
+
end
|
591
|
+
|
592
|
+
# True if called within suite-level setup/teardown window
|
593
|
+
def _suite_active?
|
594
|
+
!(@__name__ == "test_00_setup" || @__name__ == "test_zzzzzz_teardown")
|
595
|
+
end
|
596
|
+
|
597
|
+
def setup
|
598
|
+
if _suite_active?
|
599
|
+
# If only a specific test was requested, the
|
600
|
+
# suite setup may not have run... if not, do it now.
|
601
|
+
if !defined? @@suiteSetup
|
602
|
+
suite_setup
|
603
|
+
end
|
604
|
+
return
|
605
|
+
end
|
606
|
+
method_setup
|
607
|
+
end
|
608
|
+
|
609
|
+
def teardown
|
610
|
+
if _suite_active?
|
611
|
+
if !defined? @@suiteSetup
|
612
|
+
suite_teardown
|
613
|
+
end
|
614
|
+
return
|
615
|
+
end
|
616
|
+
method_teardown
|
617
|
+
end
|
618
|
+
|
619
|
+
def suite_setup
|
620
|
+
end
|
621
|
+
|
622
|
+
def suite_teardown
|
623
|
+
end
|
624
|
+
|
625
|
+
def method_setup
|
626
|
+
end
|
627
|
+
|
628
|
+
def method_teardown
|
629
|
+
end
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|