-
Notifications
You must be signed in to change notification settings - Fork 4.9k
System.Lazy regression tests #15577
System.Lazy regression tests #15577
Conversation
This is a prelude to an alternative Lazy implementation in a PR at dotnet/coreclr#8963
| /// the original suite as well as this one. These tests were written with the express purpose of testing | ||
| /// that the implementation conforms to the existing implementation, rather than to specification. | ||
| /// The two reasons obviously have major overlap (obviously), but leads to the flavour of this suite | ||
| /// being rather dense and repeditive, which for regression I feel is suitable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I appreciate the sentiment here, in a month or a year or whatever, it's not really going to matter that the implementation changed: then we really just want a suite that validates Lazy's behavior. We should avoid duplication and have a single suite that validates the behavior. Let's just add tests for things that aren't currently being exercised.
|
@dotnet-bot test this please |
AlexGhiondea
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
|
I will probably bash this code a little bit; so don't pull out in yet. I have just been a bit busy, sorry. Just looking at consolidating some of the tests as per @stephentoub comments. Hopefully sometime over this coming weekend. |
|
@manofstick sounds good! Thanks for working on this! |
|
OK; I think I'm good now. |
| Lazy<string> lazy = null; | ||
| lazy = new Lazy<string>(() => x++ < 5 ? lazy.Value : "Test", true); | ||
| [Fact] | ||
| public static void Ctor_ExceptionRecovery() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general we try to use [Theory] with [MemberData] for things like this. Your previous case made sense not to use that, as it would have been difficult/cumbersome to handle needing to construct the lazy instances with a delegate that referred back to the same instance. In this case, though, it'd be pretty straightforward to do it that way, e.g.
public static IEnumerable<object[]> Ctor_ExceptionRecovery_MemberData()
{
yield return new object[] { new Lazy<InitiallyExceptionThrowingCtor>(), 5 };
yield return new object[] { new Lazy<InitiallyExceptionThrowingCtor>(true), 5 };
... // etc.
}
[Theory]
[MemberData(nameof(Ctor_ExceptionRecovery_MemberData))]
public static void Ctor_ExceptionRecovery(Lazy<InitiallyExceptionThrowingCtor> lazy, int? expected)
{
... // same body of Ctor_ExceptionRecovery_Impl test you currently have
}That helps in a variety of ways, e.g. having xunit run each test case as its own test such that one failing doesn't cause the rest to fail, a description from xunit about exactly which case failed, etc., plus consistency with how we're trying to do these across corefx (there are a bunch of tests across corefx we haven't yet converted to that style, that we just ported as is from the internal tests used for desktop, but for all new ones we write we're trying to follow that style).
| [Fact] | ||
| public static void CheckExceptions() | ||
| { | ||
| CheckException(typeof(MyException), new Lazy<int>(() => { throw new MyException(99); })); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't Assert.Throws<MyException> be used instead of this custom CheckException method?
| { | ||
| Assert.Same(first, thrown2); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would be better as something like:
private static void AssertIdempotentException<T>(Lazy<T> x)
{
MyException e = Assert.Throws<MyException>(() => x.Value);
Assert.Same(e, Assert.Throws<MyException>(() => x.Value));
]In addition to being more concise, it also ensure that the second call to Value throws (the existing version will still succeed if the second call doesn't throw).
| Assert.NotNull(second); | ||
|
|
||
| Assert.NotEqual(first, second); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can similarly be condensed, and just use Assert.NotSame instead of Assert.Same.
| SameException(new Lazy<ExceptionInCtor>(() => new ExceptionInCtor(99), LazyThreadSafetyMode.None)); | ||
|
|
||
| DifferentException(new Lazy<ExceptionInCtor>(() => new ExceptionInCtor(99), LazyThreadSafetyMode.PublicationOnly)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another case where a [Theory] would be good (or, actually, two, one for the same case and one for the different case... effectively the assert methods I previously described become the [Theory], with a separate MemberData supplying the values).
| } | ||
|
|
||
| [Fact] | ||
| static void Serialization_ValueType() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this run if it's not public?
| } | ||
|
|
||
| [Fact] | ||
| static void Serialization_RefType() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this run if it's not public?
| var fortytwo = (Lazy<int>)formatter.Deserialize(stream); | ||
| Assert.True(fortytwo.IsValueCreated); | ||
| Assert.Equal(fortytwo.Value, 42); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a small helper for this in:
https://github.com/dotnet/corefx/blob/master/src/Common/tests/System/Runtime/Serialization/Formatters/BinaryFormatterHelpers.cs
With that this would become something like:
[Fact]
public static void Serialization_Int32_Roundtrips()
{
Lazy<int> clone = BinaryFormatterHelpers.Clone(new Lazy<int>(() => 42));
Assert.True(clone.IsValueCreated);
Assert.Equal(42, clone.Value);
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Argh, running out of time and for some reason this just isn't working... I can see it used in ModuleTests.cs and PointerTests.cs, and I can see in the project file that there is a link to BinaryFormatterHelper.cs, but for some reason it just isn't being picked up; I'm just not seeing the System.Runtime.Serialization.Formatters.Tests namespace at all, and my time is running out, so I've left this as it originally was (baring the public change)
|
|
||
| var fortytwo = (Lazy<string>)formatter.Deserialize(stream); | ||
| Assert.True(fortytwo.IsValueCreated); | ||
| Assert.Equal(fortytwo.Value, "42"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
|
@manofstick, thank you for adding these (and addressing my previous comments)! I've left a few more comments on things I think it would be good to address. |
|
OK modified to use the Theory attribute (and other changes you had mentioned). Theory didn't seem to work with generic arguments, so had to duplicate some of the code with type specific methods; not sure if it ends up clearer than the original (But I'm not a huge fan of attributes so maybe just my personal preference!) |
|
@stephentoub is this addressed to your satisfaction? |
|
@dotnet-bot test this please (logs gone) |
| using System.Threading; | ||
| using System.Reflection; | ||
| using System.IO; | ||
| using System.Runtime.Serialization.Formatters.Binary; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is missing a using System.Runtime.Serialization.Formatters.Tests;... I believe that's the cause of the CI failures.
| private static void Value_Invalid_Impl<T>(ref Lazy<T> x, Lazy<T> lazy) | ||
| { | ||
| x = lazy; | ||
| Assert.Throws<InvalidOperationException>(() => { var dummy = lazy.Value; }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: the () => { var dummy = lazy.Value; } can I believe be simplified to just () => lazy.Value
| public static void Ctor_ExceptionRecovery(Lazy<InitiallyExceptionThrowingCtor> lazy, int? expected) | ||
| { | ||
| InitiallyExceptionThrowingCtor.counter = 0; | ||
| var result = default(InitiallyExceptionThrowingCtor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: InitialiallyExceptionthrowingCtor result = null;
| try { result = lazy.Value; } catch (Exception) { } | ||
| } | ||
| if (expected == null) | ||
| Assert.Null(result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
null doesn't appear to ever be expected, is it?
| private static void Value_ExceptionRecovery_IntImpl(Lazy<int> lazy, ref int counter, int expected) | ||
| { | ||
| counter = 0; | ||
| var result = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: var => int
| if (expected == null) | ||
| Assert.Null(result); | ||
| else | ||
| Assert.Equal(result, expected); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: this could just be Assert.Equal(expected, result) rather than special-casing expected == null... if expected is null, we expect result to be null, so Assert.Equal will work fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also note that expected should be the first argument to Equal, here and elsewhere
| [Fact] | ||
| public static void Value_ExceptionRecovery() | ||
| { | ||
| var counter = default(int); // set in test function |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: int counter = 0;
stephentoub
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding all of these. A few comments, but otherwise LGTM.
|
Thanks! |
* System.Lazy regression tests This is a prelude to an alternative Lazy implementation in a PR at dotnet/coreclr#8963 Commit migrated from dotnet/corefx@029f4c4
This is a prelude to an alternative Lazy implementation in a PR at
dotnet/coreclr#8963