A game about forced loneliness, made by TACStudios
at master 535 lines 17 kB view raw
1using NUnit.Framework; 2using Unity.Collections; 3using Unity.Jobs; 4using Unity.Collections.LowLevel.Unsafe; 5using Unity.Collections.LowLevel.Unsafe.NotBurstCompatible; 6using Unity.Collections.Tests; 7using System; 8using Unity.Burst; 9using System.Diagnostics; 10 11internal class UnsafeHashMapTests : CollectionsTestCommonBase 12{ 13 [Test] 14 public void UnsafeHashMap_ForEach([Values(10, 1000)] int n) 15 { 16 var seen = new NativeArray<int>(n, Allocator.Temp); 17 using (var container = new UnsafeHashMap<int, int>(32, CommonRwdAllocator.Handle)) 18 { 19 for (int i = 0; i < n; i++) 20 { 21 container.Add(i, i * 37); 22 } 23 24 var count = 0; 25 foreach (var kv in container) 26 { 27 int value; 28 Assert.True(container.TryGetValue(kv.Key, out value)); 29 Assert.AreEqual(value, kv.Value); 30 Assert.AreEqual(kv.Key * 37, kv.Value); 31 32 seen[kv.Key] = seen[kv.Key] + 1; 33 ++count; 34 } 35 36 Assert.AreEqual(container.Count, count); 37 for (int i = 0; i < n; i++) 38 { 39 Assert.AreEqual(1, seen[i], $"Incorrect key count {i}"); 40 } 41 } 42 } 43 44 [Test] 45 public void UnsafeHashMap_ForEach_FixedStringKey() 46 { 47 using (var stringList = new NativeList<FixedString32Bytes>(10, Allocator.Persistent) { "Hello", ",", "World", "!" }) 48 { 49 var seen = new NativeArray<int>(stringList.Length, Allocator.Temp); 50 var container = new UnsafeHashMap<FixedString128Bytes, float>(50, Allocator.Temp); 51 foreach (var str in stringList) 52 { 53 container.Add(str, 0); 54 } 55 56 foreach (var pair in container) 57 { 58 int index = stringList.IndexOf(pair.Key); 59 Assert.AreEqual(stringList[index], pair.Key.ToString()); 60 seen[index] = seen[index] + 1; 61 } 62 63 for (int i = 0; i < stringList.Length; i++) 64 { 65 Assert.AreEqual(1, seen[i], $"Incorrect value count {stringList[i]}"); 66 } 67 } 68 } 69 70 struct UnsafeHashMap_ForEachIterator : IJob 71 { 72 [ReadOnly] 73 public UnsafeHashMap<int, int>.Enumerator Iter; 74 75 public void Execute() 76 { 77 while (Iter.MoveNext()) 78 { 79 } 80 } 81 } 82 83 [Test] 84 public void UnsafeHashMap_ForEach_Throws_Job_Iterator() 85 { 86 using (var container = new UnsafeHashMap<int, int>(32, CommonRwdAllocator.Handle)) 87 { 88 var jobHandle = new UnsafeHashMap_ForEachIterator 89 { 90 Iter = container.GetEnumerator() 91 92 }.Schedule(); 93 94 jobHandle.Complete(); 95 } 96 } 97 98 struct UnsafeHashMap_ForEach_Job : IJob 99 { 100 public UnsafeHashMap<int, int>.ReadOnly Input; 101 102 [ReadOnly] 103 public int Num; 104 105 public void Execute() 106 { 107 var seen = new NativeArray<int>(Num, Allocator.Temp); 108 109 var count = 0; 110 foreach (var kv in Input) 111 { 112 int value; 113 Assert.True(Input.TryGetValue(kv.Key, out value)); 114 Assert.AreEqual(value, kv.Value); 115 Assert.AreEqual(kv.Key * 37, kv.Value); 116 117 seen[kv.Key] = seen[kv.Key] + 1; 118 ++count; 119 } 120 121 Assert.AreEqual(Input.Count, count); 122 for (int i = 0; i < Num; i++) 123 { 124 Assert.AreEqual(1, seen[i], $"Incorrect key count {i}"); 125 } 126 127 seen.Dispose(); 128 } 129 } 130 131 [Test] 132 public void UnsafeHashMap_ForEach_From_Job([Values(10, 1000)] int n) 133 { 134 var seen = new NativeArray<int>(n, Allocator.Temp); 135 using (var container = new UnsafeHashMap<int, int>(32, CommonRwdAllocator.Handle)) 136 { 137 for (int i = 0; i < n; i++) 138 { 139 container.Add(i, i * 37); 140 } 141 142 new UnsafeHashMap_ForEach_Job 143 { 144 Input = container.AsReadOnly(), 145 Num = n, 146 147 }.Run(); 148 } 149 } 150 151 [Test] 152 public void UnsafeHashMap_EnumeratorDoesNotReturnRemovedElementsTest() 153 { 154 UnsafeHashMap<int, int> container = new UnsafeHashMap<int, int>(5, Allocator.Temp); 155 for (int i = 0; i < 5; i++) 156 { 157 container.Add(i, i); 158 } 159 160 int elementToRemove = 2; 161 Assert.IsTrue(container.Remove(elementToRemove)); 162 Assert.IsFalse(container.Remove(elementToRemove)); 163 164 using (var enumerator = container.GetEnumerator()) 165 { 166 while (enumerator.MoveNext()) 167 { 168 Assert.AreNotEqual(elementToRemove, enumerator.Current.Key); 169 } 170 } 171 172 container.Dispose(); 173 } 174 175 [Test] 176 public void UnsafeHashMap_EnumeratorInfiniteIterationTest() 177 { 178 UnsafeHashMap<int, int> container = new UnsafeHashMap<int, int>(5, Allocator.Temp); 179 for (int i = 0; i < 5; i++) 180 { 181 container.Add(i, i); 182 } 183 184 for (int i = 0; i < 2; i++) 185 { 186 Assert.IsTrue(container.Remove(i)); 187 } 188 189 var expected = container.Count; 190 int count = 0; 191 using (var enumerator = container.GetEnumerator()) 192 { 193 while (enumerator.MoveNext()) 194 { 195 if (count++ > expected) 196 { 197 break; 198 } 199 } 200 } 201 202 Assert.AreEqual(expected, count); 203 container.Dispose(); 204 } 205 206 [Test] 207 public void UnsafeHashMap_CustomAllocatorTest() 208 { 209 AllocatorManager.Initialize(); 210 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent); 211 ref var allocator = ref allocatorHelper.Allocator; 212 allocator.Initialize(); 213 214 using (var container = new UnsafeHashMap<int, int>(1, allocator.Handle)) 215 { 216 } 217 218 Assert.IsTrue(allocator.WasUsed); 219 allocator.Dispose(); 220 allocatorHelper.Dispose(); 221 AllocatorManager.Shutdown(); 222 } 223 224 [BurstCompile] 225 struct BurstedCustomAllocatorJob : IJob 226 { 227 [NativeDisableUnsafePtrRestriction] 228 public unsafe CustomAllocatorTests.CountingAllocator* Allocator; 229 230 public void Execute() 231 { 232 unsafe 233 { 234 using (var container = new UnsafeHashMap<int, int>(1, Allocator->Handle)) 235 { 236 } 237 } 238 } 239 } 240 241 [Test] 242 public unsafe void UnsafeHashMap_BurstedCustomAllocatorTest() 243 { 244 AllocatorManager.Initialize(); 245 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent); 246 ref var allocator = ref allocatorHelper.Allocator; 247 allocator.Initialize(); 248 249 var allocatorPtr = (CustomAllocatorTests.CountingAllocator*)UnsafeUtility.AddressOf<CustomAllocatorTests.CountingAllocator>(ref allocator); 250 unsafe 251 { 252 var handle = new BurstedCustomAllocatorJob { Allocator = allocatorPtr }.Schedule(); 253 handle.Complete(); 254 } 255 256 Assert.IsTrue(allocator.WasUsed); 257 allocator.Dispose(); 258 allocatorHelper.Dispose(); 259 AllocatorManager.Shutdown(); 260 } 261 262 static void ExpectedCount<TKey, TValue>(ref UnsafeHashMap<TKey, TValue> container, int expected) 263 where TKey : unmanaged, IEquatable<TKey> 264 where TValue : unmanaged 265 { 266 Assert.AreEqual(expected == 0, container.IsEmpty); 267 Assert.AreEqual(expected, container.Count); 268 } 269 270 [Test] 271 public void UnsafeHashMap_TryAdd_TryGetValue_Clear() 272 { 273 var container = new UnsafeHashMap<int, int>(16, Allocator.Temp); 274 ExpectedCount(ref container, 0); 275 276 int iSquared; 277 // Make sure GetValue fails if container is empty 278 Assert.IsFalse(container.TryGetValue(0, out iSquared), "TryGetValue on empty container did not fail"); 279 280 // Make sure inserting values work 281 for (int i = 0; i < 16; ++i) 282 Assert.IsTrue(container.TryAdd(i, i * i), "Failed to add value"); 283 ExpectedCount(ref container, 16); 284 285 // Make sure inserting duplicate keys fails 286 for (int i = 0; i < 16; ++i) 287 Assert.IsFalse(container.TryAdd(i, i), "Adding duplicate keys did not fail"); 288 ExpectedCount(ref container, 16); 289 290 // Make sure reading the inserted values work 291 for (int i = 0; i < 16; ++i) 292 { 293 Assert.IsTrue(container.TryGetValue(i, out iSquared), "Failed get value from hash table"); 294 Assert.AreEqual(iSquared, i * i, "Got the wrong value from the hash table"); 295 } 296 297 // Make sure clearing removes all keys 298 container.Clear(); 299 ExpectedCount(ref container, 0); 300 301 for (int i = 0; i < 16; ++i) 302 Assert.IsFalse(container.TryGetValue(i, out iSquared), "Got value from hash table after clearing"); 303 304 container.Dispose(); 305 } 306 307 [Test] 308 public void UnsafeHashMap_Key_Collisions() 309 { 310 var container = new UnsafeHashMap<int, int>(16, Allocator.Temp); 311 int iSquared; 312 313 // Make sure GetValue fails if container is empty 314 Assert.IsFalse(container.TryGetValue(0, out iSquared), "TryGetValue on empty container did not fail"); 315 316 // Make sure inserting values work 317 for (int i = 0; i < 8; ++i) 318 { 319 Assert.IsTrue(container.TryAdd(i, i * i), "Failed to add value"); 320 } 321 322 // The bucket size is capacity * 2, adding that number should result in hash collisions 323 for (int i = 0; i < 8; ++i) 324 { 325 Assert.IsTrue(container.TryAdd(i + 32, i), "Failed to add value with potential hash collision"); 326 } 327 328 // Make sure reading the inserted values work 329 for (int i = 0; i < 8; ++i) 330 { 331 Assert.IsTrue(container.TryGetValue(i, out iSquared), "Failed get value from hash table"); 332 Assert.AreEqual(iSquared, i * i, "Got the wrong value from the hash table"); 333 } 334 335 for (int i = 0; i < 8; ++i) 336 { 337 Assert.IsTrue(container.TryGetValue(i + 32, out iSquared), "Failed get value from hash table"); 338 Assert.AreEqual(iSquared, i, "Got the wrong value from the hash table"); 339 } 340 341 container.Dispose(); 342 } 343 344 [Test] 345 public void UnsafeHashMap_SupportsAutomaticCapacityChange() 346 { 347 var container = new UnsafeHashMap<int, int>(1, Allocator.Temp); 348 int iSquared; 349 350 // Make sure inserting values work and grows the capacity 351 for (int i = 0; i < 8; ++i) 352 { 353 Assert.IsTrue(container.TryAdd(i, i * i), "Failed to add value"); 354 } 355 Assert.IsTrue(container.Capacity >= 8, "Capacity was not updated correctly"); 356 357 // Make sure reading the inserted values work 358 for (int i = 0; i < 8; ++i) 359 { 360 Assert.IsTrue(container.TryGetValue(i, out iSquared), "Failed get value from hash table"); 361 Assert.AreEqual(iSquared, i * i, "Got the wrong value from the hash table"); 362 } 363 364 container.Dispose(); 365 } 366 367 [Test] 368 [TestRequiresDotsDebugOrCollectionChecks] 369 public void UnsafeHashMap_SameKey() 370 { 371 using (var container = new UnsafeHashMap<int, int>(0, Allocator.Persistent)) 372 { 373 Assert.DoesNotThrow(() => container.Add(0, 0)); 374 Assert.Throws<ArgumentException>(() => container.Add(0, 0)); 375 } 376 377 using (var container = new UnsafeHashMap<int, int>(0, Allocator.Persistent)) 378 { 379 Assert.IsTrue(container.TryAdd(0, 0)); 380 Assert.IsFalse(container.TryAdd(0, 0)); 381 } 382 } 383 384 [Test] 385 public void UnsafeHashMap_IsEmpty() 386 { 387 var container = new UnsafeHashMap<int, int>(0, Allocator.Persistent); 388 Assert.IsTrue(container.IsEmpty); 389 390 container.TryAdd(0, 0); 391 Assert.IsFalse(container.IsEmpty); 392 ExpectedCount(ref container, 1); 393 394 Assert.IsTrue(container.Remove(0)); 395 Assert.IsFalse(container.Remove(0)); 396 Assert.IsTrue(container.IsEmpty); 397 398 container.TryAdd(0, 0); 399 container.Clear(); 400 Assert.IsTrue(container.IsEmpty); 401 402 container.Dispose(); 403 } 404 405 [Test] 406 public void UnsafeHashMap_EmptyCapacity() 407 { 408 var container = new UnsafeHashMap<int, int>(0, Allocator.Persistent); 409 container.TryAdd(0, 0); 410 ExpectedCount(ref container, 1); 411 container.Dispose(); 412 } 413 414 [Test] 415 public void UnsafeHashMap_Remove() 416 { 417 var container = new UnsafeHashMap<int, int>(8, Allocator.Temp); 418 int iSquared; 419 420 for (int rm = 0; rm < 8; ++rm) 421 { 422 Assert.IsFalse(container.Remove(rm)); 423 } 424 425 // Make sure inserting values work 426 for (int i = 0; i < 8; ++i) 427 { 428 Assert.IsTrue(container.TryAdd(i, i * i), "Failed to add value"); 429 } 430 Assert.AreEqual(8, container.Count); 431 432 // Make sure reading the inserted values work 433 for (int i = 0; i < 8; ++i) 434 { 435 Assert.IsTrue(container.TryGetValue(i, out iSquared), "Failed get value from hash table"); 436 Assert.AreEqual(iSquared, i * i, "Got the wrong value from the hash table"); 437 } 438 439 for (int rm = 0; rm < 8; ++rm) 440 { 441 Assert.IsTrue(container.Remove(rm)); 442 Assert.IsFalse(container.TryGetValue(rm, out iSquared), "Failed to remove value from hash table"); 443 for (int i = rm + 1; i < 8; ++i) 444 { 445 Assert.IsTrue(container.TryGetValue(i, out iSquared), "Failed get value from hash table"); 446 Assert.AreEqual(iSquared, i * i, "Got the wrong value from the hash table"); 447 } 448 } 449 Assert.AreEqual(0, container.Count); 450 451 // Make sure entries were freed 452 for (int i = 0; i < 8; ++i) 453 { 454 Assert.IsTrue(container.TryAdd(i, i * i), "Failed to add value"); 455 } 456 457 Assert.AreEqual(8, container.Count); 458 container.Dispose(); 459 } 460 461 [Test] 462 public void UnsafeHashMap_RemoveOnEmptyMap_DoesNotThrow() 463 { 464 var container = new UnsafeHashMap<int, int>(0, Allocator.Temp); 465 Assert.DoesNotThrow(() => container.Remove(0)); 466 Assert.DoesNotThrow(() => container.Remove(-425196)); 467 container.Dispose(); 468 } 469 470 [Test] 471 public void UnsafeHashMap_TryAddScalability() 472 { 473 var container = new UnsafeHashMap<int, int>(1, Allocator.Persistent); 474 Assert.AreEqual(container.Capacity, (1 << container.m_Data.Log2MinGrowth)); 475 for (int i = 0; i != 1000 * 100; i++) 476 { 477 container.Add(i, i); 478 } 479 480 int value; 481 Assert.AreEqual(container.Count, 100000); 482 Assert.IsFalse(container.TryGetValue(-1, out value)); 483 Assert.IsFalse(container.TryGetValue(1000 * 1000, out value)); 484 485 for (int i = 0; i != 1000 * 100; i++) 486 { 487 Assert.IsTrue(container.TryGetValue(i, out value)); 488 Assert.AreEqual(i, value); 489 } 490 491 container.Dispose(); 492 } 493 494 [Test] 495 public unsafe void UnsafeHashMap_TrimExcess() 496 { 497 using (var container = new UnsafeHashMap<int, int>(1024, Allocator.Persistent)) 498 { 499 var oldCapacity = container.Capacity; 500 501 container.Add(123, 345); 502 container.TrimExcess(); 503 Assert.AreEqual(container.Capacity, (1 << container.m_Data.Log2MinGrowth)); 504 Assert.AreEqual(1, container.Count); 505 Assert.AreNotEqual(oldCapacity, container.Capacity); 506 507 oldCapacity = container.Capacity; 508 509 container.Remove(123); 510 Assert.AreEqual(container.Count, 0); 511 container.TrimExcess(); 512 Assert.AreEqual(oldCapacity, container.Capacity); 513 514 container.Add(123, 345); 515 Assert.AreEqual(container.Count, 1); 516 Assert.AreEqual(oldCapacity, container.Capacity); 517 518 container.Clear(); 519 Assert.AreEqual(container.Count, 0); 520 Assert.AreEqual(oldCapacity, container.Capacity); 521 } 522 } 523 524 [Test] 525 public void UnsafeHashMap_IndexerAdd_ResizesContainer() 526 { 527 var container = new UnsafeHashMap<int, int>(8, Allocator.Persistent); 528 for (int i = 0; i < 1024; i++) 529 { 530 container[i] = i; 531 } 532 Assert.AreEqual(1024, container.Count); 533 container.Dispose(); 534 } 535}