pool_of_entropy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,362 @@
1
+ require 'spec_helper'
2
+
3
+ describe PoolOfEntropy do
4
+ describe "class methods" do
5
+
6
+ describe "#new" do
7
+ it "should instantiate a default object" do
8
+ pool = PoolOfEntropy.new
9
+ pool.should be_a PoolOfEntropy
10
+ end
11
+
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
+ pool.should be_a PoolOfEntropy
16
+ num = pool.rand()
17
+ num.should be_a Float
18
+ num.should >= 0.0
19
+ num.should < 1.0
20
+ end
21
+ end
22
+
23
+ it "should fail with incorrect :size param" do
24
+ expect { PoolOfEntropy.new( :size => -12 ) }.to raise_error ArgumentError
25
+ expect { PoolOfEntropy.new( :size => -1 ) }.to raise_error ArgumentError
26
+ expect { PoolOfEntropy.new( :size => 0 ) }.to raise_error ArgumentError
27
+ expect { PoolOfEntropy.new( :size => 257 ) }.to raise_error ArgumentError
28
+ expect { PoolOfEntropy.new( :size => 1000 ) }.to raise_error ArgumentError
29
+ expect { PoolOfEntropy.new( :size => '' ) }.to raise_error ArgumentError
30
+ expect { PoolOfEntropy.new( :size => { :foo => 'bar' } ) }.to raise_error TypeError
31
+ end
32
+
33
+ it "should default to unpredicatble internal state" do
34
+ pool = PoolOfEntropy.new()
35
+ pool.should be_a PoolOfEntropy
36
+ (10..20).map { |x| pool.rand(x) }.should_not == [8, 3, 0, 12, 11, 2, 4, 8, 1, 11, 18]
37
+ end
38
+
39
+ it "should accept { :blank => false } as explicit statement of default" do
40
+ pool = PoolOfEntropy.new( :blank => false )
41
+ pool.should be_a PoolOfEntropy
42
+ (10..20).map { |x| pool.rand(x) }.should_not == [8, 3, 0, 12, 11, 2, 4, 8, 1, 11, 18]
43
+ end
44
+
45
+ it "should allow an initial blank internal state" do
46
+ pool = PoolOfEntropy.new( :blank => true )
47
+ pool.should be_a PoolOfEntropy
48
+ (10..20).map { |x| pool.rand(x) }.should == [8, 3, 0, 12, 11, 2, 4, 8, 1, 11, 18]
49
+ end
50
+
51
+ it "should accept and use :seed array" do
52
+ pool = PoolOfEntropy.new( :blank => true, :seeds => ['foo'] )
53
+ pool.should be_a PoolOfEntropy
54
+ (10..20).map { |x| pool.rand(x) }.should == [9, 1, 3, 7, 8, 12, 14, 5, 11, 5, 6]
55
+
56
+ pool = PoolOfEntropy.new( :blank => true, :seeds => ['foo', 'bar'] )
57
+ pool.should be_a PoolOfEntropy
58
+ (10..20).map { |x| pool.rand(x) }.should == [8, 1, 5, 8, 2, 7, 11, 8, 8, 13, 14]
59
+ end
60
+ end
61
+ end
62
+
63
+
64
+ describe "instance methods" do
65
+ pool_types = [
66
+ [
67
+ 'default instance',
68
+ PoolOfEntropy.new
69
+ ],
70
+ [
71
+ 'instance with 2KB pool size',
72
+ PoolOfEntropy.new( :size => 32 )
73
+ ],
74
+ [
75
+ 'instance with 1KB pool size, blank start',
76
+ PoolOfEntropy.new( :size => 16, :blank => true )
77
+ ],
78
+ [
79
+ 'instance with default size, seeded',
80
+ PoolOfEntropy.new( :blank => true, :seeds => ['of change'] )
81
+ ],
82
+ [
83
+ 'instance cloned from another instance',
84
+ PoolOfEntropy.new( :size => 3 ).clone
85
+ ],
86
+ [
87
+ 'instance with maximum size, 16KB pool',
88
+ PoolOfEntropy.new( :size => 256, :blank => true, :seeds => ['dgeq','dsfsf','dsafsaf'] )
89
+ ],
90
+ ]
91
+
92
+ # NB "probability" and "randomness" tests in the following block are very light, just
93
+ # intended to capture high-level failures in logic. See DIEHARDER_TEST.md for thorough
94
+ # checks on statistical randomness of PoolOfEntropy::CorePRNG
95
+ pool_types.each do |pool_name, pool|
96
+
97
+ context "using #{pool_name}" do
98
+ before do
99
+ pool.clear_all_modifiers
100
+ end
101
+
102
+ describe "#clone" do
103
+ it "should return a deep copy with same state and modifiers" do
104
+ pool.modify_next( *((0..4).map {|i| "foo" + i.to_s} ) )
105
+ pool.modify_all( 'bar' )
106
+
107
+ pool_copy = pool.clone
108
+ 100.times do
109
+ pool_copy.rand().should == pool.rand()
110
+ end
111
+ end
112
+ end
113
+
114
+ describe "#rand" do
115
+
116
+ context "with no param" do
117
+ it "should call PoolOfEntropy::CorePRNG::read_bytes internally" do
118
+ allow_any_instance_of( PoolOfEntropy::CorePRNG).
119
+ to receive( :read_bytes ).and_return( "\x1e\xfe" * 8 )
120
+ 20.times { pool.rand.should == 0.12106507972838931 }
121
+ end
122
+
123
+ it "should return a Float between 0.0 and 1.0" do
124
+ 100.times do
125
+ num = pool.rand
126
+ num.should be_a Float
127
+ num.should >= 0.0
128
+ num.should < 1.0
129
+ end
130
+ end
131
+
132
+ it "should return a different Float each time (with high probability) " do
133
+ Set[ *(1..100).map{ pool.rand } ].size.should == 100
134
+ end
135
+ end
136
+
137
+ context "with an Integer param" do
138
+ it "should call PoolOfEntropy::CorePRNG::read_bytes internally" do
139
+ allow_any_instance_of( PoolOfEntropy::CorePRNG).
140
+ to receive( :read_bytes ).and_return( "\x1e\xfe" * 8 )
141
+ 20.times { pool.rand(20).should == 2 }
142
+ end
143
+
144
+ it "should return an Integer between 0 and x (excluding x)" do
145
+ [ 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ].each do |x|
146
+ 100.times do
147
+ num = pool.rand( x )
148
+ num.should be_a Fixnum
149
+ num.should >= 0
150
+ num.should < x
151
+ end
152
+ end
153
+ end
154
+
155
+ # This is a very weak test of randomness, see DIEHARDER_TEST.md
156
+ it "should select values without obvious bias" do
157
+ Set[ *(1..200).map{ pool.rand( 20 ) } ].size.should == 20
158
+ end
159
+
160
+ it "should return a different Integer each time (with high probability for large x) " do
161
+ Set[ *(1..100).map{ pool.rand( 2**64 ) } ].size.should == 100
162
+ end
163
+ end
164
+
165
+ context "with a Range param" do
166
+ it "should call PoolOfEntropy::CorePRNG::read_bytes internally" do
167
+ allow_any_instance_of( PoolOfEntropy::CorePRNG).
168
+ to receive( :read_bytes ).and_return( "\x1e\xfe" * 8 )
169
+ 20.times { pool.rand( 7..28 ).should == 9 }
170
+ end
171
+
172
+ it "should return an Integer that is a member of the range" do
173
+ [ 1..2, 2..5, 3..8, 5..13, 21..34, 55..89 ].each do |r|
174
+ 100.times do
175
+ num = pool.rand( r )
176
+ num.should be_a Fixnum
177
+ num.should >= r.min
178
+ num.should <= r.max
179
+ end
180
+ end
181
+ end
182
+
183
+ it "should return a different Integer each time (with high probability for large range) " do
184
+ Set[ *(1..100).map{ pool.rand( 100000000000..200000000000 ) } ].size.should == 100
185
+ end
186
+
187
+ end
188
+
189
+ end
190
+
191
+
192
+ describe "#modify_next" do
193
+ it "changes output value from next call to #rand" do
194
+ pool_copy = pool.clone
195
+ 100.times do
196
+ modifier = SecureRandom.hex
197
+ pool_copy.modify_next( modifier ).rand.should_not == pool.rand
198
+ end
199
+ end
200
+
201
+ it "changes output value consistently" do
202
+ pool_copy = pool.clone
203
+ 100.times do
204
+ modifier = SecureRandom.hex
205
+ pool_copy.modify_next( modifier ).rand.should == pool.modify_next( modifier ).rand
206
+ end
207
+ end
208
+
209
+ it "changes next output value but not future ones" do
210
+ pool_copy = pool.clone
211
+ 100.times do
212
+ modifier = SecureRandom.hex
213
+ pool_copy.modify_next( modifier ).rand.should_not == pool.rand
214
+ pool_copy.rand.should == pool.rand
215
+ end
216
+ end
217
+
218
+ it "can create a 'queue' of modifiers, used in turn" do
219
+ pool_copy = pool.clone
220
+ 10.times do
221
+ modifiers = (0..5).map { SecureRandom.hex }
222
+
223
+ # Syntax for all-at-once
224
+ pool_copy.modify_next( *modifiers )
225
+ modifiers.each do |modifier|
226
+ pool_copy.rand.should == pool.modify_next( modifier ).rand
227
+ end
228
+ # Assert we're back in sync without modifiers
229
+ pool_copy.rand.should == pool.rand
230
+
231
+ # Adding to queue one-at-a-time
232
+ modifiers.each do |modifier|
233
+ pool_copy.modify_next( modifier )
234
+ end
235
+ modifiers.each do |modifier|
236
+ pool_copy.rand.should == pool.modify_next( modifier ).rand
237
+ end
238
+ # Assert we're back in sync without modifiers
239
+ pool_copy.rand.should == pool.rand
240
+ end
241
+ end
242
+ end # modify_next
243
+
244
+ describe "#modify_all" do
245
+ it "changes output value from future calls to #rand" do
246
+ pool_copy = pool.clone
247
+ 10.times do
248
+ modifier = SecureRandom.hex
249
+ pool_copy.modify_all( modifier ).rand.should_not == pool.rand
250
+ 10.times do
251
+ pool_copy.rand.should_not == pool.rand
252
+ end
253
+ end
254
+ end
255
+
256
+ it "changes output value consistently" do
257
+ pool_copy = pool.clone
258
+ 10.times do
259
+ modifier = SecureRandom.hex
260
+ pool_copy.modify_all( modifier ).rand.should == pool.modify_all( modifier ).rand
261
+ 10.times do
262
+ pool_copy.rand.should == pool.rand
263
+ end
264
+ end
265
+ end
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
+ pool_copy.modify_all( modifier ).rand.should == pool.modify_next( modifier ).rand
272
+ end
273
+ end
274
+
275
+ it "can be reset wih nil modifier" do
276
+ pool_copy = pool.clone
277
+ 10.times do
278
+ modifier = SecureRandom.hex
279
+ pool_copy.modify_all( modifier ).rand.should_not == pool.rand
280
+ pool_copy.rand.should_not == pool.rand
281
+ pool_copy.modify_all( nil )
282
+ 10.times do
283
+ pool_copy.rand.should == pool.rand
284
+ end
285
+ end
286
+ end
287
+
288
+ end
289
+
290
+ describe "#clear_all_modifiers" do
291
+
292
+ it "removes a queue of 'next' modifiers" do
293
+ pool_copy = pool.clone
294
+ 10.times do
295
+ modifiers = (0..5).map { SecureRandom.hex }
296
+ pool_copy.modify_next( *modifiers )
297
+ pool_copy.rand.should_not == pool.rand
298
+ pool_copy.clear_all_modifiers
299
+ 10.times do
300
+ pool_copy.rand.should == pool.rand
301
+ end
302
+ end
303
+ end
304
+
305
+ it "removes a current 'all' modifier" do
306
+ pool_copy = pool.clone
307
+ 10.times do
308
+ modifier = SecureRandom.hex
309
+ pool_copy.modify_all( modifier ).rand.should_not == pool.rand
310
+ pool_copy.rand.should_not == pool.rand
311
+ pool_copy.clear_all_modifiers
312
+ 10.times do
313
+ pool_copy.rand.should == pool.rand
314
+ end
315
+ end
316
+ end
317
+
318
+ it "removes both 'all' and 'next' modifiers in one go" do
319
+ pool_copy = pool.clone
320
+ 10.times do
321
+ modifier = SecureRandom.hex
322
+ pool_copy.modify_next( *(0..5).map { SecureRandom.hex } )
323
+ pool_copy.modify_all( modifier ).rand.should_not == pool.rand
324
+ pool_copy.rand.should_not == pool.rand
325
+ pool_copy.clear_all_modifiers
326
+ 10.times do
327
+ pool_copy.rand.should == pool.rand
328
+ end
329
+ end
330
+ end
331
+ end
332
+
333
+ describe "#add_to_pool" do
334
+
335
+ it "alters all future output values" do
336
+ pool_copy = pool.clone
337
+ pool_copy.add_to_pool( 'Some user data!' )
338
+ 100.times do
339
+ pool_copy.rand.should_not == pool.rand
340
+ end
341
+ end
342
+
343
+ it "alters all future output values consistently" do
344
+ pool_copy = pool.clone
345
+
346
+ 10.times do
347
+ user_data = SecureRandom.hex
348
+ pool_copy.add_to_pool( user_data )
349
+ pool.add_to_pool( user_data )
350
+
351
+ 10.times do
352
+ pool_copy.rand.should == pool.rand
353
+ end
354
+ end
355
+ end
356
+
357
+ end
358
+
359
+ end
360
+ end
361
+ end
362
+ end
@@ -0,0 +1,7 @@
1
+ require 'coveralls'
2
+ require 'set'
3
+
4
+ Coveralls.wear!
5
+
6
+ require 'pool_of_entropy'
7
+
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pool_of_entropy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Neil Slater
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: yard
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.7.2
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.7.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 2.13.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 2.13.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 1.9.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 1.9.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: coveralls
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 0.6.7
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 0.6.7
83
+ description: PoolOfEntropy is a PRNG based on cryptographic secure PRNGs, intended
84
+ to bring back the feeling of 'personal luck' that some gamers feel when rolling
85
+ their own dice.
86
+ email:
87
+ - slobo777@gmail.com
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - ".gitignore"
93
+ - ".travis.yml"
94
+ - DIEHARDER_TEST.md
95
+ - Gemfile
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - lib/pool_of_entropy.rb
100
+ - lib/pool_of_entropy/core_prng.rb
101
+ - lib/pool_of_entropy/version.rb
102
+ - pool_of_entropy.gemspec
103
+ - spec/core_prng_spec.rb
104
+ - spec/pool_of_entropy_spec.rb
105
+ - spec/spec_helper.rb
106
+ homepage: https://github.com/neilslater/pool_of_entropy
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.2.2
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Random number generator with extra features for gamers.
130
+ test_files:
131
+ - spec/core_prng_spec.rb
132
+ - spec/pool_of_entropy_spec.rb
133
+ - spec/spec_helper.rb
134
+ has_rdoc: