Skip to content

Commit e409f50

Browse files
manofstickstephentoub
authored andcommitted
System.Lazy regression tests (dotnet/corefx#15577)
* 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
1 parent 1999413 commit e409f50

File tree

1 file changed

+272
-6
lines changed

1 file changed

+272
-6
lines changed

src/libraries/System.Runtime/tests/System/LazyTests.cs

Lines changed: 272 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Collections.Generic;
56
using System.Threading;
7+
using System.Reflection;
8+
using System.IO;
9+
using System.Runtime.Serialization.Formatters.Binary;
10+
using System.Runtime.Serialization.Formatters.Tests;
611
using Xunit;
712

813
namespace System.Tests
@@ -103,17 +108,278 @@ public static void ToString_DoesntForceAllocation()
103108
Assert.Equal("1", lazy.ToString());
104109
}
105110

111+
private static void Value_Invalid_Impl<T>(ref Lazy<T> x, Lazy<T> lazy)
112+
{
113+
x = lazy;
114+
Assert.Throws<InvalidOperationException>(() => lazy.Value);
115+
}
116+
106117
[Fact]
107118
public static void Value_Invalid()
108119
{
109-
string lazilyAllocatedValue = "abc";
120+
Lazy<int> x = null;
121+
Func<int> f = () => x.Value;
122+
123+
Value_Invalid_Impl(ref x, new Lazy<int>(f));
124+
Value_Invalid_Impl(ref x, new Lazy<int>(f, true));
125+
Value_Invalid_Impl(ref x, new Lazy<int>(f, false));
126+
Value_Invalid_Impl(ref x, new Lazy<int>(f, LazyThreadSafetyMode.ExecutionAndPublication));
127+
Value_Invalid_Impl(ref x, new Lazy<int>(f, LazyThreadSafetyMode.None));
128+
129+
// When used with LazyThreadSafetyMode.PublicationOnly this causes a stack overflow
130+
// Value_Invalid_Impl(ref x, new Lazy<int>(f, LazyThreadSafetyMode.PublicationOnly));
131+
}
132+
133+
public class InitiallyExceptionThrowingCtor
134+
{
135+
public static int counter = 0;
136+
public static int getValue()
137+
{
138+
if (++counter < 5)
139+
throw new Exception();
140+
else
141+
return counter;
142+
}
143+
144+
public int Value { get; }
145+
146+
public InitiallyExceptionThrowingCtor()
147+
{
148+
Value = getValue();
149+
}
150+
}
151+
152+
public static IEnumerable<object[]> Ctor_ExceptionRecovery_MemberData()
153+
{
154+
yield return new object[] { new Lazy<InitiallyExceptionThrowingCtor>(), 5 };
155+
yield return new object[] { new Lazy<InitiallyExceptionThrowingCtor>(true), 5 };
156+
yield return new object[] { new Lazy<InitiallyExceptionThrowingCtor>(false), 5 };
157+
yield return new object[] { new Lazy<InitiallyExceptionThrowingCtor>(LazyThreadSafetyMode.ExecutionAndPublication), 5 };
158+
yield return new object[] { new Lazy<InitiallyExceptionThrowingCtor>(LazyThreadSafetyMode.None), 5 };
159+
yield return new object[] { new Lazy<InitiallyExceptionThrowingCtor>(LazyThreadSafetyMode.PublicationOnly), 5 };
160+
}
161+
162+
[Theory]
163+
[MemberData(nameof(Ctor_ExceptionRecovery_MemberData))]
164+
public static void Ctor_ExceptionRecovery(Lazy<InitiallyExceptionThrowingCtor> lazy, int expected)
165+
{
166+
InitiallyExceptionThrowingCtor.counter = 0;
167+
InitiallyExceptionThrowingCtor result = null;
168+
for (var i = 0; i < 10; ++i)
169+
{
170+
try { result = lazy.Value; } catch (Exception) { }
171+
}
172+
Assert.Equal(result.Value, expected);
173+
}
174+
175+
private static void Value_ExceptionRecovery_IntImpl(Lazy<int> lazy, ref int counter, int expected)
176+
{
177+
counter = 0;
178+
int result = 0;
179+
for (var i = 0; i < 10; ++i)
180+
{
181+
try { result = lazy.Value; } catch (Exception) { }
182+
}
183+
Assert.Equal(result, expected);
184+
}
185+
186+
private static void Value_ExceptionRecovery_StringImpl(Lazy<string> lazy, ref int counter, string expected)
187+
{
188+
counter = 0;
189+
var result = default(string);
190+
for (var i = 0; i < 10; ++i)
191+
{
192+
try { result = lazy.Value; } catch (Exception) { }
193+
}
194+
Assert.Equal(expected, result);
195+
}
196+
197+
[Fact]
198+
public static void Value_ExceptionRecovery()
199+
{
200+
int counter = 0; // set in test function
201+
202+
var fint = new Func<int> (() => { if (++counter < 5) throw new Exception(); else return counter; });
203+
var fobj = new Func<string>(() => { if (++counter < 5) throw new Exception(); else return counter.ToString(); });
204+
205+
Value_ExceptionRecovery_IntImpl(new Lazy<int>(fint), ref counter, 0);
206+
Value_ExceptionRecovery_IntImpl(new Lazy<int>(fint, true), ref counter, 0);
207+
Value_ExceptionRecovery_IntImpl(new Lazy<int>(fint, false), ref counter, 0);
208+
Value_ExceptionRecovery_IntImpl(new Lazy<int>(fint, LazyThreadSafetyMode.ExecutionAndPublication), ref counter, 0);
209+
Value_ExceptionRecovery_IntImpl(new Lazy<int>(fint, LazyThreadSafetyMode.None), ref counter, 0);
210+
Value_ExceptionRecovery_IntImpl(new Lazy<int>(fint, LazyThreadSafetyMode.PublicationOnly), ref counter, 5);
211+
212+
Value_ExceptionRecovery_StringImpl(new Lazy<string>(fobj), ref counter, null);
213+
Value_ExceptionRecovery_StringImpl(new Lazy<string>(fobj, true), ref counter, null);
214+
Value_ExceptionRecovery_StringImpl(new Lazy<string>(fobj, false), ref counter, null);
215+
Value_ExceptionRecovery_StringImpl(new Lazy<string>(fobj, LazyThreadSafetyMode.ExecutionAndPublication), ref counter, null);
216+
Value_ExceptionRecovery_StringImpl(new Lazy<string>(fobj, LazyThreadSafetyMode.None), ref counter, null);
217+
Value_ExceptionRecovery_StringImpl(new Lazy<string>(fobj, LazyThreadSafetyMode.PublicationOnly), ref counter, 5.ToString());
218+
}
219+
220+
class MyException
221+
: Exception
222+
{
223+
public int Value { get; }
224+
225+
public MyException(int value)
226+
{
227+
Value = value;
228+
}
229+
}
230+
231+
public class ExceptionInCtor
232+
{
233+
public ExceptionInCtor() : this(99) { }
234+
235+
public ExceptionInCtor(int value)
236+
{
237+
throw new MyException(value);
238+
}
239+
}
240+
241+
public static IEnumerable<object[]> Value_Func_Exception_MemberData()
242+
{
243+
yield return new object[] { new Lazy<int>(() => { throw new MyException(99); }) };
244+
yield return new object[] { new Lazy<int>(() => { throw new MyException(99); }, true) };
245+
yield return new object[] { new Lazy<int>(() => { throw new MyException(99); }, false) };
246+
yield return new object[] { new Lazy<int>(() => { throw new MyException(99); }, LazyThreadSafetyMode.ExecutionAndPublication) };
247+
yield return new object[] { new Lazy<int>(() => { throw new MyException(99); }, LazyThreadSafetyMode.None) };
248+
yield return new object[] { new Lazy<int>(() => { throw new MyException(99); }, LazyThreadSafetyMode.PublicationOnly) };
249+
}
250+
251+
[Theory]
252+
[MemberData(nameof(Value_Func_Exception_MemberData))]
253+
public static void Value_Func_Exception(Lazy<int> lazy)
254+
{
255+
Assert.Throws<MyException>(() => lazy.Value);
256+
}
257+
258+
public static IEnumerable<object[]> Value_FuncCtor_Exception_MemberData()
259+
{
260+
yield return new object[] { new Lazy<ExceptionInCtor>(() => new ExceptionInCtor(99)) };
261+
yield return new object[] { new Lazy<ExceptionInCtor>(() => new ExceptionInCtor(99), true) };
262+
yield return new object[] { new Lazy<ExceptionInCtor>(() => new ExceptionInCtor(99), false) };
263+
yield return new object[] { new Lazy<ExceptionInCtor>(() => new ExceptionInCtor(99), LazyThreadSafetyMode.ExecutionAndPublication) };
264+
yield return new object[] { new Lazy<ExceptionInCtor>(() => new ExceptionInCtor(99), LazyThreadSafetyMode.None) };
265+
yield return new object[] { new Lazy<ExceptionInCtor>(() => new ExceptionInCtor(99), LazyThreadSafetyMode.PublicationOnly) };
266+
}
267+
268+
[Theory]
269+
[MemberData(nameof(Value_FuncCtor_Exception_MemberData))]
270+
public static void Value_FuncCtor_Exception(Lazy<ExceptionInCtor> lazy)
271+
{
272+
Assert.Throws<MyException>(() => lazy.Value);
273+
}
274+
275+
public static IEnumerable<object[]> Value_TargetInvocationException_MemberData()
276+
{
277+
yield return new object[] { new Lazy<ExceptionInCtor>() };
278+
yield return new object[] { new Lazy<ExceptionInCtor>(true) };
279+
yield return new object[] { new Lazy<ExceptionInCtor>(false) };
280+
yield return new object[] { new Lazy<ExceptionInCtor>(LazyThreadSafetyMode.ExecutionAndPublication) };
281+
yield return new object[] { new Lazy<ExceptionInCtor>(LazyThreadSafetyMode.None) };
282+
yield return new object[] { new Lazy<ExceptionInCtor>(LazyThreadSafetyMode.PublicationOnly) };
283+
}
110284

111-
int x = 0;
112-
Lazy<string> lazy = null;
113-
lazy = new Lazy<string>(() => x++ < 5 ? lazy.Value : "Test", true);
285+
[Theory]
286+
[MemberData(nameof(Value_TargetInvocationException_MemberData))]
287+
public static void Value_TargetInvocationException(Lazy<ExceptionInCtor> lazy)
288+
{
289+
Assert.Throws<TargetInvocationException>(() => lazy.Value);
290+
}
291+
292+
public static IEnumerable<object[]> Exceptions_Func_Idempotent_MemberData()
293+
{
294+
yield return new object[] { new Lazy<int>(() => { throw new MyException(99); }) };
295+
yield return new object[] { new Lazy<int>(() => { throw new MyException(99); }, true) };
296+
yield return new object[] { new Lazy<int>(() => { throw new MyException(99); }, false) };
297+
yield return new object[] { new Lazy<int>(() => { throw new MyException(99); }, LazyThreadSafetyMode.ExecutionAndPublication) };
298+
yield return new object[] { new Lazy<int>(() => { throw new MyException(99); }, LazyThreadSafetyMode.None) };
299+
}
114300

115-
Assert.Throws<InvalidOperationException>(() => lazilyAllocatedValue = lazy.Value);
116-
Assert.Equal("abc", lazilyAllocatedValue);
301+
[Theory]
302+
[MemberData(nameof(Exceptions_Func_Idempotent_MemberData))]
303+
public static void Exceptions_Func_Idempotent(Lazy<int> x)
304+
{
305+
var e = Assert.ThrowsAny<Exception>(() => x.Value);
306+
Assert.Same(e, Assert.ThrowsAny<Exception>(() => x.Value));
307+
}
308+
309+
public static IEnumerable<object[]> Exceptions_Ctor_Idempotent_MemberData()
310+
{
311+
yield return new object[] { new Lazy<ExceptionInCtor>(() => new ExceptionInCtor(99)) };
312+
yield return new object[] { new Lazy<ExceptionInCtor>(() => new ExceptionInCtor(99), true) };
313+
yield return new object[] { new Lazy<ExceptionInCtor>(() => new ExceptionInCtor(99), false) };
314+
yield return new object[] { new Lazy<ExceptionInCtor>(() => new ExceptionInCtor(99), LazyThreadSafetyMode.ExecutionAndPublication) };
315+
yield return new object[] { new Lazy<ExceptionInCtor>(() => new ExceptionInCtor(99), LazyThreadSafetyMode.None) };
316+
}
317+
318+
[Theory]
319+
[MemberData(nameof(Exceptions_Ctor_Idempotent_MemberData))]
320+
public static void Exceptions_Ctor_Idempotent(Lazy<ExceptionInCtor> x)
321+
{
322+
var e = Assert.ThrowsAny<Exception>(() => x.Value);
323+
Assert.Same(e, Assert.ThrowsAny<Exception>(() => x.Value));
324+
}
325+
326+
public static IEnumerable<object[]> Exceptions_Func_NotIdempotent_MemberData()
327+
{
328+
yield return new object[] { new Lazy<int>(() => { throw new MyException(99); }, LazyThreadSafetyMode.PublicationOnly) };
329+
}
330+
331+
public static IEnumerable<object[]> Exceptions_Ctor_NotIdempotent_MemberData()
332+
{
333+
yield return new object[] { new Lazy<ExceptionInCtor>() };
334+
yield return new object[] { new Lazy<ExceptionInCtor>(true) };
335+
yield return new object[] { new Lazy<ExceptionInCtor>(false) };
336+
yield return new object[] { new Lazy<ExceptionInCtor>(LazyThreadSafetyMode.ExecutionAndPublication) };
337+
yield return new object[] { new Lazy<ExceptionInCtor>(LazyThreadSafetyMode.None) };
338+
yield return new object[] { new Lazy<ExceptionInCtor>(LazyThreadSafetyMode.PublicationOnly) };
339+
yield return new object[] { new Lazy<ExceptionInCtor>(() => new ExceptionInCtor(99), LazyThreadSafetyMode.PublicationOnly) };
340+
}
341+
342+
[Theory]
343+
[MemberData(nameof(Exceptions_Func_NotIdempotent_MemberData))]
344+
public static void Exceptions_Func_NotIdempotent(Lazy<int> x)
345+
{
346+
var e = Assert.ThrowsAny<Exception>(() => x.Value);
347+
Assert.NotSame(e, Assert.ThrowsAny<Exception>(() => x.Value));
348+
}
349+
350+
[Theory]
351+
[MemberData(nameof(Exceptions_Ctor_NotIdempotent_MemberData))]
352+
public static void Exceptions_Ctor_NotIdempotent(Lazy<ExceptionInCtor> x)
353+
{
354+
var e = Assert.ThrowsAny<Exception>(() => x.Value);
355+
Assert.NotSame(e, Assert.ThrowsAny<Exception>(() => x.Value));
356+
}
357+
358+
[Fact]
359+
public static void Serialization_ValueType()
360+
{
361+
var stream = new MemoryStream();
362+
var formatter = new BinaryFormatter();
363+
formatter.Serialize(stream, new Lazy<int>(() => 42));
364+
stream.Seek(0, SeekOrigin.Begin);
365+
366+
var fortytwo = (Lazy<int>)formatter.Deserialize(stream);
367+
Assert.True(fortytwo.IsValueCreated);
368+
Assert.Equal(fortytwo.Value, 42);
369+
}
370+
371+
[Fact]
372+
public static void Serialization_RefType()
373+
{
374+
var stream = new MemoryStream();
375+
var formatter = new BinaryFormatter();
376+
formatter.Serialize(stream, new Lazy<string>(() => "42"));
377+
stream.Seek(0, SeekOrigin.Begin);
378+
379+
var x = BinaryFormatterHelpers.Clone(new object());
380+
var fortytwo = (Lazy<string>)formatter.Deserialize(stream);
381+
Assert.True(fortytwo.IsValueCreated);
382+
Assert.Equal(fortytwo.Value, "42");
117383
}
118384

119385
[Theory]

0 commit comments

Comments
 (0)