summaryrefslogtreecommitdiff
path: root/spec/ruby/core/range/minmax_spec.rb
blob: 6651ae3726d9062a8472fbd8871d10e5c9c30d15 (plain)
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
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
require_relative '../../spec_helper'

describe 'Range#minmax' do
  before(:each) do
    @x = mock('x')
    @y = mock('y')

    @x.should_receive(:<=>).with(@y).any_number_of_times.and_return(-1) # x < y
    @x.should_receive(:<=>).with(@x).any_number_of_times.and_return(0) # x == x
    @y.should_receive(:<=>).with(@x).any_number_of_times.and_return(1) # y > x
    @y.should_receive(:<=>).with(@y).any_number_of_times.and_return(0) # y == y
  end

  describe 'on an inclusive range' do
    it 'should raise RangeError on an endless range without iterating the range' do
      @x.should_not_receive(:succ)

      range = (@x..)

      -> { range.minmax }.should raise_error(RangeError, 'cannot get the maximum of endless range')
    end

    it 'raises RangeError or ArgumentError on a beginless range' do
      range = (..@x)

      -> { range.minmax }.should raise_error(StandardError) { |e|
        if RangeError === e
          # error from #min
          -> { raise e }.should raise_error(RangeError, 'cannot get the minimum of beginless range')
        else
          # error from #max
          -> { raise e }.should raise_error(ArgumentError, 'comparison of NilClass with MockObject failed')
        end
      }
    end

    it 'should return beginning of range if beginning and end are equal without iterating the range' do
      @x.should_not_receive(:succ)

      (@x..@x).minmax.should == [@x, @x]
    end

    it 'should return nil pair if beginning is greater than end without iterating the range' do
      @y.should_not_receive(:succ)

      (@y..@x).minmax.should == [nil, nil]
    end

    it 'should return the minimum and maximum values for a non-numeric range without iterating the range' do
      @x.should_not_receive(:succ)

      (@x..@y).minmax.should == [@x, @y]
    end

    it 'should return the minimum and maximum values for a numeric range' do
      (1..3).minmax.should == [1, 3]
    end

    it 'should return the minimum and maximum values for a numeric range without iterating the range' do
      # We cannot set expectations on integers,
      # so we "prevent" iteration by picking a value that would iterate until the spec times out.
      range_end = Float::INFINITY

      (1..range_end).minmax.should == [1, range_end]
    end

    it 'should return the minimum and maximum values according to the provided block by iterating the range' do
      @x.should_receive(:succ).once.and_return(@y)

      (@x..@y).minmax { |x, y| - (x <=> y) }.should == [@y, @x]
    end
  end

  describe 'on an exclusive range' do
    it 'should raise RangeError on an endless range' do
      @x.should_not_receive(:succ)
      range = (@x...)

      -> { range.minmax }.should raise_error(RangeError, 'cannot get the maximum of endless range')
    end

    it 'should raise RangeError on a beginless range' do
      range = (...@x)

      -> { range.minmax }.should raise_error(RangeError,
        /cannot get the maximum of beginless range with custom comparison method|cannot get the minimum of beginless range/)
    end

    it 'should return nil pair if beginning and end are equal without iterating the range' do
      @x.should_not_receive(:succ)

      (@x...@x).minmax.should == [nil, nil]
    end

    it 'should return nil pair if beginning is greater than end without iterating the range' do
      @y.should_not_receive(:succ)

      (@y...@x).minmax.should == [nil, nil]
    end

    it 'should return the minimum and maximum values for a non-numeric range by iterating the range' do
      @x.should_receive(:succ).once.and_return(@y)

      (@x...@y).minmax.should == [@x, @x]
    end

    it 'should return the minimum and maximum values for a numeric range' do
      (1...3).minmax.should == [1, 2]
    end

    it 'should return the minimum and maximum values for a numeric range without iterating the range' do
      # We cannot set expectations on integers,
      # so we "prevent" iteration by picking a value that would iterate until the spec times out.
      range_end = bignum_value

      (1...range_end).minmax.should == [1, range_end - 1]
    end

    it 'raises TypeError if the end value is not an integer' do
      range = (0...Float::INFINITY)
      -> { range.minmax }.should raise_error(TypeError, 'cannot exclude non Integer end value')
    end

    it 'should return the minimum and maximum values according to the provided block by iterating the range' do
      @x.should_receive(:succ).once.and_return(@y)

      (@x...@y).minmax { |x, y| - (x <=> y) }.should == [@x, @x]
    end
  end
end