1 module ut.wrap.module_;
2 
3 import test;
4 import ut.wrap.wrapped;
5 import xlld.wrap;
6 import xlld.conv.to: toXlOper;
7 import std.datetime;
8 import std.experimental.allocator.mallocator: Mallocator;
9 alias theMallocator = Mallocator.instance;
10 
11 
12 ///
13 @("Wrap double[][] -> double")
14 @system unittest {
15     import xlld.memorymanager: allocator;
16 
17     auto arg = toSRef(cast(double[][])[[1, 2, 3, 4], [11, 12, 13, 14]], allocator);
18     FuncAddEverything(&arg).shouldEqualDlang(60.0);
19 
20     arg = toSRef(cast(double[][])[[0, 1, 2, 3], [4, 5, 6, 7]], allocator);
21     FuncAddEverything(&arg).shouldEqualDlang(28.0);
22 }
23 
24 ///
25 @("Wrap double[][] -> double[][]")
26 @system unittest {
27     import xlld.memorymanager: allocator;
28 
29     auto arg = toSRef(cast(double[][])[[1, 2, 3, 4], [11, 12, 13, 14]], allocator);
30     FuncTripleEverything(&arg).shouldEqualDlang(cast(double[][])[[3, 6, 9, 12], [33, 36, 39, 42]]);
31 
32     arg = toSRef(cast(double[][])[[0, 1, 2, 3], [4, 5, 6, 7]], allocator);
33     FuncTripleEverything(&arg).shouldEqualDlang(cast(double[][])[[0, 3, 6, 9], [12, 15, 18, 21]]);
34 }
35 
36 
37 ///
38 @("Wrap string[][] -> double")
39 @system unittest {
40 
41     import xlld.memorymanager: allocator;
42 
43     auto arg = toSRef([["foo", "bar", "baz", "quux"], ["toto", "titi", "tutu", "tete"]], allocator);
44     FuncAllLengths(&arg).shouldEqualDlang(29.0);
45 
46     arg = toSRef([["", "", "", ""], ["", "", "", ""]], allocator);
47     FuncAllLengths(&arg).shouldEqualDlang(0.0);
48 }
49 
50 ///
51 @("Wrap string[][] -> double[][]")
52 @system unittest {
53 
54     import xlld.memorymanager: allocator;
55 
56     auto arg = toSRef([["foo", "bar", "baz", "quux"], ["toto", "titi", "tutu", "tete"]], allocator);
57     FuncLengths(&arg).shouldEqualDlang(cast(double[][])[[3, 3, 3, 4], [4, 4, 4, 4]]);
58 
59     arg = toSRef([["", "", ""], ["", "", "huh"]], allocator);
60     FuncLengths(&arg).shouldEqualDlang(cast(double[][])[[0, 0, 0], [0, 0, 3]]);
61 }
62 
63 ///
64 @("Wrap string[][] -> string[][]")
65 @system unittest {
66 
67     import xlld.memorymanager: allocator;
68 
69     auto arg = toSRef([["foo", "bar", "baz", "quux"], ["toto", "titi", "tutu", "tete"]], allocator);
70     FuncBob(&arg).shouldEqualDlang([["foobob", "barbob", "bazbob", "quuxbob"],
71                                     ["totobob", "titibob", "tutubob", "tetebob"]]);
72 }
73 
74 ///
75 @("Wrap string[] -> double")
76 @system unittest {
77     import xlld.memorymanager: allocator;
78 
79     auto arg = toSRef([["foo", "bar"], ["baz", "quux"]], allocator);
80     FuncStringSlice(&arg).shouldEqualDlang(4.0);
81 }
82 
83 ///
84 @("Wrap double[] -> double")
85 @system unittest {
86     import xlld.memorymanager: allocator;
87     auto arg = toSRef([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], allocator);
88     FuncDoubleSlice(&arg).shouldEqualDlang(6.0);
89 }
90 
91 ///
92 @("Wrap double[] -> double[]")
93 @system unittest {
94     import xlld.memorymanager: allocator;
95     auto arg = toSRef([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], allocator);
96     FuncSliceTimes3(&arg).shouldEqualDlang([3.0, 6.0, 9.0, 12.0, 15.0, 18.0]);
97 }
98 
99 ///
100 @("Wrap string[] -> string[]")
101 @system unittest {
102     import xlld.memorymanager: allocator;
103     auto arg = toSRef(["quux", "toto"], allocator);
104     StringsToStrings(&arg).shouldEqualDlang(["quuxfoo", "totofoo"]);
105 }
106 
107 ///
108 @("Wrap string[] -> string")
109 @system unittest {
110     import xlld.memorymanager: allocator;
111     auto arg = toSRef(["quux", "toto"], allocator);
112     StringsToString(&arg).shouldEqualDlang("quux, toto");
113 }
114 
115 ///
116 @("Wrap string -> string")
117 @system unittest {
118     import xlld.memorymanager: allocator;
119     auto arg = toXlOper("foo", allocator);
120     StringToString(&arg).shouldEqualDlang("foobar");
121 }
122 
123 ///
124 @("Wrap string, string, string -> string")
125 @system unittest {
126     import xlld.memorymanager: allocator;
127     auto arg0 = toXlOper("foo", allocator);
128     auto arg1 = toXlOper("bar", allocator);
129     auto arg2 = toXlOper("baz", allocator);
130     ManyToString(&arg0, &arg1, &arg2).shouldEqualDlang("foobarbaz");
131 }
132 
133 ///
134 @("nothrow functions")
135 @system unittest {
136     import xlld.memorymanager: allocator;
137     auto arg = toXlOper(2.0, allocator);
138     static assert(__traits(compiles, FuncThrows(&arg)));
139 }
140 
141 ///
142 @("FuncAddEverything wrapper is @nogc")
143 @system @nogc unittest {
144     import std.experimental.allocator.mallocator: Mallocator;
145     import xlld.sdk.framework: freeXLOper;
146 
147     auto arg = toXlOper(2.0, Mallocator.instance);
148     scope(exit) freeXLOper(&arg, Mallocator.instance);
149     FuncAddEverything(&arg);
150 }
151 
152 ///
153 @("Wrap a function that throws")
154 @system unittest {
155     auto arg = toSRef(33.3, theGC);
156     FuncThrows(&arg); // should not actually throw
157 }
158 
159 ///
160 @("Wrap a function that asserts")
161 @system unittest {
162     auto arg = toSRef(33.3, theGC);
163     FuncAsserts(&arg); // should not actually throw
164 }
165 
166 ///
167 @("Wrap a function that accepts DateTime")
168 @system unittest {
169     import xlld.sdk.xlcall: XlType;
170     import xlld.conv.misc: stripMemoryBitmask;
171     import std.conv: text;
172 
173     // the argument doesn't matter since we mock extracting the year from it
174     // but it does have to be of double type (DateTime for Excel)
175     auto arg = 33.3.toXlOper(theGC);
176 
177     const year = 2017;
178     const mock = MockDateTime(year, 1, 2, 3, 4, 5);
179     const ret = DateTimeToDouble(&arg);
180 
181     try
182         ret.xltype.stripMemoryBitmask.shouldEqual(XlType.xltypeNum);
183     catch(Exception _)
184         assert(false, text("Expected xltypeNum, got ", *ret));
185 
186     ret.val.num.shouldEqual(year * 2);
187 }
188 
189 ///
190 @("Wrap a function that accepts DateTime[]")
191 @system unittest {
192     //the arguments don't matter since we mock extracting year, etc. from them
193     //they just need to be double (DateTime to Excel)
194     auto arg = [0.1, 0.2].toXlOper(theGC);
195 
196     auto mockDateTimes = MockDateTimes(DateTime(1, 1, 31),
197                                        DateTime(1, 1, 30));
198 
199     auto ret = DateTimesToString(&arg);
200 
201     ret.shouldEqualDlang("31, 30");
202 }
203 
204 
205 @Serial
206 @("Wrap a function that takes an enum")
207 @safe unittest {
208     import test.d_funcs: MyEnum;
209 
210     auto arg = MyEnum.baz.toXlOper(theGC);
211     auto ret = () @trusted { return FuncMyEnumArg(&arg); }();
212     ret.shouldEqualDlang("prefix_baz");
213 }
214 
215 @Serial
216 @("Wrap a function that returns an enum")
217 @safe unittest {
218     import test.d_funcs: MyEnum;
219 
220     auto arg = 1.toXlOper(theGC);
221     auto ret = () @trusted { return FuncMyEnumRet(&arg); }();
222     ret.shouldEqualDlang("bar");
223 }
224 
225 
226 @Serial
227 @("Register a custom to enum conversion")
228 @safe unittest {
229     import std.conv: to;
230     import test.d_funcs: MyEnum;
231     import xlld.conv.from: registerConversionTo, unregisterConversionTo;
232 
233     registerConversionTo!MyEnum((str) => str[3 .. $].to!MyEnum);
234     scope(exit) unregisterConversionTo!MyEnum;
235 
236     auto arg = "___baz".toXlOper(theGC);
237     auto ret = () @trusted { return FuncMyEnumArg(&arg); }();
238 
239     ret.shouldEqualDlang("prefix_baz");
240 }
241 
242 @Serial
243 @("Register a custom from enum conversion")
244 @safe unittest {
245 
246     import std.conv: text;
247     import test.d_funcs: MyEnum;
248     import xlld.conv: registerConversionFrom, unregisterConversionFrom;
249 
250     registerConversionFrom!MyEnum((val) => "___" ~ text(cast(MyEnum)val));
251     scope(exit)unregisterConversionFrom!MyEnum;
252 
253     auto arg = 1.toXlOper(theGC);
254     auto ret = () @trusted { return FuncMyEnumRet(&arg); }();
255 
256     ret.shouldEqualDlang("___bar");
257 }
258 
259 @("Wrap a function that takes a struct using 1D array")
260 unittest {
261     auto arg = [2, 3].toXlOper(theGC);
262     auto ret = () @trusted { return FuncPointArg(&arg); }();
263 
264     ret.shouldEqualDlang(5);
265 }
266 
267 @("Wrap a function that returns a struct")
268 unittest {
269     auto arg1 = 2.toXlOper(theGC);
270     auto arg2 = 3.toXlOper(theGC);
271     auto ret = () @trusted { return FuncPointRet(&arg1, &arg2); }();
272 
273     ret.shouldEqualDlang("Point(2, 3)");
274 }
275 
276 @("Wrap a function that returns a simple tuple")
277 unittest {
278     import xlld.conv.from: fromXlOper;
279     import xlld.conv.misc: stripMemoryBitmask;
280     import std.conv: to;
281 
282     auto arg1 = 42.toXlOper(theGC);
283     auto arg2 = "foo".toXlOper(theGC);
284     auto ret = () @trusted { return FuncSimpleTupleRet(&arg1, &arg2); }();
285 
286     if(ret.xltype.stripMemoryBitmask != XlType.xltypeMulti) {
287         if(ret.xltype.stripMemoryBitmask == XlType.xltypeStr)
288             throw new Exception(ret.fromXlOper!string(theGC));
289         else
290             throw new Exception("Oper not of multi type: " ~ to!string(*ret));
291     }
292 
293     ret.val.array.rows.shouldEqual(1);
294     ret.val.array.columns.shouldEqual(2);
295     ret.val.array.lparray[0].fromXlOper!int(theGC).shouldEqual(42);
296     ret.val.array.lparray[1].fromXlOper!string(theGC).shouldEqual("foo");
297 }
298 
299 @("Wrap a function that returns a complex tuple")
300 unittest {
301     import xlld.conv.from: fromXlOper;
302     import xlld.conv.misc: stripMemoryBitmask;
303     import std.conv: to;
304 
305     auto dates = MockDates([1.0, 2.0, 3.0, 4.0]);
306     auto times = MockTimes([101.0, 102.0, 103.0, 104.0]);
307     auto dateTimes = MockDateTimes(DateTime(2017, 1, 2), DateTime(2017, 2, 2),
308                                    DateTime(2018, 1, 3), DateTime(2018, 2, 3));
309     auto arg1 = 2.toXlOper(theGC);
310     auto arg2 = 3.toXlOper(theGC);
311     auto ret = () @trusted { return FuncComplexTupleRet(&arg1, &arg2); }();
312 
313     if(ret.xltype.stripMemoryBitmask != XlType.xltypeMulti) {
314         if(ret.xltype.stripMemoryBitmask == XlType.xltypeStr)
315             throw new Exception(ret.fromXlOper!string(theGC));
316         else
317             throw new Exception("Oper not of multi type: " ~ to!string(*ret));
318     }
319 
320     ret.xltype.stripMemoryBitmask.shouldEqual(XlType.xltypeMulti);
321     ret.val.array.rows.shouldEqual(1);
322     ret.val.array.columns.shouldEqual(2);
323 
324     ret.val.array.lparray[0].fromXlOper!(DateTime[])(theGC).shouldEqual(
325         [DateTime(2017, 1, 2), DateTime(2017, 2, 2)]);
326 
327     ret.val.array.lparray[1].fromXlOper!(DateTime[])(theGC).shouldEqual(
328         [DateTime(2018, 1, 3), DateTime(2018, 2, 3)]);
329 }
330 
331 @("Wrap a function that returns an array of tuples")
332 unittest {
333     import xlld.conv.from: fromXlOper;
334     import xlld.conv.misc: stripMemoryBitmask;
335     import std.conv: to;
336 
337     auto dates = MockDates([0.1, 0.2, 0.3]);
338     auto times = MockTimes([10.1, 11.1, 12.1]);
339     auto dateTimes = MockDateTimes(DateTime(2017, 1, 1), DateTime(2018, 1, 1), DateTime(2019, 1, 1));
340 
341     auto oper = () @trusted { return FuncTupleArrayRet(); }();
342 
343     void assertMulti(in XLOPER12 oper) {
344         if(oper.xltype.stripMemoryBitmask != XlType.xltypeMulti) {
345             if(oper.xltype.stripMemoryBitmask == XlType.xltypeStr)
346                 throw new Exception(oper.fromXlOper!string(theGC));
347             else
348                 throw new Exception("Oper not of multi type: " ~ to!string(oper));
349         }
350     }
351 
352     assertMulti(*oper);
353     oper.val.array.rows.shouldEqual(1);
354     oper.val.array.columns.shouldEqual(3);
355 
356     auto elts = oper.val.array.lparray[0 .. 3];
357     foreach(elt; elts) assertMulti(elt);
358 
359     elts[0].val.array.lparray[0].fromXlOper!DateTime(theGC).shouldEqual(DateTime(2017, 1, 1));
360     elts[0].val.array.lparray[1].fromXlOper!double(theGC).shouldEqual(11.1);
361 
362     elts[1].val.array.lparray[0].fromXlOper!DateTime(theGC).shouldEqual(DateTime(2018, 1, 1));
363     elts[1].val.array.lparray[1].fromXlOper!double(theGC).shouldEqual(22.2);
364 
365     elts[2].val.array.lparray[0].fromXlOper!DateTime(theGC).shouldEqual(DateTime(2019, 1, 1));
366     elts[2].val.array.lparray[1].fromXlOper!double(theGC).shouldEqual(33.3);
367 }
368 
369 @("Wrap a function that takes an enum array")
370 unittest {
371     import test.d_funcs: MyEnum;
372     auto arg = [MyEnum.foo].toXlOper(theGC);
373     FuncEnumArray(&arg);
374 }
375 
376 ///
377 @("wrapModuleFunctionStr")
378 @system unittest {
379     import xlld.wrap.worksheet;
380     import std.traits: getUDAs;
381 
382     mixin(wrapModuleFunctionStr!("test.d_funcs", "FuncAddEverything"));
383     alias registerAttrs = getUDAs!(FuncAddEverything, Register);
384     static assert(registerAttrs[0].argumentText.value == "Array to add");
385 }
386 
387 
388 ///
389 @("No memory allocation bugs in wrapModuleFunctionImpl for double return Mallocator")
390 @system unittest {
391     import test.d_funcs: FuncAddEverything;
392     import xlld.sdk.xlcall: xlbitDLLFree;
393     import xlld.memorymanager: autoFree;
394 
395     TestAllocator allocator;
396     auto arg = toSRef([1.0, 2.0], theMallocator);
397     auto oper = wrapModuleFunctionImpl!FuncAddEverything(allocator, &arg);
398     (oper.xltype & xlbitDLLFree).shouldBeTrue;
399     allocator.numAllocations.shouldEqual(2);
400     oper.shouldEqualDlang(3.0);
401     autoFree(oper); // normally this is done by Excel
402 }
403 
404 ///
405 @("No memory allocation bugs in wrapModuleFunctionImpl for double[][] return Mallocator")
406 @system unittest {
407     import test.d_funcs: FuncTripleEverything;
408     import xlld.sdk.xlcall: xlbitDLLFree, XlType;
409     import xlld.memorymanager: autoFree;
410 
411     TestAllocator allocator;
412     auto arg = toSRef([1.0, 2.0, 3.0], theMallocator);
413     auto oper = wrapModuleFunctionImpl!FuncTripleEverything(allocator, &arg);
414     (oper.xltype & xlbitDLLFree).shouldBeTrue;
415     (oper.xltype & ~xlbitDLLFree).shouldEqual(XlType.xltypeMulti);
416     allocator.numAllocations.shouldEqual(2);
417     oper.shouldEqualDlang([[3.0, 6.0, 9.0]]);
418     autoFree(oper); // normally this is done by Excel
419 }
420 
421 ///
422 @("No memory allocation bugs in wrapModuleFunctionImpl for double[][] return pool")
423 @system unittest {
424     import std.typecons: Ternary;
425     import xlld.memorymanager: gTempAllocator, autoFree;
426     import test.d_funcs: FuncTripleEverything;
427 
428     auto arg = toSRef([1.0, 2.0, 3.0], gTempAllocator);
429     auto oper = wrapModuleFunctionImpl!FuncTripleEverything(gTempAllocator, &arg);
430     gTempAllocator.empty.shouldEqual(Ternary.yes);
431     oper.shouldEqualDlang([[3.0, 6.0, 9.0]]);
432     autoFree(oper); // normally this is done by Excel
433 }
434 
435 ///
436 @("No memory allocation bugs in wrapModuleFunctionImpl for string")
437 @system unittest {
438     import std.typecons: Ternary;
439     import xlld.memorymanager: gTempAllocator;
440     import test.d_funcs: StringToString;
441 
442     auto arg = "foo".toSRef(gTempAllocator);
443     auto oper = wrapModuleFunctionImpl!StringToString(gTempAllocator, &arg);
444     gTempAllocator.empty.shouldEqual(Ternary.yes);
445     oper.shouldEqualDlang("foobar");
446 }
447 
448 ///
449 @("No memory allocation bugs in wrapModuleFunctionImpl for Any[][] -> Any[][] -> Any[][] mallocator")
450 @system unittest {
451     import xlld.memorymanager: allocatorContext;
452     import test.d_funcs: FirstOfTwoAnyArrays;
453 
454     with(allocatorContext(theGC)) {
455         auto dArg = [[any(1.0), any("foo"), any(3.0)], [any(4.0), any(5.0), any(6.0)]];
456         auto arg = toXlOper(dArg);
457         auto oper = wrapModuleFunctionImpl!FirstOfTwoAnyArrays(theMallocator, &arg, &arg);
458         oper.shouldEqualDlang(dArg);
459     }
460 }
461 
462 ///
463 @("No memory allocation bugs in wrapModuleFunctionImpl for Any[][] -> Any[][] -> Any[][] TestAllocator")
464 @system unittest {
465     import xlld.memorymanager: allocatorContext;
466     import test.d_funcs: FirstOfTwoAnyArrays;
467 
468     auto testAllocator = TestAllocator();
469 
470     with(allocatorContext(theGC)) {
471         auto dArg = [
472             [ any(1.0), any("foo"), any(3.0) ],
473             [ any(4.0), any(5.0),   any(6.0) ],
474         ];
475         auto arg = toXlOper(dArg);
476         auto oper = wrapModuleFunctionImpl!FirstOfTwoAnyArrays(testAllocator, &arg, &arg);
477         oper.shouldEqualDlang(dArg);
478     }
479 }
480 
481 ///
482 @("Correct number of coercions and frees in wrapModuleFunctionImpl")
483 @system unittest {
484     import test.d_funcs: FuncAddEverything;
485     import xlld.test.util: gNumXlAllocated, gNumXlFree;
486 
487     const oldNumAllocated = gNumXlAllocated;
488     const oldNumFree = gNumXlFree;
489 
490     auto arg = toSRef([1.0, 2.0], theGC);
491     auto oper = wrapModuleFunctionImpl!FuncAddEverything(theGC, &arg);
492 
493     (gNumXlAllocated - oldNumAllocated).shouldEqual(1);
494     (gNumXlFree   - oldNumFree).shouldEqual(1);
495 }
496 
497 
498 ///
499 @("Can't return empty 1D array to Excel")
500 @system unittest {
501     import xlld.memorymanager: allocatorContext;
502     import test.d_funcs: EmptyStrings1D;
503 
504     with(allocatorContext(theGC)) {
505         auto dArg = any(1.0);
506         auto arg = toXlOper(dArg);
507         auto oper = wrapModuleFunctionImpl!EmptyStrings1D(theGC, &arg);
508         oper.shouldEqualDlang("#ERROR: empty result");
509     }
510 }
511 
512 
513 ///
514 @("Can't return empty 2D array to Excel")
515 @system unittest {
516     import xlld.memorymanager: allocatorContext;
517     import test.d_funcs: EmptyStrings2D;
518 
519     with(allocatorContext(theGC)) {
520         auto dArg = any(1.0);
521         auto arg = toXlOper(dArg);
522         auto oper = wrapModuleFunctionImpl!EmptyStrings2D(theGC, &arg);
523         oper.shouldEqualDlang("#ERROR: empty result");
524     }
525 }
526 
527 ///
528 @("Can't return half empty 2D array to Excel")
529 @system unittest {
530     import xlld.memorymanager: allocatorContext;
531     import test.d_funcs: EmptyStringsHalfEmpty2D;
532 
533     with(allocatorContext(theGC)) {
534         auto dArg = any(1.0);
535         auto arg = toXlOper(dArg);
536         auto oper = wrapModuleFunctionImpl!EmptyStringsHalfEmpty2D(theGC, &arg);
537         oper.shouldEqualDlang("#ERROR: empty result");
538     }
539 }
540 
541 ///
542 @("issue 25 - make sure to reserve memory for all dArgs")
543 @system unittest {
544     import std.typecons: Ternary;
545     import xlld.memorymanager: allocatorContext, MemoryPool;
546     import test.d_funcs: FirstOfTwoAnyArrays;
547 
548     auto pool = MemoryPool();
549 
550     with(allocatorContext(theGC)) {
551         auto dArg = [[any(1.0), any("foo"), any(3.0)], [any(4.0), any(5.0), any(6.0)]];
552         auto arg = toSRef(dArg);
553         auto oper = wrapModuleFunctionImpl!FirstOfTwoAnyArrays(pool, &arg, &arg);
554     }
555 
556     pool.empty.shouldEqual(Ternary.yes); // deallocateAll in wrapImpl
557 }
558 
559 
560 ///
561 @("wrapModuleFunctionStr function that returns Any[][]")
562 @safe unittest {
563     mixin(wrapModuleFunctionStr!("test.d_funcs", "DoubleArrayToAnyArray"));
564 
565     auto oper = [[1.0, 2.0], [3.0, 4.0]].toSRef(theGC);
566     auto arg = () @trusted { return &oper; }();
567     auto ret = DoubleArrayToAnyArray(arg);
568 
569     auto opers = () @trusted { return ret.val.array.lparray[0 .. 4]; }();
570     opers[0].shouldEqualDlang(2.0);
571     opers[1].shouldEqualDlang(6.0);
572     opers[2].shouldEqualDlang("3quux");
573     opers[3].shouldEqualDlang("4toto");
574 }
575 
576 ///
577 @("wrapModuleFunctionStr int -> int")
578 @safe unittest {
579     mixin(wrapModuleFunctionStr!("test.d_funcs", "Twice"));
580 
581     auto oper = 3.toSRef(theGC);
582     auto arg = () @trusted { return &oper; }();
583     Twice(arg).shouldEqualDlang(6);
584 }
585 
586 ///
587 @("issue 31 - D functions can have const arguments")
588 @safe unittest {
589     mixin(wrapModuleFunctionStr!("test.d_funcs", "FuncConstDouble"));
590 
591     auto oper = (3.0).toSRef(theGC);
592     auto arg = () @trusted { return &oper; }();
593     FuncConstDouble(arg).shouldEqualDlang(3.0);
594 }
595 
596 
597 @Flaky
598 @("wrapModuleFunctionStr async double -> double")
599 unittest {
600     import xlld.conv.from: fromXlOper;
601     import xlld.wrap.traits: Async;
602     import xlld.test.util: asyncReturn, newAsyncHandle;
603     import core.time: MonoTime;
604     import core.thread;
605 
606     mixin(wrapModuleFunctionStr!("test.d_funcs", "AsyncDoubleToDouble"));
607 
608     auto oper = (3.0).toXlOper(theGC);
609     auto arg = () @trusted { return &oper; }();
610     auto asyncHandle = newAsyncHandle;
611     () @trusted { AsyncDoubleToDouble(arg, &asyncHandle); }();
612 
613     const start = MonoTime.currTime;
614     const expected = 6.0;
615     while(asyncReturn(asyncHandle).fromXlOper!double(theGC) != expected &&
616           MonoTime.currTime - start < 1.seconds)
617     {
618         Thread.sleep(10.msecs);
619     }
620     asyncReturn(asyncHandle).shouldEqualDlang(expected);
621 }
622 
623 @("wrapModuleFunctionStr () -> NaN")
624 unittest {
625     mixin(wrapModuleFunctionStr!("test.d_funcs", "NaN"));
626     NaN().shouldEqualDlang("#NaN");
627 }
628 
629 
630 
631 @("wrapModuleFunctionImpl gTempAllocator @safe")
632 @safe unittest {
633     import xlld.memorymanager: gTempAllocator;
634     import test.d_funcs: Twice;
635 
636     auto arg = 3.toXlOper(theGC);
637     auto argPtr = () @trusted { return &arg; }();
638     auto oper = wrapModuleFunctionImpl!Twice(gTempAllocator, argPtr);
639     oper.shouldEqualDlang(6);
640 }
641 
642 
643 @("oper")
644 @safe unittest {
645     auto arg = (3.3).toXlOper(theGC);
646     scope argPtr = &arg;
647     FuncOper(argPtr).shouldEqualDlang(6.6);
648 }
649 
650 
651 @("vector.1d")
652 @safe unittest {
653     auto arg = 7.toXlOper(theGC);
654     scope argPtr = &arg;
655     FuncVector(argPtr).shouldEqualDlang([0, 1, 2, 3, 4, 5, 6]);
656 }
657 
658 
659 @("vector.2d")
660 @safe unittest {
661     auto arg = 7.toXlOper(theGC);
662     scope argPtr = &arg;
663     FuncVector2D(argPtr).shouldEqualDlang([[7, 7, 7], [8, 8, 8]]);
664 }