Skip to content

Commit 94d6420

Browse files
committed
Fixed RandomNumber class, random number generation not working correctly for boundary values (Int.MinValue, Int.MaxValue)
1 parent 907e82d commit 94d6420

File tree

2 files changed

+168
-14
lines changed

2 files changed

+168
-14
lines changed

src/Faker/RandomNumber.cs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,27 @@
33

44
namespace Faker
55
{
6+
// constrain our values to between our min and max
7+
// https://stackoverflow.com/a/3057867/86411
8+
//
69
public static class RandomNumber
710
{
811
private static readonly RandomNumberGenerator Rnd = RandomNumberGenerator.Create();
912

1013
private static int Next(this RandomNumberGenerator generator, int min, int max)
1114
{
12-
var bytes = new byte[sizeof(int)]; // 4 bytes
15+
var bytes = new byte[sizeof(int)];
16+
generator.GetNonZeroBytes(bytes);
17+
var val = BitConverter.ToUInt32(bytes, 0);
18+
var result = ((val - min) % (max - min + 1) + (max - min) + 1) % (max - min + 1) + min;
19+
return (int)result;
20+
}
21+
22+
private static long Next(this RandomNumberGenerator generator, long min, long max)
23+
{
24+
var bytes = new byte[sizeof(long)];
1325
generator.GetNonZeroBytes(bytes);
1426
var val = BitConverter.ToInt32(bytes, 0);
15-
// constrain our values to between our min and max
16-
// https://stackoverflow.com/a/3057867/86411C:\Work\GitHub\faker-cs\src\Faker\RandomNumber.cs
1727
var result = ((val - min) % (max - min + 1) + (max - min) + 1) % (max - min + 1) + min;
1828
return result;
1929
}
@@ -28,9 +38,19 @@ public static int Next(int max)
2838
return Rnd.Next(0, max);
2939
}
3040

41+
public static long Next(long max)
42+
{
43+
return Rnd.Next(0, max);
44+
}
45+
3146
public static int Next(int min, int max)
3247
{
3348
return Rnd.Next(min, max);
3449
}
50+
51+
public static long Next(long min, long max)
52+
{
53+
return Rnd.Next(min, max);
54+
}
3555
}
3656
}

tests/Faker.Tests/RandomNumberFixture.cs

Lines changed: 145 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using System;
2-
using System.Linq;
2+
using System.Collections.Generic;
33
using NUnit.Framework;
44

55
namespace Faker.Tests
@@ -11,21 +11,45 @@ public class RandomNumberFixture
1111
[TestCase(6, "6 sided die")] // 0 .. 6
1212
[TestCase(9, "Random single digit")] // 0 ... 9
1313
[TestCase(20, "D20")] // 0 ... 20 The signature dice of the dungeons and dragons
14-
public void Should_Yield_All_ValuesWithinRange(int maxValue, string testName)
14+
public void Should_Generate_All_Positive_Values_From_Zero(int max, string testName)
1515
{
16-
var maxExclusiveLimit = maxValue + 1;
16+
if (max < 0)
17+
throw new ArgumentException(@"Value must be greater than zero!", nameof(max));
18+
1719
Console.WriteLine($@"RandomNumber.Next [{testName}]");
18-
var results = Enumerable.Range(0, maxExclusiveLimit)
19-
.ToDictionary(k => k, k => false);
20+
21+
var results = new Dictionary<int, int>();
2022
do
2123
{
22-
var randValue = RandomNumber.Next(0, maxExclusiveLimit);
23-
results[randValue] = true;
24-
Console.WriteLine($@"RandomNumber.Next(0,{maxExclusiveLimit})=[{randValue}]");
25-
} while (!results.All(j => j.Value));
24+
var value = RandomNumber.Next(0, max);
25+
26+
if (value < 0)
27+
throw new Exception($"Value is less than zero, value=[{value}]");
2628

27-
Assert.IsTrue(results.Select(z => z.Value)
28-
.All(y => y));
29+
results[value] = value;
30+
31+
Console.WriteLine($@"RandomNumber.Next(0,{max})=[{value}]");
32+
} while (results.Count != max - 0);
33+
}
34+
35+
[TestCase(-1000, "Negative 1000 Numbers")]
36+
public void Should_Generate_All_Negative_Values_To_Zero(int min, string testName)
37+
{
38+
if (min > 0)
39+
throw new ArgumentException(@"Value must be less than zero!", nameof(min));
40+
41+
var results = new Dictionary<int, int>();
42+
do
43+
{
44+
var value = RandomNumber.Next(min,0);
45+
46+
if (value > 0)
47+
throw new Exception($"Value is greater than zero, value=[{value}]");
48+
49+
results[value] = value;
50+
51+
Console.WriteLine($@"RandomNumber.Next({min}, 0)=[{value}]");
52+
} while (results.Count != min * -1);
2953
}
3054

3155
[Test]
@@ -70,5 +94,115 @@ public void Should_Generate_Minimum()
7094

7195
Assert.IsTrue(result);
7296
}
97+
98+
[Test]
99+
public void Should_Generate_To_Int32_Max_Value()
100+
{
101+
var results = new Dictionary<int, int>();
102+
var min = int.MaxValue - 1000;
103+
var max = int.MaxValue;
104+
105+
do
106+
{
107+
var value = RandomNumber.Next(min, max);
108+
109+
if (value < min)
110+
throw new Exception($"Value is less than min, value=[{value}], min=[{min}]");
111+
112+
results[value] = value;
113+
114+
Console.WriteLine($@"RandomNumber.Next({min},{max})=[{value}]");
115+
} while (results.Count != 1000);
116+
117+
Assert.That(results.ContainsKey(min));
118+
Assert.That(results.ContainsKey(max));
119+
}
120+
121+
[Test]
122+
public void Should_Generate_To_Int64_Max_Value()
123+
{
124+
var results = new Dictionary<long, long>();
125+
var min = long.MaxValue - 1000;
126+
var max = long.MaxValue;
127+
128+
do
129+
{
130+
var value = RandomNumber.Next(min, max);
131+
132+
if (value < min)
133+
throw new Exception($"Value is less than min, value=[{value}], min=[{min}]");
134+
135+
results[value] = value;
136+
137+
Console.WriteLine($@"RandomNumber.Next({min},{max})=[{value}]");
138+
} while (results.Count != 1000);
139+
140+
Assert.That(results.ContainsKey(min));
141+
Assert.That(results.ContainsKey(max));
142+
}
143+
144+
[Test]
145+
public void Should_Generate_To_Int32_Min_Value()
146+
{
147+
var results = new Dictionary<int, int>();
148+
var min = int.MinValue;
149+
var max = min + 1000;
150+
151+
do
152+
{
153+
var value = RandomNumber.Next(min, max);
154+
155+
if (value > max)
156+
throw new Exception($"Value is greater than max, value=[{value}], max=[{max}]");
157+
158+
results[value] = value;
159+
160+
Console.WriteLine($@"RandomNumber.Next({min},{max})=[{value}]");
161+
} while (results.Count != 1000);
162+
163+
Assert.That(results.ContainsKey(min));
164+
Assert.That(results.ContainsKey(max));
165+
}
166+
167+
[Test]
168+
public void Should_Generate_To_Int64_Min_Value()
169+
{
170+
var results = new Dictionary<long, long>();
171+
var min = long.MinValue;
172+
var max = min + 1000;
173+
174+
do
175+
{
176+
var value = RandomNumber.Next(min, max);
177+
178+
if (value > max)
179+
throw new Exception($"Value is greater than max, value=[{value}], max=[{max}]");
180+
181+
results[value] = value;
182+
183+
Console.WriteLine($@"RandomNumber.Next({min},{max})=[{value}]");
184+
} while (results.Count != 1000);
185+
186+
Assert.That(results.ContainsKey(min));
187+
Assert.That(results.ContainsKey(max));
188+
}
189+
190+
[Test]
191+
public void Should_Generate_Values_Greater_Than_Zero()
192+
{
193+
var result = true;
194+
for (var i = 0; i < 1000; i++)
195+
{
196+
var number = RandomNumber.Next();
197+
Console.WriteLine(number);
198+
if (number < 0)
199+
{
200+
result = false;
201+
break;
202+
}
203+
}
204+
205+
Assert.IsTrue(result);
206+
}
73207
}
74208
}

0 commit comments

Comments
 (0)