Skip to content

Instantly share code, notes, and snippets.

@matthewd
Created March 14, 2010 06:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matthewd/331810 to your computer and use it in GitHub Desktop.
Save matthewd/331810 to your computer and use it in GitHub Desktop.
From 7b4f0646096edb78b4ca48ba028576fded3369d0 Mon Sep 17 00:00:00 2001
From: Matthew Draper <matthew@trebex.net>
Date: Sun, 14 Mar 2010 15:28:39 +1030
Subject: [PATCH] Improve & extend Kernel.{rand,srand} specs.
---
spec/ruby/core/kernel/rand_spec.rb | 101 +++++++++++++++++++++++++++++++----
spec/ruby/core/kernel/srand_spec.rb | 35 +++++++++++--
2 files changed, 121 insertions(+), 15 deletions(-)
diff --git a/spec/ruby/core/kernel/rand_spec.rb b/spec/ruby/core/kernel/rand_spec.rb
index 581c11f..19b48d1 100644
--- a/spec/ruby/core/kernel/rand_spec.rb
+++ b/spec/ruby/core/kernel/rand_spec.rb
@@ -5,34 +5,113 @@ describe "Kernel.rand" do
it "is a private method" do
Kernel.should have_private_instance_method(:rand)
end
-
- it "returns a random float less than 1 if no max argument is passed" do
- rand.kind_of?(Float).should == true
+
+ it "returns a float if no argument is passed" do
+ rand.should be_kind_of(Float)
+ end
+
+ it "returns an integer for an integer argument" do
+ rand(77).should be_kind_of(Integer)
+ end
+
+ it "returns an integer for a float argument greater than 1" do
+ rand(1.3).should be_kind_of(Integer)
+ end
+
+ it "returns a float for an argument between -1 and 1" do
+ rand(-0.999).should be_kind_of(Float)
+ rand(-0.01).should be_kind_of(Float)
+ rand(0).should be_kind_of(Float)
+ rand(0.01).should be_kind_of(Float)
+ rand(0.999).should be_kind_of(Float)
+ end
+
+ it "ignores the sign of the argument" do
+ [0, 1, 2, 3].should include(rand(-4))
+ end
+
+ it "can return a bignum when given a large enough limit" do
+ # Probability of random failure:
+ # 64-bit: 1 in 10**42
+ # 32-bit: 1 in 10**139
+
+ values = []
+ 10.times do
+ values << rand(0x12345678901234567890)
+ end
+ values.max.should be_kind_of(Bignum)
end
- it "returns a random int or bigint less than the argument for an integer argument" do
- rand(77).kind_of?(Integer).should == true
+ it "can return a large bignum when given a large enough limit" do
+ # Probability of random failure: 1 in 10**80
+
+ values = []
+ 10.times do
+ values << rand(0x123456789012345678901234567890)
+ end
+ values.max.should > (0x123456789012345678901234567890 / 100_000_000)
end
- it "returns a random integer less than the argument casted to an int for a float argument greater than 1" do
- rand(1.3).kind_of?(Integer).should == true
+ it "can return a fixnum even when given a bignum limit" do
+ # Probability of random failure: 1 in 10**60
+
+ small_bignum = 1
+ small_bignum *= 2 until small_bignum.is_a? Bignum
+ small_bignum *= 2 # for good measure
+
+ values = []
+ 200.times do
+ values << rand(small_bignum)
+ end
+ values.min.should be_kind_of(Fixnum)
+ values.max.should be_kind_of(Bignum)
end
- it "returns a random float less than 1 for float arguments less than 1" do
- rand(0.01).kind_of?(Float).should == true
+ it "produces a vaguely even distribution" do
+ # Like several other rand specs, this one is based on probabilities;
+ # with a sufficiently high quality RNG, this spec should fail
+ # occassionally.
+
+ # Probability of random failure: 1 in 10**20
+
+ counts = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+ 5000.times do |i|
+ n = rand(10)
+ counts[n] += 1
+ end
+
+ # Under a perfect distribution (which obviously wouldn't be random),
+ # all values in 'counts' are 500.
+ # We ignore the highest and lowest value, then apply a +/-30%
+ # tolerance on the remaining 8 -- this is a trade-off between
+ # strictness, and likelihood of random failure. For comparison, a
+ # +/-20% tolerance would increase probability of random failure to
+ # about 1 in 10**10, and +/-10% would make the probability
+ # approximately 1 in 100.
+
+ counts.sort!
+ counts[1].should > 350
+ counts[-2].should < 650
end
it "never returns a value greater or equal to 1.0 with no arguments" do
1000.times do
- (rand < 1.0).should == true
+ (0...1.0).should include(rand)
end
end
it "never returns a value greater or equal to any passed in max argument" do
1000.times do
- (rand(100) < 100).should == true
+ (0...100).to_a.should include(rand(100))
end
end
+
+ it "calls to_i on its argument" do
+ l = mock('limit')
+ l.should_receive(:to_i).and_return 7
+
+ rand l
+ end
end
describe "Kernel#rand" do
diff --git a/spec/ruby/core/kernel/srand_spec.rb b/spec/ruby/core/kernel/srand_spec.rb
index dd4d288..33f99f5 100644
--- a/spec/ruby/core/kernel/srand_spec.rb
+++ b/spec/ruby/core/kernel/srand_spec.rb
@@ -5,13 +5,13 @@ describe "Kernel.srand" do
it "is a private method" do
Kernel.should have_private_instance_method(:srand)
end
-
- it "srand should return the previous seed value" do
+
+ it "returns the previous seed value" do
srand(10)
srand(20).should == 10
end
- it "srand should seed the RNG correctly and repeatably" do
+ it "seeds the RNG correctly and repeatably" do
srand(10)
x = rand
srand(10)
@@ -23,9 +23,36 @@ describe "Kernel.srand" do
srand.should_not == 0
end
- it "calls #to_i on number" do
+ it "accepts and uses a seed of 0" do
+ srand(0)
+ srand.should == 0
+ end
+
+ it "accepts a negative seed" do
+ srand(-17)
+ srand.should == -17
+ end
+
+ it "accepts a Bignum as a seed" do
+ srand(0x12345678901234567890)
+ srand.should == 0x12345678901234567890
+ end
+
+ it "calls #to_int on seed" do
srand(3.8)
srand.should == 3
+
+ s = mock('seed')
+ s.should_receive(:to_int).and_return 0
+ srand(s)
+ end
+
+ it "raises a TypeError when passed nil" do
+ lambda { srand(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ lambda { srand("7") }.should raise_error(TypeError)
end
end
--
1.7.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment