1 module ut.ref_counted;
2 
3 import ut;
4 import automem.ref_counted;
5 
6 mixin TestUtils;
7 
8 ///
9 @("struct test allocator no copies")
10 @system unittest {
11     auto allocator = TestAllocator();
12     {
13         auto ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
14         Struct.numStructs.shouldEqual(1);
15     }
16     Struct.numStructs.shouldEqual(0);
17 }
18 
19 @("struct test allocator one lvalue assignment")
20 @system unittest {
21     auto allocator = TestAllocator();
22     {
23         auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
24         Struct.numStructs.shouldEqual(1);
25 
26         RefCounted!(Struct, TestAllocator*) ptr2;
27         ptr2 = ptr1;
28         Struct.numStructs.shouldEqual(1);
29     }
30     Struct.numStructs.shouldEqual(0);
31 }
32 
33 @("struct test allocator one lvalue assignment from T.init")
34 @system unittest {
35 
36     auto allocator = TestAllocator();
37 
38     {
39         RefCounted!(Struct, TestAllocator*) ptr1;
40         Struct.numStructs.shouldEqual(0);
41 
42         auto ptr2 = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
43         Struct.numStructs.shouldEqual(1);
44 
45         ptr2 = ptr1;
46         Struct.numStructs.shouldEqual(0);
47     }
48 
49     Struct.numStructs.shouldEqual(0);
50 }
51 
52 @("struct test allocator one lvalue assignment both non-null")
53 @system unittest {
54 
55     auto allocator = TestAllocator();
56 
57     {
58         auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
59         Struct.numStructs.shouldEqual(1);
60 
61         auto ptr2 = RefCounted!(Struct, TestAllocator*)(&allocator, 7);
62         Struct.numStructs.shouldEqual(2);
63 
64         ptr2 = ptr1;
65         Struct.numStructs.shouldEqual(1);
66     }
67 
68     Struct.numStructs.shouldEqual(0);
69 }
70 
71 
72 
73 @("struct test allocator one rvalue assignment test allocator")
74 @system unittest {
75     auto allocator = TestAllocator();
76     {
77         RefCounted!(Struct, TestAllocator*) ptr;
78         ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
79         Struct.numStructs.shouldEqual(1);
80     }
81     Struct.numStructs.shouldEqual(0);
82 }
83 
84 @("struct test allocator one rvalue assignment mallocator")
85 @system unittest {
86     import stdx.allocator.mallocator: Mallocator;
87     {
88         RefCounted!(Struct, Mallocator) ptr;
89         ptr = RefCounted!(Struct, Mallocator)(5);
90         Struct.numStructs.shouldEqual(1);
91     }
92     Struct.numStructs.shouldEqual(0);
93 }
94 
95 
96 @("struct test allocator one lvalue copy constructor")
97 @system unittest {
98     auto allocator = TestAllocator();
99     {
100         auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
101         Struct.numStructs.shouldEqual(1);
102         auto ptr2 = ptr1;
103         Struct.numStructs.shouldEqual(1);
104 
105         ptr1.i.shouldEqual(5);
106         ptr2.i.shouldEqual(5);
107     }
108     Struct.numStructs.shouldEqual(0);
109 }
110 
111 @("struct test allocator one rvalue copy constructor")
112 @system unittest {
113     auto allocator = TestAllocator();
114     {
115         auto ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
116         Struct.numStructs.shouldEqual(1);
117     }
118     Struct.numStructs.shouldEqual(0);
119 }
120 
121 @("many copies made")
122 @system unittest {
123     auto allocator = TestAllocator();
124 
125     // helper function for intrusive testing, in case the implementation
126     // ever changes
127     size_t refCount(T)(ref T ptr) {
128         return ptr._impl._count;
129     }
130 
131     {
132         auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
133         Struct.numStructs.shouldEqual(1);
134 
135         auto ptr2 = ptr1;
136         Struct.numStructs.shouldEqual(1);
137 
138         {
139             auto ptr3 = ptr2;
140             Struct.numStructs.shouldEqual(1);
141 
142             refCount(ptr1).shouldEqual(3);
143             refCount(ptr2).shouldEqual(3);
144             refCount(ptr3).shouldEqual(3);
145         }
146 
147         Struct.numStructs.shouldEqual(1);
148         refCount(ptr1).shouldEqual(2);
149         refCount(ptr2).shouldEqual(2);
150 
151         auto produce() {
152             return RefCounted!(Struct, TestAllocator*)(&allocator, 3);
153         }
154 
155         ptr1 = produce;
156         Struct.numStructs.shouldEqual(2);
157         refCount(ptr1).shouldEqual(1);
158         refCount(ptr2).shouldEqual(1);
159 
160         ptr1.twice.shouldEqual(6);
161         ptr2.twice.shouldEqual(10);
162     }
163 
164     Struct.numStructs.shouldEqual(0);
165 }
166 
167 @("default allocator")
168 @system unittest {
169     {
170         auto ptr = RefCounted!Struct(5);
171         Struct.numStructs.shouldEqual(1);
172     }
173     Struct.numStructs.shouldEqual(0);
174 }
175 
176 static if (__VERSION__ >= 2079)
177 @("default allocator (shared)")
178 @system unittest {
179     {
180         auto ptr = RefCounted!(shared SharedStruct)(5);
181         SharedStruct.numStructs.shouldEqual(1);
182     }
183     SharedStruct.numStructs.shouldEqual(0);
184 }
185 
186 @("deref")
187 @system unittest {
188     auto allocator = TestAllocator();
189     auto rc1 = RefCounted!(int, TestAllocator*)(&allocator, 5);
190 
191     (*rc1).shouldEqual(5);
192     auto rc2 = rc1;
193     *rc2 = 42;
194     (*rc1).shouldEqual(42);
195 }
196 
197 @("swap")
198 @system unittest {
199     import std.algorithm: swap;
200     RefCounted!(int, TestAllocator*) rc1, rc2;
201     swap(rc1, rc2);
202 }
203 
204 @("phobos bug 6606")
205 @system unittest {
206 
207     union U {
208        size_t i;
209        void* p;
210     }
211 
212     struct S {
213        U u;
214     }
215 
216     alias SRC = RefCounted!(S, TestAllocator*);
217 }
218 
219 @("phobos bug 6436")
220 @system unittest
221 {
222     static struct S {
223         this(ref int val, string file = __FILE__, size_t line = __LINE__) {
224             val.shouldEqual(3, file, line);
225             ++val;
226         }
227     }
228 
229     auto allocator = TestAllocator();
230     int val = 3;
231     auto s = RefCounted!(S, TestAllocator*)(&allocator, val);
232     val.shouldEqual(4);
233 }
234 
235 @("assign from T")
236 @system unittest {
237     import stdx.allocator.mallocator: Mallocator;
238 
239     {
240         auto a = RefCounted!(Struct, Mallocator)(3);
241         Struct.numStructs.shouldEqual(1);
242 
243         *a = Struct(5);
244         Struct.numStructs.shouldEqual(1);
245         (*a).shouldEqual(Struct(5));
246 
247         RefCounted!(Struct, Mallocator) b;
248         b = a;
249         (*b).shouldEqual(Struct(5));
250         Struct.numStructs.shouldEqual(1);
251     }
252 
253     Struct.numStructs.shouldEqual(0);
254 }
255 
256 @("assign self")
257 @system unittest {
258     auto allocator = TestAllocator();
259     {
260         auto a = RefCounted!(Struct, TestAllocator*)(&allocator, 1);
261         a = a;
262         Struct.numStructs.shouldEqual(1);
263     }
264     Struct.numStructs.shouldEqual(0);
265 }
266 
267 static if (__VERSION__ >= 2079)
268 @("SharedStruct")
269 @system unittest {
270     auto allocator = TestAllocator();
271     {
272         auto ptr = RefCounted!(shared SharedStruct, TestAllocator*)(&allocator, 5);
273         SharedStruct.numStructs.shouldEqual(1);
274     }
275     SharedStruct.numStructs.shouldEqual(0);
276 }
277 
278 @("@nogc @safe")
279 @safe @nogc unittest {
280 
281     auto allocator = SafeAllocator();
282 
283     {
284         const ptr = RefCounted!(NoGcStruct, SafeAllocator)(SafeAllocator(), 6);
285         assert(ptr.i == 6);
286         assert(NoGcStruct.numStructs == 1);
287     }
288 
289     assert(NoGcStruct.numStructs == 0);
290 }
291 
292 
293 @("const object")
294 @system unittest {
295     auto allocator = TestAllocator();
296     auto ptr1 = RefCounted!(const Struct, TestAllocator*)(&allocator, 5);
297 }
298 
299 
300 @("theAllocator")
301 @system unittest {
302 
303     with(theTestAllocator) {
304         auto ptr = RefCounted!Struct(42);
305         (*ptr).shouldEqual(Struct(42));
306         Struct.numStructs.shouldEqual(1);
307     }
308 
309     Struct.numStructs.shouldEqual(0);
310 }
311 
312 static if (__VERSION__ >= 2079) {
313 
314     @("threads Mallocator")
315         @system unittest {
316         import stdx.allocator.mallocator: Mallocator;
317         static assert(__traits(compiles, sendRefCounted!Mallocator(7)));
318     }
319 
320     @("threads SafeAllocator by value")
321         @system unittest {
322         // can't even use TestAllocator because it has indirections
323         // can't pass by pointer since it's an indirection
324         auto allocator = SafeAllocator();
325         static assert(__traits(compiles, sendRefCounted!(SafeAllocator)(allocator, 7)));
326     }
327 
328     @("threads SafeAllocator by shared pointer")
329         @system unittest {
330         // can't even use TestAllocator because it has indirections
331         // can't only pass by pointer if shared
332         auto allocator = shared SafeAllocator();
333         static assert(__traits(compiles, sendRefCounted!(shared SafeAllocator*)(&allocator, 7)));
334     }
335 }
336 
337 @("Construct RefCounted from Unique")
338 @system unittest {
339     import automem.unique: Unique;
340     auto allocator = TestAllocator();
341     auto ptr = refCounted(Unique!(int, TestAllocator*)(&allocator, 42));
342     (*ptr).shouldEqual(42);
343 }
344 
345 @("RefCounted with class")
346 @system unittest {
347     auto allocator = TestAllocator();
348     {
349         writelnUt("Creating ptr");
350         auto ptr = RefCounted!(Class, TestAllocator*)(&allocator, 33);
351         (*ptr).i.shouldEqual(33);
352         Class.numClasses.shouldEqual(1);
353     }
354     Class.numClasses.shouldEqual(0);
355 }
356 
357 @("@nogc class destructor")
358 @nogc unittest {
359 
360     import automem: Unique;
361 
362     auto allocator = SafeAllocator();
363 
364     {
365         const ptr = Unique!(NoGcClass, SafeAllocator)(SafeAllocator(), 6);
366         // shouldEqual isn't @nogc
367         assert(ptr.i == 6);
368         assert(NoGcClass.numClasses == 1);
369     }
370 
371     assert(NoGcClass.numClasses == 0);
372 }
373 
374 @("RefCounted opSlice and opIndex")
375 @system unittest {
376     import std.mmfile: MmFile;
377     auto file = RefCounted!MmFile(null, MmFile.Mode.readWriteNew, 120, null);
378     // The type of file[0] should be ubyte, not Impl.
379     static assert(is(typeof(file[0]) == typeof(MmFile.init[0])));
380     // opSlice should result in void[] not Impl[].
381     static assert(is(typeof(file[0 .. size_t.max]) == typeof(MmFile.init[0 .. size_t.max])));
382     ubyte[] data = cast(ubyte[]) file[0 .. cast(size_t) file.length];
383     immutable ubyte b = file[1];
384     file[1] = cast(ubyte) (b + 1);
385     assert(data[1] == cast(ubyte) (b + 1));
386 }
387 
388 @("Construct RefCounted using global allocator for struct with zero-args ctor")
389 @system unittest {
390     struct S {
391         private ulong zeroArgsCtorTest = 3;
392     }
393     auto s = RefCounted!S.construct();
394     static assert(is(typeof(s) == RefCounted!S));
395     assert(s._impl !is null);
396     assert(s.zeroArgsCtorTest == 3);
397 }
398 
399 
400 
401 void sendRefCounted(Allocator, Args...)(Args args) {
402     import std.concurrency: spawn, send;
403 
404     auto tid = spawn(&threadFunc);
405     auto ptr = RefCounted!(shared SharedStruct, Allocator)(args);
406 
407     tid.send(ptr);
408 }
409 
410 void threadFunc() {
411 
412 }
413 
414 @("shared struct with indirection")
415 @system unittest {
416     auto s = RefCounted!(shared SharedStructWithIndirection)("foobar");
417 }