Created
April 13, 2012 20:17
-
-
Save h0rs3r4dish/2379843 to your computer and use it in GitHub Desktop.
Inline Ruby tests
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ ruby inline-test.rb demo.rb | |
demo.rb | |
my_function .. | |
first .. | |
Foo#bar . | |
Bar::Baz#bar .. | |
7/7 tests passed in 0.113 seconds | |
$ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
=begin | |
This is a demo of how inline testing works. It's really simple. Everything that gets | |
tested has to be specified via comment blocks tagged with 'test' before the thing that | |
it describes: | |
=begin test | |
# Tests here | |
=end | |
def my_method ... | |
To describe an outcome, just use parentheses to specify the method's arguments in Ruby | |
code: | |
fn() # no arguments | |
fn(1, 2, "blah") # some arguments | |
...And then use != or == to specify what it should/shouldn't be: | |
fn() == 5 | |
fn(10) != 6 | |
If you have classes, you can test those just fine. Pre-test setup conditions are pretty | |
basic, basically creating an instance using any given arguments via the "setup" keyword. | |
Multiple setup conditions means that every test for that class will be run with every | |
type of instance. In the example below, the Baz class has one test specified, but two | |
constructors, so the test is run twice. | |
new() # No arguments to Foo.new | |
new(10) # Now we're cooking | |
=end | |
=begin test | |
fn() == 5 | |
fn(10) == 10 | |
=end | |
def my_function(val=5) | |
return val | |
end | |
=begin test | |
fn([0,1,2]) == 0 | |
fn([1,1,1]) != 0 | |
=end | |
def first(array) | |
return array.first | |
end | |
=begin test | |
new(1) | |
=end | |
class Foo | |
def initialize(var) | |
@bar = var | |
end | |
=begin test | |
fn() == 1 | |
=end | |
def bar | |
@bar | |
end | |
end | |
module Bar | |
=begin test | |
new() | |
new(1) | |
=end | |
class Baz | |
def initialize(v=5) | |
@bar = 10 | |
end | |
=begin test | |
fn() == 10 | |
=end | |
def bar | |
@bar | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ARGV.each { |filename| | |
require_relative filename | |
print filename | |
$context = { | |
:method => "", # current method name | |
:class_name => "", # class name | |
:class_init => [ ], # class constructors | |
:class_last_line => 0, # where the class ends | |
:ns_name => "", # namespace name | |
:ns_last_line => 0 # namespace's end | |
} | |
$tests = { :passed => 0, :total => 0, :time => Time.now } | |
lines = IO.readlines(filename) | |
lines.each_with_index { |line, i| | |
if line.strip =~ /^module (.+)$/ then | |
$context[:ns_name] = $1 | |
ends_needed = 1 | |
current_index = i | |
until ends_needed == 0 do | |
current_index += 1 | |
current_line = lines[current_index].strip | |
ends_needed += 1 if current_line =~ /( do|^def|^class| then|[^=]begin)/ | |
ends_needed -= 1 if current_line == "end" | |
end | |
$context[:ns_last_line] = current_index | |
end | |
next unless line.strip == "=begin test" | |
test_start_index = i+1 | |
test_end_index = i+2 | |
test_end_index += 1 until lines[test_end_index].strip == "=end" | |
in_class = (test_end_index < $context[:class_last_line]) | |
in_ns = (test_end_index < $context[:ns_last_line]) | |
next_line_a = lines[test_end_index+1].strip.scan(/^(def|class|namespace) ([^ ]*)/).flatten | |
case next_line_a.first | |
when "def" | |
$context[:method] = next_line_a.last.split('(').first | |
print "\n" + ((in_ns) ? ($context[:ns_name] + "::") : "") + | |
((in_class) ? ($context[:class_name] + "#") : "") + $context[:method] + " " | |
when "class" | |
$context[:class_name] = next_line_a.last | |
$context[:class_init] = Array.new | |
when "namespace" | |
$context[:ns_name] = next_line_a.last | |
end | |
relavent_lines = lines[test_start_index...test_end_index] # exclude the last, "=end" line | |
relavent_lines.each { |test_line| | |
if test_line =~ /^new\((.+)\)$/ then | |
$context[:class_init].push "(%s)" % $1 | |
ends_needed = 1 | |
current_line = test_end_index | |
while ends_needed > 0 do | |
current_line += 1 | |
line = lines[current_line] | |
ends_needed += 1 if line =~ /( then| do|def |[^=]begin)/ | |
ends_needed -= 1 if line.strip == "end" | |
end | |
$context[:class_last_line] = current_line | |
elsif test_line =~ /^fn(\(.*\)) ([=!]=) (.*)$/ then | |
test = { | |
:actual => Array.new, | |
:expected => (eval $3), | |
:inverse => ($2 == "!=") | |
} | |
method_args = $1 | |
if in_class then | |
$context[:class_init].each { |init| | |
obj = eval( ((in_ns) ? $context[:ns_name] + "::" : "" ) + | |
$context[:class_name] + ".new" + init ) | |
test[:actual].push( obj.instance_eval $context[:method] + method_args ) | |
} | |
else | |
test[:actual].push( eval $context[:method] + method_args) | |
end | |
test[:actual].each { |actual| | |
assertion = actual == test[:expected] | |
assertion = !assertion if test[:inverse] | |
if assertion then | |
print '.' | |
$tests[:passed] += 1 | |
else | |
print 'f' | |
end | |
$tests[:total] += 1 | |
} | |
end | |
} | |
} | |
puts "\n%d/%d tests passed in %0.3f seconds" % [$tests[:passed], $tests[:total], | |
Time.now - $tests[:time]] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment