1 module memutils.tests; 2 import memutils.all; 3 import core.thread : Fiber; 4 import std.conv : to; 5 6 version(MemutilsTests): 7 static if (!SkipUnitTests && !DisableDebugAllocations): 8 9 // Test hashmap, freelists 10 void hashmapFreeListTest(ALLOC)() { 11 assert(getAllocator!(ALLOC.ident)().bytesAllocated() == 0); 12 { 13 import std.stdio; 14 HashMapRef!(string, string, ALLOC) hm; 15 hm["hey"] = "you"; 16 assert(getAllocator!(ALLOC.ident)().bytesAllocated() > 0); 17 void hello(HashMapRef!(string, string, ALLOC) map) { 18 assert(map["hey"] == "you"); 19 map["you"] = "hey"; 20 } 21 hello(hm); 22 assert(hm["you"] == "hey"); 23 hm.clear(); 24 assert(hm.empty); 25 } 26 assert(getAllocator!(ALLOC.ident)().bytesAllocated() == 0); 27 28 } 29 30 // Test Vector, FreeLists & Array 31 void vectorArrayTest(ALLOC)() { 32 { 33 assert(getAllocator!(ALLOC.ident)().bytesAllocated() == 0); 34 Vector!(ubyte, ALLOC) data; 35 data ~= "Hello there"; 36 37 assert(getAllocator!(ALLOC.ident)().bytesAllocated() > 0); 38 assert(data[] == "Hello there"); 39 40 Vector!(Array!(ubyte, ALLOC), ALLOC) arr; 41 arr ~= data.cloneToRef; 42 assert(arr[0] == data && arr[0][] == "Hello there"); 43 assert(arr[0] == data); 44 assert(arr[0][] == "Hello there"); 45 { 46 Vector!(ubyte, ALLOC) outbuf_; 47 ubyte[] reference; 48 int i; 49 for (i = 0; i < 16; i++) { 50 string abc = "abcdefghijklmnop"; 51 outbuf_ ~= cast(ubyte[])abc; 52 reference ~= cast(ubyte[]) abc; 53 54 assert(outbuf_[] == reference, "realloc error"); 55 } 56 } 57 Array!ubyte def_buf; 58 def_buf.reserve(8); 59 } 60 scope(failure) getAllocator!(ALLOC.ident)().printMap(); 61 assert(getAllocator!(ALLOC.ident)().bytesAllocated() == 0, "we got " ~ getAllocator!(ALLOC.ident)().bytesAllocated().to!string ~ " bytes, expected 0"); 62 } 63 64 // Test HashMap, FreeLists & Array 65 void hashmapComplexTest(ALLOC)() { 66 assert(getAllocator!(ALLOC.ident)().bytesAllocated() == 0); 67 { 68 HashMap!(string, Array!dchar, ALLOC) hm; 69 hm["hey"] = array("you"d); 70 hm["hello"] = hm["hey"]; 71 assert(*hm["hello"] is *hm["hey"]); 72 hm["hello"] = hm["hey"].cloneToRef; 73 assert(*hm["hello"] !is *hm["hey"]); 74 auto vec = hm["hey"].clone; 75 assert(vec[] == hm["hey"][]); 76 77 78 assert(!__traits(compiles, { void handler(HashMap!(string, Array!dchar, ALLOC) hm) { } handler(hm); })); 79 } 80 81 assert(getAllocator!(ALLOC.ident)().bytesAllocated() == 0); 82 } 83 84 // Test RBTree 85 void rbTreeTest(ALLOC)() { 86 assert(getAllocator!(ALLOC.ident)().bytesAllocated() == 0); 87 { 88 RBTree!(int, "a < b", true, ALLOC) rbtree; 89 90 rbtree.insert( [50, 51, 52, 53, 54] ); 91 auto vec = rbtree.lowerBoundRange(52).vector(); 92 assert(vec[] == [50, 51]); 93 } 94 assert(getAllocator!(ALLOC.ident)().bytesAllocated() == 0); 95 } 96 97 // Test Unique 98 void uniqueTest(ALLOC)() { 99 assert(getAllocator!(ALLOC.ident)().bytesAllocated() == 0); 100 { 101 class A { int a; } 102 Unique!(A, ALLOC) a; 103 auto inst = ObjectAllocator!(A, ALLOC).alloc(); 104 A a_check = inst; 105 inst.a = 10; 106 auto bytes = getAllocator!(ALLOC.ident)().bytesAllocated(); 107 assert(bytes > 0); 108 a = inst; 109 assert(!inst); 110 assert(a.a == 10); 111 a.free(); 112 } 113 assert(getAllocator!(ALLOC.ident)().bytesAllocated() == 0); 114 } 115 116 // Test FreeList casting 117 void refCountedCastTest(ALLOC)() { 118 class A { 119 this() { a=0; } 120 protected int a; 121 protected void incr() { 122 a += 1; 123 } 124 public final int get() { 125 return a; 126 } 127 } 128 class B : A { 129 int c; 130 int d; 131 long e; 132 override protected void incr() { 133 a += 3; 134 } 135 } 136 alias ARef = RefCounted!(A, ALLOC); 137 alias BRef = RefCounted!(B, ALLOC); 138 139 assert(getAllocator!(ALLOC.ident)().bytesAllocated() == 0); 140 { 141 { 142 ARef a = ARef(); 143 a.incr(); 144 assert(a.get() == 1); 145 destroy(a); /// destruction test 146 assert(!a); 147 } 148 { 149 ARef a; 150 assert(getAllocator!(ALLOC.ident)().bytesAllocated() == 0); 151 152 { /// cast test 153 BRef b = BRef(); 154 a = cast(ARef) b; 155 static void doIncr(ARef a_ref) { a_ref.incr(); } 156 doIncr(a); 157 assert(a.get() == 3, "Got: " ~ a.get().to!string); 158 } 159 ARef c = a; 160 assert(c.get() == 3); 161 destroy(c); 162 assert(a); 163 } 164 } 165 // The B object allocates a lot more. If A destructor called B's dtor we get 0 here. 166 assert(getAllocator!(ALLOC.ident)().bytesAllocated() == 0); 167 } 168 169 /// test Circular buffer 170 void circularBufferTest(ALLOC)() { 171 auto buf1 = CircularBuffer!(ubyte, 0, ALLOC)(65536); 172 ubyte[] data = new ubyte[150]; 173 data[50] = 'b'; 174 buf1.put(data); 175 assert(buf1.length == 150); 176 assert(buf1[50] == 'b'); 177 178 // pulled from vibe.d - vibe.utils.array 179 auto buf = CircularBuffer!(int, 0, ALLOC)(5); 180 assert(buf.length == 0 && buf.freeSpace == 5); buf.put(1); // |1 . . . . 181 assert(buf.length == 1 && buf.freeSpace == 4); buf.put(2); // |1 2 . . . 182 assert(buf.length == 2 && buf.freeSpace == 3); buf.put(3); // |1 2 3 . . 183 assert(buf.length == 3 && buf.freeSpace == 2); buf.put(4); // |1 2 3 4 . 184 assert(buf.length == 4 && buf.freeSpace == 1); buf.put(5); // |1 2 3 4 5 185 assert(buf.length == 5 && buf.freeSpace == 0); 186 assert(buf.front == 1); 187 buf.popFront(); // .|2 3 4 5 188 assert(buf.front == 2); 189 buf.popFrontN(2); // . . .|4 5 190 assert(buf.front == 4); 191 assert(buf.length == 2 && buf.freeSpace == 3); 192 buf.put([6, 7, 8]); // 6 7 8|4 5 193 assert(buf.length == 5 && buf.freeSpace == 0); 194 int[5] dst; 195 buf.read(dst); // . . .|. . 196 assert(dst == [4, 5, 6, 7, 8]); 197 assert(buf.length == 0 && buf.freeSpace == 5); 198 buf.put([1, 2]); // . . .|1 2 199 assert(buf.length == 2 && buf.freeSpace == 3); 200 buf.read(dst[0 .. 2]); //|. . . . . 201 assert(dst[0 .. 2] == [1, 2]); 202 } 203 204 void dictionaryListTest(ALLOC)() 205 { 206 DictionaryList!(string, int, ALLOC) a; 207 a.insert("a", 1); 208 a.insert("a", 2); 209 assert(a["a"] == 1); 210 assert(a.getValuesAt("a") == [1, 2]); 211 //logTrace("Done getValuesAt"); 212 a["a"] = 3; 213 assert(a["a"] == 3); 214 assert(a.getValuesAt("a") == [3, 2]); 215 a.removeAll("a"); 216 assert(a.getValuesAt("a").length == 0); 217 assert(a.get("a", 4) == 4); 218 a.insert("b", 2); 219 a.insert("b", 1); 220 a.remove("b"); 221 assert(a.getValuesAt("b") == [1]); 222 223 DictionaryList!(string, int, ALLOC, false) b; 224 b.insert("a", 1); 225 b.insert("A", 2); 226 assert(b["A"] == 1); 227 assert(b.getValuesAt("a") == [1, 2]); 228 229 foreach (int i; 0 .. 15_000) { 230 b.insert("a", i); 231 } 232 233 // TODO: Fix case insensitive comparison on x86 234 assert(b.getValuesAt("a").length >= 15_001, "Found " ~ b.getValuesAt("a").length.to!string); 235 236 } 237 238 void propagateTests(alias fct)() { 239 logDebug("Testing ", fct.stringof); 240 fct!AppMem(); 241 fct!SecureMem(); 242 fct!ThreadMem(); 243 } 244 245 void highLevelAllocTest() { 246 logDebug("Testing High Level Allocators"); 247 class A { 248 int a; 249 250 ~this() { 251 a = 0; 252 } 253 } 254 A a = ThreadMem.alloc!A(); 255 a.a = 10; 256 ThreadMem.free(a); 257 assert(!a); 258 259 A appAllocated() { 260 A c = AppMem.alloc!A(); 261 c.a = 10; 262 return c; 263 } 264 265 assert(appAllocated().a == 10); 266 267 ubyte[] ub = ThreadMem.alloc!(ubyte[])(150); 268 269 assert(ub.length == 150); 270 ub[50] = 'a'; 271 ThreadMem.free(ub); 272 assert(ub is null); 273 } 274 struct A { 275 int a; 276 277 ~this() { 278 //logDebug("Dtor called"); 279 a = 0; 280 } 281 } 282 283 void scopedTest() { 284 285 286 logDebug("Testing ScopedPool"); 287 288 A* num; 289 { 290 PoolStack.push(); 291 num = alloc!A(0); 292 num.a = 2; 293 //logDebug("Freezing"); 294 PoolStack.disable(); PoolStack.enable(); 295 PoolStack.freeze(1); 296 //logDebug("Frozen"); 297 assert(PoolStack.empty, "Stack is not empty"); 298 PoolStack.unfreeze(1); PoolStack.pop(); 299 assert(num.a == 0, "Dtor not called"); 300 } 301 { 302 auto pool1 = ScopedPool(); 303 num = alloc!A(0); 304 } 305 306 307 Fiber f; 308 f = new Fiber(delegate { auto pool = ScopedPool(); pool.freeze(); pool.unfreeze(); PoolStack.disable(); PoolStack.enable(); }); 309 f.call(); 310 } 311 312 alias StringObjRef = RefCounted!StringObj; 313 final class StringObj 314 { 315 void check_value(ref StringObjRef str_obj) { 316 assert(str_obj.m_str == m_str); 317 } 318 this(string a = "") { 319 m_str = a; 320 } 321 string m_str; 322 } 323 324 void stringObjRefTest(ALLOC)() { 325 StringObjRef str_ref = StringObjRef(); 326 StringObjRef str_ref2 = StringObjRef("abc"); 327 str_ref2 = str_ref; 328 str_ref.check_value(str_ref2); 329 } 330 void rbTreeTestTwo(ALLOC)() { 331 auto m_trusted_hashes = RBTree!string(); 332 333 334 m_trusted_hashes.insert("SHA-224"); 335 m_trusted_hashes.insert("SHA-256"); 336 m_trusted_hashes.insert("SHA-384"); 337 m_trusted_hashes.insert("SHA-512"); 338 string[] strings = ["SHA-224","SHA-256","SHA-384","SHA-512"]; 339 int i; 340 foreach(str; m_trusted_hashes) 341 { 342 assert(str == strings[i], "invalid string: " ~ str); 343 i++; 344 } 345 } 346 347 unittest { 348 propagateTests!stringObjRefTest(); 349 propagateTests!hashmapFreeListTest(); 350 propagateTests!vectorArrayTest(); 351 propagateTests!hashmapComplexTest(); 352 propagateTests!rbTreeTest(); 353 propagateTests!uniqueTest(); 354 propagateTests!refCountedCastTest(); 355 propagateTests!circularBufferTest(); 356 propagateTests!dictionaryListTest(); 357 propagateTests!rbTreeTestTwo(); 358 359 scopedTest(); 360 361 highLevelAllocTest(); 362 363 364 }