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 }