trial.stackresult 308/328(93%) 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
2042
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
2701
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
3640
36550
3660
3670
3680
36950
3700
37150
3721
3730
3740
37550
376100
3770
37850
3798
3800
3810
38250
3834
3840
3850
38650
3872
3880
3892
3901
3910
3920
3932
3940
3950
39650
3970
3980
3990
4000
4010
4026
4030
4040
4050
4060
4070
4086
4091
4100
4110
4126
41312
4140
4156
4163
4173
4180
4190
4206
4214
4224
4230
4240
4256
4262
4272
4280
4292
4301
4311
4320
4330
4342
4350
4360
4370
4380
4390
4400
4410
4420
4432
4440
4450
4460
4470
4480
4490
4500
4512
4520
4530
4540
4550
4560
4570
4580
4592
4600
4610
4620
4630
4640
4650
4660
4672
4680
4690
4700
4710
4720
4730
4740
4752
4760
4770
4780
4790
4800
4810
4820
4832
4840
4850
4860
4870
4880
4890
4900
4910
4920
4930
49418
4950
4960
4970
49817
4990
5000
5010
5020
5030
5040
5050
5060
5070
5080
5090
5100
5110
5120
5130
5140
5150
5160
5170
5180
5190
5200
5210
5221
5231
5240
5252
5260
5270
5280
5290
5300
5310
5320
5331
5341
5350
5362
5370
5380
5390
5400
5410
5420
5430
5441
5451
5460
5472
5480
5490
5500
5510
5520
5530
5540
5551
5561
5570
5582
5590
5600
5610
5620
5630
5640
5650
5661
5671
5680
5692
5700
5710
5720
5730
5740
5750
5760
5771
5781
5790
5802
5810
5820
5830
5840
5850
5860
5870
5880
5890
5900
5910
5920
5930
5940
5950
5960
5970
5980
59938
6000
60138
6020
6030
60476
6050
60638
60737
6080
6090
6101
6111
6121
6131
6141
6151
6160
6171
6180
6190
6200
6210
6220
62338
6240
62576
6260
6270
62838
62937
6300
6310
6321
6331
6341
6351
6360
6373
6380
6391
6400
6410
6420
6430
6440
64538
6460
64776
6480
64938
65036
6510
6520
6532
6542
6550
6564
6570
6582
6590
6600
6610
6620
6630
66438
6650
66676
6670
66838
66915
6700
6710
67223
67323
67423
6750
67623
6770
67823
6790
6801
6811
6820
6830
68469
68523
6860
68723
6880
6890
6900
6910
6920
69338
6940
69576
6960
69738
69837
6990
7000
7011
7021
7031
7041
7050
7064
7070
7081
7090
7100
7110
7120
71338
7140
71576
7160
71738
71834
7190
7200
7214
7224
7234
7244
7250
72616
7270
7284
7290
7300
7310
7320
73338
7340
73576
7360
73738
73810
7390
7400
74128
74228
7430
74456
7450
74628
7470
7480
7490
7500
7510
75238
75338
75438
7550
75638
7570
7580
7590
7600
7610
7620
7630
7640
7650
7660
767268
7680
7690
7700
7710
7720
7731
7740
7751
7762
7772
7782
7792
7802
7812
7820
7830
7840
7850
7860
7871
7880
7891
7902
7912
7922
7932
7942
7952
7960
7970
7980
7990
8000
8011
8020
8031
8042
8052
8062
8072
8082
8092
8102
8112
8120
8130
8140
8150
8160
8171
8180
8191
8200
8212
8222
8232
8242
8252
8262
8270
8280
8290
8300
8310
8321
8330
8341
8350
8362
8372
8382
8392
8402
8412
8420
8430
8440
8450
8460
8471
8480
8491
8500
8512
8522
8532
8542
8552
8562
8570
8580
8590
8600
8611
8620
8631
8640
8652
8662
8672
8682
8692
8702
8712
8722
8730
8740
8750
8760
8771
8781
8790
8802
8812
8822
8832
8842
8852
8862
8872
8880
8890
8900
8910
8921
8931
8940
8952
8962
8972
8982
8992
9002
9012
9022
9030
9040
9050
9060
9071
9081
9090
9102
9112
9122
9132
9142
9152
9162
9172
9180
9190
9200
9210
9221
9231
9240
9252
9262
9272
9282
9292
9302
9312
9322
9330
/++ 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.toGLibCFrame, line.toNetBsdFrame, line.toLinuxFrame, 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 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(""); }