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.NotBurstCompatible;
7using Unity.Jobs;
8using Unity.Collections.Tests;
9
10internal class NativeHashSetTests: CollectionsTestFixture
11{
12 static void ExpectedCount<T>(ref NativeHashSet<T> container, int expected)
13 where T : unmanaged, IEquatable<T>
14 {
15 Assert.AreEqual(expected == 0, container.IsEmpty);
16 Assert.AreEqual(expected, container.Count);
17 }
18
19 [Test]
20 public void NativeHashSet_IsEmpty()
21 {
22 var container = new NativeHashSet<int>(0, Allocator.Persistent);
23 Assert.IsTrue(container.IsEmpty);
24
25 Assert.IsTrue(container.Add(0));
26 Assert.IsFalse(container.IsEmpty);
27 Assert.AreNotEqual(0, container.Capacity);
28 ExpectedCount(ref container, 1);
29
30 container.Remove(0);
31 Assert.IsTrue(container.IsEmpty);
32
33 Assert.IsTrue(container.Add(0));
34 container.Clear();
35 Assert.IsTrue(container.IsEmpty);
36
37 container.Dispose();
38 }
39
40 [Test]
41 public void NativeHashSet_Capacity()
42 {
43 var container = new NativeHashSet<int>(0, Allocator.Persistent);
44 Assert.IsTrue(container.IsEmpty);
45
46 container.Capacity = 10;
47 Assert.AreNotEqual(0, container.Capacity);
48
49 container.Dispose();
50 }
51
52 [Test]
53 public void NativeHashSet_CapacityAtLeastCount()
54 {
55 var container = new NativeHashSet<int>(0, Allocator.Persistent);
56
57 for (int i = 0; i < 300; i++)
58 container.Add(i);
59
60 container.Capacity = 3;
61 Assert.IsTrue(container.Capacity >= container.Count);
62
63 container.Dispose();
64 }
65
66 [Test]
67 public void NativeHashSet_RemoveOnEmptyMap_DoesNotThrow()
68 {
69 var container = new NativeHashSet<int>(0, Allocator.Temp);
70 Assert.DoesNotThrow(() => container.Remove(0));
71 Assert.DoesNotThrow(() => container.Remove(-425196));
72 container.Dispose();
73 }
74
75 [Test]
76 public void NativeHashSet_Collisions()
77 {
78 var container = new NativeHashSet<int>(16, Allocator.Temp);
79
80 Assert.IsFalse(container.Contains(0), "Contains on empty hash map did not fail");
81 ExpectedCount(ref container, 0);
82
83 // Make sure inserting values work
84 for (int i = 0; i < 8; ++i)
85 {
86 Assert.IsTrue(container.Add(i), "Failed to add value");
87 }
88 ExpectedCount(ref container, 8);
89
90 // The bucket size is capacity * 2, adding that number should result in hash collisions
91 for (int i = 0; i < 8; ++i)
92 {
93 Assert.IsTrue(container.Add(i + 32), "Failed to add value with potential hash collision");
94 }
95
96 // Make sure reading the inserted values work
97 for (int i = 0; i < 8; ++i)
98 {
99 Assert.IsTrue(container.Contains(i), "Failed get value from hash set");
100 }
101
102 for (int i = 0; i < 8; ++i)
103 {
104 Assert.IsTrue(container.Contains(i + 32), "Failed get value from hash set");
105 }
106
107 container.Dispose();
108 }
109
110 [Test]
111 public void NativeHashSet_SameElement()
112 {
113 using (var container = new NativeHashSet<int>(0, Allocator.Persistent))
114 {
115 Assert.IsTrue(container.Add(0));
116 Assert.IsFalse(container.Add(0));
117 }
118 }
119
120 [Test]
121 public void NativeHashSet_CanBeReadFromJob()
122 {
123 using (var hashSet = new NativeHashSet<int>(1, CommonRwdAllocator.Handle))
124 using (var result = new NativeReference<int>(CommonRwdAllocator.Handle))
125 {
126 hashSet.Add(42);
127 new ReadHashSetJob
128 {
129 Input = hashSet.AsReadOnly(),
130 Output = result,
131 }.Run();
132 Assert.AreEqual(42, result.Value);
133 }
134 }
135
136 struct TempHashSet : IJob
137 {
138 public void Execute()
139 {
140 using (var stringList = new NativeList<FixedString32Bytes>(10, Allocator.Persistent) { "Hello", ",", "World", "!" })
141 {
142 var container = new NativeHashSet<FixedString128Bytes>(50, Allocator.Temp);
143 var seen = new NativeArray<int>(stringList.Length, Allocator.Temp);
144 foreach (var str in stringList)
145 {
146 container.Add(str);
147 }
148
149 foreach (var value in container)
150 {
151 int index = stringList.IndexOf(value);
152 Assert.AreEqual(stringList[index], value.ToString());
153 seen[index] = seen[index] + 1;
154 }
155
156 for (int i = 0; i < stringList.Length; i++)
157 {
158 Assert.AreEqual(1, seen[i], $"Incorrect value count {stringList[i]}");
159 }
160 }
161 }
162 }
163
164 [Test]
165 public void NativeHashSet_TempHashSetInJob()
166 {
167 new TempHashSet { }.Schedule().Complete();
168 }
169
170 struct ReadHashSetJob : IJob
171 {
172 [ReadOnly]
173 public NativeHashSet<int>.ReadOnly Input;
174
175 public NativeReference<int> Output;
176 public void Execute()
177 {
178 Output.Value = Input.ToNativeArray(Allocator.Temp)[0];
179
180 foreach (var value in Input)
181 {
182 Assert.AreEqual(42, value);
183 }
184 }
185 }
186
187 [Test]
188 public void NativeHashSet_ForEach_FixedStringInHashMap()
189 {
190 using (var stringList = new NativeList<FixedString32Bytes>(10, Allocator.Persistent) { "Hello", ",", "World", "!" })
191 {
192 var container = new NativeHashSet<FixedString128Bytes>(50, Allocator.Temp);
193 var seen = new NativeArray<int>(stringList.Length, Allocator.Temp);
194 foreach (var str in stringList)
195 {
196 container.Add(str);
197 }
198
199 foreach (var value in container)
200 {
201 int index = stringList.IndexOf(value);
202 Assert.AreEqual(stringList[index], value.ToString());
203 seen[index] = seen[index] + 1;
204 }
205
206 for (int i = 0; i < stringList.Length; i++)
207 {
208 Assert.AreEqual(1, seen[i], $"Incorrect value count {stringList[i]}");
209 }
210 }
211 }
212
213 [Test]
214 public void NativeHashSet_ForEach([Values(10, 1000)]int n)
215 {
216 var seen = new NativeArray<int>(n, Allocator.Temp);
217 using (var container = new NativeHashSet<int>(32, CommonRwdAllocator.Handle))
218 {
219 for (int i = 0; i < n; i++)
220 {
221 Assert.True(container.Add(i));
222 }
223
224 var count = 0;
225 foreach (var item in container)
226 {
227 Assert.True(container.Contains(item));
228 seen[item] = seen[item] + 1;
229 ++count;
230 }
231
232 Assert.AreEqual(container.Count, count);
233 for (int i = 0; i < n; i++)
234 {
235 Assert.AreEqual(1, seen[i], $"Incorrect item count {i}");
236 }
237 }
238 }
239
240 struct NativeHashSet_ForEach_Job : IJob
241 {
242 [ReadOnly]
243 public NativeHashSet<int>.ReadOnly Input;
244
245 [ReadOnly]
246 public int Num;
247
248 public void Execute()
249 {
250 var seen = new NativeArray<int>(Num, Allocator.Temp);
251
252 var count = 0;
253 foreach (var item in Input)
254 {
255 Assert.True(Input.Contains(item));
256 seen[item] = seen[item] + 1;
257 ++count;
258 }
259
260 Assert.AreEqual(Input.Count, count);
261 for (int i = 0; i < Num; i++)
262 {
263 Assert.AreEqual(1, seen[i], $"Incorrect item count {i}");
264 }
265
266 seen.Dispose();
267 }
268 }
269
270 [Test]
271 public void NativeHashSet_ForEach_From_Job([Values(10, 1000)] int n)
272 {
273 using (var container = new NativeHashSet<int>(32, CommonRwdAllocator.Handle))
274 {
275 for (int i = 0; i < n; i++)
276 {
277 container.Add(i);
278 }
279
280 new NativeHashSet_ForEach_Job
281 {
282 Input = container.AsReadOnly(),
283 Num = n,
284
285 }.Run();
286 }
287 }
288
289 struct NativeHashSet_Write_Job : IJob
290 {
291 public NativeHashSet<int> Input;
292
293 public void Execute()
294 {
295 Input.Clear();
296 }
297 }
298
299 [Test]
300 public void NativeHashSet_Write_From_Job()
301 {
302 using (var container = new NativeHashSet<int>(32, CommonRwdAllocator.Handle))
303 {
304 container.Add(0);
305 Assert.IsFalse(container.IsEmpty);
306 new NativeHashSet_Write_Job
307 {
308 Input = container,
309 }.Run();
310 Assert.IsTrue(container.IsEmpty);
311 }
312 }
313
314 [Test]
315 [TestRequiresCollectionChecks]
316 public void NativeHashSet_ForEach_Throws_When_Modified()
317 {
318 using (var container = new NativeHashSet<int>(32, CommonRwdAllocator.Handle))
319 {
320 container.Add(0);
321 container.Add(1);
322 container.Add(2);
323 container.Add(3);
324 container.Add(4);
325 container.Add(5);
326 container.Add(6);
327 container.Add(7);
328 container.Add(8);
329 container.Add(9);
330
331 Assert.Throws<ObjectDisposedException>(() =>
332 {
333 foreach (var item in container)
334 {
335 container.Add(10);
336 }
337 });
338
339 Assert.Throws<ObjectDisposedException>(() =>
340 {
341 foreach (var item in container)
342 {
343 container.Remove(1);
344 }
345 });
346 }
347 }
348
349 struct ForEachIterator : IJob
350 {
351 [ReadOnly]
352 public NativeHashSet<int>.Enumerator Iter;
353
354 public void Execute()
355 {
356 while (Iter.MoveNext())
357 {
358 }
359 }
360 }
361
362 [Test]
363 [TestRequiresCollectionChecks]
364 public void NativeHashSet_ForEach_Throws_Job_Iterator()
365 {
366 using (var container = new NativeHashSet<int>(32, CommonRwdAllocator.Handle))
367 {
368 var jobHandle = new ForEachIterator
369 {
370 Iter = container.GetEnumerator()
371
372 }.Schedule();
373
374 Assert.Throws<InvalidOperationException>(() => { container.Add(1); });
375
376 jobHandle.Complete();
377 }
378 }
379
380 [Test]
381 public void NativeHashSet_EIU_ExceptWith_Empty()
382 {
383 var setA = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { };
384 var setB = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { };
385 setA.ExceptWith(setB);
386
387 ExpectedCount(ref setA, 0);
388
389 setA.Dispose();
390 setB.Dispose();
391 }
392
393 [Test]
394 public void NativeHashSet_EIU_ExceptWith_AxB()
395 {
396 var setA = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { 0, 1, 2, 3, 4, 5 };
397 var setB = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { 3, 4, 5, 6, 7, 8 };
398 setA.ExceptWith(setB);
399
400 ExpectedCount(ref setA, 3);
401 Assert.True(setA.Contains(0));
402 Assert.True(setA.Contains(1));
403 Assert.True(setA.Contains(2));
404
405 setA.Dispose();
406 setB.Dispose();
407 }
408
409 [Test]
410 public void NativeHashSet_EIU_ExceptWith_BxA()
411 {
412 var setA = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { 0, 1, 2, 3, 4, 5 };
413 var setB = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { 3, 4, 5, 6, 7, 8 };
414 setB.ExceptWith(setA);
415
416 ExpectedCount(ref setB, 3);
417 Assert.True(setB.Contains(6));
418 Assert.True(setB.Contains(7));
419 Assert.True(setB.Contains(8));
420
421 setA.Dispose();
422 setB.Dispose();
423 }
424
425 [Test]
426 public void NativeHashSet_EIU_IntersectWith_Empty()
427 {
428 var setA = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { };
429 var setB = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { };
430 setA.IntersectWith(setB);
431
432 ExpectedCount(ref setA, 0);
433
434 setA.Dispose();
435 setB.Dispose();
436 }
437
438 [Test]
439 public void NativeHashSet_EIU_IntersectWith()
440 {
441 var setA = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { 0, 1, 2, 3, 4, 5 };
442 var setB = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { 3, 4, 5, 6, 7, 8 };
443 setA.IntersectWith(setB);
444
445 ExpectedCount(ref setA, 3);
446 Assert.True(setA.Contains(3));
447 Assert.True(setA.Contains(4));
448 Assert.True(setA.Contains(5));
449
450 setA.Dispose();
451 setB.Dispose();
452 }
453
454 [Test]
455 public void NativeHashSet_EIU_UnionWith_Empty()
456 {
457 var setA = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { };
458 var setB = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { };
459 setA.UnionWith(setB);
460
461 ExpectedCount(ref setA, 0);
462
463 setA.Dispose();
464 setB.Dispose();
465 }
466
467 [Test]
468 public void NativeHashSet_EIU_UnionWith()
469 {
470 var setA = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { 0, 1, 2, 3, 4, 5 };
471 var setB = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { 3, 4, 5, 6, 7, 8 };
472 setA.UnionWith(setB);
473
474 ExpectedCount(ref setA, 9);
475 Assert.True(setA.Contains(0));
476 Assert.True(setA.Contains(1));
477 Assert.True(setA.Contains(2));
478 Assert.True(setA.Contains(3));
479 Assert.True(setA.Contains(4));
480 Assert.True(setA.Contains(5));
481 Assert.True(setA.Contains(6));
482 Assert.True(setA.Contains(7));
483 Assert.True(setA.Contains(8));
484
485 setA.Dispose();
486 setB.Dispose();
487 }
488
489 [Test]
490 public void NativeHashSet_ToArray()
491 {
492 using (var set = new NativeHashSet<int>(8, CommonRwdAllocator.Handle) { 0, 1, 2, 3, 4, 5 })
493 {
494 var array = set.ToArray();
495 Array.Sort(array);
496 for (int i = 0, num = set.Count; i < num; i++)
497 {
498 Assert.AreEqual(array[i], i);
499 }
500 }
501 }
502
503 [Test]
504 public void NativeHashSet_CustomAllocatorTest()
505 {
506 AllocatorManager.Initialize();
507 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent);
508 ref var allocator = ref allocatorHelper.Allocator;
509 allocator.Initialize();
510
511 using (var container = new NativeHashSet<int>(1, allocator.Handle))
512 {
513 }
514
515 FastAssert.IsTrue(allocator.WasUsed);
516 allocator.Dispose();
517 allocatorHelper.Dispose();
518 AllocatorManager.Shutdown();
519 }
520
521 [BurstCompile]
522 struct BurstedCustomAllocatorJob : IJob
523 {
524 [NativeDisableUnsafePtrRestriction]
525 public unsafe CustomAllocatorTests.CountingAllocator* Allocator;
526
527 public void Execute()
528 {
529 unsafe
530 {
531 using (var container = new NativeHashSet<int>(1, Allocator->Handle))
532 {
533 }
534 }
535 }
536 }
537
538 [Test]
539 public unsafe void NativeHashSet_BurstedCustomAllocatorTest()
540 {
541 AllocatorManager.Initialize();
542 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent);
543 ref var allocator = ref allocatorHelper.Allocator;
544 allocator.Initialize();
545
546 var allocatorPtr = (CustomAllocatorTests.CountingAllocator*)UnsafeUtility.AddressOf<CustomAllocatorTests.CountingAllocator>(ref allocator);
547 unsafe
548 {
549 var handle = new BurstedCustomAllocatorJob {Allocator = allocatorPtr }.Schedule();
550 handle.Complete();
551 }
552
553 FastAssert.IsTrue(allocator.WasUsed);
554 allocator.Dispose();
555 allocatorHelper.Dispose();
556 AllocatorManager.Shutdown();
557 }
558}