| Index: pkg/analysis_server/test/typed_mock.dart
|
| diff --git a/pkg/analysis_server/test/typed_mock.dart b/pkg/analysis_server/test/typed_mock.dart
|
| index 74b19abf8979351b6132ac40a8b8efcbdb2af1dd..0b991257f7fc64c78fd99b8bd84b28af02a36d49 100644
|
| --- a/pkg/analysis_server/test/typed_mock.dart
|
| +++ b/pkg/analysis_server/test/typed_mock.dart
|
| @@ -9,7 +9,10 @@ _InvocationMatcher _lastMatcher;
|
| /// method is called.
|
| Behavior when(_ignored) {
|
| try {
|
| - var behavior = new Behavior();
|
| + var mock = _lastMatcher._mock;
|
| + mock._removeLastInvocation();
|
| + // set behavior
|
| + var behavior = new Behavior._(_lastMatcher);
|
| _lastMatcher._behavior = behavior;
|
| return behavior;
|
| } finally {
|
| @@ -18,13 +21,34 @@ Behavior when(_ignored) {
|
| }
|
| }
|
|
|
| +/// Verifies certain behavior happened a specified number of times.
|
| +Verifier verify(_ignored) {
|
| + try {
|
| + var mock = _lastMatcher._mock;
|
| + mock._removeLastInvocation();
|
| + // set verifier
|
| + return new Verifier._(mock, _lastMatcher);
|
| + } finally {
|
| + // clear to prevent memory leak
|
| + _lastMatcher = null;
|
| + }
|
| +}
|
| +
|
| +/// [VerifyError] is thrown when one of the [verify] checks fails.
|
| +class VerifyError {
|
| + final String message;
|
| + VerifyError(this.message);
|
| + String toString() => 'VerifyError: $message';
|
| +}
|
|
|
| class _InvocationMatcher {
|
| - List<ArgumentMatcher> _matchers = [];
|
| + final Symbol _member;
|
| + final TypedMock _mock;
|
| + final List<ArgumentMatcher> _matchers = [];
|
|
|
| Behavior _behavior;
|
|
|
| - _InvocationMatcher(Invocation invocation) {
|
| + _InvocationMatcher(this._mock, this._member, Invocation invocation) {
|
| invocation.positionalArguments.forEach((argument) {
|
| ArgumentMatcher matcher;
|
| if (argument is ArgumentMatcher) {
|
| @@ -52,7 +76,12 @@ class _InvocationMatcher {
|
| }
|
| }
|
|
|
| +
|
| class Behavior {
|
| + final _InvocationMatcher _matcher;
|
| +
|
| + Behavior._(this._matcher);
|
| +
|
| bool _thenFunctionEnabled = false;
|
| Function _thenFunction;
|
|
|
| @@ -115,8 +144,8 @@ class Behavior {
|
| // list
|
| if (_returnListEnabled) {
|
| if (_returnListIndex >= _returnList.length) {
|
| - throw new StateError('List of ${_returnList.length} elements'
|
| - ' $_returnList has been exhausted.');
|
| + throw new StateError('All ${_returnList.length} elements for '
|
| + '${_matcher._member} from $_returnList have been exhausted.');
|
| }
|
| return _returnList[_returnListIndex++];
|
| }
|
| @@ -130,29 +159,98 @@ class Behavior {
|
| }
|
|
|
|
|
| +class Verifier {
|
| + final TypedMock _mock;
|
| + final _InvocationMatcher _matcher;
|
| +
|
| + Verifier._(this._mock, this._matcher);
|
| +
|
| + void never() {
|
| + times(0);
|
| + }
|
| +
|
| + void once() {
|
| + times(1);
|
| + }
|
| +
|
| + void times(int expected) {
|
| + var times = _count();
|
| + if (times != expected) {
|
| + var member = _matcher._member;
|
| + throw new VerifyError('$expected expected, but $times'
|
| + ' invocations of $member recorded.');
|
| + }
|
| + }
|
| +
|
| + void atLeast(int expected) {
|
| + var times = _count();
|
| + if (times < expected) {
|
| + var member = _matcher._member;
|
| + throw new VerifyError('At least $expected expected, but only $times'
|
| + ' invocations of $member recorded.');
|
| + }
|
| + }
|
| +
|
| + void atMost(int expected) {
|
| + var times = _count();
|
| + if (times > expected) {
|
| + var member = _matcher._member;
|
| + throw new VerifyError('At most $expected expected, but $times'
|
| + ' invocations of $member recorded.');
|
| + }
|
| + }
|
| +
|
| + int _count() {
|
| + var times = 0;
|
| + _mock._invocations.forEach((invocation) {
|
| + if (invocation.memberName != _matcher._member) {
|
| + return;
|
| + }
|
| + if (!_matcher.match(invocation)) {
|
| + return;
|
| + }
|
| + times++;
|
| + });
|
| + return times;
|
| + }
|
| +}
|
| +
|
| +
|
| class TypedMock {
|
| - final Map<Symbol, List<_InvocationMatcher>> _invocationMatchersMap = {};
|
| + final Map<Symbol, List<_InvocationMatcher>> _matchersMap = {};
|
| +
|
| + final List<Invocation> _invocations = [];
|
|
|
| noSuchMethod(Invocation invocation) {
|
| + _invocations.add(invocation);
|
| var member = invocation.memberName;
|
| // prepare invocation matchers
|
| - var matchers = _invocationMatchersMap[member];
|
| + var matchers = _matchersMap[member];
|
| if (matchers == null) {
|
| matchers = [];
|
| - _invocationMatchersMap[member] = matchers;
|
| + _matchersMap[member] = matchers;
|
| }
|
| // check if there is a matcher
|
| for (var matcher in matchers) {
|
| if (matcher.match(invocation)) {
|
| _lastMatcher = matcher;
|
| - return matcher._behavior._getReturnValue(invocation);
|
| + // generate value if there is a behavior
|
| + if (matcher._behavior != null) {
|
| + return matcher._behavior._getReturnValue(invocation);
|
| + }
|
| + // probably verification
|
| + return null;
|
| }
|
| }
|
| // add a new matcher
|
| - _InvocationMatcher matcher = new _InvocationMatcher(invocation);
|
| + var matcher = new _InvocationMatcher(this, member, invocation);
|
| matchers.add(matcher);
|
| _lastMatcher = matcher;
|
| }
|
| +
|
| + void _removeLastInvocation() {
|
| + _invocations.removeLast();
|
| + }
|
| }
|
|
|
|
|
|
|