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.
- checksums.yaml +5 -5
- data/.github/workflows/rspec.yml +23 -0
- data/.rubocop.yml +18 -0
- data/Gemfile +11 -1
- data/README.md +0 -5
- data/Rakefile +7 -5
- data/lib/pool_of_entropy/core_prng.rb +149 -143
- data/lib/pool_of_entropy/version.rb +3 -1
- data/lib/pool_of_entropy.rb +55 -59
- data/pool_of_entropy.gemspec +15 -16
- data/spec/pool_of_entropy/core_prng_spec.rb +386 -0
- data/spec/pool_of_entropy_spec.rb +264 -303
- data/spec/spec_helper.rb +49 -3
- metadata +12 -88
- data/.travis.yml +0 -11
- data/spec/core_prng_spec.rb +0 -543
|
@@ -1,384 +1,345 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'spec_helper'
|
|
2
4
|
|
|
3
5
|
describe PoolOfEntropy do
|
|
4
|
-
describe
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
pool =
|
|
9
|
-
expect(
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
expect
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
144
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
172
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
367
|
-
|
|
313
|
+
it "removes a current 'all' modifier" do
|
|
314
|
+
expect(clear_all_results).to all be true
|
|
315
|
+
end
|
|
368
316
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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
|