1 module fluentasserts.core.callable; 2 3 public import fluentasserts.core.base; 4 import std..string; 5 import std.datetime; 6 import std.conv; 7 import std.traits; 8 9 import fluentasserts.core.results; 10 11 @safe: 12 /// 13 struct ShouldCallable(T) { 14 private { 15 T callable; 16 } 17 18 mixin ShouldCommons; 19 mixin ShouldThrowableCommons; 20 21 /// 22 this(lazy T callable) { 23 auto result = callable.evaluate; 24 25 valueEvaluation = result.evaluation; 26 this.callable = result.value; 27 } 28 29 /// 30 auto haveExecutionTime(string file = __FILE__, size_t line = __LINE__) { 31 validateException; 32 33 auto tmpShould = ShouldBaseType!Duration(evaluate(valueEvaluation.duration)).forceMessage(" have execution time"); 34 35 return tmpShould; 36 } 37 38 /// 39 auto beNull(string file = __FILE__, size_t line = __LINE__) { 40 validateException; 41 42 addMessage(" be "); 43 addValue("null"); 44 beginCheck; 45 46 bool isNull = callable is null; 47 48 string expected; 49 50 static if(isDelegate!callable) { 51 string actual = callable.ptr.to!string; 52 } else { 53 string actual = (cast(void*)callable).to!string; 54 } 55 56 if(expectedValue) { 57 expected = "null"; 58 } else { 59 expected = "not null"; 60 } 61 62 return result(isNull, [], new ExpectedActualResult(expected, actual), file, line); 63 } 64 } 65 66 /// Should be able to catch any exception 67 unittest { 68 ({ 69 throw new Exception("test"); 70 }).should.throwAnyException.msg.should.equal("test"); 71 } 72 73 /// Should be able to catch any assert 74 unittest { 75 ({ 76 assert(false, "test"); 77 }).should.throwSomething.withMessage.equal("test"); 78 } 79 80 /// Should be able to use with message without a custom assert 81 unittest { 82 ({ 83 assert(false, "test"); 84 }).should.throwSomething.withMessage("test"); 85 } 86 87 /// Should be able to catch a certain exception type 88 unittest { 89 class CustomException : Exception { 90 this(string msg, string fileName = "", size_t line = 0, Throwable next = null) { 91 super(msg, fileName, line, next); 92 } 93 } 94 95 ({ 96 throw new CustomException("test"); 97 }).should.throwException!CustomException.withMessage("test"); 98 99 bool hasException; 100 try { 101 ({ 102 throw new Exception("test"); 103 }).should.throwException!CustomException.withMessage("test"); 104 } catch(TestException t) { 105 hasException = true; 106 t.msg.should.contain(" }) should throw exception with message equal \"test\". `object.Exception` saying `test` was thrown."); 107 } 108 hasException.should.equal(true).because("we want to catch a CustomException not an Exception"); 109 } 110 111 /// Should be able to retrieve a typed version of a custom exception 112 unittest { 113 class CustomException : Exception { 114 int data; 115 this(int data, string msg, string fileName = "", size_t line = 0, Throwable next = null) { 116 super(msg, fileName, line, next); 117 118 this.data = data; 119 } 120 } 121 122 auto thrown = ({ 123 throw new CustomException(2, "test"); 124 }).should.throwException!CustomException.thrown; 125 126 thrown.should.not.beNull; 127 thrown.msg.should.equal("test"); 128 (cast(CustomException) thrown).data.should.equal(2); 129 } 130 131 /// Should fail if an exception is not thrown 132 unittest { 133 auto thrown = false; 134 try { 135 ({ }).should.throwAnyException; 136 } catch(TestException e) { 137 thrown = true; 138 e.msg.split("\n")[0].should.equal("({ }) should throw any exception. No exception was thrown."); 139 } 140 141 thrown.should.equal(true); 142 } 143 144 /// Should fail if an exception is not expected 145 unittest { 146 auto thrown = false; 147 try { 148 ({ 149 throw new Exception("test"); 150 }).should.not.throwAnyException; 151 } catch(TestException e) { 152 thrown = true; 153 e.msg.split("\n")[2].should.equal(" }) should not throw any exception. `object.Exception` saying `test` was thrown."); 154 } 155 156 thrown.should.equal(true); 157 } 158 159 /// Should be able to benchmark some code 160 unittest { 161 ({ 162 163 }).should.haveExecutionTime.lessThan(1.seconds); 164 } 165 166 /// Should fail on benchmark timeout 167 unittest { 168 import core.thread; 169 170 TestException exception = null; 171 172 try { 173 ({ 174 Thread.sleep(2.msecs); 175 }).should.haveExecutionTime.lessThan(1.msecs); 176 } catch(TestException e) { 177 exception = e; 178 } 179 180 exception.should.not.beNull.because("we wait 20 milliseconds"); 181 exception.msg.should.startWith("({\n Thread.sleep(2.msecs);\n }) should have execution time less than 1 ms."); 182 } 183 184 /// It should check if a delegate is null 185 unittest { 186 void delegate() action; 187 action.should.beNull; 188 189 ({ }).should.not.beNull; 190 191 auto msg = ({ 192 action.should.not.beNull; 193 }).should.throwException!TestException.msg; 194 195 msg.should.startWith("action should not be null."); 196 msg.should.contain("Expected:not null"); 197 msg.should.contain("Actual:null"); 198 199 msg = ({ 200 ({ }).should.beNull; 201 }).should.throwException!TestException.msg; 202 203 msg.should.startWith("({ }) should be null."); 204 msg.should.contain("Expected:null\n"); 205 msg.should.not.contain("Actual:null\n"); 206 }