Keine Beschreibung
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

NativeListTests.cs 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. using NUnit.Framework;
  2. using System;
  3. using Unity.Burst;
  4. using Unity.Collections;
  5. using Unity.Collections.NotBurstCompatible;
  6. using Unity.Collections.LowLevel.Unsafe;
  7. using Unity.Collections.Tests;
  8. using Unity.Jobs;
  9. using UnityEngine;
  10. using UnityEngine.TestTools;
  11. #if !UNITY_PORTABLE_TEST_RUNNER
  12. using System.Text.RegularExpressions;
  13. #endif
  14. internal class NativeListTests : CollectionsTestFixture
  15. {
  16. static void ExpectedLength<T>(ref NativeList<T> container, int expected)
  17. where T : unmanaged
  18. {
  19. Assert.AreEqual(expected == 0, container.IsEmpty);
  20. Assert.AreEqual(expected, container.Length);
  21. }
  22. [Test]
  23. public void NullListThrow()
  24. {
  25. var list = new NativeList<int>();
  26. Assert.Throws<NullReferenceException>(() => list[0] = 5);
  27. Assert.Throws<ObjectDisposedException>(
  28. () => list.Add(1));
  29. }
  30. [Test]
  31. public void NativeList_Allocate_Deallocate_Read_Write()
  32. {
  33. var list = new NativeList<int>(Allocator.Persistent);
  34. list.Add(1);
  35. list.Add(2);
  36. ExpectedLength(ref list, 2);
  37. Assert.AreEqual(1, list[0]);
  38. Assert.AreEqual(2, list[1]);
  39. list.Dispose();
  40. }
  41. [Test]
  42. public void NativeArrayFromNativeList()
  43. {
  44. var list = new NativeList<int>(Allocator.Persistent);
  45. list.Add(42);
  46. list.Add(2);
  47. NativeArray<int> array = list;
  48. Assert.AreEqual(2, array.Length);
  49. Assert.AreEqual(42, array[0]);
  50. Assert.AreEqual(2, array[1]);
  51. list.Dispose();
  52. }
  53. [Test]
  54. public void NativeArrayFromNativeListInvalidatesOnAdd()
  55. {
  56. var list = new NativeList<int>(Allocator.Persistent);
  57. // This test checks that adding an element without reallocation invalidates the native array
  58. // (length changes)
  59. list.Capacity = 2;
  60. list.Add(42);
  61. NativeArray<int> array = list;
  62. list.Add(1000);
  63. ExpectedLength(ref list, 2);
  64. Assert.Throws<ObjectDisposedException>(
  65. () => { array[0] = 1; });
  66. list.Dispose();
  67. }
  68. [Test]
  69. public void NativeArrayFromNativeListInvalidatesOnCapacityChange()
  70. {
  71. var list = new NativeList<int>(Allocator.Persistent);
  72. list.Add(42);
  73. NativeArray<int> array = list;
  74. ExpectedLength(ref list, 1);
  75. list.Capacity = 10;
  76. //Assert.AreEqual(1, array.Length); - temporarily commenting out updated assert checks to ensure editor version promotion succeeds
  77. Assert.Throws<ObjectDisposedException>(
  78. () => { array[0] = 1; });
  79. list.Dispose();
  80. }
  81. [Test]
  82. public void NativeArrayFromNativeListInvalidatesOnDispose()
  83. {
  84. var list = new NativeList<int>(Allocator.Persistent);
  85. list.Add(42);
  86. NativeArray<int> array = list;
  87. list.Dispose();
  88. Assert.Throws<ObjectDisposedException>(
  89. () => { array[0] = 1; });
  90. Assert.Throws<ObjectDisposedException>(
  91. () => { list[0] = 1; });
  92. }
  93. [Test]
  94. public void NativeArrayFromNativeListMayDeallocate()
  95. {
  96. var list = new NativeList<int>(Allocator.Persistent);
  97. list.Add(42);
  98. NativeArray<int> array = list;
  99. Assert.DoesNotThrow(() => { array.Dispose(); });
  100. list.Dispose();
  101. }
  102. [Test]
  103. public void CopiedNativeListIsKeptInSync()
  104. {
  105. var list = new NativeList<int>(Allocator.Persistent);
  106. var listCpy = list;
  107. list.Add(42);
  108. Assert.AreEqual(42, listCpy[0]);
  109. Assert.AreEqual(42, list[0]);
  110. Assert.AreEqual(1, listCpy.Length);
  111. ExpectedLength(ref list, 1);
  112. list.Dispose();
  113. }
  114. [Test]
  115. public void NativeList_CopyFrom_Managed()
  116. {
  117. var list = new NativeList<float>(4, Allocator.Persistent);
  118. var ar = new float[] { 0, 1, 2, 3, 4, 5, 6, 7 };
  119. list.CopyFromNBC(ar);
  120. ExpectedLength(ref list, 8);
  121. for (int i = 0; i < list.Length; ++i)
  122. {
  123. Assert.AreEqual(i, list[i]);
  124. }
  125. list.Dispose();
  126. }
  127. [Test]
  128. public void NativeList_CopyFrom_Unmanaged()
  129. {
  130. var list = new NativeList<float>(4, Allocator.Persistent);
  131. var ar = new NativeArray<float>(new float[] { 0, 1, 2, 3, 4, 5, 6, 7 }, Allocator.Persistent);
  132. list.CopyFrom(ar);
  133. ExpectedLength(ref list, 8);
  134. for (int i = 0; i < list.Length; ++i)
  135. {
  136. Assert.AreEqual(i, list[i]);
  137. }
  138. ar.Dispose();
  139. list.Dispose();
  140. }
  141. [BurstCompile(CompileSynchronously = true)]
  142. struct TempListInJob : IJob
  143. {
  144. public NativeArray<int> Output;
  145. public void Execute()
  146. {
  147. var list = new NativeList<int>(Allocator.Temp);
  148. list.Add(17);
  149. Output[0] = list[0];
  150. list.Dispose();
  151. }
  152. }
  153. [Test]
  154. [Ignore("Unstable on CI, DOTS-1965")]
  155. public void TempListInBurstJob()
  156. {
  157. var job = new TempListInJob() { Output = CollectionHelper.CreateNativeArray<int>(1, CommonRwdAllocator.Handle) };
  158. job.Schedule().Complete();
  159. Assert.AreEqual(17, job.Output[0]);
  160. job.Output.Dispose();
  161. }
  162. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  163. [Test]
  164. public void SetCapacityLessThanLength()
  165. {
  166. var list = new NativeList<int>(Allocator.Persistent);
  167. list.Resize(10, NativeArrayOptions.UninitializedMemory);
  168. Assert.Throws<ArgumentOutOfRangeException>(() => { list.Capacity = 5; });
  169. list.Dispose();
  170. }
  171. [Test]
  172. public void DisposingNativeListDerivedArrayDoesNotThrow()
  173. {
  174. var list = new NativeList<int>(Allocator.Persistent);
  175. list.Add(1);
  176. NativeArray<int> array = list;
  177. Assert.DoesNotThrow(() => { array.Dispose(); });
  178. list.Dispose();
  179. }
  180. #endif
  181. [Test]
  182. public void NativeList_DisposeJob()
  183. {
  184. var container = new NativeList<int>(Allocator.Persistent);
  185. Assert.True(container.IsCreated);
  186. Assert.DoesNotThrow(() => { container.Add(0); });
  187. Assert.DoesNotThrow(() => { container.Contains(0); });
  188. var disposeJob = container.Dispose(default);
  189. Assert.False(container.IsCreated);
  190. Assert.Throws<ObjectDisposedException>(
  191. () => { container.Contains(0); });
  192. disposeJob.Complete();
  193. }
  194. [Test]
  195. public void ForEachWorks()
  196. {
  197. var container = new NativeList<int>(Allocator.Persistent);
  198. container.Add(10);
  199. container.Add(20);
  200. int sum = 0;
  201. int count = 0;
  202. GCAllocRecorder.ValidateNoGCAllocs(() =>
  203. {
  204. sum = 0;
  205. count = 0;
  206. foreach (var p in container)
  207. {
  208. sum += p;
  209. count++;
  210. }
  211. });
  212. Assert.AreEqual(30, sum);
  213. Assert.AreEqual(2, count);
  214. container.Dispose();
  215. }
  216. struct NativeQueueAddJob : IJob
  217. {
  218. NativeQueue<int> queue;
  219. public NativeQueueAddJob(NativeQueue<int> queue) { this.queue = queue; }
  220. public void Execute()
  221. {
  222. queue.Enqueue(1);
  223. }
  224. }
  225. [Test]
  226. public void NativeQueue_DisposeJobWithMissingDependencyThrows()
  227. {
  228. var queue = new NativeQueue<int>(Allocator.Persistent);
  229. var deps = new NativeQueueAddJob(queue).Schedule();
  230. Assert.Throws<InvalidOperationException>(() => { queue.Dispose(default); });
  231. deps.Complete();
  232. queue.Dispose();
  233. }
  234. [Test]
  235. public void NativeQueue_DisposeJobCantBeScheduled()
  236. {
  237. var queue = new NativeQueue<int>(Allocator.Persistent);
  238. var deps = queue.Dispose(default);
  239. Assert.Throws<InvalidOperationException>(() => { new NativeQueueAddJob(queue).Schedule(deps); });
  240. deps.Complete();
  241. }
  242. // These tests require:
  243. // - JobsDebugger support for static safety IDs (added in 2020.1)
  244. // - Asserting throws
  245. #if !UNITY_DOTSRUNTIME
  246. [Test,DotsRuntimeIgnore]
  247. public void NativeList_UseAfterFree_UsesCustomOwnerTypeName()
  248. {
  249. var list = new NativeList<int>(10, CommonRwdAllocator.Handle);
  250. list.Add(17);
  251. list.Dispose();
  252. Assert.That(() => list[0],
  253. Throws.Exception.TypeOf<ObjectDisposedException>()
  254. .With.Message.Contains($"The {list.GetType()} has been deallocated"));
  255. }
  256. [Test,DotsRuntimeIgnore]
  257. public void AtomicSafetyHandle_AllocatorTemp_UniqueStaticSafetyIds()
  258. {
  259. // All collections that use Allocator.Temp share the same core AtomicSafetyHandle.
  260. // This test verifies that containers can proceed to assign unique static safety IDs to each
  261. // AtomicSafetyHandle value, which will not be shared by other containers using Allocator.Temp.
  262. var listInt = new NativeList<int>(10, Allocator.Temp);
  263. var listFloat = new NativeList<float>(10, Allocator.Temp);
  264. listInt.Add(17);
  265. listInt.Dispose();
  266. Assert.That(() => listInt[0],
  267. Throws.Exception.TypeOf<ObjectDisposedException>()
  268. .With.Message.Contains($"The {listInt.GetType()} has been deallocated"));
  269. listFloat.Add(1.0f);
  270. listFloat.Dispose();
  271. Assert.That(() => listFloat[0],
  272. Throws.Exception.TypeOf<ObjectDisposedException>()
  273. .With.Message.Contains($"The {listFloat.GetType()} has been deallocated"));
  274. }
  275. [BurstCompile(CompileSynchronously = true)]
  276. struct NativeListCreateAndUseAfterFreeBurst : IJob
  277. {
  278. public void Execute()
  279. {
  280. var list = new NativeList<int>(10, Allocator.Temp);
  281. list.Add(17);
  282. list.Dispose();
  283. list.Add(42);
  284. }
  285. }
  286. [Test,DotsRuntimeIgnore]
  287. public void NativeList_CreateAndUseAfterFreeInBurstJob_UsesCustomOwnerTypeName()
  288. {
  289. // Make sure this isn't the first container of this type ever created, so that valid static safety data exists
  290. var list = new NativeList<int>(10, CommonRwdAllocator.Handle);
  291. list.Dispose();
  292. var job = new NativeListCreateAndUseAfterFreeBurst
  293. {
  294. };
  295. // Two things:
  296. // 1. This exception is logged, not thrown; thus, we use LogAssert to detect it.
  297. // 2. Calling write operation after container.Dispose() emits an unintuitive error message. For now, all this test cares about is whether it contains the
  298. // expected type name.
  299. job.Run();
  300. LogAssert.Expect(LogType.Exception,
  301. new Regex($"InvalidOperationException: The {Regex.Escape(list.GetType().ToString())} has been declared as \\[ReadOnly\\] in the job, but you are writing to it"));
  302. }
  303. #endif
  304. [Test]
  305. public unsafe void NativeList_IndexOf()
  306. {
  307. using (var list = new NativeList<int>(10, Allocator.Persistent) { 123, 789 })
  308. {
  309. bool r0 = false, r1 = false, r2 = false;
  310. GCAllocRecorder.ValidateNoGCAllocs(() =>
  311. {
  312. r0 = -1 != list.IndexOf(456);
  313. r1 = list.Contains(123);
  314. r2 = list.Contains(789);
  315. });
  316. Assert.False(r0);
  317. Assert.True(r1);
  318. Assert.True(r2);
  319. }
  320. }
  321. [Test]
  322. public void NativeList_InsertRangeWithBeginEnd()
  323. {
  324. var list = new NativeList<byte>(3, Allocator.Persistent);
  325. list.Add(0);
  326. list.Add(3);
  327. list.Add(4);
  328. Assert.Throws<ArgumentOutOfRangeException>(() => list.InsertRangeWithBeginEnd(-1, 8));
  329. Assert.Throws<ArgumentOutOfRangeException>(() => list.InsertRangeWithBeginEnd(0, 8));
  330. Assert.Throws<ArgumentException>(() => list.InsertRangeWithBeginEnd(3, 1));
  331. Assert.DoesNotThrow(() => list.InsertRangeWithBeginEnd(1, 3));
  332. list[1] = 1;
  333. list[2] = 2;
  334. for (var i = 0; i < 5; ++i)
  335. {
  336. Assert.AreEqual(i, list[i]);
  337. }
  338. list.Dispose();
  339. }
  340. [Test]
  341. public void NativeList_CustomAllocatorTest()
  342. {
  343. AllocatorManager.Initialize();
  344. var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent);
  345. ref var allocator = ref allocatorHelper.Allocator;
  346. allocator.Initialize();
  347. using (var container = new NativeList<byte>(1, allocator.Handle))
  348. {
  349. }
  350. Assert.IsTrue(allocator.WasUsed);
  351. allocator.Dispose();
  352. allocatorHelper.Dispose();
  353. AllocatorManager.Shutdown();
  354. }
  355. [BurstCompile]
  356. struct BurstedCustomAllocatorJob : IJob
  357. {
  358. [NativeDisableUnsafePtrRestriction]
  359. public unsafe CustomAllocatorTests.CountingAllocator* Allocator;
  360. public void Execute()
  361. {
  362. unsafe
  363. {
  364. using (var container = new NativeList<byte>(1, Allocator->Handle))
  365. {
  366. }
  367. }
  368. }
  369. }
  370. [Test]
  371. public unsafe void NativeList_BurstedCustomAllocatorTest()
  372. {
  373. AllocatorManager.Initialize();
  374. var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent);
  375. ref var allocator = ref allocatorHelper.Allocator;
  376. allocator.Initialize();
  377. var allocatorPtr = (CustomAllocatorTests.CountingAllocator*)UnsafeUtility.AddressOf<CustomAllocatorTests.CountingAllocator>(ref allocator);
  378. unsafe
  379. {
  380. var handle = new BurstedCustomAllocatorJob {Allocator = allocatorPtr}.Schedule();
  381. handle.Complete();
  382. }
  383. Assert.IsTrue(allocator.WasUsed);
  384. allocator.Dispose();
  385. allocatorHelper.Dispose();
  386. AllocatorManager.Shutdown();
  387. }
  388. [Test]
  389. public unsafe void NativeList_SetCapacity()
  390. {
  391. using (var list = new NativeList<int>(1, Allocator.Persistent))
  392. {
  393. list.Add(1);
  394. Assert.DoesNotThrow(() => list.SetCapacity(128));
  395. list.Add(1);
  396. Assert.AreEqual(2, list.Length);
  397. Assert.Throws<ArgumentOutOfRangeException>(() => list.SetCapacity(1));
  398. list.RemoveAtSwapBack(0);
  399. Assert.AreEqual(1, list.Length);
  400. Assert.DoesNotThrow(() => list.SetCapacity(1));
  401. list.TrimExcess();
  402. Assert.AreEqual(1, list.Capacity);
  403. }
  404. }
  405. [Test]
  406. public unsafe void NativeList_TrimExcess()
  407. {
  408. using (var list = new NativeList<int>(32, Allocator.Persistent))
  409. {
  410. list.Add(1);
  411. list.TrimExcess();
  412. Assert.AreEqual(1, list.Length);
  413. Assert.AreEqual(1, list.Capacity);
  414. list.RemoveAtSwapBack(0);
  415. Assert.AreEqual(list.Length, 0);
  416. list.TrimExcess();
  417. Assert.AreEqual(list.Capacity, 0);
  418. list.Add(1);
  419. Assert.AreEqual(list.Length, 1);
  420. Assert.AreNotEqual(list.Capacity, 0);
  421. list.Clear();
  422. }
  423. }
  424. }