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}