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