Nondeterministic unit tests
danielmpetrov opened this issue Β· 25 comments
Unit tests are assuming the user has not modified the default culture settings. I have the regional format set as English (United States)
on my machine, but have customized some of the default formats under the Date & time
menu in the Windows settings app. This is my config:
And with these settings, five unit tests are failing for me. One example below:
Nice find, these might be bugs or unit tests that need to force the culture (in the options, like we do for several ones already).
The culture is specified, it's just that the expected outcome is hardcoded and doesn't match with the computer configuration.
Is this what you mean by forcing the culture - https://github.com/sebastienros/fluid/blob/main/Fluid.Tests/MiscFiltersTests.cs#L259 (this is one of the tests that fails), or am I misunderstanding?
the expected outcome is hardcoded
Hard coded as in "this is exactly what should be rendered, based on the current culture options and any server configuration.
Is this what you mean by forcing the culture
Yes, some culture-dependent tests/templates will have that to ensure that the "hard-coded" expected result is deterministic. I am never setting the current thread culture to a weird one it might blead to other tests that are executed concurrently (there is a risk with async that the code runs on two different threads). Another option would be to run the whole set of tests with a different culture to be certain that we didnt' miss the option in some places, or that the configure culture is correctly taken into account.
Specifying a second parameter useUserOverride: false
fixes this issue. Acceptable solution?
new CultureInfo("en-US", useUserOverride: false)
Oh, I see what you mean now, that's a different issue. I am well aware of this option as we are currently discussing its usage in ASP.NET request localization and Orchard Core ;)
For the tests yes we should do that such that the environment doesn't break them.
So the above overload fixed 4/5 tests.
However, there's this one problematic test case left -
fluid/Fluid.Tests/MiscFiltersTests.cs
Line 413 in 63bade9
When calling FluidValue.Create(timeSpan, options);
with timespan all zeroes, the following line
fluid/Fluid/Values/FluidValue.cs
Line 170 in 63bade9
throws
Message:β
System.ArgumentOutOfRangeException : The UTC time represented when the offset is applied must be between year 0 and 10,000. (Parameter 'offset')
Stack Trace:β
DateTimeOffset.ValidateDate(DateTime dateTime, TimeSpan offset)
DateTimeOffset.ctor(DateTime dateTime)
DateTimeOffset.op_Implicit(DateTime dateTime)
FluidValue.Create(Object value, TemplateOptions options)βlineβ170
MiscFiltersTests.DateTimeSpan(String timespan, String expected)βlineβ421
--- End of stack trace from previous location where exception was thrown ---
You have added this recently, any idea why this is failing locally for me?
You have added this recently, any idea why this is failing locally for me?
What culture and TFM are you running on?
Try to change to English or use a different TFM (if one works) and debug to see the differences.
I might handle this because I already worked in exact usecase in Orchard Core
I might handle this because I already worked in exact usecase in Orchard Core
Failing on all - netcoreapp 3.1, net 5, and net 6. The below code throws locally, so it's something for sure on my end
I can open a PR for the rest for now..
I might handle this because I already worked in exact usecase in Orchard Core
Something to do with my time zone it seems.
// this throws :(
var datetime = new DateTime(0).AddMinutes(59);
var offset = new DateTimeOffset(datetime);
// this works :)
var datetime = new DateTime(0).AddMinutes(60);
var offset = new DateTimeOffset(datetime);
The offset is becoming a negative when new DateTime(0)
is passed into it.
What is your timezone?
If that is timezone dependent then we might need to force one in the DateTimeOffset constructor, or the new DateTime(0)
the new DateTime()
assumes "Local" AFAIR, that might be the issue here when passed to new DateTimeOffset
.
@danielmpetrov let me knoe if you need help on this, I can push a PR to continue what you already started with
@danielmpetrov let me knoe if you need help on this, I can push a PR to continue what you already started with
@sebastienros in your code on SharpLab, the offset it printed as +00:00
, I get +01:00
locally.
var datetime = new DateTime(0).AddMinutes(60);
var offset = new DateTimeOffset(datetime);
Console.WriteLine(offset); // 01-Jan-01 01:00:00 +01:00
@hishamco If you have an idea go ahead, I have nothing right now.
Maybe SharpLab is highjacking the culture. The challenge is to create some code that will fail with the same error on any machine. I will try the same code on my local machine then.
@hishamco If you have an idea go ahead, I have nothing right now.
π
Ok, it's in my plate now ..
From the docs of the constructor - https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset.-ctor?view=net-6.0
If the value of DateTime.Kind is DateTimeKind.Utc, the DateTime property of the new instance is set equal to dateTime, and the Offset property is set equal to Zero.
If the value of DateTime.Kind is DateTimeKind.Local or DateTimeKind.Unspecified, the DateTime property of the new instance is set equal to dateTime, and the Offset property is set equal to the offset of the local system's current time zone.
So specifying DateTimeKind.Utc
in the constructor would solve this problem. By default, calling new DateTime(0)
sets the DateTimeKind
property to Unspecified
.
var datetime = new DateTime(0, DateTimeKind.Utc); // this clears up the offset
var offset = new DateTimeOffset(datetime); // so this line does not throw anymore
Console.WriteLine(offset); // 01-Jan-01 00:00:00 +00:00
fluid/Fluid/Values/FluidValue.cs
Line 170 in 63bade9
Tests pass, I don't know if anything else would break conceptually.
Awesome, just submit the PR, with a comment on why the UTC is used in the ctor. Maybe check if we use the same TZ-less constructor somewhere else.
@danielmpetrov seems you did some progress on this, to save the time for us I will stop working on this, and let you start a PR