trial.stackresult 317/337(94%) line coverage

      
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
200
210
220
230
240
250
260
270
280
290
300
310
320
330
340
350
360
370
380
391
401
411
420
431
440
451
460
470
480
490
500
510
520
530
540
550
560
570
580
590
600
610
620
631
641
650
662
670
680
690
700
710
723
730
743
750
763
770
783
790
802
810
820
836
840
851
860
870
883
890
900
910
920
933
940
950
960
970
980
991
1001
1010
1022
1032
1042
1050
1060
1070
1080
1090
1101
1111
1120
1132
1142
1152
1160
1170
1180
1190
1200
1210
1220
1230
1240
1250
1260
1270
1285
1295
1300
1315
1325
1335
134227
1350
13674
1370
1386
1390
1400
14174
1420
1436
1440
1450
14674
1470
1485
1490
15069
1510
1520
1535
1545
1555
1560
1575
1585
1590
1605
1610
1621
1630
1640
1654
1660
1679
1680
1690
1700
1710
1720
1730
1741
1750
1762
1770
1780
1792
1800
1812
1820
1830
1840
1852
1860
1870
1880
1890
1900
1910
1920
1930
1940
1950
1960
1970
1980
1990
2000
2010
2022
2030
20429
2050
20627
20727
20827
2090
2100
2110
2120
2130
2140
2150
21647
21744
2180
2190
2200
2210
2220
2230
2240
2250
2263
2270
228138
2290
23044
2310
2320
2333
2340
2350
2360
2370
2380
2390
2400
2410
2420
2430
2440
2450
2460
2470
2480
2490
2500
2510
2520
2530
2540
2550
2560
2570
2580
2590
2600
2610
2620
2630
2640
2650
2661
2670
2680
2690
2700
2710
2720
2730
2741
2750
2760
2771
2780
2792
2802
2810
2820
2830
2840
2850
2860
2870
2880
2890
2900
2910
2920
2930
2940
2950
2960
2970
2980
2990
3000
3010
3020
3030
3040
3050
3060
3070
3080
3090
3100
3110
3120
3130
3140
3150
3160
3170
3180
3190
3200
3210
3220
3230
3240
3250
3260
3270
3280
3290
3300
3310
3320
3330
3340
3350
3360
3370
3380
3390
3400
3410
3420
3430
3440
3450
3460
3470
3480
3490
3500
3510
3520
3530
3540
3550
3560
3570
3580
3590
3600
3610
3620
3630
36450
3650
3660
3670
36850
3690
37050
3711
3720
3730
37450
375100
3760
37750
3783
3790
3800
38150
3824
3830
3840
38550
38643
3870
38843
38942
3900
3910
39243
3930
3940
39550
3960
3970
3980
3990
4000
4016
4020
4030
4040
4050
4060
4076
4081
4090
4100
4116
41212
4130
4146
4153
4163
4170
4180
4196
4204
4214
4220
4230
4246
4252
4262
4270
4282
4291
4301
4310
4320
4332
4340
4350
4360
4370
4380
4390
4400
4410
4422
4430
4440
4450
4460
4470
4480
4490
4502
4510
4520
4530
4540
4550
4560
4570
4582
4590
4600
4610
4620
4630
4640
4650
4662
4670
4680
4690
4700
4710
4720
4730
4742
4750
4760
4770
4780
4790
4800
4810
4822
4830
4840
4850
4860
4870
4880
4890
4900
4910
4920
49318
4940
4950
4960
49717
4980
4990
5000
5010
5020
5030
5040
5050
5060
5070
5080
5090
5100
5110
5120
5130
5140
5150
5160
5170
5180
5190
5200
5211
5221
5230
5242
5250
5260
5270
5280
5290
5300
5310
5321
5331
5340
5352
5360
5370
5380
5390
5400
5410
5420
5431
5441
5450
5462
5470
5480
5490
5500
5510
5520
5530
5541
5551
5560
5572
5580
5590
5600
5610
5620
5630
5640
5651
5661
5670
5682
5690
5700
5710
5720
5730
5740
5750
5761
5771
5780
5792
5800
5810
5820
5830
5840
5850
5860
5870
5880
5890
5900
5910
5920
5930
5940
5950
5960
5970
59839
5990
60039
6010
6020
60378
6040
60539
60638
6070
6080
6091
6101
6111
6121
6131
6141
6150
6161
6170
6180
6190
6200
6210
62239
6230
62478
6250
6260
62739
62838
6290
6300
6311
6321
6331
6341
6350
6363
6370
6381
6390
6400
6410
6420
6430
64439
6450
64678
6470
64839
64937
6500
6510
6522
6532
6540
6554
6560
6572
6580
6590
6600
6610
6620
66339
6640
66578
6660
66739
66818
6690
6700
67121
67221
67321
6740
67521
6760
67721
6780
6791
6801
6810
6820
68363
68421
68521
6860
6870
6880
6890
6900
69139
6920
69378
6940
69539
69638
6970
6980
6991
7001
7011
7021
7030
7044
7050
7061
7070
7080
7090
7100
71139
7120
71378
7140
71539
7169
7170
7180
71930
72030
72130
72230
7230
724120
7250
72630
7270
7280
7290
7300
73139
7320
73378
7340
73539
73636
7370
7380
7393
7403
7410
7426
7430
7443
7450
7460
7470
7480
7490
75039
75139
75239
7530
75439
7550
7560
7570
7580
7590
7600
7610
7620
7630
7640
765202
7660
7670
7680
7690
7701
7710
7721
7732
7742
7752
7762
7772
7782
7790
7800
7810
7820
7830
7841
7850
7861
7872
7882
7892
7902
7912
7922
7930
7940
7950
7960
7970
7981
7990
8001
8012
8022
8032
8042
8052
8062
8072
8082
8090
8100
8110
8120
8131
8140
8151
8160
8172
8182
8192
8202
8212
8222
8230
8240
8250
8260
8271
8280
8291
8300
8312
8322
8332
8342
8352
8362
8370
8380
8390
8400
8410
8421
8430
8441
8450
8462
8472
8482
8492
8502
8512
8520
8530
8540
8550
8561
8570
8581
8590
8602
8612
8622
8632
8642
8652
8662
8672
8680
8690
8700
8710
8721
8731
8740
8752
8762
8772
8782
8792
8802
8812
8822
8830
8840
8850
8860
8871
8881
8890
8902
8912
8922
8932
8942
8952
8962
8972
8980
8990
9000
9010
9021
9031
9040
9052
9062
9072
9082
9092
9102
9112
9122
9130
9140
9150
9160
9171
9181
9190
9202
9212
9222
9232
9242
9252
9262
9272
9280
9290
9300
9310
9321
9331
9340
9352
9362
9372
9382
9392
9402
9412
9422
9430
/++ A module containing custom exceptions for display convenience Copyright: © 2017 Szabo Bogdan License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. Authors: Szabo Bogdan +/ module trial.stackresult; import std.conv; import std.regex; import std.exception; import std.stdio; import std.string; import std.algorithm; import core.demangle; version (Have_fluent_asserts) { } else { auto toTestException(Throwable t) { return t; } } version (Have_fluent_asserts) { import fluentasserts.core.base; import fluentasserts.core.results; /// class TestExceptionWrapper : TestException { private { TestException exception; IResult[] results; } /// this(TestException exception, IResult[] results, string fileName, size_t line, Throwable next = null) { this.exception = exception; this.results = results; super(results, fileName, line, next); this.msg = exception.msg ~ "\n" ~ this.msg; } /// override void print(ResultPrinter printer) { exception.print(printer); results.each!(a => a.print(printer)); } /// override string toString() { return exception.toString ~ results.map!(a => a.toString).join("\n").to!string; } } /// The message of a wrapped exception should contain the original exception unittest { auto exception = new TestException([ new MessageResult("first message") ], "", 0); auto wrappedException = new TestExceptionWrapper(exception, [ new MessageResult("second message") ], "", 0); wrappedException.msg.should.equal("first message\n\nsecond message\n"); } /// Converts a Throwable to a TestException which improves the failure verbosity TestException toTestException(Throwable t) { auto exception = cast(TestException) t; if (exception is null) { IResult[] results = [cast(IResult) new MessageResult(t.classinfo.name ~ ": " ~ t.msg)]; if (t.file.indexOf("../") == -1) { results ~= cast(IResult) new SourceResult(t.file, t.line); } if (t !is null && t.info !is null) { results ~= cast(IResult) new StackResult(t.info); } exception = new TestException(results, t.file, t.line, t); } else { exception = new TestExceptionWrapper(exception, [ cast(IResult) new StackResult(t.info) ], t.file, t.line, t); } return exception; } @("toTestException should convert an Exception from the current project to a TestException with 2 reporters") unittest { auto exception = new Exception("random text"); auto testException = exception.toTestException; (testException !is null).should.equal(true); testException.toString.should.contain("random text"); testException.toString.should.contain("lifecycle/trial/runner.d"); } @("toTestException should convert an Exception from other project to a TestException with 1 reporter") unittest { auto exception = new Exception("random text", "../file.d"); auto testException = exception.toTestException; (testException !is null).should.equal(true); testException.toString.should.contain("random text"); testException.toString.should.not.contain("lifecycle/trial/runner.d"); } /// A structure that allows you to detect which modules are relevant to display struct ExternalValidator { /// The list of external modules like the standard library or dub dependencies string[] externalModules; /// Check if the provided name comes from an external dependency bool isExternal(const string name) @safe { auto reversed = name.dup; reverse(reversed); string substring = name; int sum = 0; int index = 0; foreach (ch; reversed) { if (ch == ')') { sum++; } if (ch == '(') { sum--; } if (sum == 0) { break; } index++; } auto tmpSubstring = reversed[index .. $]; reverse(tmpSubstring); substring = tmpSubstring.to!string; auto wordEnd = substring.lastIndexOf(' ') + 1; auto chainEnd = substring.lastIndexOf(").") + 1; if (chainEnd > wordEnd) { return isExternal(name[0 .. chainEnd]); } auto functionName = substring[wordEnd .. $]; return !externalModules.filter!(a => functionName.indexOf(a) == 0).empty; } } @("It should detect external functions") unittest { auto validator = ExternalValidator(["selenium.api", "selenium.session"]); validator.isExternal("selenium.api.SeleniumApiConnector selenium.api.SeleniumApiConnector.__ctor()") .should.equal(true); validator.isExternal("void selenium.api.SeleniumApiConnector.__ctor()").should.equal(true); validator.isExternal( "pure @safe bool selenium.api.enforce!(Exception, bool).enforce(bool, lazy const(char)[], immutable(char)[], ulong)") .should.equal(true); validator.isExternal("immutable(immutable(selenium.session.SeleniumSession) function(immutable(char)[], selenium.api.Capabilities, selenium.api.Capabilities, selenium.api.Capabilities)) selenium.session.SeleniumSession.__ctor") .should.equal(true); } /// Used to display the stack class StackResult : IResult { static { /// string[] externalModules; } /// Frame[] frames; /// this(Throwable.TraceInfo t) { foreach (line; t) { auto frame = line.to!string.toFrame; frame.name = demangle(frame.name).to!string; frames ~= frame; } } private { auto getFrames() { return frames.until!(a => a.name.indexOf("generated") != -1) .until!(a => a.name.indexOf("D5trial") != -1); } } override { /// Converts the result to a string string toString() @safe { string result = "Stack trace:\n-------------------\n...\n"; foreach (frame; getFrames) { result ~= frame.toString ~ "\n"; } return result ~ "..."; } /// Prints the stack using the default writer void print(ResultPrinter printer) { int colorIndex = 0; printer.primary("Stack trace:\n-------------------\n...\n"); auto validator = ExternalValidator(externalModules); foreach (frame; getFrames) { if (validator.isExternal(frame.name)) { printer.primary(frame.toString); } else { frame.print(printer); } printer.primary("\n"); } printer.primary("..."); } } } /// The stack result should display the stack in a readable form unittest { Throwable exception; try { assert(false, "random message"); } catch (Throwable t) { exception = t; } auto result = new StackResult(exception.info).toString; result.should.startWith("Stack trace:\n-------------------\n..."); result.should.endWith("\n..."); } } else { /// Used to display the stack class StackResult { static { /// string[] externalModules; } /// Frame[] frames; /// this(Throwable.TraceInfo t) { foreach (line; t) { auto frame = line.to!string.toFrame; frame.name = demangle(frame.name).to!string; frames ~= frame; } } private { auto getFrames() { return frames.until!(a => a.name.indexOf("generated") != -1) .until!(a => a.name.indexOf("D5trial") != -1); } } override { /// Converts the result to a string string toString() @safe { string result = "Stack trace:\n-------------------\n...\n"; foreach (frame; getFrames) { result ~= frame.toString ~ "\n"; } return result ~ "..."; } } } } /// Represents a stack frame struct Frame { /// int index = -1; /// string moduleName; /// string address; /// string name; /// string offset; /// string file; /// int line = -1; /// bool invalid = true; /// string raw; string toString() const @safe { if(raw != "") { return raw; } string result; if(index >= 0) { result ~= leftJustifier(index.to!string, 4).to!string; } result ~= address ~ " "; result ~= name == "" ? "????" : name; if(moduleName != "") { result ~= " at " ~ moduleName; } if(offset != "") { result ~= " + " ~ offset; } if(file != "") { result ~= " (" ~ file; if(line > 0) { result ~= ":" ~ line.to!string; } result ~= ")"; } return result; } version(Have_fluent_asserts) { void print(ResultPrinter printer) @safe { if(raw != "") { printer.primary(raw); return; } if(index >= 0) { printer.info(leftJustifier(index.to!string, 4).to!string); } printer.primary(address ~ " "); printer.info(name == "" ? "????" : name); if(moduleName != "") { printer.primary(" at "); printer.info(moduleName); } if(offset != "") { printer.primary(" + "); printer.info(offset); } if(file != "") { printer.primary(" ("); printer.info(file); if(line > 0) { printer.primary(":"); printer.info(line.to!string); } printer.primary(")"); } } } } /// The frame should convert a frame to string unittest { Frame(10, "some.module", "0xffffff", "name", "offset", "file.d", 120).toString.should.equal( `10 0xffffff name at some.module + offset (file.d:120)` ); } /// The frame should not output an index < 0 or a line < 0 unittest { Frame(-1, "some.module", "0xffffff", "name", "offset", "file.d", -1).toString.should.equal( `0xffffff name at some.module + offset (file.d)` ); } /// The frame should not output the file if it is missing from the stack unittest { Frame(-1, "some.module", "0xffffff", "name", "offset", "", 10).toString.should.equal( `0xffffff name at some.module + offset` ); } /// The frame should not output the module if it is missing from the stack unittest { Frame(-1, "", "0xffffff", "name", "offset", "", 10).toString.should.equal( `0xffffff name + offset` ); } /// The frame should not output the offset if it is missing from the stack unittest { Frame(-1, "", "0xffffff", "name", "", "", 10).toString.should.equal( `0xffffff name` ); } /// The frame should display `????` when the name is missing unittest { Frame(-1, "", "0xffffff", "", "", "", 10).toString.should.equal( `0xffffff ????` ); } version(unittest) { version(Have_fluent_asserts): class MockPrinter : ResultPrinter { string buffer; void primary(string val) { buffer ~= val; } void info(string val) { buffer ~= "[info:" ~ val ~ "]"; } void danger(string val) { buffer ~= "[danger:" ~ val ~ "]"; } void success(string val) { buffer ~= "[success:" ~ val ~ "]"; } void dangerReverse(string val) { buffer ~= "[dangerReverse:" ~ val ~ "]"; } void successReverse(string val) { buffer ~= "[successReverse:" ~ val ~ "]"; } } } /// The frame should print all fields unittest { auto printer = new MockPrinter; Frame(10, "some.module", "0xffffff", "name", "offset", "file.d", 120).print(printer); printer.buffer.should.equal( `[info:10 ]0xffffff [info:name] at [info:some.module] + [info:offset] ([info:file.d]:[info:120])` ); } /// The frame should not print an index < 0 or a line < 0 unittest { auto printer = new MockPrinter; Frame(-1, "some.module", "0xffffff", "name", "offset", "file.d", -1).print(printer); printer.buffer.should.equal( `0xffffff [info:name] at [info:some.module] + [info:offset] ([info:file.d])` ); } /// The frame should not print the file if it's missing unittest { auto printer = new MockPrinter; Frame(-1, "some.module", "0xffffff", "name", "offset", "", 10).print(printer); printer.buffer.should.equal( `0xffffff [info:name] at [info:some.module] + [info:offset]` ); } /// The frame should not print the module if it's missing unittest { auto printer = new MockPrinter; Frame(-1, "", "0xffffff", "name", "offset", "", 10).print(printer); printer.buffer.should.equal( `0xffffff [info:name] + [info:offset]` ); } /// The frame should not print the offset if it's missing unittest { auto printer = new MockPrinter; Frame(-1, "", "0xffffff", "name", "", "", 10).print(printer); printer.buffer.should.equal( `0xffffff [info:name]` ); } /// The frame should print ???? when the name is missing unittest { auto printer = new MockPrinter; Frame(-1, "", "0xffffff", "", "", "", 10).print(printer); printer.buffer.should.equal( `0xffffff [info:????]` ); } immutable static { string index = `(?P<index>[0-9]+)`; string moduleName = `(?P<module>\S+)`; string address = `(?P<address>0x[0-9a-fA-F]+)`; string name = `(?P<name>.+)`; string offset = `(?P<offset>(0x[0-9A-Za-z]+)|([0-9]+))`; string file = `(?P<file>.+)`; string linePattern = `(?P<line>[0-9]+)`; } /// Parse a MacOS string frame Frame toDarwinFrame(string line) { Frame frame; auto darwinPattern = index ~ `(\s+)` ~ moduleName ~ `(\s+)` ~ address ~ `(\s+)` ~ name ~ `\s\+\s` ~ offset; auto matched = matchFirst(line, darwinPattern); if(matched.length < 5) { return frame; } frame.invalid = false; frame.index = matched["index"].to!int; frame.moduleName = matched["module"]; frame.address = matched["address"]; frame.name = matched["name"]; frame.offset = matched["offset"]; return frame; } /// Parse a Windows string frame Frame toWindows1Frame(string line) { Frame frame; auto matched = matchFirst(line, address ~ `(\s+)in(\s+)` ~ name ~ `(\s+)at(\s+)` ~ file ~ `\(` ~ linePattern ~ `\)`); // ~ ); if(matched.length < 4) { return frame; } frame.address = matched["address"]; frame.name = matched["name"]; frame.file = matched["file"]; frame.line = matched["line"].to!int; frame.invalid = frame.address == "" || frame.name == "" || frame.file == ""; return frame; } /// ditto Frame toWindows2Frame(string line) { Frame frame; auto matched = matchFirst(line, address ~ `(\s+)in(\s+)` ~ name); if(matched.length < 2) { return frame; } frame.address = matched["address"]; frame.name = matched["name"]; frame.invalid = frame.address == "" || frame.name == ""; return frame; } /// Parse a GLibC string frame Frame toGLibCFrame(string line) { Frame frame; auto matched = matchFirst(line, moduleName ~ `\(` ~ name ~ `\)\s+\[` ~ address ~ `\]`); if(matched.length < 3) { return frame; } frame.address = matched["address"]; frame.name = matched["name"]; frame.moduleName = matched["module"]; auto plusSign = frame.name.indexOf("+"); if (plusSign != -1) { frame.offset = frame.name[plusSign + 1 .. $]; frame.name = frame.name[0 .. plusSign]; } frame.invalid = frame.address == "" || frame.name == "" || frame.moduleName == "" || frame.name.indexOf("(") >= 0; return frame; } /// Parse a NetBsd string frame Frame toNetBsdFrame(string line) { Frame frame; auto matched = matchFirst(line, address ~ `\s+<` ~ name ~ `\+` ~ offset ~ `>\s+at\s+` ~ moduleName); if(matched.length < 4) { return frame; } frame.address = matched["address"]; frame.name = matched["name"]; frame.moduleName = matched["module"]; frame.offset = matched["offset"]; frame.invalid = frame.address == "" || frame.name == "" || frame.moduleName == "" || frame.offset == ""; return frame; } /// Parse a Linux frame Frame toLinuxFrame(string line) { Frame frame; auto matched = matchFirst(line, file ~ `:` ~ linePattern ~ `\s+` ~ name ~ `\s+\[` ~ address ~ `\]`); if(matched.length < 4) { return frame; } frame.file = matched["file"]; frame.name = matched["name"]; frame.address = matched["address"]; frame.line = matched["line"].to!int; frame.invalid = frame.address == "" || frame.name == "" || frame.file == "" || frame.line == 0; return frame; } /// Parse a Linux frame Frame toMissingInfoLinuxFrame(string line) { Frame frame; auto matched = matchFirst(line, `\?\?:\?\s+` ~ name ~ `\s+\[` ~ address ~ `\]`); if(matched.length < 2) { return frame; } frame.name = matched["name"]; frame.address = matched["address"]; frame.invalid = frame.address == "" || frame.name == ""; return frame; } /// Converts a stack trace line to a Frame structure Frame toFrame(string line) { Frame frame; frame.raw = line; frame.invalid = false; auto frames = [ line.toDarwinFrame, line.toWindows1Frame, line.toWindows2Frame, line.toLinuxFrame, line.toGLibCFrame, line.toNetBsdFrame, line.toMissingInfoLinuxFrame, frame ]; return frames.filter!(a => !a.invalid).front; } @("Get frame info from Darwin platform format") unittest { auto line = "1 ???fluent-asserts 0x00abcdef000000 D6module4funcAFZv + 0"; auto frame = line.toFrame; frame.invalid.should.equal(false); frame.index.should.equal(1); frame.moduleName.should.equal("???fluent-asserts"); frame.address.should.equal("0x00abcdef000000"); frame.name.should.equal("D6module4funcAFZv"); frame.offset.should.equal("0"); } @("Get frame info from windows platform format without path") unittest { auto line = "0x779CAB5A in RtlInitializeExceptionChain"; auto frame = line.toFrame; frame.invalid.should.equal(false); frame.index.should.equal(-1); frame.moduleName.should.equal(""); frame.address.should.equal("0x779CAB5A"); frame.name.should.equal("RtlInitializeExceptionChain"); frame.offset.should.equal(""); } @("Get frame info from windows platform format with path") unittest { auto line = `0x00402669 in void app.__unitestL82_8() at D:\tidynumbers\source\app.d(84)`; auto frame = line.toFrame; frame.invalid.should.equal(false); frame.index.should.equal(-1); frame.moduleName.should.equal(""); frame.address.should.equal("0x00402669"); frame.name.should.equal("void app.__unitestL82_8()"); frame.file.should.equal(`D:\tidynumbers\source\app.d`); frame.line.should.equal(84); frame.offset.should.equal(""); } @("Get frame info from CRuntime_Glibc format without offset") unittest { auto line = `module(_D6module4funcAFZv) [0x00000000]`; auto frame = line.toFrame; frame.invalid.should.equal(false); frame.moduleName.should.equal("module"); frame.name.should.equal("_D6module4funcAFZv"); frame.address.should.equal("0x00000000"); frame.index.should.equal(-1); frame.offset.should.equal(""); } @("Get frame info from CRuntime_Glibc format with offset") unittest { auto line = `module(_D6module4funcAFZv+0x78) [0x00000000]`; auto frame = line.toFrame; frame.invalid.should.equal(false); frame.moduleName.should.equal("module"); frame.name.should.equal("_D6module4funcAFZv"); frame.address.should.equal("0x00000000"); frame.index.should.equal(-1); frame.offset.should.equal("0x78"); } @("Get frame info from NetBSD format") unittest { auto line = `0x00000000 <_D6module4funcAFZv+0x78> at module`; auto frame = line.toFrame; frame.invalid.should.equal(false); frame.moduleName.should.equal("module"); frame.name.should.equal("_D6module4funcAFZv"); frame.address.should.equal("0x00000000"); frame.index.should.equal(-1); frame.offset.should.equal("0x78"); } /// Get the main frame info from linux format unittest { auto line = `generated.d:45 _Dmain [0x8e80c4]`; auto frame = line.toFrame; frame.invalid.should.equal(false); frame.moduleName.should.equal(""); frame.file.should.equal("generated.d"); frame.line.should.equal(45); frame.name.should.equal("_Dmain"); frame.address.should.equal("0x8e80c4"); frame.index.should.equal(-1); frame.offset.should.equal(""); } /// Get a function frame info from linux format unittest { auto line = `lifecycle/trial/runner.d:106 trial.interfaces.SuiteResult[] trial.runner.runTests(const(trial.interfaces.TestCase)[], immutable(char)[]) [0x8b0ec1]`; auto frame = line.toFrame; frame.invalid.should.equal(false); frame.moduleName.should.equal(""); frame.file.should.equal("lifecycle/trial/runner.d"); frame.line.should.equal(106); frame.name.should.equal("trial.interfaces.SuiteResult[] trial.runner.runTests(const(trial.interfaces.TestCase)[], immutable(char)[])"); frame.address.should.equal("0x8b0ec1"); frame.index.should.equal(-1); frame.offset.should.equal(""); } /// Get an external function frame info from linux format unittest { auto line = `../../.dub/packages/fluent-asserts-0.6.6/fluent-asserts/core/fluentasserts/core/base.d:39 void fluentasserts.core.base.Result.perform() [0x8f4b47]`; auto frame = line.toFrame; frame.invalid.should.equal(false); frame.moduleName.should.equal(""); frame.file.should.equal("../../.dub/packages/fluent-asserts-0.6.6/fluent-asserts/core/fluentasserts/core/base.d"); frame.line.should.equal(39); frame.name.should.equal("void fluentasserts.core.base.Result.perform()"); frame.address.should.equal("0x8f4b47"); frame.index.should.equal(-1); frame.offset.should.equal(""); } /// Get an external function frame info from linux format unittest { auto line = `lifecycle/trial/discovery/unit.d:268 _D5trial9discovery4unit17UnitTestDiscovery231__T12addTestCasesVAyaa62_2f686f6d652f626f737a2f776f726b73706163652f64746573742f6c6966656379636c652f747269616c2f6578656375746f722f706172616c6c656c2e64VAyaa23_747269616c2e6578656375746f722e706172616c6c656cS245trial8executor8parallelZ12addTestCasesMFZ9__lambda4FZv [0x872000]`; auto frame = line.toFrame; frame.invalid.should.equal(false); frame.moduleName.should.equal(""); frame.file.should.equal("lifecycle/trial/discovery/unit.d"); frame.line.should.equal(268); frame.name.should.equal("_D5trial9discovery4unit17UnitTestDiscovery231__T12addTestCasesVAyaa62_2f686f6d652f626f737a2f776f726b73706163652f64746573742f6c6966656379636c652f747269616c2f6578656375746f722f706172616c6c656c2e64VAyaa23_747269616c2e6578656375746f722e706172616c6c656cS245trial8executor8parallelZ12addTestCasesMFZ9__lambda4FZv"); frame.address.should.equal("0x872000"); frame.index.should.equal(-1); frame.offset.should.equal(""); } /// Get an internal function frame info from linux format unittest { auto line = `../../../../../fluent-asserts/source/fluentasserts/core/operations/arrayEqual.d:26 nothrow @safe fluentasserts.core.results.IResult[] fluentasserts.core.operations.arrayEqual.arrayEqual(ref fluentasserts.core.evaluation.Evaluation) [0x27a57ce]`; auto frame = line.toFrame; frame.invalid.should.equal(false); frame.moduleName.should.equal(""); frame.file.should.equal("../../../../../fluent-asserts/source/fluentasserts/core/operations/arrayEqual.d"); frame.line.should.equal(26); frame.name.should.equal("nothrow @safe fluentasserts.core.results.IResult[] fluentasserts.core.operations.arrayEqual.arrayEqual(ref fluentasserts.core.evaluation.Evaluation)"); frame.address.should.equal("0x27a57ce"); frame.index.should.equal(-1); frame.offset.should.equal(""); } /// Get an missing info function frame info from linux format unittest { auto line = `??:? __libc_start_main [0x174bbf44]`; auto frame = line.toFrame; frame.invalid.should.equal(false); frame.moduleName.should.equal(""); frame.file.should.equal(""); frame.line.should.equal(-1); frame.name.should.equal("__libc_start_main"); frame.address.should.equal("0x174bbf44"); frame.index.should.equal(-1); frame.offset.should.equal(""); }