From 02f6b7c69a0d1ceb595d2d550e64558320f4c75f Mon Sep 17 00:00:00 2001 From: Magnus Mikkelsen Date: Tue, 30 Jun 2020 08:16:45 +0200 Subject: [PATCH] Proposed fix for #1183 - creating multiple regex within short time might yield identical stringsr #1183 (+1 squashed commits) Squashed commits: [e8e3387d] Proposed fix for #1183 - creating multiple regex within short time might yield identical strings --- Src/AutoFixture/RegularExpressionGenerator.cs | 18 ++++++++++--- .../RegularExpressionGeneratorTest.cs | 25 +++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/Src/AutoFixture/RegularExpressionGenerator.cs b/Src/AutoFixture/RegularExpressionGenerator.cs index 31346f9f9..6cbdf320a 100644 --- a/Src/AutoFixture/RegularExpressionGenerator.cs +++ b/Src/AutoFixture/RegularExpressionGenerator.cs @@ -10,6 +10,16 @@ namespace AutoFixture /// public class RegularExpressionGenerator : ISpecimenBuilder { + private readonly Random random; + + /// + /// Initializes a new instance of the class. + /// + public RegularExpressionGenerator() + { + this.random = new Random(); + } + /// /// Creates a string that is guaranteed to match a RegularExpressionRequest. /// @@ -28,16 +38,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, this.random).Generate(); if (Regex.IsMatch(regex, pattern)) { return regex; diff --git a/Src/AutoFixtureUnitTest/RegularExpressionGeneratorTest.cs b/Src/AutoFixtureUnitTest/RegularExpressionGeneratorTest.cs index 570461564..2eff00d8c 100644 --- a/Src/AutoFixtureUnitTest/RegularExpressionGeneratorTest.cs +++ b/Src/AutoFixtureUnitTest/RegularExpressionGeneratorTest.cs @@ -103,6 +103,31 @@ 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 - because the issue seen comes from Random class using tickcount as seed. + for (int i = 0; i < 10; i++) + { + var result1 = sut.Create(request, dummyContext); + var result2 = sut.Create(request, dummyContext); + + // Assert + Assert.NotEqual(result1, result2); + } + } + [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]+\])?")]