1require_relative '../../spec_helper'
2require_relative 'fixtures/classes'
3
4describe "Array#fill" do
5  before :all do
6    @never_passed = lambda do |i|
7      raise ExpectationNotMetError, "the control path should not pass here"
8    end
9  end
10
11  it "returns self" do
12    ary = [1, 2, 3]
13    ary.fill(:a).should equal(ary)
14  end
15
16  it "is destructive" do
17    ary = [1, 2, 3]
18    ary.fill(:a)
19    ary.should == [:a, :a, :a]
20  end
21
22  it "does not replicate the filler" do
23    ary = [1, 2, 3, 4]
24    str = "x"
25    ary.fill(str).should == [str, str, str, str]
26    str << "y"
27    ary.should == [str, str, str, str]
28    ary[0].should equal(str)
29    ary[1].should equal(str)
30    ary[2].should equal(str)
31    ary[3].should equal(str)
32  end
33
34  it "replaces all elements in the array with the filler if not given a index nor a length" do
35    ary = ['a', 'b', 'c', 'duh']
36    ary.fill(8).should == [8, 8, 8, 8]
37
38    str = "x"
39    ary.fill(str).should == [str, str, str, str]
40  end
41
42  it "replaces all elements with the value of block (index given to block)" do
43    [nil, nil, nil, nil].fill { |i| i * 2 }.should == [0, 2, 4, 6]
44  end
45
46  it "raises a #{frozen_error_class} on a frozen array" do
47    lambda { ArraySpecs.frozen_array.fill('x') }.should raise_error(frozen_error_class)
48  end
49
50  it "raises a #{frozen_error_class} on an empty frozen array" do
51    lambda { ArraySpecs.empty_frozen_array.fill('x') }.should raise_error(frozen_error_class)
52  end
53
54  it "raises an ArgumentError if 4 or more arguments are passed when no block given" do
55    lambda { [].fill('a') }.should_not raise_error(ArgumentError)
56
57    lambda { [].fill('a', 1) }.should_not raise_error(ArgumentError)
58
59    lambda { [].fill('a', 1, 2) }.should_not raise_error(ArgumentError)
60    lambda { [].fill('a', 1, 2, true) }.should raise_error(ArgumentError)
61  end
62
63  it "raises an ArgumentError if no argument passed and no block given" do
64    lambda { [].fill }.should raise_error(ArgumentError)
65  end
66
67  it "raises an ArgumentError if 3 or more arguments are passed when a block given" do
68    lambda { [].fill() {|i|} }.should_not raise_error(ArgumentError)
69
70    lambda { [].fill(1) {|i|} }.should_not raise_error(ArgumentError)
71
72    lambda { [].fill(1, 2) {|i|} }.should_not raise_error(ArgumentError)
73    lambda { [].fill(1, 2, true) {|i|} }.should raise_error(ArgumentError)
74  end
75end
76
77describe "Array#fill with (filler, index, length)" do
78  it "replaces length elements beginning with the index with the filler if given an index and a length" do
79    ary = [1, 2, 3, 4, 5, 6]
80    ary.fill('x', 2, 3).should == [1, 2, 'x', 'x', 'x', 6]
81  end
82
83  it "replaces length elements beginning with the index with the value of block" do
84    [true, false, true, false, true, false, true].fill(1, 4) { |i| i + 3 }.should == [true, 4, 5, 6, 7, false, true]
85  end
86
87  it "replaces all elements after the index if given an index and no length" do
88    ary = [1, 2, 3]
89    ary.fill('x', 1).should == [1, 'x', 'x']
90    ary.fill(1){|i| i*2}.should == [1, 2, 4]
91  end
92
93  it "replaces all elements after the index if given an index and nil as a length" do
94    a = [1, 2, 3]
95    a.fill('x', 1, nil).should == [1, 'x', 'x']
96    a.fill(1, nil){|i| i*2}.should == [1, 2, 4]
97    a.fill('y', nil).should == ['y', 'y', 'y']
98  end
99
100  it "replaces the last (-n) elements if given an index n which is negative and no length" do
101    a = [1, 2, 3, 4, 5]
102    a.fill('x', -2).should == [1, 2, 3, 'x', 'x']
103    a.fill(-2){|i| i.to_s}.should == [1, 2, 3, '3', '4']
104  end
105
106  it "replaces the last (-n) elements if given an index n which is negative and nil as a length" do
107    a = [1, 2, 3, 4, 5]
108    a.fill('x', -2, nil).should == [1, 2, 3, 'x', 'x']
109    a.fill(-2, nil){|i| i.to_s}.should == [1, 2, 3, '3', '4']
110  end
111
112  it "makes no modifications if given an index greater than end and no length" do
113    [1, 2, 3, 4, 5].fill('a', 5).should == [1, 2, 3, 4, 5]
114    [1, 2, 3, 4, 5].fill(5, &@never_passed).should == [1, 2, 3, 4, 5]
115  end
116
117  it "makes no modifications if given an index greater than end and nil as a length" do
118    [1, 2, 3, 4, 5].fill('a', 5, nil).should == [1, 2, 3, 4, 5]
119    [1, 2, 3, 4, 5].fill(5, nil, &@never_passed).should == [1, 2, 3, 4, 5]
120  end
121
122  it "replaces length elements beginning with start index if given an index >= 0 and a length >= 0" do
123    [1, 2, 3, 4, 5].fill('a', 2, 0).should == [1, 2, 3, 4, 5]
124    [1, 2, 3, 4, 5].fill('a', 2, 2).should == [1, 2, "a", "a", 5]
125
126    [1, 2, 3, 4, 5].fill(2, 0, &@never_passed).should == [1, 2, 3, 4, 5]
127    [1, 2, 3, 4, 5].fill(2, 2){|i| i*2}.should == [1, 2, 4, 6, 5]
128  end
129
130  it "increases the Array size when necessary" do
131    a = [1, 2, 3]
132    a.size.should == 3
133    a.fill 'a', 0, 10
134    a.size.should == 10
135  end
136
137  it "pads between the last element and the index with nil if given an index which is greater than size of the array" do
138    [1, 2, 3, 4, 5].fill('a', 8, 5).should == [1, 2, 3, 4, 5, nil, nil, nil, 'a', 'a', 'a', 'a', 'a']
139    [1, 2, 3, 4, 5].fill(8, 5){|i| 'a'}.should == [1, 2, 3, 4, 5, nil, nil, nil, 'a', 'a', 'a', 'a', 'a']
140  end
141
142  it "replaces length elements beginning with the (-n)th if given an index n < 0 and a length > 0" do
143    [1, 2, 3, 4, 5].fill('a', -2, 2).should == [1, 2, 3, "a", "a"]
144    [1, 2, 3, 4, 5].fill('a', -2, 4).should == [1, 2, 3, "a", "a", "a", "a"]
145
146    [1, 2, 3, 4, 5].fill(-2, 2){|i| 'a'}.should == [1, 2, 3, "a", "a"]
147    [1, 2, 3, 4, 5].fill(-2, 4){|i| 'a'}.should == [1, 2, 3, "a", "a", "a", "a"]
148  end
149
150  it "starts at 0 if the negative index is before the start of the array" do
151    [1, 2, 3, 4, 5].fill('a', -25, 3).should == ['a', 'a', 'a', 4, 5]
152    [1, 2, 3, 4, 5].fill('a', -10, 10).should == %w|a a a a a a a a a a|
153
154    [1, 2, 3, 4, 5].fill(-25, 3){|i| 'a'}.should == ['a', 'a', 'a', 4, 5]
155    [1, 2, 3, 4, 5].fill(-10, 10){|i| 'a'}.should == %w|a a a a a a a a a a|
156  end
157
158  it "makes no modifications if the given length <= 0" do
159    [1, 2, 3, 4, 5].fill('a', 2, 0).should == [1, 2, 3, 4, 5]
160    [1, 2, 3, 4, 5].fill('a', -2, 0).should == [1, 2, 3, 4, 5]
161
162    [1, 2, 3, 4, 5].fill('a', 2, -2).should == [1, 2, 3, 4, 5]
163    [1, 2, 3, 4, 5].fill('a', -2, -2).should == [1, 2, 3, 4, 5]
164
165    [1, 2, 3, 4, 5].fill(2, 0, &@never_passed).should == [1, 2, 3, 4, 5]
166    [1, 2, 3, 4, 5].fill(-2, 0, &@never_passed).should == [1, 2, 3, 4, 5]
167
168    [1, 2, 3, 4, 5].fill(2, -2, &@never_passed).should == [1, 2, 3, 4, 5]
169    [1, 2, 3, 4, 5].fill(-2, -2, &@never_passed).should == [1, 2, 3, 4, 5]
170  end
171
172  # See: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/17481
173  it "does not raise an exception if the given length is negative and its absolute value does not exceed the index" do
174    lambda { [1, 2, 3, 4].fill('a', 3, -1)}.should_not raise_error(ArgumentError)
175    lambda { [1, 2, 3, 4].fill('a', 3, -2)}.should_not raise_error(ArgumentError)
176    lambda { [1, 2, 3, 4].fill('a', 3, -3)}.should_not raise_error(ArgumentError)
177
178    lambda { [1, 2, 3, 4].fill(3, -1, &@never_passed)}.should_not raise_error(ArgumentError)
179    lambda { [1, 2, 3, 4].fill(3, -2, &@never_passed)}.should_not raise_error(ArgumentError)
180    lambda { [1, 2, 3, 4].fill(3, -3, &@never_passed)}.should_not raise_error(ArgumentError)
181  end
182
183  it "does not raise an exception even if the given length is negative and its absolute value exceeds the index" do
184    lambda { [1, 2, 3, 4].fill('a', 3, -4)}.should_not raise_error(ArgumentError)
185    lambda { [1, 2, 3, 4].fill('a', 3, -5)}.should_not raise_error(ArgumentError)
186    lambda { [1, 2, 3, 4].fill('a', 3, -10000)}.should_not raise_error(ArgumentError)
187
188    lambda { [1, 2, 3, 4].fill(3, -4, &@never_passed)}.should_not raise_error(ArgumentError)
189    lambda { [1, 2, 3, 4].fill(3, -5, &@never_passed)}.should_not raise_error(ArgumentError)
190    lambda { [1, 2, 3, 4].fill(3, -10000, &@never_passed)}.should_not raise_error(ArgumentError)
191  end
192
193  it "tries to convert the second and third arguments to Integers using #to_int" do
194    obj = mock('to_int')
195    obj.should_receive(:to_int).and_return(2, 2)
196    filler = mock('filler')
197    filler.should_not_receive(:to_int)
198    [1, 2, 3, 4, 5].fill(filler, obj, obj).should == [1, 2, filler, filler, 5]
199  end
200
201  it "raises a TypeError if the index is not numeric" do
202    lambda { [].fill 'a', true }.should raise_error(TypeError)
203
204    obj = mock('nonnumeric')
205    lambda { [].fill('a', obj) }.should raise_error(TypeError)
206  end
207
208  not_supported_on :opal do
209    it "raises an ArgumentError or RangeError for too-large sizes" do
210      arr = [1, 2, 3]
211      lambda { arr.fill(10, 1, fixnum_max) }.should raise_error(ArgumentError)
212      lambda { arr.fill(10, 1, bignum_value) }.should raise_error(RangeError)
213    end
214  end
215end
216
217describe "Array#fill with (filler, range)" do
218  it "replaces elements in range with object" do
219    [1, 2, 3, 4, 5, 6].fill(8, 0..3).should == [8, 8, 8, 8, 5, 6]
220    [1, 2, 3, 4, 5, 6].fill(8, 0...3).should == [8, 8, 8, 4, 5, 6]
221    [1, 2, 3, 4, 5, 6].fill('x', 4..6).should == [1, 2, 3, 4, 'x', 'x', 'x']
222    [1, 2, 3, 4, 5, 6].fill('x', 4...6).should == [1, 2, 3, 4, 'x', 'x']
223    [1, 2, 3, 4, 5, 6].fill('x', -2..-1).should == [1, 2, 3, 4, 'x', 'x']
224    [1, 2, 3, 4, 5, 6].fill('x', -2...-1).should == [1, 2, 3, 4, 'x', 6]
225    [1, 2, 3, 4, 5, 6].fill('x', -2...-2).should == [1, 2, 3, 4, 5, 6]
226    [1, 2, 3, 4, 5, 6].fill('x', -2..-2).should == [1, 2, 3, 4, 'x', 6]
227    [1, 2, 3, 4, 5, 6].fill('x', -2..0).should == [1, 2, 3, 4, 5, 6]
228    [1, 2, 3, 4, 5, 6].fill('x', 0...0).should == [1, 2, 3, 4, 5, 6]
229    [1, 2, 3, 4, 5, 6].fill('x', 1..1).should == [1, 'x', 3, 4, 5, 6]
230  end
231
232  it "replaces all elements in range with the value of block" do
233    [1, 1, 1, 1, 1, 1].fill(1..6) { |i| i + 1 }.should == [1, 2, 3, 4, 5, 6, 7]
234  end
235
236  it "increases the Array size when necessary" do
237    [1, 2, 3].fill('x', 1..6).should == [1, 'x', 'x', 'x', 'x', 'x', 'x']
238    [1, 2, 3].fill(1..6){|i| i+1}.should == [1, 2, 3, 4, 5, 6, 7]
239  end
240
241  it "raises a TypeError with range and length argument" do
242    lambda { [].fill('x', 0 .. 2, 5) }.should raise_error(TypeError)
243  end
244
245  it "replaces elements between the (-m)th to the last and the (n+1)th from the first if given an range m..n where m < 0 and n >= 0" do
246    [1, 2, 3, 4, 5, 6].fill('x', -4..4).should == [1, 2, 'x', 'x', 'x', 6]
247    [1, 2, 3, 4, 5, 6].fill('x', -4...4).should == [1, 2, 'x', 'x', 5, 6]
248
249    [1, 2, 3, 4, 5, 6].fill(-4..4){|i| (i+1).to_s}.should == [1, 2, '3', '4', '5', 6]
250    [1, 2, 3, 4, 5, 6].fill(-4...4){|i| (i+1).to_s}.should == [1, 2, '3', '4', 5, 6]
251  end
252
253  it "replaces elements between the (-m)th and (-n)th to the last if given an range m..n where m < 0 and n < 0" do
254    [1, 2, 3, 4, 5, 6].fill('x', -4..-2).should == [1, 2, 'x', 'x', 'x', 6]
255    [1, 2, 3, 4, 5, 6].fill('x', -4...-2).should == [1, 2, 'x', 'x', 5, 6]
256
257    [1, 2, 3, 4, 5, 6].fill(-4..-2){|i| (i+1).to_s}.should == [1, 2, '3', '4', '5', 6]
258    [1, 2, 3, 4, 5, 6].fill(-4...-2){|i| (i+1).to_s}.should == [1, 2, '3', '4', 5, 6]
259  end
260
261  it "replaces elements between the (m+1)th from the first and (-n)th to the last if given an range m..n where m >= 0 and n < 0" do
262    [1, 2, 3, 4, 5, 6].fill('x', 2..-2).should == [1, 2, 'x', 'x', 'x', 6]
263    [1, 2, 3, 4, 5, 6].fill('x', 2...-2).should == [1, 2, 'x', 'x', 5, 6]
264
265    [1, 2, 3, 4, 5, 6].fill(2..-2){|i| (i+1).to_s}.should == [1, 2, '3', '4', '5', 6]
266    [1, 2, 3, 4, 5, 6].fill(2...-2){|i| (i+1).to_s}.should == [1, 2, '3', '4', 5, 6]
267  end
268
269  it "makes no modifications if given an range which implies a section of zero width" do
270    [1, 2, 3, 4, 5, 6].fill('x', 2...2).should == [1, 2, 3, 4, 5, 6]
271    [1, 2, 3, 4, 5, 6].fill('x', -4...2).should == [1, 2, 3, 4, 5, 6]
272    [1, 2, 3, 4, 5, 6].fill('x', -4...-4).should == [1, 2, 3, 4, 5, 6]
273    [1, 2, 3, 4, 5, 6].fill('x', 2...-4).should == [1, 2, 3, 4, 5, 6]
274
275    [1, 2, 3, 4, 5, 6].fill(2...2, &@never_passed).should == [1, 2, 3, 4, 5, 6]
276    [1, 2, 3, 4, 5, 6].fill(-4...2, &@never_passed).should == [1, 2, 3, 4, 5, 6]
277    [1, 2, 3, 4, 5, 6].fill(-4...-4, &@never_passed).should == [1, 2, 3, 4, 5, 6]
278    [1, 2, 3, 4, 5, 6].fill(2...-4, &@never_passed).should == [1, 2, 3, 4, 5, 6]
279  end
280
281  it "makes no modifications if given an range which implies a section of negative width" do
282    [1, 2, 3, 4, 5, 6].fill('x', 2..1).should == [1, 2, 3, 4, 5, 6]
283    [1, 2, 3, 4, 5, 6].fill('x', -4..1).should == [1, 2, 3, 4, 5, 6]
284    [1, 2, 3, 4, 5, 6].fill('x', -2..-4).should == [1, 2, 3, 4, 5, 6]
285    [1, 2, 3, 4, 5, 6].fill('x', 2..-5).should == [1, 2, 3, 4, 5, 6]
286
287    [1, 2, 3, 4, 5, 6].fill(2..1, &@never_passed).should == [1, 2, 3, 4, 5, 6]
288    [1, 2, 3, 4, 5, 6].fill(-4..1, &@never_passed).should == [1, 2, 3, 4, 5, 6]
289    [1, 2, 3, 4, 5, 6].fill(-2..-4, &@never_passed).should == [1, 2, 3, 4, 5, 6]
290    [1, 2, 3, 4, 5, 6].fill(2..-5, &@never_passed).should == [1, 2, 3, 4, 5, 6]
291  end
292
293  it "raises an exception if some of the given range lies before the first of the array" do
294    lambda { [1, 2, 3].fill('x', -5..-3) }.should raise_error(RangeError)
295    lambda { [1, 2, 3].fill('x', -5...-3) }.should raise_error(RangeError)
296    lambda { [1, 2, 3].fill('x', -5..-4) }.should raise_error(RangeError)
297
298    lambda { [1, 2, 3].fill(-5..-3, &@never_passed) }.should raise_error(RangeError)
299    lambda { [1, 2, 3].fill(-5...-3, &@never_passed) }.should raise_error(RangeError)
300    lambda { [1, 2, 3].fill(-5..-4, &@never_passed) }.should raise_error(RangeError)
301  end
302
303  it "tries to convert the start and end of the passed range to Integers using #to_int" do
304    obj = mock('to_int')
305    def obj.<=>(rhs); rhs == self ? 0 : nil end
306    obj.should_receive(:to_int).twice.and_return(2)
307    filler = mock('filler')
308    filler.should_not_receive(:to_int)
309    [1, 2, 3, 4, 5].fill(filler, obj..obj).should == [1, 2, filler, 4, 5]
310  end
311
312  it "raises a TypeError if the start or end of the passed range is not numeric" do
313    obj = mock('nonnumeric')
314    def obj.<=>(rhs); rhs == self ? 0 : nil end
315    lambda { [].fill('a', obj..obj) }.should raise_error(TypeError)
316  end
317end
318