pool_of_entropy 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,384 +1,345 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe PoolOfEntropy do
4
- describe "class methods" do
6
+ describe '.new' do
7
+ it 'instantiates a default object' do
8
+ pool = described_class.new
9
+ expect(pool).to be_a described_class
10
+ end
5
11
 
6
- describe "#new" do
7
- it "should instantiate a default object" do
8
- pool = PoolOfEntropy.new
9
- expect( pool ).to be_a PoolOfEntropy
12
+ it 'allows setting number of blocks in pool' do
13
+ [1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233].each do |size|
14
+ pool = described_class.new(size: size)
15
+ expect(pool.rand).to be_a_float_in(0.0...1.0)
10
16
  end
17
+ end
11
18
 
12
- it "should allow setting number of blocks in pool" do
13
- [1,3,5,8,13,21,34,55,89,144,233].each do |s|
14
- pool = PoolOfEntropy.new( :size => 12 )
15
- expect( pool ).to be_a PoolOfEntropy
16
- num = pool.rand()
17
- expect( num ).to be_a Float
18
- expect( num ).to be >= 0.0
19
- expect( num ).to be < 1.0
20
- end
19
+ [[:size, 12], '', :size].each do |parameter|
20
+ it "rejects non-hash parameter #{parameter.inspect}" do
21
+ expect { described_class.new(parameter) }.to raise_error TypeError
21
22
  end
23
+ end
22
24
 
23
- it "should fail when param is not a hash" do
24
- expect { PoolOfEntropy.new( [:size,12] ) }.to raise_error TypeError
25
- expect { PoolOfEntropy.new( '' ) }.to raise_error TypeError
26
- expect { PoolOfEntropy.new( :size ) }.to raise_error TypeError
25
+ { -12 => ArgumentError, -1 => ArgumentError, 0 => ArgumentError,
26
+ 257 => ArgumentError, 1000 => ArgumentError, '' => ArgumentError,
27
+ { foo: 'bar' } => TypeError }.each do |size, error|
28
+ it "rejects size #{size.inspect}" do
29
+ expect { described_class.new(size: size) }.to raise_error error
27
30
  end
31
+ end
28
32
 
29
- it "should fail with incorrect :size param" do
30
- expect { PoolOfEntropy.new( :size => -12 ) }.to raise_error ArgumentError
31
- expect { PoolOfEntropy.new( :size => -1 ) }.to raise_error ArgumentError
32
- expect { PoolOfEntropy.new( :size => 0 ) }.to raise_error ArgumentError
33
- expect { PoolOfEntropy.new( :size => 257 ) }.to raise_error ArgumentError
34
- expect { PoolOfEntropy.new( :size => 1000 ) }.to raise_error ArgumentError
35
- expect { PoolOfEntropy.new( :size => '' ) }.to raise_error ArgumentError
36
- expect { PoolOfEntropy.new( :size => { :foo => 'bar' } ) }.to raise_error TypeError
37
- end
33
+ it 'defaults to unpredicatble internal state' do
34
+ pool = described_class.new
35
+ expect((10..20).map { |x| pool.rand(x) }).not_to eql [8, 3, 0, 12, 11, 2, 4, 8, 1, 11, 18]
36
+ end
38
37
 
39
- it "should default to unpredicatble internal state" do
40
- pool = PoolOfEntropy.new()
41
- expect( pool ).to be_a PoolOfEntropy
42
- expect( (10..20).map { |x| pool.rand(x) } ).to_not eql [8, 3, 0, 12, 11, 2, 4, 8, 1, 11, 18]
43
- end
38
+ it 'accepts { :blank => false } as explicit statement of default' do
39
+ pool = described_class.new(blank: false)
40
+ expect((10..20).map { |x| pool.rand(x) }).not_to eql [8, 3, 0, 12, 11, 2, 4, 8, 1, 11, 18]
41
+ end
44
42
 
45
- it "should accept { :blank => false } as explicit statement of default" do
46
- pool = PoolOfEntropy.new( :blank => false )
47
- expect( pool ).to be_a PoolOfEntropy
48
- expect( (10..20).map { |x| pool.rand(x) } ).to_not eql [8, 3, 0, 12, 11, 2, 4, 8, 1, 11, 18]
49
- end
43
+ it 'allows an initial blank internal state' do
44
+ pool = described_class.new(blank: true)
45
+ expect((10..20).map { |x| pool.rand(x) }).to eql [8, 3, 0, 12, 11, 2, 4, 8, 1, 11, 18]
46
+ end
50
47
 
51
- it "should allow an initial blank internal state" do
52
- pool = PoolOfEntropy.new( :blank => true )
53
- expect( pool ).to be_a PoolOfEntropy
54
- expect( (10..20).map { |x| pool.rand(x) } ).to eql [8, 3, 0, 12, 11, 2, 4, 8, 1, 11, 18]
55
- end
48
+ it 'accepts a single-element :seeds array' do
49
+ pool = described_class.new(blank: true, seeds: ['foo'])
50
+ expect((10..20).map { |x| pool.rand(x) }).to eql [9, 1, 3, 7, 8, 12, 14, 5, 11, 5, 6]
51
+ end
56
52
 
57
- it "should accept and use :seeds array" do
58
- pool = PoolOfEntropy.new( :blank => true, :seeds => ['foo'] )
59
- expect( pool ).to be_a PoolOfEntropy
60
- expect( (10..20).map { |x| pool.rand(x) } ).to eql [9, 1, 3, 7, 8, 12, 14, 5, 11, 5, 6]
53
+ it 'accepts a multiple-element :seeds array' do
54
+ pool = described_class.new(blank: true, seeds: %w[foo bar])
55
+ expect((10..20).map { |x| pool.rand(x) }).to eql [8, 1, 5, 8, 2, 7, 11, 8, 8, 13, 14]
56
+ end
61
57
 
62
- pool = PoolOfEntropy.new( :blank => true, :seeds => ['foo', 'bar'] )
63
- expect( pool ).to be_a PoolOfEntropy
64
- expect( (10..20).map { |x| pool.rand(x) } ).to eql [8, 1, 5, 8, 2, 7, 11, 8, 8, 13, 14]
58
+ [-12, { seeds: [2] }, 'more_seeds'].each do |seeds|
59
+ it "rejects non-array seeds #{seeds.inspect}" do
60
+ expect { described_class.new(seeds: seeds) }.to raise_error TypeError
65
61
  end
62
+ end
63
+ end
66
64
 
67
- it "should fail if :seeds param is not an array" do
68
- expect { PoolOfEntropy.new( :seeds => -12 ) }.to raise_error TypeError
69
- expect { PoolOfEntropy.new( :seeds => { :seeds => [2] } ) }.to raise_error TypeError
70
- expect { PoolOfEntropy.new( :seeds => 'more_seeds' ) }.to raise_error TypeError
71
- end
65
+ describe '#rand' do
66
+ it 'returns nil for a descending Range' do
67
+ expect(described_class.new.rand(10..5)).to be_nil
72
68
  end
73
69
  end
74
70
 
75
- describe "instance methods" do
76
- pool_types = [
77
- [
78
- 'default instance',
79
- PoolOfEntropy.new
80
- ],
81
- [
82
- 'instance with 2KB pool size',
83
- PoolOfEntropy.new( :size => 32 )
84
- ],
85
- [
86
- 'instance with 1KB pool size, blank start',
87
- PoolOfEntropy.new( :size => 16, :blank => true )
88
- ],
89
- [
90
- 'instance with default size, seeded',
91
- PoolOfEntropy.new( :blank => true, :seeds => ['of change'] )
92
- ],
93
- [
94
- 'instance cloned from another instance',
95
- PoolOfEntropy.new( :size => 3 ).clone
96
- ],
97
- [
98
- 'instance with maximum size, 16KB pool',
99
- PoolOfEntropy.new( :size => 256, :blank => true, :seeds => ['dgeq','dsfsf','dsafsaf'] )
100
- ],
71
+ pool_types = [
72
+ [
73
+ 'default instance',
74
+ described_class.new
75
+ ],
76
+ [
77
+ 'instance with 2KB pool size',
78
+ described_class.new(size: 32)
79
+ ],
80
+ [
81
+ 'instance with 1KB pool size, blank start',
82
+ described_class.new(size: 16, blank: true)
83
+ ],
84
+ [
85
+ 'instance with default size, seeded',
86
+ described_class.new(blank: true, seeds: ['of change'])
87
+ ],
88
+ [
89
+ 'instance cloned from another instance',
90
+ described_class.new(size: 3).clone
91
+ ],
92
+ [
93
+ 'instance with maximum size, 16KB pool',
94
+ described_class.new(size: 256, blank: true, seeds: %w[dgeq dsfsf dsafsaf])
101
95
  ]
96
+ ]
102
97
 
103
- # NB "probability" and "randomness" tests in the following block are very light, just
104
- # intended to capture high-level failures in logic. See DIEHARDER_TEST.md for thorough
105
- # checks on statistical randomness of PoolOfEntropy::CorePRNG
106
- pool_types.each do |pool_name, pool|
98
+ # NB "probability" and "randomness" tests in the following block are very light, just
99
+ # intended to capture high-level failures in logic. See DIEHARDER_TEST.md for thorough
100
+ # checks on statistical randomness of PoolOfEntropy::CorePRNG
101
+ pool_types.each do |pool_name, pool|
102
+ context "with #{pool_name}" do
103
+ subject(:pool_copy) { pool.clone }
107
104
 
108
- context "using #{pool_name}" do
109
- before do
110
- pool.clear_all_modifiers
105
+ before do
106
+ pool.clear_all_modifiers
107
+ end
108
+
109
+ describe '#clone' do
110
+ it 'returns a deep copy with same state and modifiers' do
111
+ pool.modify_next(*(0..4).map { |i| "foo#{i}" })
112
+ pool.modify_all('bar')
113
+ expect(outputs_match?(pool_copy, pool, count: 100)).to be true
111
114
  end
115
+ end
112
116
 
113
- describe "#clone" do
114
- it "should return a deep copy with same state and modifiers" do
115
- pool.modify_next( *((0..4).map {|i| "foo" + i.to_s} ) )
116
- pool.modify_all( 'bar' )
117
+ describe '#rand' do
118
+ it 'calls CorePRNG#read_bytes when no argument is given' do
119
+ core_prng = pool.instance_variable_get(:@core_prng)
120
+ allow(core_prng).to receive(:read_bytes).and_return("\x1e\xfe" * 8)
121
+ 20.times { expect(pool.rand).to be 0.12106507972838931 }
122
+ end
117
123
 
118
- pool_copy = pool.clone
119
- 100.times do
120
- expect( pool_copy.rand() ).to eql pool.rand()
121
- end
124
+ it 'returns a Float between 0.0 and 1.0 when no argument is given' do
125
+ 100.times do
126
+ expect(pool.rand).to be_a_float_in(0.0...1.0)
122
127
  end
123
128
  end
124
129
 
125
- describe "#rand" do
126
-
127
- context "with no param" do
128
- it "should call PoolOfEntropy::CorePRNG::read_bytes internally" do
129
- allow_any_instance_of( PoolOfEntropy::CorePRNG).
130
- to receive( :read_bytes ).and_return( "\x1e\xfe" * 8 )
131
- 20.times { expect( pool.rand ).to eql 0.12106507972838931 }
132
- end
130
+ it 'returns a different Float each time with no argument (with high probability)' do
131
+ expect(Set[*(1..100).map { pool.rand }].size).to be 100
132
+ end
133
133
 
134
- it "should return a Float between 0.0 and 1.0" do
135
- 100.times do
136
- num = pool.rand
137
- expect( num ).to be_a Float
138
- expect( num ).to be >= 0.0
139
- expect( num ).to be < 1.0
140
- end
141
- end
134
+ it 'calls CorePRNG#read_bytes with an Integer argument' do
135
+ core_prng = pool.instance_variable_get(:@core_prng)
136
+ allow(core_prng).to receive(:read_bytes).and_return("\x1e\xfe" * 8)
137
+ 20.times { expect(pool.rand(20)).to be 2 }
138
+ end
142
139
 
143
- it "should return a different Float each time (with high probability) " do
144
- expect( Set[ *(1..100).map{ pool.rand } ].size ).to eql 100
140
+ it 'returns an Integer between 0 and x (excluding x)' do
141
+ [1, 2, 3, 5, 8, 13, 21, 34, 55, 89].each do |x|
142
+ 100.times do
143
+ expect(pool.rand(x)).to be_an_integer_in(0...x)
145
144
  end
146
145
  end
146
+ end
147
147
 
148
- context "with an Integer param" do
149
- it "should call PoolOfEntropy::CorePRNG::read_bytes internally" do
150
- allow_any_instance_of( PoolOfEntropy::CorePRNG).
151
- to receive( :read_bytes ).and_return( "\x1e\xfe" * 8 )
152
- 20.times { expect( pool.rand(20) ).to eql 2 }
153
- end
148
+ # This is a very weak test of randomness, see DIEHARDER_TEST.md
149
+ it 'selects Integer values without obvious bias' do
150
+ expect(Set[*(1..500).map { pool.rand(20) }].size).to be 20
151
+ end
154
152
 
155
- it "should return an Integer between 0 and x (excluding x)" do
156
- [ 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ].each do |x|
157
- 100.times do
158
- num = pool.rand( x )
159
- expect( num ).to be_a Fixnum
160
- expect( num ).to be >= 0
161
- expect( num ).to be < x
162
- end
163
- end
164
- end
153
+ it 'returns a different Integer each time (with high probability for large x)' do
154
+ expect(Set[*(1..100).map { pool.rand(2**64) }].size).to be 100
155
+ end
165
156
 
166
- # This is a very weak test of randomness, see DIEHARDER_TEST.md
167
- it "should select values without obvious bias" do
168
- expect( Set[ *(1..200).map{ pool.rand( 20 ) } ].size ).to eql 20
169
- end
157
+ it 'calls CorePRNG#read_bytes with a Range argument' do
158
+ core_prng = pool.instance_variable_get(:@core_prng)
159
+ allow(core_prng).to receive(:read_bytes).and_return("\x1e\xfe" * 8)
160
+ 20.times { expect(pool.rand(7..28)).to be 9 }
161
+ end
170
162
 
171
- it "should return a different Integer each time (with high probability for large x) " do
172
- expect( Set[ *(1..100).map{ pool.rand( 2**64 ) } ].size ).to eql 100
163
+ it 'returns an Integer that is a member of the Range argument' do
164
+ [1..2, 2..5, 3..8, 5..13, 21..34, 55..89].each do |r|
165
+ 100.times do
166
+ expect(pool.rand(r)).to be_an_integer_in(r)
173
167
  end
174
168
  end
169
+ end
175
170
 
176
- context "with a Range param" do
177
- it "should call PoolOfEntropy::CorePRNG::read_bytes internally" do
178
- allow_any_instance_of( PoolOfEntropy::CorePRNG).
179
- to receive( :read_bytes ).and_return( "\x1e\xfe" * 8 )
180
- 20.times { expect( pool.rand( 7..28 ) ).to eql 9 }
181
- end
182
-
183
- it "should return an Integer that is a member of the range" do
184
- [ 1..2, 2..5, 3..8, 5..13, 21..34, 55..89 ].each do |r|
185
- 100.times do
186
- num = pool.rand( r )
187
- expect( num ).to be_a Fixnum
188
- expect( num ).to be >= r.min
189
- expect( num ).to be <= r.max
190
- end
191
- end
192
- end
193
-
194
- it "should return a different Integer each time (with high probability for large range) " do
195
- expect( Set[ *(1..100).map{ pool.rand( 100000000000..200000000000 ) } ].size ).to eql 100
196
- end
171
+ it 'returns a different Integer each time (with high probability for a large Range)' do
172
+ expect(Set[*(1..100).map { pool.rand(100_000_000_000..200_000_000_000) }].size).to be 100
173
+ end
174
+ end
197
175
 
176
+ describe '#modify_next' do
177
+ let(:bulk_queue_results) do
178
+ repeat_results do
179
+ modifiers = (0..5).map { SecureRandom.hex }
180
+ pool_copy.modify_next(*modifiers)
181
+ modifiers.map { pool_copy.rand == pool.modify_next(_1).rand } << outputs_match?(pool_copy, pool, count: 1)
198
182
  end
199
-
200
183
  end
201
184
 
202
-
203
- describe "#modify_next" do
204
- it "changes output value from next call to #rand" do
205
- pool_copy = pool.clone
206
- 100.times do
207
- modifier = SecureRandom.hex
208
- expect( pool_copy.modify_next( modifier ).rand ).to_not eql pool.rand
185
+ let(:incremental_queue_results) do
186
+ repeat_results do
187
+ modifiers = (0..5).map { SecureRandom.hex }
188
+ matches = modifiers.map do |modifier|
189
+ pool_copy.modify_next(modifier)
190
+ pool_copy.rand == pool.modify_next(modifier).rand
209
191
  end
192
+ matches << outputs_match?(pool_copy, pool, count: 1)
210
193
  end
194
+ end
211
195
 
212
- it "changes output value consistently" do
213
- pool_copy = pool.clone
214
- 100.times do
215
- modifier = SecureRandom.hex
216
- expect( pool_copy.modify_next( modifier ).rand ).to eql pool.modify_next( modifier ).rand
217
- end
196
+ let(:nil_modifier_results) do
197
+ repeat_results do
198
+ pool_copy.modify_next('hello', nil, 'goodbye')
199
+ [pool_copy.rand != pool.rand, pool_copy.rand == pool.rand, pool_copy.rand != pool.rand,
200
+ pool_copy.rand == pool.rand, pool_copy.rand == pool.rand]
218
201
  end
202
+ end
219
203
 
220
- it "changes next output value but not future ones" do
221
- pool_copy = pool.clone
222
- 100.times do
223
- modifier = SecureRandom.hex
224
- expect( pool_copy.modify_next( modifier ).rand ).to_not eql pool.rand
225
- expect( pool_copy.rand ).to eql pool.rand
226
- end
204
+ it 'changes output value from next call to #rand' do
205
+ pool_copy = pool.clone
206
+ 100.times do
207
+ modifier = SecureRandom.hex
208
+ expect(pool_copy.modify_next(modifier).rand).not_to eql pool.rand
227
209
  end
210
+ end
228
211
 
229
- it "can create a 'queue' of modifiers, used in turn" do
230
- pool_copy = pool.clone
231
- 10.times do
232
- modifiers = (0..5).map { SecureRandom.hex }
233
-
234
- # Syntax for all-at-once
235
- pool_copy.modify_next( *modifiers )
236
- modifiers.each do |modifier|
237
- expect( pool_copy.rand ).to eql pool.modify_next( modifier ).rand
238
- end
239
- # Assert we're back in sync without modifiers
240
- expect( pool_copy.rand ).to eql pool.rand
241
-
242
- # Adding to queue one-at-a-time
243
- modifiers.each do |modifier|
244
- pool_copy.modify_next( modifier )
245
- end
246
- modifiers.each do |modifier|
247
- expect( pool_copy.rand ).to eql pool.modify_next( modifier ).rand
248
- end
249
- # Assert we're back in sync without modifiers
250
- expect( pool_copy.rand ).to eql pool.rand
251
- end
212
+ it 'changes output value consistently' do
213
+ pool_copy = pool.clone
214
+ 100.times do
215
+ modifier = SecureRandom.hex
216
+ expect(pool_copy.modify_next(modifier).rand).to eql pool.modify_next(modifier).rand
252
217
  end
218
+ end
253
219
 
254
- it "treats a nil modifier as 'do not modify'" do
255
- pool_copy = pool.clone
256
- 10.times do
257
- pool_copy.modify_next( 'hello', nil, 'goodbye' )
258
- expect( pool_copy.rand ).to_not eql pool.rand
259
- expect( pool_copy.rand ).to eql pool.rand
260
- expect( pool_copy.rand ).to_not eql pool.rand
261
- expect( pool_copy.rand ).to eql pool.rand
262
- expect( pool_copy.rand ).to eql pool.rand
263
- end
264
- end
265
- end # modify_next
266
-
267
- describe "#modify_all" do
268
- it "changes output value from future calls to #rand" do
269
- pool_copy = pool.clone
270
- 10.times do
271
- modifier = SecureRandom.hex
272
- expect( pool_copy.modify_all( modifier ).rand ).to_not eql pool.rand
273
- 10.times do
274
- expect( pool_copy.rand ).to_not eql pool.rand
275
- end
276
- end
220
+ it 'changes next output value but not future ones' do
221
+ results = repeat_results(100) do
222
+ modifier = SecureRandom.hex
223
+ [pool_copy.modify_next(modifier).rand != pool.rand, pool_copy.rand == pool.rand]
277
224
  end
225
+ expect(results).to all be true
226
+ end
278
227
 
279
- it "changes output value consistently" do
280
- pool_copy = pool.clone
281
- 10.times do
282
- modifier = SecureRandom.hex
283
- expect( pool_copy.modify_all( modifier ).rand ).to eql pool.modify_all( modifier ).rand
284
- 10.times do
285
- expect( pool_copy.rand ).to eql pool.rand
286
- end
287
- end
228
+ it 'consumes modifiers queued in bulk' do
229
+ expect(bulk_queue_results).to all be true
230
+ end
231
+
232
+ it 'consumes modifiers queued incrementally' do
233
+ expect(incremental_queue_results).to all be true
234
+ end
235
+
236
+ it "treats a nil modifier as 'do not modify'" do
237
+ expect(nil_modifier_results).to all be true
238
+ end
239
+ end
240
+
241
+ describe '#modify_all' do
242
+ let(:reset_modifier_results) do
243
+ repeat_results do
244
+ modifier = SecureRandom.hex
245
+ changed = [pool_copy.modify_all(modifier).rand != pool.rand, pool_copy.rand != pool.rand]
246
+ pool_copy.modify_all(nil)
247
+ changed << outputs_match?(pool_copy, pool)
288
248
  end
249
+ end
289
250
 
290
- it "changes output value consistently with #modify_next" do
291
- pool_copy = pool.clone
292
- 100.times do
293
- modifier = SecureRandom.hex
294
- expect( pool_copy.modify_all( modifier ).rand ).to eql pool.modify_next( modifier ).rand
295
- end
251
+ it 'changes output value from future calls to #rand' do
252
+ results = repeat_results do
253
+ modifier = SecureRandom.hex
254
+ [pool_copy.modify_all(modifier).rand != pool.rand, outputs_differ?(pool_copy, pool)]
296
255
  end
256
+ expect(results).to all be true
257
+ end
297
258
 
298
- it "can be reset wih nil modifier" do
299
- pool_copy = pool.clone
300
- 10.times do
301
- modifier = SecureRandom.hex
302
- expect( pool_copy.modify_all( modifier ).rand ).to_not eql pool.rand
303
- expect( pool_copy.rand ).to_not eql pool.rand
304
- pool_copy.modify_all( nil )
305
- 10.times do
306
- expect( pool_copy.rand ).to eql pool.rand
307
- end
308
- end
259
+ it 'changes output value consistently' do
260
+ results = repeat_results do
261
+ modifier = SecureRandom.hex
262
+ [pool_copy.modify_all(modifier).rand == pool.modify_all(modifier).rand, outputs_match?(pool_copy, pool)]
309
263
  end
264
+ expect(results).to all be true
265
+ end
310
266
 
267
+ it 'changes output value consistently with #modify_next' do
268
+ pool_copy = pool.clone
269
+ 100.times do
270
+ modifier = SecureRandom.hex
271
+ expect(pool_copy.modify_all(modifier).rand).to eql pool.modify_next(modifier).rand
272
+ end
311
273
  end
312
274
 
313
- describe "#clear_all_modifiers" do
275
+ it 'can be reset wih nil modifier' do
276
+ expect(reset_modifier_results).to all be true
277
+ end
278
+ end
314
279
 
315
- it "removes a queue of 'next' modifiers" do
316
- pool_copy = pool.clone
317
- 10.times do
318
- modifiers = (0..5).map { SecureRandom.hex }
319
- pool_copy.modify_next( *modifiers )
320
- expect( pool_copy.rand ).to_not eql pool.rand
321
- pool_copy.clear_all_modifiers
322
- 10.times do
323
- expect( pool_copy.rand ).to eql pool.rand
324
- end
325
- end
280
+ describe '#clear_all_modifiers' do
281
+ let(:clear_next_results) do
282
+ repeat_results do
283
+ pool_copy.modify_next(*(0..5).map { SecureRandom.hex })
284
+ changed = pool_copy.rand != pool.rand
285
+ pool_copy.clear_all_modifiers
286
+ [changed, outputs_match?(pool_copy, pool)]
326
287
  end
288
+ end
327
289
 
328
- it "removes a current 'all' modifier" do
329
- pool_copy = pool.clone
330
- 10.times do
331
- modifier = SecureRandom.hex
332
- expect( pool_copy.modify_all( modifier ).rand ).to_not eql pool.rand
333
- expect( pool_copy.rand ).to_not eql pool.rand
334
- pool_copy.clear_all_modifiers
335
- 10.times do
336
- expect( pool_copy.rand ).to eql pool.rand
337
- end
338
- end
290
+ let(:clear_all_results) do
291
+ repeat_results do
292
+ modifier = SecureRandom.hex
293
+ changed = [pool_copy.modify_all(modifier).rand != pool.rand, pool_copy.rand != pool.rand]
294
+ pool_copy.clear_all_modifiers
295
+ changed << outputs_match?(pool_copy, pool)
339
296
  end
297
+ end
340
298
 
341
- it "removes both 'all' and 'next' modifiers in one go" do
342
- pool_copy = pool.clone
343
- 10.times do
344
- modifier = SecureRandom.hex
345
- pool_copy.modify_next( *(0..5).map { SecureRandom.hex } )
346
- expect( pool_copy.modify_all( modifier ).rand ).to_not eql pool.rand
347
- expect( pool_copy.rand ).to_not eql pool.rand
348
- pool_copy.clear_all_modifiers
349
- 10.times do
350
- expect( pool_copy.rand ).to eql pool.rand
351
- end
352
- end
299
+ let(:clear_combined_results) do
300
+ repeat_results do
301
+ modifier = SecureRandom.hex
302
+ pool_copy.modify_next(*(0..5).map { SecureRandom.hex })
303
+ changed = [pool_copy.modify_all(modifier).rand != pool.rand, pool_copy.rand != pool.rand]
304
+ pool_copy.clear_all_modifiers
305
+ changed << outputs_match?(pool_copy, pool)
353
306
  end
354
307
  end
355
308
 
356
- describe "#add_to_pool" do
357
-
358
- it "alters all future output values" do
359
- pool_copy = pool.clone
360
- pool_copy.add_to_pool( 'Some user data!' )
361
- 100.times do
362
- expect( pool_copy.rand ).to_not eql pool.rand
363
- end
364
- end
309
+ it "removes a queue of 'next' modifiers" do
310
+ expect(clear_next_results).to all be true
311
+ end
365
312
 
366
- it "alters all future output values consistently" do
367
- pool_copy = pool.clone
313
+ it "removes a current 'all' modifier" do
314
+ expect(clear_all_results).to all be true
315
+ end
368
316
 
369
- 10.times do
370
- user_data = SecureRandom.hex
371
- pool_copy.add_to_pool( user_data )
372
- pool.add_to_pool( user_data )
317
+ it "removes both 'all' and 'next' modifiers in one go" do
318
+ expect(clear_combined_results).to all be true
319
+ end
320
+ end
373
321
 
374
- 10.times do
375
- expect( pool_copy.rand ).to eql pool.rand
376
- end
377
- end
322
+ describe '#add_to_pool' do
323
+ let(:add_to_pool_results) do
324
+ repeat_results do
325
+ user_data = SecureRandom.hex
326
+ pool_copy.add_to_pool(user_data)
327
+ pool.add_to_pool(user_data)
328
+ outputs_match?(pool_copy, pool)
378
329
  end
330
+ end
379
331
 
332
+ it 'alters all future output values' do
333
+ pool_copy = pool.clone
334
+ pool_copy.add_to_pool('Some user data!')
335
+ 100.times do
336
+ expect(pool_copy.rand).not_to eql pool.rand
337
+ end
380
338
  end
381
339
 
340
+ it 'alters all future output values consistently' do
341
+ expect(add_to_pool_results).to all be true
342
+ end
382
343
  end
383
344
  end
384
345
  end