diff --git a/Directory.Build.props b/Directory.Build.props index f287a25..96677dd 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -25,7 +25,7 @@ git README.md autopoco;testdata;test-data;poco;generator;fluent;fake-data - 6.0: Lambda data sources (FuncSource, Setup(...).From(() => ...), Source(expr, () => ...) at generation time). Opt-in nullability-aware null generation via RespectNullableAnnotations(threshold). Generic NumberSource<T> and NullableNumberSource<T> with min/max for every built-in numeric type (integers uniform and inclusive, floating point and decimal overflow safe). Records and immutable types are constructed through their constructor, parameters are fed from the registered members with the same name, no parameterless constructor needed anymore; unconfigured struct parameters stay default instead of throwing. IDataSource<T> is covariant. Bug fixes: Nullable* sources honor an explicit null creation threshold (null positions of stable sequences changed), DateTime/DateOnly/TimeOnly/DateOfBirth sources cover their whole range including the upper bound (sequences changed), RandomUtfTextSource no longer loops forever. Build and packaging modernized: GitHub Actions CI, trusted publishing to nuget.org, MinVer tag based versioning, central package management, xunit.v3 tests on net8/9/10. FluentAssertions and Moq are no longer dependencies of the library. See README for details. + 6.0: Lambda data sources (FuncSource, Setup(...).From(() => ...), Source(expr, () => ...) at generation time). Opt-in nullability-aware null generation via RespectNullableAnnotations(threshold). Generic NumberSource<T> and NullableNumberSource<T> with min/max for every built-in numeric type (integers uniform and inclusive, floating point and decimal overflow safe). Records and immutable types are constructed through their constructor, parameters are fed from the registered members with the same name, no parameterless constructor needed anymore; unconfigured struct parameters stay default instead of throwing. Seeds: UseSeed(seed) per factory, CreateSession(recursionLimit, seed) per session, every member gets its own stream derived from the seed. Own deterministic generator (StableRandom, xoshiro256**) instead of System.Random, sequences are stable across .NET versions. Breaking: AutoPocoGlobalSettings removed (AutoPocoDefaults constants instead), every generated sequence changed once. IDataSource<T> is covariant. Bug fixes: Nullable* sources honor an explicit null creation threshold (null positions of stable sequences changed), DateTime/DateOnly/TimeOnly/DateOfBirth sources cover their whole range including the upper bound (sequences changed), RandomUtfTextSource no longer loops forever. Build and packaging modernized: GitHub Actions CI, trusted publishing to nuget.org, MinVer tag based versioning, central package management, xunit.v3 tests on net8/9/10. FluentAssertions and Moq are no longer dependencies of the library. See README for details. diff --git a/README.md b/README.md index 9729451..afbc9ba 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,23 @@ var factory = AutoPocoContainer.Configure(x => { ``` The rules: the constructor that takes the most members which cannot be set otherwise wins, then the one with the fewest parameters, so ordinary classes keep using their parameterless constructor and their setters. With the default conventions get-only properties that match a constructor parameter are registered too, so an unconfigured `Money` gets the default member values through its constructor. A parameter without a matching member gets its declared default value, the default of its value type, or for other reference types an object generated by the session. A struct without any configured member stays at its default, so an unconfigured `DateOnly` parameter no longer throws. A get-only property with a data source but no matching constructor parameter throws an `InvalidOperationException` that names the property. `CtorSource` is the class behind it; `new CtorSource(constructorInfo)` pins a constructor and still takes the matching members. Constructor arguments honor `RespectNullableAnnotations` and the recursion limit, so a self referencing record such as `record Node(string Name, Node? Child)` terminates. +* Seeds: `x.UseSeed(seed)` sets the seed of a factory, `factory.CreateSession(recursionLimit, seed)` gives a single session another seed. Every data source in a session gets its own random stream derived from the session seed and the member it feeds (`Type.Member`), so two members with the same source type no longer produce the same values, the nullable sources of one object no longer become null together, and the values of a member do not depend on which other members are configured. A seed set on a source in the configuration (`Use(s => s.SetSeedToRandomValue(7))`) wins over the session seed. The null patterns of `RespectNullableAnnotations`, the shuffle behind `Random(count)` and constructor arguments follow the seed as well. + +```CSHARP +var factory = AutoPocoContainer.Configure(x => { + x.UseSeed(2024); // default is AutoPocoDefaults.Seed (1337) + x.Include() + .Setup(c => c.FirstName).Use() + .Setup(c => c.LastName).Use(); +}); + +var session = factory.CreateSession(); // seed 2024 +var other = factory.CreateSession(5, 4711); // seed 4711, same configuration +``` + +* Own random number generator: every source draws from `StableRandom` (xoshiro256** seeded through SplitMix64), a `Random` subclass owned by AutoPoco, instead of `System.Random`. The sequence for a seed is defined by AutoPoco's code alone, so test data stays the same across .NET versions; Microsoft explicitly does not promise that for a seeded `System.Random`. The generator is pinned by tests. Rule from now on: catalogs (names, cities, zip codes, ...) and source algorithms are frozen within a major version, a change to either is a major version bump. +* Breaking: `AutoPocoGlobalSettings` is gone, it was mutable process-wide state that leaked between test fixtures. The defaults are read-only constants in `AutoPocoDefaults` (`Seed`, `NullCreationThreshold`, `RecursionLimit`). Set a seed with `UseSeed`, null thresholds per source or with `RespectNullableAnnotations(threshold)`. +* Breaking: because of the own generator and the per-member seeds, every generated sequence changed once with 6.0. Tests that pin generated values need new expectations, this is the last time. * `IDataSource` is covariant now, so a source of `string` (e.g. `CitySource`) can be used for a `string?` member without a nullability warning. * Bug fix: every `Nullable*` source ignored an explicit null creation threshold. The fixed array and dictionary based sources (names, companies, cities, capitals, countries, states, zip codes, urls) even used the threshold as their random seed. The threshold is honored now. As a consequence the null positions in the stable sequences of these sources changed, the data itself comes in the same order as before. * Bug fix: `DateTimeSource`, `DateOnlySource`, `TimeOnlySource` and `DateOfBirthSource` never produced the upper end of their ranges: no December, no 31st, no 23:00, no minute or second 59, and the maximum year of a date of birth was never reached. Ranges within a single year could produce values outside the range. All four sources now pick uniformly from the whole range, both bounds inclusive, and throw an `ArgumentOutOfRangeException` when the maximum lies before the minimum. `TimeOnlySource` supports ranges that wrap around midnight (22:00 to 02:00). `DateTimeSource` keeps the `DateTimeKind` of the minimum date. The generated sequences for a given seed changed. @@ -166,12 +183,12 @@ We plan to add more datasources in future and so we want to provide a better ove # Behavior change (maybe, only some datasources) -All datasources reproduce same results within same seed - important for testing. Additionally you can use +All datasources reproduce same results within same seed - important for testing. Since 6.0 the seed of a factory is set with `x.UseSeed(seed)` and a single session can get another one with `CreateSession(recursionLimit, seed)`, see "New in 6.0.0". Additionally you can use * DataSource.SetSeedToRandomValue() * DataSource.SetSeedToRandomValue(seed) -to change behavior or get other values as the internal seed constant generates. +on a single source; a seed set this way wins over the session seed. # AutoPoco.NetCore AutoPoco is a highly configurable framework for the purpose of fluently building readable test data from Plain Old CLR Objects. This is a modernized (i.e. dotnet core 2.0.0) port of the excellent AutoPoco project which hasn't been maintained since 2014. Thanks to [@robashton](https://twitter.com/robashton) for graciously agreeing to let me pull this fossil out of the amber and give it a light dusting. diff --git a/holonsoft.AutoPoco.Tests/Common/TwoNullableNumbersClass.cs b/holonsoft.AutoPoco.Tests/Common/TwoNullableNumbersClass.cs new file mode 100644 index 0000000..b8e8880 --- /dev/null +++ b/holonsoft.AutoPoco.Tests/Common/TwoNullableNumbersClass.cs @@ -0,0 +1,8 @@ +namespace holonsoft.AutoPoco.Tests.Common; + +public class TwoNullableNumbersClass { + public int? First { get; set; } + public int? Second { get; set; } + public string? FirstName { get; set; } + public string? LastName { get; set; } +} diff --git a/holonsoft.AutoPoco.Tests/Demo/DemoAutoPoco.cs b/holonsoft.AutoPoco.Tests/Demo/DemoAutoPoco.cs index 9c5ca28..ff8e2aa 100644 --- a/holonsoft.AutoPoco.Tests/Demo/DemoAutoPoco.cs +++ b/holonsoft.AutoPoco.Tests/Demo/DemoAutoPoco.cs @@ -101,9 +101,9 @@ public void SomeDemonstrationOfComplexGenerations() .Single() .Get(); - user.FirstName.ShouldBe("Olivia"); - user.LastName.ShouldBe("Turner"); - user.RevealedPassword.ShouldBe("GRvqwwLW"); + user.FirstName.ShouldBe("Alexa"); + user.LastName.ShouldBe("Wilson"); + user.RevealedPassword.ShouldBe("DPj`I`FMBd"); user.ExternalId.ShouldNotBe(0); user.Id.ShouldNotBe(0); user.City.ShouldNotBeNullOrWhiteSpace(); @@ -161,9 +161,9 @@ public void SomeDemonstrationOfComplexGenerationsWithRecords() .Single() .Get(); - user.FirstName.ShouldBe("Olivia"); - user.LastName.ShouldBe("Turner"); - user.RevealedPassword.ShouldBe("GRvqwwLW"); + user.FirstName.ShouldBe("Arthur"); + user.LastName.ShouldBe("Walker"); + user.RevealedPassword.ShouldBe("_AqPqoJvdRvaHXmjJK"); // Create three roles // Create 100 users diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Base/CSharpLiteral.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Base/CSharpLiteral.cs new file mode 100644 index 0000000..30ad762 --- /dev/null +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Base/CSharpLiteral.cs @@ -0,0 +1,68 @@ +using System.Drawing; +using System.Globalization; +using System.Text; + +namespace holonsoft.AutoPoco.Tests.Functionality.Tests.DataSources.Base; + +/// +/// Formats generated values as C# source, so a stable sequence test that fails after an intended +/// change prints the new expected array ready to paste. +/// +public static class CSharpLiteral { + public static string Array(IEnumerable values) { + var type = typeof(T); + var elementType = Nullable.GetUnderlyingType(type) ?? type; + var list = values.ToList(); + var nullable = type != elementType || (!type.IsValueType && list.Any(v => v is null)); + var typeName = TypeName(elementType) + (nullable ? "?" : ""); + return $"new {typeName}[] {{ {string.Join(", ", list.Select(v => Value(v)))} }}"; + } + + public static string Value(object? value) + => value switch { + null => "null", + string s => Quote(s), + bool b => b ? "true" : "false", + int or long or short or byte or sbyte or ushort or uint or ulong => Convert.ToString(value, CultureInfo.InvariantCulture)!, + Int128 or UInt128 => $"{TypeName(value.GetType())}.Parse(\"{value}\")", + decimal m => m.ToString(CultureInfo.InvariantCulture) + "M", + double d => d.ToString("R", CultureInfo.InvariantCulture) + "D", + float f => f.ToString("R", CultureInfo.InvariantCulture) + "F", + DateTime dt => $"new DateTime({dt.Ticks}, DateTimeKind.{dt.Kind})", + DateOnly d => $"new DateOnly({d.Year}, {d.Month}, {d.Day})", + TimeOnly t => $"new TimeOnly({t.Ticks})", + TimeSpan ts => $"new TimeSpan({ts.Ticks})", + Guid g => $"new Guid(\"{g}\")", + Color c => $"Color.FromArgb({c.A}, {c.R}, {c.G}, {c.B})", + Enum e => $"{e.GetType().Name}.{e}", + _ => value.ToString() ?? "null" + }; + + private static string TypeName(Type type) + => type switch { + _ when type == typeof(int) => "int", + _ when type == typeof(long) => "long", + _ when type == typeof(string) => "string", + _ when type == typeof(bool) => "bool", + _ when type == typeof(decimal) => "decimal", + _ when type == typeof(double) => "double", + _ when type == typeof(float) => "float", + _ => type.Name + }; + + private static string Quote(string value) { + var builder = new StringBuilder("\""); + foreach (var c in value) + builder.Append(c switch { + '"' => "\\\"", + '\\' => "\\\\", + '\n' => "\\n", + '\r' => "\\r", + '\t' => "\\t", + _ when c < ' ' => $"\\u{(int) c:x4}", + _ => c.ToString() + }); + + return builder.Append('"').ToString(); + } +} diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Base/TestBase.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Base/TestBase.cs index 5b226d0..c57c3c8 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Base/TestBase.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Base/TestBase.cs @@ -10,8 +10,13 @@ public static void NextReturnsStableElementListInTermsOfTestability(DataSourc for (var i = 0; i < expectedValues.Length; i++) generated.Add(source.Next(null)); - // same count and same order as the expected values - generated.ShouldBe(expectedValues); + // same count and same order as the expected values; on failure the message carries the actual + // values as a C# literal, ready to paste into the test after an intended sequence change + try { + generated.ShouldBe(expectedValues); + } catch (ShouldAssertException e) { + throw new ShouldAssertException($"{e.Message}\nREGENERATE: {CSharpLiteral.Array(generated)}"); + } if (typeof(T) == typeof(bool) || (typeof(T) == typeof(bool?))) return; diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/CompanySourceTest.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/CompanySourceTest.cs index 854669f..60fe5e3 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/CompanySourceTest.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/CompanySourceTest.cs @@ -8,16 +8,10 @@ public class CompanySourceTest : TestBase { [Fact] public void NextReturnsStableCompanyListInTermsOfTestability() => NextReturnsStableElementListInTermsOfTestability( - new CompanySource(), - new[] { - "Sto Plains Holdings", "Ankh-Sto Associates", "Gringotts", "Minuteman Cafe", "Niagular", "Moes Tavern", "Moes Tavern", - "Praxis Corporation", "Kumatsu Motors", "Mammoth Pictures" - }); + new CompanySource(), new string[] { "Charles Townsend Agency", "Chotchkies", "Carrys Candles", "LexCorp", "Monks Diner", "Big T Burgers and Fries", "Keedsler Motors", "Wernham Hogg", "Moes Tavern", "C.H. Lavatory and Sons" }); [Fact] public void NextReturnsStableCompanyListInTermsOfTestabilityAndListCanContainNull() => NextReturnsStableElementListInTermsOfTestability( - new NullableCompanySource()!, - "Sto Plains Holdings", null, "Ankh-Sto Associates", "Gringotts", "Minuteman Cafe", "Niagular", "Moes Tavern", "Moes Tavern", "Praxis Corporation", "Kumatsu Motors", - "Mammoth Pictures", "Mammoth Pictures", "Water and Power", null, "Rouster and Sideways", null, "ABC Telecom", "Niagular", "Widget Corp", null); + new NullableCompanySource()!, new string[] { "Charles Townsend Agency", "Chotchkies", "Carrys Candles", "LexCorp", "Monks Diner", "Big T Burgers and Fries", "Keedsler Motors", "Wernham Hogg", "Moes Tavern", "C.H. Lavatory and Sons", "Omni Consimer Products", "Charles Townsend Agency", "Barrytron", "The New Firm", "Transworld Consortium", "Gizmonic Institute", "Chasers", "Globo-Chem", "Thrift Bank", "Kumatsu Motors" }); } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/CreditCardSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/CreditCardSourceTests.cs index 374e65b..60e62f4 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/CreditCardSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/CreditCardSourceTests.cs @@ -36,18 +36,10 @@ public void NextReturnsRandomCreditCardNumber() { [Fact] public void NextReturnsStableCreditCardListInTermsOfTestability() => NextReturnsStableElementListInTermsOfTestability( - new CreditCardSource(), - new[] { - "5139 8991 3232 7084", "5898 0489 1744 1883", "3695 475531 91051", "3224 797328 93124", "5358 3556 3251 4365", "3027 325203 91438", - "5615 3395 0850 1892", "3644 657657 96148", "4440 3264 5197 1047", "5316 2174 7772 0575" - }); + new CreditCardSource(), new string[] { "5343 1380 7226 1800", "4963 6432 2865 0035", "4655 0609 2255 2430", "5914 8329 7669 1747", "3306 710005 18638", "3576 703413 80305", "5171 2438 3073 1141", "5030 2600 8079 1583", "5159 0467 9793 1279", "5238 6720 4807 5702" }); [Fact] public void NextReturnsStableCreditCardListInTermsOfTestabilityAndListCanContainNull() => NextReturnsStableElementListInTermsOfTestability( - new NullableCreditCardSource()!, - new string?[] { - "5139 8991 3232 7084", null, "5898 0489 1744 1883", "3695 475531 91051", "3224 797328 93124", "5358 3556 3251 4365", "3027 325203 91438", - "5615 3395 0850 1892", "3644 657657 96148", "4440 3264 5197 1047" - }); + new NullableCreditCardSource()!, new string[] { "5343 1380 7226 1800", "4963 6432 2865 0035", "4655 0609 2255 2430", "5914 8329 7669 1747", "3306 710005 18638", "3576 703413 80305", "5171 2438 3073 1141", "5030 2600 8079 1583", "5159 0467 9793 1279", "5238 6720 4807 5702" }); } \ No newline at end of file diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/EmailAddressSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/EmailAddressSourceTests.cs index b7fd206..3697940 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/EmailAddressSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/EmailAddressSourceTests.cs @@ -29,9 +29,5 @@ public void NextReturnsStableEmailListInTermsOfTestability() [Fact] public void NextReturnsStableEmailListInTermsOfTestabilityAndListCanContainNull() => NextReturnsStableElementListInTermsOfTestability( - new NullableEmailAddressSource()!, - "eg0@example.test", null, "eg1@example.test", "eg2@example.test", "eg3@example.test", "eg4@example.test", "eg5@example.test", "eg6@example.test", - "eg7@example.test", "eg8@example.test", "eg9@example.test", "eg10@example.test", "eg11@example.test", null, - "eg12@example.test", null, "eg13@example.test", "eg14@example.test", "eg15@example.test" - ); + new NullableEmailAddressSource()!, new string[] { "eg0@example.test", "eg1@example.test", "eg2@example.test", "eg3@example.test", "eg4@example.test", "eg5@example.test", "eg6@example.test", "eg7@example.test", "eg8@example.test", "eg9@example.test", "eg10@example.test", "eg11@example.test", "eg12@example.test", "eg13@example.test", "eg14@example.test", "eg15@example.test", "eg16@example.test", "eg17@example.test", "eg18@example.test" }); } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/ExtendedEmailAddressSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/ExtendedEmailAddressSourceTests.cs index 0c5f8f0..5a2a6cb 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/ExtendedEmailAddressSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/ExtendedEmailAddressSourceTests.cs @@ -8,16 +8,9 @@ public class ExtendedEmailAddressSourceTests : TestBase { [Fact] public void NextReturnsStableEmailListInTermsOfTestability() => NextReturnsStableElementListInTermsOfTestability( - new ExtendedEmailAddressSource(), - "Olivia.Turner@holonsoft.invalid", "Christopher.Martin@hotmail.invalid", "Tyler.Murphy@golem.invalid", "Jason.Pierce@microsoft.example", - "Zoe.Wells@yahoo.example", "Eric.Scott@heise.example", "Eric.Scott@heise.example", "Sophia.Baker@yahoo.invalid", "Abigail.Perez@msn.test", - "Ethan.Evans@heise.invalid" - ); + new ExtendedEmailAddressSource(), new string[] { "Daniel.Knight@heise.example", "Zoey.Harrison@yahoo.test", "Sophia.Baker@hotmail.invalid", "Victoria.Diaz@google.example", "Aubrey.Harper@golem.test", "George.Dean@hotmail.test", "Ava.Thomas@microsoft.test", "Daniel.Knight@golem.test", "Nora.Long@aol.example", "Scott.Gordon@google.invalid" }); [Fact] public void NextReturnsStableEmailListInTermsOfTestabilityAndListCanContainNull() => NextReturnsStableElementListInTermsOfTestability( - new NullableExtendedEmailAddressSource()!, - "Olivia.Turner@holonsoft.invalid", null, "Christopher.Martin@hotmail.invalid", "Tyler.Murphy@golem.invalid", "Jason.Pierce@microsoft.example", - "Zoe.Wells@yahoo.example", "Eric.Scott@heise.example", "Eric.Scott@heise.example", "Sophia.Baker@yahoo.invalid", "Abigail.Perez@msn.test" - ); + new NullableExtendedEmailAddressSource()!, new string[] { "Daniel.Knight@heise.example", "Zoey.Harrison@yahoo.test", "Sophia.Baker@hotmail.invalid", "Victoria.Diaz@google.example", "Aubrey.Harper@golem.test", "George.Dean@hotmail.test", "Ava.Thomas@microsoft.test", "Daniel.Knight@golem.test", "Nora.Long@aol.example", "Scott.Gordon@google.invalid" }); } \ No newline at end of file diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/FirstNameSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/FirstNameSourceTests.cs index 70f716e..34fd648 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/FirstNameSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/FirstNameSourceTests.cs @@ -8,16 +8,11 @@ public class FirstNameSourceTests : TestBase { [Fact] public void NextReturnsStableFirstNameListInTermsOfTestability() => NextReturnsStableElementListInTermsOfTestability( - new FirstNameSource(), - "Olivia", "Christopher", "Tyler", "Jason", "Zoe", "Eric", "Eric", "Sophia", "Abigail", "Ethan"); + new FirstNameSource(), new string[] { "Daniel", "Zoey", "Sophia", "Victoria", "Aubrey", "George", "Ava", "Daniel", "Nora", "Scott" }); [Fact] public void NextReturnsStableFirstNameListInTermsOfTestabilityAndListCanContainNull() => NextReturnsStableElementListInTermsOfTestability( - new NullableFirstNameSource()!, - "Olivia", null, "Christopher", "Tyler", "Jason", "Zoe", "Eric", "Eric", "Sophia", "Abigail", - "Ethan", "Nicholas", "Evelyn", null, "James", null, "Aria", "Zoe", "Claire", null, - "Daniel", "Scott", "David", null, "Alexa", "Scarlett", "Joseph", null, "Kenneth", "David" - ); + new NullableFirstNameSource()!, new string?[] { "Daniel", "Zoey", "Sophia", "Victoria", "Aubrey", "George", "Ava", "Daniel", "Nora", "Scott", "Ella", "Steven", "Anthony", "Abigail", "Scarlett", "Charles", "Zoey", "Amelia", "Sophia", "Alexander", "Joseph", "Charlotte", "Nathan", "Joseph", null, "Robert", "Sophie", null, "Owen", "Ethan" }); } \ No newline at end of file diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/LastNameSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/LastNameSourceTests.cs index be89014..1af6b16 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/LastNameSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/LastNameSourceTests.cs @@ -8,13 +8,11 @@ public class LastNameSourceTests : TestBase { [Fact] public void NextReturnsStableLastNamesListInTermsOfTestability() => NextReturnsStableElementListInTermsOfTestability( - new LastNameSource(), - "Turner", "Martin", "Murphy", "Pierce", "Wells", "Scott", "Scott", "Baker", "Perez", "Evans"); + new LastNameSource(), new string[] { "Knight", "Harrison", "Baker", "Diaz", "Harper", "Dean", "Thomas", "Knight", "Long", "Gordon" }); [Fact] public void NextReturnsStableLastNamesListInTermsOfTestabilityAndListCanContainNull() => NextReturnsStableElementListInTermsOfTestability( - new NullableLastNameSource()!, - "Turner", null, "Martin", "Murphy", "Pierce", "Wells", "Scott", "Scott", "Baker", "Perez"); + new NullableLastNameSource()!, new string[] { "Knight", "Harrison", "Baker", "Diaz", "Harper", "Dean", "Thomas", "Knight", "Long", "Gordon" }); } \ No newline at end of file diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/LoremIpsumTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/LoremIpsumTests.cs index 4603268..50206a6 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/LoremIpsumTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/LoremIpsumTests.cs @@ -41,8 +41,9 @@ public void NextReturnsSometimesNull() { loopProtectionCounter++; - if (loopProtectionCounter > 20) - throw new Exception($"Expceted to get a result NULL, but did not occure the last {loopProtectionCounter} times"); + // 15 percent per draw: no null in 200 draws has a probability below 1e-13 + if (loopProtectionCounter > 200) + throw new Exception($"Expected to get a result NULL, but did not occur the last {loopProtectionCounter} times"); } } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/RandomTextTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/RandomTextTests.cs index 89c550a..3303d33 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/RandomTextTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/RandomTextTests.cs @@ -41,7 +41,8 @@ public void NextReturnsSometimesNull() { loopProtectionCounter++; - if (loopProtectionCounter > 20) + // 15 percent per draw: no null in 200 draws has a probability below 1e-13 + if (loopProtectionCounter > 200) throw new Exception($"Expected to get a result NULL, but did not occur the last {loopProtectionCounter} times"); } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/UrlSourceTest.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/UrlSourceTest.cs index 76b5ed5..fb0821a 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/UrlSourceTest.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Business/UrlSourceTest.cs @@ -8,20 +8,11 @@ public class UrlSourceTest() : TestBase { [Fact] public void NextReturnsStableUrlListInTermsOfTestability() => NextReturnsStableElementListInTermsOfTestability( - new UrlSource(), - "http://www.chxv.se", "http://www.xejhhhsbva.fr", "http://www.xvalvxcslk.pt", "http://www.vvt.in", "http://www.xmmrnnhc.su", - "http://www.ebouffktxs.iq", "http://www.hvwie.iq", "http://www.ejnv.nl", "http://www.nmqig.me", "http://www.dmiptch.hu", - "http://www.jhngaiwcl.hu", "http://www.bpdni.gr", "http://www.xobun.kg", "http://www.dvy.mil", "http://www.plkqntpot.su", - "http://www.qdmmkkbigq.org", "http://www.odyrea.mv", "http://www.gidqhe.in", "http://www.lrtthaos.mm", "http://www.qtcnjc.com" - ); + new UrlSource(), new string[] { "http://www.pdutnrt.ua", "http://www.axc.iq", "http://www.mgriq.pe", "http://www.jgdowudn.in", "http://www.siowk.om", "http://www.faqdogfvk.tr", "http://www.wqj.nz", "http://www.scfmvsendy.by", "http://www.bmey.ee", "http://www.ksxgwj.pl", "http://www.nxud.ua", "http://www.qgoxobql.ke", "http://www.aqfbywdw.vn", "http://www.vxwhakt.me", "http://www.ebdiqmt.mv", "http://www.oqmorhrsed.ru", "http://www.idqhtrremq.si", "http://www.qswnqa.al", "http://www.aop.pk", "http://www.hbvimbnfj.mx" }); [Fact] public void NextReturnsStableUrlListInTermsOfTestabilityAndListCanContainNull() => NextReturnsStableElementListInTermsOfTestability( - new NullableUrlSource()!, - "http://www.chxv.se", null, "http://www.xejhhhsbva.fr", "http://www.xvalvxcslk.pt", "http://www.vvt.in", "http://www.xmmrnnhc.su", "http://www.ebouffktxs.iq", "http://www.hvwie.iq", "http://www.ejnv.nl", "http://www.nmqig.me", - "http://www.dmiptch.hu", "http://www.jhngaiwcl.hu", "http://www.bpdni.gr", null, "http://www.xobun.kg", null, "http://www.dvy.mil", "http://www.plkqntpot.su", "http://www.qdmmkkbigq.org", null, - "http://www.odyrea.mv", "http://www.gidqhe.in" - ); + new NullableUrlSource()!, new string[] { "http://www.pdutnrt.ua", "http://www.axc.iq", "http://www.mgriq.pe", "http://www.jgdowudn.in", "http://www.siowk.om", "http://www.faqdogfvk.tr", "http://www.wqj.nz", "http://www.scfmvsendy.by", "http://www.bmey.ee", "http://www.ksxgwj.pl", "http://www.nxud.ua", "http://www.qgoxobql.ke", "http://www.aqfbywdw.vn", "http://www.vxwhakt.me", "http://www.ebdiqmt.mv", "http://www.oqmorhrsed.ru", "http://www.idqhtrremq.si", "http://www.qswnqa.al", "http://www.aop.pk", "http://www.hbvimbnfj.mx", "http://www.elp.pe", "http://www.hjxmtllbs.ir" }); } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/CapitalSourceTest.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/CapitalSourceTest.cs index fe19594..c5cfd0e 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/CapitalSourceTest.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/CapitalSourceTest.cs @@ -8,14 +8,10 @@ public class CapitalSourceTest() : TestBase { [Fact] public void NextReturnsStableCapitalListInTermsOfTestability() => NextReturnsStableElementListInTermsOfTestability( - new CapitalSource(), - "Ankara", "Stockholm", "Kuwait City", "Algiers", "Amman", "Nouakchott", "Nouakchott", "Athens", "Damascus", "Nairobi", "Riyadh", "Accra", "Hanoi", - "Amsterdam", "Jerusalem", "Paris", "Abu Dhabi", "Rabat", "Manama", "Paris"); + new CapitalSource(), new string[] { "Pyongyang", "Kuwait City", "Manila", "Thimphu", "Shanghai", "Lisbon", "Seoul", "Muscat", "Tripoli", "Reykjavik", "Jerusalem", "Rabat", "Bangkok", "Pyongyang", "Beirut", "Ankara", "Ashgabat", "Manila", "Beirut", "Kuala Lumpur" }); [Fact] public void NextReturnsStableCapitalListInTermsOfTestabilityAndListCanContainNull() => NextReturnsStableElementListInTermsOfTestability( - new NullableCapitalSource()!, - "Ankara", null, "Stockholm", "Kuwait City", "Algiers", "Amman", "Nouakchott", "Nouakchott", "Athens", "Damascus", - "Nairobi", "Riyadh", "Accra", null, "Hanoi", null, "Amsterdam", "Jerusalem", "Paris", null); + new NullableCapitalSource()!, new string[] { "Pyongyang", "Kuwait City", "Manila", "Thimphu", "Shanghai", "Lisbon", "Seoul", "Muscat", "Tripoli", "Reykjavik", "Jerusalem", "Rabat", "Bangkok", "Pyongyang", "Beirut", "Ankara", "Ashgabat", "Manila", "Beirut", "Kuala Lumpur" }); } \ No newline at end of file diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/CitySourceTest.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/CitySourceTest.cs index 8b0fd96..12ebfbd 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/CitySourceTest.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/CitySourceTest.cs @@ -8,15 +8,10 @@ public class CitySourceTest() : TestBase { [Fact] public void NextReturnsStableCityListInTermsOfTestability() => NextReturnsStableElementListInTermsOfTestability( - new CitySource(), - "Lima", "Lahore", "Rio de Janeiro", "Monaco", "Ulaanbaatar", "Basseterre", "Basseterre", "Mexico City", "Guangzhou", "Chennai", "Taipei", "Kuala Lumpur", - "Tripoli", "Dhaka", "Ulaanbaatar", "Beijing", "Asmara", "Vatican City", "Hargeisa", "Shanghai" - ); + new CitySource(), new string[] { "Freetown", "London", "Abidjan", "Kingstown", "Ho Chi Minh City", "Tripoli", "Berlin", "Mexico City", "Xi'an", "Alexandria", "Monrovia", "Freetown", "Rio de Janeiro", "London", "Los Angeles", "Taipei", "Rome", "Lima", "Tianjin", "Jinan" }); [Fact] public void NextReturnsStableCityListInTermsOfTestabilityAndListCanContainNull() => NextReturnsStableElementListInTermsOfTestability( - new NullableCitySource()!, - "Lima", null, "Lahore", "Rio de Janeiro", "Monaco", "Ulaanbaatar", "Basseterre", "Basseterre", "Mexico City", "Guangzhou", - "Chennai", "Taipei", "Kuala Lumpur", null, "Tripoli", null, "Dhaka", "Ulaanbaatar", "Beijing", null); + new NullableCitySource()!, new string[] { "Freetown", "London", "Abidjan", "Kingstown", "Ho Chi Minh City", "Tripoli", "Berlin", "Mexico City", "Xi'an", "Alexandria", "Monrovia", "Freetown", "Rio de Janeiro", "London", "Los Angeles", "Taipei", "Rome", "Lima", "Tianjin", "Jinan" }); } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/CountrySourceTest.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/CountrySourceTest.cs index 790f9dc..4bf5d31 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/CountrySourceTest.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/CountrySourceTest.cs @@ -8,33 +8,21 @@ public class CountrySourceTest : TestBase { [Fact] public void NextReturnsStableCountryListInTermsOfTestability() => NextReturnsStableElementListInTermsOfTestability( - new CountrySource(), - "Croatia", "Brazil", "Gambia", "Uganda", "Sri Lanka", "Uzbekistan", "Uruguay", "Comoros", "India", "Fiji" - ); + new CountrySource(), new string[] { "Guatemala", "Samoa", "Monaco", "Marshall Islands", "Tunisia", "Jamaica", "Lebanon", "Brazil", "Zimbabwe", "Liberia" }); [Fact] public void NextReturnsStableCountryListInTermsOfTestabilityAndListCanContainNull() => NextReturnsStableElementListInTermsOfTestability( - new NullableCountrySource()!, - "Croatia", null, "Brazil", "Gambia", "Uganda", "Sri Lanka", "Uzbekistan", "Uruguay", "Comoros", "India", - "Fiji", "Finland", "Eswatini", null, "Saint Vincent and the Grenadines", null, "Austria", "Sri Lanka", "Algeria", null, - "Tanzania", "Ukraine" - ); + new NullableCountrySource()!, new string[] { "Guatemala", "Samoa", "Monaco", "Marshall Islands", "Tunisia", "Jamaica", "Lebanon", "Brazil", "Zimbabwe", "Liberia", "Denmark", "Nauru", "Belize", "Switzerland", "East Timor", "New Zealand", "Guyana", "Guatemala", "Mauritania", "Burundi", "Samoa", "Spain" }); [Fact] public void NextReturnsStableCountryAbbreviationListInTermsOfTestability() => NextReturnsStableElementListInTermsOfTestability( - new CountrySource(true), - "HRV", "BRA", "GMB", "UGA", "LKA", "UZB", "URY", "COM", "IND", "FJI" - ); + new CountrySource(true), new string[] { "GTM", "WSM", "MCO", "MHL", "TUN", "JAM", "LBN", "BRA", "ZWE", "LBR" }); [Fact] public void NextReturnsStableCountryAbbreviationListInTermsOfTestabilityAndListCanContainNull() => NextReturnsStableElementListInTermsOfTestability( - new NullableCountrySource(true)!, - "HRV", null, "BRA", "GMB", "UGA", "LKA", "UZB", "URY", "COM", "IND", - "FJI", "FIN", "SWZ", null, "VCT", null, "AUT", "LKA", "DZA", null, - "TZA", "UKR" - ); + new NullableCountrySource(true)!, new string[] { "GTM", "WSM", "MCO", "MHL", "TUN", "JAM", "LBN", "BRA", "ZWE", "LBR", "DNK", "NRU", "BLZ", "CHE", "TLS", "NZL", "GUY", "GTM", "MRT", "BDI", "WSM", "ESP" }); } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/PostalZipCodesSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/PostalZipCodesSourceTests.cs index 4969567..b7567a4 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/PostalZipCodesSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/PostalZipCodesSourceTests.cs @@ -8,39 +8,24 @@ public class PostalZipCodeGermanySourceTest : TestBase { [Fact] public void NextReturnsStableZipCodeListsInTermsOfTestability() { NextReturnsStableElementListInTermsOfTestability( - new PostalZipCodeGermanySource(), - "13627", "01156", "19399", "99988", "99894", "99994", "99994", "06279", "20099", "14059"); + new PostalZipCodeGermanySource(), new string[] { "40227", "14052", "30161", "99999", "14057", "40225", "29683", "13627", "20255", "30159" }); NextReturnsStableElementListInTermsOfTestability( - new PostalZipCodeNetherlandsSource(), - "9413XG", "8728KV", "0459TW", "5101VF", "0190XX", "0141KD", "5724ZJ", "1742ZV", "1266DP", "6152SB"); + new PostalZipCodeNetherlandsSource(), new string[] { "1277VM", "0486FL", "9413XG", "7335MY", "8179PD", "0141KD", "4295YK", "1277VM", "6613WL", "6529IB" }); NextReturnsStableElementListInTermsOfTestability( - new PostalZipCodeUSASource(), - "63396", "38605", "77071", "67146", "63914", "68048", "62480", "47160", "91747", "42485"); + new PostalZipCodeUSASource(), new string[] { "66070", "05685", "63396", "58810", "29100", "68048", "09501", "66070", "12922", "88893" }); } [Fact] public void NextReturnsStableZipCodeListsInTermsOfTestabilityAndListCanContainNull() { NextReturnsStableElementListInTermsOfTestability( - new NullablePostalZipCodeGermanySource()!, - "13627", null, "01156", "19399", "99988", "99894", "99994", "99994", "06279", "20099", - "14059", "19395", "14057", null, "40219", null, "01108", "99894", "01069", null, - "99958", "99988", "99955", null, "01067", "20253", "99947", null, "99991", "01156" - ); + new NullablePostalZipCodeGermanySource()!, new string?[] { "40227", "14052", "30161", "99999", "14057", "40225", "29683", "13627", "20255", "30159", "99894", "40227", "19406", "14052", "20148", "19399", "30163", "13629", "01139", "39638", "20095", "01109", "20099", "99994", null, "40227", "29693", null, "99894", "99891" }); NextReturnsStableElementListInTermsOfTestability( - new NullablePostalZipCodeNetherlandsSource()!, - "9413XG", null, "8728KV", "0459TW", "5101VF", "0190XX", "0141KD", "5724ZJ", "1742ZV", "1266DP", - "6152SB", "0997UB", "4673YW", null, "2062NU", null, "3769LI", "0190XX", "6771ZY", null, - "6576SS", "5101VF", "7865AL", null, "4603NG", "3080FO", "0344XA", null, "5724ZJ", "7541VY" - ); + new NullablePostalZipCodeNetherlandsSource()!, new string?[] { "1277VM", "0486FL", "9413XG", "7335MY", "8179PD", "0141KD", "4295YK", "1277VM", "6613WL", "6529IB", "9239RI", "4656VN", "9491BV", "3863CA", "1488DB", "0486FL", "8232MH", "9413XG", "6917LS", "7678AF", "7819HL", "3216HZ", "7678AF", "6056WK", null, "8070FC", "2505TE", null, "0997UB", "3769LI" }); NextReturnsStableElementListInTermsOfTestability( - new NullablePostalZipCodeUSASource()!, - "63396", null, "38605", "77071", "67146", "63914", "68048", "62480", "47160", "91747", - "42485", "45429", "10770", null, "95933", null, "59426", "63914", "47418", null, - "71881", "67146", "26048", null, "60764", "42964", "61265", null, "62480", "93946" - ); + new NullablePostalZipCodeUSASource()!, new string?[] { "66070", "05685", "63396", "58810", "29100", "68048", "09501", "66070", "12922", "88893", "90617", "28937", "02380", "27809", "00377", "05685", "58249", "63396", "37880", "32279", "78946", "57428", "32279", "38011", null, "26395", "96788", null, "45429", "59426" }); } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/StatesOfACountrySourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/StatesOfACountrySourceTests.cs index eecfd6f..c88329b 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/StatesOfACountrySourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Country/StatesOfACountrySourceTests.cs @@ -32,43 +32,24 @@ private static void NextReturnsACountryState(int lengthOfAbbreviationCode) [Fact] public void NextReturnsStableStateListInTermsOfTestability() { NextReturnsStableElementListInTermsOfTestability( - new GermanStatesSource(), - "Brandenburg", "Bayern", "Hamburg", "Schleswig-Holstein", "Sachsen-Anhalt", "Thüringen", "Thüringen", "Brandenburg", "Hessen", "Bremen", "Bremen", - "Bremen", "Sachsen", "Baden-Württemberg", "Sachsen-Anhalt", "Baden-Württemberg", "Schleswig-Holstein", "Schleswig-Holstein", "Sachsen-Anhalt", - "Baden-Württemberg" - ); + new GermanStatesSource(), new string[] { "Sachsen", "Thüringen", "Brandenburg", "Bremen", "Brandenburg", "Sachsen-Anhalt", "Bayern", "Brandenburg", "Niedersachsen", "Baden-Württemberg", "Mecklenburg-Vorpommern", "Sachsen", "Berlin", "Berlin", "Sachsen", "Hessen", "Rheinland-Pfalz", "Bayern", "Niedersachsen", "Baden-Württemberg" }); NextReturnsStableElementListInTermsOfTestability( - new DutchStatesSource(), - "Friesland", "Flevoland", "Gelderland", "Zuid-Holland", "Zeeland", "Zuid-Holland", "Zuid-Holland", "Friesland", "Groningen", "Gelderland", - "Gelderland", "Gelderland", "Utrecht", "Drenthe", "Zeeland", "Drenthe", "Zeeland", "Zuid-Holland", "Zeeland", "Drenthe" - ); + new DutchStatesSource(), new string[] { "Gelderland", "Groningen", "Gelderland", "Flevoland", "Gelderland", "Overijssel", "Drenthe", "Noord-Holland", "Friesland", "Friesland", "Noord-Brabant", "Zeeland", "Flevoland", "Overijssel", "Drenthe", "Utrecht", "Noord-Brabant", "Gelderland", "Zuid-Holland", "Noord-Brabant" }); NextReturnsStableElementListInTermsOfTestability( - new USStatesSource(), - "Idaho", "Connecticut", "Louisiana", "Guam", "West Virginia", "U.S. Virgin Islands", "U.S. Virgin Islands", "Hawaii", "Michigan", "Kentucky", - "Kentucky", "Kansas", "Texas", "Arizona", "West Virginia", "Alabama", "Wyoming", "Guam", "Wisconsin", "Alabama" - ); + new USStatesSource(), new string[] { "New Hampshire", "West Virginia", "Arkansas", "Massachusetts", "Puerto Rico", "Virginia", "Wyoming", "Maryland", "North Carolina", "Mississippi", "New Hampshire", "Arizona", "Ohio", "Vermont", "Rhode Island", "Louisiana", "South Dakota", "Wisconsin", "Tennessee", "Connecticut" }); } [Fact] public void NextReturnsStableStateListInTermsOfTestabilityAndListCanContainNull() { NextReturnsStableElementListInTermsOfTestability( - new NullableGermanStatesSource()!, - "Brandenburg", null, "Bayern", "Hamburg", "Schleswig-Holstein", "Sachsen-Anhalt", "Thüringen", "Thüringen", "Brandenburg", "Hessen", - "Bremen", "Bremen", "Bremen", null, "Sachsen", null, "Baden-Württemberg", "Sachsen-Anhalt", "Baden-Württemberg", null - ); + new NullableGermanStatesSource()!, new string[] { "Sachsen", "Thüringen", "Brandenburg", "Bremen", "Brandenburg", "Sachsen-Anhalt", "Bayern", "Brandenburg", "Niedersachsen", "Baden-Württemberg", "Mecklenburg-Vorpommern", "Sachsen", "Berlin", "Berlin", "Sachsen", "Hessen", "Rheinland-Pfalz", "Bayern", "Niedersachsen", "Baden-Württemberg" }); NextReturnsStableElementListInTermsOfTestability( - new NullableDutchStatesSource()!, - "Friesland", null, "Flevoland", "Gelderland", "Zuid-Holland", "Zeeland", "Zuid-Holland", "Zuid-Holland", "Friesland", "Groningen", - "Gelderland", "Gelderland", "Gelderland", null, "Utrecht", null, "Drenthe", "Zeeland", "Drenthe", null - ); + new NullableDutchStatesSource()!, new string[] { "Gelderland", "Groningen", "Gelderland", "Flevoland", "Gelderland", "Overijssel", "Drenthe", "Noord-Holland", "Friesland", "Friesland", "Noord-Brabant", "Zeeland", "Flevoland", "Overijssel", "Drenthe", "Utrecht", "Noord-Brabant", "Gelderland", "Zuid-Holland", "Noord-Brabant" }); NextReturnsStableElementListInTermsOfTestability( - new NullableUSStatesSource()!, - "Idaho", null, "Connecticut", "Louisiana", "Guam", "West Virginia", "U.S. Virgin Islands", "U.S. Virgin Islands", "Hawaii", "Michigan", - "Kentucky", "Kentucky", "Kansas", null, "Texas", null, "Arizona", "West Virginia", "Alabama", null - ); + new NullableUSStatesSource()!, new string[] { "New Hampshire", "West Virginia", "Arkansas", "Massachusetts", "Puerto Rico", "Virginia", "Wyoming", "Maryland", "North Carolina", "Mississippi", "New Hampshire", "Arizona", "Ohio", "Vermont", "Rhode Island", "Louisiana", "South Dakota", "Wisconsin", "Tennessee", "Connecticut" }); } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/NullCreationThresholdTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/NullCreationThresholdTests.cs index ee1d74f..a9008de 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/NullCreationThresholdTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/NullCreationThresholdTests.cs @@ -85,7 +85,7 @@ public void ThresholdIsAppliedToTheEvaluator(string name) [MemberData(nameof(DefaultSourceNames))] public void DefaultConstructorUsesTheGlobalThreshold(string name) => _defaultFactories[name]().RandomNullEvaluator.ShouldBeOfType() - .ThresholdPercentage.ShouldBe(AutoPocoGlobalSettings.NullCreationThreshold); + .ThresholdPercentage.ShouldBe(AutoPocoDefaults.NullCreationThreshold); [Fact] public void ThresholdDoesNotChangeTheDataSequence() { diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/BooleanSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/BooleanSourceTests.cs index 63c235f..0d56ad9 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/BooleanSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/BooleanSourceTests.cs @@ -8,12 +8,12 @@ public class BooleanSourceTests : TestBase { [Fact] public void NextReturnsStableBooleanListInTermsOfTestability() { var source = new BooleanSource(); - NextReturnsStableElementListInTermsOfTestability(source, new bool[] { false, false, false, true, true, true, true, false, false, false }); + NextReturnsStableElementListInTermsOfTestability(source, new bool[] { false, true, true, false, true, true, true, true, false, false }); } [Fact] public void NextReturnsStableBooleanListInTermsOfTestabilityAndListCanContainNull() { var source = new NullableBooleanSource(); - NextReturnsStableElementListInTermsOfTestability(source, new bool?[] { false, null, false, false, true, true, true, true, false, false }); + NextReturnsStableElementListInTermsOfTestability(source, new bool?[] { false, true, true, false, true, true, true, true, false, false }); } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/ColorSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/ColorSourceTests.cs index 67ce675..c5c4966 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/ColorSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/ColorSourceTests.cs @@ -10,33 +10,13 @@ public class ColorSourceTests : TestBase { public void NextReturnsStableColorListInTermsOfTestability() { var source = new ColorSource(); - NextReturnsStableElementListInTermsOfTestability(source, new Color[] { - Color.FromArgb(255, 53, 30, 80), - Color.FromArgb(255, 237, 214, 244), - Color.FromArgb(255, 244, 49, 98), - Color.FromArgb(255, 74, 76, 72), - Color.FromArgb(255, 192, 12, 215), - Color.FromArgb(255, 3, 225, 238), - Color.FromArgb(255, 221, 1, 118), - Color.FromArgb(255, 220, 242, 27), - Color.FromArgb(255, 187, 117, 112), - Color.FromArgb(255, 26, 215, 215) }); + NextReturnsStableElementListInTermsOfTestability(source, new Color[] { Color.FromArgb(255, 220, 239, 67), Color.FromArgb(255, 148, 115, 109), Color.FromArgb(255, 177, 83, 248), Color.FromArgb(255, 96, 23, 220), Color.FromArgb(255, 194, 98, 44), Color.FromArgb(255, 230, 122, 17), Color.FromArgb(255, 168, 48, 125), Color.FromArgb(255, 233, 70, 67), Color.FromArgb(255, 110, 27, 246), Color.FromArgb(255, 148, 163, 90) }); } [Fact] public void NextReturnsStableColorListInTermsOfTestabilityAndListCanContainNull() { var source = new NullableColorSource(); - NextReturnsStableElementListInTermsOfTestability(source, new Color?[] { - Color.FromArgb(255, 53, 30, 80), - null, - Color.FromArgb(255, 237, 214, 244), - Color.FromArgb(255, 244, 49, 98), - Color.FromArgb(255, 74, 76, 72), - Color.FromArgb(255, 192, 12, 215), - Color.FromArgb(255, 3, 225, 238), - Color.FromArgb(255, 221, 1, 118), - Color.FromArgb(255, 220, 242, 27), - Color.FromArgb(255, 187, 117, 112) }); + NextReturnsStableElementListInTermsOfTestability(source, new Color?[] { Color.FromArgb(255, 220, 239, 67), Color.FromArgb(255, 148, 115, 109), Color.FromArgb(255, 177, 83, 248), Color.FromArgb(255, 96, 23, 220), Color.FromArgb(255, 194, 98, 44), Color.FromArgb(255, 230, 122, 17), Color.FromArgb(255, 168, 48, 125), Color.FromArgb(255, 233, 70, 67), Color.FromArgb(255, 110, 27, 246), Color.FromArgb(255, 148, 163, 90) }); } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DateOfBirthSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DateOfBirthSourceTests.cs index 0c74759..9323739 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DateOfBirthSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DateOfBirthSourceTests.cs @@ -64,34 +64,12 @@ public void SetMinMaxYearsChangesTheRange() { [Fact] public void NextReturnsStableDateTimeListInTermsOfTestability() { var source = new DateOfBirthSource(); - NextReturnsStableElementListInTermsOfTestability(source, new DateTime[] { - new (1941, 12, 27, 0, 0, 0, DateTimeKind.Utc), - new (1923, 9, 22, 0, 0, 0, DateTimeKind.Utc), - new (1963, 4, 23, 0, 0, 0, DateTimeKind.Utc), - new (2087, 3, 15, 0, 0, 0, DateTimeKind.Utc), - new (2069, 5, 2, 0, 0, 0, DateTimeKind.Utc), - new (2093, 1, 3, 0, 0, 0, DateTimeKind.Utc), - new (2092, 7, 10, 0, 0, 0, DateTimeKind.Utc), - new (1939, 1, 15, 0, 0, 0, DateTimeKind.Utc), - new (1977, 9, 28, 0, 0, 0, DateTimeKind.Utc), - new (1959, 1, 29, 0, 0, 0, DateTimeKind.Utc) - }); + NextReturnsStableElementListInTermsOfTestability(source, new DateTime[] { new DateTime(609630624000000000, DateTimeKind.Utc), new DateTime(620042688000000000, DateTimeKind.Utc), new DateTime(613037376000000000, DateTimeKind.Utc), new DateTime(625907520000000000, DateTimeKind.Utc), new DateTime(621699840000000000, DateTimeKind.Utc), new DateTime(653166720000000000, DateTimeKind.Utc), new DateTime(646449984000000000, DateTimeKind.Utc), new DateTime(656324640000000000, DateTimeKind.Utc), new DateTime(652370112000000000, DateTimeKind.Utc), new DateTime(611629920000000000, DateTimeKind.Utc) }); } [Fact] public void NextReturnsStableDateTimeListInTermsOfTestabilityAndListCanContainNull() { var source = new NullableDateOfBirthSource(); - NextReturnsStableElementListInTermsOfTestability(source, new DateTime?[] { - new (1941, 12, 27, 0, 0, 0, DateTimeKind.Utc), - null, - new (1923, 9, 22, 0, 0, 0, DateTimeKind.Utc), - new (1963, 4, 23, 0, 0, 0, DateTimeKind.Utc), - new (2087, 3, 15, 0, 0, 0, DateTimeKind.Utc), - new (2069, 5, 2, 0, 0, 0, DateTimeKind.Utc), - new (2093, 1, 3, 0, 0, 0, DateTimeKind.Utc), - new (2092, 7, 10, 0, 0, 0, DateTimeKind.Utc), - new (1939, 1, 15, 0, 0, 0, DateTimeKind.Utc), - new (1977, 9, 28, 0, 0, 0, DateTimeKind.Utc) - }); + NextReturnsStableElementListInTermsOfTestability(source, new DateTime?[] { new DateTime(609630624000000000, DateTimeKind.Utc), new DateTime(620042688000000000, DateTimeKind.Utc), new DateTime(613037376000000000, DateTimeKind.Utc), new DateTime(625907520000000000, DateTimeKind.Utc), new DateTime(621699840000000000, DateTimeKind.Utc), new DateTime(653166720000000000, DateTimeKind.Utc), new DateTime(646449984000000000, DateTimeKind.Utc), new DateTime(656324640000000000, DateTimeKind.Utc), new DateTime(652370112000000000, DateTimeKind.Utc), new DateTime(611629920000000000, DateTimeKind.Utc) }); } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DateOnlySourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DateOnlySourceTests.cs index d0e17a4..253d43d 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DateOnlySourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DateOnlySourceTests.cs @@ -82,24 +82,12 @@ public void SetMinAndMaxDateChangeTheRange() { [Fact] public void NextReturnsStableDateTimeListInTermsOfTestability() { var source = new DateOnlySource(_minDate, _maxDate); - NextReturnsStableElementListInTermsOfTestability(source, new DateOnly[] { - new DateOnly(2006, 6, 23), - new DateOnly(2003, 8, 29), - new DateOnly(2009, 10, 6), - new DateOnly(2028, 11, 14), - new DateOnly(2026, 2, 12) - }); + NextReturnsStableElementListInTermsOfTestability(source, new DateOnly[] { new DateOnly(2020, 12, 24), new DateOnly(2023, 7, 15), new DateOnly(2026, 3, 26), new DateOnly(2014, 12, 12), new DateOnly(2001, 5, 19) }); } [Fact] public void NextReturnsStableDateTimeListInTermsOfTestabilityAndListCanContainNull() { var source = new NullableDateOnlySource(_minDate, _maxDate); - NextReturnsStableElementListInTermsOfTestability(source, new DateOnly?[] { - new DateOnly(2006, 6, 23), - null, - new DateOnly(2003, 8, 29), - new DateOnly(2009, 10, 6), - new DateOnly(2028, 11, 14) - }); + NextReturnsStableElementListInTermsOfTestability(source, new DateOnly?[] { new DateOnly(2020, 12, 24), new DateOnly(2023, 7, 15), new DateOnly(2026, 3, 26), new DateOnly(2014, 12, 12), new DateOnly(2001, 5, 19) }); } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DateTimeSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DateTimeSourceTests.cs index 2a0cef6..48ff667 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DateTimeSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DateTimeSourceTests.cs @@ -96,24 +96,12 @@ public void SetDateRangeChangesTheRange() { [Fact] public void NextReturnsStableDateTimeListInTermsOfTestability() { var source = new DateTimeSource(_minDate, _maxDate); - NextReturnsStableElementListInTermsOfTestability(source, new DateTime[] { - new DateTime(636496727523570519, DateTimeKind.Utc), - new DateTime(637790390162231123, DateTimeKind.Utc), - new DateTime(635912374590276787, DateTimeKind.Utc), - new DateTime(639196215649992172, DateTimeKind.Utc), - new DateTime(632769569224965583, DateTimeKind.Utc) - }); + NextReturnsStableElementListInTermsOfTestability(source, new DateTime[] { new DateTime(633814176156249820, DateTimeKind.Utc), new DateTime(631201399357349359, DateTimeKind.Utc), new DateTime(633944634959756867, DateTimeKind.Utc), new DateTime(634621712587170196, DateTimeKind.Utc), new DateTime(640304386247800947, DateTimeKind.Utc) }); } [Fact] public void NextReturnsStableDateTimeListInTermsOfTestabilityAndListCanContainNull() { var source = new NullableDateTimeSource(_minDate, _maxDate); - NextReturnsStableElementListInTermsOfTestability(source, new DateTime?[] { - new DateTime(636496727523570519, DateTimeKind.Utc), - null, - new DateTime(637790390162231123, DateTimeKind.Utc), - new DateTime(635912374590276787, DateTimeKind.Utc), - new DateTime(639196215649992172, DateTimeKind.Utc) - }); + NextReturnsStableElementListInTermsOfTestability(source, new DateTime?[] { new DateTime(633814176156249820, DateTimeKind.Utc), new DateTime(631201399357349359, DateTimeKind.Utc), new DateTime(633944634959756867, DateTimeKind.Utc), new DateTime(634621712587170196, DateTimeKind.Utc), new DateTime(640304386247800947, DateTimeKind.Utc) }); } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DecimalSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DecimalSourceTests.cs index 987247d..c54d0f9 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DecimalSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DecimalSourceTests.cs @@ -16,12 +16,12 @@ public void NextReturnsNotZero() { [Fact] public void NextReturnsStableBooleanListInTermsOfTestability() { var source = new DecimalSource(-1000, 1000, 3); - NextReturnsStableElementListInTermsOfTestability(source, new decimal[] { -582.223M, -763.966M, -370.068M, 862.719M, 684.934M, 920.505M, 915.663M, -611.551M, -226.444M, -412.156M }); + NextReturnsStableElementListInTermsOfTestability(source, new decimal[] { 351.887M, 628.947M, 555.026M, 861.740M, -641.550M, 309.005M, -801.130M, -478.745M, -543.466M, 638.454M }); } [Fact] public void NextReturnsStableBooleanListInTermsOfTestabilityAndListCanContainNull() { var source = new NullableDecimalSource(-1000, 1000, 3); - NextReturnsStableElementListInTermsOfTestability(source, new decimal?[] { -582.223M, null, -763.966M, -370.068M, 862.719M, 684.934M, 920.505M, 915.663M, -611.551M, -226.444M }); + NextReturnsStableElementListInTermsOfTestability(source, new decimal?[] { 351.887M, 628.947M, 555.026M, 861.740M, -641.550M, 309.005M, -801.130M, -478.745M, -543.466M, 638.454M }); } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DoubleSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DoubleSourceTests.cs index 6980f28..f9d7fcc 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DoubleSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/DoubleSourceTests.cs @@ -14,7 +14,7 @@ public void NextReturnsStableDoubleListInTermsOfTestability() { var value1 = source.Next(null); var value2 = source.Next(null); - var expectedValues = new[] { 9320.4579, 8440.4255, 9606.4981, 9582.5342, 2022.8219, 3929.1042, 3009.8279, 3076.1592, 2897.0263, 7571.7751 }; + var expectedValues = new double[] { 9315.613D, 1874.3269D, 6579.5728D, 1084.4057D, 2680.2109D, 2359.8418D, 8210.346D, 9181.7256D, 5000.4863D, 6898.7527D }; NextReturnsStableElementListInTermsOfTestability(source, expectedValues); } @@ -27,11 +27,7 @@ public void NextReturnsStableDoubleListInTermsOfTestabilityAndListCanContainNull var value1 = source.Next(null); var value2 = source.Next(null); - var expectedValues = new double?[] { - 3218.163, 9320.4579, 8440.4255, 9606.4981, 9582.5342, 2022.8219, 3929.1042, 3009.8279, - 3076.1592, 2897.0263, null, 7571.7751, null, 602.9881, 8460.9037, 246.5728, null, - 8836.2247, 9351.0288, 8715.8482 - }; + var expectedValues = new double?[] { 9315.613D, 1874.3269D, 6579.5728D, 1084.4057D, 2680.2109D, 2359.8418D, 8210.346D, 9181.7256D, 5000.4863D, 6898.7527D, 1927.6558D, 307.7781D, 9872.8269D, 8948.2294D, 8812.8767D, 1588.4502D, 4050.1859D, 4272.4747D, 2646.7468D, 8460.8539D }; NextReturnsStableElementListInTermsOfTestability(source, expectedValues); } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/EnumSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/EnumSourceTests.cs index 4c4c049..4218a63 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/EnumSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/EnumSourceTests.cs @@ -24,15 +24,13 @@ public void NextReturnsAEnumValue() { var source = new EnumSource(); var value = source.Next(null); - value.ShouldBe(MyTestEnum.three); + value.ShouldBe(MyTestEnum.four); } [Fact] public void NextReturnsStableDateTimeListInTermsOfTestability() { var source = new EnumSource(); - NextReturnsStableElementListInTermsOfTestability(source, new MyTestEnum[] { - MyTestEnum.three, MyTestEnum.two, MyTestEnum.four, MyTestEnum.ten, - }); + NextReturnsStableElementListInTermsOfTestability(source, new MyTestEnum[] { MyTestEnum.four, MyTestEnum.five, MyTestEnum.four, MyTestEnum.two }); } //[Fact] diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/FloatSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/FloatSourceTests.cs index 1c69164..e8fd520 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/FloatSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/FloatSourceTests.cs @@ -14,7 +14,7 @@ public void NextReturnsStableFloatListInTermsOfTestability() { value2.ShouldNotBe(value1); - var expectedValues = new[] { -1.2592764E+38F, 2.9356796E+38F, 2.330711E+38F, 3.132315E+38F, 3.1158412E+38F, -2.0810006E+38F, -7.7054755E+37F, -1.402494E+38F, -1.3568953E+38F, -1.4800383E+38F }; + var expectedValues = new float[] { 1.8886554E+38F, 2.932349E+38F, -2.1830817E+38F, 1.0514881E+38F, -2.7261045E+38F, -1.6290857E+38F, -1.8493199E+38F, 2.1725454E+38F, 2.8403097E+38F, -3.403767E+36F }; NextReturnsStableElementListInTermsOfTestability(source, expectedValues); } @@ -27,11 +27,7 @@ public void NextReturnsStableFloatListInTermsOfTestabilityAndListCanContainNull( value2.ShouldNotBe(value1); - var expectedValues = new float?[] { - -2.5996409E+38F, -1.2592764E+38F, 2.9356796E+38F, 2.330711E+38F, 3.132315E+38F, 3.1158412E+38F, -2.0810006E+38F, -7.7054755E+37F, - -1.402494E+38F, -1.3568953E+38F, -1.4800383E+38F, null, 1.7335668E+38F, null, -3.0570498E+38F, 2.3447885E+38F, -3.3020635E+38F, - null, 2.6027988E+38F, 2.9566952E+38F, - }; + var expectedValues = new float?[] { 1.8886554E+38F, 2.932349E+38F, -2.1830817E+38F, 1.0514881E+38F, -2.7261045E+38F, -1.6290857E+38F, -1.8493199E+38F, 2.1725454E+38F, 2.8403097E+38F, -3.403767E+36F, 1.2709048E+38F, -2.1464215E+38F, -3.2599887E+38F, 3.3153998E+38F, 2.6797953E+38F, 2.5867485E+38F, -2.3796047E+38F, -6.873113E+37F, -5.345013E+37F, -1.6520902E+38F }; NextReturnsStableElementListInTermsOfTestability(source, expectedValues); } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/GuidSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/GuidSourceTests.cs index 6247beb..73c5a62 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/GuidSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/GuidSourceTests.cs @@ -24,18 +24,7 @@ public void NextWithOneSourceReturnsDifferentGuids() { [Fact] public void NextReturnsStableGuidListInTermsOfTestability() { var source = new GuidSource(); - var expectedValues = new Guid[] { - new("c7f40068-5e43-aa02-c27c-4fd927fc2227"), - new("b254896a-12e5-1eef-9af7-227ef036e328"), - new("c1d474a5-ba17-69e3-c756-e60d4fa4da45"), - new("baf3bb5a-59f6-5524-10d6-2d4c3c84b98b"), - new("b3332698-6127-b00f-a6eb-ea8f2ce2cef6"), - new("3423dd1d-2943-4c32-4f7f-b1418d962c43"), - new("29755a9c-2670-f91d-ac43-130a6f95282d"), - new("5f2f3607-8461-6f48-f4e7-63a8afce220f"), - new("d2f2a7bf-a56d-f685-bf29-88ff1527dcba"), - new("56398356-cd67-18e1-1206-3489c3754ade") - }; + var expectedValues = new Guid[] { new Guid("4f822edc-a0a0-ad0a-ef5d-88ce515881d0"), new Guid("1e263e43-1747-c70b-94e1-9d89137f4dee"), new Guid("1a887873-af70-2de1-6d65-da9478768da7"), new Guid("710ef3b1-9109-1974-53d5-92c39179b842"), new Guid("00b301f8-b23b-3a6f-60ff-3f6121dab8d1"), new Guid("c62ef017-304f-ead7-dc73-b7bda73ab87e"), new Guid("a7fbfcc2-5e87-afce-625b-b50b3eb6422f"), new Guid("1d45a92c-72f2-055f-e637-0451c923b6fc"), new Guid("9449f97a-7dbd-e4cd-1114-ee5dbe7b4de1"), new Guid("336dd8a8-3d4c-267d-3054-6b36cc6e2566") }; NextReturnsStableElementListInTermsOfTestability(source, expectedValues); } @@ -43,18 +32,7 @@ public void NextReturnsStableGuidListInTermsOfTestability() { [Fact] public void NextReturnsStableGuidListInTermsOfTestabilityAndListCanContainNull() { var source = new NullableGuidSource(); - var expectedValues = new Guid?[] { - new("c7f40068-5e43-aa02-c27c-4fd927fc2227"), - null, - new("b254896a-12e5-1eef-9af7-227ef036e328"), - new("c1d474a5-ba17-69e3-c756-e60d4fa4da45"), - new("baf3bb5a-59f6-5524-10d6-2d4c3c84b98b"), - new("b3332698-6127-b00f-a6eb-ea8f2ce2cef6"), - new("3423dd1d-2943-4c32-4f7f-b1418d962c43"), - new("29755a9c-2670-f91d-ac43-130a6f95282d"), - new("5f2f3607-8461-6f48-f4e7-63a8afce220f"), - new("d2f2a7bf-a56d-f685-bf29-88ff1527dcba") - }; + var expectedValues = new Guid?[] { new Guid("4f822edc-a0a0-ad0a-ef5d-88ce515881d0"), new Guid("1e263e43-1747-c70b-94e1-9d89137f4dee"), new Guid("1a887873-af70-2de1-6d65-da9478768da7"), new Guid("710ef3b1-9109-1974-53d5-92c39179b842"), new Guid("00b301f8-b23b-3a6f-60ff-3f6121dab8d1"), new Guid("c62ef017-304f-ead7-dc73-b7bda73ab87e"), new Guid("a7fbfcc2-5e87-afce-625b-b50b3eb6422f"), new Guid("1d45a92c-72f2-055f-e637-0451c923b6fc"), new Guid("9449f97a-7dbd-e4cd-1114-ee5dbe7b4de1"), new Guid("336dd8a8-3d4c-267d-3054-6b36cc6e2566") }; NextReturnsStableElementListInTermsOfTestability(source, expectedValues); } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/Int128SourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/Int128SourceTests.cs index 2a2ddd1..06b94a5 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/Int128SourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/Int128SourceTests.cs @@ -14,36 +14,14 @@ public void NextReturnsStableIntegerListInTermsOfTestability() { value2.ShouldNotBe(value1); - var expectedValues = new Int128[] { - Int128.Parse("143690171173498817360163827402810620233"), - Int128.Parse("79084523709590909672801088012826681208"), - Int128.Parse("74823831097960774313025574080029609872"), - Int128.Parse("159407834237347846052185826375228211524"), - Int128.Parse("52223885703587809960109664647061407365"), - Int128.Parse("139788082672604822438111731211146017605"), - Int128.Parse("126225536855414366150553758439335117946"), - Int128.Parse("28314137635927039146777420747176845036"), - Int128.Parse("59657195644755483058324710281895298770"), - Int128.Parse("94647823324471480647605927980975812549") - }; + var expectedValues = new Int128[] { Int128.Parse("60987084906937984112046819220676830573"), Int128.Parse("33835948027648810824318586988430284115"), Int128.Parse("77675183650409931858877803340199100256"), Int128.Parse("142015491253806660708691336154857305052"), Int128.Parse("63545246249042533839977056743032445794"), Int128.Parse("7141739557454812798578630150314014694"), Int128.Parse("133989770755707609774151602249422214161"), Int128.Parse("51160944211386362012696806524424246320"), Int128.Parse("143416118351510298964194540107973806313"), Int128.Parse("117237721780817223276236274990635170371") }; NextReturnsStableElementListInTermsOfTestability(source, expectedValues); } [Fact] public void NextReturnsStableIntegerListInTermsOfTestabilityAndListCanContainNull() { var source = new NullableInt128Source(); - var expectedValues = new Int128?[] { - Int128.Parse("53588579211354529504051107809525091762"), - null, - Int128.Parse("65806877381257608849394510731932166095"), - Int128.Parse("143690171173498817360163827402810620233"), - Int128.Parse("79084523709590909672801088012826681208"), - Int128.Parse("74823831097960774313025574080029609872"), - Int128.Parse("159407834237347846052185826375228211524"), - Int128.Parse("52223885703587809960109664647061407365"), - Int128.Parse("139788082672604822438111731211146017605"), - Int128.Parse("126225536855414366150553758439335117946") - }; + var expectedValues = new Int128?[] { Int128.Parse("59870440665555846531897554548306763247"), Int128.Parse("94432775096124116087659504134120137108"), Int128.Parse("60987084906937984112046819220676830573"), Int128.Parse("33835948027648810824318586988430284115"), Int128.Parse("77675183650409931858877803340199100256"), Int128.Parse("142015491253806660708691336154857305052"), Int128.Parse("63545246249042533839977056743032445794"), Int128.Parse("7141739557454812798578630150314014694"), Int128.Parse("133989770755707609774151602249422214161"), Int128.Parse("51160944211386362012696806524424246320") }; NextReturnsStableElementListInTermsOfTestability(source, expectedValues); } } \ No newline at end of file diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/IntegerSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/IntegerSourceTests.cs index a7ac98d..ddf80d7 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/IntegerSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/IntegerSourceTests.cs @@ -14,14 +14,14 @@ public void NextReturnsStableIntegerListInTermsOfTestability() { value2.ShouldNotBe(value1); - var expectedValues = new[] { -1809184581, -2056928004, -830599876, 645581134, -1620759081, 1813626657, 1895040361, -1868928598, -998188007, -2043547633 }; + var expectedValues = new int[] { -1641660861, 161341844, -1702332301, 349857133, -250678351, 1133696339, -2135752200, -515899552, 1177481239, 1035432924 }; NextReturnsStableElementListInTermsOfTestability(source, expectedValues); } [Fact] public void NextReturnsStableIntegerListInTermsOfTestabilityAndListCanContainNull() { var source = new NullableIntegerSource(); - var expectedValues = new int?[] { -448584298, null, 676384243, -1809184581, -2056928004, -830599876, 645581134, -1620759081, 1813626657, 1895040361 }; + var expectedValues = new int?[] { -813551908, 1317559791, -1641660861, 161341844, -1702332301, 349857133, -250678351, 1133696339, -2135752200, -515899552 }; NextReturnsStableElementListInTermsOfTestability(source, expectedValues); } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/LongSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/LongSourceTests.cs index 77d61f6..04163ad 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/LongSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/LongSourceTests.cs @@ -14,14 +14,14 @@ public void NextReturnsStableIntegerListInTermsOfTestability() { value2.ShouldNotBe(value1); - var expectedValues = new long[] { -2088576094730105571L, -4011664040411344994L, 6355547538257366377L, 8014153468887495314L, -649010795262790965L, -7229896734490018064L, -1110956013845024003L, 6398855854917328672L, 8059659982279395047L, 3798038521325847177L }; + var expectedValues = new long[] { 5119210995652640323, 7948148639268725140, -5917255539371771789, 2850064399460885869, -7389121618870602831, -4415645767678372525, -5012591893755526664, 5888696349687283552, 7698675207198666775, -92259300244229156 }; NextReturnsStableElementListInTermsOfTestability(source, expectedValues); } [Fact] public void NextReturnsStableIntegerListInTermsOfTestabilityAndListCanContainNull() { var source = new NullableLongSource(); - var expectedValues = new long?[] { -3413286636718563731L, null, 8490162135550892900L, -2088576094730105571L, -4011664040411344994L, 6355547538257366377L, 8014153468887495314L, -649010795262790965L, -7229896734490018064L, -1110956013845024003L }; + var expectedValues = new long?[] { 3245583091863006940, 5801014903410548207, 5119210995652640323, 7948148639268725140, -5917255539371771789, 2850064399460885869, -7389121618870602831, -4415645767678372525, -5012591893755526664, 5888696349687283552 }; NextReturnsStableElementListInTermsOfTestability(source, expectedValues); } } \ No newline at end of file diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/NumberSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/NumberSourceTests.cs index 6b9490c..33f8a12 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/NumberSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/NumberSourceTests.cs @@ -210,18 +210,18 @@ public void NullableSourceHonorsAnExplicitNullCreationThreshold() { [Fact] public void NextReturnsStableNumberListInTermsOfTestability() { var source = new NumberSource(1, 100); - NextReturnsStableElementListInTermsOfTestability(source, 41, 50, 37, 60, 14, 57, 91, 40, 22, 52); + NextReturnsStableElementListInTermsOfTestability(source, new int[] { 93, 68, 21, 50, 84, 97, 24, 93, 67, 99 }); } [Fact] public void NextReturnsStableDecimalListInTermsOfTestability() { var source = new NumberSource(0m, 10m); - NextReturnsStableElementListInTermsOfTestability(source, 2.088883408386670m, 1.18017077500940m, 3.14965957922380m, 9.313593841769540m, 8.424672204267550m); + NextReturnsStableElementListInTermsOfTestability(source, new decimal[] { 6.759434119590040M, 8.144736480449250M, 7.775129841449140M, 9.308700010966430M, 1.792249344530620M }); } [Fact] public void NextReturnsStableNumberListInTermsOfTestabilityAndListCanContainNull() { var source = new NullableNumberSource(1, 100); - NextReturnsStableElementListInTermsOfTestability(source, new int?[] { 41, null, 50, 37, 60, 14, 57, 91, 40, 22 }); + NextReturnsStableElementListInTermsOfTestability(source, new int?[] { 93, 68, 21, 50, 84, 97, 24, 93, 67, 99 }); } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/RandomNumberSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/RandomNumberSourceTests.cs index ac905ee..ac38093 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/RandomNumberSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/RandomNumberSourceTests.cs @@ -15,8 +15,6 @@ public void NextReturnsStableIntegerListInTermsOfTestability() { value2.ShouldNotBe(value1); NextReturnsStableElementListInTermsOfTestability( - source, - -1809184581, -2056928004, -830599876, 645581134, -1620759081, 1813626657, 1895040361, -1868928598, -998188007, -2043547633 - ); + source, new int[] { -1641660861, 161341844, -1702332301, 349857133, -250678351, 1133696339, -2135752200, -515899552, 1177481239, 1035432924 }); } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/RandomStringSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/RandomStringSourceTests.cs index fd7ffb9..0db3ec3 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/RandomStringSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/RandomStringSourceTests.cs @@ -23,16 +23,12 @@ public void NextReturnsStringOfCorrectSize(int min, int max) { [Fact] public void NextReturnsStableStringsInTermsOfTestability() { var source = new RandomStringSource(5, 7, 'A', (char) 123); - NextReturnsStableElementListInTermsOfTestability(source, new[] { - "GSwqx", "LWRRQlC", "AtwsA[s", "Gk[ZGqr", "ew^]i_`", "GvJEb", "MMXmwlR", "suUJM", "W_rU`", "gUOaI\\" - }); + NextReturnsStableElementListInTermsOfTestability(source, new string[] { "pDUtn", "TyaX]C", "mgRiqjG", "\\wUd[[n", "[SIowk_", "aQD^oG", "VKa^WQ", "`ScFmV", "End`yZB", "E]yDK" }); } [Fact] public void NextReturnsStableStringsInTermsOfTestabilityAndListCanContainNull() { var source = new NullableRandomStringSource(5, 7, 'A', (char) 123); - NextReturnsStableElementListInTermsOfTestability(source!, new string?[] { - "GSwqx", null, "LWRRQlC", "AtwsA[s", "Gk[ZGqr", "ew^]i_`", "GvJEb", "MMXmwlR", "suUJM", "W_rU`" - }); + NextReturnsStableElementListInTermsOfTestability(source!, new string?[] { "pDUtn", "TyaX]C", "mgRiqjG", "\\wUd[[n", "[SIowk_", "aQD^oG", "VKa^WQ", "`ScFmV", "End`yZB", "E]yDK" }); } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/TimeOnlySourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/TimeOnlySourceTests.cs index 3ae488c..5aa128c 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/TimeOnlySourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/TimeOnlySourceTests.cs @@ -40,13 +40,15 @@ public void NextReachesTheLastHourMinuteAndSecondOfTheDay() { [Fact] public void NextReachesTheUpperBoundOfAShortRange() { - var min = new TimeOnly(23, 59, 59); + // a 10 millisecond window: missing millisecond 999 in 300 draws has a probability of 0.9^300 + var min = new TimeOnly(23, 59, 59, 990); var max = TimeOnly.MaxValue; var source = new TimeOnlySource(min, max); var values = Draw(source, 300); values.ShouldAllBe(x => x >= min && x <= max); values.ShouldContain(x => x.Millisecond == 999); + values.ShouldContain(x => x.Millisecond == 990); } [Fact] @@ -78,24 +80,12 @@ public void SetMinMaxRangeChangesTheRange() { [Fact] public void NextReturnsStableDateTimeListInTermsOfTestability() { var source = new TimeOnlySource(_minDate, _maxDate); - NextReturnsStableElementListInTermsOfTestability(source, new TimeOnly[] { - new TimeOnly(461154038194), // 12:48:35.4038194 - new TimeOnly(500633488837), // 13:54:23.3488837 - new TimeOnly(443321001900), // 12:18:52.1001900 - new TimeOnly(543535877990), // 15:05:53.5877990 - new TimeOnly(347410193632) // 09:39:01.0193632 - }); + NextReturnsStableElementListInTermsOfTestability(source, new TimeOnly[] { new TimeOnly(426772885212), new TimeOnly(593448500803), new TimeOnly(371913204116), new TimeOnly(328551510961), new TimeOnly(364295624019) }); } [Fact] public void NextReturnsStableDateTimeListInTermsOfTestabilityAndListCanContainNull() { var source = new NullableTimeOnlySource(_minDate, _maxDate); - NextReturnsStableElementListInTermsOfTestability(source, new TimeOnly?[] { - new TimeOnly(461154038194), // 12:48:35.4038194 - null, - new TimeOnly(500633488837), // 13:54:23.3488837 - new TimeOnly(443321001900), // 12:18:52.1001900 - new TimeOnly(543535877990) // 15:05:53.5877990 - }); + NextReturnsStableElementListInTermsOfTestability(source, new TimeOnly?[] { new TimeOnly(426772885212), new TimeOnly(593448500803), new TimeOnly(371913204116), new TimeOnly(328551510961), new TimeOnly(364295624019) }); } } diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/TimeSpanSourceTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/TimeSpanSourceTests.cs index 4737368..f892e61 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/TimeSpanSourceTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/DataSources/Primitives/TimeSpanSourceTests.cs @@ -14,35 +14,13 @@ public void NextReturnsStableTimeSpanListInTermsOfTestability() { value2.ShouldNotBe(value1); - NextReturnsStableElementListInTermsOfTestability(source, new TimeSpan[] { - new(-3700680841), - new(8627187683), - new(6849344408), - new(9205046584), - new(9156634835), - new(-6115511262), - new(-2264435865), - new(-4121559892), - new(-3987557149), - new(-4349441888), - }); + NextReturnsStableElementListInTermsOfTestability(source, new TimeSpan[] { new TimeSpan(5550259682), new TimeSpan(8617400021), new TimeSpan(-6415501310), new TimeSpan(3090046013), new TimeSpan(-8011301711), new TimeSpan(-4787452734), new TimeSpan(-5434663020), new TimeSpan(6384537375), new TimeSpan(8346920384), new TimeSpan(-100027733) }); } [Fact] public void NextReturnsStableTimeSpanListInTermsOfTestabilityAndListCanContainNull() { var source = new NullableTimeSpanSource(new TimeSpan(-10000000000), new TimeSpan(10000000000)); - NextReturnsStableElementListInTermsOfTestability(source, new TimeSpan?[] { - new(-5822233183), - null, - new(-7639658449), - new(-3700680841), - new(8627187683), - new(6849344408), - new(9205046584), - new(9156634835), - new(-6115511262), - new(-2264435865), - }); + NextReturnsStableElementListInTermsOfTestability(source, new TimeSpan?[] { new TimeSpan(3518868239), new TimeSpan(6289472960), new TimeSpan(5550259682), new TimeSpan(8617400021), new TimeSpan(-6415501310), new TimeSpan(3090046013), new TimeSpan(-8011301711), new TimeSpan(-4787452734), new TimeSpan(-5434663020), new TimeSpan(6384537375) }); } } \ No newline at end of file diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/Engine/DataSourceBaseTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/Engine/DataSourceBaseTests.cs index 89227ca..c102435 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/Engine/DataSourceBaseTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/Engine/DataSourceBaseTests.cs @@ -111,6 +111,56 @@ public void RandomSeedLeavesTheDefaultSequence() { Take(reseeded, 20).ShouldNotBe(reference); } + [Fact] + public void SessionSeedChangesTheSequenceAndIsAppliedOnce() { + var reference = new RandomSource(); + reference.SetSeedToRandomValue(42); + var source = new RandomSource(); + + ((ISessionSeedable) source).ApplySessionSeed(42); + var first = Take(source, 5); + ((ISessionSeedable) source).ApplySessionSeed(42); + var second = Take(source, 5); + + first.ShouldBe(Take(reference, 5)); + second.ShouldBe(Take(reference, 5), "the second application must not restart the stream"); + } + + [Fact] + public void SessionSeedIsForwardedToTheNullEvaluator() { + var source = new NullableCountingSource(); + var evaluator = new AlwaysNullEvaluator(); + source.SetRandomNullEvaluator(evaluator); + + ((ISessionSeedable) source).ApplySessionSeed(4711); + + evaluator.SeedSeen.ShouldBe(4711); + } + + [Fact] + public void AnExplicitSeedWinsOverTheSessionSeed() { + var reference = new RandomSource(); + reference.SetSeedToRandomValue(7); + var source = new RandomSource(); + source.SetSeedToRandomValue(7); + + ((ISessionSeedable) source).ApplySessionSeed(42); + + Take(source, 5).ShouldBe(Take(reference, 5)); + } + + [Fact] + public void AnExplicitSeedAfterTheSessionSeedStillApplies() { + var reference = new RandomSource(); + reference.SetSeedToRandomValue(7); + var source = new RandomSource(); + ((ISessionSeedable) source).ApplySessionSeed(42); + + source.SetSeedToRandomValue(7); + + Take(source, 5).ShouldBe(Take(reference, 5)); + } + [Fact] public void InternalNextReturnsTheSameValuesAsNext() { IDataSource asInterface = new ConstantSource(); diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/Engine/DefaultRandomNullEvaluatorTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/Engine/DefaultRandomNullEvaluatorTests.cs index 685cfbd..6fd0ee8 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/Engine/DefaultRandomNullEvaluatorTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/Engine/DefaultRandomNullEvaluatorTests.cs @@ -11,7 +11,7 @@ private static List Draw(DefaultRandomNullEvaluator evaluator, int count) [Fact] public void DefaultThresholdComesFromGlobalSettings() - => new DefaultRandomNullEvaluator().ThresholdPercentage.ShouldBe(AutoPocoGlobalSettings.NullCreationThreshold); + => new DefaultRandomNullEvaluator().ThresholdPercentage.ShouldBe(AutoPocoDefaults.NullCreationThreshold); [Fact] public void ExplicitThresholdIsKept() diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/Engine/NullableAnnotationSettingsTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/Engine/NullableAnnotationSettingsTests.cs index 7b09495..4a3180f 100644 --- a/holonsoft.AutoPoco.Tests/Functionality.Tests/Engine/NullableAnnotationSettingsTests.cs +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/Engine/NullableAnnotationSettingsTests.cs @@ -19,7 +19,7 @@ public void EnabledWithoutThresholdUsesTheGlobalDefault() { var settings = NullableAnnotationSettings.Enabled(); settings.RespectNullableAnnotations.ShouldBeTrue(); - settings.NullCreationThreshold.ShouldBe(AutoPocoGlobalSettings.NullCreationThreshold); + settings.NullCreationThreshold.ShouldBe(AutoPocoDefaults.NullCreationThreshold); } [Fact] @@ -62,7 +62,7 @@ public void ConfigurationBuilderRespectNullableAnnotationsWithoutThresholdUsesTh builder.RespectNullableAnnotations(); - builder.NullableAnnotations.NullCreationThreshold.ShouldBe(AutoPocoGlobalSettings.NullCreationThreshold); + builder.NullableAnnotations.NullCreationThreshold.ShouldBe(AutoPocoDefaults.NullCreationThreshold); } [Fact] diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/Util/SeedDerivationTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/Util/SeedDerivationTests.cs new file mode 100644 index 0000000..d593dde --- /dev/null +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/Util/SeedDerivationTests.cs @@ -0,0 +1,39 @@ +using Shouldly; +using Xunit; +using holonsoft.AutoPoco.Tests.Common; +using holonsoft.AutoPoco.Util; + +namespace holonsoft.AutoPoco.Tests.Functionality.Tests.Util; + +public class SeedDerivationTests { + [Fact] + public void Fnv1aIsTheReferenceImplementation() { + // reference values of 32 bit FNV-1a, as signed int + SeedDerivation.Fnv1a("").ShouldBe(unchecked((int) 0x811c9dc5)); + SeedDerivation.Fnv1a("a").ShouldBe(unchecked((int) 0xe40c292c)); + SeedDerivation.Fnv1a("foobar").ShouldBe(unchecked((int) 0xbf9cf968)); + } + + [Fact] + public void DerivationIsDeterministic() { + SeedDerivation.ForMember(1337, typeof(SimpleUser), "FirstName").ShouldBe(SeedDerivation.ForMember(1337, typeof(SimpleUser), "FirstName")); + SeedDerivation.ForType(1337, typeof(SimpleUser)).ShouldBe(SeedDerivation.ForType(1337, typeof(SimpleUser))); + } + + [Fact] + public void DifferentPlacesGetDifferentSeeds() { + var firstName = SeedDerivation.ForMember(1337, typeof(SimpleUser), "FirstName"); + + firstName.ShouldNotBe(SeedDerivation.ForMember(1337, typeof(SimpleUser), "LastName")); + firstName.ShouldNotBe(SeedDerivation.ForMember(1337, typeof(SimpleUserRecord), "FirstName")); + firstName.ShouldNotBe(SeedDerivation.ForMember(1338, typeof(SimpleUser), "FirstName")); + firstName.ShouldNotBe(SeedDerivation.ForNullEvaluator(1337, typeof(SimpleUser), "FirstName")); + firstName.ShouldNotBe(SeedDerivation.ForType(1337, typeof(SimpleUser))); + SeedDerivation.ForMethodArgument(1337, typeof(SimpleUser), "SetPassword", 0) + .ShouldNotBe(SeedDerivation.ForMethodArgument(1337, typeof(SimpleUser), "SetPassword", 1)); + } + + [Fact] + public void Fnv1aRejectsNull() + => Should.Throw(() => SeedDerivation.Fnv1a(null!)); +} diff --git a/holonsoft.AutoPoco.Tests/Functionality.Tests/Util/StableRandomTests.cs b/holonsoft.AutoPoco.Tests/Functionality.Tests/Util/StableRandomTests.cs new file mode 100644 index 0000000..7332863 --- /dev/null +++ b/holonsoft.AutoPoco.Tests/Functionality.Tests/Util/StableRandomTests.cs @@ -0,0 +1,142 @@ +using Shouldly; +using Xunit; +using holonsoft.AutoPoco.Util; + +namespace holonsoft.AutoPoco.Tests.Functionality.Tests.Util; + +/// +/// The pinned values are the contract: the sequence of a seed must never change, whatever the runtime. +/// +public class StableRandomTests { + private static List Draw(int count, Func next, int seed = 1337) { + var random = new StableRandom(seed); + return Enumerable.Range(0, count).Select(_ => next(random)).ToList(); + } + + [Fact] + public void RawOutputIsPinned() + => Draw(4, r => r.NextUInt64()).ShouldBe([12468955128717782748UL, 15024386940265324015UL, 14342583032507416131UL, 17171520676123500948UL]); + + [Fact] + public void NextIsPinned() { + Draw(6, r => r.Next()).ShouldBe([1451577424, 1749068840, 1669696419, 1999028105, 384882616, 1405532988]); + Draw(3, r => r.Next(), seed: 0).ShouldBe([1291202459, 1605832636, 221233742]); + Draw(3, r => r.Next(), seed: -1).ShouldBe([1202360426, 1648054284, 1089411296]); + } + + [Fact] + public void BoundedNextIsPinned() { + Draw(10, r => r.Next(100)).ShouldBe([92, 67, 20, 49, 83, 96, 23, 92, 66, 98]); + Draw(10, r => r.Next(-50, 50)).ShouldBe([42, 17, -30, -1, 33, 46, -27, 42, 16, 48]); + } + + [Fact] + public void NextInt64IsPinned() + => Draw(4, r => r.NextInt64()).ShouldBe([3245583091863006940L, 5801014903410548207L, 5119210995652640323L, 7948148639268725140L]); + + [Fact] + public void FloatingPointIsPinned() { + Draw(4, r => r.NextDouble()).ShouldBe([0.6759434119590045D, 0.8144736480449252D, 0.7775129841449137D, 0.9308700010966429D]); + Draw(4, r => r.NextSingle()).ShouldBe([0.6759434F, 0.8144736F, 0.77751297F, 0.93087F]); + } + + [Fact] + public void NextBytesIsPinnedAndFillsPartialBlocks() { + var random = new StableRandom(1337); + var bytes = new byte[11]; + + random.NextBytes(bytes); + + bytes.ShouldBe([220, 46, 130, 79, 160, 160, 10, 173, 239, 93, 136]); + } + + [Fact] + public void NextBytesOnASpanMatchesTheArrayOverload() { + var expected = new byte[13]; + new StableRandom(5).NextBytes(expected); + Span span = stackalloc byte[13]; + + new StableRandom(5).NextBytes(span); + + span.ToArray().ShouldBe(expected); + } + + [Fact] + public void SameSeedSameSequenceDifferentSeedDifferentSequence() { + Draw(20, r => r.Next(), 42).ShouldBe(Draw(20, r => r.Next(), 42)); + Draw(20, r => r.Next(), 42).ShouldNotBe(Draw(20, r => r.Next(), 43)); + Draw(20, r => r.Next(), int.MinValue).ShouldNotBe(Draw(20, r => r.Next(), int.MaxValue)); + } + + [Fact] + public void NextStaysBelowIntMaxValue() + => Draw(10_000, r => r.Next()).ShouldAllBe(x => x >= 0 && x < int.MaxValue); + + [Fact] + public void BoundedValuesStayInRange() { + Draw(10_000, r => r.Next(7)).ShouldAllBe(x => x >= 0 && x < 7); + Draw(10_000, r => r.Next(-3, 4)).ShouldAllBe(x => x >= -3 && x < 4); + Draw(10_000, r => r.Next(int.MinValue, int.MaxValue)).Count.ShouldBe(10_000); + Draw(10_000, r => r.NextInt64(9)).ShouldAllBe(x => x >= 0 && x < 9); + Draw(10_000, r => r.NextInt64(long.MinValue, long.MaxValue)).Count.ShouldBe(10_000); + Draw(10_000, r => r.NextInt64()).ShouldAllBe(x => x >= 0); + Draw(10_000, r => r.NextDouble()).ShouldAllBe(x => x >= 0.0 && x < 1.0); + Draw(10_000, r => r.NextSingle()).ShouldAllBe(x => x >= 0.0f && x < 1.0f); + } + + [Fact] + public void EmptyRangesReturnTheMinimum() { + new StableRandom(1).Next(0).ShouldBe(0); + new StableRandom(1).Next(5, 5).ShouldBe(5); + new StableRandom(1).NextInt64(0).ShouldBe(0); + new StableRandom(1).NextInt64(-9, -9).ShouldBe(-9); + } + + [Fact] + public void FullRangesReachBothSigns() { + Draw(1000, r => r.Next(int.MinValue, int.MaxValue)).ShouldContain(x => x < 0); + Draw(1000, r => r.Next(int.MinValue, int.MaxValue)).ShouldContain(x => x > 0); + Draw(1000, r => r.NextInt64(long.MinValue, long.MaxValue)).ShouldContain(x => x < int.MinValue); + Draw(1000, r => r.NextInt64(long.MinValue, long.MaxValue)).ShouldContain(x => x > int.MaxValue); + } + + [Fact] + public void SmallRangesAreRoughlyUniform() { + var histogram = Draw(100_000, r => r.Next(10)).GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count()); + + histogram.Keys.Order().ShouldBe(Enumerable.Range(0, 10)); + histogram.Values.ShouldAllBe(count => count > 9_500 && count < 10_500); + } + + [Fact] + public void ARangeJustAbovePowerOfTwoIsRoughlyUniform() { + // 129 forces rejection sampling with a 255 mask + var histogram = Draw(129_000, r => r.Next(129)).GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count()); + + histogram.Count.ShouldBe(129); + histogram.Values.ShouldAllBe(count => count > 800 && count < 1200); + } + + [Fact] + public void InvalidArgumentsThrow() { + var random = new StableRandom(1); + + Should.Throw(() => random.Next(-1)); + Should.Throw(() => random.Next(2, 1)); + Should.Throw(() => random.NextInt64(-1)); + Should.Throw(() => random.NextInt64(2, 1)); + Should.Throw(() => random.NextBytes((byte[]) null!)); + } + + [Fact] + public void ShuffleUsesTheStableStream() { + var first = Enumerable.Range(0, 20).ToArray(); + var second = Enumerable.Range(0, 20).ToArray(); + + new StableRandom(8).Shuffle(first); + new StableRandom(8).Shuffle(second); + + first.ShouldBe(second); + first.ShouldNotBe(Enumerable.Range(0, 20)); + } +} diff --git a/holonsoft.AutoPoco.Tests/Integration.Tests/Complete/WhenUsingSessionSeeds.cs b/holonsoft.AutoPoco.Tests/Integration.Tests/Complete/WhenUsingSessionSeeds.cs new file mode 100644 index 0000000..1eef39f --- /dev/null +++ b/holonsoft.AutoPoco.Tests/Integration.Tests/Complete/WhenUsingSessionSeeds.cs @@ -0,0 +1,159 @@ +using Shouldly; +using Xunit; +using holonsoft.AutoPoco.Configuration; +using holonsoft.AutoPoco.Configuration.Interfaces; +using holonsoft.AutoPoco.DataSources.Business; +using holonsoft.AutoPoco.DataSources.Primitives; +using holonsoft.AutoPoco.Engine; +using holonsoft.AutoPoco.Engine.Interfaces; +using holonsoft.AutoPoco.Extensions; +using holonsoft.AutoPoco.Tests.Common; + +namespace holonsoft.AutoPoco.Tests.Integration.Tests.Complete; + +public class WhenUsingSessionSeeds { + private static IGenerationSessionFactory CreateFactory(int? seed = null, bool withLastName = true) + => AutoPocoContainer.Configure(x => { + if (seed.HasValue) + x.UseSeed(seed.Value); + + var user = x.Include() + .Setup(c => c.First).Use() + .Setup(c => c.Second).Use() + .Setup(c => c.FirstName).Use(); + + if (withLastName) + user.Setup(c => c.LastName).Use(); + }); + + private static List Generate(IGenerationSession session, int count = 50) + => session.Collection(count).ToList(); + + private static List FirstNames(IGenerationSession session, int count = 50) + => Generate(session, count).Select(x => x.FirstName).ToList(); + + [Fact] + public void TwoSessionsOfTheSameFactoryProduceTheSameData() { + var factory = CreateFactory(); + + FirstNames(factory.CreateSession()).ShouldBe(FirstNames(factory.CreateSession())); + } + + [Fact] + public void TwoFactoriesWithTheSameSeedProduceTheSameData() + => FirstNames(CreateFactory(42).CreateSession()).ShouldBe(FirstNames(CreateFactory(42).CreateSession())); + + [Fact] + public void TheDefaultSeedIsTheLibraryDefault() { + FirstNames(CreateFactory().CreateSession()).ShouldBe(FirstNames(CreateFactory(AutoPocoDefaults.Seed).CreateSession())); + ((GenerationContext) CreateFactory().CreateSession()).Builders.Seed.ShouldBe(AutoPocoDefaults.Seed); + } + + [Fact] + public void ADifferentSeedProducesDifferentData() + => FirstNames(CreateFactory(1).CreateSession()).ShouldNotBe(FirstNames(CreateFactory(2).CreateSession())); + + [Fact] + public void ASessionSeedOverridesTheFactorySeed() { + var fromFactorySeed = FirstNames(CreateFactory(42).CreateSession()); + var fromSessionSeed = FirstNames(CreateFactory(7).CreateSession(AutoPocoDefaults.RecursionLimit, 42)); + + fromSessionSeed.ShouldBe(fromFactorySeed); + ((GenerationContext) CreateFactory(7).CreateSession(3, 42)).Builders.Seed.ShouldBe(42); + } + + [Fact] + public void TwoMembersWithTheSameSourceTypeGetDifferentValues() { + var items = Generate(CreateFactory().CreateSession()); + + items.Select(x => x.FirstName).ShouldNotBe(items.Select(x => x.LastName)); + } + + [Fact] + public void NullableMembersDoNotBecomeNullTogether() { + var items = Generate(CreateFactory().CreateSession(), 300); + + items.Count(x => x.First == null).ShouldBeInRange(20, 80); + items.Count(x => x.Second == null).ShouldBeInRange(20, 80); + items.ShouldContain(x => x.First == null && x.Second != null); + items.ShouldContain(x => x.First != null && x.Second == null); + } + + [Fact] + public void TheValuesOfAMemberDoNotDependOnOtherMembers() + => FirstNames(CreateFactory(withLastName: false).CreateSession()).ShouldBe(FirstNames(CreateFactory(withLastName: true).CreateSession())); + + [Fact] + public void AnExplicitSeedInTheConfigurationWinsOverTheSessionSeed() { + var session = AutoPocoContainer.Configure(x => { + x.UseSeed(99); + x.Include() + .Setup(c => c.First).Use(s => s.SetSeedToRandomValue(7)); + }).CreateSession(); + var standalone = new NullableIntegerSource(); + standalone.SetSeedToRandomValue(7); + + Generate(session, 10).Select(x => x.First).ShouldBe(Enumerable.Range(0, 10).Select(_ => standalone.Next(null))); + } + + [Fact] + public void AGenerationTimeSourceSharedByAListIsSeededOnceAndKeepsRunning() { + var names = CreateFactory().CreateSession() + .List(20) + .Source(x => x.LastName, new LastNameSource()) + .Get() + .Select(x => x.LastName) + .ToList(); + + names.Distinct().Count().ShouldBeGreaterThan(5); + } + + [Fact] + public void ARandomSelectionIsTheSameForTheSameSeed() { + static List SelectedIndexes(IGenerationSession session) { + var items = session.List(30) + .Random(10).Impose(x => x.First, -1) + .All() + .Get(); + return items.Select((x, i) => (x, i)).Where(t => t.x.First == -1).Select(t => t.i).ToList(); + } + + SelectedIndexes(CreateFactory(5).CreateSession()).ShouldBe(SelectedIndexes(CreateFactory(5).CreateSession())); + SelectedIndexes(CreateFactory(5).CreateSession()).ShouldNotBe(SelectedIndexes(CreateFactory(6).CreateSession())); + } + + [Fact] + public void ConstructorArgumentsFollowTheSeedAsWell() { + static List Names(int seed) + => AutoPocoContainer.Configure(x => { + x.UseSeed(seed); + x.Include().Setup(c => c.Name).Use(); + }).CreateSession().Collection(20).Select(r => r.Name).ToList(); + + Names(1).ShouldBe(Names(1)); + Names(1).ShouldNotBe(Names(2)); + } + + [Fact] + public void NullableAnnotationPatternsFollowTheSeed() { + static List NullPattern(int seed) + => AutoPocoContainer.Configure(x => { + x.UseSeed(seed); + x.RespectNullableAnnotations(50); + x.Include().Setup(c => c.FirstName).Use(); + }).CreateSession().Collection(40).Select(r => r.FirstName == null).ToList(); + + NullPattern(1).ShouldBe(NullPattern(1)); + NullPattern(1).ShouldNotBe(NullPattern(2)); + } + + [Fact] + public void TheConfigurationBuilderStoresTheSeed() { + var builder = new EngineConfigurationBuilder(); + builder.Seed.ShouldBe(AutoPocoDefaults.Seed); + + ((IEngineConfigurationBuilder) builder).UseSeed(4711); + + builder.Seed.ShouldBe(4711); + } +} diff --git a/holonsoft.AutoPoco/AutoPocoContainer.cs b/holonsoft.AutoPoco/AutoPocoContainer.cs index 2221d97..66c8aaa 100644 --- a/holonsoft.AutoPoco/AutoPocoContainer.cs +++ b/holonsoft.AutoPoco/AutoPocoContainer.cs @@ -33,7 +33,8 @@ public static IGenerationSessionFactory Configure(Action diff --git a/holonsoft.AutoPoco/Configuration/AutoPocoDefaults.cs b/holonsoft.AutoPoco/Configuration/AutoPocoDefaults.cs new file mode 100644 index 0000000..fb6cdbc --- /dev/null +++ b/holonsoft.AutoPoco/Configuration/AutoPocoDefaults.cs @@ -0,0 +1,23 @@ +namespace holonsoft.AutoPoco.Configuration; + +/// +/// Library defaults. Read-only on purpose: a session seed is configured with UseSeed on the +/// configuration builder or CreateSession(recursionLimit, seed), null thresholds per source or +/// with RespectNullableAnnotations(threshold). +/// +public static class AutoPocoDefaults { + /// + /// Seed of every factory and of every data source used standalone. + /// + public const int Seed = 1337; + + /// + /// Probability in percent that a nullable source returns null. + /// + public const int NullCreationThreshold = 15; + + /// + /// Recursion limit of a session created without one. + /// + public const int RecursionLimit = 5; +} diff --git a/holonsoft.AutoPoco/Configuration/AutoPocoGlobalSettings.cs b/holonsoft.AutoPoco/Configuration/AutoPocoGlobalSettings.cs deleted file mode 100644 index 19febaf..0000000 --- a/holonsoft.AutoPoco/Configuration/AutoPocoGlobalSettings.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace holonsoft.AutoPoco.Configuration; -public static class AutoPocoGlobalSettings { - public static int NullCreationThreshold { get; set; } = 15; - - public static int StandardSeed { get; set; } = 1337; -} diff --git a/holonsoft.AutoPoco/Configuration/EngineConfigurationBuilder.cs b/holonsoft.AutoPoco/Configuration/EngineConfigurationBuilder.cs index 7b5003c..e3a76bd 100644 --- a/holonsoft.AutoPoco/Configuration/EngineConfigurationBuilder.cs +++ b/holonsoft.AutoPoco/Configuration/EngineConfigurationBuilder.cs @@ -11,6 +11,8 @@ public class EngineConfigurationBuilder : IEngineConfigurationBuilder, IEngineCo public NullableAnnotationSettings NullableAnnotations { get; private set; } = NullableAnnotationSettings.Disabled; + public int Seed { get; private set; } = AutoPocoDefaults.Seed; + public IEngineConfigurationTypeBuilder Include() { // Create the configuration var configuration = new EngineConfigurationTypeBuilder(); @@ -44,4 +46,7 @@ public IEnumerable GetConfigurationTypes() public void RespectNullableAnnotations(int? nullCreationThreshold = null) => NullableAnnotations = NullableAnnotationSettings.Enabled(nullCreationThreshold); + + public void UseSeed(int seed) + => Seed = seed; } \ No newline at end of file diff --git a/holonsoft.AutoPoco/Configuration/Interfaces/IEngineConfigurationBuilder.cs b/holonsoft.AutoPoco/Configuration/Interfaces/IEngineConfigurationBuilder.cs index 5d85ebd..5959d22 100644 --- a/holonsoft.AutoPoco/Configuration/Interfaces/IEngineConfigurationBuilder.cs +++ b/holonsoft.AutoPoco/Configuration/Interfaces/IEngineConfigurationBuilder.cs @@ -29,6 +29,13 @@ public interface IEngineConfigurationBuilder { /// (string?, int?, ...) becomes null with the given probability, whatever its data source. /// Off by default. Members without a nullable annotation and values set by Impose are never touched. /// - /// probability in percent (0 to 100), default is + /// probability in percent (0 to 100), default is void RespectNullableAnnotations(int? nullCreationThreshold = null); + + /// + /// Sets the seed of the factory. Every session created from it, and every data source in those sessions, + /// derives its random stream from this seed, so the same seed produces the same data in every run. + /// Default is . + /// + void UseSeed(int seed); } \ No newline at end of file diff --git a/holonsoft.AutoPoco/DataSources/Business/CompanySource.cs b/holonsoft.AutoPoco/DataSources/Business/CompanySource.cs index 3ba715f..2c15f34 100644 --- a/holonsoft.AutoPoco/DataSources/Business/CompanySource.cs +++ b/holonsoft.AutoPoco/DataSources/Business/CompanySource.cs @@ -141,7 +141,7 @@ public CompanySource() : base() { } } public class NullableCompanySource : CompanySourceBase { - public NullableCompanySource() : base(AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableCompanySource() : base(AutoPocoDefaults.NullCreationThreshold) { } public NullableCompanySource(int nullCreationThreshold) : base(nullCreationThreshold) { } } \ No newline at end of file diff --git a/holonsoft.AutoPoco/DataSources/Business/CreditCardSource.cs b/holonsoft.AutoPoco/DataSources/Business/CreditCardSource.cs index 75b57c5..a195a30 100644 --- a/holonsoft.AutoPoco/DataSources/Business/CreditCardSource.cs +++ b/holonsoft.AutoPoco/DataSources/Business/CreditCardSource.cs @@ -120,9 +120,9 @@ public CreditCardSource() : this(CreditCardType.Random) { } } public class NullableCreditCardSource(CreditCardType creditCardType, int nullCreationThreshold) : CreditCardSourceBase(creditCardType, nullCreationThreshold) { - public NullableCreditCardSource() : this(CreditCardType.Random, AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableCreditCardSource() : this(CreditCardType.Random, AutoPocoDefaults.NullCreationThreshold) { } - public NullableCreditCardSource(CreditCardType creditCardType) : this(creditCardType, AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableCreditCardSource(CreditCardType creditCardType) : this(creditCardType, AutoPocoDefaults.NullCreationThreshold) { } public NullableCreditCardSource(int nullCreationThreshold) : this(CreditCardType.Random, nullCreationThreshold) { } } diff --git a/holonsoft.AutoPoco/DataSources/Business/EmailAddressSource.cs b/holonsoft.AutoPoco/DataSources/Business/EmailAddressSource.cs index 718964b..3f1c1e3 100644 --- a/holonsoft.AutoPoco/DataSources/Business/EmailAddressSource.cs +++ b/holonsoft.AutoPoco/DataSources/Business/EmailAddressSource.cs @@ -27,9 +27,9 @@ public EmailAddressSource() : this("eg", "example.test") { } } public class NullableEmailAddressSource(string namePartPrefix, string domain, int nullCreationThreshold) : EmailAddressSourceBase(namePartPrefix, domain, nullCreationThreshold) { - public NullableEmailAddressSource() : this("eg", "example.test", AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableEmailAddressSource() : this("eg", "example.test", AutoPocoDefaults.NullCreationThreshold) { } - public NullableEmailAddressSource(string namePartPrefix, string domain) : this(namePartPrefix, domain, AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableEmailAddressSource(string namePartPrefix, string domain) : this(namePartPrefix, domain, AutoPocoDefaults.NullCreationThreshold) { } public NullableEmailAddressSource(int nullCreationThreshold) : this("eg", "example.test", nullCreationThreshold) { } } diff --git a/holonsoft.AutoPoco/DataSources/Business/ExtendedEmailAddressSource.cs b/holonsoft.AutoPoco/DataSources/Business/ExtendedEmailAddressSource.cs index e8e6f26..ce64ec9 100644 --- a/holonsoft.AutoPoco/DataSources/Business/ExtendedEmailAddressSource.cs +++ b/holonsoft.AutoPoco/DataSources/Business/ExtendedEmailAddressSource.cs @@ -78,7 +78,7 @@ string GetDomain() { public class ExtendedEmailAddressSource(params string[] domains) : ExtendedEmailAddressSourceBase(null, domains) { } public class NullableExtendedEmailAddressSource(int nullCreationThreshold, params string[] domains) : ExtendedEmailAddressSourceBase(nullCreationThreshold, domains) { - public NullableExtendedEmailAddressSource() : this(AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableExtendedEmailAddressSource() : this(AutoPocoDefaults.NullCreationThreshold) { } - public NullableExtendedEmailAddressSource(params string[] domains) : this(AutoPocoGlobalSettings.NullCreationThreshold, domains) { } + public NullableExtendedEmailAddressSource(params string[] domains) : this(AutoPocoDefaults.NullCreationThreshold, domains) { } } diff --git a/holonsoft.AutoPoco/DataSources/Business/FirstNameSource.cs b/holonsoft.AutoPoco/DataSources/Business/FirstNameSource.cs index 9bc2a73..fdb1c82 100644 --- a/holonsoft.AutoPoco/DataSources/Business/FirstNameSource.cs +++ b/holonsoft.AutoPoco/DataSources/Business/FirstNameSource.cs @@ -27,7 +27,7 @@ public abstract class FirstNameSourceBase(int? nullCreationThreshold = null) : F public class FirstNameSource : FirstNameSourceBase { } public class NullableFirstNameSource : FirstNameSourceBase { - public NullableFirstNameSource() : base(AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableFirstNameSource() : base(AutoPocoDefaults.NullCreationThreshold) { } public NullableFirstNameSource(int nullCreationThreshold) : base(nullCreationThreshold) { } } \ No newline at end of file diff --git a/holonsoft.AutoPoco/DataSources/Business/LastNameSource.cs b/holonsoft.AutoPoco/DataSources/Business/LastNameSource.cs index 47ce583..a639f1e 100644 --- a/holonsoft.AutoPoco/DataSources/Business/LastNameSource.cs +++ b/holonsoft.AutoPoco/DataSources/Business/LastNameSource.cs @@ -131,7 +131,7 @@ public abstract class LastNameSourceBase(int? nullCreationThreshold = null) : Fi public class LastNameSource : LastNameSourceBase { } public class NullableLastNameSource : LastNameSourceBase { - public NullableLastNameSource() : base(AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableLastNameSource() : base(AutoPocoDefaults.NullCreationThreshold) { } public NullableLastNameSource(int nullCreationThreshold) : base(nullCreationThreshold) { } } \ No newline at end of file diff --git a/holonsoft.AutoPoco/DataSources/Business/LoremIpsumSource.cs b/holonsoft.AutoPoco/DataSources/Business/LoremIpsumSource.cs index 9cc60f5..aa3b824 100644 --- a/holonsoft.AutoPoco/DataSources/Business/LoremIpsumSource.cs +++ b/holonsoft.AutoPoco/DataSources/Business/LoremIpsumSource.cs @@ -34,5 +34,5 @@ public LoremIpsumSource() : this(1) { } } public class NullableLoremIpsumSource(int count, int nullCreationThreshold) : LoremIpsumSourceBase(count, nullCreationThreshold) { - public NullableLoremIpsumSource() : this(1, AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableLoremIpsumSource() : this(1, AutoPocoDefaults.NullCreationThreshold) { } } \ No newline at end of file diff --git a/holonsoft.AutoPoco/DataSources/Business/RandomTextSource.cs b/holonsoft.AutoPoco/DataSources/Business/RandomTextSource.cs index 31fc8ab..cc36e93 100644 --- a/holonsoft.AutoPoco/DataSources/Business/RandomTextSource.cs +++ b/holonsoft.AutoPoco/DataSources/Business/RandomTextSource.cs @@ -62,5 +62,5 @@ public RandomTextSource() : this(500, 3, 6, 3, 7, "ABCDEFGHIJKLMNOPQRSTUVWXYZabc public class NullableRandomTextSource(int? nullCreationThreshold, int maxLengthOfText, int minParagraphCount, int maxParagraphCount, int minSentenceCount, int maxSentenceCount, params char[] allowedCharacters) : RandomTextSourceBase(nullCreationThreshold, maxLengthOfText, minParagraphCount, maxParagraphCount, minSentenceCount, maxSentenceCount, allowedCharacters) { - public NullableRandomTextSource() : this(AutoPocoGlobalSettings.NullCreationThreshold, 500, 3, 6, 3, 7, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray()) { } + public NullableRandomTextSource() : this(AutoPocoDefaults.NullCreationThreshold, 500, 3, 6, 3, 7, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray()) { } } \ No newline at end of file diff --git a/holonsoft.AutoPoco/DataSources/Business/RandomUtfTextSource.cs b/holonsoft.AutoPoco/DataSources/Business/RandomUtfTextSource.cs index 38e6c4c..5fd4a2c 100644 --- a/holonsoft.AutoPoco/DataSources/Business/RandomUtfTextSource.cs +++ b/holonsoft.AutoPoco/DataSources/Business/RandomUtfTextSource.cs @@ -59,7 +59,7 @@ public RandomUtfTextSource() : this(500, 3, 6, 3, 7) { } public class NullableRandomUtfTextSource(int? nullCreationThreshold, int maxLengthOfText, int minParagraphCount, int maxParagraphCount, int minSentenceCount, int maxSentenceCount, IReadOnlySet? mayExcludeCategories = null) : RandomUtfTextSourceBase(nullCreationThreshold, maxLengthOfText, minParagraphCount, maxParagraphCount, minSentenceCount, maxSentenceCount, mayExcludeCategories) { - public NullableRandomUtfTextSource() : this(AutoPocoGlobalSettings.NullCreationThreshold, 500, 3, 6, 3, 7) { } + public NullableRandomUtfTextSource() : this(AutoPocoDefaults.NullCreationThreshold, 500, 3, 6, 3, 7) { } } public record UnicodeBlock(int Start, int End, int CharCount, int ExistingChars) { diff --git a/holonsoft.AutoPoco/DataSources/Business/UrlSource.cs b/holonsoft.AutoPoco/DataSources/Business/UrlSource.cs index efc0154..09de783 100644 --- a/holonsoft.AutoPoco/DataSources/Business/UrlSource.cs +++ b/holonsoft.AutoPoco/DataSources/Business/UrlSource.cs @@ -32,5 +32,5 @@ protected override string GetNextValue(IGenerationContext? context) { public class UrlSource : UrlSourceBase { } public class NullableUrlSource(int nullCreationThreshold) : UrlSourceBase(nullCreationThreshold) { - public NullableUrlSource() : this(AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableUrlSource() : this(AutoPocoDefaults.NullCreationThreshold) { } } \ No newline at end of file diff --git a/holonsoft.AutoPoco/DataSources/Country/CapitalSource.cs b/holonsoft.AutoPoco/DataSources/Country/CapitalSource.cs index a56038b..6ad1db3 100644 --- a/holonsoft.AutoPoco/DataSources/Country/CapitalSource.cs +++ b/holonsoft.AutoPoco/DataSources/Country/CapitalSource.cs @@ -156,7 +156,7 @@ public CapitalSource() : base() { } /// Result can be NULL, too /// public class NullableCapitalSource : CapitalSourceBase { - public NullableCapitalSource() : base(AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableCapitalSource() : base(AutoPocoDefaults.NullCreationThreshold) { } public NullableCapitalSource(int nullCreationThreshold) : base(nullCreationThreshold) { } } diff --git a/holonsoft.AutoPoco/DataSources/Country/CitySource.cs b/holonsoft.AutoPoco/DataSources/Country/CitySource.cs index 50acf76..ffc4dfb 100644 --- a/holonsoft.AutoPoco/DataSources/Country/CitySource.cs +++ b/holonsoft.AutoPoco/DataSources/Country/CitySource.cs @@ -110,7 +110,7 @@ public CitySource() : base() { } /// Result can be NULL, too /// public class NullableCitySource : CitySourceBase { - public NullableCitySource() : base(AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableCitySource() : base(AutoPocoDefaults.NullCreationThreshold) { } public NullableCitySource(int nullCreationThreshold) : base(nullCreationThreshold) { } } diff --git a/holonsoft.AutoPoco/DataSources/Country/CountryFromCultureListSource.cs b/holonsoft.AutoPoco/DataSources/Country/CountryFromCultureListSource.cs index 7a374de..f58699f 100644 --- a/holonsoft.AutoPoco/DataSources/Country/CountryFromCultureListSource.cs +++ b/holonsoft.AutoPoco/DataSources/Country/CountryFromCultureListSource.cs @@ -45,7 +45,7 @@ public CountryFromCultureListSource() : base() { } /// Result can be NULL, too /// public class NullableCountryFromCultureListSource : CountryFromCultureListSourceBase { - public NullableCountryFromCultureListSource() : base(AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableCountryFromCultureListSource() : base(AutoPocoDefaults.NullCreationThreshold) { } public NullableCountryFromCultureListSource(int nullCreationThreshold) : base(nullCreationThreshold) { } } diff --git a/holonsoft.AutoPoco/DataSources/Country/CountrySource.cs b/holonsoft.AutoPoco/DataSources/Country/CountrySource.cs index b9bcfc2..4ec13fa 100644 --- a/holonsoft.AutoPoco/DataSources/Country/CountrySource.cs +++ b/holonsoft.AutoPoco/DataSources/Country/CountrySource.cs @@ -218,7 +218,7 @@ public CountrySource() : this(false) { } /// Result can be NULL, too /// public class NullableCountrySource(bool useAbbreviations, int nullCreationThreshold) : CountrySourceBase(useAbbreviations, nullCreationThreshold) { - public NullableCountrySource() : this(false, AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableCountrySource() : this(false, AutoPocoDefaults.NullCreationThreshold) { } - public NullableCountrySource(bool useAbbreviations) : this(useAbbreviations, AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableCountrySource(bool useAbbreviations) : this(useAbbreviations, AutoPocoDefaults.NullCreationThreshold) { } } diff --git a/holonsoft.AutoPoco/DataSources/Country/DutchStatesSource.cs b/holonsoft.AutoPoco/DataSources/Country/DutchStatesSource.cs index cc9a5e1..87d1a19 100644 --- a/holonsoft.AutoPoco/DataSources/Country/DutchStatesSource.cs +++ b/holonsoft.AutoPoco/DataSources/Country/DutchStatesSource.cs @@ -30,5 +30,5 @@ public DutchStatesSource() : this(false) { } } public class NullableDutchStatesSource(bool useAbbreviations, int nullCreationThreshold) : DutchStatesSourceBase(useAbbreviations, nullCreationThreshold) { - public NullableDutchStatesSource() : this(false, AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableDutchStatesSource() : this(false, AutoPocoDefaults.NullCreationThreshold) { } } \ No newline at end of file diff --git a/holonsoft.AutoPoco/DataSources/Country/GermanStatesSource.cs b/holonsoft.AutoPoco/DataSources/Country/GermanStatesSource.cs index b21083e..e9555c0 100644 --- a/holonsoft.AutoPoco/DataSources/Country/GermanStatesSource.cs +++ b/holonsoft.AutoPoco/DataSources/Country/GermanStatesSource.cs @@ -34,5 +34,5 @@ public GermanStatesSource() : this(false) { } } public class NullableGermanStatesSource(bool useAbbreviations, int nullCreationThreshold) : GermanStatesSourceBase(useAbbreviations, nullCreationThreshold) { - public NullableGermanStatesSource() : this(false, AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableGermanStatesSource() : this(false, AutoPocoDefaults.NullCreationThreshold) { } } diff --git a/holonsoft.AutoPoco/DataSources/Country/PostalZipCodeGermanySource.cs b/holonsoft.AutoPoco/DataSources/Country/PostalZipCodeGermanySource.cs index 39135cb..f354ccf 100644 --- a/holonsoft.AutoPoco/DataSources/Country/PostalZipCodeGermanySource.cs +++ b/holonsoft.AutoPoco/DataSources/Country/PostalZipCodeGermanySource.cs @@ -98,7 +98,7 @@ public PostalZipCodeGermanySource() : base() { } } public class NullablePostalZipCodeGermanySource : PostalZipCodeGermanySourceBase { - public NullablePostalZipCodeGermanySource() : base(AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullablePostalZipCodeGermanySource() : base(AutoPocoDefaults.NullCreationThreshold) { } public NullablePostalZipCodeGermanySource(int nullCreationThreshold) : base(nullCreationThreshold) { } } diff --git a/holonsoft.AutoPoco/DataSources/Country/PostalZipCodeNetherlandsSource.cs b/holonsoft.AutoPoco/DataSources/Country/PostalZipCodeNetherlandsSource.cs index 7cadfe2..f096157 100644 --- a/holonsoft.AutoPoco/DataSources/Country/PostalZipCodeNetherlandsSource.cs +++ b/holonsoft.AutoPoco/DataSources/Country/PostalZipCodeNetherlandsSource.cs @@ -114,7 +114,7 @@ public PostalZipCodeNetherlandsSource() : base() { } } public class NullablePostalZipCodeNetherlandsSource : PostalZipCodeNetherlandsSourceBase { - public NullablePostalZipCodeNetherlandsSource() : base(AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullablePostalZipCodeNetherlandsSource() : base(AutoPocoDefaults.NullCreationThreshold) { } public NullablePostalZipCodeNetherlandsSource(int nullCreationThreshold) : base(nullCreationThreshold) { } } diff --git a/holonsoft.AutoPoco/DataSources/Country/PostalZipCodeUSASource.cs b/holonsoft.AutoPoco/DataSources/Country/PostalZipCodeUSASource.cs index 123981e..e4cd5c9 100644 --- a/holonsoft.AutoPoco/DataSources/Country/PostalZipCodeUSASource.cs +++ b/holonsoft.AutoPoco/DataSources/Country/PostalZipCodeUSASource.cs @@ -114,7 +114,7 @@ public PostalZipCodeUSASource() : base() { } } public class NullablePostalZipCodeUSASource : PostalZipCodeUSASourceBase { - public NullablePostalZipCodeUSASource() : base(AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullablePostalZipCodeUSASource() : base(AutoPocoDefaults.NullCreationThreshold) { } public NullablePostalZipCodeUSASource(int nullCreationThreshold) : base(nullCreationThreshold) { } } \ No newline at end of file diff --git a/holonsoft.AutoPoco/DataSources/Country/UsStatesSource.cs b/holonsoft.AutoPoco/DataSources/Country/UsStatesSource.cs index 613a0e0..071d7db 100644 --- a/holonsoft.AutoPoco/DataSources/Country/UsStatesSource.cs +++ b/holonsoft.AutoPoco/DataSources/Country/UsStatesSource.cs @@ -73,5 +73,5 @@ public USStatesSource() : this(false) { } } public class NullableUSStatesSource(bool useAbbreviations, int nullCreationThreshold) : USStatesSourceBase(useAbbreviations, nullCreationThreshold) { - public NullableUSStatesSource() : this(false, AutoPocoGlobalSettings.NullCreationThreshold) { } + public NullableUSStatesSource() : this(false, AutoPocoDefaults.NullCreationThreshold) { } } \ No newline at end of file diff --git a/holonsoft.AutoPoco/DataSources/Primitives/BooleanSource.cs b/holonsoft.AutoPoco/DataSources/Primitives/BooleanSource.cs index 396b384..800dec2 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/BooleanSource.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/BooleanSource.cs @@ -17,5 +17,5 @@ public class BooleanSource : BooleanSourceBase { } /// /// Create a source for nullable boolean values. /// -/// +/// public class NullableBooleanSource : BooleanSourceBase { } diff --git a/holonsoft.AutoPoco/DataSources/Primitives/ColorSource.cs b/holonsoft.AutoPoco/DataSources/Primitives/ColorSource.cs index e5e7232..6c8f9b0 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/ColorSource.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/ColorSource.cs @@ -51,7 +51,7 @@ public ColorSource() /// /// /// -/// +/// public class NullableColorSource(byte rangeStart, byte rangeEnd) : ColorSourceBase(255, rangeStart, rangeEnd, rangeStart, rangeEnd, rangeStart, rangeEnd) { public NullableColorSource() : this(0, 255) { } diff --git a/holonsoft.AutoPoco/DataSources/Primitives/DateOnlySource.cs b/holonsoft.AutoPoco/DataSources/Primitives/DateOnlySource.cs index 6a9577d..791dd30 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/DateOnlySource.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/DateOnlySource.cs @@ -42,7 +42,7 @@ public DateOnlySource() : this(DateOnly.MinValue, DateOnly.MaxValue) { } /// /// Minimum date /// Maximum date -/// +/// public class NullableDateOnlySource(DateOnly minDate, DateOnly maxDate) : DateOnlySourceBase(minDate, maxDate) { public NullableDateOnlySource() : this(DateOnly.MinValue, DateOnly.MaxValue) { } } diff --git a/holonsoft.AutoPoco/DataSources/Primitives/DateTimeSource.cs b/holonsoft.AutoPoco/DataSources/Primitives/DateTimeSource.cs index b1be6f8..87ede5c 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/DateTimeSource.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/DateTimeSource.cs @@ -77,7 +77,7 @@ public DateTimeSource() /// /// minimum date /// maximum date -/// +/// public class NullableDateTimeSource(DateTime minDate, DateTime maxDate) : DateTimeSourceBase(minDate, maxDate) { public NullableDateTimeSource() : this(DateTime.MinValue, DateTime.MaxValue) { } diff --git a/holonsoft.AutoPoco/DataSources/Primitives/DecimalSource.cs b/holonsoft.AutoPoco/DataSources/Primitives/DecimalSource.cs index 4ef010a..bd5f53f 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/DecimalSource.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/DecimalSource.cs @@ -53,7 +53,7 @@ public DecimalSource() /// Minimum value /// Maximum value /// Count of decimals -/// +/// public class NullableDecimalSource(decimal min, decimal max, int? decimals = null) : DecimalSourceBase(min, max, decimals) { public NullableDecimalSource() : this(decimal.MinValue, decimal.MaxValue, null) { } diff --git a/holonsoft.AutoPoco/DataSources/Primitives/EnumSource.cs b/holonsoft.AutoPoco/DataSources/Primitives/EnumSource.cs index 2dc8625..07bbb06 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/EnumSource.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/EnumSource.cs @@ -21,5 +21,5 @@ public class EnumSource : EnumSourceBase //public class NullableEnumSource : EnumSourceBase // where T : Enum { -// public NullableEnumSource() : base(AutoPocoGlobalSettings.NullCreationThreshold) { } +// public NullableEnumSource() : base(AutoPocoDefaults.NullCreationThreshold) { } //} \ No newline at end of file diff --git a/holonsoft.AutoPoco/DataSources/Primitives/FloatSource.cs b/holonsoft.AutoPoco/DataSources/Primitives/FloatSource.cs index ebd652b..51a8f83 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/FloatSource.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/FloatSource.cs @@ -56,7 +56,7 @@ public FloatSource(int decimals) /// Minimum value /// Maximum value /// Count of decimals -/// +/// public class NullableFloatSource(float min, float max, int? decimals) : FloatSourceBase(min, max, decimals) { public NullableFloatSource() : this(float.MinValue, float.MaxValue, null) { } diff --git a/holonsoft.AutoPoco/DataSources/Primitives/GuidSource.cs b/holonsoft.AutoPoco/DataSources/Primitives/GuidSource.cs index 6f0de7a..7436a1d 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/GuidSource.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/GuidSource.cs @@ -21,5 +21,5 @@ public class GuidSource : GuidSourceBase { } /// /// Create a nullable GUID source /// -/// +/// public class NullableGuidSource : GuidSourceBase { } \ No newline at end of file diff --git a/holonsoft.AutoPoco/DataSources/Primitives/Int128Source.cs b/holonsoft.AutoPoco/DataSources/Primitives/Int128Source.cs index 8278038..616be75 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/Int128Source.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/Int128Source.cs @@ -48,7 +48,7 @@ public Int128Source() /// /// Minimum value /// Maximum value -/// +/// public class NullableInt128Source(Int128 min, Int128 max) : Int128SourceBase(min, max) { public NullableInt128Source() : this(Int128.MinValue, Int128.MaxValue) { } diff --git a/holonsoft.AutoPoco/DataSources/Primitives/IntegerSource.cs b/holonsoft.AutoPoco/DataSources/Primitives/IntegerSource.cs index 49e9a18..c5df226 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/IntegerSource.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/IntegerSource.cs @@ -33,7 +33,7 @@ public IntegerSource() /// /// Minimum value /// Maximum value -/// +/// public class NullableIntegerSource(int min, int max) : IntegerSourceBase(min, max) { public NullableIntegerSource() : this(int.MinValue, int.MaxValue) { } diff --git a/holonsoft.AutoPoco/DataSources/Primitives/LongSource.cs b/holonsoft.AutoPoco/DataSources/Primitives/LongSource.cs index b792b4a..81f0a4f 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/LongSource.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/LongSource.cs @@ -32,7 +32,7 @@ public LongSource() /// /// Minimum value /// Maximum value -/// +/// public class NullableLongSource(long min, long max) : LongSourceBase(min, max) { public NullableLongSource() : this(long.MinValue, long.MaxValue) { } diff --git a/holonsoft.AutoPoco/DataSources/Primitives/NumberSource.cs b/holonsoft.AutoPoco/DataSources/Primitives/NumberSource.cs index f6ee358..6f0e59e 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/NumberSource.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/NumberSource.cs @@ -153,7 +153,7 @@ public NumberSource() /// Minimum value, inclusive /// Maximum value, inclusive for integer types /// -/// Probability of null in percent, or null for the default, see +/// Probability of null in percent, or null for the default, see /// public class NullableNumberSource(T min, T max, int? nullCreationThreshold) : NumberSourceBase(min, max, nullCreationThreshold) where T : struct, INumber, IMinMaxValue { diff --git a/holonsoft.AutoPoco/DataSources/Primitives/RandomStringSource.cs b/holonsoft.AutoPoco/DataSources/Primitives/RandomStringSource.cs index 6ebca3a..4c83ba1 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/RandomStringSource.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/RandomStringSource.cs @@ -77,7 +77,7 @@ public RandomStringSource(int minLength, int maxLength, char[] allowedChars) /// Create a nullable random string source. /// Default: char set between 65 and 123, min length 5 and max length 10 /// -/// +/// public class NullableRandomStringSource : RandomStringSourceBase { public NullableRandomStringSource() : this(5, 10, 'A', 'z') { } @@ -86,13 +86,13 @@ public NullableRandomStringSource(int minLength, int maxLength) : this(minLength, maxLength, (char) 65, (char) 123) { } public NullableRandomStringSource(int minLength, int maxLength, char minChar, char maxChar) - : base(minLength, maxLength, minChar, maxChar, AutoPocoGlobalSettings.NullCreationThreshold) { } + : base(minLength, maxLength, minChar, maxChar, AutoPocoDefaults.NullCreationThreshold) { } public NullableRandomStringSource(int minLength, int maxLength, char minChar, char maxChar, int? nullCreationThreshold) : base(minLength, maxLength, minChar, maxChar, nullCreationThreshold) { } public NullableRandomStringSource(int minLength, int maxLength, char[] allowedChars) - : base(minLength, maxLength, AutoPocoGlobalSettings.NullCreationThreshold, allowedChars) { } + : base(minLength, maxLength, AutoPocoDefaults.NullCreationThreshold, allowedChars) { } public NullableRandomStringSource(int minLength, int maxLength, char[] allowedChars, int? nullCreationThreshold) : base(minLength, maxLength, nullCreationThreshold, allowedChars) { } diff --git a/holonsoft.AutoPoco/DataSources/Primitives/TimeOnlySourceBase.cs b/holonsoft.AutoPoco/DataSources/Primitives/TimeOnlySourceBase.cs index 29b0413..6a9b387 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/TimeOnlySourceBase.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/TimeOnlySourceBase.cs @@ -33,7 +33,7 @@ public TimeOnlySource() : this(TimeOnly.MinValue, TimeOnly.MaxValue) { } /// /// /// -/// +/// public class NullableTimeOnlySource(TimeOnly minTime, TimeOnly maxTime) : TimeOnlySourceBase(minTime, maxTime) { public NullableTimeOnlySource() : this(TimeOnly.MinValue, TimeOnly.MaxValue) { } } diff --git a/holonsoft.AutoPoco/DataSources/Primitives/TimeSpanSource.cs b/holonsoft.AutoPoco/DataSources/Primitives/TimeSpanSource.cs index ec40e1c..14ff41e 100644 --- a/holonsoft.AutoPoco/DataSources/Primitives/TimeSpanSource.cs +++ b/holonsoft.AutoPoco/DataSources/Primitives/TimeSpanSource.cs @@ -52,7 +52,7 @@ public TimeSpanSource() /// /// Minimum value /// maximum value -/// +/// public class NullableTimeSpanSource(TimeSpan minTimeSpan, TimeSpan maxTimeSpan) : TimeSpanSourceBase(minTimeSpan, maxTimeSpan) { public NullableTimeSpanSource() diff --git a/holonsoft.AutoPoco/Engine/CollectionContext.cs b/holonsoft.AutoPoco/Engine/CollectionContext.cs index fc4633a..cdf3262 100644 --- a/holonsoft.AutoPoco/Engine/CollectionContext.cs +++ b/holonsoft.AutoPoco/Engine/CollectionContext.cs @@ -1,12 +1,15 @@ using System.Linq.Expressions; using holonsoft.AutoPoco.Configuration; using holonsoft.AutoPoco.Engine.Interfaces; +using holonsoft.AutoPoco.Util; namespace holonsoft.AutoPoco.Engine; -public class CollectionContext(IEnumerable> generators) +/// one generator per element +/// seed of the shuffle behind , the session seed when created by a session +public class CollectionContext(IEnumerable> generators, int seed = AutoPocoDefaults.Seed) : ICollectionContext where TCollection : ICollection { - private readonly Random _random = new(AutoPocoGlobalSettings.StandardSeed); + private readonly Random _random = new StableRandom(seed); public ICollectionContext Impose(Expression> propertyExpr, TMember value) { diff --git a/holonsoft.AutoPoco/Engine/DatasourceBase.cs b/holonsoft.AutoPoco/Engine/DatasourceBase.cs index e58e610..89ffd0b 100644 --- a/holonsoft.AutoPoco/Engine/DatasourceBase.cs +++ b/holonsoft.AutoPoco/Engine/DatasourceBase.cs @@ -1,18 +1,26 @@ using holonsoft.AutoPoco.Configuration; using holonsoft.AutoPoco.Engine.Interfaces; +using holonsoft.AutoPoco.Util; namespace holonsoft.AutoPoco.Engine; -public abstract class DataSourceBase : IDataSource, IRandomNullEvaluatorSupport { +public abstract class DataSourceBase : IDataSource, IRandomNullEvaluatorSupport, ISessionSeedable { + private bool _hasExplicitSeed; + private bool _sessionSeedApplied; + public IRandomNullEvaluator RandomNullEvaluator { get; set; } = new DefaultRandomNullEvaluator(); - protected Random Random { get; private set; } = new(AutoPocoGlobalSettings.StandardSeed); + /// + /// The random stream of this source, a seeded with + /// until the engine or reseeds it. + /// + protected Random Random { get; private set; } = new StableRandom(AutoPocoDefaults.Seed); protected DataSourceBase() { } /// /// Creates the source with an explicit null creation threshold (percent). - /// A null threshold keeps the default evaluator, see . + /// A null threshold keeps the default evaluator, see . /// protected DataSourceBase(int? nullCreationThreshold) { NullCreationThreshold = nullCreationThreshold; @@ -26,15 +34,32 @@ protected DataSourceBase(int? nullCreationThreshold) { /// protected int? NullCreationThreshold { get; } - public virtual void SetSeedToRandomValue() { - var seed = Guid.NewGuid().GetHashCode(); - Random = new(seed); - RandomNullEvaluator.SetSeedToRandomValue(seed); - } + /// + /// Reseeds with a value that differs on every call. The source is no longer repeatable afterwards. + /// + public virtual void SetSeedToRandomValue() + => SetSeedToRandomValue(Guid.NewGuid().GetHashCode()); + /// + /// Reseeds the random stream and the null evaluator. An explicit seed wins over the session seed of the engine. + /// public virtual void SetSeedToRandomValue(int seed) { - Random = new(seed); + Random = new StableRandom(seed); RandomNullEvaluator.SetSeedToRandomValue(seed); + _hasExplicitSeed = true; + } + + /// + /// Called by the engine with the seed derived for the member this source feeds. Applied once, + /// and only when no explicit seed was set. + /// + void ISessionSeedable.ApplySessionSeed(int seed) { + if (_hasExplicitSeed || _sessionSeedApplied) + return; + + _sessionSeedApplied = true; + SetSeedToRandomValue(seed); + _hasExplicitSeed = false; } public DataSourceBase SetNullCreationThreshold(int nullCreationThreshold) { diff --git a/holonsoft.AutoPoco/Engine/GenerationConfiguration.cs b/holonsoft.AutoPoco/Engine/GenerationConfiguration.cs index 4d0abd0..579692a 100644 --- a/holonsoft.AutoPoco/Engine/GenerationConfiguration.cs +++ b/holonsoft.AutoPoco/Engine/GenerationConfiguration.cs @@ -6,13 +6,15 @@ namespace holonsoft.AutoPoco.Engine; public class GenerationConfiguration(IEngineConfiguration configuration, IEngineConventionProvider conventionProvider, - int recursionLimit, NullableAnnotationSettings? nullableAnnotations = null) : IGenerationConfiguration { + int recursionLimit, NullableAnnotationSettings? nullableAnnotations = null, int seed = AutoPocoDefaults.Seed) : IGenerationConfiguration { private readonly List _objectBuilders = new(); public int RecursionLimit { get; } = recursionLimit; public NullableAnnotationSettings NullableAnnotations { get; } = nullableAnnotations ?? NullableAnnotationSettings.Disabled; + public int Seed { get; } = seed; + public IObjectBuilder GetBuilderForType(Type searchType) { var builder = _objectBuilders.SingleOrDefault(x => x.InnerType == searchType); builder ??= CreateBuilderForType(searchType); @@ -22,7 +24,7 @@ public IObjectBuilder GetBuilderForType(Type searchType) { private IObjectBuilder CreateBuilderForType(Type searchType) { EnsureTypeExists(searchType); var type = configuration.GetRegisteredType(searchType); - var builder = new ObjectBuilder(type!, NullableAnnotations); + var builder = new ObjectBuilder(type!, NullableAnnotations, Seed); _objectBuilders.Add(builder); return builder; } diff --git a/holonsoft.AutoPoco/Engine/GenerationContext.cs b/holonsoft.AutoPoco/Engine/GenerationContext.cs index 4d518fb..b03d1b4 100644 --- a/holonsoft.AutoPoco/Engine/GenerationContext.cs +++ b/holonsoft.AutoPoco/Engine/GenerationContext.cs @@ -1,4 +1,5 @@ -using holonsoft.AutoPoco.Engine.Enums; +using holonsoft.AutoPoco.Configuration; +using holonsoft.AutoPoco.Engine.Enums; using holonsoft.AutoPoco.Engine.Interfaces; namespace holonsoft.AutoPoco.Engine; @@ -29,7 +30,8 @@ public virtual IObjectGenerator Single() { public ICollectionContext> List(int count) => new CollectionContext>( Enumerable.Range(0, count) .Select(x => Single()).ToArray() - .AsEnumerable()); + .AsEnumerable(), + Builders?.Seed ?? AutoPocoDefaults.Seed); public TPoco Next() => Single().Get(); diff --git a/holonsoft.AutoPoco/Engine/GenerationSessionFactory.cs b/holonsoft.AutoPoco/Engine/GenerationSessionFactory.cs index 162cd95..9ef45a0 100644 --- a/holonsoft.AutoPoco/Engine/GenerationSessionFactory.cs +++ b/holonsoft.AutoPoco/Engine/GenerationSessionFactory.cs @@ -1,14 +1,28 @@ -using holonsoft.AutoPoco.Configuration.Interfaces; +using holonsoft.AutoPoco.Configuration; +using holonsoft.AutoPoco.Configuration.Interfaces; using holonsoft.AutoPoco.Engine.Interfaces; namespace holonsoft.AutoPoco.Engine; +/// the finished engine configuration +/// conventions for types requested without configuration +/// optional, see +/// seed of every session created without an explicit one public class GenerationSessionFactory(IEngineConfiguration config, IEngineConventionProvider conventionProvider, - NullableAnnotationSettings? nullableAnnotations = null) : IGenerationSessionFactory { + NullableAnnotationSettings? nullableAnnotations = null, int seed = AutoPocoDefaults.Seed) : IGenerationSessionFactory { + + /// + /// The seed of this factory. + /// + public int Seed { get; } = seed; + public IGenerationSession CreateSession(int recursionLimit) - => new GenerationContext(new GenerationConfiguration(config, conventionProvider, recursionLimit, nullableAnnotations)); + => CreateSession(recursionLimit, Seed); + + public IGenerationSession CreateSession(int recursionLimit, int seed) + => new GenerationContext(new GenerationConfiguration(config, conventionProvider, recursionLimit, nullableAnnotations, seed)); public IGenerationSession CreateSession() => // TODO: Need to deep-clone the config - CreateSession(5); -} \ No newline at end of file + CreateSession(AutoPocoDefaults.RecursionLimit); +} diff --git a/holonsoft.AutoPoco/Engine/Interfaces/DefaultRandomNullEvaluator.cs b/holonsoft.AutoPoco/Engine/Interfaces/DefaultRandomNullEvaluator.cs index ccf1d74..7838668 100644 --- a/holonsoft.AutoPoco/Engine/Interfaces/DefaultRandomNullEvaluator.cs +++ b/holonsoft.AutoPoco/Engine/Interfaces/DefaultRandomNullEvaluator.cs @@ -1,12 +1,13 @@ using holonsoft.AutoPoco.Configuration; +using holonsoft.AutoPoco.Util; namespace holonsoft.AutoPoco.Engine.Interfaces; public class DefaultRandomNullEvaluator : IRandomNullEvaluator { - private Random _random = new(AutoPocoGlobalSettings.StandardSeed); + private Random _random = new StableRandom(AutoPocoDefaults.Seed); - public int ThresholdPercentage { get; set; } = AutoPocoGlobalSettings.NullCreationThreshold; + public int ThresholdPercentage { get; set; } = AutoPocoDefaults.NullCreationThreshold; public DefaultRandomNullEvaluator() { } @@ -14,7 +15,7 @@ public DefaultRandomNullEvaluator(int thresholdPercentage) => ThresholdPercentage = thresholdPercentage; public void SetSeedToRandomValue(int seed) - => _random = new(seed); + => _random = new StableRandom(seed); public bool ShouldNextValueReturnNull() => _random.Next(1, 100) <= ThresholdPercentage; diff --git a/holonsoft.AutoPoco/Engine/Interfaces/IGenerationConfiguration.cs b/holonsoft.AutoPoco/Engine/Interfaces/IGenerationConfiguration.cs index 6fb796f..5e5c65e 100644 --- a/holonsoft.AutoPoco/Engine/Interfaces/IGenerationConfiguration.cs +++ b/holonsoft.AutoPoco/Engine/Interfaces/IGenerationConfiguration.cs @@ -11,6 +11,11 @@ public interface IGenerationConfiguration { /// NullableAnnotationSettings NullableAnnotations { get; } + /// + /// Gets the seed of this session. Every data source gets its own stream derived from it. + /// + int Seed { get; } + /// /// Gets the object builder for a certain type /// diff --git a/holonsoft.AutoPoco/Engine/Interfaces/IGenerationSessionFactory.cs b/holonsoft.AutoPoco/Engine/Interfaces/IGenerationSessionFactory.cs index ed24bba..b977453 100644 --- a/holonsoft.AutoPoco/Engine/Interfaces/IGenerationSessionFactory.cs +++ b/holonsoft.AutoPoco/Engine/Interfaces/IGenerationSessionFactory.cs @@ -9,8 +9,12 @@ public interface IGenerationSessionFactory { /// /// Creates a session, overriding the default recursion limit - /// Note: This method signature will probably change at some point /// - /// IGenerationSession CreateSession(int recursionLimit); + + /// + /// Creates a session with its own seed instead of the seed of the factory. Two sessions with the same + /// seed produce the same data, the seed alone defines every sequence. + /// + IGenerationSession CreateSession(int recursionLimit, int seed); } \ No newline at end of file diff --git a/holonsoft.AutoPoco/Engine/Interfaces/ISessionSeedable.cs b/holonsoft.AutoPoco/Engine/Interfaces/ISessionSeedable.cs new file mode 100644 index 0000000..abeabab --- /dev/null +++ b/holonsoft.AutoPoco/Engine/Interfaces/ISessionSeedable.cs @@ -0,0 +1,10 @@ +namespace holonsoft.AutoPoco.Engine.Interfaces; + +/// +/// A data source whose random stream the engine seeds from the session seed. Implemented by +/// : the engine seed is applied once and only when no seed was +/// set explicitly with SetSeedToRandomValue. +/// +public interface ISessionSeedable { + void ApplySessionSeed(int seed); +} diff --git a/holonsoft.AutoPoco/Engine/NullableAnnotationSettings.cs b/holonsoft.AutoPoco/Engine/NullableAnnotationSettings.cs index e301d05..0f2a76b 100644 --- a/holonsoft.AutoPoco/Engine/NullableAnnotationSettings.cs +++ b/holonsoft.AutoPoco/Engine/NullableAnnotationSettings.cs @@ -23,11 +23,11 @@ public NullableAnnotationSettings(bool respectNullableAnnotations, int nullCreat } /// - /// Creates enabled settings. A null threshold uses . + /// Creates enabled settings. A null threshold uses . /// /// probability in percent (0 to 100) that a nullable member becomes null public static NullableAnnotationSettings Enabled(int? nullCreationThreshold = null) - => new(true, nullCreationThreshold ?? AutoPocoGlobalSettings.NullCreationThreshold); + => new(true, nullCreationThreshold ?? AutoPocoDefaults.NullCreationThreshold); /// /// True when nullable annotations drive null generation. diff --git a/holonsoft.AutoPoco/Engine/NullableMemberDataSource.cs b/holonsoft.AutoPoco/Engine/NullableMemberDataSource.cs index 6d84e4c..78257ac 100644 --- a/holonsoft.AutoPoco/Engine/NullableMemberDataSource.cs +++ b/holonsoft.AutoPoco/Engine/NullableMemberDataSource.cs @@ -31,17 +31,21 @@ public NullableMemberDataSource(IDataSource inner, IRandomNullEvaluator randomNu /// Wraps the source when the settings are enabled and the member allows null. Returns the source /// unchanged otherwise. Method members are never wrapped. /// - public static IDataSource ForMember(IDataSource source, EngineTypeMember member, NullableAnnotationSettings? settings) { + /// the source producing the values + /// the member the source feeds + /// the nullable annotation settings of the session + /// the session seed, every member gets its own null pattern derived from it + public static IDataSource ForMember(IDataSource source, EngineTypeMember member, NullableAnnotationSettings? settings, int seed = AutoPocoDefaults.Seed) { ArgumentNullException.ThrowIfNull(source); ArgumentNullException.ThrowIfNull(member); if (settings is not { RespectNullableAnnotations: true }) return source; - var (allowsNull, memberKey) = member switch { - EngineTypePropertyMember p => (NullabilityHelper.AllowsNull(p.PropertyInfo), MemberKey(p.PropertyInfo.DeclaringType, p.Name)), - EngineTypeFieldMember f => (NullabilityHelper.AllowsNull(f.FieldInfo), MemberKey(f.FieldInfo.DeclaringType, f.Name)), - _ => (false, string.Empty) + var (allowsNull, declaringType) = member switch { + EngineTypePropertyMember p => (NullabilityHelper.AllowsNull(p.PropertyInfo), p.PropertyInfo.DeclaringType), + EngineTypeFieldMember f => (NullabilityHelper.AllowsNull(f.FieldInfo), f.FieldInfo.DeclaringType), + _ => (false, null) }; if (!allowsNull) @@ -50,26 +54,8 @@ public static IDataSource ForMember(IDataSource source, EngineTypeMember member, var evaluator = new DefaultRandomNullEvaluator(settings.NullCreationThreshold); // Every member gets its own deterministic seed, otherwise all nullable members of an object // would become null at the same time. - evaluator.SetSeedToRandomValue(AutoPocoGlobalSettings.StandardSeed ^ StableHash(memberKey)); + evaluator.SetSeedToRandomValue(SeedDerivation.ForNullEvaluator(seed, declaringType, member.Name)); return new NullableMemberDataSource(source, evaluator); } - - private static string MemberKey(Type? declaringType, string name) - => $"{declaringType?.FullName}.{name}"; - - /// - /// FNV-1a. string.GetHashCode is randomized per process and would break repeatable test data. - /// - private static int StableHash(string value) { - unchecked { - var hash = 2166136261u; - foreach (var c in value) { - hash ^= c; - hash *= 16777619u; - } - - return (int) hash; - } - } } diff --git a/holonsoft.AutoPoco/Engine/ObjectBuilder.cs b/holonsoft.AutoPoco/Engine/ObjectBuilder.cs index 3df9075..ea167ef 100644 --- a/holonsoft.AutoPoco/Engine/ObjectBuilder.cs +++ b/holonsoft.AutoPoco/Engine/ObjectBuilder.cs @@ -15,12 +15,15 @@ public class ObjectBuilder : IObjectBuilder { /// /// the registered type this builder creates /// optional, when enabled the sources of nullable members are wrapped in a - public ObjectBuilder(IEngineConfigurationType type, NullableAnnotationSettings? nullableAnnotations = null) { + /// session seed, every source of this type gets its own stream derived from it + public ObjectBuilder(IEngineConfigurationType type, NullableAnnotationSettings? nullableAnnotations = null, int seed = AutoPocoDefaults.Seed) { ArgumentNullException.ThrowIfNull(type); InnerType = type.RegisteredType; - if (type.GetFactory() != null) + if (type.GetFactory() != null) { _factory = type.GetFactory()?.Build() ?? throw new InvalidOperationException(); + (_factory as ISessionSeedable)?.ApplySessionSeed(SeedDerivation.ForType(seed, InnerType)); + } // build every source once, in registration order; the factory may take some of the members over var entries = new List<(EngineTypeMember Member, List Sources)>(); @@ -28,11 +31,16 @@ public ObjectBuilder(IEngineConfigurationType type, NullableAnnotationSettings? foreach (var registered in type.GetRegisteredMembers()) { var sources = registered.GetDataSources().Select(s => s.Build()).ToList(); - if (!registered.Member.IsMethod) { + if (registered.Member.IsMethod) { + for (var i = 0; i < sources.Count; i++) + (sources[i] as ISessionSeedable)?.ApplySessionSeed(SeedDerivation.ForMethodArgument(seed, InnerType, registered.Member.Name, i)); + } else { if (sources.Count == 0) continue; - var source = NullableMemberDataSource.ForMember(sources[0] ?? throw new InvalidOperationException(), registered.Member, nullableAnnotations); + var inner = sources[0] ?? throw new InvalidOperationException(); + (inner as ISessionSeedable)?.ApplySessionSeed(SeedDerivation.ForMember(seed, InnerType, registered.Member.Name)); + var source = NullableMemberDataSource.ForMember(inner, registered.Member, nullableAnnotations, seed); sources[0] = source; memberSources.Add(new MemberSource(registered.Member, source)); } diff --git a/holonsoft.AutoPoco/Engine/ObjectGenerator.cs b/holonsoft.AutoPoco/Engine/ObjectGenerator.cs index 0461813..d3b83b6 100644 --- a/holonsoft.AutoPoco/Engine/ObjectGenerator.cs +++ b/holonsoft.AutoPoco/Engine/ObjectGenerator.cs @@ -36,7 +36,9 @@ public IObjectGenerator Impose(Expression> property public IObjectGenerator Source(Expression> propertyExpr, IDataSource dataSource) { ArgumentNullException.ThrowIfNull(dataSource); var member = ReflectionHelper.GetMember(propertyExpr); - dataSource = NullableMemberDataSource.ForMember(dataSource, member, session.Builders?.NullableAnnotations); + var seed = session.Builders?.Seed ?? AutoPocoDefaults.Seed; + (dataSource as ISessionSeedable)?.ApplySessionSeed(SeedDerivation.ForMember(seed, typeof(T), member.Name)); + dataSource = NullableMemberDataSource.ForMember(dataSource, member, session.Builders?.NullableAnnotations, seed); if (member.IsField) AddAction(new ObjectFieldSetFromSourceAction((EngineTypeFieldMember) member, dataSource)); else if (member.IsProperty) diff --git a/holonsoft.AutoPoco/Util/SeedDerivation.cs b/holonsoft.AutoPoco/Util/SeedDerivation.cs new file mode 100644 index 0000000..be0644c --- /dev/null +++ b/holonsoft.AutoPoco/Util/SeedDerivation.cs @@ -0,0 +1,39 @@ +namespace holonsoft.AutoPoco.Util; + +/// +/// Derives the seed of a single data source from the session seed and the place the source is used at. +/// Every member gets its own stream, so two members with the same source type do not produce the same +/// values, and the values of a member do not depend on which other members exist. +/// +public static class SeedDerivation { + public static int ForMember(int seed, Type type, string memberName) + => seed ^ Fnv1a($"{Name(type)}.{memberName}"); + + public static int ForNullEvaluator(int seed, Type? type, string memberName) + => seed ^ Fnv1a($"{Name(type)}.{memberName}#null"); + + public static int ForMethodArgument(int seed, Type type, string methodName, int argumentIndex) + => seed ^ Fnv1a($"{Name(type)}.{methodName}({argumentIndex})"); + + public static int ForType(int seed, Type type) + => seed ^ Fnv1a($"{Name(type)}#factory"); + + /// + /// FNV-1a. string.GetHashCode is randomized per process and would break repeatable test data. + /// + public static int Fnv1a(string value) { + ArgumentNullException.ThrowIfNull(value); + unchecked { + var hash = 2166136261u; + foreach (var c in value) { + hash ^= c; + hash *= 16777619u; + } + + return (int) hash; + } + } + + private static string Name(Type? type) + => type?.FullName ?? type?.Name ?? string.Empty; +} diff --git a/holonsoft.AutoPoco/Util/StableRandom.cs b/holonsoft.AutoPoco/Util/StableRandom.cs new file mode 100644 index 0000000..4e2d185 --- /dev/null +++ b/holonsoft.AutoPoco/Util/StableRandom.cs @@ -0,0 +1,145 @@ +using System.Buffers.Binary; +using System.Numerics; + +namespace holonsoft.AutoPoco.Util; + +/// +/// Deterministic random number generator owned by AutoPoco: xoshiro256** seeded through SplitMix64. +/// The sequence for a seed is defined by this code alone, not by the .NET runtime, so test data stays +/// the same across runtime versions. Drop-in replacement for , every public member +/// is overridden. +/// +public sealed class StableRandom : Random { + private ulong _s0; + private ulong _s1; + private ulong _s2; + private ulong _s3; + + public StableRandom(int seed) + : this((long) seed) { } + + public StableRandom(long seed) + : base(0) { + var state = (ulong) seed; + _s0 = SplitMix64(ref state); + _s1 = SplitMix64(ref state); + _s2 = SplitMix64(ref state); + _s3 = SplitMix64(ref state); + } + + /// + /// The raw 64 bit output of the generator. + /// + public ulong NextUInt64() { + var result = BitOperations.RotateLeft(_s1 * 5, 7) * 9; + var t = _s1 << 17; + + _s2 ^= _s0; + _s3 ^= _s1; + _s1 ^= _s2; + _s0 ^= _s3; + _s2 ^= t; + _s3 = BitOperations.RotateLeft(_s3, 45); + + return result; + } + + /// + public override int Next() { + int value; + do + value = (int) (NextUInt64() >> 33); + while (value == int.MaxValue); + + return value; + } + + /// + public override int Next(int maxValue) { + ArgumentOutOfRangeException.ThrowIfNegative(maxValue); + return maxValue == 0 ? 0 : (int) NextBelow((ulong) maxValue); + } + + /// + public override int Next(int minValue, int maxValue) { + if (minValue > maxValue) + throw new ArgumentOutOfRangeException(nameof(minValue), minValue, "The minimum must not be greater than the maximum."); + + var range = (ulong) ((long) maxValue - minValue); + return range == 0 ? minValue : (int) (minValue + (long) NextBelow(range)); + } + + /// + public override long NextInt64() + => (long) NextBelow(long.MaxValue); + + /// + public override long NextInt64(long maxValue) { + ArgumentOutOfRangeException.ThrowIfNegative(maxValue); + return maxValue == 0 ? 0 : (long) NextBelow((ulong) maxValue); + } + + /// + public override long NextInt64(long minValue, long maxValue) { + if (minValue > maxValue) + throw new ArgumentOutOfRangeException(nameof(minValue), minValue, "The minimum must not be greater than the maximum."); + + var range = unchecked((ulong) (maxValue - minValue)); + return range == 0 ? minValue : unchecked(minValue + (long) NextBelow(range)); + } + + /// + public override double NextDouble() + => (NextUInt64() >> 11) * (1.0 / (1UL << 53)); + + /// + public override float NextSingle() + => (NextUInt64() >> 40) * (1.0f / (1U << 24)); + + /// + public override void NextBytes(byte[] buffer) { + ArgumentNullException.ThrowIfNull(buffer); + NextBytes(buffer.AsSpan()); + } + + /// + public override void NextBytes(Span buffer) { + while (buffer.Length >= sizeof(ulong)) { + BinaryPrimitives.WriteUInt64LittleEndian(buffer, NextUInt64()); + buffer = buffer[sizeof(ulong)..]; + } + + if (buffer.IsEmpty) + return; + + Span rest = stackalloc byte[sizeof(ulong)]; + BinaryPrimitives.WriteUInt64LittleEndian(rest, NextUInt64()); + rest[..buffer.Length].CopyTo(buffer); + } + + /// + protected override double Sample() + => NextDouble(); + + /// + /// Uniform value in [0, bound) without modulo bias: mask to the next power of two and reject what is out of range. + /// + private ulong NextBelow(ulong bound) { + var mask = ulong.MaxValue >> BitOperations.LeadingZeroCount((bound - 1) | 1); + + ulong candidate; + do + candidate = NextUInt64() & mask; + while (candidate >= bound); + + return candidate; + } + + private static ulong SplitMix64(ref ulong state) { + state += 0x9E3779B97F4A7C15UL; + var z = state; + z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9UL; + z = (z ^ (z >> 27)) * 0x94D049BB133111EBUL; + return z ^ (z >> 31); + } +}