|
2 | 2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | 3 | // See the LICENSE file in the project root for more information. |
4 | 4 |
|
| 5 | +usingSystem.Collections.Generic; |
5 | 6 | usingSystem.Threading; |
| 7 | +usingSystem.Reflection; |
| 8 | +usingSystem.IO; |
| 9 | +usingSystem.Runtime.Serialization.Formatters.Binary; |
| 10 | +usingSystem.Runtime.Serialization.Formatters.Tests; |
6 | 11 | usingXunit; |
7 | 12 |
|
8 | 13 | namespaceSystem.Tests |
@@ -103,17 +108,278 @@ public static void ToString_DoesntForceAllocation() |
103 | 108 | Assert.Equal("1",lazy.ToString()); |
104 | 109 | } |
105 | 110 |
|
| 111 | +privatestaticvoidValue_Invalid_Impl<T>(refLazy<T>x,Lazy<T>lazy) |
| 112 | +{ |
| 113 | +x=lazy; |
| 114 | +Assert.Throws<InvalidOperationException>(()=>lazy.Value); |
| 115 | +} |
| 116 | + |
106 | 117 | [Fact] |
107 | 118 | publicstaticvoidValue_Invalid() |
108 | 119 | { |
109 | | -stringlazilyAllocatedValue="abc"; |
| 120 | +Lazy<int>x=null; |
| 121 | +Func<int>f=()=>x.Value; |
| 122 | + |
| 123 | +Value_Invalid_Impl(refx,newLazy<int>(f)); |
| 124 | +Value_Invalid_Impl(refx,newLazy<int>(f,true)); |
| 125 | +Value_Invalid_Impl(refx,newLazy<int>(f,false)); |
| 126 | +Value_Invalid_Impl(refx,newLazy<int>(f,LazyThreadSafetyMode.ExecutionAndPublication)); |
| 127 | +Value_Invalid_Impl(refx,newLazy<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 | +publicclassInitiallyExceptionThrowingCtor |
| 134 | +{ |
| 135 | +publicstaticintcounter=0; |
| 136 | +publicstaticintgetValue() |
| 137 | +{ |
| 138 | +if(++counter<5) |
| 139 | +thrownewException(); |
| 140 | +else |
| 141 | +returncounter; |
| 142 | +} |
| 143 | + |
| 144 | +publicintValue{get;} |
| 145 | + |
| 146 | +publicInitiallyExceptionThrowingCtor() |
| 147 | +{ |
| 148 | +Value=getValue(); |
| 149 | +} |
| 150 | +} |
| 151 | + |
| 152 | +publicstaticIEnumerable<object[]>Ctor_ExceptionRecovery_MemberData() |
| 153 | +{ |
| 154 | +yieldreturnnewobject[]{newLazy<InitiallyExceptionThrowingCtor>(),5}; |
| 155 | +yieldreturnnewobject[]{newLazy<InitiallyExceptionThrowingCtor>(true),5}; |
| 156 | +yieldreturnnewobject[]{newLazy<InitiallyExceptionThrowingCtor>(false),5}; |
| 157 | +yieldreturnnewobject[]{newLazy<InitiallyExceptionThrowingCtor>(LazyThreadSafetyMode.ExecutionAndPublication),5}; |
| 158 | +yieldreturnnewobject[]{newLazy<InitiallyExceptionThrowingCtor>(LazyThreadSafetyMode.None),5}; |
| 159 | +yieldreturnnewobject[]{newLazy<InitiallyExceptionThrowingCtor>(LazyThreadSafetyMode.PublicationOnly),5}; |
| 160 | +} |
| 161 | + |
| 162 | +[Theory] |
| 163 | +[MemberData(nameof(Ctor_ExceptionRecovery_MemberData))] |
| 164 | +publicstaticvoidCtor_ExceptionRecovery(Lazy<InitiallyExceptionThrowingCtor>lazy,intexpected) |
| 165 | +{ |
| 166 | +InitiallyExceptionThrowingCtor.counter=0; |
| 167 | +InitiallyExceptionThrowingCtorresult=null; |
| 168 | +for(vari=0;i<10;++i) |
| 169 | +{ |
| 170 | +try{result=lazy.Value;}catch(Exception){} |
| 171 | +} |
| 172 | +Assert.Equal(result.Value,expected); |
| 173 | +} |
| 174 | + |
| 175 | +privatestaticvoidValue_ExceptionRecovery_IntImpl(Lazy<int>lazy,refintcounter,intexpected) |
| 176 | +{ |
| 177 | +counter=0; |
| 178 | +intresult=0; |
| 179 | +for(vari=0;i<10;++i) |
| 180 | +{ |
| 181 | +try{result=lazy.Value;}catch(Exception){} |
| 182 | +} |
| 183 | +Assert.Equal(result,expected); |
| 184 | +} |
| 185 | + |
| 186 | +privatestaticvoidValue_ExceptionRecovery_StringImpl(Lazy<string>lazy,refintcounter,stringexpected) |
| 187 | +{ |
| 188 | +counter=0; |
| 189 | +varresult=default(string); |
| 190 | +for(vari=0;i<10;++i) |
| 191 | +{ |
| 192 | +try{result=lazy.Value;}catch(Exception){} |
| 193 | +} |
| 194 | +Assert.Equal(expected,result); |
| 195 | +} |
| 196 | + |
| 197 | +[Fact] |
| 198 | +publicstaticvoidValue_ExceptionRecovery() |
| 199 | +{ |
| 200 | +intcounter=0;// set in test function |
| 201 | + |
| 202 | +varfint=newFunc<int>(()=>{if(++counter<5)thrownewException();elsereturncounter;}); |
| 203 | +varfobj=newFunc<string>(()=>{if(++counter<5)thrownewException();elsereturncounter.ToString();}); |
| 204 | + |
| 205 | +Value_ExceptionRecovery_IntImpl(newLazy<int>(fint),refcounter,0); |
| 206 | +Value_ExceptionRecovery_IntImpl(newLazy<int>(fint,true),refcounter,0); |
| 207 | +Value_ExceptionRecovery_IntImpl(newLazy<int>(fint,false),refcounter,0); |
| 208 | +Value_ExceptionRecovery_IntImpl(newLazy<int>(fint,LazyThreadSafetyMode.ExecutionAndPublication),refcounter,0); |
| 209 | +Value_ExceptionRecovery_IntImpl(newLazy<int>(fint,LazyThreadSafetyMode.None),refcounter,0); |
| 210 | +Value_ExceptionRecovery_IntImpl(newLazy<int>(fint,LazyThreadSafetyMode.PublicationOnly),refcounter,5); |
| 211 | + |
| 212 | +Value_ExceptionRecovery_StringImpl(newLazy<string>(fobj),refcounter,null); |
| 213 | +Value_ExceptionRecovery_StringImpl(newLazy<string>(fobj,true),refcounter,null); |
| 214 | +Value_ExceptionRecovery_StringImpl(newLazy<string>(fobj,false),refcounter,null); |
| 215 | +Value_ExceptionRecovery_StringImpl(newLazy<string>(fobj,LazyThreadSafetyMode.ExecutionAndPublication),refcounter,null); |
| 216 | +Value_ExceptionRecovery_StringImpl(newLazy<string>(fobj,LazyThreadSafetyMode.None),refcounter,null); |
| 217 | +Value_ExceptionRecovery_StringImpl(newLazy<string>(fobj,LazyThreadSafetyMode.PublicationOnly),refcounter,5.ToString()); |
| 218 | +} |
| 219 | + |
| 220 | +classMyException |
| 221 | +:Exception |
| 222 | +{ |
| 223 | +publicintValue{get;} |
| 224 | + |
| 225 | +publicMyException(intvalue) |
| 226 | +{ |
| 227 | +Value=value; |
| 228 | +} |
| 229 | +} |
| 230 | + |
| 231 | +publicclassExceptionInCtor |
| 232 | +{ |
| 233 | +publicExceptionInCtor():this(99){} |
| 234 | + |
| 235 | +publicExceptionInCtor(intvalue) |
| 236 | +{ |
| 237 | +thrownewMyException(value); |
| 238 | +} |
| 239 | +} |
| 240 | + |
| 241 | +publicstaticIEnumerable<object[]>Value_Func_Exception_MemberData() |
| 242 | +{ |
| 243 | +yieldreturnnewobject[]{newLazy<int>(()=>{thrownewMyException(99);})}; |
| 244 | +yieldreturnnewobject[]{newLazy<int>(()=>{thrownewMyException(99);},true)}; |
| 245 | +yieldreturnnewobject[]{newLazy<int>(()=>{thrownewMyException(99);},false)}; |
| 246 | +yieldreturnnewobject[]{newLazy<int>(()=>{thrownewMyException(99);},LazyThreadSafetyMode.ExecutionAndPublication)}; |
| 247 | +yieldreturnnewobject[]{newLazy<int>(()=>{thrownewMyException(99);},LazyThreadSafetyMode.None)}; |
| 248 | +yieldreturnnewobject[]{newLazy<int>(()=>{thrownewMyException(99);},LazyThreadSafetyMode.PublicationOnly)}; |
| 249 | +} |
| 250 | + |
| 251 | +[Theory] |
| 252 | +[MemberData(nameof(Value_Func_Exception_MemberData))] |
| 253 | +publicstaticvoidValue_Func_Exception(Lazy<int>lazy) |
| 254 | +{ |
| 255 | +Assert.Throws<MyException>(()=>lazy.Value); |
| 256 | +} |
| 257 | + |
| 258 | +publicstaticIEnumerable<object[]>Value_FuncCtor_Exception_MemberData() |
| 259 | +{ |
| 260 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(()=>newExceptionInCtor(99))}; |
| 261 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(()=>newExceptionInCtor(99),true)}; |
| 262 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(()=>newExceptionInCtor(99),false)}; |
| 263 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(()=>newExceptionInCtor(99),LazyThreadSafetyMode.ExecutionAndPublication)}; |
| 264 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(()=>newExceptionInCtor(99),LazyThreadSafetyMode.None)}; |
| 265 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(()=>newExceptionInCtor(99),LazyThreadSafetyMode.PublicationOnly)}; |
| 266 | +} |
| 267 | + |
| 268 | +[Theory] |
| 269 | +[MemberData(nameof(Value_FuncCtor_Exception_MemberData))] |
| 270 | +publicstaticvoidValue_FuncCtor_Exception(Lazy<ExceptionInCtor>lazy) |
| 271 | +{ |
| 272 | +Assert.Throws<MyException>(()=>lazy.Value); |
| 273 | +} |
| 274 | + |
| 275 | +publicstaticIEnumerable<object[]>Value_TargetInvocationException_MemberData() |
| 276 | +{ |
| 277 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>()}; |
| 278 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(true)}; |
| 279 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(false)}; |
| 280 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(LazyThreadSafetyMode.ExecutionAndPublication)}; |
| 281 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(LazyThreadSafetyMode.None)}; |
| 282 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(LazyThreadSafetyMode.PublicationOnly)}; |
| 283 | +} |
110 | 284 |
|
111 | | -intx=0; |
112 | | -Lazy<string>lazy=null; |
113 | | -lazy=newLazy<string>(()=>x++<5?lazy.Value:"Test",true); |
| 285 | +[Theory] |
| 286 | +[MemberData(nameof(Value_TargetInvocationException_MemberData))] |
| 287 | +publicstaticvoidValue_TargetInvocationException(Lazy<ExceptionInCtor>lazy) |
| 288 | +{ |
| 289 | +Assert.Throws<TargetInvocationException>(()=>lazy.Value); |
| 290 | +} |
| 291 | + |
| 292 | +publicstaticIEnumerable<object[]>Exceptions_Func_Idempotent_MemberData() |
| 293 | +{ |
| 294 | +yieldreturnnewobject[]{newLazy<int>(()=>{thrownewMyException(99);})}; |
| 295 | +yieldreturnnewobject[]{newLazy<int>(()=>{thrownewMyException(99);},true)}; |
| 296 | +yieldreturnnewobject[]{newLazy<int>(()=>{thrownewMyException(99);},false)}; |
| 297 | +yieldreturnnewobject[]{newLazy<int>(()=>{thrownewMyException(99);},LazyThreadSafetyMode.ExecutionAndPublication)}; |
| 298 | +yieldreturnnewobject[]{newLazy<int>(()=>{thrownewMyException(99);},LazyThreadSafetyMode.None)}; |
| 299 | +} |
114 | 300 |
|
115 | | -Assert.Throws<InvalidOperationException>(()=>lazilyAllocatedValue=lazy.Value); |
116 | | -Assert.Equal("abc",lazilyAllocatedValue); |
| 301 | +[Theory] |
| 302 | +[MemberData(nameof(Exceptions_Func_Idempotent_MemberData))] |
| 303 | +publicstaticvoidExceptions_Func_Idempotent(Lazy<int>x) |
| 304 | +{ |
| 305 | +vare=Assert.ThrowsAny<Exception>(()=>x.Value); |
| 306 | +Assert.Same(e,Assert.ThrowsAny<Exception>(()=>x.Value)); |
| 307 | +} |
| 308 | + |
| 309 | +publicstaticIEnumerable<object[]>Exceptions_Ctor_Idempotent_MemberData() |
| 310 | +{ |
| 311 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(()=>newExceptionInCtor(99))}; |
| 312 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(()=>newExceptionInCtor(99),true)}; |
| 313 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(()=>newExceptionInCtor(99),false)}; |
| 314 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(()=>newExceptionInCtor(99),LazyThreadSafetyMode.ExecutionAndPublication)}; |
| 315 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(()=>newExceptionInCtor(99),LazyThreadSafetyMode.None)}; |
| 316 | +} |
| 317 | + |
| 318 | +[Theory] |
| 319 | +[MemberData(nameof(Exceptions_Ctor_Idempotent_MemberData))] |
| 320 | +publicstaticvoidExceptions_Ctor_Idempotent(Lazy<ExceptionInCtor>x) |
| 321 | +{ |
| 322 | +vare=Assert.ThrowsAny<Exception>(()=>x.Value); |
| 323 | +Assert.Same(e,Assert.ThrowsAny<Exception>(()=>x.Value)); |
| 324 | +} |
| 325 | + |
| 326 | +publicstaticIEnumerable<object[]>Exceptions_Func_NotIdempotent_MemberData() |
| 327 | +{ |
| 328 | +yieldreturnnewobject[]{newLazy<int>(()=>{thrownewMyException(99);},LazyThreadSafetyMode.PublicationOnly)}; |
| 329 | +} |
| 330 | + |
| 331 | +publicstaticIEnumerable<object[]>Exceptions_Ctor_NotIdempotent_MemberData() |
| 332 | +{ |
| 333 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>()}; |
| 334 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(true)}; |
| 335 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(false)}; |
| 336 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(LazyThreadSafetyMode.ExecutionAndPublication)}; |
| 337 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(LazyThreadSafetyMode.None)}; |
| 338 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(LazyThreadSafetyMode.PublicationOnly)}; |
| 339 | +yieldreturnnewobject[]{newLazy<ExceptionInCtor>(()=>newExceptionInCtor(99),LazyThreadSafetyMode.PublicationOnly)}; |
| 340 | +} |
| 341 | + |
| 342 | +[Theory] |
| 343 | +[MemberData(nameof(Exceptions_Func_NotIdempotent_MemberData))] |
| 344 | +publicstaticvoidExceptions_Func_NotIdempotent(Lazy<int>x) |
| 345 | +{ |
| 346 | +vare=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 | +publicstaticvoidExceptions_Ctor_NotIdempotent(Lazy<ExceptionInCtor>x) |
| 353 | +{ |
| 354 | +vare=Assert.ThrowsAny<Exception>(()=>x.Value); |
| 355 | +Assert.NotSame(e,Assert.ThrowsAny<Exception>(()=>x.Value)); |
| 356 | +} |
| 357 | + |
| 358 | +[Fact] |
| 359 | +publicstaticvoidSerialization_ValueType() |
| 360 | +{ |
| 361 | +varstream=newMemoryStream(); |
| 362 | +varformatter=newBinaryFormatter(); |
| 363 | +formatter.Serialize(stream,newLazy<int>(()=>42)); |
| 364 | +stream.Seek(0,SeekOrigin.Begin); |
| 365 | + |
| 366 | +varfortytwo=(Lazy<int>)formatter.Deserialize(stream); |
| 367 | +Assert.True(fortytwo.IsValueCreated); |
| 368 | +Assert.Equal(fortytwo.Value,42); |
| 369 | +} |
| 370 | + |
| 371 | +[Fact] |
| 372 | +publicstaticvoidSerialization_RefType() |
| 373 | +{ |
| 374 | +varstream=newMemoryStream(); |
| 375 | +varformatter=newBinaryFormatter(); |
| 376 | +formatter.Serialize(stream,newLazy<string>(()=>"42")); |
| 377 | +stream.Seek(0,SeekOrigin.Begin); |
| 378 | + |
| 379 | +varx=BinaryFormatterHelpers.Clone(newobject()); |
| 380 | +varfortytwo=(Lazy<string>)formatter.Deserialize(stream); |
| 381 | +Assert.True(fortytwo.IsValueCreated); |
| 382 | +Assert.Equal(fortytwo.Value,"42"); |
117 | 383 | } |
118 | 384 |
|
119 | 385 | [Theory] |
|