File: property.rb

package info (click to toggle)
ruby-rantly 3.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 196 kB
  • sloc: ruby: 832; makefile: 4
file content (81 lines) | stat: -rw-r--r-- 2,325 bytes parent folder | download
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