A game about forced loneliness, made by TACStudios
1using System; 2using NUnit.Framework; 3using Unity.Burst; 4using Unity.Collections; 5using Unity.Collections.LowLevel.Unsafe; 6using Unity.Collections.Tests; 7using Unity.Jobs; 8using Assert = FastAssert; 9 10[BurstCompile] 11internal class NativeQueueTests : CollectionsTestCommonBase 12{ 13 static void ExpectedCount<T>(ref NativeQueue<T> container, int expected) where T : unmanaged 14 { 15 Assert.AreEqual(expected == 0, container.IsEmpty()); 16 Assert.AreEqual(expected, container.Count); 17 } 18 19 [Test] 20 public void Enqueue_Dequeue() 21 { 22 var queue = new NativeQueue<int>(Allocator.Temp); 23 ExpectedCount(ref queue, 0); 24 25#if ENABLE_UNITY_COLLECTIONS_CHECKS 26 Assert.Throws<System.InvalidOperationException>(() => { queue.Dequeue(); }); 27#endif 28 29 for (int i = 0; i < 16; ++i) 30 queue.Enqueue(i); 31 ExpectedCount(ref queue, 16); 32 for (int i = 0; i < 16; ++i) 33 Assert.AreEqual(i, queue.Dequeue(), "Got the wrong value from the queue"); 34 ExpectedCount(ref queue, 0); 35 36#if ENABLE_UNITY_COLLECTIONS_CHECKS 37 Assert.Throws<System.InvalidOperationException>(() => { queue.Dequeue(); }); 38#endif 39 40 queue.Dispose(); 41 } 42 43 [Test] 44 public void ConcurrentEnqueue_Dequeue() 45 { 46 var queue = new NativeQueue<int>(Allocator.Temp); 47 var cQueue = queue.AsParallelWriter(); 48 ExpectedCount(ref queue, 0); 49 50#if ENABLE_UNITY_COLLECTIONS_CHECKS 51 Assert.Throws<System.InvalidOperationException>(() => { queue.Dequeue(); }); 52#endif 53 54 for (int i = 0; i < 16; ++i) 55 cQueue.Enqueue(i); 56 ExpectedCount(ref queue, 16); 57 for (int i = 0; i < 16; ++i) 58 Assert.AreEqual(i, queue.Dequeue(), "Got the wrong value from the queue"); 59 ExpectedCount(ref queue, 0); 60 61#if ENABLE_UNITY_COLLECTIONS_CHECKS 62 Assert.Throws<System.InvalidOperationException>(() => { queue.Dequeue(); }); 63#endif 64 65 queue.Dispose(); 66 } 67 68 [Test] 69 public void Enqueue_Dequeue_Peek() 70 { 71 var queue = new NativeQueue<int>(Allocator.Temp); 72 ExpectedCount(ref queue, 0); 73 74#if ENABLE_UNITY_COLLECTIONS_CHECKS 75 Assert.Throws<System.InvalidOperationException>(() => { queue.Dequeue(); }); 76#endif 77 78 for (int i = 0; i < 16; ++i) 79 queue.Enqueue(i); 80 ExpectedCount(ref queue, 16); 81 for (int i = 0; i < 16; ++i) 82 { 83 Assert.AreEqual(i, queue.Peek(), "Got the wrong value from the queue"); 84 queue.Dequeue(); 85 } 86 ExpectedCount(ref queue, 0); 87 88#if ENABLE_UNITY_COLLECTIONS_CHECKS 89 Assert.Throws<System.InvalidOperationException>(() => { queue.Dequeue(); }); 90#endif 91 92 queue.Dispose(); 93 } 94 95 [Test] 96 public void Enqueue_Dequeue_Clear() 97 { 98 var queue = new NativeQueue<int>(Allocator.Temp); 99 ExpectedCount(ref queue, 0); 100 101#if ENABLE_UNITY_COLLECTIONS_CHECKS 102 Assert.Throws<System.InvalidOperationException>(() => { queue.Dequeue(); }); 103#endif 104 105 for (int i = 0; i < 16; ++i) 106 queue.Enqueue(i); 107 ExpectedCount(ref queue, 16); 108 for (int i = 0; i < 8; ++i) 109 Assert.AreEqual(i, queue.Dequeue(), "Got the wrong value from the queue"); 110 ExpectedCount(ref queue, 8); 111 queue.Clear(); 112 ExpectedCount(ref queue, 0); 113 114#if ENABLE_UNITY_COLLECTIONS_CHECKS 115 Assert.Throws<System.InvalidOperationException>(() => { queue.Dequeue(); }); 116#endif 117 118 queue.Dispose(); 119 } 120 121 [Test] 122 [TestRequiresCollectionChecks] 123 public void Double_Deallocate_Throws() 124 { 125 var queue = new NativeQueue<int>(CommonRwdAllocator.Handle); 126 queue.Dispose(); 127 Assert.Throws<ObjectDisposedException>( 128 () => { queue.Dispose(); }); 129 } 130 131 [Test] 132 public void EnqueueScalability() 133 { 134 var queue = new NativeQueue<int>(Allocator.Persistent); 135 for (int i = 0; i != 1000 * 100; i++) 136 { 137 queue.Enqueue(i); 138 } 139 140 ExpectedCount(ref queue, 1000 * 100); 141 142 for (int i = 0; i != 1000 * 100; i++) 143 Assert.AreEqual(i, queue.Dequeue()); 144 ExpectedCount(ref queue, 0); 145 146 queue.Dispose(); 147 } 148 149 [Test] 150 public void Enqueue_Wrap() 151 { 152 var queue = new NativeQueue<int>(Allocator.Temp); 153 ExpectedCount(ref queue, 0); 154 155#if ENABLE_UNITY_COLLECTIONS_CHECKS 156 Assert.Throws<System.InvalidOperationException>(() => { queue.Dequeue(); }); 157#endif 158 159 for (int i = 0; i < 256; ++i) 160 queue.Enqueue(i); 161 ExpectedCount(ref queue, 256); 162 163 for (int i = 0; i < 128; ++i) 164 Assert.AreEqual(i, queue.Dequeue(), "Got the wrong value from the queue"); 165 ExpectedCount(ref queue, 128); 166 167 for (int i = 0; i < 128; ++i) 168 queue.Enqueue(i); 169 ExpectedCount(ref queue, 256); 170 171 for (int i = 128; i < 256; ++i) 172 Assert.AreEqual(i, queue.Dequeue(), "Got the wrong value from the queue"); 173 ExpectedCount(ref queue, 128); 174 175 for (int i = 0; i < 128; ++i) 176 Assert.AreEqual(i, queue.Dequeue(), "Got the wrong value from the queue"); 177 ExpectedCount(ref queue, 0); 178 179#if ENABLE_UNITY_COLLECTIONS_CHECKS 180 Assert.Throws<System.InvalidOperationException>(() => { queue.Dequeue(); }); 181#endif 182 183 queue.Dispose(); 184 } 185 186 [Test] 187 public void ConcurrentEnqueue_Wrap() 188 { 189 var queue = new NativeQueue<int>(Allocator.Temp); 190 var cQueue = queue.AsParallelWriter(); 191 ExpectedCount(ref queue, 0); 192 193#if ENABLE_UNITY_COLLECTIONS_CHECKS 194 Assert.Throws<System.InvalidOperationException>(() => { queue.Dequeue(); }); 195#endif 196 197 for (int i = 0; i < 256; ++i) 198 cQueue.Enqueue(i); 199 ExpectedCount(ref queue, 256); 200 201 for (int i = 0; i < 128; ++i) 202 Assert.AreEqual(i, queue.Dequeue(), "Got the wrong value from the queue"); 203 ExpectedCount(ref queue, 128); 204 205 for (int i = 0; i < 128; ++i) 206 cQueue.Enqueue(i); 207 ExpectedCount(ref queue, 256); 208 209 for (int i = 128; i < 256; ++i) 210 Assert.AreEqual(i, queue.Dequeue(), "Got the wrong value from the queue"); 211 ExpectedCount(ref queue, 128); 212 213 for (int i = 0; i < 128; ++i) 214 Assert.AreEqual(i, queue.Dequeue(), "Got the wrong value from the queue"); 215 ExpectedCount(ref queue, 0); 216 217#if ENABLE_UNITY_COLLECTIONS_CHECKS 218 Assert.Throws<System.InvalidOperationException>(() => { queue.Dequeue(); }); 219#endif 220 221 queue.Dispose(); 222 } 223 224 [Test] 225 public void NativeQueue_DisposeJob() 226 { 227 var container = new NativeQueue<int>(Allocator.Persistent); 228 Assert.True(container.IsCreated); 229 Assert.DoesNotThrow(() => { container.Enqueue(0); }); 230 231 var disposeJob = container.Dispose(default); 232 Assert.False(container.IsCreated); 233 234#if ENABLE_UNITY_COLLECTIONS_CHECKS 235 Assert.Throws<ObjectDisposedException>( 236 () => { container.Enqueue(0); }); 237#endif 238 239 disposeJob.Complete(); 240 } 241 242 [Test] 243 public void TryDequeue_OnEmptyQueueWhichHadElements_RetainsValidState() 244 { 245 using (var queue = new NativeQueue<int>(Allocator.Temp)) 246 { 247 for (int i = 0; i < 3; i++) 248 { 249 queue.Enqueue(i); 250 Assert.AreEqual(1, queue.Count); 251 252 int value; 253 while (queue.TryDequeue(out value)) 254 { 255 Assert.AreEqual(i, value); 256 } 257 258 Assert.AreEqual(0, queue.Count); 259 } 260 } 261 } 262 263 [Test] 264 public void TryDequeue_OnEmptyQueue_RetainsValidState() 265 { 266 using (var queue = new NativeQueue<int>(Allocator.Temp)) 267 { 268 Assert.IsFalse(queue.TryDequeue(out _)); 269 queue.Enqueue(1); 270 Assert.AreEqual(1, queue.Count); 271 } 272 } 273 274 [Test] 275 public void ToArray_ContainsCorrectElements() 276 { 277 using (var queue = new NativeQueue<int>(Allocator.Temp)) 278 { 279 for (int i = 0; i < 100; i++) 280 queue.Enqueue(i); 281 using (var array = queue.ToArray(Allocator.Temp)) 282 { 283 Assert.AreEqual(queue.Count, array.Length); 284 for (int i = 0; i < array.Length; i++) 285 Assert.AreEqual(i, array[i]); 286 } 287 } 288 } 289 290 [Test] 291 public void ToArray_RespectsDequeue() 292 { 293 using (var queue = new NativeQueue<int>(Allocator.Temp)) 294 { 295 for (int i = 0; i < 100; i++) 296 queue.Enqueue(i); 297 for (int i = 0; i < 50; i++) 298 queue.Dequeue(); 299 using (var array = queue.ToArray(Allocator.Temp)) 300 { 301 Assert.AreEqual(queue.Count, array.Length); 302 for (int i = 0; i < array.Length; i++) 303 Assert.AreEqual(50 + i, array[i]); 304 } 305 } 306 } 307 308 [Test] 309 [TestRequiresCollectionChecks] 310 public void NativeQueue_UseAfterFree_UsesCustomOwnerTypeName() 311 { 312 var container = new NativeQueue<int>(CommonRwdAllocator.Handle); 313 container.Enqueue(123); 314 container.Dispose(); 315 NUnit.Framework.Assert.That(() => container.Dequeue(), 316 Throws.Exception.TypeOf<ObjectDisposedException>() 317 .With.Message.Contains($"The {container.GetType()} has been deallocated")); 318 } 319 320 [Test] 321 public void NativeQueue_CustomAllocatorTest() 322 { 323 AllocatorManager.Initialize(); 324 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent); 325 ref var allocator = ref allocatorHelper.Allocator; 326 allocator.Initialize(); 327 328 using (var container = new NativeQueue<int>(allocator.Handle)) 329 { 330 } 331 332 Assert.IsTrue(allocator.WasUsed); 333 allocator.Dispose(); 334 allocatorHelper.Dispose(); 335 AllocatorManager.Shutdown(); 336 } 337 338 [BurstCompile(CompileSynchronously = true)] 339 struct BurstedCustomAllocatorJob : IJob 340 { 341 [NativeDisableUnsafePtrRestriction] 342 public unsafe CustomAllocatorTests.CountingAllocator* Allocator; 343 344 public void Execute() 345 { 346 unsafe 347 { 348 using (var container = new NativeQueue<int>(Allocator->Handle)) 349 { 350 } 351 } 352 } 353 } 354 355 [Test] 356 public unsafe void NativeQueue_BurstedCustomAllocatorTest() 357 { 358 AllocatorManager.Initialize(); 359 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent); 360 ref var allocator = ref allocatorHelper.Allocator; 361 362 allocator.Initialize(); 363 364 var allocatorPtr = (CustomAllocatorTests.CountingAllocator*)UnsafeUtility.AddressOf<CustomAllocatorTests.CountingAllocator>(ref allocator); 365 unsafe 366 { 367 var handle = new BurstedCustomAllocatorJob { Allocator = allocatorPtr }.Schedule(); 368 handle.Complete(); 369 } 370 371 Assert.IsTrue(allocator.WasUsed); 372 allocator.Dispose(); 373 allocatorHelper.Dispose(); 374 AllocatorManager.Shutdown(); 375 } 376 377 public struct NestedContainer 378 { 379 public NativeQueue<int> data; 380 } 381 382 [Test] 383 public void NativeQueue_Nested() 384 { 385 var inner = new NativeQueue<int>(CommonRwdAllocator.Handle); 386 NestedContainer nestedStruct = new NestedContainer { data = inner }; 387 388 var containerNestedStruct = new NativeQueue<NestedContainer>(CommonRwdAllocator.Handle); 389 var containerNested = new NativeQueue<NativeQueue<int>>(CommonRwdAllocator.Handle); 390 391 containerNested.Enqueue(inner); 392 containerNestedStruct.Enqueue(nestedStruct); 393 394 containerNested.Dispose(); 395 containerNestedStruct.Dispose(); 396 inner.Dispose(); 397 } 398 399 400 [Test] 401 public void NativeQueue_ReadOnly() 402 { 403 var container = new NativeQueue<int>(CommonRwdAllocator.Handle); 404 container.Enqueue(123); 405 container.Enqueue(456); 406 container.Enqueue(789); 407 408 var ro = container.AsReadOnly(); 409 Assert.AreEqual(3, ro.Count); 410 Assert.AreEqual(123, ro[0]); 411 Assert.AreEqual(456, ro[1]); 412 Assert.AreEqual(789, ro[2]); 413#if ENABLE_UNITY_COLLECTIONS_CHECKS 414 Assert.Throws<IndexOutOfRangeException>(() => { _ = ro[3]; }); 415 Assert.Throws<IndexOutOfRangeException>(() => { _ = ro[-1]; }); 416 Assert.Throws<IndexOutOfRangeException>(() => { _ = ro[int.MaxValue]; }); 417 Assert.Throws<IndexOutOfRangeException>(() => { _ = ro[int.MinValue]; }); 418#endif 419 420 container.Dispose(); 421 } 422 423 // Burst error BC1071: Unsupported assert type 424 // [BurstCompile(CompileSynchronously = true)] 425 struct NativeQueueTestAsReadOnly : IJob 426 { 427 [ReadOnly] 428 NativeQueue<int>.ReadOnly container; 429 430 public NativeQueueTestAsReadOnly(NativeQueue<int>.ReadOnly container) { this.container = container; } 431 432 public void Execute() 433 { 434 var ro = container; 435 Assert.AreEqual(3, ro.Count); 436 Assert.AreEqual(123, ro[0]); 437 Assert.AreEqual(456, ro[1]); 438 Assert.AreEqual(789, ro[2]); 439#if ENABLE_UNITY_COLLECTIONS_CHECKS 440 Assert.Throws<IndexOutOfRangeException>(() => { _ = ro[3]; }); 441 Assert.Throws<IndexOutOfRangeException>(() => { _ = ro[-1]; }); 442 Assert.Throws<IndexOutOfRangeException>(() => { _ = ro[int.MaxValue]; }); 443 Assert.Throws<IndexOutOfRangeException>(() => { _ = ro[int.MinValue]; }); 444#endif 445 } 446 } 447 448 [Test] 449 [TestRequiresCollectionChecks] 450 public void NativeQueue_ReadOnlyJob() 451 { 452 var container = new NativeQueue<int>(CommonRwdAllocator.Handle); 453 container.Enqueue(123); 454 container.Enqueue(456); 455 container.Enqueue(789); 456 457 var job = new NativeQueueTestAsReadOnly(container.AsReadOnly()).Schedule(); 458 459 Assert.Throws<InvalidOperationException>(() => { container.Enqueue(987); }); 460 Assert.Throws<InvalidOperationException>(() => { container.Dequeue(); }); 461 Assert.Throws<InvalidOperationException>(() => { container.Dispose(); }); 462 463 job.Complete(); 464 465 Assert.DoesNotThrow(() => { container.Enqueue(987); }); 466 Assert.DoesNotThrow(() => { container.Dequeue(); }); 467 Assert.DoesNotThrow(() => { container.Dispose(); }); 468 } 469 470 struct NativeQueueTestWriteMappedToReadOnly : IJob 471 { 472 [WriteOnly] 473 public NativeQueue<int>.ParallelWriter Container; 474 public void Execute() { } 475 } 476 477 [Test] 478 [TestRequiresCollectionChecks] 479 public void NativeQueue_ReadOnlyCannotScheduledForWrite() 480 { 481 var container = new NativeQueue<int>(CommonRwdAllocator.Handle); 482 container.Enqueue(123); 483 container.Enqueue(456); 484 container.Enqueue(789); 485 486 var ro = container.AsReadOnly(); 487 var job = new NativeQueueTestWriteMappedToReadOnly { Container = container.AsParallelWriter() }.Schedule(); 488 489 Assert.Throws<InvalidOperationException>(() => { _ = ro.Count; }); 490 Assert.Throws<InvalidOperationException>(() => { _ = ro[0]; }); 491 Assert.Throws<InvalidOperationException>(() => { _ = ro[1]; }); 492 Assert.Throws<InvalidOperationException>(() => { _ = ro[2]; }); 493 Assert.Throws<InvalidOperationException>(() => { foreach (var item in ro) { } }); 494 495 job.Complete(); 496 497 Assert.AreEqual(3, ro.Count); 498 Assert.AreEqual(123, ro[0]); 499 Assert.AreEqual(456, ro[1]); 500 Assert.AreEqual(789, ro[2]); 501 Assert.DoesNotThrow(() => { foreach (var item in ro) { } }); 502 503 container.Dispose(); 504 } 505 506 [Test] 507 public void NativeQueue_ReadOnlyForEach() 508 { 509 var container = new NativeQueue<int>(CommonRwdAllocator.Handle); 510 container.Enqueue(123); 511 container.Enqueue(456); 512 container.Enqueue(789); 513 514 var ro = container.AsReadOnly(); 515 516 var idx = 0; 517 foreach (var item in ro) 518 { 519 Assert.AreEqual(item, ro[idx++]); 520 } 521 522 container.Dispose(); 523 } 524 525 struct NativeQueue_ForEachIterator : IJob 526 { 527 [ReadOnly] 528 public NativeQueue<int>.Enumerator Iter; 529 530 public void Execute() 531 { 532 while (Iter.MoveNext()) 533 { 534 } 535 } 536 } 537 538 [Test] 539 [TestRequiresCollectionChecks] 540 public void NativeQueue_ForEach_Throws_Job_Iterator() 541 { 542 using (var container = new NativeQueue<int>(CommonRwdAllocator.Handle)) 543 { 544 var jobHandle = new NativeQueue_ForEachIterator 545 { 546 Iter = container.AsReadOnly().GetEnumerator() 547 548 }.Schedule(); 549 550 Assert.Throws<InvalidOperationException>(() => { container.Enqueue(987); }); 551 552 jobHandle.Complete(); 553 } 554 } 555 556 struct NativeQueueParallelWriteJob : IJobParallelFor 557 { 558 [WriteOnly] 559 public NativeQueue<int>.ParallelWriter Writer; 560 561 public void Execute(int index) 562 { 563 Writer.Enqueue(index); 564 } 565 } 566 567 [Test] 568 [TestRequiresCollectionChecks] 569 public void NativeQueue_ForEach_Throws() 570 { 571 using (var container = new NativeQueue<int>(CommonRwdAllocator.Handle)) 572 { 573 var iter = container.AsReadOnly().GetEnumerator(); 574 575 var jobHandle = new NativeQueueParallelWriteJob 576 { 577 Writer = container.AsParallelWriter() 578 579 }.Schedule(1, 2); 580 581 Assert.Throws<InvalidOperationException>(() => 582 { 583 while (iter.MoveNext()) 584 { 585 } 586 }); 587 588 jobHandle.Complete(); 589 } 590 } 591 592 struct NativeQueue_ForEach_Job : IJob 593 { 594 public NativeQueue<int>.ReadOnly Input; 595 596 public void Execute() 597 { 598 var index = 0; 599 foreach (var value in Input) 600 { 601 Assert.AreEqual(value, Input[index++]); 602 } 603 } 604 } 605 606 [Test] 607 public void NativeQueue_ForEach_From_Job([Values(10, 1000)] int n) 608 { 609 var seen = new NativeArray<int>(n, Allocator.Temp); 610 using (var container = new NativeQueue<int>(CommonRwdAllocator.Handle)) 611 { 612 for (int i = 0; i < n; i++) 613 { 614 container.Enqueue(i * 37); 615 } 616 617 new NativeQueue_ForEach_Job 618 { 619 Input = container.AsReadOnly(), 620 621 }.Run(); 622 } 623 } 624}