A game about forced loneliness, made by TACStudios
1using System; 2using NUnit.Framework; 3using Unity.Collections; 4using Unity.Collections.LowLevel.Unsafe; 5using System.Text; 6using Unity.Burst; 7using Unity.Jobs; 8 9namespace Unity.Collections.Tests 10{ 11 internal class UnsafeTextTests 12 { 13 void AssertAreEqualInTest(string expected, in UnsafeText actual) 14 { 15 var actualString = actual.ToString(); 16 Assert.AreEqual(expected, actualString); 17 } 18 19 // NOTE: If you call this function from Mono and T is not marshalable - your app (Editor or the player built with Mono scripting backend) could/will crash. 20 bool IsMarshalable<T>() where T : unmanaged 21 { 22 try 23 { 24 unsafe 25 { 26 var size = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)); 27 IntPtr memoryIntPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(size); 28 try 29 { 30 var obj = new T(); 31 System.Runtime.InteropServices.Marshal.StructureToPtr(obj, memoryIntPtr, false); 32 System.Runtime.InteropServices.Marshal.DestroyStructure<T>(memoryIntPtr); 33 } 34 finally 35 { 36 System.Runtime.InteropServices.Marshal.FreeHGlobal(memoryIntPtr); 37 } 38 39 return true; 40 } 41 } 42 catch (Exception e) 43 { 44 UnityEngine.Debug.LogError("ERROR in IsMarshalable<" + typeof(T).FullName + "> " + e); 45 return false; 46 } 47 } 48 49 [Test] 50 public void UnsafeTextIsMarshalable() 51 { 52 var result = IsMarshalable<UnsafeText>(); 53 Assert.IsTrue(result); 54 } 55 56 [Test] 57 public unsafe void UnsafeTextCorrectBinaryHeader() 58 { 59 var text = new UnsafeText(42, Allocator.Persistent); 60 var ptr = text.GetUnsafePtr(); 61 62 Assert.AreEqual(0 + 1, text.m_UntypedListData.m_length); 63 Assert.AreEqual(Allocator.Persistent, text.m_UntypedListData.Allocator.ToAllocator); 64 Assert.IsTrue(ptr == text.m_UntypedListData.Ptr, "ptr != text.m_UntypedListData.Ptr"); 65 66 var listOfBytesCast = text.AsUnsafeListOfBytes(); 67 68 Assert.AreEqual(0 + 1, listOfBytesCast.Length); 69 Assert.AreEqual(Allocator.Persistent, listOfBytesCast.Allocator.ToAllocator); 70 Assert.IsTrue(ptr == listOfBytesCast.Ptr, "ptr != listOfBytesCast.Ptr"); 71 72 Assert.AreEqual(text.m_UntypedListData.m_capacity, listOfBytesCast.Capacity); 73 74 text.Dispose(); 75 } 76 77 [Test] 78 public void UnsafeTextCorrectLengthAfterClear() 79 { 80 UnsafeText aa = new UnsafeText(4, Allocator.Temp); 81 Assert.True(aa.IsCreated); 82 Assert.AreEqual(0, aa.Length, "Length after creation is not 0"); 83 aa.AssertNullTerminated(); 84 85 aa.Junk(); 86 87 aa.Clear(); 88 Assert.AreEqual(0, aa.Length, "Length after clear is not 0"); 89 aa.AssertNullTerminated(); 90 91 aa.Dispose(); 92 } 93 94 [Test] 95 public void UnsafeTextFormatExtension1Params() 96 { 97 UnsafeText aa = new UnsafeText(4, Allocator.Temp); 98 Assert.True(aa.IsCreated); 99 aa.Junk(); 100 FixedString32Bytes format = "{0}"; 101 FixedString32Bytes arg0 = "a"; 102 aa.AppendFormat(format, arg0); 103 aa.Append('a'); 104 aa.AssertNullTerminated(); 105 AssertAreEqualInTest("aa", aa); 106 aa.Dispose(); 107 } 108 109 110 [Test] 111 public void UnsafeTextFormatExtension2Params() 112 { 113 UnsafeText aa = new UnsafeText(4, Allocator.Temp); 114 aa.Junk(); 115 FixedString32Bytes format = "{0} {1}"; 116 FixedString32Bytes arg0 = "a"; 117 FixedString32Bytes arg1 = "b"; 118 aa.AppendFormat(format, arg0, arg1); 119 AssertAreEqualInTest("a b", aa); 120 aa.AssertNullTerminated(); 121 aa.Dispose(); 122 } 123 124 125 [Test] 126 public void UnsafeTextFormatExtension3Params() 127 { 128 UnsafeText aa = new UnsafeText(4, Allocator.Temp); 129 aa.Junk(); 130 FixedString32Bytes format = "{0} {1} {2}"; 131 FixedString32Bytes arg0 = "a"; 132 FixedString32Bytes arg1 = "b"; 133 FixedString32Bytes arg2 = "c"; 134 aa.AppendFormat(format, arg0, arg1, arg2); 135 AssertAreEqualInTest("a b c", aa); 136 aa.AssertNullTerminated(); 137 aa.Dispose(); 138 } 139 140 141 [Test] 142 public void UnsafeTextFormatExtension4Params() 143 { 144 UnsafeText aa = new UnsafeText(512, Allocator.Temp); 145 aa.Junk(); 146 FixedString32Bytes format = "{0} {1} {2} {3}"; 147 FixedString32Bytes arg0 = "a"; 148 FixedString32Bytes arg1 = "b"; 149 FixedString32Bytes arg2 = "c"; 150 FixedString32Bytes arg3 = "d"; 151 aa.AppendFormat(format, arg0, arg1, arg2, arg3); 152 AssertAreEqualInTest("a b c d", aa); 153 aa.AssertNullTerminated(); 154 aa.Dispose(); 155 } 156 157 158 [Test] 159 public void UnsafeTextFormatExtension5Params() 160 { 161 UnsafeText aa = new UnsafeText(4, Allocator.Temp); 162 aa.Junk(); 163 FixedString32Bytes format = "{0} {1} {2} {3} {4}"; 164 FixedString32Bytes arg0 = "a"; 165 FixedString32Bytes arg1 = "b"; 166 FixedString32Bytes arg2 = "c"; 167 FixedString32Bytes arg3 = "d"; 168 FixedString32Bytes arg4 = "e"; 169 aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4); 170 AssertAreEqualInTest("a b c d e", aa); 171 aa.AssertNullTerminated(); 172 aa.Dispose(); 173 } 174 175 176 [Test] 177 public void UnsafeTextFormatExtension6Params() 178 { 179 UnsafeText aa = new UnsafeText(4, Allocator.Temp); 180 aa.Junk(); 181 FixedString32Bytes format = "{0} {1} {2} {3} {4} {5}"; 182 FixedString32Bytes arg0 = "a"; 183 FixedString32Bytes arg1 = "b"; 184 FixedString32Bytes arg2 = "c"; 185 FixedString32Bytes arg3 = "d"; 186 FixedString32Bytes arg4 = "e"; 187 FixedString32Bytes arg5 = "f"; 188 aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5); 189 AssertAreEqualInTest("a b c d e f", aa); 190 aa.AssertNullTerminated(); 191 aa.Dispose(); 192 } 193 194 195 [Test] 196 public void UnsafeTextFormatExtension7Params() 197 { 198 UnsafeText aa = new UnsafeText(4, Allocator.Temp); 199 aa.Junk(); 200 FixedString32Bytes format = "{0} {1} {2} {3} {4} {5} {6}"; 201 FixedString32Bytes arg0 = "a"; 202 FixedString32Bytes arg1 = "b"; 203 FixedString32Bytes arg2 = "c"; 204 FixedString32Bytes arg3 = "d"; 205 FixedString32Bytes arg4 = "e"; 206 FixedString32Bytes arg5 = "f"; 207 FixedString32Bytes arg6 = "g"; 208 aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5, arg6); 209 AssertAreEqualInTest("a b c d e f g", aa); 210 aa.AssertNullTerminated(); 211 aa.Dispose(); 212 } 213 214 215 [Test] 216 public void UnsafeTextFormatExtension8Params() 217 { 218 UnsafeText aa = new UnsafeText(4, Allocator.Temp); 219 aa.Junk(); 220 FixedString128Bytes format = "{0} {1} {2} {3} {4} {5} {6} {7}"; 221 FixedString32Bytes arg0 = "a"; 222 FixedString32Bytes arg1 = "b"; 223 FixedString32Bytes arg2 = "c"; 224 FixedString32Bytes arg3 = "d"; 225 FixedString32Bytes arg4 = "e"; 226 FixedString32Bytes arg5 = "f"; 227 FixedString32Bytes arg6 = "g"; 228 FixedString32Bytes arg7 = "h"; 229 aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); 230 AssertAreEqualInTest("a b c d e f g h", aa); 231 aa.AssertNullTerminated(); 232 aa.Dispose(); 233 } 234 235 236 [Test] 237 public void UnsafeTextFormatExtension9Params() 238 { 239 UnsafeText aa = new UnsafeText(4, Allocator.Temp); 240 aa.Junk(); 241 FixedString128Bytes format = "{0} {1} {2} {3} {4} {5} {6} {7} {8}"; 242 FixedString32Bytes arg0 = "a"; 243 FixedString32Bytes arg1 = "b"; 244 FixedString32Bytes arg2 = "c"; 245 FixedString32Bytes arg3 = "d"; 246 FixedString32Bytes arg4 = "e"; 247 FixedString32Bytes arg5 = "f"; 248 FixedString32Bytes arg6 = "g"; 249 FixedString32Bytes arg7 = "h"; 250 FixedString32Bytes arg8 = "i"; 251 aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); 252 AssertAreEqualInTest("a b c d e f g h i", aa); 253 aa.AssertNullTerminated(); 254 aa.Dispose(); 255 } 256 257 258 [Test] 259 public void UnsafeTextFormatExtension10Params() 260 { 261 UnsafeText aa = new UnsafeText(4, Allocator.Temp); 262 aa.Junk(); 263 FixedString128Bytes format = "{0} {1} {2} {3} {4} {5} {6} {7} {8} {9}"; 264 FixedString32Bytes arg0 = "a"; 265 FixedString32Bytes arg1 = "b"; 266 FixedString32Bytes arg2 = "c"; 267 FixedString32Bytes arg3 = "d"; 268 FixedString32Bytes arg4 = "e"; 269 FixedString32Bytes arg5 = "f"; 270 FixedString32Bytes arg6 = "g"; 271 FixedString32Bytes arg7 = "h"; 272 FixedString32Bytes arg8 = "i"; 273 FixedString32Bytes arg9 = "j"; 274 aa.AppendFormat(format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); 275 AssertAreEqualInTest("a b c d e f g h i j", aa); 276 aa.AssertNullTerminated(); 277 aa.Dispose(); 278 } 279 280 [Test] 281 public void UnsafeTextAppendGrows() 282 { 283 UnsafeText aa = new UnsafeText(1, Allocator.Temp); 284 var origCapacity = aa.Capacity; 285 for (int i = 0; i < origCapacity; ++i) 286 aa.Append('a'); 287 Assert.AreEqual(origCapacity, aa.Capacity); 288 aa.Append('b'); 289 Assert.GreaterOrEqual(aa.Capacity, origCapacity); 290 Assert.AreEqual(new String('a', origCapacity) + "b", aa.ToString()); 291 aa.Dispose(); 292 } 293 294 [Test] 295 public void UnsafeTextAppendString() 296 { 297 UnsafeText aa = new UnsafeText(4, Allocator.Temp); 298 aa.Append("aa"); 299 Assert.AreEqual("aa", aa.ToString()); 300 aa.Append("bb"); 301 Assert.AreEqual("aabb", aa.ToString()); 302 aa.Dispose(); 303 } 304 305 306 [TestCase("Antidisestablishmentarianism")] 307 [TestCase("⁣🌹🌻🌷🌿🌵🌾⁣")] 308 public void UnsafeTextCopyFromBytesWorks(String a) 309 { 310 UnsafeText aa = new UnsafeText(4, Allocator.Temp); 311 aa.Junk(); 312 var utf8 = Encoding.UTF8.GetBytes(a); 313 unsafe 314 { 315 fixed (byte* b = utf8) 316 aa.Append(b, (ushort) utf8.Length); 317 } 318 319 Assert.AreEqual(a, aa.ToString()); 320 aa.AssertNullTerminated(); 321 322 aa.Append("tail"); 323 Assert.AreEqual(a + "tail", aa.ToString()); 324 aa.AssertNullTerminated(); 325 326 aa.Dispose(); 327 } 328 329 [TestCase("red")] 330 [TestCase("紅色", TestName = "{m}(Chinese-Red)")] 331 [TestCase("George Washington")] 332 [TestCase("村上春樹", TestName = "{m}(HarukiMurakami)")] 333 public void UnsafeTextToStringWorks(String a) 334 { 335 UnsafeText aa = new UnsafeText(4, Allocator.Temp); 336 aa.Append(new FixedString128Bytes(a)); 337 Assert.AreEqual(a, aa.ToString()); 338 aa.AssertNullTerminated(); 339 aa.Dispose(); 340 } 341 342 [Test] 343 public void UnsafeTextIndexOf() 344 { 345 UnsafeText a = new UnsafeText(16, Allocator.Temp); 346 a.Append((FixedString64Bytes) "bookkeeper bookkeeper"); 347 UnsafeText b = new UnsafeText(8, Allocator.Temp); 348 b.Append((FixedString32Bytes) "ookkee"); 349 350 Assert.AreEqual(1, a.IndexOf(b)); 351 Assert.AreEqual(-1, b.IndexOf(a)); 352 a.Dispose(); 353 b.Dispose(); 354 } 355 356 [Test] 357 public void UnsafeTextLastIndexOf() 358 { 359 UnsafeText a = new UnsafeText(16, Allocator.Temp); 360 a.Append((FixedString64Bytes) "bookkeeper bookkeeper"); 361 UnsafeText b = new UnsafeText(8, Allocator.Temp); 362 b.Append((FixedString32Bytes) "ookkee"); 363 364 Assert.AreEqual(12, a.LastIndexOf(b)); 365 Assert.AreEqual(-1, b.LastIndexOf(a)); 366 a.Dispose(); 367 b.Dispose(); 368 } 369 370 [Test] 371 public void UnsafeTextContains() 372 { 373 UnsafeText a = new UnsafeText(16, Allocator.Temp); 374 a.Append((FixedString64Bytes) "bookkeeper bookkeeper"); 375 UnsafeText b = new UnsafeText(8, Allocator.Temp); 376 b.Append((FixedString32Bytes) "ookkee"); 377 378 Assert.AreEqual(true, a.Contains(b)); 379 a.Dispose(); 380 b.Dispose(); 381 } 382 383 [Test] 384 public void UnsafeTextComparisons() 385 { 386 UnsafeText a = new UnsafeText(16, Allocator.Temp); 387 a.Append((FixedString64Bytes) "apple"); 388 UnsafeText b = new UnsafeText(8, Allocator.Temp); 389 b.Append((FixedString32Bytes) "banana"); 390 391 Assert.AreEqual(false, a.Equals(b)); 392 Assert.AreEqual(true, !b.Equals(a)); 393 a.Dispose(); 394 b.Dispose(); 395 } 396 397 [Test] 398 public void UnsafeText_CustomAllocatorTest() 399 { 400 AllocatorManager.Initialize(); 401 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent); 402 ref var allocator = ref allocatorHelper.Allocator; 403 allocator.Initialize(); 404 405 using (var container = new UnsafeText(1, allocator.Handle)) 406 { 407 } 408 409 Assert.IsTrue(allocator.WasUsed); 410 allocator.Dispose(); 411 allocatorHelper.Dispose(); 412 AllocatorManager.Shutdown(); 413 } 414 415 [BurstCompile] 416 struct BurstedCustomAllocatorJob : IJob 417 { 418 [NativeDisableUnsafePtrRestriction] 419 public unsafe CustomAllocatorTests.CountingAllocator* Allocator; 420 421 public void Execute() 422 { 423 unsafe 424 { 425 using (var container = new UnsafeText(1, Allocator->Handle)) 426 { 427 } 428 } 429 } 430 } 431 432 [Test] 433 public unsafe void UnsafeText_BurstedCustomAllocatorTest() 434 { 435 AllocatorManager.Initialize(); 436 var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent); 437 ref var allocator = ref allocatorHelper.Allocator; 438 allocator.Initialize(); 439 440 var allocatorPtr = (CustomAllocatorTests.CountingAllocator*)UnsafeUtility.AddressOf<CustomAllocatorTests.CountingAllocator>(ref allocator); 441 unsafe 442 { 443 var handle = new BurstedCustomAllocatorJob {Allocator = allocatorPtr}.Schedule(); 444 handle.Complete(); 445 } 446 447 Assert.IsTrue(allocator.WasUsed); 448 allocator.Dispose(); 449 allocatorHelper.Dispose(); 450 AllocatorManager.Shutdown(); 451 } 452 453 [TestCase("red", 'r', 'd')] 454 [TestCase("紅色", '紅', '色')] 455 [TestCase("црвена", 'ц', 'а')] 456 [TestCase("George Washington", 'G', 'n')] 457 [TestCase("村上春樹", '村', '樹')] 458 [TestCase("로마는 하루아침에 이루어진 것이 아니다", '로', '다')] 459 [TestCase("Лако ти је плитку воду замутити и будалу наљутити", 'Л', 'и')] 460 [TestCase("Үнэн үг хэлсэн хүнд ноёд өстэй, үхэр унасан хүнд ноход өстэй.", 'Ү', '.')] 461 public void UnsafeText_StartsEndsWithChar(String a, char starts, char ends) 462 { 463 UnsafeText actual = new UnsafeText(16, Allocator.Temp); 464 actual.Append(a); 465 Assert.True(actual.StartsWith(starts)); 466 Assert.True(actual.EndsWith(ends)); 467 } 468 469 [TestCase("red", "r", "d")] 470 [TestCase("紅色", "紅", "色")] 471 [TestCase("црвена", "црв", "ена")] 472 [TestCase("George Washington", "George", "Washington")] 473 [TestCase("村上春樹", "村上", "春樹")] 474 [TestCase("🌕🌖🌗🌘🌑🌒🌓🌔", "🌕🌖🌗", "🌒🌓🌔")] 475 [TestCase("𝒞𝒯𝒮𝒟𝒳𝒩𝒫𝒢", "𝒞𝒯𝒮", "𝒩𝒫𝒢")] 476 [TestCase("로마는 하루아침에 이루어진 것이 아니다", "로마는", "아니다")] 477 [TestCase("Лако ти је плитку воду замутити и будалу наљутити", "Лако", "наљутити")] 478 [TestCase("Үнэн үг хэлсэн хүнд ноёд өстэй, үхэр унасан хүнд ноход өстэй.", "Үнэн", "өстэй.")] 479 public void UnsafeText_StartsEndsWithString(String a, String starts, String ends) 480 { 481 UnsafeText actual = new UnsafeText(16, Allocator.Temp); 482 actual.Append(a); 483 484 Assert.True(actual.StartsWith((FixedString64Bytes)starts)); 485 Assert.True(actual.EndsWith((FixedString64Bytes)ends)); 486 } 487 488 [TestCase("red ", ' ', "red ", "red", "red")] 489 [TestCase(" red ", ' ', "red ", " red", "red")] 490 [TestCase(" ", ' ', "", "", "")] 491 public void UnsafeText_TrimStart(String a, char trim, String expectedStart, String expectedEnd, String expected) 492 { 493 UnsafeText actual = new UnsafeText(16, Allocator.Temp); 494 actual.Append(a); 495 496 Assert.AreEqual(expectedStart, actual.TrimStart(Allocator.Temp).ToString()); 497 Assert.AreEqual(expectedEnd, actual.TrimEnd(Allocator.Temp).ToString()); 498 Assert.AreEqual(expected, actual.Trim(Allocator.Temp).ToString()); 499 } 500 501 [TestCase(" red ", "ed ", " red", "ed")] 502 [TestCase("црвена", "црвена", "црвена", "црвена")] 503 [TestCase(" ", "", "", "")] 504 public void UnsafeText_TrimStartWithRunes(String a, String expectedStart, String expectedEnd, String expected) 505 { 506 UnsafeText actual = new UnsafeText(16, Allocator.Temp); 507 actual.Append(a); 508 509 Assert.AreEqual(expectedStart, actual.TrimStart(Allocator.Temp, new Unicode.Rune[] { ' ', 'r' }).ToString()); 510 Assert.AreEqual(expectedEnd, actual.TrimEnd(Allocator.Temp, new Unicode.Rune[] { ' ', 'r' }).ToString()); 511 Assert.AreEqual(expected, actual.Trim(Allocator.Temp, new Unicode.Rune[] { ' ', 'r' }).ToString()); 512 } 513 514 [TestCase("Red", "red", "RED")] 515 [TestCase("црвена", "црвена", "црвена")] 516 [TestCase(" ", " ", " ")] 517 public void UnsafeText_ToLowerUpperAscii(String a, String expectedLower, String expectedUpped) 518 { 519 UnsafeText actual = new UnsafeText(16, Allocator.Temp); 520 actual.Append(a); 521 522 Assert.AreEqual(expectedLower, actual.ToLowerAscii(Allocator.Temp).ToString()); 523 Assert.AreEqual(expectedUpped, actual.ToUpperAscii(Allocator.Temp).ToString()); 524 } 525 } 526}