1 /**
2    Tests for converting from XLOPER12 to D types.
3  */
4 module ut.conv.from;
5 
6 import test;
7 import xlld.conv.to: toXlOper;
8 import xlld.conv.from;
9 
10 
11 @("fromXlOper!double")
12 @system unittest {
13     import xlld.sdk.framework: freeXLOper;
14 
15     TestAllocator allocator;
16     auto num = 4.0;
17     auto oper = num.toXlOper(allocator);
18     auto back = oper.fromXlOper!double(allocator);
19     back.shouldEqual(num);
20 
21     freeXLOper(&oper, allocator);
22 }
23 
24 
25 ///
26 @("isNan for fromXlOper!double")
27 @system unittest {
28     import std.math: isNaN;
29     import xlld.memorymanager: allocator;
30     XLOPER12 oper;
31     oper.xltype = XlType.xltypeMissing;
32     fromXlOper!double(&oper, allocator).isNaN.shouldBeTrue;
33 }
34 
35 
36 @("fromXlOper!double wrong oper type")
37 @system unittest {
38     "foo".toXlOper(theGC).fromXlOper!double(theGC).shouldThrowWithMessage("Wrong type for fromXlOper!double");
39 }
40 
41 
42 ///
43 @("fromXlOper!int")
44 @system unittest {
45     42.toXlOper(theGC).fromXlOper!int(theGC).shouldEqual(42);
46 }
47 
48 ///
49 @("fromXlOper!int when given xltypeNum")
50 @system unittest {
51     42.0.toXlOper(theGC).fromXlOper!int(theGC).shouldEqual(42);
52 }
53 
54 ///
55 @("0 for fromXlOper!int missing oper")
56 @system unittest {
57     XLOPER12 oper;
58     oper.xltype = XlType.xltypeMissing;
59     oper.fromXlOper!int(theGC).shouldEqual(0);
60 }
61 
62 @("fromXlOper!int wrong oper type")
63 @system unittest {
64     "foo".toXlOper(theGC).fromXlOper!int(theGC).shouldThrowWithMessage("Wrong type for fromXlOper!int");
65 }
66 
67 ///
68 @("fromXlOper!string missing")
69 @system unittest {
70     import xlld.memorymanager: allocator;
71     XLOPER12 oper;
72     oper.xltype = XlType.xltypeMissing;
73     fromXlOper!string(&oper, allocator).shouldBeNull;
74 }
75 
76 ///
77 @("fromXlOper!string")
78 @system unittest {
79     import std.experimental.allocator: dispose;
80     import xlld.sdk.framework: freeXLOper;
81 
82     TestAllocator allocator;
83     auto oper = "foo".toXlOper(allocator);
84     auto str = fromXlOper!string(&oper, allocator);
85     allocator.numAllocations.shouldEqual(2);
86 
87     freeXLOper(&oper, allocator);
88     str.shouldEqual("foo");
89     allocator.dispose(cast(void[])str);
90 }
91 
92 ///
93 @("fromXlOper!string unicode")
94 @system unittest {
95     auto oper = "é".toXlOper(theGC);
96     auto str = fromXlOper!string(&oper, theGC);
97     str.shouldEqual("é");
98 }
99 
100 @("fromXlOper!string allocation failure")
101 @system unittest {
102     auto allocator = FailingAllocator();
103     "foo".toXlOper(theGC).fromXlOper!string(allocator).shouldThrowWithMessage("Could not allocate memory for array of char");
104 }
105 
106 
107 @("fromXlOper!string wrong oper type")
108 @system unittest {
109     42.toXlOper(theGC).fromXlOper!string(theGC).shouldThrowWithMessage("Wrong type for fromXlOper!string");
110 }
111 
112 ///
113 @("fromXlOper any double")
114 @system unittest {
115     any(5.0, theGC).fromXlOper!Any(theGC).shouldEqual(any(5.0, theGC));
116 }
117 
118 ///
119 @("fromXlOper any string")
120 @system unittest {
121     any("foo", theGC).fromXlOper!Any(theGC)._impl
122         .fromXlOper!string(theGC).shouldEqual("foo");
123 }
124 
125 ///
126 @("fromXlOper!string[][]")
127 unittest {
128     import xlld.memorymanager: allocator;
129     import xlld.sdk.framework: freeXLOper;
130 
131     auto strings = [["foo", "bar", "baz"], ["toto", "titi", "quux"]];
132     auto oper = strings.toXlOper(allocator);
133     scope(exit) freeXLOper(&oper, allocator);
134     oper.fromXlOper!(string[][])(allocator).shouldEqual(strings);
135 }
136 
137 ///
138 @("fromXlOper!double[][]")
139 unittest {
140     import xlld.memorymanager: allocator;
141     import xlld.sdk.framework: freeXLOper;
142 
143     auto doubles = [[1.0, 2.0], [3.0, 4.0]];
144     auto oper = doubles.toXlOper(allocator);
145     scope(exit) freeXLOper(&oper, allocator);
146     oper.fromXlOper!(double[][])(allocator).shouldEqual(doubles);
147 }
148 
149 ///
150 @("fromXlOper!string[][] TestAllocator")
151 unittest {
152     import std.experimental.allocator: disposeMultidimensionalArray;
153     import xlld.sdk.framework: freeXLOper;
154 
155     TestAllocator allocator;
156     auto strings = [["foo", "bar", "baz"], ["toto", "titi", "quux"]];
157     auto oper = strings.toXlOper(allocator);
158     auto backAgain = oper.fromXlOper!(string[][])(allocator);
159 
160     allocator.numAllocations.shouldEqual(16);
161 
162     freeXLOper(&oper, allocator);
163     backAgain.shouldEqual(strings);
164     allocator.disposeMultidimensionalArray(cast(void[][][])backAgain);
165 }
166 
167 ///
168 @("fromXlOper!string[][] when not all opers are strings")
169 unittest {
170     import xlld.conv.misc: multi;
171     import std.experimental.allocator.mallocator: Mallocator;
172     alias allocator = theGC;
173 
174     const rows = 2;
175     const cols = 3;
176     auto array = multi(rows, cols, allocator);
177     auto opers = array.val.array.lparray[0 .. rows*cols];
178     const strings = ["foo", "bar", "baz"];
179     const numbers = [1.0, 2.0, 3.0];
180 
181     int i;
182     foreach(r; 0 .. rows) {
183         foreach(c; 0 .. cols) {
184             if(r == 0)
185                 opers[i++] = strings[c].toXlOper(allocator);
186             else
187                 opers[i++] = numbers[c].toXlOper(allocator);
188         }
189     }
190 
191     opers[3].fromXlOper!string(allocator).shouldEqual("1.000000");
192     // sanity checks
193     opers[0].fromXlOper!string(allocator).shouldEqual("foo");
194     opers[3].fromXlOper!double(allocator).shouldEqual(1.0);
195     // the actual assertion
196     array.fromXlOper!(string[][])(allocator).shouldEqual([["foo", "bar", "baz"],
197                                                           ["1.000000", "2.000000", "3.000000"]]);
198 }
199 
200 
201 ///
202 @("fromXlOper!double[][] TestAllocator")
203 unittest {
204     import xlld.sdk.framework: freeXLOper;
205     import std.experimental.allocator: disposeMultidimensionalArray;
206 
207     TestAllocator allocator;
208     auto doubles = [[1.0, 2.0], [3.0, 4.0]];
209     auto oper = doubles.toXlOper(allocator);
210     auto backAgain = oper.fromXlOper!(double[][])(allocator);
211 
212     allocator.numAllocations.shouldEqual(4);
213 
214     freeXLOper(&oper, allocator);
215     backAgain.shouldEqual(doubles);
216     allocator.disposeMultidimensionalArray(backAgain);
217 }
218 
219 ///
220 @("fromXlOper!string[]")
221 unittest {
222     import xlld.memorymanager: allocator;
223     import xlld.sdk.framework: freeXLOper;
224 
225     auto strings = ["foo", "bar", "baz", "toto", "titi", "quux"];
226     auto oper = strings.toXlOper(allocator);
227     scope(exit) freeXLOper(&oper, allocator);
228     oper.fromXlOper!(string[])(allocator).shouldEqual(strings);
229 }
230 
231 ///
232 @("fromXlOper!double[] from row")
233 unittest {
234     import xlld.sdk.xlcall: xlfCaller;
235 
236     XLOPER12 caller;
237     caller.xltype = XlType.xltypeSRef;
238     caller.val.sref.ref_.rwFirst = 1;
239     caller.val.sref.ref_.rwLast = 1;
240     caller.val.sref.ref_.colFirst = 2;
241     caller.val.sref.ref_.colLast = 4;
242 
243     with(MockXlFunction(xlfCaller, caller)) {
244         auto doubles = [1.0, 2.0, 3.0, 4.0];
245         auto oper = doubles.toXlOper(theGC);
246         oper.shouldEqualDlang(doubles);
247     }
248 }
249 
250 ///
251 @("fromXlOper!double[]")
252 unittest {
253     auto doubles = [1.0, 2.0, 3.0, 4.0];
254     doubles.toXlOper(theGC).fromXlOper!(double[])(theGC).shouldEqual(doubles);
255 }
256 
257 @("fromXlOper!int[]")
258 unittest {
259     auto ints = [1, 2, 3, 4];
260     ints.toXlOper(theGC).fromXlOper!(int[])(theGC).shouldEqual(ints);
261 }
262 
263 
264 ///
265 @("fromXlOper!string[] TestAllocator")
266 unittest {
267     import std.experimental.allocator: disposeMultidimensionalArray;
268     import xlld.sdk.framework: freeXLOper;
269 
270     TestAllocator allocator;
271     auto strings = ["foo", "bar", "baz", "toto", "titi", "quux"];
272     auto oper = strings.toXlOper(allocator);
273     auto backAgain = oper.fromXlOper!(string[])(allocator);
274 
275     allocator.numAllocations.shouldEqual(14);
276 
277     backAgain.shouldEqual(strings);
278     freeXLOper(&oper, allocator);
279     allocator.disposeMultidimensionalArray(cast(void[][])backAgain);
280 }
281 
282 ///
283 @("fromXlOper!double[] TestAllocator")
284 unittest {
285     import std.experimental.allocator: dispose;
286     import xlld.sdk.framework: freeXLOper;
287 
288     TestAllocator allocator;
289     auto doubles = [1.0, 2.0, 3.0, 4.0];
290     auto oper = doubles.toXlOper(allocator);
291     auto backAgain = oper.fromXlOper!(double[])(allocator);
292 
293     allocator.numAllocations.shouldEqual(2);
294 
295     backAgain.shouldEqual(doubles);
296     freeXLOper(&oper, allocator);
297     allocator.dispose(backAgain);
298 }
299 
300 @("fromXlOper!double[][] nil")
301 @system unittest {
302     XLOPER12 oper;
303     oper.xltype = XlType.xltypeNil;
304     double[][] empty;
305     oper.fromXlOper!(double[][])(theGC).shouldEqual(empty);
306 }
307 
308 
309 @("fromXlOper any 1D array")
310 @system unittest {
311     import xlld.memorymanager: allocatorContext;
312     with(allocatorContext(theGC)) {
313         auto array = [any(1.0), any("foo")];
314         auto oper = toXlOper(array);
315         auto back = fromXlOper!(Any[])(oper);
316         back.shouldEqual(array);
317     }
318 }
319 
320 
321 ///
322 @("fromXlOper Any 2D array")
323 @system unittest {
324     import xlld.memorymanager: allocatorContext;
325     with(allocatorContext(theGC)) {
326         auto array = [[any(1.0), any(2.0)], [any("foo"), any("bar")]];
327         auto oper = toXlOper(array);
328         auto back = fromXlOper!(Any[][])(oper);
329         back.shouldEqual(array);
330     }
331 }
332 
333 ///
334 @("fromXlOper!DateTime")
335 @system unittest {
336     XLOPER12 oper;
337     auto mockDateTime = MockDateTime(2017, 12, 31, 1, 2, 3);
338 
339     const dateTime = oper.fromXlOper!DateTime(theGC);
340 
341     dateTime.year.shouldEqual(2017);
342     dateTime.month.shouldEqual(12);
343     dateTime.day.shouldEqual(31);
344     dateTime.hour.shouldEqual(1);
345     dateTime.minute.shouldEqual(2);
346     dateTime.second.shouldEqual(3);
347 }
348 
349 @("fromXlOper!DateTime wrong oper type")
350 @system unittest {
351     42.toXlOper(theGC).fromXlOper!DateTime(theGC).shouldThrowWithMessage(
352         "Wrong type for fromXlOper!DateTime");
353 }
354 
355 
356 @("fromXlOper!bool when bool")
357 @system unittest {
358     import xlld.sdk.xlcall: XLOPER12, XlType;
359     XLOPER12 oper;
360     oper.xltype = XlType.xltypeBool;
361     oper.val.bool_ = 1;
362     oper.fromXlOper!bool(theGC).shouldEqual(true);
363 
364     oper.val.bool_ = 0;
365     oper.fromXlOper!bool(theGC).shouldEqual(false);
366 
367     oper.val.bool_ = 2;
368     oper.fromXlOper!bool(theGC).shouldEqual(true);
369 }
370 
371 @("fromXlOper!bool when int")
372 @system unittest {
373     42.toXlOper(theGC).fromXlOper!bool(theGC).shouldEqual(true);
374     0.toXlOper(theGC).fromXlOper!bool(theGC).shouldEqual(false);
375 }
376 
377 @("fromXlOper!bool when double")
378 @system unittest {
379     33.3.toXlOper(theGC).fromXlOper!bool(theGC).shouldEqual(true);
380     0.0.toXlOper(theGC).fromXlOper!bool(theGC).shouldEqual(false);
381 }
382 
383 
384 @("fromXlOper!bool when string")
385 @system unittest {
386     "true".toXlOper(theGC).fromXlOper!bool(theGC).shouldEqual(true);
387     "True".toXlOper(theGC).fromXlOper!bool(theGC).shouldEqual(true);
388     "TRUE".toXlOper(theGC).fromXlOper!bool(theGC).shouldEqual(true);
389     "false".toXlOper(theGC).fromXlOper!bool(theGC).shouldEqual(false);
390 }
391 
392 @("fromXlOper!enum")
393 @system unittest {
394     enum Enum {
395         foo, bar, baz,
396     }
397 
398     "bar".toXlOper(theGC).fromXlOper!Enum(theGC).shouldEqual(Enum.bar);
399     "quux".toXlOper(theGC).fromXlOper!Enum(theGC).shouldThrowWithMessage("Enum does not have a member named 'quux'");
400 }
401 
402 @("fromXlOper!enum wrong type")
403 @system unittest {
404     enum Enum { foo, bar, baz, }
405 
406     42.toXlOper(theGC).fromXlOper!Enum(theGC).shouldThrowWithMessage(
407         "Wrong type for fromXlOper!Enum");
408 }
409 
410 
411 @("1D array to struct")
412 @system unittest {
413     static struct Foo { int x, y; }
414     [2, 3].toXlOper(theGC).fromXlOper!Foo(theGC).shouldEqual(Foo(2, 3));
415 }
416 
417 @("wrong oper type to struct")
418 @system unittest {
419     static struct Foo { int x, y; }
420 
421     2.toXlOper(theGC).fromXlOper!Foo(theGC).shouldThrowWithMessage(
422         "Can only convert arrays to structs. Must be either 1xN, Nx1, 2xN or Nx2");
423 }
424 
425 @("1D array to struct with wrong length")
426 @system unittest {
427 
428     import nogc: NoGcException;
429 
430     static struct Foo { int x, y; }
431 
432     [2, 3, 4].toXlOper(theGC).fromXlOper!Foo(theGC).shouldThrowWithMessage!NoGcException(
433         "1D array length must match number of members in Foo. Expected 2, got 3");
434 
435     [2].toXlOper(theGC).fromXlOper!Foo(theGC).shouldThrowWithMessage!NoGcException(
436         "1D array length must match number of members in Foo. Expected 2, got 1");
437 }
438 
439 @("1D array to struct with wrong type")
440 @system unittest {
441     static struct Foo { int x, y; }
442 
443     ["foo", "bar"].toXlOper(theGC).fromXlOper!Foo(theGC).shouldThrowWithMessage(
444         "Wrong type converting oper to Foo");
445 }
446 
447 @("2D horizontal array to struct")
448 unittest {
449     import xlld.memorymanager: allocatorContext;
450 
451     static struct Foo { int x, y, z; }
452 
453     with(allocatorContext(theGC)) {
454         [[any("x"), any("y"), any("z")], [any(2), any(3), any(4)]].toFrom!Foo.shouldEqual(Foo(2, 3, 4));
455     }
456 }
457 
458 @("2D vertical array to struct")
459 unittest {
460     import xlld.memorymanager: allocatorContext;
461 
462     static struct Foo { int x, y, z; }
463 
464     with(allocatorContext(theGC)) {
465         [[any("x"), any(2)], [any("y"), any(3)], [any("z"), any(4)]].toFrom!Foo.shouldEqual(Foo(2, 3, 4));
466     }
467 }
468 
469 
470 @("2D array wrong size")
471 unittest {
472     import xlld.memorymanager: allocatorContext;
473     import nogc: NoGcException;
474 
475     static struct Foo { int x, y, z; }
476 
477     with(allocatorContext(theGC)) {
478         [[any("x"), any(2)], [any("y"), any(3)], [any("z"), any(4)], [any("w"), any(5)]].toFrom!Foo.
479             shouldThrowWithMessage!NoGcException("2D array must be 2x3 or 3x2 for Foo");
480     }
481 }
482 
483 
484 @("PriceBar[]")
485 @system /*allocatorContext*/ unittest {
486 
487     import xlld.memorymanager: allocatorContext;
488 
489     static struct PriceBar {
490         double open, high, low, close;
491     }
492 
493     with(allocatorContext(theGC)) {
494         auto array =
495         [
496             [any("open"), any("high"), any("low"), any("close")],
497             [any(1.1),    any(2.2),    any(3.3),   any(4.4)],
498             [any(2.1),    any(3.2),    any(4.3),   any(5.4)],
499         ];
500 
501         array.toFrom!(PriceBar[]).should == [
502             PriceBar(1.1, 2.2, 3.3, 4.4),
503             PriceBar(2.1, 3.2, 4.3, 5.4),
504         ];
505     }
506 }
507 
508 
509 @("fromXlOperCoerce")
510 unittest {
511     double[][] doubles = [[1, 2, 3, 4], [11, 12, 13, 14]];
512     auto doublesOper = toSRef(doubles, theGC);
513     doublesOper.fromXlOper!(double[][])(theGC).shouldThrowWithMessage(
514         "fromXlOper: oper not of multi type");
515     doublesOper.fromXlOperCoerce!(double[][]).shouldEqual(doubles);
516 }
517 
518 
519 private auto toFrom(R, T)(T val) {
520     import std.experimental.allocator.gc_allocator: GCAllocator;
521     return val.toXlOper(GCAllocator.instance).fromXlOper!R(GCAllocator.instance);
522 }
523 
524 
525 @("fromXlOper!(Tuple!(double, double))")
526 @system unittest {
527     import std.typecons: tuple, Tuple;
528     import xlld.conv.from: fromXlOper;
529     tuple(22.2, 33.3)
530         .toXlOper(theGC)
531         .fromXlOper!(Tuple!(double, double))(theGC)
532         .shouldEqual(tuple(22.2, 33.3));
533 }
534 
535 @("fromXlOper!(Tuple!(int, int, int))")
536 @system unittest {
537     import std.typecons: tuple, Tuple;
538     import xlld.conv.from: fromXlOper;
539     tuple(1, 2, 3)
540         .toXlOper(theGC)
541         .fromXlOper!(Tuple!(int, int, int))(theGC)
542         .shouldEqual(tuple(1, 2, 3));
543 }
544 
545 
546 @("double[].fromXlOper!int[]")
547 @system unittest {
548     [1.0, 2.0]
549         .toXlOper(theGC)
550         .fromXlOper!(int[])(theGC)
551         .should == [1, 2];
552 }
553 
554 
555 @("autoFreeOper")
556 @system unittest {
557     import xlld.conv.to: toAutoFreeOper;
558     [42.0].toAutoFreeOper.fromXlOper!(double[])(theGC).should == [42.0];
559     [42].toAutoFreeOper.fromXlOper!(int[])(theGC).should == [42];
560 }