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), 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>
<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). TimeSpanSource was rewritten: it returned TimeSpan.MinValue on every draw of the default range, produced values outside a requested range and looped forever on a reversed one. DoubleSource returned infinity over its default range, DoubleSource/FloatSource/DecimalSource interpolate overflow safe now and DecimalSource keeps its full precision. 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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ holonsoft ported this famous lib to newest version of dotnet
* `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.
* `TimeSpanSource` and `NullableTimeSpanSource` pick uniformly from the inclusive tick range now. The old implementation returned `TimeSpan.MinValue` on every draw of the default range, produced values outside the range that was asked for (1 to 2 hours gave 22 to 58 minutes) and looped forever when the maximum lay before the minimum.
* `DoubleSource`, `FloatSource` and `DecimalSource` interpolate between their bounds without overflowing. `new DoubleSource()` returned infinity on every draw, and `DecimalSource` lost about half of its significant digits on the way through `double`.
* `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.
Expand Down Expand Up @@ -136,6 +138,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: `TimeSpanSource` did not work at all. `new TimeSpanSource()` returned `TimeSpan.MinValue` on every single draw, `new TimeSpanSource(TimeSpan.FromHours(1), TimeSpan.FromHours(2))` returned values between 22 and 58 minutes, so every value was outside the range that was asked for, and a maximum before the minimum sent it into an endless loop. It picks uniformly from the inclusive tick range now, the same way `DateTimeSource` does, and throws an `ArgumentOutOfRangeException` on a reversed range.
* Bug fix: `new DoubleSource()` returned infinity on every draw, because the default range is the whole range of `double` and the old code computed `max - min`. `DoubleSource`, `FloatSource` and `DecimalSource` interpolate as `min * (1 - sample) + max * sample` now, which cannot overflow. `DecimalSource` does it in decimal arithmetic instead of going through `double`, so it keeps its full precision; it used to give numbers like `27879346472004300000000000000` where it now gives `27879346472004256951386422019`. All three throw an `ArgumentOutOfRangeException` when the maximum lies below the minimum.
* 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,21 @@ public void NextReturnsStableBooleanListInTermsOfTestabilityAndListCanContainNul
var source = new NullableDecimalSource(-1000, 1000, 3);
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 NextOverTheWholeRangeOfTheTypeKeepsTheDecimalPrecision() {
// the detour through double used to cut the value down to about 15 significant digits
var source = new DecimalSource();
var values = Enumerable.Range(0, 50).Select(_ => source.Next(null)).ToList();

values.ShouldAllBe(x => x >= decimal.MinValue && x <= decimal.MaxValue);
values.ShouldContain(x => x % 1000m != 0m);
}

[Fact]
public void NextThrowsWhenMaxIsBelowMin() {
var source = new DecimalSource(10, 5);

Should.Throw<ArgumentOutOfRangeException>(() => source.Next(null));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,30 @@ public void NextReturnsStableDoubleListInTermsOfTestabilityAndListCanContainNull

NextReturnsStableElementListInTermsOfTestability(source, expectedValues);
}

[Fact]
public void NextOverTheWholeRangeOfTheTypeStaysFinite() {
// the default range used to compute max - min, which is infinity for double
var values = Enumerable.Range(0, 200).Select(_ => new DoubleSource().Next(null)).ToList();

values.ShouldAllBe(x => double.IsFinite(x));
values.ShouldAllBe(x => x >= double.MinValue && x <= double.MaxValue);
}

[Fact]
public void NextOverTheWholeRangeOfTheTypeVaries() {
var source = new DoubleSource();
var values = Enumerable.Range(0, 200).Select(_ => source.Next(null)).ToList();

values.Distinct().Count().ShouldBe(200);
values.ShouldContain(x => x > 0);
values.ShouldContain(x => x < 0);
}

[Fact]
public void NextThrowsWhenMaxIsBelowMin() {
var source = new DoubleSource(10, 5);

Should.Throw<ArgumentOutOfRangeException>(() => source.Next(null));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public void NextReturnsStableFloatListInTermsOfTestability() {

value2.ShouldNotBe(value1);

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 };
var expectedValues = new float[] { 1.8886552E+38F, 2.932349E+38F, -2.1830817E+38F, 1.0514879E+38F, -2.7261045E+38F, -1.6290856E+38F, -1.8493199E+38F, 2.1725452E+38F, 2.8403095E+38F, -3.4037737E+36F };

NextReturnsStableElementListInTermsOfTestability(source, expectedValues);
}
Expand All @@ -27,8 +27,15 @@ public void NextReturnsStableFloatListInTermsOfTestabilityAndListCanContainNull(

value2.ShouldNotBe(value1);

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 };
var expectedValues = new float?[] { 1.8886552E+38F, 2.932349E+38F, -2.1830817E+38F, 1.0514879E+38F, -2.7261045E+38F, -1.6290856E+38F, -1.8493199E+38F, 2.1725452E+38F, 2.8403095E+38F, -3.4037737E+36F, 1.270905E+38F, -2.1464215E+38F, -3.2599887E+38F, 3.3153998E+38F, 2.6797953E+38F, 2.5867485E+38F, -2.3796047E+38F, -6.8731113E+37F, -5.3450133E+37F, -1.6520902E+38F };

NextReturnsStableElementListInTermsOfTestability(source, expectedValues);
}

[Fact]
public void NextThrowsWhenMaxIsBelowMin() {
var source = new FloatSource(10f, 5f);

Should.Throw<ArgumentOutOfRangeException>(() => source.Next(null));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,70 @@ public void NextReturnsStableTimeSpanListInTermsOfTestability() {

value2.ShouldNotBe(value1);

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) });
NextReturnsStableElementListInTermsOfTestability(source, new TimeSpan[] { new TimeSpan(5193727380), new TimeSpan(-9554848653), new TimeSpan(-7502659219), new TimeSpan(-3808227407), new TimeSpan(-2423852717), new TimeSpan(2896633336), new TimeSpan(-4073448608), new TimeSpan(-918960852), new TimeSpan(-4345801754), new TimeSpan(8042706088) });
}

[Fact]
public void NextReturnsStableTimeSpanListInTermsOfTestabilityAndListCanContainNull() {
var source = new NullableTimeSpanSource(new TimeSpan(-10000000000), new TimeSpan(10000000000));

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) });
NextReturnsStableElementListInTermsOfTestability(source, new TimeSpan?[] { new TimeSpan(-8666068260), new TimeSpan(-2239989265), new TimeSpan(5193727380), new TimeSpan(-9554848653), new TimeSpan(-7502659219), new TimeSpan(-3808227407), new TimeSpan(-2423852717), new TimeSpan(2896633336), new TimeSpan(-4073448608), new TimeSpan(-918960852) });
}

[Fact]
public void NextStaysInsideAPositiveRange() {
// the old implementation returned 22 to 58 minutes for this range, every value outside it
var min = TimeSpan.FromHours(1);
var max = TimeSpan.FromHours(2);
var source = new TimeSpanSource(min, max);

Draw(source, 500).ShouldAllBe(x => x >= min && x <= max);
}

[Fact]
public void NextVariesOverTheWholeRangeOfTheType() {
// the old implementation returned TimeSpan.MinValue on every draw here
var values = Draw(new TimeSpanSource(), 200);

values.Distinct().Count().ShouldBe(200);
values.ShouldContain(x => x > TimeSpan.Zero);
values.ShouldContain(x => x < TimeSpan.Zero);
}

[Fact]
public void NextReachesBothEndsOfAShortRange() {
var min = new TimeSpan(100);
var max = new TimeSpan(102);
var values = Draw(new TimeSpanSource(min, max), 300);

values.ShouldAllBe(x => x >= min && x <= max);
values.ShouldContain(min);
values.ShouldContain(max);
}

[Fact]
public void NextWithMinEqualToMaxReturnsThatValue() {
var span = TimeSpan.FromMinutes(7);

Draw(new TimeSpanSource(span, span), 10).ShouldAllBe(x => x == span);
}

[Fact]
public void NextThrowsWhenMaxIsBeforeMin() {
// the old implementation looped forever here
var source = new TimeSpanSource(TimeSpan.FromHours(2), TimeSpan.FromHours(1));

Should.Throw<ArgumentOutOfRangeException>(() => source.Next(null));
}

[Fact]
public void SetMinMaxRangeChangesTheRange() {
var source = new TimeSpanSource();
source.SetMinMaxRange(TimeSpan.FromDays(1), TimeSpan.FromDays(2));

Draw(source, 100).ShouldAllBe(x => x >= TimeSpan.FromDays(1) && x <= TimeSpan.FromDays(2));
}

private static List<TimeSpan> Draw(TimeSpanSource source, int count)
=> Enumerable.Range(0, count).Select(_ => source.Next(null)).ToList();
}
Loading