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