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

      
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
183
190
200
210
220
230
240
250
260
2712
280
29377
300
310
320
330
340
350
360
370
380
390
400
410
420
430
440
45117
46117
47117
48117
490
500
510
52113
530
54113
550
561212
570
58524
590
6076
610
620
63291
6481
650
66210
670
680
690
70113
710
720
730
74113
750
76113
770
781143
790
8049
810
82245
830
840
85268
8658
870
88210
890
900
910
92113
930
940
950
96108
970
98108
990
1001142
101290
10226
1030
1040
1050
10647
1070
108243
1090
1100
111264
112211
113211
1140
1150
1160
117108
1180
1190
1200
1210
1220
1231
1240
1251
1260
1271
1281
1291
1301
1310
1320
1330
1340
1351
1360
1371
1380
1391
1401
1410
1420
1430
1440
1451
1460
1471
1480
1491
1501
1511
1521
1530
1540
1550
1560
1571
1580
1591
1600
1611
1621
1630
1640
1650
1660
1671
1680
1691
1700
1711
1721
1731
1740
1750
1760
1770
1781
1790
1801
1810
1821
1831
1841
1850
1860
1870
1880
1890
1900
1910
1920
1930
1940
1950
19652
1970
19852
19952
20052
20152
20252
2030
20452
2050
2060
2070
2080
2090
21060
2110
21260
21360
21460
21560
21660
2170
21860
2190
22060
22160
22260
2230
22460
225200
2260
2270
22834
2290
23034
23126
2320
23330
2340
2350
23626
2370
2380
2390
24034
2410
24242
24362
2440
2458
2460
2470
24852
2490
2500
25160
2520
253624
2540
255143
2560
25779
2580
2590
2600
26160
26240
2630
26440
2650
2660
26720
2680
26920
2700
2710
2720
2730
2740
27526
2760
27726
27826
27926
2800
28126
2820
28326
28426
28526
28626
28726
2880
28926
29026
2910
29226
29348
2940
29518
2961
2970
2980
29918
3003
3010
3020
3030
30412
3058
3060
3078
3086
3090
3100
3110
31226
3130
3140
3150
3160
3170
3180
31920
3200
32120
32220
32320
3240
32520
3260
32720
32820
32920
3300
33120
3320
333228
33460
3350
33660
33728
3380
3390
3400
34148
34292
3430
34420
3450
34620
34724
3480
34912
3500
3510
3520
3530
3540
3550
3560
3570
3580
3590
36016
3610
3628
3630
3640
3650
3660
3670
3680
3690
3700
3710
3720
3730
3740
3750
3760
37715
37815
37915
3800
38115
38254
3830
38415
3850
38615
38715
3880
38915
3900
3910
3920
3930
39415
3950
39614
3970
3980
3990
4000
4011
4020
4030
4040
4050
4060
4070
4080
4090
4100
4111
4124
4130
4140
4153
4162
4170
4180
4193
4202
4210
4220
4233
4242
4250
4260
4273
4282
4290
4300
4310
4320
4330
4342
4352
4362
4370
4380
4392
4402
4410
4420
4433
4442
4450
4460
4472
4482
4492
4500
4513
4522
4530
4540
4552
4562
4572
4580
4593
4602
4610
4620
4632
4642
4652
4660
4670
4680
4690
4701
4712
4722
4732
4740
4752
4762
4770
4780
4790
4800
4810
4821
4832
4842
4852
4860
4872
4882
4890
4900
4910
4920
4930
4942
4952
4962
4970
4982
4992
5000
5012
5022
5030
5040
5053
5062
5070
5080
5092
5102
5112
5120
5133
5142
5150
5160
5172
5182
5190
5203
5212
5220
5230
5242
5252
5262
5270
5283
5292
5300
5310
5322
5332
5342
5352
5360
5373
5382
5390
5400
5412
5422
5432
5440
5450
5460
5470
5481
5492
5500
5510
5520
5530
5540
5551
5562
5572
5582
5590
5602
5612
5620
5630
5640
5650
5660
5671
5682
5692
5702
5710
5722
5732
5740
5750
5760
5770
5780
5792
5802
5812
5820
5832
5840
5850
5863
5872
5880
5890
5902
5912
5922
5932
5940
5953
5962
5970
5980
5992
6002
6012
6022
6030
6043
6052
6060
6070
6082
6092
6102
6112
6120
6133
6142
6150
6160
6172
6182
6192
6202
6210
6223
6232
6240
6250
6262
6272
6282
6292
6300
6310
6320
6330
6342
6352
6360
6370
6382
6392
6402
6412
6420
6430
6443
6452
6460
6470
6482
6492
6502
6510
6523
6532
6540
6550
6562
6572
6582
6592
6602
6610
6623
6632
6640
6650
6662
6672
6682
6690
6703
6712
6720
6730
6742
6752
6762
6770
6780
6790
6800
6811
6820
6830
6840
6850
6860
6872
6882
6890
6900
6913
6922
6930
6940
6950
6960
6970
6981
6991
7000
7012
7022
7030
7042
7052
7060
7070
7080
7090
7100
7110
7126
7130
7140
7150
7160
7170
7180
7190
7202
7211
7222
7230
7240
7253
7262
7270
7280
7290
7300
7310
7322
7332
7340
7350
7362
7372
7382
7392
7400
7410
7423
7432
7440
7450
7462
7472
7482
7490
7503
7512
7520
7530
7542
7552
7562
7570
7583
7592
7600
7610
7622
7632
7642
7650
7663
7672
7680
7690
7702
7712
7722
7730
7740
7750
7760
7771
7780
7790
78052
7810
7820
78351
7840
7850
78695
7870
7880
7890
7902
7912
7922
7930
7943
7952
7960
7970
7982
7992
8002
8010
8023
8032
8040
8050
8062
8072
8082
8090
8103
8112
8120
8130
8142
8152
8162
8170
8180
8190
8200
8211
8220
8230
82415
8250
8260
8270
82815
8290
8300
8310
83226
8330
8340
8350
8362
8372
8380
8390
8400
8410
8421
8430
8440
84515
8460
8470
8480
84915
8500
8510
8520
85326
8540
8550
8560
8572
8582
8590
8600
8610
8620
8632
8640
8652
8662
8672
8682
8692
8700
8710
8723
8732
8740
8750
8762
8772
8780
8790
8800
8810
8821
8831
8840
8850
8860
8870
8881
8890
8902
8910
8920
8930
8940
8950
8961
8971
8981
8992
9000
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; } 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 = 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.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([]); } /// Compare const objects unittest { class A {} A a = new A(); const(A)[] arr = [a]; arr.should.equal([a]); }