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