1 module dshould.thrown; 2 3 import std.format : format; 4 import std.traits : CommonType; 5 import std.typecons; 6 import dshould.ShouldType; 7 import dshould.basic : be, equal, not, should; 8 9 /** 10 * The phrase `.should.throwA!Type` (or `.throwAn!Exception`, depending on grammar) expects the left-hand side expression 11 * to throw an exception of the given type. 12 * The exception is caught. If no exception was thrown, `.throwA` itself throws a `FluentException` to complain. 13 * If the left-hand side threw an exception, the word `.where` may be used to inspect this exception further. 14 * The meaning of `.throwA` may be negated with `.not`, in which case nothing is returned. 15 */ 16 public template throwA(T : Throwable) 17 { 18 auto throwA(Should)(Should should, Fence _ = Fence(), string file = __FILE__, size_t line = __LINE__) 19 if (isInstanceOf!(ShouldType, Should)) 20 { 21 should.allowOnlyWords!("not").before!"throwA"; 22 23 should.terminateChain; 24 25 FluentException innerError = null; 26 27 auto inner() 28 { 29 try 30 { 31 should.got(); 32 } 33 catch (T throwable) 34 { 35 static if (should.hasWord!"not") 36 { 37 innerError = new FluentException( 38 format!`no exception of type %s`(T.stringof), 39 format!`%s`(throwable), 40 file, line 41 ); 42 } 43 else 44 { 45 return throwable; 46 } 47 } 48 49 static if (!should.hasWord!"not") 50 { 51 return null; 52 } 53 } 54 55 try 56 { 57 static if (is(typeof(inner()) == void)) 58 { 59 inner; 60 } 61 else 62 { 63 if (auto throwable = inner()) 64 { 65 return tuple!("where", "which")(throwable, throwable); 66 } 67 } 68 } 69 // don't go up beyond Exception unless we're not from beneath it: 70 // keeps us from needlessly breaking purity. 71 catch (CommonType!(Exception, T) otherThrowable) 72 { 73 static if (should.hasWord!"not") 74 { 75 return; 76 } 77 else 78 { 79 throw new FluentException( 80 format!`exception of type %s`(T.stringof), 81 format!`%s`(otherThrowable), 82 file, line 83 ); 84 } 85 } 86 87 static if (should.hasWord!"not") 88 { 89 if (innerError !is null) 90 { 91 throw innerError; 92 } 93 } 94 else 95 { 96 throw new FluentException( 97 format!`exception of type %s`(T.stringof), 98 `no exception`, 99 file, line 100 ); 101 } 102 } 103 } 104 105 /// ditto 106 public alias throwAn = throwA; 107 108 /// 109 unittest 110 { 111 auto exception = new Exception(""); 112 113 /** 114 * Throws: Exception 115 */ 116 void throwsException() { throw exception; } 117 118 throwsException.should.throwAn!Exception.which.should.be(exception); 119 throwsException.should.throwAn!Exception.which.should.not.be(null); 120 121 2.should.be(5).should.throwA!FluentException; 122 2.should.be(5).should.throwAn!Error.should.throwA!FluentException; 123 124 2.should.be(2).should.not.throwA!FluentException; 125 }