A game about forced loneliness, made by TACStudios
at master 545 lines 16 kB view raw
1using System; 2using NUnit.Framework; 3using Unity.Burst; 4using Unity.Collections; 5using Unity.Collections.LowLevel.Unsafe; 6using Unity.Collections.Tests; 7using Unity.Jobs; 8using UnityEngine; 9using Assert = FastAssert; 10 11internal class NativeStreamTests : CollectionsTestFixture 12{ 13 [BurstCompile(CompileSynchronously = true)] 14 struct WriteInts : IJobParallelFor 15 { 16 public NativeStream.Writer Writer; 17 18 public void Execute(int index) 19 { 20 Writer.BeginForEachIndex(index); 21 for (int i = 0; i != index; i++) 22 Writer.Write(i); 23 Writer.EndForEachIndex(); 24 } 25 } 26 27 struct ReadInts : IJobParallelFor 28 { 29 public NativeStream.Reader Reader; 30 31 public void Execute(int index) 32 { 33 int count = Reader.BeginForEachIndex(index); 34 Assert.AreEqual(count, index); 35 36 for (int i = 0; i != index; i++) 37 { 38 Assert.AreEqual(index - i, Reader.RemainingItemCount); 39 var peekedValue = Reader.Peek<int>(); 40 var value = Reader.Read<int>(); 41 Assert.AreEqual(i, value); 42 Assert.AreEqual(i, peekedValue); 43 } 44 45 Reader.EndForEachIndex(); 46 } 47 } 48 49 [Test] 50 public void NativeStream_PopulateInts([Values(1, 100, 200)] int count, [Values(1, 3, 10)] int batchSize) 51 { 52 var stream = new NativeStream(count, CommonRwdAllocator.Handle); 53 var fillInts = new WriteInts {Writer = stream.AsWriter()}; 54 var jobHandle = fillInts.Schedule(count, batchSize); 55 56 var compareInts = new ReadInts {Reader = stream.AsReader()}; 57 var res0 = compareInts.Schedule(count, batchSize, jobHandle); 58 var res1 = compareInts.Schedule(count, batchSize, jobHandle); 59 60 res0.Complete(); 61 res1.Complete(); 62 63 stream.Dispose(); 64 } 65 66 static void ExpectedCount(ref NativeStream container, int expected) 67 { 68 Assert.AreEqual(expected == 0, container.IsEmpty()); 69 Assert.AreEqual(expected, container.Count()); 70 } 71 72 [Test] 73 public void NativeStream_CreateAndDestroy([Values(1, 100, 200)] int count) 74 { 75 var stream = new NativeStream(count, Allocator.Temp); 76 77 Assert.IsTrue(stream.IsCreated); 78 Assert.IsTrue(stream.ForEachCount == count); 79 ExpectedCount(ref stream, 0); 80 81 stream.Dispose(); 82 Assert.IsFalse(stream.IsCreated); 83 } 84 85 [Test] 86 public void NativeStream_ItemCount([Values(1, 100, 200)] int count, [Values(1, 3, 10)] int batchSize) 87 { 88 var stream = new NativeStream(count, CommonRwdAllocator.Handle); 89 var fillInts = new WriteInts {Writer = stream.AsWriter()}; 90 fillInts.Schedule(count, batchSize).Complete(); 91 92 ExpectedCount(ref stream, count * (count - 1) / 2); 93 94 stream.Dispose(); 95 } 96 97 [Test] 98 public void NativeStream_ToArray([Values(1, 100, 200)] int count, [Values(1, 3, 10)] int batchSize) 99 { 100 var stream = new NativeStream(count, CommonRwdAllocator.Handle); 101 var fillInts = new WriteInts {Writer = stream.AsWriter()}; 102 fillInts.Schedule(count, batchSize).Complete(); 103 ExpectedCount(ref stream, count * (count - 1) / 2); 104 105 var array = stream.ToNativeArray<int>(Allocator.Temp); 106 int itemIndex = 0; 107 108 for (int i = 0; i != count; ++i) 109 { 110 for (int j = 0; j < i; ++j) 111 { 112 Assert.AreEqual(j, array[itemIndex]); 113 itemIndex++; 114 } 115 } 116 117 array.Dispose(); 118 stream.Dispose(); 119 } 120 121 [Test] 122 public void NativeStream_DisposeJob() 123 { 124 var stream = new NativeStream(100, CommonRwdAllocator.Handle); 125 Assert.IsTrue(stream.IsCreated); 126 127 var fillInts = new WriteInts {Writer = stream.AsWriter()}; 128 var writerJob = fillInts.Schedule(100, 16); 129 130 var disposeJob = stream.Dispose(writerJob); 131 Assert.IsFalse(stream.IsCreated); 132 133 disposeJob.Complete(); 134 } 135 136#if ENABLE_UNITY_COLLECTIONS_CHECKS 137 [Test] 138 public void NativeStream_ParallelWriteThrows() 139 { 140 var stream = new NativeStream(100, CommonRwdAllocator.Handle); 141 var fillInts = new WriteInts {Writer = stream.AsWriter()}; 142 143 var writerJob = fillInts.Schedule(100, 16); 144 Assert.Throws<InvalidOperationException>(() => fillInts.Schedule(100, 16)); 145 146 writerJob.Complete(); 147 stream.Dispose(); 148 } 149 150 [Test] 151 public void NativeStream_ScheduleCreateThrows_NativeList() 152 { 153 var container = new NativeList<int>(Allocator.Persistent); 154 container.Add(2); 155 156 NativeStream stream; 157 var jobHandle = NativeStream.ScheduleConstruct(out stream, container, default, CommonRwdAllocator.Handle); 158 159 Assert.Throws<InvalidOperationException>(() => { int val = stream.ForEachCount; }); 160 161 jobHandle.Complete(); 162 163 Assert.AreEqual(1, stream.ForEachCount); 164 165 stream.Dispose(); 166 container.Dispose(); 167 } 168 169 [Test] 170 public void NativeStream_ScheduleCreateThrows_NativeArray() 171 { 172 var container = new NativeArray<int>(1, Allocator.Persistent); 173 container[0] = 1; 174 175 NativeStream stream; 176 var jobHandle = NativeStream.ScheduleConstruct(out stream, container, default, CommonRwdAllocator.Handle); 177 178 Assert.Throws<InvalidOperationException>(() => { int val = stream.ForEachCount; }); 179 180 jobHandle.Complete(); 181 182 Assert.AreEqual(1, stream.ForEachCount); 183 184 stream.Dispose(); 185 container.Dispose(); 186 } 187 188 [Test] 189 public void NativeStream_OutOfBoundsWriteThrows() 190 { 191 var stream = new NativeStream(1, Allocator.Temp); 192 var writer = stream.AsWriter(); 193 Assert.Throws<ArgumentException>(() => writer.BeginForEachIndex(-1)); 194 Assert.Throws<ArgumentException>(() => writer.BeginForEachIndex(2)); 195 196 stream.Dispose(); 197 } 198 199 [Test] 200 public void NativeStream_EndForEachIndexWithoutBeginThrows() 201 { 202 var stream = new NativeStream(1, Allocator.Temp); 203 var writer = stream.AsWriter(); 204 Assert.Throws<ArgumentException>(() => writer.EndForEachIndex()); 205 206 stream.Dispose(); 207 } 208 209 [Test] 210 public void NativeStream_WriteWithoutBeginThrows() 211 { 212 var stream = new NativeStream(1, Allocator.Temp); 213 var writer = stream.AsWriter(); 214 Assert.Throws<ArgumentException>(() => writer.Write(5)); 215 216 stream.Dispose(); 217 } 218 219 [Test] 220 public void NativeStream_WriteAfterEndThrows() 221 { 222 var stream = new NativeStream(1, Allocator.Temp); 223 var writer = stream.AsWriter(); 224 writer.BeginForEachIndex(0); 225 writer.Write(2); 226 Assert.AreEqual(1, writer.ForEachCount); 227 writer.EndForEachIndex(); 228 229 Assert.AreEqual(1, writer.ForEachCount); 230 Assert.Throws<ArgumentException>(() => writer.Write(5)); 231 232 stream.Dispose(); 233 } 234 235 [Test] 236 public void NativeStream_UnbalancedBeginThrows() 237 { 238 var stream = new NativeStream(2, Allocator.Temp); 239 var writer = stream.AsWriter(); 240 writer.BeginForEachIndex(0); 241 // Missing EndForEachIndex(); 242 Assert.Throws<ArgumentException>(() => writer.BeginForEachIndex(1)); 243 244 stream.Dispose(); 245 } 246 247 static void CreateBlockStream1And2Int(out NativeStream stream) 248 { 249 stream = new NativeStream(2, Allocator.Temp); 250 251 var writer = stream.AsWriter(); 252 writer.BeginForEachIndex(0); 253 writer.Write(0); 254 writer.EndForEachIndex(); 255 256 writer.BeginForEachIndex(1); 257 writer.Write(1); 258 writer.Write(2); 259 writer.EndForEachIndex(); 260 } 261 262 [Test] 263 public void NativeStream_IncompleteReadThrows() 264 { 265 NativeStream stream; 266 CreateBlockStream1And2Int(out stream); 267 268 var reader = stream.AsReader(); 269 270 reader.BeginForEachIndex(0); 271 reader.Read<byte>(); 272 Assert.Throws<ArgumentException>(() => reader.EndForEachIndex()); 273 274 reader.BeginForEachIndex(1); 275 276 stream.Dispose(); 277 } 278 279 [Test] 280 public void NativeStream_ReadWithoutBeginThrows() 281 { 282 NativeStream stream; 283 CreateBlockStream1And2Int(out stream); 284 285 var reader = stream.AsReader(); 286 Assert.Throws<ArgumentException>(() => reader.Read<int>()); 287 288 stream.Dispose(); 289 } 290 291 [Test] 292 public void NativeStream_TooManyReadsThrows() 293 { 294 NativeStream stream; 295 CreateBlockStream1And2Int(out stream); 296 297 var reader = stream.AsReader(); 298 299 reader.BeginForEachIndex(0); 300 reader.Read<byte>(); 301 Assert.Throws<ArgumentException>(() => reader.Read<byte>()); 302 303 stream.Dispose(); 304 } 305 306 [Test] 307 public void NativeStream_OutOfBoundsReadThrows() 308 { 309 NativeStream stream; 310 CreateBlockStream1And2Int(out stream); 311 312 var reader = stream.AsReader(); 313 314 reader.BeginForEachIndex(0); 315 Assert.Throws<ArgumentException>(() => reader.Read<long>()); 316 317 stream.Dispose(); 318 } 319 320 [Test] 321 public void NativeStream_CopyWriterByValueThrows() 322 { 323 var stream = new NativeStream(1, Allocator.Temp); 324 var writer = stream.AsWriter(); 325 326 writer.BeginForEachIndex(0); 327 328 Assert.Throws<ArgumentException>(() => 329 { 330 var writerCopy = writer; 331 writerCopy.Write(5); 332 }); 333 334 Assert.Throws<ArgumentException>(() => 335 { 336 var writerCopy = writer; 337 writerCopy.BeginForEachIndex(1); 338 writerCopy.Write(5); 339 }); 340 341 stream.Dispose(); 342 } 343 344 [Test] 345 public void NativeStream_WriteSameIndexTwiceThrows() 346 { 347 var stream = new NativeStream(1, Allocator.Temp); 348 var writer = stream.AsWriter(); 349 350 writer.BeginForEachIndex(0); 351 writer.Write(1); 352 writer.EndForEachIndex(); 353 354 Assert.Throws<ArgumentException>(() => 355 { 356 writer.BeginForEachIndex(0); 357 writer.Write(2); 358 }); 359 360 stream.Dispose(); 361 } 362 363 static void WriteNotPassedByRef(NativeStream.Writer notPassedByRef) 364 { 365 notPassedByRef.Write(10); 366 } 367 368 static void WritePassedByRef(ref NativeStream.Writer passedByRef) 369 { 370 passedByRef.Write(10); 371 } 372 373 [Test] 374 public void NativeStream_ThrowsOnIncorrectUsage() 375 { 376 var stream = new NativeStream(1, Allocator.Temp); 377 var writer = stream.AsWriter(); 378 379 Assert.Throws<ArgumentException>(() => stream.AsWriter().Write(10)); 380 381 writer.BeginForEachIndex(0); 382 Assert.Throws<ArgumentException>(() => WriteNotPassedByRef(writer)); 383 Assert.DoesNotThrow(() => WritePassedByRef(ref writer)); 384 writer.EndForEachIndex(); 385 386 stream.Dispose(); 387 } 388 389#endif 390 391 [Test] 392 public void NativeStream_CustomAllocatorTest() 393 { 394 AllocatorManager.Initialize(); 395 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent); 396 ref var allocator = ref allocatorHelper.Allocator; 397 allocator.Initialize(); 398 399 using (var container = new NativeStream(1, allocator.Handle)) 400 { 401 } 402 403 Assert.IsTrue(allocator.WasUsed); 404 allocator.Dispose(); 405 allocatorHelper.Dispose(); 406 AllocatorManager.Shutdown(); 407 } 408 409 [BurstCompile] 410 struct BurstedCustomAllocatorJob : IJob 411 { 412 [NativeDisableUnsafePtrRestriction] 413 public unsafe CustomAllocatorTests.CountingAllocator* Allocator; 414 415 public void Execute() 416 { 417 unsafe 418 { 419 using (var container = new NativeStream(1, Allocator->Handle)) 420 { 421 } 422 } 423 } 424 } 425 426 [Test] 427 public unsafe void NativeStream_BurstedCustomAllocatorTest() 428 { 429 AllocatorManager.Initialize(); 430 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent); 431 ref var allocator = ref allocatorHelper.Allocator; 432 allocator.Initialize(); 433 434 var allocatorPtr = (CustomAllocatorTests.CountingAllocator*)UnsafeUtility.AddressOf<CustomAllocatorTests.CountingAllocator>(ref allocator); 435 unsafe 436 { 437 var handle = new BurstedCustomAllocatorJob {Allocator = allocatorPtr}.Schedule(); 438 handle.Complete(); 439 } 440 441 Assert.IsTrue(allocator.WasUsed); 442 allocator.Dispose(); 443 allocatorHelper.Dispose(); 444 AllocatorManager.Shutdown(); 445 } 446 447 public struct NestedContainer 448 { 449 public NativeList<int> data; 450 } 451 452 [Test] 453 public void NativeStream_Nested() 454 { 455 var inner = new NativeList<int>(CommonRwdAllocator.Handle); 456 NestedContainer nestedStruct = new NestedContainer { data = inner }; 457 458 var containerNestedStruct = new NativeStream(100, CommonRwdAllocator.Handle); 459 var containerNested = new NativeStream(100, CommonRwdAllocator.Handle); 460 461 var writer = containerNested.AsWriter(); 462 writer.BeginForEachIndex(0); 463 writer.Write(inner); 464 writer.EndForEachIndex(); 465 var writerStruct = containerNestedStruct.AsWriter(); 466 writerStruct.BeginForEachIndex(0); 467 writerStruct.Write(nestedStruct); 468 writerStruct.EndForEachIndex(); 469 470 containerNested.Dispose(); 471 containerNestedStruct.Dispose(); 472 inner.Dispose(); 473 } 474 475 [Test] 476 public unsafe void NativeStream_Continue_Append() 477 { 478 AllocatorManager.Initialize(); 479 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent); 480 ref var allocator = ref allocatorHelper.Allocator; 481 allocator.Initialize(); 482 483 for (var i = 0; i < 1024; i++) 484 { 485 var stream = new NativeStream(2, allocator.Handle); 486 487 var writer = stream.AsWriter(); 488 writer.BeginForEachIndex(0); 489 writer.Allocate(4000); 490 writer.EndForEachIndex(); 491 492 var writer2 = stream.AsWriter(); 493 writer2.BeginForEachIndex(1); 494 writer2.Allocate(4000); 495 writer2.Allocate(4000); 496 writer2.EndForEachIndex(); 497 498 stream.Dispose(); 499 500 Assert.AreEqual(0, allocatorHelper.Allocator.Used); 501 } 502 503 allocator.Dispose(); 504 allocatorHelper.Dispose(); 505 AllocatorManager.Shutdown(); 506 } 507 508 struct NestedContainerJob : IJob 509 { 510 public NativeStream nestedContainer; 511 512 public void Execute() 513 { 514 var writer = nestedContainer.AsWriter(); 515 writer.BeginForEachIndex(0); 516 writer.Write(1); 517 writer.EndForEachIndex(); 518 } 519 } 520 521 [Test] 522 [TestRequiresCollectionChecks] 523 public void NativeStream_NestedJob_Error() 524 { 525 var inner = new NativeList<int>(CommonRwdAllocator.Handle); 526 var container = new NativeStream(100, CommonRwdAllocator.Handle); 527 528 // This should mark the NativeStream as having nested containers and therefore should not be able to be scheduled 529 var writer = container.AsWriter(); 530 writer.BeginForEachIndex(0); 531 writer.Write(inner); 532 writer.EndForEachIndex(); 533 534 var nestedJob = new NestedContainerJob 535 { 536 nestedContainer = container 537 }; 538 539 JobHandle job = default; 540 Assert.Throws<System.InvalidOperationException>(() => { job = nestedJob.Schedule(); }); 541 job.Complete(); 542 543 container.Dispose(); 544 } 545}