fluentasserts.core.operations.registry 78/79(98%) 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
3690
3790
3815
390
400
410
4215
430
440
450
460
4715
4815
490
500
510
520
531569
540
551569
561569
570
581569
590
600
610
620
631554
640
650
660
670
683267
693267
700
713267
723267
730
7415360
754029
763264
773264
780
790
800
813267
820
833264
840
850
860
870
889801
890
900
910
923267
930
940
950
960
973264
980
990
1000
1010
10210
1030
1040
1050
1060
10719
1089
1090
1100
11110
1120
1130
1140
1150
11619
1170
1180
1190
1200
1213
1222362
1232362
1240
1250
1260
1270
1280
1290
1300
1310
1322
1330
1342
13521
1360
1370
1382
1390
1400
1410
1420
1430
1440
1450
1460
1470
1481
1490
1501
1511
1520
1532
1540
1550
1560
1573267
1580
15919632
16019752
1613307
1620
1630
1640
1653267
1660
1670
1680
1696550
1706550
1715638
1720
1730
174912
1750
176912
1770
178912
179912
180912
181912
1820
1838799
1842021
1852021
186943
1870
1880
1892021
190943
1910
1920
1932963
194918
1950
1960
1972963
19824
19924
2000
2010
2022021
203942
2040
2050
2060
207912
20823
20923
2100
2110
212912
2130
214912
2150
2160
2170
2180
2190
2200
2210
2220
2232
2240
2250
2260
2270
2282
2290
2300
2310
2320
2332
2340
2350
2360
2370
2382
2390
2400
2410
2420
2432
2440
2450
2460
2470
2482
2490
module fluentasserts.core.operations.registry; import fluentasserts.core.results; import fluentasserts.core.evaluation; import std.functional; import std.string; import std.array; import std.algorithm; /// Delegate type that can handle asserts alias Operation = IResult[] delegate(ref Evaluation) @safe nothrow; /// ditto alias OperationFunc = IResult[] delegate(ref Evaluation) @safe nothrow; struct OperationPair { string valueType; string expectedValueType; } /// class Registry { /// Global instance for the assert operations static Registry instance; private { Operation[string] operations; OperationPair[][string] pairs; string[string] descriptions; } /// Register a new assert operation Registry register(T, U)(string name, Operation operation) { foreach(valueType; extractTypes!T) { foreach(expectedValueType; extractTypes!U) { register(valueType, expectedValueType, name, operation); } } return this; } /// ditto Registry register(T, U)(string name, IResult[] function(ref Evaluation) @safe nothrow operation) { const operationDelegate = operation.toDelegate; return this.register!(T, U)(name, operationDelegate); } /// ditto Registry register(string valueType, string expectedValueType, string name, Operation operation) { string key = valueType ~ "." ~ expectedValueType ~ "." ~ name; operations[key] = operation; pairs[name] ~= OperationPair(valueType, expectedValueType); return this; } /// ditto Registry register(string valueType, string expectedValueType, string name, IResult[] function(ref Evaluation) @safe nothrow operation) { return this.register(valueType, expectedValueType, name, operation.toDelegate); } /// Get an operation function Operation get(string valueType, string expectedValueType, string name) @safe nothrow { assert(valueType != "", "The value type is not set!"); assert(name != "", "The operation name is not set!"); auto genericKeys = [valueType ~ "." ~ expectedValueType ~ "." ~ name] ~ generalizeKey(valueType, expectedValueType, name); string matchedKey; foreach(key; genericKeys) { if(key in operations) { matchedKey = key; break; } } assert(matchedKey != "", "There are no matching assert operations. Register any of `" ~ genericKeys.join("`, `") ~ "` to perform this assert."); return operations[matchedKey]; } /// IResult[] handle(ref Evaluation evaluation) @safe nothrow { if(evaluation.operationName == "" || evaluation.operationName == "to" || evaluation.operationName == "should") { return []; } auto operation = this.get( evaluation.currentValue.typeName, evaluation.expectedValue.typeName, evaluation.operationName); return operation(evaluation); } /// void describe(string name, string text) { descriptions[name] = text; } /// string describe(string name) { if(name !in descriptions) { return ""; } return descriptions[name]; } /// OperationPair[] bindingsForName(string name) { return pairs[name]; } /// string[] registeredOperations() { return operations.keys .map!(a => a.split(".")) .map!(a => a[a.length - 1]) .array .sort .uniq .array; } /// string docs() { string result = ""; string[] operationNames = registeredOperations .map!(a => "- [" ~ a ~ "](api/" ~ a ~ ".md)") .array; return operationNames.join("\n"); } } /// It generates a list of md links for docs unittest { import std.datetime; import fluentasserts.core.operations.equal; import fluentasserts.core.operations.lessThan; auto instance = new Registry(); instance.register("*", "*", "equal", &equal); instance.register!(Duration, Duration)("lessThan", &lessThanDuration); instance.docs.should.equal("- [equal](api/equal.md)\n" ~ "- [lessThan](api/lessThan.md)"); } string[] generalizeKey(string valueType, string expectedValueType, string name) @safe nothrow { string[] results; foreach (string generalizedValueType; generalizeType(valueType)) { foreach (string generalizedExpectedValueType; generalizeType(expectedValueType)) { results ~= generalizedValueType ~ "." ~ generalizedExpectedValueType ~ "." ~ name; } } return results; } string[] generalizeType(string typeName) @safe nothrow { auto pos = typeName.indexOf("["); if(pos == -1) { return ["*"]; } string[] results = []; const pieces = typeName.split("["); string arrayType; bool isHashMap; int index = 0; int diff = 0; foreach (ch; typeName[pos..$]) { diff++; if(ch == '[') { index++; } if(ch == ']') { index--; } if(index == 0 && diff == 2) { arrayType ~= "[]"; } if(index == 0 && diff != 2) { arrayType ~= "[*]"; isHashMap = true; } if(index == 0) { diff = 0; } } if(isHashMap) { results ~= "*" ~ typeName[pos..$]; results ~= pieces[0] ~ arrayType; } results ~= "*" ~ arrayType; return results; } version(unittest) { import fluentasserts.core.base; } /// It can generalize an int unittest { generalizeType("int").should.equal(["*"]); } /// It can generalize a list unittest { generalizeType("int[]").should.equal(["*[]"]); } /// It can generalize a list of lists unittest { generalizeType("int[][]").should.equal(["*[][]"]); } /// It can generalize an assoc array unittest { generalizeType("int[int]").should.equal(["*[int]", "int[*]", "*[*]"]); } /// It can generalize a combination of assoc arrays and lists unittest { generalizeType("int[int][][string][]").should.equal(["*[int][][string][]", "int[*][][*][]", "*[*][][*][]"]); } /// It can generalize an assoc array with a key list unittest { generalizeType("int[int[]]").should.equal(["*[int[]]", "int[*]", "*[*]"]); }