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}