fluentasserts.core.array 399/399(100%) line coverage

      
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
183
190
2029
210
22240
230
240
250
260
270
280
290
300
310
320
330
340
35116
36116
37116
38116
390
400
410
42112
430
44112
450
461206
470
48524
490
5075
510
520
53290
5481
550
56209
570
580
590
60112
610
620
630
64112
650
66112
670
681137
690
7049
710
72244
730
740
75267
7658
770
78209
790
800
810
82112
830
840
850
86107
870
88107
890
901136
91289
9226
930
940
950
9647
970
98242
990
1000
101263
102210
103210
1040
1050
1060
107107
1080
1090
1100
1110
1120
1131
1140
1151
1160
1171
1181
1191
1201
1210
1220
1230
1240
1251
1260
1271
1280
1291
1301
1310
1320
1330
1340
1351
1360
1371
1380
1391
1401
1411
1421
1430
1440
1450
1460
1471
1480
1491
1500
1511
1521
1530
1540
1550
1560
1571
1580
1591
1600
1611
1621
1631
1640
1650
1660
1670
1681
1690
1701
1710
1721
1731
1741
1750
1760
1770
1780
1790
1800
1810
1820
1830
1840
1850
18651
1870
18851
18951
19051
19151
19251
1930
19451
1950
1960
1970
1980
1990
20059
2010
20259
20359
20459
20559
20659
2070
20859
2090
21059
21159
21259
2130
21459
21559
2160
2170
21834
2190
22034
22126
2220
22330
2240
2250
22625
2270
2280
2290
23034
2310
23242
23362
2340
2358
2360
2370
23850
2390
2400
24159
2420
243618
2440
245143
2460
24777
2480
2490
2500
25159
25239
2530
25439
2550
2560
25720
2580
25920
2600
2610
2620
2630
2640
26526
2660
26726
26826
26926
2700
27126
2720
27326
27426
27526
27626
27726
2780
27926
28026
2810
28226
28348
2840
28518
2861
2870
2880
28918
2903
2910
2920
2930
29412
2958
2960
2978
2986
2990
3000
3010
30226
3030
3040
3050
3060
3070
3080
3090
31020
3110
31220
31320
31420
3150
31620
3170
31820
31920
32020
3210
32220
3230
324228
32560
3260
32760
32828
3290
3300
3310
33248
33392
3340
33520
3360
33720
33824
3390
34012
3410
3420
3430
3440
3450
3460
3470
3480
3490
3500
35116
3520
3538
3540
3550
3560
3570
3580
3590
3600
3610
3620
3630
3640
3650
3660
3670
3688
3698
3708
3710
3728
3730
3748
3758
3760
3778
3780
3790
3800
3810
3828
3837
3840
3850
3860
3870
3881
3890
3900
3910
3920
3930
3940
3950
3960
3970
3981
3994
4000
4010
4023
4032
4040
4050
4063
4072
4080
4090
4103
4112
4120
4130
4143
4152
4160
4170
4180
4190
4200
4212
4222
4232
4240
4250
4262
4272
4280
4290
4303
4312
4320
4330
4342
4352
4362
4370
4383
4392
4400
4410
4422
4432
4442
4450
4463
4472
4480
4490
4502
4512
4522
4530
4540
4550
4560
4571
4582
4592
4602
4610
4622
4632
4640
4650
4660
4670
4680
4691
4702
4712
4722
4730
4742
4752
4760
4770
4780
4790
4800
4812
4822
4832
4840
4852
4862
4870
4882
4892
4900
4910
4923
4932
4940
4950
4962
4972
4982
4990
5003
5012
5020
5030
5042
5052
5060
5073
5082
5090
5100
5112
5122
5132
5140
5153
5162
5170
5180
5192
5202
5212
5222
5230
5243
5252
5260
5270
5282
5292
5302
5310
5320
5330
5340
5351
5362
5370
5380
5390
5400
5410
5421
5432
5442
5452
5460
5472
5482
5490
5500
5510
5520
5530
5541
5552
5562
5572
5580
5592
5602
5610
5620
5630
5640
5650
5662
5672
5682
5690
5702
5710
5720
5733
5742
5750
5760
5772
5782
5792
5802
5810
5823
5832
5840
5850
5862
5872
5882
5892
5900
5913
5922
5930
5940
5952
5962
5972
5982
5990
6003
6012
6020
6030
6042
6052
6062
6072
6080
6093
6102
6110
6120
6132
6142
6152
6162
6170
6180
6190
6200
6212
6222
6230
6240
6252
6262
6272
6282
6290
6300
6313
6322
6330
6340
6352
6362
6372
6380
6393
6402
6410
6420
6432
6442
6452
6462
6472
6480
6493
6502
6510
6520
6532
6542
6552
6560
6573
6582
6590
6600
6612
6622
6632
6640
6650
6660
6670
6681
6690
6700
6710
6720
6730
6742
6752
6760
6770
6783
6792
6800
6810
6820
6830
6840
6851
6861
6870
6882
6892
6900
6912
6922
6930
6940
6950
6960
6970
6980
6996
7000
7010
7020
7030
7040
7050
7060
7072
7081
7092
7100
7110
7123
7132
7140
7150
7160
7170
7180
7192
7202
7210
7220
7232
7242
7252
7262
7270
7280
7293
7302
7310
7320
7332
7342
7352
7360
7373
7382
7390
7400
7412
7422
7432
7440
7453
7462
7470
7480
7492
7502
7512
7520
7533
7542
7550
7560
7572
7582
7592
7600
7610
7620
7630
7641
7650
7660
76752
7680
7690
77051
7710
7720
77371
7740
7750
7760
7772
7782
7792
7800
7813
7822
7830
7840
7852
7862
7872
7880
7893
7902
7910
7920
7932
7942
7952
7960
7973
7982
7990
8000
8012
8022
8032
8040
8050
8060
8070
8081
8090
8100
81112
8120
8130
8140
81512
8160
8170
8180
81916
8200
8210
8220
8232
8242
8250
8260
8270
8280
8291
8300
8310
83212
8330
8340
8350
83612
8370
8380
8390
84016
8410
8420
8430
8442
8452
8460
8470
8480
8490
8502
8510
8522
8532
8542
8552
8562
8570
8580
8593
8602
8610
8620
8632
8642
8650
8660
8670
8680
8691
8701
8710
8720
8730
8740
8751
8760
8772
8780
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)) { return expectedValueList.array.idup; } else { return cast(U[]) expectedValueList.array.dup; } } @trusted: struct ListComparison(T) { private { T[] referenceList; T[] list; double maxRelDiff; } this(U, V)(U reference, V list, double maxRelDiff = 0) { this.referenceList = toValueList!T(reference); this.list = list; this.maxRelDiff = maxRelDiff; } T[] missing() @trusted { T[] result; auto tmpList = list.dup; foreach(element; referenceList) { static if(std.traits.isNumeric!(T)) { auto index = tmpList.countUntil!(a => approxEqual(element, a, maxRelDiff)); } else { auto index = tmpList.countUntil(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) { static if(isFloatingPoint!(T)) { auto index = tmpReferenceList.countUntil!(a => approxEqual(element, a, maxRelDiff)); } else { auto index = tmpReferenceList.countUntil(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; } static if(isFloatingPoint!(T)) { auto index = tmpList.countUntil!(a => approxEqual(element, a, maxRelDiff)); } else { auto index = tmpList.countUntil(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 = ElementType!T; mixin ShouldCommons; mixin DisabledShouldThrowableCommons; auto equal(V)(V expectedValueList, const string file = __FILE__, const size_t line = __LINE__) @trusted { U[] valueList = toValueList!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; U[] valueList = toValueList!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 = arrayTestData.to!string; 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 { U[] valueList = toValueList!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 { U[] valueList = toValueList!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("`"); beginCheck; auto isPresent = testData.canFind(value); auto msg = [ Message(true, value.to!string), Message(false, isPresent ? " is present in " : " is missing from "), Message(true, testData.to!string), Message(false, ".") ]; if(expectedValue) { return result(isPresent, msg, [ cast(IResult) new ExpectedActualResult("to contain `" ~ value.to!string ~ "`", testData.to!string), cast(IResult) new ExtraMissingResult("", value.to!string) ], file, line); } else { return result(isPresent, msg, [ cast(IResult) new ExpectedActualResult("to not contain `" ~ value.to!string ~ "`", testData.to!string), 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.split('\n')[2].strip.should.equal("Expected:all of [4, 5]"); msg.split('\n')[3].strip.should.equal("Actual:[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.split('\n')[2].strip.should.equal("Expected:none of [1, 2]"); msg.split('\n')[3].strip.should.equal("Actual:[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]"); msg.split('\n')[2].strip.should.equal("Expected:to contain `4`"); msg.split('\n')[3].strip.should.equal("Actual:[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.split('\n')[2].strip.should.equal("Actual:[1, 2, 3]"); msg.split('\n')[4].strip.should.equal("Extra:[3]"); 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.split('\n')[2].strip.should.equal("Actual:[1, 2]"); msg = ({ [2, 2].should.containOnly([2]); }).should.throwException!TestException.msg; msg.split('\n')[0].should.equal("[2, 2] should contain only [2]."); msg.split('\n')[2].strip.should.equal("Actual:[2, 2]"); msg.split('\n')[4].strip.should.equal("Extra:[2]"); msg = ({ [3, 3].should.containOnly([2]); }).should.throwException!TestException.msg; msg.split('\n')[0].should.equal("[3, 3] should contain only [2]."); msg.split('\n')[2].strip.should.equal("Actual:[3, 3]"); msg.split('\n')[4].strip.should.equal("Extra:[3, 3]"); msg.split('\n')[5].strip.should.equal("Missing:[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]."); msg.split('\n')[2].strip.should.equal("Actual:[2, 2]"); msg.split('\n')[4].strip.should.equal("Extra:[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[2].strip.should.equal("Expected:all of [4, 5]"); msg[3].strip.should.equal("Actual:[1, 2, 3]"); msg[5].strip.should.equal("Missing:[4, 5]"); 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[2].strip.should.equal("Expected:none of [2, 3]"); msg[3].strip.should.equal("Actual:[1, 2, 3]"); msg[5].strip.should.equal("Extra:[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[2].strip.should.equal("Expected:none of [4, 3]"); msg[3].strip.should.equal("Actual:[1, 2, 3]"); msg[5].strip.should.equal("Extra:[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[2].strip.should.equal("Expected:to contain `4`"); msg[3].strip.should.equal("Actual:[1, 2, 3]"); msg[5].strip.should.equal("Missing:4"); 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]."); msg[2].strip.should.equal("Expected:to not contain `2`"); msg[3].strip.should.equal("Actual:[1, 2, 3]"); msg[5].strip.should.equal("Extra:2"); } /// 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[2].strip.should.equal("Expected:[4, 5]"); msg[3].strip.should.equal("Actual:[1, 2, 3]"); 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[2].strip.should.equal("Expected:[4, 5]"); msg[3].strip.should.equal("Actual:[1, 2]"); msg[5].strip.should.equal("Extra:[1, 2]"); msg[6].strip.should.equal("Missing:[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[2].strip.should.equal("Expected:[2, 3, 1]"); msg[3].strip.should.equal("Actual:[1, 2, 3]"); 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]`"); msg[2].strip.should.equal("Expected:not [1, 2, 3]"); msg[3].strip.should.equal("Actual:[1, 2, 3]"); } ///array equals with structs unittest { struct TestStruct { int value; void f() {} } ({ [TestStruct(1)].should.equal([TestStruct(1)]); }).should.not.throwAnyException; ({ [TestStruct(2)].should.equal([TestStruct(1)]); }).should.throwException!TestException.withMessage.startWith("[TestStruct(2)] should equal `[TestStruct(1, null)]`"); } /// 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.withMessage.startWith("[new TestEqualsClass(2)] should equal `[fluentasserts.core.array.TestEqualsClass]`."); } /// 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.split("\n")[2].strip.should.equal("Expected:[4, 5]"); msg.split("\n")[3].strip.should.equal("Actual:[1, 2, 3]"); 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.split("\n")[2].strip.should.equal("Expected:[4, 5]"); msg.split("\n")[3].strip.should.equal("Actual:[1, 2]"); 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.split("\n")[2].strip.should.equal("Expected:[2, 3, 1]"); msg.split("\n")[3].strip.should.equal("Actual:[1, 2, 3]"); 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]`"); msg.split("\n")[2].strip.should.equal("Expected:not [1, 2, 3]"); msg.split("\n")[3].strip.should.equal("Actual:[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.split("\n")[2].strip.should.equal("Expected:[0, 1]"); msg.split("\n")[3].strip.should.equal("Actual:[0, 1, 2]"); 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.split("\n")[2].strip.should.equal("Expected:all of [2, 3]"); msg.split("\n")[3].strip.should.equal("Actual:[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]."); msg.split("\n")[2].strip.should.equal("Expected:to contain `3`"); msg.split("\n")[3].strip.should.equal("Actual:[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); }).should.not.throwAnyException; 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.5±0.0001, 0.34±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([]); }