Class: Arrow::TestCase

Inherits:
Test::Unit::TestCase
  • Object
show all
Defined in:
lib/arrow/testcase.rb

Overview

Test case class

Constant Summary collapse

AnsiAttributes =

Set some ANSI escape code constants (Shamelessly stolen from Perl’s Term::ANSIColor by Russ Allbery <[email protected]> and Zenin <[email protected]>

{
	'clear'      => 0,
	'reset'      => 0,
	'bold'       => 1,
	'dark'       => 2,
	'underline'  => 4,
	'underscore' => 4,
	'blink'      => 5,
	'reverse'    => 7,
	'concealed'  => 8,

	'black'      => 30,   'on_black'   => 40, 
	'red'        => 31,   'on_red'     => 41, 
	'green'      => 32,   'on_green'   => 42, 
	'yellow'     => 33,   'on_yellow'  => 43, 
	'blue'       => 34,   'on_blue'    => 44, 
	'magenta'    => 35,   'on_magenta' => 45, 
	'cyan'       => 36,   'on_cyan'    => 46, 
	'white'      => 37,   'on_white'   => 47
}
ConfigSaveFile =

The name of the file containing marshalled configuration values

"test.cfg"
@@methodCounter =
0

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.addSetupBlock(&block) ⇒ Object

Append a setup block for the current testcase



153
154
155
156
157
158
# File 'lib/arrow/testcase.rb', line 153

def self::addSetupBlock( &block )
	@@methodCounter += 1
	newMethodName = "setup_#{@@methodCounter}".to_sym
	define_method( newMethodName, &block )
	self.setupMethods.push newMethodName
end

.addTeardownBlock(&block) ⇒ Object

Prepend a teardown block for the current testcase



162
163
164
165
166
167
# File 'lib/arrow/testcase.rb', line 162

def self::addTeardownBlock( &block )
	@@methodCounter += 1
	newMethodName = "teardown_#{@@methodCounter}".to_sym
	define_method( newMethodName, &block )
	self.teardownMethods.unshift newMethodName
end

.ansiCode(*attributes) ⇒ Object

Returns a String containing the specified ANSI escapes suitable for inclusion in another string. The attributes should be one or more of the keys of AnsiAttributes.



122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/arrow/testcase.rb', line 122

def self::ansiCode( *attributes )
	return '' unless /(?:xterm(?:-color)?|eterm|linux)/i =~ ENV['TERM']

	attr = attributes.collect {|a|
		AnsiAttributes[a] ? AnsiAttributes[a] : nil
	}.compact.join(';')
	if attr.empty? 
		return ''
	else
		return "\e[%sm" % attr
	end
end

.debugMsg(*msgs) ⇒ Object

Output the specified msgs joined together to STDERR if $DEBUG is set.



138
139
140
141
142
# File 'lib/arrow/testcase.rb', line 138

def self::debugMsg( *msgs )
	return unless $DEBUG
	self.message "%sDEBUG>>> %s %s" %
		[ ansiCode('dark', 'white'), msgs.join(''), ansiCode('reset') ]
end

.inherited(klass) ⇒ Object

Inheritance callback – adds @setupMethods and @teardownMethods ivars and accessors to the inheriting class.



106
107
108
109
110
111
112
113
114
115
116
# File 'lib/arrow/testcase.rb', line 106

def self::inherited( klass )
	klass.module_eval {
		@setupMethods = []
		@teardownMethods = []

		class << self
			attr_accessor :setupMethods
			attr_accessor :teardownMethods
		end
	}
end

.message(*msgs) ⇒ Object

Output the specified msgs joined together to STDOUT.



147
148
149
150
# File 'lib/arrow/testcase.rb', line 147

def self::message( *msgs )
	$stderr.puts msgs.join('')
	$stderr.flush
end

Instance Method Details

#addSetupBlock(&block) ⇒ Object

Add the specified block to the code that gets executed by #setup.



219
# File 'lib/arrow/testcase.rb', line 219

def addSetupBlock( &block ); self.class.addSetupBlock( &block ); end

#addTeardownBlock(&block) ⇒ Object

Add the specified block to the code that gets executed by #teardown.



223
# File 'lib/arrow/testcase.rb', line 223

def addTeardownBlock( &block ); self.class.addTeardownBlock( &block ); end

#ansiCode(*attributes) ⇒ Object

Instance-alias for the like-named class method



461
462
463
# File 'lib/arrow/testcase.rb', line 461

def ansiCode( *attributes )
	self.class.ansiCode( *attributes )
end

#assert_has_instance_method(klass, meth) ⇒ Object

Assert that the specified klass defines the specified instance method meth.



434
435
436
437
438
439
440
441
442
443
444
445
446
# File 'lib/arrow/testcase.rb', line 434

def assert_has_instance_method( klass, meth )
	msg = "<%s> expected to define instance method #%s" %
		[ klass, meth ]
	assert_block( msg ) {
		klass.instance_methods.include?( meth.to_s )
	}
rescue Test::Unit::AssertionFailedError => err
	cutframe = err.backtrace.reverse.find {|frame|
		/assert_has_instance_method/ =~ frame
	}
	firstIdx = (err.backtrace.rindex( cutframe )||0) + 1
	Kernel.raise( err, err.message, err.backtrace[firstIdx..-1] )
end

#assert_has_ivar(sym, object) ⇒ Object

Assert that the specified object has an instance variable which matches the specified sym. The ‘@’ at the beginning of the sym will be prepended if not present.



399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/arrow/testcase.rb', line 399

def assert_has_ivar( sym, object )
	sym = "@#{sym}" unless /^@/ =~ sym.to_s
	msg = "Object <%s> expected to have an instance variable <%s>" %
		[ object.inspect, sym ]
	assert_block( msg ) {
		object.instance_variables.include?( sym.to_s )
	}
rescue Test::Unit::AssertionFailedError => err
	cutframe = err.backtrace.reverse.find {|frame|
		/assert_has_ivar/ =~ frame
	}
	firstIdx = (err.backtrace.rindex( cutframe )||0) + 1
	Kernel.raise( err, err.message, err.backtrace[firstIdx..-1] )
end

#assert_hash_equal(expected, actual, msg = "") ⇒ Object

Test Hashes for equivalent content



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/arrow/testcase.rb', line 234

def assert_hash_equal( expected, actual, msg="" )
	errmsg = "Expected hash <%p> to be equal to <%p>" % [expected, actual]
	errmsg += ": #{msg}" unless msg.empty?

	assert_block( errmsg ) {
		diffs = compare_hashes( expected, actual )
		unless diffs.empty?
			errmsg += ": " + diffs.join("; ")
			return false
		else
			return true
		end
	}
rescue Test::Unit::AssertionFailedError => err
	cutframe = err.backtrace.reverse.find {|frame|
		/assert_hash_equal/ =~ frame
	}
	firstIdx = (err.backtrace.rindex( cutframe )||0) + 1
	Kernel.raise( err, err.message, err.backtrace[firstIdx..-1] )
end

#assert_include(item, obj, msg = nil) ⇒ Object

Succeeds if obj include? item.



333
334
335
336
337
338
339
340
341
342
# File 'lib/arrow/testcase.rb', line 333

def assert_include( item, obj, msg=nil )
	msg ||= "<%p> expected to include <%p>." % [ obj, item ]
	assert_block( msg ) { obj.respond_to?(:include?) && obj.include?(item) }
rescue Test::Unit::AssertionFailedError => err
	cutframe = err.backtrace.reverse.find {|frame|
		/assert_include/ =~ frame
	}
	firstIdx = (err.backtrace.rindex( cutframe )||0) + 1
	Kernel.raise( err, err.message, err.backtrace[firstIdx..-1] )
end

#assert_ivar_equal(value, object, sym) ⇒ Object

Assert that the instance variable specified by sym of an object is equal to the specified value. The ‘@’ at the beginning of the sym will be prepended if not present.



379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
# File 'lib/arrow/testcase.rb', line 379

def assert_ivar_equal( value, object, sym )
	sym = "@#{sym}".to_sym unless /^@/ =~ sym.to_s
	msg = "Instance variable '%s'\n\tof <%s>\n\texpected to be <%s>\n" %
		[ sym, object.inspect, value.inspect ]
	msg += "\tbut was: <%p>" % [ object.instance_variable_get(sym) ]
	assert_block( msg ) {
		value == object.instance_variable_get(sym)
	}
rescue Test::Unit::AssertionFailedError => err
	cutframe = err.backtrace.reverse.find {|frame|
		/assert_ivar_equal/ =~ frame
	}
	firstIdx = (err.backtrace.rindex( cutframe )||0) + 1
	Kernel.raise( err, err.message, err.backtrace[firstIdx..-1] )
end

#assert_not_match(re, str) ⇒ Object

Assert that the specified str does not match the given regular expression re.



417
418
419
420
421
422
423
424
425
426
427
428
429
# File 'lib/arrow/testcase.rb', line 417

def assert_not_match( re, str )
	msg = "<%s> expected not to match %p" %
		[ str, re ]
	assert_block( msg ) {
		!re.match( str )
	}
rescue Test::Unit::AssertionFailedError => err
	cutframe = err.backtrace.reverse.find {|frame|
		/assert_not_match/ =~ frame
	}
	firstIdx = (err.backtrace.rindex( cutframe )||0) + 1
	Kernel.raise( err, err.message, err.backtrace[firstIdx..-1] )
end

#assert_not_nil(obj, msg = nil) ⇒ Object

Override the stupid deprecated #assert_not_nil so when it disappears, code doesn’t break.



320
321
322
323
324
325
326
327
328
329
# File 'lib/arrow/testcase.rb', line 320

def assert_not_nil( obj, msg=nil )
	msg ||= "<%p> expected to not be nil." % obj
	assert_block( msg ) { !obj.nil? }
rescue Test::Unit::AssertionFailedError => err
	cutframe = err.backtrace.reverse.find {|frame|
		/assert_not_nil/ =~ frame
	}
	firstIdx = (err.backtrace.rindex( cutframe )||0) + 1
	Kernel.raise( err, err.message, err.backtrace[firstIdx..-1] )
end

#assert_not_respond_to(obj, meth) ⇒ Object

Negative of assert_respond_to



361
362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/arrow/testcase.rb', line 361

def assert_not_respond_to( obj, meth )
	msg = "%s expected NOT to respond to '%s'" %
		[ obj.inspect, meth ]
	assert_block( msg ) {
		!obj.respond_to?( meth )
	}
rescue Test::Unit::AssertionFailedError => err
	cutframe = err.backtrace.reverse.find {|frame|
		/assert_not_respond_to/ =~ frame
	}
	firstIdx = (err.backtrace.rindex( cutframe )||0) + 1
	Kernel.raise( err, err.message, err.backtrace[firstIdx..-1] )
end

#assert_not_tainted(obj, msg = nil) ⇒ Object

Negative of assert_respond_to



346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/arrow/testcase.rb', line 346

def assert_not_tainted( obj, msg=nil )
	msg ||= "<%p> expected to NOT be tainted" % [ obj ]
	assert_block( msg ) {
		!obj.tainted?
	}
rescue Test::Unit::AssertionFailedError => err
	cutframe = err.backtrace.reverse.find {|frame|
		/assert_not_tainted/ =~ frame
	}
	firstIdx = (err.backtrace.rindex( cutframe )||0) + 1
	Kernel.raise( err, err.message, err.backtrace[firstIdx..-1] )
end

#assert_same_keys(expected, actual, msg = "") ⇒ Object

Test Hashes (or any other objects with a #keys method) for key set equality



284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/arrow/testcase.rb', line 284

def assert_same_keys( expected, actual, msg="" )
	errmsg = "Expected keys of <%p> to be equal to those of <%p>" %
		[ actual, expected ]
	errmsg += ": #{msg}" unless msg.empty?

	ekeys = expected.keys; akeys = actual.keys
	assert_block( errmsg ) {
		diffs = []

		# XOR the arrays and make a diff for each one
		((ekeys + akeys) - (ekeys & akeys)).each do |key|
			if ekeys.include?( key )
				diffs << "missing key %p" % [key]
			else
				diffs << "extra key %p" % [key]
			end
		end

		unless diffs.empty?
			errmsg += "\n" + diffs.join("; ")
			return false
		else
			return true
		end
	}
rescue Test::Unit::AssertionFailedError => err
	cutframe = err.backtrace.reverse.find {|frame|
		/assert_hash_equal/ =~ frame
	}
	firstIdx = (err.backtrace.rindex( cutframe )||0) + 1
	Kernel.raise( err, err.message, err.backtrace[firstIdx..-1] )
end

#collectGarbageObject

Try to force garbage collection to start.



502
503
504
505
506
507
# File 'lib/arrow/testcase.rb', line 502

def collectGarbage
	a = []
	1000.times { a << {} }
	a = nil
	GC.start
end

#compare_hashes(hash1, hash2, subkeys = nil) ⇒ Object

Compare two hashes for content, returning a list of their differences as descriptions. An empty Array return-value means they were the same.



258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/arrow/testcase.rb', line 258

def compare_hashes( hash1, hash2, subkeys=nil )
	diffs = []
	seenKeys = []

	hash1.each {|k,v|
		if !hash2.key?( k )
			diffs << "missing %p pair" % k
		elsif hash1[k].is_a?( Hash ) && hash2[k].is_a?( Hash )
			diffs.push( compare_hashes(hash1[k], hash2[k]) )
		elsif hash2[k] != hash1[k]
			diffs << "value for %p expected to be %p, but was %p" %
				[ k, hash1[k], hash2[k] ]
		else
			seenKeys << k
		end
	}

	extraKeys = (hash2.keys - hash1.keys)
	diffs << "extra key/s: #{extraKeys.join(', ')}" unless extraKeys.empty?

	return diffs.flatten
end

#debugMsg(*msgs) ⇒ Object

Instance alias for the like-named class method



467
468
469
# File 'lib/arrow/testcase.rb', line 467

def debugMsg( *msgs )
	self.class.debugMsg( *msgs )
end

#default_testObject

Turn off the stupid ‘No tests were specified’



227
# File 'lib/arrow/testcase.rb', line 227

def default_test; end

#hrule(length = 75, char = "-") ⇒ Object

Return a separator line made up of length of the specified char.



474
475
476
# File 'lib/arrow/testcase.rb', line 474

def hrule( length=75, char="-" )
	return (char * length ) + "\n"
end

#hruleSection(content, caption = '') ⇒ Object

Return a section delimited by hrules with the specified caption and content.



480
481
482
483
484
485
486
# File 'lib/arrow/testcase.rb', line 480

def hruleSection( content, caption='' )
	caption << ' ' unless caption.empty?
	return caption +
		hrule( 75 - caption.length ) +
		content.chomp + "\n" +
		hrule()
end

#message(*msgs) ⇒ Object

Instance alias for the like-named class method.



455
456
457
# File 'lib/arrow/testcase.rb', line 455

def message( *msgs )
	self.class.message( *msgs )
end

#printTestHeader(desc) ⇒ Object

Output a header for delimiting tests



490
491
492
493
494
# File 'lib/arrow/testcase.rb', line 490

def printTestHeader( desc )
	return unless $VERBOSE || $DEBUG
	message "%s>>> %s <<<%s" % 
		[ ansiCode('bold','yellow','on_blue'), desc, ansiCode('reset') ]
end

#prompt(promptString) ⇒ Object

Output the specified promptString as a prompt (in green) and return the user’s input with leading and trailing spaces removed.



547
548
549
550
551
# File 'lib/arrow/testcase.rb', line 547

def prompt( promptString )
	promptString.chomp!
	return readline( ansiCode('bold', 'green') + "#{promptString}: " +
					 ansiCode('reset') ).strip
end

#promptWithDefault(promptString, default) ⇒ Object

Prompt the user with the given promptString via #prompt, substituting the given default if the user doesn’t input anything.



557
558
559
560
561
562
563
564
# File 'lib/arrow/testcase.rb', line 557

def promptWithDefault( promptString, default )
	response = prompt( "%s [%s]" % [ promptString, default ] )
	if response.empty?
		return default
	else
		return response
	end
end

#run(result) ⇒ Object

Output the name of the test as it’s running if in verbose mode.



518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
# File 'lib/arrow/testcase.rb', line 518

def run( result )
	$stderr.puts self.name if $VERBOSE || $DEBUG

	# Support debugging for individual tests
	olddb = nil
	if $DebugPattern && $DebugPattern =~ @method_name
		Arrow::Logger.global.outputters <<
			Arrow::Logger::Outputter.create( 'file:stderr' )
		Arrow::Logger.global.level = :debug

		olddb = $DEBUG
		$DEBUG = true
	end
	
	super

	unless olddb.nil?
		$DEBUG = olddb 
		Arrow::Logger.global.outputters.clear
	end
end

#setup(*args) ⇒ Object Also known as: set_up

Run dynamically-added setup methods



180
181
182
183
184
185
186
# File 'lib/arrow/testcase.rb', line 180

def setup( *args )
	if self.class < Arrow::TestCase
		self.class.setupMethods.each {|sblock|
			self.send( sblock )
		}
	end
end

#skip(reason = nil) ⇒ Object

Skip the current step (called from #setup) with the reason given.



202
203
204
205
206
207
208
209
210
211
# File 'lib/arrow/testcase.rb', line 202

def skip( reason=nil )
	if reason
		msg = "Skipping %s: %s" % [ @method_name, reason ]
	else
		msg = "Skipping %s: No reason given." % @method_name
	end

	$stderr.puts( msg ) if $VERBOSE
	@method_name = :skipped_test
end

#skipped_testObject

:nodoc:



214
215
# File 'lib/arrow/testcase.rb', line 214

def skipped_test # :nodoc:
end

#teardown(*args) ⇒ Object Also known as: tear_down

Run dynamically-added teardown methods



191
192
193
194
195
196
197
# File 'lib/arrow/testcase.rb', line 191

def teardown( *args )
	if self.class < Arrow::TestCase
		self.class.teardownMethods.each {|tblock|
			self.send( tblock )
		}
	end
end

#zerofile(filename) ⇒ Object

Touch a file and truncate it to 0 bytes



511
512
513
514
# File 'lib/arrow/testcase.rb', line 511

def zerofile( filename )
	File.open( filename, File::WRONLY|File::CREAT ) {}
	File.truncate( filename, 0 )
end