diff --git a/Src/AutoFixture/RegularExpressionGenerator.cs b/Src/AutoFixture/RegularExpressionGenerator.cs
index 31346f9f9..a8cf4847e 100644
--- a/Src/AutoFixture/RegularExpressionGenerator.cs
+++ b/Src/AutoFixture/RegularExpressionGenerator.cs
@@ -10,6 +10,18 @@ namespace AutoFixture
///
public class RegularExpressionGenerator : ISpecimenBuilder
{
+ private readonly Random random;
+ private readonly object syncRoot;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public RegularExpressionGenerator()
+ {
+ this.random = new Random();
+ this.syncRoot = new object();
+ }
+
///
/// Creates a string that is guaranteed to match a RegularExpressionRequest.
///
@@ -28,16 +40,18 @@ public object Create(object request, ISpecimenContext context)
return new NoSpecimen();
}
- return GenerateRegularExpression(regularExpressionRequest);
+ return this.GenerateRegularExpression(regularExpressionRequest);
}
- private static object GenerateRegularExpression(RegularExpressionRequest request)
+ private object GenerateRegularExpression(RegularExpressionRequest request)
{
string pattern = request.Pattern;
try
{
- string regex = new Xeger(pattern).Generate();
+ // Use the Xeger constructor overload that that takes an instance of Random.
+ // Otherwise identically strings can be generated, if regex are generated within short time.
+ string regex = new Xeger(pattern, new Random(this.GenerateSeed())).Generate();
if (Regex.IsMatch(regex, pattern))
{
return regex;
@@ -54,5 +68,13 @@ private static object GenerateRegularExpression(RegularExpressionRequest request
return new NoSpecimen();
}
+
+ private int GenerateSeed()
+ {
+ lock (this.syncRoot)
+ {
+ return this.random.Next();
+ }
+ }
}
-}
+}
\ No newline at end of file
diff --git a/Src/AutoFixtureUnitTest/DataAnnotations/RegularExpressionValidatedType.cs b/Src/AutoFixtureUnitTest/DataAnnotations/RegularExpressionValidatedType.cs
index 394f665db..8d05bdff8 100644
--- a/Src/AutoFixtureUnitTest/DataAnnotations/RegularExpressionValidatedType.cs
+++ b/Src/AutoFixtureUnitTest/DataAnnotations/RegularExpressionValidatedType.cs
@@ -4,7 +4,7 @@ namespace AutoFixtureUnitTest.DataAnnotations
{
public class RegularExpressionValidatedType
{
- public const string Pattern = @"^[a-zA-Z''-'\s]{1,40}$";
+ public const string Pattern = @"^[a-zA-Z''-'\s]{20,40}$";
[RegularExpression(Pattern)]
public string Property { get; set; }
diff --git a/Src/AutoFixtureUnitTest/FixtureTest.cs b/Src/AutoFixtureUnitTest/FixtureTest.cs
index c8e972f5d..728cebf7a 100644
--- a/Src/AutoFixtureUnitTest/FixtureTest.cs
+++ b/Src/AutoFixtureUnitTest/FixtureTest.cs
@@ -1636,6 +1636,21 @@ public void CreateAnonymousWithRegularExpressionValidatedTypeReturnsCorrectResul
Assert.Matches(RegularExpressionValidatedType.Pattern, result.Property);
}
+ [Fact]
+ public void CreateManyAnonymousWithRegularExpressionValidatedTypeReturnsDifferentResults()
+ {
+ // This test exposes an issue with Xeger/Random.
+ // Xeger(pattern) internally creates an instance of Random with the default seed.
+ // This means that the RegularExpressionGenerator might create identical strings
+ // if called multiple times within a short time.
+
+ // Arrange
+ var fixture = new Fixture();
+ var result = fixture.CreateMany(10).Select(x => x.Property).ToArray();
+ // Assert
+ Assert.Equal(result.Distinct(), result);
+ }
+
[Fact]
public void CreateAnonymousWithStringLengthValidatedTypeReturnsCorrectResult()
{
diff --git a/Src/AutoFixtureUnitTest/RegularExpressionGeneratorTest.cs b/Src/AutoFixtureUnitTest/RegularExpressionGeneratorTest.cs
index 570461564..e0dff1f4d 100644
--- a/Src/AutoFixtureUnitTest/RegularExpressionGeneratorTest.cs
+++ b/Src/AutoFixtureUnitTest/RegularExpressionGeneratorTest.cs
@@ -1,4 +1,5 @@
-using System.Text.RegularExpressions;
+using System.Linq;
+using System.Text.RegularExpressions;
using AutoFixture;
using AutoFixture.Kernel;
using AutoFixtureUnitTest.Kernel;
@@ -103,6 +104,27 @@ public void CreateWithRegularExpressionRequestReturnsCorrectResult(string patter
Assert.True(Regex.IsMatch(result.ToString(), pattern), string.Format("result: {0}", result));
}
+ [Theory]
+ [InlineData(@"^[A-Z]{27}$")]
+ public void CreateMultipleWithRegularExpressionRequestReturnsDifferentResults(string pattern)
+ {
+ // This test exposes an issue with Xeger/Random.
+ // Xeger(pattern) internally creates an instance of Random with the default seed.
+ // This means that the RegularExpressionGenerator might create identical strings
+ // if called multiple times within a short time.
+
+ // Arrange
+ var sut = new RegularExpressionGenerator();
+ var request = new RegularExpressionRequest(pattern);
+ var dummyContext = new DelegatingSpecimenContext();
+
+ // Repeat a few times to make the test more robust.
+ // Use ToArray to iterate the IEnumerable at this point.
+ var result = Enumerable.Range(0, 10).Select(_ => sut.Create(request, dummyContext)).ToArray();
+
+ Assert.Equal(result.Distinct(), result);
+ }
+
[Theory]
[InlineData("[")]
[InlineData(@"(?\[Test\]|\[Foo\]|\[Bar\])?(?:-)?(?\[[()a-zA-Z0-9_\s]+\])?(?:-)?(?\[[a-zA-Z0-9_\s]+\])?(?:-)?(?\[[a-zA-Z0-9_\s]+\])?(?:-)?(?\[[a-zA-Z0-9_\s]+\])?")]