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