A game about forced loneliness, made by TACStudios
1#if ENABLE_UNITY_COLLECTIONS_CHECKS
2using UnityEngine;
3using NUnit.Framework;
4using System;
5using Unity.Jobs;
6using Unity.Burst;
7using Unity.Collections;
8using Unity.Collections.NotBurstCompatible;
9using Unity.Collections.Tests;
10
11#pragma warning disable 0219
12#pragma warning disable 0414
13
14internal class NativeListJobDebuggerTests : CollectionsTestFixture
15{
16 [BurstCompile(CompileSynchronously = true)]
17 struct NativeListAddJob : IJob
18 {
19 NativeList<int> list;
20
21 public NativeListAddJob(NativeList<int> list) { this.list = list; }
22
23 public void Execute()
24 {
25 list.Add(1);
26 }
27 }
28
29 [BurstCompile(CompileSynchronously = true)]
30 struct NativeArrayTest : IJob
31 {
32 NativeArray<int> array;
33
34 public NativeArrayTest(NativeArray<int> array) { this.array = array; }
35
36 public void Execute()
37 {
38 }
39 }
40
41 struct NestedContainerJob : IJob
42 {
43 public NativeList<NativeList<int>> nestedContainer;
44
45 public void Execute()
46 {
47 nestedContainer.Clear();
48 }
49 }
50
51 [Test]
52 [TestRequiresCollectionChecks]
53 public void NativeList_NestedJob_Error()
54 {
55 var container = new NativeList<NativeList<int>>(CommonRwdAllocator.Handle);
56
57 var nestedJob = new NestedContainerJob
58 {
59 nestedContainer = container
60 };
61
62 JobHandle job = default;
63 Assert.Throws<InvalidOperationException>(() => { job = nestedJob.Schedule(); });
64 job.Complete();
65
66 container.Dispose();
67 }
68
69 [Test]
70 [TestRequiresCollectionChecks]
71 public void AddElementToListFromJobInvalidatesArray()
72 {
73 var list = new NativeList<int>(CommonRwdAllocator.Handle);
74 list.Add(0);
75
76 NativeArray<int> arrayBeforeSchedule = list.AsArray();
77 Assert.AreEqual(list.Length, 1);
78
79 var jobData = new NativeListAddJob(list);
80 var job = jobData.Schedule();
81
82 Assert.Throws<ObjectDisposedException>(
83 () => {
84 int readVal = arrayBeforeSchedule[0];
85 });
86 Assert.Throws<InvalidOperationException>(() => { NativeArray<int> array = list.AsArray(); Debug.Log(array.Length); });
87 Assert.Throws<InvalidOperationException>(() => { int readVal = list.Capacity; });
88 Assert.Throws<InvalidOperationException>(() => { list.Dispose(); });
89 Assert.Throws<InvalidOperationException>(() => { int readVal = list[0]; });
90
91 job.Complete();
92
93 // Assert.AreEqual(1, arrayBeforeSchedule.Length); - temporarily commenting out updated assert checks to ensure editor version promotion succeeds
94 Assert.Throws<ObjectDisposedException>(
95 () => {
96 int readVal = arrayBeforeSchedule[0];
97 });
98
99 Assert.AreEqual(2, list.Length);
100 Assert.AreEqual(0, list[0]);
101 Assert.AreEqual(1, list[1]);
102
103 NativeArray<int> arrayAfter = list.AsArray();
104 Assert.AreEqual(2, arrayAfter.Length);
105 Assert.AreEqual(0, arrayAfter[0]);
106 Assert.AreEqual(1, arrayAfter[1]);
107
108 list.Dispose();
109 }
110
111 [Test]
112 [TestRequiresCollectionChecks]
113 public void AccessBefore()
114 {
115 var list = new NativeList<int>(CommonRwdAllocator.Handle);
116
117 var jobHandle = new NativeListAddJob(list).Schedule();
118 Assert.Throws<InvalidOperationException>(() =>
119 {
120 list.AsArray();
121 });
122
123 jobHandle.Complete();
124 list.Dispose();
125 }
126
127 [Test]
128 [TestRequiresCollectionChecks]
129 public void AccessAfter()
130 {
131 var list = new NativeList<int>(CommonRwdAllocator.Handle);
132 var array = list.AsArray();
133 var jobHandle = new NativeListAddJob(list).Schedule();
134 Assert.Throws<InvalidOperationException>(() =>
135 {
136 new NativeArrayTest(array).Schedule(jobHandle);
137 });
138 jobHandle.Complete();
139
140 list.Dispose();
141 }
142
143 [Test]
144 public void ScheduleDerivedArrayAllowDerivingArrayAgain()
145 {
146 var list = new NativeList<int>(1, Allocator.Persistent);
147
148 // The scheduled job only receives a NativeArray thus it can't be resized
149 var writeJobHandle = new NativeArrayTest(list.AsArray()).Schedule();
150
151 // For that reason casting here is legal, as opposed to AddElementToListFromJobInvalidatesArray case where it is not legal
152 // Since we NativeList is passed to the job
153#pragma warning disable 0219 // assigned but its value is never used
154 NativeArray<int> array = list.AsArray();
155#pragma warning restore 0219
156
157 list.Dispose(writeJobHandle);
158 }
159
160 [Test]
161 [TestRequiresCollectionChecks]
162 public void ScheduleDerivedArrayExceptions()
163 {
164 var list = new NativeList<int>(1, Allocator.Persistent);
165
166 var addListJobHandle = new NativeListAddJob(list).Schedule();
167#pragma warning disable 0219 // assigned but its value is never used
168 Assert.Throws<InvalidOperationException>(() => { NativeArray<int> array = list.AsArray(); });
169#pragma warning restore 0219
170
171 addListJobHandle.Complete();
172 list.Dispose();
173 }
174
175 [Test]
176 [TestRequiresCollectionChecks]
177 public void ScheduleDerivedArrayExceptions2()
178 {
179 var list = new NativeList<int>(1, Allocator.Persistent);
180 NativeArray<int> array = list.AsArray();
181
182 var addListJobHandle = new NativeListAddJob(list).Schedule();
183 // The array previously cast should become invalid
184 // as soon as the job is scheduled, since we can't predict if an element will be added or not
185 Assert.Throws<InvalidOperationException>(() => { new NativeArrayTest(array).Schedule(); });
186
187 addListJobHandle.Complete();
188 list.Dispose();
189 }
190
191 [BurstCompile(CompileSynchronously = true)]
192 struct ReadOnlyListAccess : IJob
193 {
194 [ReadOnly]
195 NativeList<int> list;
196
197 public ReadOnlyListAccess(NativeList<int> list) { this.list = list; }
198
199 public void Execute()
200 {
201 }
202 }
203
204 [Test]
205 public void ReadOnlyListInJobKeepsAsArrayValid()
206 {
207 var list = new NativeList<int>(CommonRwdAllocator.Handle);
208 list.Add(0);
209 var arrayBeforeSchedule = list.AsArray();
210
211 var jobData = new ReadOnlyListAccess(list);
212 var job = jobData.Schedule();
213 job.Complete();
214
215 Assert.AreEqual(0, arrayBeforeSchedule[0]);
216
217 list.Dispose();
218 }
219
220 [Test]
221 public void AsArrayJobKeepsAsArrayValid()
222 {
223 var list = new NativeList<int>(CommonRwdAllocator.Handle);
224 list.Add(0);
225 var arrayBeforeSchedule = list.AsArray();
226
227 var jobData = new NativeArrayTest(list.AsArray());
228 var job = jobData.Schedule();
229 job.Complete();
230
231 Assert.AreEqual(0, arrayBeforeSchedule[0]);
232
233 list.Dispose();
234 }
235
236 [BurstCompile(CompileSynchronously = true)]
237 struct NativeListToArrayConversionFromJob : IJob
238 {
239 public NativeList<int> list;
240
241 public void Execute()
242 {
243 list.Add(0);
244 list.Add(0);
245
246 NativeArray<int> arr = list.AsArray();
247 arr[0] = 1;
248 arr[1] = 2;
249 }
250 }
251
252 [Test]
253 public void CastListToArrayInsideJob()
254 {
255 var jobData = new NativeListToArrayConversionFromJob();
256 jobData.list = new NativeList<int>(1, Allocator.Persistent);
257 jobData.Schedule().Complete();
258
259 Assert.AreEqual(new int[] { 1, 2 }, jobData.list.ToArrayNBC());
260 jobData.list.Dispose();
261 }
262
263 [BurstCompile(CompileSynchronously = true)]
264 struct WriteJob : IJobParallelFor
265 {
266 public NativeArray<float> output;
267
268 public void Execute(int i)
269 {
270 output[i] = i;
271 }
272 }
273
274 [Test]
275 [TestRequiresCollectionChecks]
276 public void WriteToArrayFromJobThenReadListFromMainThread()
277 {
278 var list = new NativeList<float>(1, Allocator.Persistent);
279 list.Add(0);
280 list.Add(1);
281
282 for (int i = 0; i < 2; i++)
283 {
284 var writeJob = new WriteJob();
285 writeJob.output = list.AsArray();
286 var writeJobHandle = writeJob.Schedule(list.Length, 1);
287
288 Assert.Throws<InvalidOperationException>(() => { float val = writeJob.output[0]; });
289
290 writeJobHandle.Complete();
291 }
292 list.Dispose();
293 }
294
295 [Test]
296 public void NativeList_DisposeJob()
297 {
298 var list = new NativeList<int>(Allocator.Persistent);
299 var deps = new NativeListAddJob(list).Schedule();
300 deps = list.Dispose(deps);
301 Assert.IsFalse(list.IsCreated);
302 deps.Complete();
303 }
304
305 struct InvalidArrayAccessFromListJob : IJob
306 {
307 public NativeList<int> list;
308
309 public void Execute()
310 {
311 list.Add(1);
312 NativeArray<int> array = list.AsArray();
313 list.Add(2);
314
315 // Assert.Throws<InvalidOperationException>(() => { array[0] = 5; }); - temporarily commenting out updated assert checks to ensure editor version promotion succeeds
316 }
317 }
318
319 [Test]
320 [TestRequiresCollectionChecks]
321 public void InvalidatedArrayAccessFromListThrowsInsideJob()
322 {
323 var job = new InvalidArrayAccessFromListJob { list = new NativeList<int>(CommonRwdAllocator.Handle) };
324 job.Schedule().Complete();
325 job.list.Dispose();
326 }
327
328 [Test]
329 public void DisposeAliasedArrayDoesNotThrow()
330 {
331 var list = new NativeList<int>(Allocator.Persistent);
332 var array = list.AsArray();
333 Assert.DoesNotThrow(() => { array.Dispose(); });
334
335 list.Dispose();
336 }
337
338 // Burst error BC1071: Unsupported assert type
339 // [BurstCompile(CompileSynchronously = true)]
340 struct NativeArrayTestReadOnly : IJob
341 {
342 [ReadOnly]
343 NativeArray<int> array;
344
345 public NativeArrayTestReadOnly(NativeArray<int> array) { this.array = array; }
346
347 public void Execute()
348 {
349 var arr = array;
350 Assert.Throws<InvalidOperationException>(() => { arr[0] = 5; });
351 Assert.AreEqual(7, array[0]);
352 }
353 }
354
355 // Burst error BC1071: Unsupported assert type
356 // [BurstCompile(CompileSynchronously = true)]
357 struct NativeArrayTestAsReadOnly : IJob
358 {
359 [ReadOnly]
360 NativeArray<int>.ReadOnly array;
361
362 public NativeArrayTestAsReadOnly(NativeArray<int>.ReadOnly array) { this.array = array; }
363
364 public void Execute()
365 {
366 var arr = array;
367 Assert.AreEqual(7, array[0]);
368 }
369 }
370
371 [Test]
372 [TestRequiresCollectionChecks]
373 public void ReadOnlyAliasedArrayThrows()
374 {
375 var list = new NativeList<int>(Allocator.Persistent);
376 list.Add(7);
377 new NativeArrayTestReadOnly(list.AsArray()).Schedule().Complete();
378
379 list.Dispose();
380 }
381
382 // Burst error BC1071: Unsupported assert type
383 // [BurstCompile(CompileSynchronously = true)]
384 struct NativeArrayTestWriteOnly : IJob
385 {
386 [WriteOnly]
387 NativeArray<int> array;
388
389 public NativeArrayTestWriteOnly(NativeArray<int> array) { this.array = array; }
390
391 public void Execute()
392 {
393 var arr = array;
394 Assert.Throws<InvalidOperationException>(() => { int read = arr[0]; });
395 arr[0] = 7;
396 }
397 }
398
399 [Test]
400 [TestRequiresCollectionChecks]
401 public void NativeList_AsArray_Jobs()
402 {
403 var list = new NativeList<int>(Allocator.Persistent);
404 list.Add(0);
405
406 var writer = list.AsArray();
407 var writerJob = new NativeArrayTestWriteOnly(writer).Schedule();
408
409 var reader = list.AsArray();
410 var readerJob = new NativeArrayTestReadOnly(reader).Schedule(writerJob);
411
412 // Tests that read only container safety check trows...
413 var writerJob2 = new NativeArrayTestWriteOnly(reader).Schedule(readerJob);
414
415 // Tests that write only container safety check trows...
416 var readerJob2 = new NativeArrayTestReadOnly(writer).Schedule(writerJob2);
417
418 readerJob2.Complete();
419
420 list.Dispose();
421 }
422
423 [Test]
424 [TestRequiresCollectionChecks]
425 public void NativeList_AsReadOnly_Jobs()
426 {
427 var list = new NativeList<int>(Allocator.Persistent);
428 list.Add(0);
429
430 var writer = list.AsArray();
431 var writerJob = new NativeArrayTestWriteOnly(writer).Schedule();
432
433 var reader = list.AsReadOnly();
434 var readerJob = new NativeArrayTestAsReadOnly(reader).Schedule(writerJob);
435
436 readerJob.Complete();
437
438 list.Dispose();
439 }
440
441 // Burst error BC1071: Unsupported assert type
442 // [BurstCompile(CompileSynchronously = true)]
443 struct NativeListTestReadOnly : IJob
444 {
445 [ReadOnly]
446 public NativeArray<int>.ReadOnly reader;
447
448 public void Execute()
449 {
450 Assert.True(reader.Contains(7));
451 Assert.AreEqual(7, reader[0]);
452 }
453 }
454
455 [Test]
456 public void NativeList_AsReadOnly()
457 {
458 NativeList<int> list;
459 JobHandle readerJob;
460
461 {
462 list = new NativeList<int>(Allocator.Persistent);
463 list.Add(7);
464
465 var reader = list.AsReadOnly();
466 list.Dispose(); // <- cause invalid use
467 Assert.Throws<InvalidOperationException>(() => { readerJob = new NativeListTestReadOnly { reader = reader }.Schedule(); });
468 }
469
470 {
471 list = new NativeList<int>(Allocator.Persistent);
472 list.Add(7);
473
474 var reader = list.AsReadOnly();
475 readerJob = new NativeListTestReadOnly { reader = reader }.Schedule();
476 }
477
478 list.Dispose(readerJob);
479 readerJob.Complete();
480 }
481
482 [BurstCompile(CompileSynchronously = true)]
483 struct NativeListTestParallelWriter : IJob
484 {
485 [WriteOnly]
486 public NativeList<int>.ParallelWriter writer;
487
488 public unsafe void Execute()
489 {
490 var range = stackalloc int[2] { 7, 3 };
491
492 writer.AddNoResize(range[0]);
493 writer.AddRangeNoResize(range, 1);
494 }
495 }
496
497 [Test]
498 public void NativeList_ParallelWriter()
499 {
500 NativeList<int> list;
501
502 {
503 list = new NativeList<int>(2, Allocator.Persistent);
504 var writer = list.AsParallelWriter();
505 list.Dispose(); // <- cause invalid use
506 Assert.Throws<InvalidOperationException>(() =>
507 {
508 var writerJob = new NativeListTestParallelWriter { writer = writer }.Schedule();
509 writerJob.Complete();
510 });
511 }
512
513 {
514 list = new NativeList<int>(2, Allocator.Persistent);
515 var writer = list.AsParallelWriter();
516 var writerJob = new NativeListTestParallelWriter { writer = writer }.Schedule();
517 writerJob.Complete();
518 }
519
520 Assert.AreEqual(2, list.Length);
521 Assert.AreEqual(7, list[0]);
522 Assert.AreEqual(7, list[1]);
523
524 list.Dispose();
525 }
526
527 [Test]
528 public void NativeList_ParallelWriter_NoPtrCaching()
529 {
530 NativeList<int> list;
531
532 {
533 list = new NativeList<int>(2, Allocator.Persistent);
534 var writer = list.AsParallelWriter();
535 list.Capacity = 100;
536 var writerJob = new NativeListTestParallelWriter { writer = writer }.Schedule();
537 writerJob.Complete();
538 }
539
540 Assert.AreEqual(2, list.Length);
541 Assert.AreEqual(7, list[0]);
542 Assert.AreEqual(7, list[1]);
543
544 list.Dispose();
545 }
546
547 [Test]
548 [TestRequiresCollectionChecks]
549 public void NativeList_AsReadOnlyAndParallelWriter()
550 {
551 NativeList<int> list;
552 JobHandle jobHandle;
553
554 list = new NativeList<int>(Allocator.Persistent);
555 list.Add(7);
556
557 jobHandle = new NativeListTestReadOnly { reader = list.AsReadOnly() }.Schedule();
558 jobHandle = new NativeListTestParallelWriter { writer = list.AsParallelWriter() }.Schedule(jobHandle);
559 jobHandle = new NativeListTestReadOnly { reader = list.AsReadOnly() }.Schedule(jobHandle);
560 jobHandle = new NativeListTestParallelWriter { writer = list.AsParallelWriter() }.Schedule(jobHandle);
561
562 list.Dispose(jobHandle);
563 jobHandle.Complete();
564 }
565
566 unsafe void Expected(ref NativeList<int> list, int expectedLength, int[] expected)
567 {
568 Assert.AreEqual(0 == expectedLength, list.IsEmpty);
569 Assert.AreEqual(list.Length, expectedLength);
570 for (var i = 0; i < list.Length; ++i)
571 {
572 var value = list[i];
573 Assert.AreEqual(expected[i], value);
574 }
575 }
576
577 [Test]
578 public unsafe void NativeList_RemoveRange()
579 {
580 var list = new NativeList<int>(10, Allocator.Persistent);
581
582 int[] range = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
583
584 // test removing from the end
585 fixed (int* r = range) list.AddRange(r, 10);
586 list.RemoveRange(6, 3);
587 Expected(ref list, 7, new int[] { 0, 1, 2, 3, 4, 5, 9 });
588 list.Clear();
589
590 // test removing all but one
591 fixed (int* r = range) list.AddRange(r, 10);
592 list.RemoveRange(0, 9);
593 Expected(ref list, 1, new int[] { 9 });
594 list.Clear();
595
596 // test removing from the front
597 fixed (int* r = range) list.AddRange(r, 10);
598 list.RemoveRange(0, 3);
599 Expected(ref list, 7, new int[] { 3, 4, 5, 6, 7, 8, 9 });
600 list.Clear();
601
602 // test removing from the middle
603 fixed (int* r = range) list.AddRange(r, 10);
604 list.RemoveRange(0, 3);
605 Expected(ref list, 7, new int[] { 3, 4, 5, 6, 7, 8, 9 });
606 list.Clear();
607
608 // test removing whole range
609 fixed (int* r = range) list.AddRange(r, 10);
610 list.RemoveRange(0, 10);
611 Expected(ref list, 0, new int[] { 0 });
612 list.Clear();
613
614 list.Dispose();
615 }
616}
617#endif