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