1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
|
require 'rantly'
require 'pp'
require 'stringio'
class Rantly::Property
attr_reader :failed_data, :shrunk_failed_data
VERBOSITY = ENV.fetch('RANTLY_VERBOSE', 1).to_i
RANTLY_COUNT = ENV.fetch('RANTLY_COUNT', 100).to_i
def io
@io ||= if VERBOSITY >= 1
$stdout
else
StringIO.new
end
end
def pretty_print(object)
PP.pp(object, io)
end
def initialize(property)
@property = property
end
def check(n = RANTLY_COUNT, limit = 10, &assertion)
i = 0
test_data = nil
begin
Rantly.singleton.generate(n, limit, @property) do |val|
test_data = val
yield(val) if assertion
io.puts '' if (i % 100).zero?
io.print '.' if (i % 10).zero?
i += 1
end
io.puts
io.puts "SUCCESS - #{i} successful tests"
rescue Rantly::TooManyTries => e
io.puts
io.puts "FAILURE - #{i} successful tests, too many tries: #{e.tries}"
raise e.exception("#{i} successful tests, too many tries: #{e.tries} (limit: #{e.limit})")
rescue Exception => e
io.puts
io.puts "FAILURE - #{i} successful tests, failed on:"
pretty_print test_data
@failed_data = test_data
if @failed_data.respond_to?(:shrink)
@shrunk_failed_data, @depth = shrinkify(assertion, @failed_data)
io.puts "Minimal failed data (depth #{@depth}) is:"
pretty_print @shrunk_failed_data
end
raise e.exception("#{i} successful tests, failed on:\n#{test_data}\n\n#{e}\n")
end
end
# Explore the failures tree
def shrinkify(assertion, data, depth = 0, iteration = 0)
min_data = data
max_depth = depth
if data.shrinkable?
while iteration < 1024
# We assume that data.shrink is non-destructive
shrunk_data = data.shrink
begin
assertion.call(shrunk_data)
rescue Exception
# If the assertion was verified, recursively shrink failure case
branch_data, branch_depth, iteration = shrinkify(assertion, shrunk_data, depth + 1, iteration + 1)
if branch_depth > max_depth
min_data = branch_data
max_depth = branch_depth
end
end
break unless data.retry?
end
end
[min_data, max_depth, iteration]
end
end
|