Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<RepositoryType>git</RepositoryType>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageTags>autopoco;testdata;test-data;poco;generator;fluent;fake-data</PackageTags>
<PackageReleaseNotes>6.0: Lambda data sources (FuncSource, Setup(...).From(() => ...), Source(expr, () => ...) at generation time). Opt-in nullability-aware null generation via RespectNullableAnnotations(threshold). Generic NumberSource&lt;T&gt; and NullableNumberSource&lt;T&gt; 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, the max of IntegerSource/LongSource/RandomNumberSource and their nullable variants is inclusive now (it was exclusive), Int128Source/NullableInt128Source pick uniformly instead of clamping to the bounds (they never produced a negative value before). Index-aware Impose on lists and selections (value from the position, or from position and item), Impose(member, item => value) on a single generator. Invoke arguments: Use.From(lambda), constants and variables arrive as values. Context lambdas get a non-nullable IGenerationContext. Argument validation over the public API, meaningful exception messages, XML documentation file. IDataSource&lt;T&gt; 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.</PackageReleaseNotes>
<PackageReleaseNotes>6.0: Lambda data sources (FuncSource, Setup(...).From(() => ...), Source(expr, () => ...) at generation time). Opt-in nullability-aware null generation via RespectNullableAnnotations(threshold). Generic NumberSource&lt;T&gt; and NullableNumberSource&lt;T&gt; 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, the max of IntegerSource/LongSource/RandomNumberSource and their nullable variants is inclusive now (it was exclusive), Int128Source/NullableInt128Source pick uniformly instead of clamping to the bounds (they never produced a negative value before), RandomStringSource and UrlSource reach the last character resp. the last top level domain, ColorSource/RandomTextSource/RandomUtfTextSource treat their maximum as inclusive, ColorSource applies the alpha of SetColorRange, and UrlSource and ExtendedEmailAddressSource seed their nested sources (the URL host ignored the session seed completely). Index-aware Impose on lists and selections (value from the position, or from position and item), Impose(member, item => value) on a single generator. Invoke arguments: Use.From(lambda), constants and variables arrive as values. Context lambdas get a non-nullable IGenerationContext. Argument validation over the public API, meaningful exception messages, XML documentation file. IDataSource&lt;T&gt; 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.</PackageReleaseNotes>
</PropertyGroup>

<PropertyGroup Label="Package build">
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ holonsoft ported this famous lib to newest version of dotnet
* Every generated sequence changed once, mainly because every member has its own random stream now. Tests that pin generated values need new expectations. This is the last time, see "Why the sequences changed" below.
* `IntegerSource`, `NullableIntegerSource`, `LongSource`, `NullableLongSource`, `RandomNumberSource` and `NullableRandomNumberSource` treat `max` as **inclusive** now, it used to be exclusive. `new IntegerSource(1, 3)` produces 3 as well. This matches `NumberSource<T>` and the date sources: the maximum of every integer and date source is inclusive now.
* `Int128Source` and `NullableInt128Source` pick uniformly from their range instead of generating a value and clamping it to the bounds. The old implementation never produced a negative value and returned the upper bound almost always on a restricted range.
* `RandomStringSource` and `UrlSource` never used the last character of the allowed character set and the last top level domain of their list. `ColorSource`, `RandomTextSource` and `RandomUtfTextSource` treat their maximum as inclusive now, so `new ColorSource(0, 255)` reaches 255 and a text source asked for 3 to 6 paragraphs can produce 6.
* `ColorSource` applies the alpha given to `SetColorRange(alpha, ...)`, it used to be silently dropped and every color came back fully opaque.
* `UrlSource` follows the session seed with its host part as well. The nested string source kept the default seed, so every session produced the same host names no matter what `UseSeed` said. `ExtendedEmailAddressSource` gives its first and last name source separate streams, they used to share one and repeated the same name pairs.
* `AutoPocoGlobalSettings` is gone, the defaults are read-only constants in `AutoPocoDefaults`.
* The never implemented `Ctor(...)` stub on the type builder is gone.
* The lambdas receiving the generation context get a non-nullable `IGenerationContext`, no more `ctx!`.
Expand Down Expand Up @@ -133,6 +136,8 @@ x.Include<SimpleUser>()
* Hardening: the public configuration and generation API validates its arguments (`ArgumentNullException`, `ArgumentOutOfRangeException` for negative counts, `ArgumentException` for unknown members, non-source types and non-convention types) and every internal failure carries the type and member it happened at. The never implemented `Ctor(...)` stub on the type builder is gone. The package ships an XML documentation file.
* `IDataSource<T>` is covariant now, so a source of `string` (e.g. `CitySource`) can be used for a `string?` member without a nullability warning.
* Bug fix: the integer sources that existed before `NumberSource<T>` never produced their maximum, `new IntegerSource(1, 3)` gave 1 or 2 only. `IntegerSource`, `LongSource`, `RandomNumberSource` and their nullable variants draw uniformly from the inclusive range now, both bounds can be produced. `Int128Source` and `NullableInt128Source` were worse: they built a value from two non-negative draws and clamped it to the range, so the default source never produced a negative value and `new Int128Source(1, 5)` returned 5 almost every time. All of them share the pick of `NumberSource<T>` now. `LongSourceBase.SetMinMax` takes `long` arguments at last, and all four throw an `ArgumentOutOfRangeException` when the maximum lies below the minimum. The generated sequences of the long and Int128 sources changed, the ones of the integer sources did not.
* Bug fix: a handful of sources could not reach the last element of what they draw from. `RandomStringSource` never used the last character of its allowed character set, `UrlSource` never used the last top level domain of its list, and `ColorSource`, `RandomTextSource` and `RandomUtfTextSource` treated their maximum as exclusive, so a colour channel range of 0 to 255 never gave 255 and a text source asked for 3 to 6 paragraphs never gave 6. On top of that `ColorSource` dropped the alpha of `SetColorRange(alpha, ...)` and always returned an opaque colour.
* Bug fix: sources that drive another source internally seed it now. `UrlSource` builds its host with a nested `RandomStringSource` that kept the default seed, so `UseSeed` and `CreateSession(limit, seed)` changed the top level domain but every session saw the same host names. `ExtendedEmailAddressSource` passed one seed to both its first and last name source, so the two followed the same sequence of draws and the same first name came back with the same last name. Both give their nested sources a derived stream of their own now, inside a session and standalone.
* 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.
* Bug fix: `RandomUtfTextSource` could loop forever when it hit a Unicode block without any allowed character.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
锘縰sing Xunit;
锘縰sing Shouldly;
using Xunit;
using holonsoft.AutoPoco.DataSources.Business;
using holonsoft.AutoPoco.Tests.Functionality.Tests.DataSources.Base;

Expand All @@ -8,9 +9,27 @@ public class ExtendedEmailAddressSourceTests : TestBase {
[Fact]
public void NextReturnsStableEmailListInTermsOfTestability()
=> NextReturnsStableElementListInTermsOfTestability(
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" });
new ExtendedEmailAddressSource(), new string[] { "Ethan.Brown@heise.example", "Brian.Mason@yahoo.test", "Mila.Ortiz@hotmail.invalid", "Robert.Lopez@google.example", "Ellie.Murphy@golem.test", "Michael.Patterson@hotmail.test", "Sophie.Mason@microsoft.test", "Hannah.Fox@golem.test", "Isaac.Parker@aol.example", "Ariana.Walker@google.invalid" });

[Fact]
public void NextReturnsStableEmailListInTermsOfTestabilityAndListCanContainNull() => NextReturnsStableElementListInTermsOfTestability(
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" });
new NullableExtendedEmailAddressSource()!, new string[] { "Ethan.Brown@heise.example", "Brian.Mason@yahoo.test", "Mila.Ortiz@hotmail.invalid", "Robert.Lopez@google.example", "Ellie.Murphy@golem.test", "Michael.Patterson@hotmail.test", "Sophie.Mason@microsoft.test", "Hannah.Fox@golem.test", "Isaac.Parker@aol.example", "Ariana.Walker@google.invalid" });

[Fact]
public void TheFirstAndTheLastNameDoNotFollowTheSameStream() {
// both nested sources used to share one seed, so the same first name came back with the same last name
var source = new ExtendedEmailAddressSource();
var pairs = Enumerable.Range(0, 200).Select(_ => source.Next(null).Split('@')[0]).ToList();
var firstNames = pairs.Select(p => p.Split('.')[0]).ToList();

var repeatedFirstName = firstNames
.GroupBy(x => x)
.FirstOrDefault(g => g.Count() > 1)?.Key;

repeatedFirstName.ShouldNotBeNull("the sample is too small to say anything about the pairing");
pairs.Where(p => p.StartsWith($"{repeatedFirstName}.", StringComparison.Ordinal))
.Distinct()
.Count()
.ShouldBeGreaterThan(1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ public void NextReturnsAParagraph() {
value.ShouldNotBeNullOrWhiteSpace();
}

[Fact]
public void NextReachesTheMaximumParagraphCount() {
// no truncation, so every paragraph survives and can be counted
var source = new RandomTextSource(int.MaxValue, 1, 3, 1, 1, "abc".ToCharArray());
var counts = Enumerable.Range(0, 200)
.Select(_ => source.Next(null).Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries).Length)
.ToList();

counts.ShouldAllBe(c => c >= 1 && c <= 3);
counts.ShouldContain(3);
counts.ShouldContain(1);
}

[Fact]
public void NextReturnsTwoParagraph() {
var source = new RandomTextSource();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ public void NextReturnsAParagraph() {
value.ShouldNotBeNullOrWhiteSpace();
}

[Fact]
public void NextReachesTheMaximumParagraphCount() {
// no truncation, so every paragraph survives and can be counted
var source = new RandomUtfTextSource(int.MaxValue, 1, 3, 1, 1);
var counts = Enumerable.Range(0, 200)
.Select(_ => source.Next(null).Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries).Length)
.ToList();

counts.ShouldAllBe(c => c >= 1 && c <= 3);
counts.ShouldContain(3);
counts.ShouldContain(1);
}

[Fact]
public void NextTerminatesForManyDrawsEvenWhenABlockHasNoAllowedCharacters() {
// regression: a block made only of excluded categories used to loop forever
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
锘縰sing Xunit;
锘縰sing Shouldly;
using Xunit;
using holonsoft.AutoPoco.DataSources.Business;
using holonsoft.AutoPoco.Engine.Interfaces;
using holonsoft.AutoPoco.Tests.Functionality.Tests.DataSources.Base;

namespace holonsoft.AutoPoco.Tests.Functionality.Tests.DataSources.Business;
Expand All @@ -8,11 +10,33 @@ public class UrlSourceTest() : TestBase {
[Fact]
public void NextReturnsStableUrlListInTermsOfTestability()
=> NextReturnsStableElementListInTermsOfTestability(
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" });
new UrlSource(), new string[] { "http://www.varh.ua", "http://www.pislmnk.iq", "http://www.stmbxgpy.pe", "http://www.kqva.in", "http://www.xyxismavc.om", "http://www.jzbwg.tr", "http://www.pkd.nz", "http://www.ikcoogs.by", "http://www.aozzhwbm.ee", "http://www.hsck.pl", "http://www.pdlfxj.ua", "http://www.puckcn.ke", "http://www.nixcw.vn", "http://www.wflid.me", "http://www.sobnyuv.mv", "http://www.esmovka.ru", "http://www.zqvpyte.si", "http://www.bdfztuua.al", "http://www.hapccijl.pk", "http://www.ggrfehefpp.mx" });

[Fact]
public void NextReturnsStableUrlListInTermsOfTestabilityAndListCanContainNull()
=> NextReturnsStableElementListInTermsOfTestability(
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" });
new NullableUrlSource()!, new string[] { "http://www.varh.ua", "http://www.pislmnk.iq", "http://www.stmbxgpy.pe", "http://www.kqva.in", "http://www.xyxismavc.om", "http://www.jzbwg.tr", "http://www.pkd.nz", "http://www.ikcoogs.by", "http://www.aozzhwbm.ee", "http://www.hsck.pl", "http://www.pdlfxj.ua", "http://www.puckcn.ke", "http://www.nixcw.vn", "http://www.wflid.me", "http://www.sobnyuv.mv", "http://www.esmovka.ru", "http://www.zqvpyte.si", "http://www.bdfztuua.al", "http://www.hapccijl.pk", "http://www.ggrfehefpp.mx", "http://www.mdztzkew.pe", "http://www.fymd.ir" });

[Fact]
public void TheHostFollowsTheSessionSeedAndNotOnlyTheTopLevelDomain() {
// the nested string source used to keep the default seed, so every session produced the same hosts
static string[] Hosts(int seed) {
var source = new UrlSource();
((ISessionSeedable) source).ApplySessionSeed(seed);
return Enumerable.Range(0, 5)
.Select(_ => source.Next(null).Split('.')[1])
.ToArray();
}

Hosts(1337).ShouldNotBe(Hosts(4711));
}

[Fact]
public void NextUsesTheLastTopLevelDomainAsWell() {
var source = new UrlSource();
var tlds = Enumerable.Range(0, 5000).Select(_ => source.Next(null).Split('.')[^1]).ToHashSet();

tlds.ShouldContain("ye");
}
}

Loading