1 module fluentasserts.core.operations.approximately; 2 3 import fluentasserts.core.results; 4 import fluentasserts.core.evaluation; 5 import fluentasserts.core.array; 6 import fluentasserts.core.serializers; 7 import fluentasserts.core.operations.contain; 8 9 import fluentasserts.core.lifecycle; 10 11 import std.algorithm; 12 import std.array; 13 import std.conv; 14 import std.math; 15 16 version(unittest) { 17 import fluentasserts.core.expect; 18 } 19 20 /// 21 IResult[] approximately(ref Evaluation evaluation) @trusted nothrow { 22 IResult[] results = []; 23 24 evaluation.message.addValue("±"); 25 evaluation.message.addValue(evaluation.expectedValue.meta["1"]); 26 evaluation.message.addText("."); 27 28 real current; 29 real expected; 30 real delta; 31 32 try { 33 current = evaluation.currentValue.strValue.to!real; 34 expected = evaluation.expectedValue.strValue.to!real; 35 delta = evaluation.expectedValue.meta["1"].to!real; 36 } catch(Exception e) { 37 results ~= new MessageResult("Can't parse the provided arguments!"); 38 39 return results; 40 } 41 42 string strExpected = evaluation.expectedValue.strValue ~ "±" ~ evaluation.expectedValue.meta["1"]; 43 string strCurrent = evaluation.currentValue.strValue; 44 45 auto result = isClose(current, expected, 0, delta); 46 47 if(evaluation.isNegated) { 48 result = !result; 49 } 50 51 if(result) { 52 return []; 53 } 54 55 if(evaluation.currentValue.typeName != "bool") { 56 evaluation.message.addText(" "); 57 evaluation.message.addValue(strCurrent); 58 59 if(evaluation.isNegated) { 60 evaluation.message.addText(" is approximately "); 61 } else { 62 evaluation.message.addText(" is not approximately "); 63 } 64 65 evaluation.message.addValue(strExpected); 66 evaluation.message.addText("."); 67 } 68 69 try results ~= new ExpectedActualResult((evaluation.isNegated ? "not " : "") ~ strExpected, strCurrent); catch(Exception) {} 70 71 return results; 72 } 73 74 /// 75 IResult[] approximatelyList(ref Evaluation evaluation) @trusted nothrow { 76 evaluation.message.addValue("±" ~ evaluation.expectedValue.meta["1"]); 77 evaluation.message.addText("."); 78 79 double maxRelDiff; 80 real[] testData; 81 real[] expectedPieces; 82 83 try { 84 testData = evaluation.currentValue.strValue.parseList.cleanString.map!(a => a.to!real).array; 85 expectedPieces = evaluation.expectedValue.strValue.parseList.cleanString.map!(a => a.to!real).array; 86 maxRelDiff = evaluation.expectedValue.meta["1"].to!double; 87 } catch(Exception e) { 88 return [ new MessageResult("Can not perform the assert.") ]; 89 } 90 91 auto comparison = ListComparison!real(testData, expectedPieces, maxRelDiff); 92 93 auto missing = comparison.missing; 94 auto extra = comparison.extra; 95 auto common = comparison.common; 96 97 IResult[] results = []; 98 99 bool allEqual = testData.length == expectedPieces.length; 100 101 if(allEqual) { 102 foreach(i; 0..testData.length) { 103 allEqual = allEqual && isClose(testData[i], expectedPieces[i], 0, maxRelDiff) && true; 104 } 105 } 106 107 string strExpected; 108 string strMissing; 109 110 if(maxRelDiff == 0) { 111 strExpected = evaluation.expectedValue.strValue; 112 try strMissing = missing.length == 0 ? "" : missing.to!string; 113 catch(Exception) {} 114 } else try { 115 strMissing = "[" ~ missing.map!(a => a.to!string ~ "±" ~ maxRelDiff.to!string).join(", ") ~ "]"; 116 strExpected = "[" ~ expectedPieces.map!(a => a.to!string ~ "±" ~ maxRelDiff.to!string).join(", ") ~ "]"; 117 } catch(Exception) {} 118 119 if(!evaluation.isNegated) { 120 if(!allEqual) { 121 try results ~= new ExpectedActualResult(strExpected, evaluation.currentValue.strValue); 122 catch(Exception) {} 123 124 try results ~= new ExtraMissingResult(extra.length == 0 ? "" : extra.to!string, strMissing); 125 catch(Exception) {} 126 } 127 } else { 128 if(allEqual) { 129 try results ~= new ExpectedActualResult("not " ~ strExpected, evaluation.currentValue.strValue); 130 catch(Exception) {} 131 } 132 } 133 134 return results; 135 }