A game about forced loneliness, made by TACStudios
1using System;
2using NUnit.Framework;
3using Unity.Burst;
4using Unity.Collections;
5using Unity.Collections.Tests;
6using Unity.Jobs;
7using Unity.Collections.LowLevel.Unsafe;
8internal class UnsafeParallelHashSetTests : CollectionsTestCommonBase
9{
10 static void ExpectedCount<T>(ref UnsafeParallelHashSet<T> container, int expected)
11 where T : unmanaged, IEquatable<T>
12 {
13 Assert.AreEqual(expected == 0, container.IsEmpty);
14 Assert.AreEqual(expected, container.Count());
15 }
16
17 [Test]
18 public void UnsafeParallelHashSet_IsEmpty()
19 {
20 var container = new UnsafeParallelHashSet<int>(0, Allocator.Persistent);
21 Assert.IsTrue(container.IsEmpty);
22
23 Assert.IsTrue(container.Add(0));
24 Assert.IsFalse(container.IsEmpty);
25 Assert.AreEqual(1, container.Capacity);
26 ExpectedCount(ref container, 1);
27
28 container.Remove(0);
29 Assert.IsTrue(container.IsEmpty);
30
31 Assert.IsTrue(container.Add(0));
32 container.Clear();
33 Assert.IsTrue(container.IsEmpty);
34
35 container.Dispose();
36 }
37
38 [Test]
39 public void UnsafeParallelHashSet_Capacity()
40 {
41 var container = new UnsafeParallelHashSet<int>(0, Allocator.Persistent);
42 Assert.IsTrue(container.IsEmpty);
43 Assert.AreEqual(0, container.Capacity);
44
45 container.Capacity = 10;
46 Assert.AreEqual(10, container.Capacity);
47
48 container.Dispose();
49 }
50
51 [Test]
52 [TestRequiresCollectionChecks]
53 public void UnsafeParallelHashSet_Full_Throws()
54 {
55 var container = new UnsafeParallelHashSet<int>(16, Allocator.Temp);
56 ExpectedCount(ref container, 0);
57
58 for (int i = 0, capacity = container.Capacity; i < capacity; ++i)
59 {
60 Assert.DoesNotThrow(() => { container.Add(i); });
61 }
62 ExpectedCount(ref container, container.Capacity);
63
64 // Make sure overallocating throws and exception if using the Concurrent version - normal hash map would grow
65 var writer = container.AsParallelWriter();
66 Assert.Throws<System.InvalidOperationException>(() => { writer.Add(100); });
67 ExpectedCount(ref container, container.Capacity);
68
69 container.Clear();
70 ExpectedCount(ref container, 0);
71
72 container.Dispose();
73 }
74
75 [Test]
76 public void UnsafeParallelHashSet_RemoveOnEmptyMap_DoesNotThrow()
77 {
78 var container = new UnsafeParallelHashSet<int>(0, Allocator.Temp);
79 Assert.DoesNotThrow(() => container.Remove(0));
80 Assert.DoesNotThrow(() => container.Remove(-425196));
81 container.Dispose();
82 }
83
84 [Test]
85 public void UnsafeParallelHashSet_Collisions()
86 {
87 var container = new UnsafeParallelHashSet<int>(16, Allocator.Temp);
88
89 Assert.IsFalse(container.Contains(0), "Contains on empty hash map did not fail");
90 ExpectedCount(ref container, 0);
91
92 // Make sure inserting values work
93 for (int i = 0; i < 8; ++i)
94 {
95 Assert.IsTrue(container.Add(i), "Failed to add value");
96 }
97 ExpectedCount(ref container, 8);
98
99 // The bucket size is capacity * 2, adding that number should result in hash collisions
100 for (int i = 0; i < 8; ++i)
101 {
102 Assert.IsTrue(container.Add(i + 32), "Failed to add value with potential hash collision");
103 }
104
105 // Make sure reading the inserted values work
106 for (int i = 0; i < 8; ++i)
107 {
108 Assert.IsTrue(container.Contains(i), "Failed get value from hash set");
109 }
110
111 for (int i = 0; i < 8; ++i)
112 {
113 Assert.IsTrue(container.Contains(i + 32), "Failed get value from hash set");
114 }
115
116 container.Dispose();
117 }
118
119 [Test]
120 public void UnsafeParallelHashSet_SameElement()
121 {
122 using (var container = new UnsafeParallelHashSet<int>(0, Allocator.Persistent))
123 {
124 Assert.IsTrue(container.Add(0));
125 Assert.IsFalse(container.Add(0));
126 }
127 }
128
129 [Test]
130 public void UnsafeParallelHashSet_ForEach_FixedStringInHashMap()
131 {
132 using (var stringList = new NativeList<FixedString32Bytes>(10, Allocator.Persistent) { "Hello", ",", "World", "!" })
133 {
134 var container = new NativeParallelHashSet<FixedString128Bytes>(50, Allocator.Temp);
135 var seen = new NativeArray<int>(stringList.Length, Allocator.Temp);
136 foreach (var str in stringList)
137 {
138 container.Add(str);
139 }
140
141 foreach (var value in container)
142 {
143 int index = stringList.IndexOf(value);
144 Assert.AreEqual(stringList[index], value.ToString());
145 seen[index] = seen[index] + 1;
146 }
147
148 for (int i = 0; i < stringList.Length; i++)
149 {
150 Assert.AreEqual(1, seen[i], $"Incorrect value count {stringList[i]}");
151 }
152 }
153 }
154
155 [Test]
156 public void UnsafeParallelHashSet_ForEach([Values(10, 1000)]int n)
157 {
158 var seen = new NativeArray<int>(n, Allocator.Temp);
159 using (var container = new UnsafeParallelHashSet<int>(32, CommonRwdAllocator.Handle))
160 {
161 for (int i = 0; i < n; i++)
162 {
163 container.Add(i);
164 }
165
166 var count = 0;
167 foreach (var item in container)
168 {
169 Assert.True(container.Contains(item));
170 seen[item] = seen[item] + 1;
171 ++count;
172 }
173
174 Assert.AreEqual(container.Count(), count);
175 for (int i = 0; i < n; i++)
176 {
177 Assert.AreEqual(1, seen[i], $"Incorrect item count {i}");
178 }
179 }
180 }
181
182 [Test]
183 public void UnsafeParallelHashSet_EIU_ExceptWith_Empty()
184 {
185 var setA = new UnsafeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { };
186 var setB = new UnsafeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { };
187 setA.ExceptWith(setB);
188
189 ExpectedCount(ref setA, 0);
190
191 setA.Dispose();
192 setB.Dispose();
193 }
194
195 [Test]
196 public void UnsafeParallelHashSet_EIU_ExceptWith_AxB()
197 {
198 var setA = new UnsafeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 0, 1, 2, 3, 4, 5 };
199 var setB = new UnsafeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 3, 4, 5, 6, 7, 8 };
200 setA.ExceptWith(setB);
201
202 ExpectedCount(ref setA, 3);
203 Assert.True(setA.Contains(0));
204 Assert.True(setA.Contains(1));
205 Assert.True(setA.Contains(2));
206
207 setA.Dispose();
208 setB.Dispose();
209 }
210
211 [Test]
212 public void UnsafeParallelHashSet_EIU_ExceptWith_BxA()
213 {
214 var setA = new UnsafeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 0, 1, 2, 3, 4, 5 };
215 var setB = new UnsafeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 3, 4, 5, 6, 7, 8 };
216 setB.ExceptWith(setA);
217
218 ExpectedCount(ref setB, 3);
219 Assert.True(setB.Contains(6));
220 Assert.True(setB.Contains(7));
221 Assert.True(setB.Contains(8));
222
223 setA.Dispose();
224 setB.Dispose();
225 }
226
227 [Test]
228 public void UnsafeParallelHashSet_EIU_IntersectWith_Empty()
229 {
230 var setA = new UnsafeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { };
231 var setB = new UnsafeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { };
232 setA.IntersectWith(setB);
233
234 ExpectedCount(ref setA, 0);
235
236 setA.Dispose();
237 setB.Dispose();
238 }
239
240 [Test]
241 public void UnsafeParallelHashSet_EIU_IntersectWith()
242 {
243 var setA = new UnsafeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 0, 1, 2, 3, 4, 5 };
244 var setB = new UnsafeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 3, 4, 5, 6, 7, 8 };
245 setA.IntersectWith(setB);
246
247 ExpectedCount(ref setA, 3);
248 Assert.True(setA.Contains(3));
249 Assert.True(setA.Contains(4));
250 Assert.True(setA.Contains(5));
251
252 setA.Dispose();
253 setB.Dispose();
254 }
255
256 [Test]
257 public void UnsafeParallelHashSet_EIU_UnionWith_Empty()
258 {
259 var setA = new UnsafeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { };
260 var setB = new UnsafeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { };
261 setA.UnionWith(setB);
262
263 ExpectedCount(ref setA, 0);
264
265 setA.Dispose();
266 setB.Dispose();
267 }
268
269 [Test]
270 public void UnsafeParallelHashSet_EIU_UnionWith()
271 {
272 var setA = new UnsafeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 0, 1, 2, 3, 4, 5 };
273 var setB = new UnsafeParallelHashSet<int>(8, CommonRwdAllocator.Handle) { 3, 4, 5, 6, 7, 8 };
274 setA.UnionWith(setB);
275
276 ExpectedCount(ref setA, 9);
277 Assert.True(setA.Contains(0));
278 Assert.True(setA.Contains(1));
279 Assert.True(setA.Contains(2));
280 Assert.True(setA.Contains(3));
281 Assert.True(setA.Contains(4));
282 Assert.True(setA.Contains(5));
283 Assert.True(setA.Contains(6));
284 Assert.True(setA.Contains(7));
285 Assert.True(setA.Contains(8));
286
287 setA.Dispose();
288 setB.Dispose();
289 }
290
291 [Test]
292 public void UnsafeParallelHashSet_CustomAllocatorTest()
293 {
294 AllocatorManager.Initialize();
295 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent);
296 ref var allocator = ref allocatorHelper.Allocator;
297 allocator.Initialize();
298
299 using (var container = new UnsafeParallelHashSet<int>(1, allocator.Handle))
300 {
301 }
302
303 Assert.IsTrue(allocator.WasUsed);
304 allocator.Dispose();
305 allocatorHelper.Dispose();
306 AllocatorManager.Shutdown();
307 }
308
309 [BurstCompile]
310 struct BurstedCustomAllocatorJob : IJob
311 {
312 [NativeDisableUnsafePtrRestriction]
313 public unsafe CustomAllocatorTests.CountingAllocator* Allocator;
314
315 public void Execute()
316 {
317 unsafe
318 {
319 using (var container = new UnsafeParallelHashSet<int>(1, Allocator->Handle))
320 {
321 }
322 }
323 }
324 }
325
326 [Test]
327 public unsafe void UnsafeParallelHashSet_BurstedCustomAllocatorTest()
328 {
329 AllocatorManager.Initialize();
330 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent);
331 ref var allocator = ref allocatorHelper.Allocator;
332 allocator.Initialize();
333
334 var allocatorPtr = (CustomAllocatorTests.CountingAllocator*)UnsafeUtility.AddressOf<CustomAllocatorTests.CountingAllocator>(ref allocator);
335 unsafe
336 {
337 var handle = new BurstedCustomAllocatorJob {Allocator = allocatorPtr}.Schedule();
338 handle.Complete();
339 }
340
341 Assert.IsTrue(allocator.WasUsed);
342 allocator.Dispose();
343 allocatorHelper.Dispose();
344 AllocatorManager.Shutdown();
345 }
346
347 struct UnsafeParallelHashSet_ForEach_Job : IJob
348 {
349 [ReadOnly]
350 public UnsafeParallelHashSet<int>.ReadOnly Input;
351
352 [ReadOnly]
353 public int Num;
354
355 public void Execute()
356 {
357 var seen = new NativeArray<int>(Num, Allocator.Temp);
358
359 var count = 0;
360 foreach (var item in Input)
361 {
362 Assert.True(Input.Contains(item));
363 seen[item] = seen[item] + 1;
364 ++count;
365 }
366
367 Assert.AreEqual(Input.Count(), count);
368 for (int i = 0; i < Num; i++)
369 {
370 Assert.AreEqual(1, seen[i], $"Incorrect item count {i}");
371 }
372
373 seen.Dispose();
374 }
375 }
376
377 [Test]
378 public void UnsafeParallelHashSet_ForEach_From_Job([Values(10, 1000)] int n)
379 {
380 using (var container = new UnsafeParallelHashSet<int>(32, CommonRwdAllocator.Handle))
381 {
382 for (int i = 0; i < n; i++)
383 {
384 container.Add(i);
385 }
386
387 new UnsafeParallelHashSet_ForEach_Job
388 {
389 Input = container.AsReadOnly(),
390 Num = n,
391
392 }.Run();
393 }
394 }
395}