blob: ff6e5e528086305d2a336f62bbd8873a3c8a834a [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library mock.call_matcher;
import 'package:matcher/matcher.dart';
import 'util.dart';
/**
* A [CallMatcher] is a special matcher used to match method calls (i.e.
* a method name and set of arguments). It is not a [Matcher] like the
* unit test [Matcher], but instead represents a method name and a
* collection of [Matcher]s, one per argument, that will be applied
* to the parameters to decide if the method call is a match.
*/
class CallMatcher {
Matcher nameFilter;
List<Matcher> argMatchers;
/**
* Constructor for [CallMatcher]. [name] can be null to
* match anything, or a literal [String], a predicate [Function],
* or a [Matcher]. The various arguments can be scalar values or
* [Matcher]s.
*/
CallMatcher([name,
arg0 = NO_ARG,
arg1 = NO_ARG,
arg2 = NO_ARG,
arg3 = NO_ARG,
arg4 = NO_ARG,
arg5 = NO_ARG,
arg6 = NO_ARG,
arg7 = NO_ARG,
arg8 = NO_ARG,
arg9 = NO_ARG]) {
if (name == null) {
nameFilter = anything;
} else {
nameFilter = wrapMatcher(name);
}
argMatchers = new List<Matcher>();
if (identical(arg0, NO_ARG)) return;
argMatchers.add(wrapMatcher(arg0));
if (identical(arg1, NO_ARG)) return;
argMatchers.add(wrapMatcher(arg1));
if (identical(arg2, NO_ARG)) return;
argMatchers.add(wrapMatcher(arg2));
if (identical(arg3, NO_ARG)) return;
argMatchers.add(wrapMatcher(arg3));
if (identical(arg4, NO_ARG)) return;
argMatchers.add(wrapMatcher(arg4));
if (identical(arg5, NO_ARG)) return;
argMatchers.add(wrapMatcher(arg5));
if (identical(arg6, NO_ARG)) return;
argMatchers.add(wrapMatcher(arg6));
if (identical(arg7, NO_ARG)) return;
argMatchers.add(wrapMatcher(arg7));
if (identical(arg8, NO_ARG)) return;
argMatchers.add(wrapMatcher(arg8));
if (identical(arg9, NO_ARG)) return;
argMatchers.add(wrapMatcher(arg9));
}
/**
* We keep our behavior specifications in a Map, which is keyed
* by the [CallMatcher]. To make the keys unique and to get a
* descriptive value for the [CallMatcher] we have this override
* of [toString()].
*/
String toString() {
Description d = new StringDescription();
d.addDescriptionOf(nameFilter);
// If the nameFilter was a simple string - i.e. just a method name -
// strip the quotes to make this more natural in appearance.
if (d.toString()[0] == "'") {
d.replace(d.toString().substring(1, d.toString().length - 1));
}
d.add('(');
for (var i = 0; i < argMatchers.length; i++) {
if (i > 0) d.add(', ');
d.addDescriptionOf(argMatchers[i]);
}
d.add(')');
return d.toString();
}
/**
* Given a [method] name and list of [arguments], return true
* if it matches this [CallMatcher.
*/
bool matches(String method, List arguments) {
var matchState = {};
if (!nameFilter.matches(method, matchState)) {
return false;
}
var numArgs = (arguments == null) ? 0 : arguments.length;
if (numArgs < argMatchers.length) {
throw new Exception("Less arguments than matchers for $method.");
}
for (var i = 0; i < argMatchers.length; i++) {
if (!argMatchers[i].matches(arguments[i], matchState)) {
return false;
}
}
return true;
}
}
/**
* Returns a [CallMatcher] for the specified signature. [method] can be
* null to match anything, or a literal [String], a predicate [Function],
* or a [Matcher]. The various arguments can be scalar values or [Matcher]s.
* To match getters and setters, use "get " and "set " prefixes on the names.
* For example, for a property "foo", you could use "get foo" and "set foo"
* as literal string arguments to callsTo to match the getter and setter
* of "foo".
*/
CallMatcher callsTo([method,
arg0 = NO_ARG,
arg1 = NO_ARG,
arg2 = NO_ARG,
arg3 = NO_ARG,
arg4 = NO_ARG,
arg5 = NO_ARG,
arg6 = NO_ARG,
arg7 = NO_ARG,
arg8 = NO_ARG,
arg9 = NO_ARG]) {
return new CallMatcher(method, arg0, arg1, arg2, arg3, arg4,
arg5, arg6, arg7, arg8, arg9);
}