huge_enumerable 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,642 +1,642 @@
1
- require 'spec_helper'
2
-
3
- describe HugeEnumerable do
4
-
5
- let(:collection) { ('a'..'z').to_a }
6
-
7
- subject(:enumerable) do
8
- klass = Class.new(HugeEnumerable)
9
- enum_collection = collection.sort
10
- klass.define_method(:collection_size) { enum_collection.size }
11
- klass.define_method(:fetch) { |x| enum_collection[x] }
12
- klass.send(:public, :next_prime)
13
- klass.send(:public, :_fetch)
14
- klass.send(:public, :element_or_array)
15
- klass.send(:public, :full_cycle_increment)
16
- klass.new
17
- end
18
-
19
- subject(:emptied_enumerable) do
20
- enumerable.tap do |enum|
21
- enum.max_array_size = enum.size
22
- enum.next_array
23
- end
24
- end
25
-
26
- context ".new" do
27
-
28
- context "with no arguments" do
29
-
30
- it "defaults max_array_size to a Numeric" do
31
- enumerable.max_array_size.should be_kind_of(Numeric)
32
- end
33
-
34
- it "defaults rng to #rand" do
35
- enumerable.rng.should eq(enumerable.method(:rand))
36
- end
37
-
38
- end
39
-
40
- end
41
-
42
- context "#[]" do
43
-
44
- context "with a positive index" do
45
- it "returns the element from the beginning of the collection at the specified index" do
46
- enumerable[3].should eql(collection[3])
47
- end
48
- end
49
-
50
- context "with a negative index" do
51
- it "returns the element from the end of the collection at the specified index" do
52
- enumerable[-3].should eql(collection[-3])
53
- end
54
- end
55
-
56
- context "with an out of bounds index" do
57
- it "returns nil" do
58
- enumerable[enumerable.size + 1].should be_nil
59
- end
60
- end
61
-
62
- context "with a range" do
63
- it "returns an array of elements corresponding to indexes within the range" do
64
- size = collection.size + 1
65
- test_range = (-size..size).to_a
66
- ranges = test_range.product(test_range).map { |x| (x.first..x.last) }
67
- arrays = ranges.map { |range| [enumerable[range], collection[range]] }
68
- arrays.each { |x| x.first.should eq(x.last) }
69
- end
70
- end
71
-
72
- context "with a length" do
73
- it "returns an array of elements corresponding to starting with index and containing a maximum of length items" do
74
- size = collection.size + 1
75
- test_range = (-size..size).to_a
76
- index_lengths = test_range.product(test_range).map { |x| [x.first, x.last] }
77
- arrays = index_lengths.map do |idx_len|
78
- index = idx_len.first
79
- length = idx_len.last
80
- [enumerable[index, length], collection[index, length]]
81
- end
82
- arrays.each { |x| x.first.should eq(x.last) }
83
- end
84
- end
85
-
86
-
87
- it "relays to #_fetch for index mapping" do
88
- enumerable.should_receive(:_fetch).at_least(:once)
89
- enumerable[0]
90
- end
91
-
92
- end
93
-
94
- context "#each" do
95
-
96
- it "yields #max_array_size items" do
97
- items = []
98
- total_items = collection.size / 5
99
- enumerable.max_array_size = total_items
100
- enumerable.each { |x| items << x }
101
- items.size.should eql(total_items)
102
- end
103
-
104
- it "yields only the remaining items if there are fewer than #max_array_size" do
105
- items = []
106
- enumerable.max_array_size = collection.size + 1
107
- enumerable.each { |x| items << x }
108
- items.size.should eql(collection.size)
109
- end
110
-
111
- it "relays to #_fetch for index mapping" do
112
- enumerable.should_receive(:_fetch).at_least(:once)
113
- enumerable.each {}
114
- end
115
-
116
- end
117
-
118
- context "#combination" do
119
-
120
- context "with no block" do
121
-
122
- it "returns a new HugeCombination" do
123
- enumerable.combination(2).should be_instance_of(HugeCombination)
124
- end
125
-
126
- end
127
-
128
- context "with a block" do
129
-
130
- # This should really be calls the block from the combination since there is testing duplication here.
131
- # However, knowing where the block is called from would need stack investigation or similar.
132
- it "calls the block for each combination element" do
133
- combo = enumerable.combination(2)
134
- enumerable.max_array_size = combo.size # Hrm. Perhaps having to do this is a reason to have it call collection_each
135
- index = 0
136
- enumerable.combination(2) do |x|
137
- x.should eql(combo[index])
138
- index += 1
139
- end
140
- index.should eql(combo.size)
141
- end
142
-
143
- it "returns self" do
144
- enumerable.combination(2) {}.should equal(enumerable)
145
- end
146
-
147
- end
148
-
149
- it "uses self a dup of itself as the enumerable" do
150
- dupped_enumerable = enumerable.dup
151
- enumerable.stub(:dup).and_return(dupped_enumerable)
152
- HugeCombination.should_receive(:new).with(dupped_enumerable, 2, enumerable.max_array_size, nil)
153
- enumerable.combination(2)
154
- end
155
-
156
- it "uses the same max array size" do
157
- enumerable.combination(2).max_array_size.should eql(enumerable.max_array_size)
158
- end
159
-
160
- it "uses the same random number generator" do
161
- enumerable.rng = Proc.new { 1 }
162
- enumerable.combination(2).rng.should eq(enumerable.rng)
163
- end
164
-
165
- it "calls reset! on the dup" do
166
- HugeEnumerable.send(:public, :reset!)
167
- dupped_enumerable = enumerable.dup
168
- enumerable.stub(:dup).and_return(dupped_enumerable)
169
- dupped_enumerable.should_receive(:reset!).and_call_original
170
- enumerable.combination(2)
171
- end
172
-
173
- end
174
-
175
-
176
- context "#max_array_size" do
177
-
178
- context "not explicitly set" do
179
-
180
- it "defaults to DEFAULT_MAX_ARRAY_SIZE if smaller than #collection_size" do
181
- enumerable.stub(:collection_size).and_return(HugeEnumerable::DEFAULT_MAX_ARRAY_SIZE + 1)
182
- enumerable.max_array_size.should eql(HugeEnumerable::DEFAULT_MAX_ARRAY_SIZE)
183
- end
184
-
185
- it "defaults to #collection_size if smaller than DEFAULT_MAX_ARRAY_SIZE" do
186
- enumerable.stub(:collection_size).and_return(HugeEnumerable::DEFAULT_MAX_ARRAY_SIZE - 1)
187
- enumerable.max_array_size.should eql(HugeEnumerable::DEFAULT_MAX_ARRAY_SIZE - 1)
188
- end
189
-
190
- end
191
-
192
- end
193
-
194
- context "#next_array" do
195
-
196
- it "advances to the next array in the collection" do
197
- size = collection.size / 5
198
- enumerable.max_array_size = size
199
- enumerable.next_array.should eql(collection[size...size*2])
200
- end
201
-
202
- it "changes #size" do
203
- size = collection.size / 5
204
- enumerable.max_array_size = size
205
- enumerable.next_array
206
- enumerable.size.should eql(collection.size - size)
207
- end
208
-
209
- end
210
-
211
- context "#empty?" do
212
-
213
- it "returns true if the collection has been entirely emptied by #pop, #shift, or #next_array" do
214
- enumerable.max_array_size = collection.size
215
- enumerable.next_array
216
- enumerable.empty?.should be_true
217
- end
218
-
219
- it "returns false if the collection has been not entirely emptied by #pop, #shift, or #next_array" do
220
- enumerable.max_array_size = collection.size - 1
221
- enumerable.next_array
222
- enumerable.empty?.should be_false
223
- end
224
-
225
- end
226
-
227
- context "#permutation" do
228
-
229
- context "with no block" do
230
-
231
- it "returns a new HugePermutation" do
232
- enumerable.permutation(2).should be_instance_of(HugePermutation)
233
- end
234
-
235
- end
236
-
237
- context "with a block" do
238
-
239
- # This should really be calls the block from the combination since there is testing duplication here.
240
- # However, knowing where the block is called from would need stack investigation or similar.
241
- it "calls the block for each permutation element" do
242
- perm = enumerable.permutation(2)
243
- enumerable.max_array_size = perm.size # Hrm. Perhaps having to do this is a reason to have it call collection_each
244
- index = 0
245
- enumerable.permutation(2) do |x|
246
- x.should eql(perm[index])
247
- index += 1
248
- end
249
- index.should eql(perm.size)
250
- end
251
-
252
- it "returns self" do
253
- enumerable.permutation(2) {}.should equal(enumerable)
254
- end
255
-
256
- end
257
-
258
- it "uses self a dup of itself as the enumerable" do
259
- dupped_enumerable = enumerable.dup
260
- enumerable.stub(:dup).and_return(dupped_enumerable)
261
- HugePermutation.should_receive(:new).with(dupped_enumerable, 2, enumerable.max_array_size, nil)
262
- enumerable.permutation(2)
263
- end
264
-
265
- it "uses the same max array size" do
266
- enumerable.permutation(2).max_array_size.should eql(enumerable.max_array_size)
267
- end
268
-
269
- it "uses the same random number generator" do
270
- enumerable.rng = Proc.new { 1 }
271
- enumerable.permutation(2).rng.should eq(enumerable.rng)
272
- end
273
-
274
- it "calls reset! on the dup" do
275
- HugeEnumerable.send(:public, :reset!)
276
- dupped_enumerable = enumerable.dup
277
- enumerable.stub(:dup).and_return(dupped_enumerable)
278
- dupped_enumerable.should_receive(:reset!).and_call_original
279
- enumerable.permutation(2)
280
- end
281
-
282
- end
283
-
284
-
285
- context "#pop" do
286
-
287
- context "on a non empty collection" do
288
- context "with no parameter" do
289
- it "returns the next element from the end of the collection" do
290
- enumerable.pop.should eql(collection.pop)
291
- end
292
- end
293
-
294
- context "with a parameter" do
295
- it "returns an array of the next N elements from the end of the collection" do
296
- enumerable.pop(3).should eql(collection.pop(3))
297
- end
298
- end
299
-
300
- it "removes the elements from the end of the collection" do
301
- enumerable.pop
302
- enumerable.pop(3)
303
- enumerable.to_a.should eql(collection[0..-5])
304
- end
305
-
306
- it "changes #size" do
307
- enumerable.pop
308
- enumerable.pop(3)
309
- enumerable.size.should eql(collection.size - 4)
310
- end
311
-
312
- it "does not harm the original collection" do
313
- original = collection.dup
314
- enumerable.pop
315
- enumerable.pop(3)
316
- collection.should eql(original)
317
- end
318
-
319
- end
320
-
321
- it "depends on #(private)element_or_array" do
322
- enumerable.should_receive(:element_or_array).twice.and_call_original
323
- enumerable.pop
324
- enumerable.pop(3)
325
- end
326
-
327
- end
328
-
329
- context "#product" do
330
-
331
- context "with no block" do
332
-
333
- it "returns a new HugeProduct" do
334
- enumerable.product([]).should be_instance_of(HugeProduct)
335
- end
336
-
337
- end
338
-
339
- context "with a block" do
340
-
341
- # This should really be calls the block from the combination since there is testing duplication here.
342
- # However, knowing where the block is called from would need stack investigation or similar.
343
- it "calls the block for each product element" do
344
- other_enumerable = [1, 2, 3]
345
- prod = enumerable.product(other_enumerable)
346
- enumerable.max_array_size = prod.size # Hrm. Perhaps having to do this is a reason to have it call collection_each
347
- index = 0
348
- enumerable.product(other_enumerable) do |x|
349
- x.should eql(prod[index])
350
- index += 1
351
- end
352
- index.should eql(prod.size)
353
- end
354
-
355
- it "returns self" do
356
- enumerable.product([]) {}.should equal(enumerable)
357
- end
358
-
359
- end
360
-
361
- it "uses self a dup of itself as the enumerable" do
362
- dupped_enumerable = enumerable.dup
363
- enumerable.stub(:dup).and_return(dupped_enumerable)
364
- other_enumerable = []
365
- HugeProduct.should_receive(:new).with(dupped_enumerable, other_enumerable, enumerable.max_array_size, nil)
366
- enumerable.product(other_enumerable)
367
- end
368
-
369
- it "calls dup on the other enumerable if it is a HugeEnumerable" do
370
- other_enumerable = HugeCollection.new([])
371
- other_enumerable.should_receive(:dup).and_call_original
372
- enumerable.product(other_enumerable)
373
- end
374
-
375
- it "calls reset on the other enumerable if it is a HugeEnumerable" do
376
- HugeEnumerable.send(:public, :reset!)
377
- other_enumerable = HugeCollection.new([])
378
- dupped_enumerable = other_enumerable.dup
379
- other_enumerable.stub(:dup).and_return(dupped_enumerable)
380
- dupped_enumerable.should_receive(:reset!).and_call_original
381
- enumerable.product(other_enumerable)
382
- end
383
-
384
- it "does not call dup on the other enumerable if it is a HugeEnumerable" do
385
- other_enumerable = []
386
- other_enumerable.should_not_receive(:dup)
387
- enumerable.product(other_enumerable)
388
- end
389
-
390
- it "uses the same max array size" do
391
- enumerable.product([]).max_array_size.should eql(enumerable.max_array_size)
392
- end
393
-
394
- it "uses the same random number generator" do
395
- enumerable.rng = Proc.new { 1 }
396
- enumerable.product([]).rng.should eq(enumerable.rng)
397
- end
398
-
399
- it "calls reset! on the dup" do
400
- HugeEnumerable.send(:public, :reset!)
401
- dupped_enumerable = enumerable.dup
402
- enumerable.stub(:dup).and_return(dupped_enumerable)
403
- dupped_enumerable.should_receive(:reset!).and_call_original
404
- enumerable.product([])
405
- end
406
-
407
- end
408
-
409
- context "#sample" do
410
-
411
- context "on an non empty collection" do
412
- context "with no arguments" do
413
- it "returns a single element from the collection" do
414
- collection.include?(enumerable.sample).should be_true
415
- end
416
- end
417
-
418
- context "with size argument" do
419
- it "returns N elements from the collection" do
420
- samples = enumerable.sample(3)
421
- samples.should have(3).items
422
- samples.all? { |item| collection.include?(item) }.should be_true
423
- end
424
- end
425
-
426
- it "returns elements from the collection in a pseudo random pattern" do
427
- enumerable.sample(enumerable.size).should_not eq(collection)
428
- end
429
-
430
- it "visits each element exactly once before repeating" do
431
- samples = []
432
- enumerable.size.times { samples << enumerable.sample }
433
- samples.uniq.should have(collection.size).items
434
- end
435
-
436
- it "does not reorder the original collection" do
437
- original = collection.dup
438
- enumerable.sample
439
- enumerable.sample(3)
440
- collection.should eql(original)
441
- end
442
-
443
- end
444
-
445
- it "depends on #(private)element_or_array" do
446
- enumerable.should_receive(:element_or_array).twice.and_call_original
447
- enumerable.sample
448
- enumerable.sample(3)
449
- end
450
-
451
- end
452
-
453
- context "#shift" do
454
-
455
- context "on a non empty collection" do
456
- context "with no parameter" do
457
- it "returns the next element from the beginning of the collection" do
458
- enumerable.shift.should eql(collection.shift)
459
- end
460
- end
461
-
462
- context "with a parameter" do
463
- it "returns an array of the next N elements from the beginning of the collection" do
464
- enumerable.shift(3).should eql(collection.shift(3))
465
- end
466
- end
467
-
468
- it "removes the elements from the beginning of the collection" do
469
- enumerable.shift
470
- enumerable.shift(3)
471
- enumerable.to_a.should eql(collection[4..-1])
472
- end
473
-
474
- it "changes #size" do
475
- enumerable.shift
476
- enumerable.shift(3)
477
- enumerable.size.should eql(collection.size - 4)
478
- end
479
-
480
- it "does not harm the original collection" do
481
- original = collection.dup
482
- enumerable.shift
483
- enumerable.shift(3)
484
- collection.should eql(original)
485
- end
486
-
487
- end
488
-
489
- it "depends on #(private)element_or_array" do
490
- enumerable.should_receive(:element_or_array).twice.and_call_original
491
- enumerable.shift
492
- enumerable.shift(3)
493
- end
494
-
495
- end
496
-
497
- context "#shuffle" do
498
-
499
- it "returns a new HugeEnumerable" do
500
- enumerable.shuffle.should_not equal(enumerable)
501
- end
502
-
503
- end
504
-
505
- context "#shuffle!" do
506
-
507
- it "randomly alters the order of the sequence" do
508
- fake_random = Proc.new { |x| 2 % x }
509
- enumerable.max_array_size = enumerable.size
510
- original = enumerable.to_a
511
- enumerable.shuffle!(fake_random)
512
- shuffle1 = enumerable.to_a
513
- fake_random = Proc.new { |x| 3 % x }
514
- enumerable.shuffle!(fake_random)
515
- shuffle2 = enumerable.to_a
516
- original.should_not eq(shuffle1)
517
- original.should_not eq(shuffle2)
518
- shuffle1.should_not eq(shuffle2)
519
- end
520
-
521
- it "contains all of the original elements" do
522
- enumerable.shuffle!.to_a.sort.should eq(collection)
523
- end
524
-
525
- it "does noy alter the original collection" do
526
- original = collection.dup
527
- enumerable.max_array_size = enumerable.size
528
- enumerable.shuffle!
529
- enumerable.to_a.should_not eq(original)
530
- original.should eq(collection)
531
- end
532
-
533
- end
534
-
535
- context "#size" do
536
-
537
- it "returns the current size of the collection" do
538
- enumerable.size.should eql(collection.size)
539
- end
540
-
541
- end
542
-
543
- context "#(private)next_prime" do
544
-
545
- it "should return the next prime following any integer" do
546
- primes = Prime.first(100)
547
- x = 0
548
- until primes.empty?
549
- enumerable.next_prime(x).should eq(primes.first)
550
- x += 1
551
- primes.shift if x.prime?
552
- end
553
- end
554
-
555
- end
556
-
557
- context "#(private)_fetch" do
558
-
559
- context "with an index outside the range of (sequence_start..sequence_end)" do
560
- it "should never relay to fetch" do
561
- enumerable.should_not_receive(:fetch)
562
- enumerable._fetch(-1 * enumerable.size - 1)
563
- enumerable._fetch(enumerable.size)
564
- end
565
- end
566
-
567
- context "with an index inside the range of (sequence_start..sequence_end)" do
568
- it "should relay to fetch" do
569
- enumerable.should_receive(:fetch).twice
570
- enumerable._fetch(0)
571
- enumerable._fetch(enumerable.size - 1)
572
- end
573
-
574
- it "should map the relative index to an absolute index before calling fetch" do
575
- enumerable.shift(3)
576
- enumerable.stub(:shuffle_index) { |index| index + 2 }
577
- enumerable.should_receive(:fetch).with(5)
578
- enumerable._fetch(0)
579
- end
580
-
581
- end
582
-
583
- end
584
-
585
- context "#(private)element_or_array" do
586
-
587
- let(:block) { Proc.new { 1 } }
588
-
589
- context "on a non empty collection" do
590
- context "with no parameter" do
591
- it "returns a single element" do
592
- enumerable.element_or_array { block.call }.should eql(1)
593
- end
594
- end
595
-
596
- context "with a nil parameter" do
597
- it "returns a single element" do
598
- enumerable.element_or_array(nil, &block).should eql(1)
599
- end
600
- end
601
-
602
- context "with a non nil parameter" do
603
- it "returns an array of N elements" do
604
- enumerable.element_or_array(3, &block).should eql([1, 1, 1])
605
- end
606
-
607
- it "will not return more items than remain in the collection" do
608
- size = enumerable.size
609
- enumerable.element_or_array(size + 1, &block).should have(size).items
610
- end
611
- end
612
- end
613
-
614
- context "on an empty collection" do
615
- context "with no parameter" do
616
- it "returns nil" do
617
- emptied_enumerable.element_or_array(&:block).should be_nil
618
- end
619
- end
620
-
621
- context "with a parameter" do
622
- it "returns an empty array" do
623
- emptied_enumerable.element_or_array(3, &block).should eql([])
624
- end
625
- end
626
- end
627
-
628
- it "raises an exception if the parameter is negative" do
629
- expect { enumerable.element_or_array(-1, &block) }.to raise_error(ArgumentError, 'negative array size')
630
- end
631
-
632
- end
633
-
634
- context "#(private)full_cycle_increment" do
635
-
636
- it "must never return a value equal to the domain size" do
637
- (0...100).to_a.all? { |x| enumerable.full_cycle_increment(x) != x }.should be_true
638
- end
639
-
640
- end
641
-
1
+ require 'spec_helper'
2
+
3
+ describe HugeEnumerable do
4
+
5
+ let(:collection) { ('a'..'z').to_a }
6
+
7
+ subject(:enumerable) do
8
+ klass = Class.new(HugeEnumerable)
9
+ enum_collection = collection.sort
10
+ klass.send(:define_method, :collection_size) { enum_collection.size }
11
+ klass.send(:define_method, :fetch) { |x| enum_collection[x] }
12
+ klass.send(:public, :next_prime)
13
+ klass.send(:public, :_fetch)
14
+ klass.send(:public, :element_or_array)
15
+ klass.send(:public, :full_cycle_increment)
16
+ klass.new
17
+ end
18
+
19
+ subject(:emptied_enumerable) do
20
+ enumerable.tap do |enum|
21
+ enum.max_array_size = enum.size
22
+ enum.next_array
23
+ end
24
+ end
25
+
26
+ context ".new" do
27
+
28
+ context "with no arguments" do
29
+
30
+ it "defaults max_array_size to a Numeric" do
31
+ enumerable.max_array_size.should be_kind_of(Numeric)
32
+ end
33
+
34
+ it "defaults rng to #rand" do
35
+ enumerable.rng.should eq(enumerable.method(:rand))
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ context "#[]" do
43
+
44
+ context "with a positive index" do
45
+ it "returns the element from the beginning of the collection at the specified index" do
46
+ enumerable[3].should eql(collection[3])
47
+ end
48
+ end
49
+
50
+ context "with a negative index" do
51
+ it "returns the element from the end of the collection at the specified index" do
52
+ enumerable[-3].should eql(collection[-3])
53
+ end
54
+ end
55
+
56
+ context "with an out of bounds index" do
57
+ it "returns nil" do
58
+ enumerable[enumerable.size + 1].should be_nil
59
+ end
60
+ end
61
+
62
+ context "with a range" do
63
+ it "returns an array of elements corresponding to indexes within the range" do
64
+ size = collection.size + 1
65
+ test_range = (-size..size).to_a
66
+ ranges = test_range.product(test_range).map { |x| (x.first..x.last) }
67
+ arrays = ranges.map { |range| [enumerable[range], collection[range]] }
68
+ arrays.each { |x| x.first.should eq(x.last) }
69
+ end
70
+ end
71
+
72
+ context "with a length" do
73
+ it "returns an array of elements corresponding to starting with index and containing a maximum of length items" do
74
+ size = collection.size + 1
75
+ test_range = (-size..size).to_a
76
+ index_lengths = test_range.product(test_range).map { |x| [x.first, x.last] }
77
+ arrays = index_lengths.map do |idx_len|
78
+ index = idx_len.first
79
+ length = idx_len.last
80
+ [enumerable[index, length], collection[index, length]]
81
+ end
82
+ arrays.each { |x| x.first.should eq(x.last) }
83
+ end
84
+ end
85
+
86
+
87
+ it "relays to #_fetch for index mapping" do
88
+ enumerable.should_receive(:_fetch).at_least(:once)
89
+ enumerable[0]
90
+ end
91
+
92
+ end
93
+
94
+ context "#each" do
95
+
96
+ it "yields #max_array_size items" do
97
+ items = []
98
+ total_items = collection.size / 5
99
+ enumerable.max_array_size = total_items
100
+ enumerable.each { |x| items << x }
101
+ items.size.should eql(total_items)
102
+ end
103
+
104
+ it "yields only the remaining items if there are fewer than #max_array_size" do
105
+ items = []
106
+ enumerable.max_array_size = collection.size + 1
107
+ enumerable.each { |x| items << x }
108
+ items.size.should eql(collection.size)
109
+ end
110
+
111
+ it "relays to #_fetch for index mapping" do
112
+ enumerable.should_receive(:_fetch).at_least(:once)
113
+ enumerable.each {}
114
+ end
115
+
116
+ end
117
+
118
+ context "#combination" do
119
+
120
+ context "with no block" do
121
+
122
+ it "returns a new HugeCombination" do
123
+ enumerable.combination(2).should be_instance_of(HugeCombination)
124
+ end
125
+
126
+ end
127
+
128
+ context "with a block" do
129
+
130
+ # This should really be calls the block from the combination since there is testing duplication here.
131
+ # However, knowing where the block is called from would need stack investigation or similar.
132
+ it "calls the block for each combination element" do
133
+ combo = enumerable.combination(2)
134
+ enumerable.max_array_size = combo.size # Hrm. Perhaps having to do this is a reason to have it call collection_each
135
+ index = 0
136
+ enumerable.combination(2) do |x|
137
+ x.should eql(combo[index])
138
+ index += 1
139
+ end
140
+ index.should eql(combo.size)
141
+ end
142
+
143
+ it "returns self" do
144
+ enumerable.combination(2) {}.should equal(enumerable)
145
+ end
146
+
147
+ end
148
+
149
+ it "uses self a dup of itself as the enumerable" do
150
+ dupped_enumerable = enumerable.dup
151
+ enumerable.stub(:dup).and_return(dupped_enumerable)
152
+ HugeCombination.should_receive(:new).with(dupped_enumerable, 2, enumerable.max_array_size, nil)
153
+ enumerable.combination(2)
154
+ end
155
+
156
+ it "uses the same max array size" do
157
+ enumerable.combination(2).max_array_size.should eql(enumerable.max_array_size)
158
+ end
159
+
160
+ it "uses the same random number generator" do
161
+ enumerable.rng = Proc.new { 1 }
162
+ enumerable.combination(2).rng.should eq(enumerable.rng)
163
+ end
164
+
165
+ it "calls reset! on the dup" do
166
+ HugeEnumerable.send(:public, :reset!)
167
+ dupped_enumerable = enumerable.dup
168
+ enumerable.stub(:dup).and_return(dupped_enumerable)
169
+ dupped_enumerable.should_receive(:reset!).and_call_original
170
+ enumerable.combination(2)
171
+ end
172
+
173
+ end
174
+
175
+
176
+ context "#max_array_size" do
177
+
178
+ context "not explicitly set" do
179
+
180
+ it "defaults to DEFAULT_MAX_ARRAY_SIZE if smaller than #collection_size" do
181
+ enumerable.stub(:collection_size).and_return(HugeEnumerable::DEFAULT_MAX_ARRAY_SIZE + 1)
182
+ enumerable.max_array_size.should eql(HugeEnumerable::DEFAULT_MAX_ARRAY_SIZE)
183
+ end
184
+
185
+ it "defaults to #collection_size if smaller than DEFAULT_MAX_ARRAY_SIZE" do
186
+ enumerable.stub(:collection_size).and_return(HugeEnumerable::DEFAULT_MAX_ARRAY_SIZE - 1)
187
+ enumerable.max_array_size.should eql(HugeEnumerable::DEFAULT_MAX_ARRAY_SIZE - 1)
188
+ end
189
+
190
+ end
191
+
192
+ end
193
+
194
+ context "#next_array" do
195
+
196
+ it "advances to the next array in the collection" do
197
+ size = collection.size / 5
198
+ enumerable.max_array_size = size
199
+ enumerable.next_array.should eql(collection[size...size*2])
200
+ end
201
+
202
+ it "changes #size" do
203
+ size = collection.size / 5
204
+ enumerable.max_array_size = size
205
+ enumerable.next_array
206
+ enumerable.size.should eql(collection.size - size)
207
+ end
208
+
209
+ end
210
+
211
+ context "#empty?" do
212
+
213
+ it "returns true if the collection has been entirely emptied by #pop, #shift, or #next_array" do
214
+ enumerable.max_array_size = collection.size
215
+ enumerable.next_array
216
+ enumerable.empty?.should be_true
217
+ end
218
+
219
+ it "returns false if the collection has been not entirely emptied by #pop, #shift, or #next_array" do
220
+ enumerable.max_array_size = collection.size - 1
221
+ enumerable.next_array
222
+ enumerable.empty?.should be_false
223
+ end
224
+
225
+ end
226
+
227
+ context "#permutation" do
228
+
229
+ context "with no block" do
230
+
231
+ it "returns a new HugePermutation" do
232
+ enumerable.permutation(2).should be_instance_of(HugePermutation)
233
+ end
234
+
235
+ end
236
+
237
+ context "with a block" do
238
+
239
+ # This should really be calls the block from the combination since there is testing duplication here.
240
+ # However, knowing where the block is called from would need stack investigation or similar.
241
+ it "calls the block for each permutation element" do
242
+ perm = enumerable.permutation(2)
243
+ enumerable.max_array_size = perm.size # Hrm. Perhaps having to do this is a reason to have it call collection_each
244
+ index = 0
245
+ enumerable.permutation(2) do |x|
246
+ x.should eql(perm[index])
247
+ index += 1
248
+ end
249
+ index.should eql(perm.size)
250
+ end
251
+
252
+ it "returns self" do
253
+ enumerable.permutation(2) {}.should equal(enumerable)
254
+ end
255
+
256
+ end
257
+
258
+ it "uses self a dup of itself as the enumerable" do
259
+ dupped_enumerable = enumerable.dup
260
+ enumerable.stub(:dup).and_return(dupped_enumerable)
261
+ HugePermutation.should_receive(:new).with(dupped_enumerable, 2, enumerable.max_array_size, nil)
262
+ enumerable.permutation(2)
263
+ end
264
+
265
+ it "uses the same max array size" do
266
+ enumerable.permutation(2).max_array_size.should eql(enumerable.max_array_size)
267
+ end
268
+
269
+ it "uses the same random number generator" do
270
+ enumerable.rng = Proc.new { 1 }
271
+ enumerable.permutation(2).rng.should eq(enumerable.rng)
272
+ end
273
+
274
+ it "calls reset! on the dup" do
275
+ HugeEnumerable.send(:public, :reset!)
276
+ dupped_enumerable = enumerable.dup
277
+ enumerable.stub(:dup).and_return(dupped_enumerable)
278
+ dupped_enumerable.should_receive(:reset!).and_call_original
279
+ enumerable.permutation(2)
280
+ end
281
+
282
+ end
283
+
284
+
285
+ context "#pop" do
286
+
287
+ context "on a non empty collection" do
288
+ context "with no parameter" do
289
+ it "returns the next element from the end of the collection" do
290
+ enumerable.pop.should eql(collection.pop)
291
+ end
292
+ end
293
+
294
+ context "with a parameter" do
295
+ it "returns an array of the next N elements from the end of the collection" do
296
+ enumerable.pop(3).should eql(collection.pop(3))
297
+ end
298
+ end
299
+
300
+ it "removes the elements from the end of the collection" do
301
+ enumerable.pop
302
+ enumerable.pop(3)
303
+ enumerable.to_a.should eql(collection[0..-5])
304
+ end
305
+
306
+ it "changes #size" do
307
+ enumerable.pop
308
+ enumerable.pop(3)
309
+ enumerable.size.should eql(collection.size - 4)
310
+ end
311
+
312
+ it "does not harm the original collection" do
313
+ original = collection.dup
314
+ enumerable.pop
315
+ enumerable.pop(3)
316
+ collection.should eql(original)
317
+ end
318
+
319
+ end
320
+
321
+ it "depends on #(private)element_or_array" do
322
+ enumerable.should_receive(:element_or_array).twice.and_call_original
323
+ enumerable.pop
324
+ enumerable.pop(3)
325
+ end
326
+
327
+ end
328
+
329
+ context "#product" do
330
+
331
+ context "with no block" do
332
+
333
+ it "returns a new HugeProduct" do
334
+ enumerable.product([]).should be_instance_of(HugeProduct)
335
+ end
336
+
337
+ end
338
+
339
+ context "with a block" do
340
+
341
+ # This should really be calls the block from the combination since there is testing duplication here.
342
+ # However, knowing where the block is called from would need stack investigation or similar.
343
+ it "calls the block for each product element" do
344
+ other_enumerable = [1, 2, 3]
345
+ prod = enumerable.product(other_enumerable)
346
+ enumerable.max_array_size = prod.size # Hrm. Perhaps having to do this is a reason to have it call collection_each
347
+ index = 0
348
+ enumerable.product(other_enumerable) do |x|
349
+ x.should eql(prod[index])
350
+ index += 1
351
+ end
352
+ index.should eql(prod.size)
353
+ end
354
+
355
+ it "returns self" do
356
+ enumerable.product([]) {}.should equal(enumerable)
357
+ end
358
+
359
+ end
360
+
361
+ it "uses self a dup of itself as the enumerable" do
362
+ dupped_enumerable = enumerable.dup
363
+ enumerable.stub(:dup).and_return(dupped_enumerable)
364
+ other_enumerable = []
365
+ HugeProduct.should_receive(:new).with(dupped_enumerable, other_enumerable, enumerable.max_array_size, nil)
366
+ enumerable.product(other_enumerable)
367
+ end
368
+
369
+ it "calls dup on the other enumerable if it is a HugeEnumerable" do
370
+ other_enumerable = HugeCollection.new([])
371
+ other_enumerable.should_receive(:dup).and_call_original
372
+ enumerable.product(other_enumerable)
373
+ end
374
+
375
+ it "calls reset on the other enumerable if it is a HugeEnumerable" do
376
+ HugeEnumerable.send(:public, :reset!)
377
+ other_enumerable = HugeCollection.new([])
378
+ dupped_enumerable = other_enumerable.dup
379
+ other_enumerable.stub(:dup).and_return(dupped_enumerable)
380
+ dupped_enumerable.should_receive(:reset!).and_call_original
381
+ enumerable.product(other_enumerable)
382
+ end
383
+
384
+ it "does not call dup on the other enumerable if it is a HugeEnumerable" do
385
+ other_enumerable = []
386
+ other_enumerable.should_not_receive(:dup)
387
+ enumerable.product(other_enumerable)
388
+ end
389
+
390
+ it "uses the same max array size" do
391
+ enumerable.product([]).max_array_size.should eql(enumerable.max_array_size)
392
+ end
393
+
394
+ it "uses the same random number generator" do
395
+ enumerable.rng = Proc.new { 1 }
396
+ enumerable.product([]).rng.should eq(enumerable.rng)
397
+ end
398
+
399
+ it "calls reset! on the dup" do
400
+ HugeEnumerable.send(:public, :reset!)
401
+ dupped_enumerable = enumerable.dup
402
+ enumerable.stub(:dup).and_return(dupped_enumerable)
403
+ dupped_enumerable.should_receive(:reset!).and_call_original
404
+ enumerable.product([])
405
+ end
406
+
407
+ end
408
+
409
+ context "#sample" do
410
+
411
+ context "on an non empty collection" do
412
+ context "with no arguments" do
413
+ it "returns a single element from the collection" do
414
+ collection.include?(enumerable.sample).should be_true
415
+ end
416
+ end
417
+
418
+ context "with size argument" do
419
+ it "returns N elements from the collection" do
420
+ samples = enumerable.sample(3)
421
+ samples.should have(3).items
422
+ samples.all? { |item| collection.include?(item) }.should be_true
423
+ end
424
+ end
425
+
426
+ it "returns elements from the collection in a pseudo random pattern" do
427
+ enumerable.sample(enumerable.size).should_not eq(collection)
428
+ end
429
+
430
+ it "visits each element exactly once before repeating" do
431
+ samples = []
432
+ enumerable.size.times { samples << enumerable.sample }
433
+ samples.uniq.should have(collection.size).items
434
+ end
435
+
436
+ it "does not reorder the original collection" do
437
+ original = collection.dup
438
+ enumerable.sample
439
+ enumerable.sample(3)
440
+ collection.should eql(original)
441
+ end
442
+
443
+ end
444
+
445
+ it "depends on #(private)element_or_array" do
446
+ enumerable.should_receive(:element_or_array).twice.and_call_original
447
+ enumerable.sample
448
+ enumerable.sample(3)
449
+ end
450
+
451
+ end
452
+
453
+ context "#shift" do
454
+
455
+ context "on a non empty collection" do
456
+ context "with no parameter" do
457
+ it "returns the next element from the beginning of the collection" do
458
+ enumerable.shift.should eql(collection.shift)
459
+ end
460
+ end
461
+
462
+ context "with a parameter" do
463
+ it "returns an array of the next N elements from the beginning of the collection" do
464
+ enumerable.shift(3).should eql(collection.shift(3))
465
+ end
466
+ end
467
+
468
+ it "removes the elements from the beginning of the collection" do
469
+ enumerable.shift
470
+ enumerable.shift(3)
471
+ enumerable.to_a.should eql(collection[4..-1])
472
+ end
473
+
474
+ it "changes #size" do
475
+ enumerable.shift
476
+ enumerable.shift(3)
477
+ enumerable.size.should eql(collection.size - 4)
478
+ end
479
+
480
+ it "does not harm the original collection" do
481
+ original = collection.dup
482
+ enumerable.shift
483
+ enumerable.shift(3)
484
+ collection.should eql(original)
485
+ end
486
+
487
+ end
488
+
489
+ it "depends on #(private)element_or_array" do
490
+ enumerable.should_receive(:element_or_array).twice.and_call_original
491
+ enumerable.shift
492
+ enumerable.shift(3)
493
+ end
494
+
495
+ end
496
+
497
+ context "#shuffle" do
498
+
499
+ it "returns a new HugeEnumerable" do
500
+ enumerable.shuffle.should_not equal(enumerable)
501
+ end
502
+
503
+ end
504
+
505
+ context "#shuffle!" do
506
+
507
+ it "randomly alters the order of the sequence" do
508
+ fake_random = Proc.new { |x| 2 % x }
509
+ enumerable.max_array_size = enumerable.size
510
+ original = enumerable.to_a
511
+ enumerable.shuffle!(fake_random)
512
+ shuffle1 = enumerable.to_a
513
+ fake_random = Proc.new { |x| 3 % x }
514
+ enumerable.shuffle!(fake_random)
515
+ shuffle2 = enumerable.to_a
516
+ original.should_not eq(shuffle1)
517
+ original.should_not eq(shuffle2)
518
+ shuffle1.should_not eq(shuffle2)
519
+ end
520
+
521
+ it "contains all of the original elements" do
522
+ enumerable.shuffle!.to_a.sort.should eq(collection)
523
+ end
524
+
525
+ it "does noy alter the original collection" do
526
+ original = collection.dup
527
+ enumerable.max_array_size = enumerable.size
528
+ enumerable.shuffle!
529
+ enumerable.to_a.should_not eq(original)
530
+ original.should eq(collection)
531
+ end
532
+
533
+ end
534
+
535
+ context "#size" do
536
+
537
+ it "returns the current size of the collection" do
538
+ enumerable.size.should eql(collection.size)
539
+ end
540
+
541
+ end
542
+
543
+ context "#(private)next_prime" do
544
+
545
+ it "should return the next prime following any integer" do
546
+ primes = Prime.first(100)
547
+ x = 0
548
+ until primes.empty?
549
+ enumerable.next_prime(x).should eq(primes.first)
550
+ x += 1
551
+ primes.shift if x.prime?
552
+ end
553
+ end
554
+
555
+ end
556
+
557
+ context "#(private)_fetch" do
558
+
559
+ context "with an index outside the range of (sequence_start..sequence_end)" do
560
+ it "should never relay to fetch" do
561
+ enumerable.should_not_receive(:fetch)
562
+ enumerable._fetch(-1 * enumerable.size - 1)
563
+ enumerable._fetch(enumerable.size)
564
+ end
565
+ end
566
+
567
+ context "with an index inside the range of (sequence_start..sequence_end)" do
568
+ it "should relay to fetch" do
569
+ enumerable.should_receive(:fetch).twice
570
+ enumerable._fetch(0)
571
+ enumerable._fetch(enumerable.size - 1)
572
+ end
573
+
574
+ it "should map the relative index to an absolute index before calling fetch" do
575
+ enumerable.shift(3)
576
+ enumerable.stub(:shuffle_index) { |index| index + 2 }
577
+ enumerable.should_receive(:fetch).with(5)
578
+ enumerable._fetch(0)
579
+ end
580
+
581
+ end
582
+
583
+ end
584
+
585
+ context "#(private)element_or_array" do
586
+
587
+ let(:block) { Proc.new { 1 } }
588
+
589
+ context "on a non empty collection" do
590
+ context "with no parameter" do
591
+ it "returns a single element" do
592
+ enumerable.element_or_array { block.call }.should eql(1)
593
+ end
594
+ end
595
+
596
+ context "with a nil parameter" do
597
+ it "returns a single element" do
598
+ enumerable.element_or_array(nil, &block).should eql(1)
599
+ end
600
+ end
601
+
602
+ context "with a non nil parameter" do
603
+ it "returns an array of N elements" do
604
+ enumerable.element_or_array(3, &block).should eql([1, 1, 1])
605
+ end
606
+
607
+ it "will not return more items than remain in the collection" do
608
+ size = enumerable.size
609
+ enumerable.element_or_array(size + 1, &block).should have(size).items
610
+ end
611
+ end
612
+ end
613
+
614
+ context "on an empty collection" do
615
+ context "with no parameter" do
616
+ it "returns nil" do
617
+ emptied_enumerable.element_or_array(&:block).should be_nil
618
+ end
619
+ end
620
+
621
+ context "with a parameter" do
622
+ it "returns an empty array" do
623
+ emptied_enumerable.element_or_array(3, &block).should eql([])
624
+ end
625
+ end
626
+ end
627
+
628
+ it "raises an exception if the parameter is negative" do
629
+ expect { enumerable.element_or_array(-1, &block) }.to raise_error(ArgumentError, 'negative array size')
630
+ end
631
+
632
+ end
633
+
634
+ context "#(private)full_cycle_increment" do
635
+
636
+ it "must never return a value equal to the domain size" do
637
+ (0...100).to_a.all? { |x| enumerable.full_cycle_increment(x) != x }.should be_true
638
+ end
639
+
640
+ end
641
+
642
642
  end