1 module dmocks.repository; 2 3 import dmocks.util; 4 import dmocks.model; 5 import dmocks.arguments; 6 7 import std.stdio; 8 import std.conv; 9 import std.traits; 10 11 import dmocks.call; 12 import dmocks.expectation; 13 import dmocks.action; 14 15 package: 16 17 class MockRepository 18 { 19 // TODO: split this up somehow! 20 private bool _allowDefaults = false; 21 private bool _recording = true; 22 private bool _ordered = false; 23 private bool _allowUnexpected = false; 24 25 private Call[] _calls = []; 26 private Call[] _unexpectedCalls = []; 27 private GroupExpectation _rootGroupExpectation; 28 private CallExpectation _lastRecordedCallExpectation; // stores last call added to _lastGroupExpectation 29 private Call _lastRecordedCall; // stores last call with which _lastRecordedCallExpectation was created 30 private GroupExpectation _lastGroupExpectation; // stores last group added to _rootGroupExpectation 31 32 private void CheckLastCallSetup () 33 { 34 if (_allowDefaults || _lastRecordedCallExpectation is null || _lastRecordedCallExpectation.action.hasAction) 35 { 36 return; 37 } 38 39 throw new MocksSetupException( 40 "Last expectation: if you do not specify the AllowDefaults option, you need to return a value, throw an exception, execute a delegate, or pass through to base function. The expectation is: " ~ _lastRecordedCallExpectation.toString()); 41 } 42 43 this() 44 { 45 _rootGroupExpectation = createGroupExpectation(false); 46 Ordered(false); 47 } 48 49 50 void AllowDefaults (bool value) 51 { 52 _allowDefaults = value; 53 } 54 55 void AllowUnexpected(bool value) 56 { 57 _allowUnexpected = value; 58 } 59 60 bool AllowUnexpected() 61 { 62 return _allowUnexpected; 63 } 64 65 bool Recording () 66 { 67 return _recording; 68 } 69 70 bool Ordered () 71 { 72 return _ordered; 73 } 74 75 void Replay () 76 { 77 CheckLastCallSetup(); 78 _recording = false; 79 } 80 81 void BackToRecord () 82 { 83 _recording = true; 84 } 85 86 void Ordered(bool value) 87 { 88 debugLog("SETTING ORDERED: %s", value); 89 _ordered = value; 90 _lastGroupExpectation = createGroupExpectation(_ordered); 91 _rootGroupExpectation.addExpectation(_lastGroupExpectation); 92 } 93 94 void Record(CallExpectation expectation, Call call) 95 { 96 CheckLastCallSetup(); 97 _lastGroupExpectation.addExpectation(expectation); 98 _lastRecordedCallExpectation = expectation; 99 _lastRecordedCall = call; 100 } 101 102 @trusted public auto MethodCall (alias METHOD, ARGS...) (MockId mocked, string name, ARGS args) 103 { 104 alias ReturnType!(FunctionTypeOf!(METHOD)) TReturn; 105 106 ReturnOrPass!(TReturn) rope; 107 auto call = createCall!METHOD(mocked, name, args); 108 debugLog("checking Recording..."); 109 if (Recording) 110 { 111 auto expectation = createExpectation!(METHOD)(mocked, name, args); 112 Record(expectation, call); 113 return rope; 114 } 115 116 debugLog("checking for matching expectation..."); 117 auto expectation = Match(call); 118 119 debugLog("checking if expectation is null..."); 120 if (expectation is null) 121 { 122 if (AllowUnexpected()) 123 return rope; 124 throw new ExpectationViolationException("Unexpected call to method: " ~ call.toString()); 125 } 126 127 rope = expectation.action.getActor().act!(TReturn, ARGS)(args); 128 debugLog("returning..."); 129 return rope; 130 } 131 132 CallExpectation Match(Call call) 133 { 134 _calls ~= call; 135 auto exp = _rootGroupExpectation.match(call); 136 if (exp is null) 137 _unexpectedCalls ~= _calls; 138 return exp; 139 } 140 141 CallExpectation LastRecordedCallExpectation () 142 { 143 return _lastRecordedCallExpectation; 144 } 145 146 Call LastRecordedCall () 147 { 148 return _lastRecordedCall; 149 } 150 151 void Verify (bool checkUnmatchedExpectations, bool checkUnexpectedCalls) 152 { 153 string expectationError = ""; 154 if (checkUnmatchedExpectations && !_rootGroupExpectation.satisfied) 155 expectationError~="\n" ~ _rootGroupExpectation.toString(); 156 157 if (checkUnexpectedCalls && _unexpectedCalls.length > 0) 158 expectationError~="\n" ~ UnexpectedCallsReport; 159 if (expectationError != "") 160 throw new ExpectationViolationException(expectationError); 161 } 162 163 string UnexpectedCallsReport() 164 { 165 import std.array; 166 auto apndr = appender!(string); 167 apndr.put("Unexpected calls(calls):\n"); 168 foreach(Call ev; _unexpectedCalls) 169 { 170 apndr.put(ev.toString()); 171 apndr.put("\n"); 172 } 173 return apndr.data; 174 } 175 176 version (DMocksTest) 177 { 178 unittest { 179 mixin(test!("repository record/replay")); 180 181 MockRepository r = new MockRepository(); 182 assert (r.Recording()); 183 r.Replay(); 184 assert (!r.Recording()); 185 r.BackToRecord(); 186 assert (r.Recording()); 187 } 188 189 190 // test for correctly formulated template 191 unittest { 192 class A 193 { 194 public void a() 195 { 196 } 197 } 198 auto a = new A; 199 auto c = new MockRepository(); 200 auto mid = new MockId; 201 //c.Call!(a.a)(mid, "a"); 202 static assert(__traits(compiles, c.MethodCall!(a.a)(mid, "a"))); 203 } 204 } 205 }