1 module mocked.mocker; 2 3 import mocked.builder; 4 import mocked.error; 5 import mocked.meta; 6 import mocked.option; 7 import std.conv; 8 import std.format : format; 9 import std.traits; 10 11 private enum string overloadingCode = q{ 12 %2$s Overload.Return %1$s(Overload.ParameterTypes arguments) 13 { 14 auto overloads = builder.expectationTuple[j].overloads[i]; 15 16 if (overloads.empty) 17 { 18 throw unexpectedCallError!(typeof(super), Overload.ParameterTypes)("%1$s", arguments); 19 } 20 if (!overloads.front.ignoreArgs_ 21 && !overloads.front.compareArguments!options(arguments)) 22 { 23 builder.expectationTuple[j].overloads[i].clear(); 24 25 throw unexpectedArgumentError!(typeof(super), 26 Overload.ParameterTypes, Overload.Arguments)( 27 "%1$s", arguments, overloads.front.arguments); 28 } 29 30 static if (is(Overload.Return == void)) 31 { 32 if (overloads.front.action_ !is null) 33 { 34 overloads.front.action_(arguments); 35 } 36 } 37 else 38 { 39 Overload.Return ret = void; 40 41 if (overloads.front.action_ !is null) 42 { 43 ret = overloads.front.action_(arguments); 44 } 45 else 46 { 47 ret = overloads.front.return_; 48 } 49 } 50 51 static if (!is(T == interface) && is(Overload.Return == void)) 52 { 53 if (overloads.front.passThrough_) 54 { 55 __traits(getMember, super, "%1$s")(arguments); 56 } 57 } 58 else static if (!is(T == interface)) 59 { 60 if (overloads.front.passThrough_) 61 { 62 ret = __traits(getMember, super, "%1$s")(arguments); 63 } 64 } 65 66 if (builder.expectationTuple[j].overloads[i].front.repeat_ > 1) 67 { 68 --builder.expectationTuple[j].overloads[i].front.repeat_; 69 } 70 else if (builder.expectationTuple[j].overloads[i].front.repeat_ == 1) 71 { 72 builder.expectationTuple[j].overloads[i].popFront; 73 } 74 75 static if (!canFind!("nothrow", Overload.qualifiers)) 76 { 77 if (overloads.front.exception !is null) 78 { 79 throw overloads.front.exception; 80 } 81 } 82 static if (!is(Overload.Return == void)) 83 { 84 return ret; 85 } 86 } 87 }; 88 89 /** 90 * Mocker instance with default options. 91 * 92 * See_Also: $(D_PSYMBOL Factory), $(D_PSYMBOL configure). 93 */ 94 alias Mocker = Factory!(Options!()); 95 96 /** 97 * Constructs a mocker with options passed as $(D_PARAM Args). 98 * 99 * Params: 100 * Args = Mocker options. 101 * 102 * See_Also: $(D_PSYMBOL Factory), $(D_PSYMBOL Mocker). 103 */ 104 auto configure(Args...)() 105 { 106 return Factory!(Options!Args)(); 107 } 108 109 /** 110 * A class through which one creates mock objects and manages expectations 111 * about calls to their methods. 112 * 113 * Params: 114 * Options = Mocker $(D_PSYMBOL Options). 115 * 116 * See_Also: $(D_PSYMBOL Mocker), $(D_PSYMBOL CustomMocker). 117 */ 118 struct Factory(Options) 119 { 120 Verifiable[] repositories; 121 private enum Options options = Options(); 122 123 /** 124 * Mocks the type $(D_PARAM T). 125 * 126 * Params: 127 * T = The type to mock. 128 * Args = Constructor parameter types. 129 * args = Constructor arguments. 130 * 131 * Returns: A mock builder. 132 */ 133 auto mock(T, Args...)(Args args) 134 { 135 Repository!T builder; 136 137 class Mock : T 138 { 139 static if (__traits(hasMember, T, "__ctor") && Args.length > 0) 140 { 141 this() 142 { 143 super(args); 144 } 145 } 146 else static if (__traits(hasMember, T, "__ctor")) 147 { 148 this() 149 { 150 super(Parameters!(T.__ctor).init); 151 } 152 } 153 154 static foreach (j, expectation; builder.ExpectationTuple) 155 { 156 static foreach (i, Overload; expectation.Overloads) 157 { 158 static if (is(T == class)) 159 { 160 mixin(format!overloadingCode(expectation.name, 161 unwords!("override", Overload.qualifiers))); 162 } 163 else 164 { 165 mixin(format!overloadingCode(expectation.name, 166 unwords!(Overload.qualifiers))); 167 } 168 } 169 } 170 } 171 172 auto mock = new Mock(); 173 auto repository = new Mocked!T(mock, builder); 174 175 this.repositories ~= repository; 176 return repository; 177 } 178 179 /** 180 * Verifies that certain expectation requirements were satisfied. 181 * 182 * Throws: $(D_PSYMBOL ExpectationViolationException) if those issues occur. 183 */ 184 void verify() 185 { 186 foreach (repository; this.repositories) 187 { 188 repository.verify; 189 } 190 } 191 192 ~this() 193 { 194 verify; 195 } 196 }