Module: Test::Unit::Assertions

Defined in:
lib/assert_statistically.rb

Constant Summary collapse

ASSERT_STATISTICALLY_DEFAULT_FAILURE_MESSAGE =
'The block returned false on iteration %d'

Instance Method Summary collapse

Instance Method Details

#assert_statistically(options = {}, &test_block) ⇒ Object

This is the main method of this library. It takes a hash of options and a block. Based on the options, the block will be run some number of times, possibly zero. If the criteria are met, assert_statistically is in a passing state. If assert_statistically enters a passing state and determines that it can never leave, it will be default short_circuit and run no more iterations. You can override this behavior by passing :short_circuit => false in the options Hash. Other keys uses in the options Hash

:message => The message to report on failure. Takes a %d string format directive,

which is populated with the failing iteration number.

:of => The maximum number of times the test will be run (default = 1) :min => The minimum number of times the test needs to pass (default = 1) :max => The maximum number of times the test can pass (defaults to the value of of) :short_circuit => A flag determining whether to short circuit (defaults to true) :before => An optional Proc that will be called before each iteration :after => An optional Proc that will be called after each iteration

Raises:

  • (AssertionFailedError)


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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/assert_statistically.rb', line 42

def assert_statistically( options={}, &test_block )
  # If options is not a Hash, make it a hash, assuming that options.to_s is the message
  options = { :message => options.to_s } unless options.is_a?( Hash )

  # The number of times to call the block; default is 1
  of = options[:of].to_i > 0 ? options[:of].to_i : 1

  # Set min ; default is 1 (when :min is not present)
  # Note that if you pass a :min <= 0, the assertion will always pass,
  # unless max is less than min
  # Note that :min => nil is the same as :min => 0
  # Note that !defined( :min ) is the same as :min => 1
  min = options.has_key?( :min ) && options[:min].respond_to?( :to_i ) ? options[:min].to_i : 1

  # Set max ; default is :of
  # Note that you can pass a :max > :of, in which case
  # :min will be the only determinant of success
  # (since you can't have more than :of succcesses in :of attempts)
  max = options.has_key?( :max ) && options[:max].respond_to?( :to_i ) ? options[:max].to_i : of

  # Fail if min is greater than max
  raise AssertionFailedError.new( "Impossible range: [#{min}..#{max}]" ) if min > max

  # Set short_circuit ; default is true
  short_circuit = options.has_key?( :short_circuit ) && !options[:short_circuit] ? false : true

  # Create the range within which the number of successful runs must fall
  successful_range = Range.new( min, max )

  # Set up the before and after blocks
  before = options.has_key?( :before ) && options[:before].respond_to?( :call ) ? options[:before] : false
  after = options.has_key?( :after ) && options[:after].respond_to?( :call ) ? options[:after] : false

  # Set up the failure message
  message = options[:message].to_s.empty? ? ASSERT_STATISTICALLY_DEFAULT_FAILURE_MESSAGE : options[:message].to_s

  final_message = ''
  successes = 0

  1.upto( of ) do |i|
    # Check for short circuit eligibility (1 of 2)
    break if can_short_circuit?( successful_range, i - 1, of, successes, short_circuit )

    # Call any before block
    before.call( i, successes ) if before

    # Call the test block
    if test_block.call( i, successes )
      successes += 1
    else
      final_message << "#{message % i}\n"
    end

    # Check for short circuit eligibility (2 of 2)
    break if can_short_circuit?( successful_range, i, of, successes, short_circuit )

    # Call any after block
    after.call( i, successes ) if after
  end

  # Fail if the number of successes is not in the successful range
  raise AssertionFailedError.new( final_message ) unless successful_range.include?( successes )

  # Pass
  true
end

#can_short_circuit?(range, completed, total, successes, short_circuit = true) ⇒ Boolean

This method is used to determine whether we can short circuit and quit the iterations early. It is called once before the “before” block (if any), and once before the “after” block (if any). This means that if we can short circuit after the test block is called, but before the after block is called, we will. I.E., the after block will not be invoked in such a case.

Returns:

  • (Boolean)


11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/assert_statistically.rb', line 11

def can_short_circuit?( range, completed, total, successes, short_circuit=true)
  short_circuit && #first, be sure we can short circuit
  (
    # Are there enough successes, and is it impossible to get too many?
    ( range.include?( successes ) && range.include?( total - completed + successes ) ) ||

    # Are there too many successes?
    successes > range.last ||

    # Are there too few successes, and is it impossible to have enough?
    total - completed + successes < range.first
  )
end