No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

NativeStreamTests.cs 15KB

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