diff --git a/.gitignore b/.gitignore index 8955acf..68bc300 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,6 @@ BuildOutput/ releases/ packages/ _Resharper*/ -#.nuget \ No newline at end of file +#.nuget +.vscode/ +src/.vs/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/VERSION b/VERSION index 6dfa9e0..7bde352 100644 Binary files a/VERSION and b/VERSION differ diff --git a/make.ps1 b/make.ps1 index 50c70e1..0f4643a 100644 --- a/make.ps1 +++ b/make.ps1 @@ -79,7 +79,8 @@ function vstest{ write-host $vstestPath $arguments & $vstestPath $arguments > $logPath\LogTest.log - if(!$LastExitCode -eq 10){ # TURNED OFF!!! + Write-host $LastExitCode + if(!$LastExitCode -eq 0){ # TURNED OFF!!! Write-host "TESTING FAILED0!" -foregroundcolor:red $lastResult = $false } @@ -150,8 +151,8 @@ function createZip{ function publish{ # DEPLOYING write-host "Publishing Nuget package" -foregroundcolor:blue - $outputName = ".\releases\$projectName.$fullBuildVersion.nupkg" - nuget push $outputName + $outputName = ".\releases\$projectName.$buildVersion.nupkg" + nuget push -source "https://nuget.org" $outputName } $basePath = Get-Location diff --git a/src/.vs/TestDataGenerator.Core/v15/Server/sqlite3/db.lock b/src/.vs/TestDataGenerator.Core/v15/Server/sqlite3/db.lock new file mode 100644 index 0000000..e69de29 diff --git a/src/.vs/TestDataGenerator.Core/v15/Server/sqlite3/storage.ide b/src/.vs/TestDataGenerator.Core/v15/Server/sqlite3/storage.ide new file mode 100644 index 0000000..571d37f Binary files /dev/null and b/src/.vs/TestDataGenerator.Core/v15/Server/sqlite3/storage.ide differ diff --git a/src/TestDataGenerator.Core.Tests/TextTests.cs b/src/TestDataGenerator.Core.Tests/TextTests.cs index 247f86d..669101e 100644 --- a/src/TestDataGenerator.Core.Tests/TextTests.cs +++ b/src/TestDataGenerator.Core.Tests/TextTests.cs @@ -481,8 +481,10 @@ public void Can_Generate_Range_Numeric2() var template = @"<<[100-150]>>"; string text = AlphaNumericGenerator.GenerateFromTemplate(template); Console.WriteLine(@"'{0}' produced '{1}'", template, text); - int e = int.Parse(text); - if (e < 100 || e > 150) Assert.Fail("Number not between 100 and 150."); + + int e = int.Parse(text, CultureInfo.InvariantCulture); + if(e < 100 || e>150) Assert.Fail("Number not between 100 and 150."); + } [TestMethod] @@ -513,6 +515,7 @@ public void Can_Generate_Range_Numeric_DecimalFormat() string text = AlphaNumericGenerator.GenerateFromTemplate(template); Console.WriteLine(@"'{0}' produced '{1}'", template, text); double d; + if (!double.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out d)) Assert.Fail(); if (d < 1.00d || d > 10.00d) @@ -528,6 +531,7 @@ public void Can_Generate_Range_Numeric_DecimalFormat2() string text = AlphaNumericGenerator.GenerateFromTemplate(template); Console.WriteLine(@"'{0}' produced '{1}'", template, text); double d; + if (!double.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out d)) Assert.Fail(); if (d < 1.00 || d > 2.00d) Assert.Fail(); @@ -541,6 +545,7 @@ public void Can_Generate_Range_Numeric_DecimalFormat3() string text = AlphaNumericGenerator.GenerateFromTemplate(template); Console.WriteLine(@"'{0}' produced '{1}'", template, text); double d; + if (!double.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out d)) Assert.Fail(); if (d < 1.1 || d > 1.2d) Assert.Fail(); @@ -554,6 +559,7 @@ public void Can_Generate_Range_Numeric_DecimalFormat4() string text = AlphaNumericGenerator.GenerateFromTemplate(template); Console.WriteLine(@"'{0}' produced '{1}'", template, text); double d; + if (!double.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out d)) Assert.Fail(); if (d < 12345.12345 || d > 12345.12346d) Assert.Fail(); @@ -567,6 +573,7 @@ public void Can_Generate_Range_Numeric_DecimalFormat5() string text = AlphaNumericGenerator.GenerateFromTemplate(template); Console.WriteLine(@"'{0}' produced '{1}'", template, text); double d; + if (!double.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out d)) Assert.Fail(); if (d < 12345.9999d || d > 12346d) Assert.Fail(); @@ -643,7 +650,6 @@ public void Can_Generate_MultipleRange_Set5() public void Can_BuildErrorSnippet_Start() { var template = @"[a-z"; - int ndx = 4; try { @@ -1378,7 +1384,7 @@ public void Can_Generate_Correct_Output_from_Negated_Set_Range() var pattern = @"A-Z"; var text = AlphaNumericGenerator.GenerateFromPattern("[^" + pattern + "]"); Console.WriteLine(@"'{0}' produced '{1}'", pattern, text); - Assert.IsTrue(pattern.IndexOf(text, StringComparison.InvariantCulture) == -1); + Assert.IsTrue("ABCDEFGHIJKLMNOPQRSTUVWXYZ".IndexOf(text, StringComparison.InvariantCulture) == -1); } [TestMethod] @@ -1683,7 +1689,7 @@ public void Can_Serialize() [TestCategory("Pattern")] public void Can_Generate_Anagram() { - var pattern = @"[ABC]{:anagram:}"; + var pattern = @"[ABC]{:anagram}"; var text = AlphaNumericGenerator.GenerateFromPattern(pattern); Console.WriteLine(@"'{0}' produced '{1}'", pattern, text); Assert.AreEqual(3, text.Length); @@ -1697,7 +1703,7 @@ public void Can_Generate_Anagram() public void Can_Generate_Anagram_Long() { var input = "abcdefghijklmnopqrstuvwxyz"; - var pattern = @"["+input+"]{:anagram:}"; + var pattern = @"["+input+"]{:anagram}"; var text = AlphaNumericGenerator.GenerateFromPattern(pattern); Console.WriteLine(@"'{0}' produced '{1}'", pattern, text); Assert.AreEqual(input.Length, text.Length); @@ -1707,6 +1713,85 @@ public void Can_Generate_Anagram_Long() } } + + [TestMethod] + [TestCategory("Pattern")] + public void Can_Shuffle() + { + var pattern = @"[ABC]{:shuffle}"; + var text = AlphaNumericGenerator.GenerateFromPattern(pattern); + Console.WriteLine(@"'{0}' produced '{1}'", pattern, text); + Assert.AreEqual(3, text.Length); + Assert.IsTrue(text.Contains("A")); + Assert.IsTrue(text.Contains("B")); + Assert.IsTrue(text.Contains("C")); + } + + [TestMethod] + [TestCategory("Pattern")] + public void Can_Shuffle2() + { + var pattern = @"[ABC]{:shuffle2}"; + var text = AlphaNumericGenerator.GenerateFromPattern(pattern); + Console.WriteLine(@"'{0}' produced '{1}'", pattern, text); + Assert.AreEqual(3, text.Length); + Assert.IsTrue(text.Contains("A")); + Assert.IsTrue(text.Contains("B")); + Assert.IsTrue(text.Contains("C")); + } + + [TestMethod] + [TestCategory("Pattern")] + public void Can_Shuffle_Lots() + { + var pattern = @"[ABC]{:shuffle}"; + var text = ""; + var config = new GenerationConfig(); + for (int i = 0; i < 100; i++) + { + text = AlphaNumericGenerator.GenerateFromPattern(pattern, config); + Console.WriteLine(@"'{0}' produced '{1}'", pattern, text); + } + + Assert.AreEqual(3, text.Length); + Assert.IsTrue(text.Contains("A")); + Assert.IsTrue(text.Contains("B")); + Assert.IsTrue(text.Contains("C")); + } + + [TestMethod] + [TestCategory("Pattern")] + public void Can_Shuffle2_Lots() + { + var pattern = @"[ABC]{:shuffle2}"; + var text = ""; + var config = new GenerationConfig(); + for (int i = 0; i < 100; i++) + { + text = AlphaNumericGenerator.GenerateFromPattern(pattern,config); + Console.WriteLine(@"'{0}' produced '{1}'", pattern, text); + } + + Assert.AreEqual(3, text.Length); + Assert.IsTrue(text.Contains("A")); + Assert.IsTrue(text.Contains("B")); + Assert.IsTrue(text.Contains("C")); + } + + [TestMethod] + [TestCategory("Pattern")] + public void Can_Shuffle2_weird() + { + var pattern = @"([ABC]{:shuffle2}\n){10}"; + var text = ""; + var config = new GenerationConfig(); + for (int i = 0; i < 10; i++) + { + text = AlphaNumericGenerator.GenerateFromPattern(pattern, config); + Console.WriteLine(@"'{0}' produced '{1}'", pattern, text); + } + } + #endregion #region Randomness diff --git a/src/TestDataGenerator.Core/Generators/AlphaNumericGenerator.cs b/src/TestDataGenerator.Core/Generators/AlphaNumericGenerator.cs index f4d7623..62d99a2 100644 --- a/src/TestDataGenerator.Core/Generators/AlphaNumericGenerator.cs +++ b/src/TestDataGenerator.Core/Generators/AlphaNumericGenerator.cs @@ -9,7 +9,7 @@ namespace TestDataGenerator.Core.Generators { - public static class AlphaNumericGenerator + public static class AlphaNumericGenerator { private static readonly Dictionary _ShortHands = GetShortHandMap(); @@ -35,22 +35,24 @@ public static class AlphaNumericGenerator private const char _NamedPattern_Start = '@'; private const char _NamedPattern_End = '@'; - private const string _Anagram = ":anagram:"; + private const string _Anagram = ":anagram"; + private const string _Shuffle = ":shuffle"; + private const string _Shuffle2 = ":shuffle2"; private const int _ErrorSnippet_ContextLength = 50; #region public - + /// /// Takes in a string that contains 0 or more <<placeholder>> values and replaces the placeholder item with the expression it defines. /// /// /// /// - public static string GenerateFromTemplate(string template, GenerationConfig generationConfig=null) + public static string GenerateFromTemplate(string template, GenerationConfig generationConfig = null) { int index = 0; - + // CONFIG if (generationConfig == null) generationConfig = LoadAndRemoveConfigFromTemplate(ref template); if (generationConfig == null) generationConfig = new GenerationConfig(); @@ -62,7 +64,7 @@ public static string GenerateFromTemplate(string template, GenerationConfig gene // Load all from the PatternFiles in config AppendPatternsFromConfigToCollection(generationConfig, generationConfig.NamedPatterns); - + var sb = new StringBuilder(); while (index < template.Length) @@ -80,19 +82,19 @@ public static string GenerateFromTemplate(string template, GenerationConfig gene sb.Append(template.Substring(index, start - index)); // Append everything up to start as it is. if (isEscaped) start = start + 1; start = start + _Placeholder_Start.Length; // move past '<<' to start of expression - + int end = FindPositionOfNext(template, start, _Placeholder_End, _Placeholder_Start); // find end of placeholder if (end == -1) { - throw new GenerationException("Unable to find closing placeholder after "+start); + throw new GenerationException("Unable to find closing placeholder after " + start); } var pattern = template.Substring(start, end - start); // grab our expression if (isEscaped) - sb.Append("<<"+pattern+">>"); + sb.Append("<<" + pattern + ">>"); else GenerateFromPattern(sb, pattern, generationConfig); // generate value from expression - index = end+2; // update our index. + index = end + 2; // update our index. } return sb.ToString(); @@ -130,7 +132,7 @@ private static void GenerateFromPattern(StringBuilder sb, string pattern, Genera ProcessPattern(sb, pattern, config); } - + private static void GenerateFromAlternatedPattern(StringBuilder sb, string exp, ContentOptions contentOptions, GenerationConfig config) { var alternates = exp.Split(_Alternation); @@ -147,11 +149,60 @@ private static void GenerateFromAnagramPattern(StringBuilder sb, string exp, Con { var arr = exp.ToCharArray(); arr = arr.OrderBy(r => config.Random.Next()).ToArray(); - foreach(var ch in arr) + foreach (var ch in arr) AppendCharacterDerivedFromSymbol(sb, ch, false, config); } } + /// + /// Simple (naive) algorithm for randomly sorting an array of values. + /// + /// + /// + /// + /// + private static void ShuffleContent(StringBuilder sb, string expression, ContentOptions contentOptions, GenerationConfig config) + { + var content = expression.ToCharArray(); + + for (int x = 0; x < contentOptions.Repeat; x++) + { + for (int i = 0; i < content.Length - 1; i++) + { + var randomIndex = config.Random.Next(content.Length); + var tmp = content[i]; + content[i] = content[randomIndex]; + content[randomIndex] = tmp; + } + } + sb.Append(content); + } + + /// + /// Shuffle function implementing the Fisher-Yates shuffle algorithm + /// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle + /// + /// + /// + /// + /// + private static void Shuffle2Content(StringBuilder sb, string expression, ContentOptions contentOptions, GenerationConfig config) + { + var content = expression.ToCharArray(); + for (int x = 0; x < contentOptions.Repeat; x++) + { + for (int i = content.Length - 1; i > 0; i--) + { + var randomIndex = config.Random.Next(i + 1); + // notice that each iteration reduces the range of randomly selected indexes (as i decrements). + var tmp = content[i]; + content[i] = content[randomIndex]; + content[randomIndex] = tmp; + } + } + sb.Append(content); + } + private static string GenerateFloatingFormatWithScale(int scale) { var t = "0."; @@ -176,7 +227,7 @@ private static string GenerateFloatingFormatWithScale(int scale) private static string GetRandomCharacterFromRange(string range, bool isNegated, GenerationConfig config) { string ret; - string possibles=_ShortHands["."]; + string possibles = _ShortHands["."]; var items = range.Split('-'); double i; if (!Double.TryParse(items[0], out i)) @@ -222,7 +273,7 @@ private static string GetRandomAlphaItemFromRange(bool isNegated, GenerationConf { var end = possibles.IndexOf(items[1].ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal); possibles = possibles.Substring(start, end - start + 1); - if (isNegated) possibles = Regex.Replace(_ShortHands["."],"[" + possibles + "]", ""); + if (isNegated) possibles = Regex.Replace(_ShortHands["."], "[" + possibles + "]", ""); ret = possibles[config.Random.Next(0, possibles.Length)].ToString(CultureInfo.InvariantCulture); } return ret; @@ -255,7 +306,7 @@ private static string GetRandomNumericItemFromRange(bool isNegated, GenerationCo else { var t = config.Random.NextDouble(); - min = min + (t*(max - min)); + min = min + (t * (max - min)); ret = min.ToString(GenerateFloatingFormatWithScale(scale), CultureInfo.InvariantCulture); } } @@ -353,6 +404,8 @@ private static ContentOptions GetContentOptions(string characters, ref int index if (result.QuantifierContent.Contains(":")) { result.IsAnagram = ContainsAnagram(result.QuantifierContent); + result.IsShuffle = ContainsShuffle(result.QuantifierContent); + result.IsShuffle2 = ContainsShuffle2(result.QuantifierContent); } else { @@ -404,7 +457,7 @@ private static GenerationConfig GetConfiguration(string template, ref int index) #endregion #region Utility - + private static GenerationConfig LoadAndRemoveConfigFromTemplate(ref string template) { int index = 0; @@ -527,7 +580,17 @@ private static string ExpandNegatedMinMax(double min, double max) private static bool ContainsAnagram(string content) { - return content.ToLower().Equals(_Anagram); + return content.ToLower().Contains(_Anagram); + } + + private static bool ContainsShuffle(string content) + { + return content.ToLower().Contains(_Shuffle); + } + + private static bool ContainsShuffle2(string content) + { + return content.ToLower().Contains(_Shuffle2); } private static bool ContainsAlternations(string characters) @@ -568,7 +631,7 @@ private static string BuildErrorIndicationText(string template, int ndx) } catch (Exception ex) { - return "An error occured close to index " + ndx; + return "An error occured close to index " + ndx + "\n" + ex.Message; } } @@ -681,6 +744,18 @@ private static void AppendContentFromSetExpression(StringBuilder sb, string char return; } + if (contentOptions.IsShuffle) + { + ShuffleContent(sb, contentOptions.Content, contentOptions, config); + return; + } + + if (contentOptions.IsShuffle2) + { + Shuffle2Content(sb, contentOptions.Content, contentOptions, config); + return; + } + if (contentOptions.Content.Contains("-")) // Ranged - [0-7] or [a-z] or [1-9A-Za-z] for fun. { MatchCollection ranges = new Regex(@"[A-Za-z]-[A-Za-z]|\d+\.?\d*-\d+\.?\d*|.").Matches(contentOptions.Content); @@ -729,7 +804,8 @@ private static void AppendContentFromSetExpression(StringBuilder sb, string char } } } - + + private static void AppendCharacterDerivedFromSymbol(StringBuilder sb, char symbol, bool isEscaped, GenerationConfig config) { if (!isEscaped) @@ -764,7 +840,7 @@ private static void AppendCharacterDerivedFromSymbol(StringBuilder sb, char symb } } } - + private static void AppendRandomCharacterFromString(StringBuilder sb, string allowedCharacters, GenerationConfig config) { sb.Append(allowedCharacters[config.Random.Next(allowedCharacters.Length)]); @@ -772,4 +848,4 @@ private static void AppendRandomCharacterFromString(StringBuilder sb, string all #endregion } -} +} \ No newline at end of file diff --git a/src/TestDataGenerator.Core/Generators/ContentOptions.cs b/src/TestDataGenerator.Core/Generators/ContentOptions.cs index 2686235..2746a2b 100644 --- a/src/TestDataGenerator.Core/Generators/ContentOptions.cs +++ b/src/TestDataGenerator.Core/Generators/ContentOptions.cs @@ -7,12 +7,16 @@ internal class ContentOptions public string Content { get; set; } public int Repeat { get; set; } public bool IsAnagram { get; set; } + public bool IsShuffle { get; set; } + public bool IsShuffle2 { get; set; } public string QuantifierContent { get; set; } public ContentOptions() { IsNegated = false; IsAnagram = false; + IsShuffle = false; + IsShuffle2 = false; Content = ""; ContainsAlternation = false; Repeat = 1; diff --git a/src/TestDataGenerator.Core/Properties/AssemblyInfo.cs b/src/TestDataGenerator.Core/Properties/AssemblyInfo.cs index e75a0bc..f47fcd0 100644 --- a/src/TestDataGenerator.Core/Properties/AssemblyInfo.cs +++ b/src/TestDataGenerator.Core/Properties/AssemblyInfo.cs @@ -31,4 +31,4 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("5.1.0")] +[assembly: AssemblyVersion("5.1.2")]