fluentasserts.core.array 250/250(100%) 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
29326
300
310
320
330
340
350
360
370
380
390
400
410
420
430
440
45163
46163
47163
48163
490
500
510
520
53584
540
558538
561779
571080
580
590
600
61114
620
630
640
650
660
670
68159
690
70159
710
722115
73546
740
75546
76132
770
78414
790
800
810
82159
830
840
850
86159
870
88159
890
901830
91451
920
93451
9437
950
96414
970
980
990
100159
1010
1020
1030
104159
1050
106159
1070
1081902
109515
11060
1110
1120
113455
1140
115455
116417
117417
1180
1190
1200
121159
1220
1230
1240
1250
1260
1271
1280
1291
1300
1311
1321
1331
1341
1350
1360
1370
1380
1391
1400
1411
1420
1431
1441
1450
1460
1470
1480
1491
1500
1511
1520
1531
1541
1551
1561
1570
1580
1590
1600
1611
1620
1631
1640
1651
1661
1670
1680
1690
1700
1711
1720
1731
1740
1751
1761
1771
1780
1790
1800
1810
1821
1830
1841
1850
1861
1871
1881
1890
1900
1910
1920
1930
1940
1950
1960
1970
1980
1990
2000
2010
2020
2030
2040
2050
2060
2070
2080
2090
2100
2110
2120
2130
2140
2150
2160
2170
2180
2190
2200
2210
2220
2230
2240
2250
2260
2270
2280
2290
2300
2310
2320
2330
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
2660
2670
2680
2690
2700
2710
2720
2730
2740
2750
2760
2770
2780
2790
2800
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
3650
3660
3670
3680
3690
3700
3710
3720
3730
3740
3750
3760
3770
3780
3790
3800
3810
3820
3830
3840
3850
3860
3870
3880
3890
3900
3910
3920
3930
3940
3950
3960
3970
3980
3990
4000
4010
4020
4030
4040
4050
4060
4070
4080
4090
4100
4110
4120
4130
4140
4150
4164
4170
4180
4192
4202
4210
4220
4232
4242
4250
4260
4272
4282
4290
4300
4312
4322
4330
4340
4350
4360
4370
4382
4392
4402
4410
4420
4432
4442
4450
4460
4473
4482
4490
4500
4512
4520
4533
4542
4550
4560
4572
4580
4593
4602
4610
4620
4632
4640
4650
4660
4670
4681
4692
4702
4712
4720
4732
4742
4750
4760
4770
4780
4790
4801
4812
4822
4832
4840
4852
4862
4870
4880
4890
4900
4910
4922
4932
4942
4950
4962
4972
4980
4992
5002
5010
5020
5033
5042
5050
5060
5072
5080
5093
5102
5110
5120
5132
5140
5153
5162
5170
5180
5192
5200
5213
5222
5230
5240
5252
5260
5273
5282
5290
5300
5312
5320
5330
5340
5350
5361
5372
5380
5390
5400
5410
5420
5431
5442
5452
5462
5470
5482
5492
5500
5510
5520
5530
5540
5551
5562
5572
5582
5590
5602
5612
5620
5630
5640
5650
5660
5672
5682
5692
5700
5712
5720
5730
5743
5752
5760
5770
5782
5790
5803
5812
5820
5830
5842
5850
5863
5872
5880
5890
5902
5910
5923
5932
5940
5950
5962
5970
5983
5992
6000
6010
6022
6030
6040
6050
6060
6072
6082
6090
6100
6112
6122
6132
6142
6150
6160
6173
6182
6190
6200
6212
6220
6233
6242
6250
6260
6272
6280
6293
6302
6310
6320
6332
6340
6353
6362
6370
6380
6392
6400
6410
6420
6430
6440
6450
6460
6470
6480
6490
6502
6512
6520
6530
6542
6552
6560
6570
6582
6590
6600
6610
6620
6631
6641
6650
6662
6672
6680
6692
6702
6710
6720
6730
6740
6750
6760
6776
6780
6790
6800
6810
6820
6830
6840
6852
6861
6872
6880
6890
6903
6912
6920
6930
6940
6950
6960
6972
6982
6990
7000
7012
7022
7032
7042
7050
7060
7073
7082
7090
7100
7112
7120
7133
7142
7150
7160
7172
7180
7193
7202
7210
7220
7232
7240
7253
7262
7270
7280
7292
7300
7310
7320
7330
7340
7350
7360
73718
7380
7390
74018
7410
7420
74324
7440
7450
7460
7472
7482
7492
7500
7512
7522
7530
7540
7552
7560
7572
7582
7590
7600
7612
7620
7632
7642
7650
7660
7672
7680
7690
7700
7710
7720
7730
7740
7759
7760
7770
7780
7799
7800
7810
7820
78312
7840
7850
7860
7872
7882
7890
7900
7910
7920
7930
7940
7950
7969
7970
7980
7990
8009
8010
8020
8030
80412
8050
8060
8070
8082
8092
8100
8110
8120
8130
8142
8150
8162
8172
8182
8192
8200
8213
8222
8230
8240
8252
8262
8270
8280
8290
8300
8311
8321
8330
8340
8350
8360
8371
8380
8392
8400
8410
8420
8430
8440
8451
8461
8472
8480
module fluentasserts.core.array; import fluentasserts.core.results; public import fluentasserts.core.base; import std.algorithm; import std.conv; import std.traits; import std.range; import std.array; import std.string; import std.math; U[] toValueList(U, V)(V expectedValueList) @trusted { static if(is(V == void[])) { return []; } else static if(is(U == immutable) || is(U == const)) { static if(is(U == class)) { return expectedValueList.array; } else { return expectedValueList.array.idup; } } else { static if(is(U == class)) { return cast(U[]) expectedValueList.array; } else { return cast(U[]) expectedValueList.array.dup; } } } @trusted: struct ListComparison(Type) { alias T = Unqual!Type; private { T[] referenceList; T[] list; double maxRelDiff; } this(U, V)(U reference, V list, double maxRelDiff = 0) { this.referenceList = toValueList!T(reference); this.list = toValueList!T(list); this.maxRelDiff = maxRelDiff; } private long findIndex(T[] list, T element) { static if(std.traits.isNumeric!(T)) { return list.countUntil!(a => approxEqual(element, a, maxRelDiff)); } else static if(is(T == EquableValue)) { foreach(index, a; list) { if(a.isEqualTo(element)) { return index; } } return -1; } else { return list.countUntil(element); } } T[] missing() @trusted { T[] result; auto tmpList = list.dup; foreach(element; referenceList) { auto index = this.findIndex(tmpList, element); if(index == -1) { result ~= element; } else { tmpList = remove(tmpList, index); } } return result; } T[] extra() @trusted { T[] result; auto tmpReferenceList = referenceList.dup; foreach(element; list) { auto index = this.findIndex(tmpReferenceList, element); if(index == -1) { result ~= element; } else { tmpReferenceList = remove(tmpReferenceList, index); } } return result; } T[] common() @trusted { T[] result; auto tmpList = list.dup; foreach(element; referenceList) { if(tmpList.length == 0) { break; } auto index = this.findIndex(tmpList, element); if(index >= 0) { result ~= element; tmpList = std.algorithm.remove(tmpList, index); } } return result; } } /// ListComparison should be able to get the missing elements unittest { auto comparison = ListComparison!int([1, 2, 3], [4]); auto missing = comparison.missing; assert(missing.length == 3); assert(missing[0] == 1); assert(missing[1] == 2); assert(missing[2] == 3); } /// ListComparison should be able to get the missing elements with duplicates unittest { auto comparison = ListComparison!int([2, 2], [2]); auto missing = comparison.missing; assert(missing.length == 1); assert(missing[0] == 2); } /// ListComparison should be able to get the extra elements unittest { auto comparison = ListComparison!int([4], [1, 2, 3]); auto extra = comparison.extra; assert(extra.length == 3); assert(extra[0] == 1); assert(extra[1] == 2); assert(extra[2] == 3); } /// ListComparison should be able to get the extra elements with duplicates unittest { auto comparison = ListComparison!int([2], [2, 2]); auto extra = comparison.extra; assert(extra.length == 1); assert(extra[0] == 2); } /// ListComparison should be able to get the common elements unittest { auto comparison = ListComparison!int([1, 2, 3, 4], [2, 3]); auto common = comparison.common; assert(common.length == 2); assert(common[0] == 2); assert(common[1] == 3); } /// ListComparison should be able to get the common elements with duplicates unittest { auto comparison = ListComparison!int([2, 2, 2, 2], [2, 2]); auto common = comparison.common; assert(common.length == 2); assert(common[0] == 2); assert(common[1] == 2); } @safe: struct ShouldList(T) if(isInputRange!(T)) { private T testData; alias U = Unqual!(ElementType!T); mixin ShouldCommons; mixin DisabledShouldThrowableCommons; auto equal(V)(V expectedValueList, const string file = __FILE__, const size_t line = __LINE__) @trusted { auto valueList = toValueList!(Unqual!U)(expectedValueList); addMessage(" equal"); addMessage(" `"); addValue(valueList.to!string); addMessage("`"); beginCheck; return approximately(expectedValueList, 0, file, line); } auto approximately(V)(V expectedValueList, double maxRelDiff = 1e-05, const string file = __FILE__, const size_t line = __LINE__) @trusted { import fluentasserts.core.basetype; auto valueList = toValueList!(Unqual!U)(expectedValueList); addMessage(" approximately"); addMessage(" `"); addValue(valueList.to!string); addMessage("`"); beginCheck; auto comparison = ListComparison!U(valueList, testData.array, maxRelDiff); auto missing = comparison.missing; auto extra = comparison.extra; auto common = comparison.common; auto arrayTestData = testData.array; auto strArrayTestData = "[" ~ testData.map!(a => (cast()a).to!string).join(", ") ~ "]"; static if(std.traits.isNumeric!(U)) { string strValueList; if(maxRelDiff == 0) { strValueList = valueList.to!string; } else { strValueList = "[" ~ valueList.map!(a => a.to!string ~ "±" ~ maxRelDiff.to!string).join(", ") ~ "]"; } } else { auto strValueList = valueList.to!string; } static if(std.traits.isNumeric!(U)) { string strMissing; if(maxRelDiff == 0 || missing.length == 0) { strMissing = missing.length == 0 ? "" : missing.to!string; } else { strMissing = "[" ~ missing.map!(a => a.to!string ~ "±" ~ maxRelDiff.to!string).join(", ") ~ "]"; } } else { string strMissing = missing.length == 0 ? "" : missing.to!string; } bool allEqual = valueList.length == arrayTestData.length; foreach(i; 0..valueList.length) { static if(std.traits.isNumeric!(U)) { allEqual = allEqual && approxEqual(valueList[i], arrayTestData[i], maxRelDiff); } else { allEqual = allEqual && (valueList[i] == arrayTestData[i]); } } if(expectedValue) { return result(allEqual, [], [ cast(IResult) new ExpectedActualResult(strValueList, strArrayTestData), cast(IResult) new ExtraMissingResult(extra.length == 0 ? "" : extra.to!string, strMissing) ], file, line); } else { return result(allEqual, [], [ cast(IResult) new ExpectedActualResult("not " ~ strValueList, strArrayTestData), cast(IResult) new ExtraMissingResult(extra.length == 0 ? "" : extra.to!string, strMissing) ], file, line); } } auto containOnly(V)(V expectedValueList, const string file = __FILE__, const size_t line = __LINE__) @trusted { auto valueList = toValueList!(Unqual!U)(expectedValueList); addMessage(" contain only "); addValue(valueList.to!string); beginCheck; auto comparison = ListComparison!U(testData.array, valueList); auto missing = comparison.missing; auto extra = comparison.extra; auto common = comparison.common; string missingString; string extraString; bool isSuccess; string expected; if(expectedValue) { isSuccess = missing.length == 0 && extra.length == 0 && common.length == valueList.length; if(extra.length > 0) { missingString = extra.to!string; } if(missing.length > 0) { extraString = missing.to!string; } } else { isSuccess = (missing.length != 0 || extra.length != 0) || common.length != valueList.length; isSuccess = !isSuccess; if(common.length > 0) { extraString = common.to!string; } } return result(isSuccess, [], [ cast(IResult) new ExpectedActualResult("", testData.to!string), cast(IResult) new ExtraMissingResult(extraString, missingString) ], file, line); } auto contain(V)(V expectedValueList, const string file = __FILE__, const size_t line = __LINE__) @trusted { auto valueList = toValueList!(Unqual!U)(expectedValueList); addMessage(" contain "); addValue(valueList.to!string); beginCheck; auto comparison = ListComparison!U(testData.array, valueList); auto missing = comparison.missing; auto extra = comparison.extra; auto common = comparison.common; ulong[size_t] indexes; foreach(value; testData) { auto index = valueList.countUntil(value); if(index != -1) { indexes[index]++; } } auto found = indexes.keys.map!(a => valueList[a]).array; auto notFound = iota(0, valueList.length).filter!(a => !indexes.keys.canFind(a)).map!(a => valueList[a]).array; auto arePresent = indexes.keys.length == valueList.length; if(expectedValue) { string isString = notFound.length == 1 ? "is" : "are"; return result(arePresent, [ Message(true, notFound.to!string), Message(false, " " ~ isString ~ " missing from "), Message(true, testData.to!string), Message(false, ".") ], [ cast(IResult) new ExpectedActualResult("all of " ~ valueList.to!string, testData.to!string), cast(IResult) new ExtraMissingResult("", notFound.to!string) ], file, line); } else { string isString = found.length == 1 ? "is" : "are"; return result(common.length != 0, [ Message(true, common.to!string), Message(false, " " ~ isString ~ " present in "), Message(true, testData.to!string), Message(false, ".") ], [ cast(IResult) new ExpectedActualResult("none of " ~ valueList.to!string, testData.to!string), cast(IResult) new ExtraMissingResult(common.to!string, "") ], file, line); } } auto contain(U value, const string file = __FILE__, const size_t line = __LINE__) @trusted { addMessage(" contain `"); addValue(value.to!string); addMessage("`"); auto strValue = value.to!string; auto strTestData = "[" ~ testData.map!(a => (cast()a).to!string).join(", ") ~ "]"; beginCheck; auto isPresent = testData.canFind(value); auto msg = [ Message(true, strValue), Message(false, isPresent ? " is present in " : " is missing from "), Message(true, strTestData), Message(false, ".") ]; if(expectedValue) { return result(isPresent, msg, [ cast(IResult) new ExpectedActualResult("to contain `" ~ strValue ~ "`", strTestData), cast(IResult) new ExtraMissingResult("", value.to!string) ], file, line); } else { return result(isPresent, msg, [ cast(IResult) new ExpectedActualResult("to not contain `" ~ strValue ~ "`", strTestData), cast(IResult) new ExtraMissingResult(value.to!string, "") ], file, line); } } } /// When there is a lazy array that throws an it should throw that exception unittest { int[] someLazyArray() { throw new Exception("This is it."); } ({ someLazyArray.should.equal([]); }).should.throwAnyException.withMessage("This is it."); ({ someLazyArray.should.approximately([], 3); }).should.throwAnyException.withMessage("This is it."); ({ someLazyArray.should.contain([]); }).should.throwAnyException.withMessage("This is it."); ({ someLazyArray.should.contain(3); }).should.throwAnyException.withMessage("This is it."); } @("range contain") unittest { ({ [1, 2, 3].map!"a".should.contain([2, 1]); [1, 2, 3].map!"a".should.not.contain([4, 5, 6, 7]); }).should.not.throwException!TestException; ({ [1, 2, 3].map!"a".should.contain(1); }).should.not.throwException!TestException; auto msg = ({ [1, 2, 3].map!"a".should.contain([4, 5]); }).should.throwException!TestException.msg; msg.split('\n')[0].should.equal("[1, 2, 3].map!\"a\" should contain [4, 5]. [4, 5] are missing from [1, 2, 3]."); msg = ({ [1, 2, 3].map!"a".should.not.contain([1, 2]); }).should.throwException!TestException.msg; msg.split('\n')[0].should.equal("[1, 2, 3].map!\"a\" should not contain [1, 2]. [1, 2] are present in [1, 2, 3]."); msg = ({ [1, 2, 3].map!"a".should.contain(4); }).should.throwException!TestException.msg; msg.split('\n')[0].should.contain("4 is missing from [1, 2, 3]"); } /// const range contain unittest { const(int)[] data = [1, 2, 3]; data.map!"a".should.contain([2, 1]); data.map!"a".should.contain(data); [1, 2, 3].should.contain(data); ({ data.map!"a * 4".should.not.contain(data); }).should.not.throwAnyException; } /// immutable range contain unittest { immutable(int)[] data = [1, 2, 3]; data.map!"a".should.contain([2, 1]); data.map!"a".should.contain(data); [1, 2, 3].should.contain(data); ({ data.map!"a * 4".should.not.contain(data); }).should.not.throwAnyException; } /// contain only unittest { ({ [1, 2, 3].should.containOnly([3, 2, 1]); [1, 2, 3].should.not.containOnly([2, 1]); [1, 2, 2].should.not.containOnly([2, 1]); [1, 2, 2].should.containOnly([2, 1, 2]); [2, 2].should.containOnly([2, 2]); [2, 2, 2].should.not.containOnly([2, 2]); }).should.not.throwException!TestException; auto msg = ({ [1, 2, 3].should.containOnly([2, 1]); }).should.throwException!TestException.msg; msg.split('\n')[0].should.equal("[1, 2, 3] should contain only [2, 1]."); msg = ({ [1, 2].should.not.containOnly([2, 1]); }).should.throwException!TestException.msg; msg.split('\n')[0].strip.should.equal("[1, 2] should not contain only [2, 1]."); msg = ({ [2, 2].should.containOnly([2]); }).should.throwException!TestException.msg; msg.split('\n')[0].should.equal("[2, 2] should contain only [2]."); msg = ({ [3, 3].should.containOnly([2]); }).should.throwException!TestException.msg; msg.split('\n')[0].should.equal("[3, 3] should contain only [2]."); msg = ({ [2, 2].should.not.containOnly([2, 2]); }).should.throwException!TestException.msg; msg.split('\n')[0].should.equal("[2, 2] should not contain only [2, 2]."); } /// contain only with void array unittest { int[] list; list.should.containOnly([]); } /// const range containOnly unittest { const(int)[] data = [1, 2, 3]; data.map!"a".should.containOnly([3, 2, 1]); data.map!"a".should.containOnly(data); [1, 2, 3].should.containOnly(data); ({ data.map!"a * 4".should.not.containOnly(data); }).should.not.throwAnyException; } /// immutable range containOnly unittest { immutable(int)[] data = [1, 2, 3]; data.map!"a".should.containOnly([2, 1, 3]); data.map!"a".should.containOnly(data); [1, 2, 3].should.containOnly(data); ({ data.map!"a * 4".should.not.containOnly(data); }).should.not.throwAnyException; } /// array contain unittest { ({ [1, 2, 3].should.contain([2, 1]); [1, 2, 3].should.not.contain([4, 5, 6, 7]); [1, 2, 3].should.contain(1); }).should.not.throwException!TestException; auto msg = ({ [1, 2, 3].should.contain([4, 5]); }).should.throwException!TestException.msg.split('\n'); msg[0].should.equal("[1, 2, 3] should contain [4, 5]. [4, 5] are missing from [1, 2, 3]."); msg = ({ [1, 2, 3].should.not.contain([2, 3]); }).should.throwException!TestException.msg.split('\n'); msg[0].should.equal("[1, 2, 3] should not contain [2, 3]. [2, 3] are present in [1, 2, 3]."); msg = ({ [1, 2, 3].should.not.contain([4, 3]); }).should.throwException!TestException.msg.split('\n'); msg[0].should.equal("[1, 2, 3] should not contain [4, 3]. 3 is present in [1, 2, 3]."); msg = ({ [1, 2, 3].should.contain(4); }).should.throwException!TestException.msg.split('\n'); msg[0].should.equal("[1, 2, 3] should contain 4. 4 is missing from [1, 2, 3]."); msg = ({ [1, 2, 3].should.not.contain(2); }).should.throwException!TestException.msg.split('\n'); msg[0].should.equal("[1, 2, 3] should not contain 2. 2 is present in [1, 2, 3]."); } /// array equals unittest { ({ [1, 2, 3].should.equal([1, 2, 3]); }).should.not.throwAnyException; ({ [1, 2, 3].should.not.equal([2, 1, 3]); [1, 2, 3].should.not.equal([2, 3]); [2, 3].should.not.equal([1, 2, 3]); }).should.not.throwAnyException; auto msg = ({ [1, 2, 3].should.equal([4, 5]); }).should.throwException!TestException.msg.split("\n"); msg[0].strip.should.equal("[1, 2, 3] should equal [4, 5]."); msg = ({ [1, 2].should.equal([4, 5]); }).should.throwException!TestException.msg.split("\n"); msg[0].strip.should.equal("[1, 2] should equal [4, 5]."); msg = ({ [1, 2, 3].should.equal([2, 3, 1]); }).should.throwException!TestException.msg.split("\n"); msg[0].strip.should.equal("[1, 2, 3] should equal [2, 3, 1]."); msg = ({ [1, 2, 3].should.not.equal([1, 2, 3]); }).should.throwException!TestException.msg.split("\n"); msg[0].strip.should.startWith("[1, 2, 3] should not equal [1, 2, 3]"); } ///array equals with structs unittest { struct TestStruct { int value; void f() {} } ({ [TestStruct(1)].should.equal([TestStruct(1)]); }).should.not.throwAnyException; auto msg = ({ [TestStruct(2)].should.equal([TestStruct(1)]); }).should.throwException!TestException.msg; msg.should.startWith("[TestStruct(2)] should equal [TestStruct(1)]."); } /// const array equal unittest { const(string)[] constValue = ["test", "string"]; immutable(string)[] immutableValue = ["test", "string"]; constValue.should.equal(["test", "string"]); immutableValue.should.equal(["test", "string"]); ["test", "string"].should.equal(constValue); ["test", "string"].should.equal(immutableValue); } version(unittest) { class TestEqualsClass { int value; this(int value) { this.value = value; } void f() {} } } ///array equals with classes unittest { ({ auto instance = new TestEqualsClass(1); [instance].should.equal([instance]); }).should.not.throwAnyException; ({ [new TestEqualsClass(2)].should.equal([new TestEqualsClass(1)]); }).should.throwException!TestException; } /// range equals unittest { ({ [1, 2, 3].map!"a".should.equal([1, 2, 3]); }).should.not.throwAnyException; ({ [1, 2, 3].map!"a".should.not.equal([2, 1, 3]); [1, 2, 3].map!"a".should.not.equal([2, 3]); [2, 3].map!"a".should.not.equal([1, 2, 3]); }).should.not.throwAnyException; auto msg = ({ [1, 2, 3].map!"a".should.equal([4, 5]); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal(`[1, 2, 3].map!"a" should equal [4, 5].`); msg = ({ [1, 2].map!"a".should.equal([4, 5]); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal(`[1, 2].map!"a" should equal [4, 5].`); msg = ({ [1, 2, 3].map!"a".should.equal([2, 3, 1]); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal(`[1, 2, 3].map!"a" should equal [2, 3, 1].`); msg = ({ [1, 2, 3].map!"a".should.not.equal([1, 2, 3]); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.startWith(`[1, 2, 3].map!"a" should not equal [1, 2, 3]`); } /// custom range asserts unittest { struct Range { int n; int front() { return n; } void popFront() { ++n; } bool empty() { return n == 3; } } Range().should.equal([0,1,2]); Range().should.contain([0,1]); Range().should.contain(0); auto msg = ({ Range().should.equal([0,1]); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.startWith("Range() should equal [0, 1]"); msg = ({ Range().should.contain([2, 3]); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.startWith("Range() should contain [2, 3]. 3 is missing from [0, 1, 2]."); msg = ({ Range().should.contain(3); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.startWith("Range() should contain 3. 3 is missing from [0, 1, 2]."); } /// custom const range equals unittest { struct ConstRange { int n; const(int) front() { return n; } void popFront() { ++n; } bool empty() { return n == 3; } } [0,1,2].should.equal(ConstRange()); ConstRange().should.equal([0,1,2]); } /// custom immutable range equals unittest { struct ImmutableRange { int n; immutable(int) front() { return n; } void popFront() { ++n; } bool empty() { return n == 3; } } [0,1,2].should.equal(ImmutableRange()); ImmutableRange().should.equal([0,1,2]); } /// approximately equals unittest { [0.350, 0.501, 0.341].should.be.approximately([0.35, 0.50, 0.34], 0.01); [0.350, 0.501, 0.341].should.not.be.approximately([0.35, 0.50, 0.34], 0.00001); [0.350, 0.501, 0.341].should.not.be.approximately([0.501, 0.350, 0.341], 0.001); [0.350, 0.501, 0.341].should.not.be.approximately([0.350, 0.501], 0.001); [0.350, 0.501].should.not.be.approximately([0.350, 0.501, 0.341], 0.001); auto msg = ({ [0.350, 0.501, 0.341].should.be.approximately([0.35, 0.50, 0.34], 0.0001); }).should.throwException!TestException.msg; msg.should.contain("Expected:[0.35±0.0001, 0.5±0.0001, 0.34±0.0001]"); msg.should.contain("Missing:[0.501±0.0001, 0.341±0.0001]"); } /// approximately equals with Assert unittest { Assert.approximately([0.350, 0.501, 0.341], [0.35, 0.50, 0.34], 0.01); Assert.notApproximately([0.350, 0.501, 0.341], [0.350, 0.501], 0.0001); } /// immutable string unittest { immutable string[] someList; someList.should.equal([]); } /// Compare const objects unittest { class A {} A a = new A(); const(A)[] arr = [a]; arr.should.equal([a]); }