Dart API ReferenceunittestLogEntryList

LogEntryList class

We do verification on a list of LogEntrys. To allow chaining of calls to verify, we encapsulate such a list in the LogEntryList class.

class LogEntryList {
  String filter;
  List<LogEntry> logs;
  LogEntryList([this.filter]) {
    logs = new List<LogEntry>();
  }

  /** Add a [LogEntry] to the log. */
  add(LogEntry entry) => logs.add(entry);

  /** Get the first entry, or null if no entries. */
  get first => (logs == null || logs.length == 0) ? null : logs[0];

  /** Get the last entry, or null if no entries. */
  get last => (logs == null || logs.length == 0) ? null : logs.last();

  /** Creates a LogEntry predicate function from the argument. */
  Function _makePredicate(arg) {
    if (arg == null) {
      return (e) => true;
    } else if (arg is CallMatcher) {
      return (e) => arg.matches(e.methodName, e.args);
    } else if (arg is Function) {
      return arg;
    } else {
      throw new Exception("Invalid argument to _makePredicate.");
    }
  }

  /**
   * Create a new [LogEntryList] consisting of [LogEntry]s from
   * this list that match the specified [mockNameFilter] and [logFilter].
   * [mockNameFilter] can be null, a [String], a predicate [Function],
   * or a [Matcher]. If [mockNameFilter] is null, this is the same as
   * [anything].
   * If [logFilter] is null, all entries in the log will be returned.
   * Otherwise [logFilter] should be a [CallMatcher] or  predicate function
   * that takes a [LogEntry] and returns a bool.
   * If [destructive] is true, the log entries are removed from the
   * original list.
   */
  LogEntryList getMatches([mockNameFilter,
                          logFilter,
                          Matcher actionMatcher,
                          bool destructive = false]) {
    if (mockNameFilter == null) {
      mockNameFilter = anything;
    } else {
      mockNameFilter = wrapMatcher(mockNameFilter);
    }
    Function entryFilter = _makePredicate(logFilter);
    String filterName = _qualifiedName(mockNameFilter, logFilter.toString());
    LogEntryList rtn = new LogEntryList(filterName);
    MatchState matchState = new MatchState();
    for (var i = 0; i < logs.length; i++) {
      LogEntry entry = logs[i];
      if (mockNameFilter.matches(entry.mockName, matchState) &&
          entryFilter(entry)) {
        if (actionMatcher == null ||
            actionMatcher.matches(entry, matchState)) {
          rtn.add(entry);
          if (destructive) {
            logs.removeRange(i--, 1);
          }
        }
      }
    }
    return rtn;
  }

  /** Apply a unit test [Matcher] to the [LogEntryList]. */
  LogEntryList verify(Matcher matcher) {
    if (_mockFailureHandler == null) {
      _mockFailureHandler =
          new _MockFailureHandler(getOrCreateExpectFailureHandler());
    }
    expect(logs, matcher, filter, _mockFailureHandler);
    return this;
  }

  /**
   * Iterate through the list and call the [validator] function with the
   * log [List] and position. The [validator] should return the number of
   * positions to advance upon success, or zero upon failure. When zero is
   * returned an error is reported. [reason] can be used to provide a
   * more descriptive failure message. If a failure occurred false will be
   * returned (unless the failure handler itself threw an exception);
   * otherwise true is returned.
   * The use case here is to perform more complex validations; for example
   * we may want to assert that the return value from some function is
   * later used as a parameter to a following function. If we filter the logs
   * to include just these two functions we can write a simple validator to
   * do this check.
   */
  bool stepwiseValidate(StepValidator validator, [String reason = '']) {
    if (_mockFailureHandler == null) {
      _mockFailureHandler =
          new _MockFailureHandler(getOrCreateExpectFailureHandler());
    }
    var i = 0;
    while (i < logs.length) {
      var n = validator(logs, i);
      if (n == 0) {
        if (reason.length > 0) {
          reason = ': $reason';
        }
        _mockFailureHandler.fail("Stepwise validation failed at $filter "
                                 "position $i$reason");
        return false;
      } else {
        i += n;
      }
    }
    return true;
  }

  /**
   * Turn the logs into human-readable text. If [baseTime] is specified
   * then each entry is prefixed with the offset from that time in
   * milliseconds; otherwise the time of day is used.
   */
  String toString([Date baseTime]) {
    String s = '';
    for (var e in logs) {
      s = '$s${e.toString(baseTime)}\n';
    }
    return s;
  }

  /**
   *  Find the first log entry that satisfies [logFilter] and
   *  return its position. A search [start] position can be provided
   *  to allow for repeated searches. [logFilter] can be a [CallMatcher],
   *  or a predicate function that takes a [LogEntry] argument and returns
   *  a bool. If [logFilter] is null, it will match any [LogEntry].
   *  If no entry is found, then [failureReturnValue] is returned.
   *  After each check the position is updated by [skip], so using
   *  [skip] of -1 allows backward searches, using a [skip] of 2 can
   *  be used to check pairs of adjacent entries, and so on.
   */
  int findLogEntry(logFilter, [int start = 0, int failureReturnValue = -1,
      skip = 1]) {
    logFilter = _makePredicate(logFilter);
    int pos = start;
    while (pos >= 0 && pos < logs.length) {
      if (logFilter(logs[pos])) {
        return pos;
      }
      pos += skip;
    }
    return failureReturnValue;
  }

  /**
   * Returns log events that happened up to the first one that
   * satisfies [logFilter]. If [inPlace] is true, then returns
   * this LogEntryList after removing the from the first satisfier;
   * onwards otherwise a new list is created. [description]
   * is used to create a new name for the resulting list.
   * [defaultPosition] is used as the index of the matching item in
   * the case that no match is found.
   */
  LogEntryList _head(logFilter, bool inPlace,
                     String description, int defaultPosition) {
    if (filter != null) {
      description = '$filter $description';
    }
    int pos = findLogEntry(logFilter, 0, defaultPosition);
    if (inPlace) {
      if (pos < logs.length) {
        logs.removeRange(pos, logs.length - pos);
      }
      filter = description;
      return this;
    } else {
      LogEntryList newList = new LogEntryList(description);
      for (var i = 0; i < pos; i++) {
        newList.logs.add(logs[i]);
      }
      return newList;
    }
  }

  /**
   * Returns log events that happened from the first one that
   * satisfies [logFilter]. If [inPlace] is true, then returns
   * this LogEntryList after removing the entries up to the first
   * satisfier; otherwise a new list is created. [description]
   * is used to create a new name for the resulting list.
   * [defaultPosition] is used as the index of the matching item in
   * the case that no match is found.
   */
  LogEntryList _tail(logFilter, bool inPlace,
                     String description, int defaultPosition) {
    if (filter != null) {
      description = '$filter $description';
    }
    int pos = findLogEntry(logFilter, 0, defaultPosition);
    if (inPlace) {
      if (pos > 0) {
        logs.removeRange(0, pos);
      }
      filter = description;
      return this;
    } else {
      LogEntryList newList = new LogEntryList(description);
      while (pos < logs.length) {
        newList.logs.add(logs[pos++]);
      }
      return newList;
    }
  }

  /**
   * Returns log events that happened after [when]. If [inPlace]
   * is true, then it returns this LogEntryList after removing
   * the entries that happened up to [when]; otherwise a new
   * list is created.
   */
  LogEntryList after(Date when, [bool inPlace = false]) =>
      _tail((e) => e.time > when, inPlace, 'after $when', logs.length);

  /**
   * Returns log events that happened from [when] onwards. If
   * [inPlace] is true, then it returns this LogEntryList after
   * removing the entries that happened before [when]; otherwise
   * a new list is created.
   */
  LogEntryList from(Date when, [bool inPlace = false]) =>
      _tail((e) => e.time >= when, inPlace, 'from $when', logs.length);

  /**
   * Returns log events that happened until [when]. If [inPlace]
   * is true, then it returns this LogEntryList after removing
   * the entries that happened after [when]; otherwise a new
   * list is created.
   */
  LogEntryList until(Date when, [bool inPlace = false]) =>
      _head((e) => e.time > when, inPlace, 'until $when', logs.length);

  /**
   * Returns log events that happened before [when]. If [inPlace]
   * is true, then it returns this LogEntryList after removing
   * the entries that happened from [when] onwards; otherwise a new
   * list is created.
   */
  LogEntryList before(Date when, [bool inPlace = false]) =>
      _head((e) => e.time >= when, inPlace, 'before $when', logs.length);

  /**
   * Returns log events that happened after [logEntry]'s time.
   * If [inPlace] is true, then it returns this LogEntryList after
   * removing the entries that happened up to [when]; otherwise a new
   * list is created. If [logEntry] is null the current time is used.
   */
  LogEntryList afterEntry(LogEntry logEntry, [bool inPlace = false]) =>
      after(logEntry == null ? new Date.now() : logEntry.time);

  /**
   * Returns log events that happened from [logEntry]'s time onwards.
   * If [inPlace] is true, then it returns this LogEntryList after
   * removing the entries that happened before [when]; otherwise
   * a new list is created. If [logEntry] is null the current time is used.
   */
  LogEntryList fromEntry(LogEntry logEntry, [bool inPlace = false]) =>
      from(logEntry == null ? new Date.now() : logEntry.time);

  /**
   * Returns log events that happened until [logEntry]'s time. If
   * [inPlace] is true, then it returns this LogEntryList after removing
   * the entries that happened after [when]; otherwise a new
   * list is created. If [logEntry] is null the epoch time is used.
   */
  LogEntryList untilEntry(LogEntry logEntry, [bool inPlace = false]) =>
      until(logEntry == null ?
          new Date.fromMillisecondsSinceEpoch(0) : logEntry.time);

  /**
   * Returns log events that happened before [logEntry]'s time. If
   * [inPlace] is true, then it returns this LogEntryList after removing
   * the entries that happened from [when] onwards; otherwise a new
   * list is created. If [logEntry] is null the epoch time is used.
   */
  LogEntryList beforeEntry(LogEntry logEntry, [bool inPlace = false]) =>
      before(logEntry == null ?
          new Date.fromMillisecondsSinceEpoch(0) : logEntry.time);

  /**
   * Returns log events that happened after the first event in [segment].
   * If [inPlace] is true, then it returns this LogEntryList after removing
   * the entries that happened earlier; otherwise a new list is created.
   */
  LogEntryList afterFirst(LogEntryList segment, [bool inPlace = false]) =>
      afterEntry(segment.first, inPlace);

  /**
   * Returns log events that happened after the last event in [segment].
   * If [inPlace] is true, then it returns this LogEntryList after removing
   * the entries that happened earlier; otherwise a new list is created.
   */
  LogEntryList afterLast(LogEntryList segment, [bool inPlace = false]) =>
      afterEntry(segment.last, inPlace);

  /**
   * Returns log events that happened from the time of the first event in
   * [segment] onwards. If [inPlace] is true, then it returns this
   * LogEntryList after removing the earlier entries; otherwise a new list
   * is created.
   */
  LogEntryList fromFirst(LogEntryList segment, [bool inPlace = false]) =>
      fromEntry(segment.first, inPlace);

  /**
   * Returns log events that happened from the time of the last event in
   * [segment] onwards. If [inPlace] is true, then it returns this
   * LogEntryList after removing the earlier entries; otherwise a new list
   * is created.
   */
  LogEntryList fromLast(LogEntryList segment, [bool inPlace = false]) =>
      fromEntry(segment.last, inPlace);

  /**
   * Returns log events that happened until the first event in [segment].
   * If [inPlace] is true, then it returns this LogEntryList after removing
   * the entries that happened later; otherwise a new list is created.
   */
  LogEntryList untilFirst(LogEntryList segment, [bool inPlace = false]) =>
      untilEntry(segment.first, inPlace);

  /**
   * Returns log events that happened until the last event in [segment].
   * If [inPlace] is true, then it returns this LogEntryList after removing
   * the entries that happened later; otherwise a new list is created.
   */
  LogEntryList untilLast(LogEntryList segment, [bool inPlace = false]) =>
      untilEntry(segment.last, inPlace);

  /**
   * Returns log events that happened before the first event in [segment].
   * If [inPlace] is true, then it returns this LogEntryList after removing
   * the entries that happened later; otherwise a new list is created.
   */
  LogEntryList beforeFirst(LogEntryList segment, [bool inPlace = false]) =>
      beforeEntry(segment.first, inPlace);

  /**
   * Returns log events that happened before the last event in [segment].
   * If [inPlace] is true, then it returns this LogEntryList after removing
   * the entries that happened later; otherwise a new list is created.
   */
  LogEntryList beforeLast(LogEntryList segment, [bool inPlace = false]) =>
      beforeEntry(segment.last, inPlace);

  /**
   * Iterate through the LogEntryList looking for matches to the entries
   * in [keys]; for each match found the closest [distance] neighboring log
   * entries that match [mockNameFilter] and [logFilter] will be included in
   * the result. If [isPreceding] is true we use the neighbors that precede
   * the matched entry; else we use the neighbors that followed.
   * If [includeKeys] is true then the entries in [keys] that resulted in
   * entries in the output list are themselves included in the output list. If
   * [distance] is zero then all matches are included.
   */
  LogEntryList _neighboring(bool isPreceding,
                            LogEntryList keys,
                            mockNameFilter,
                            logFilter,
                            int distance,
                            bool includeKeys) {
    String filterName = 'Calls to '
        '${_qualifiedName(mockNameFilter, logFilter.toString())} '
        '${isPreceding?"preceding":"following"} ${keys.filter}';

    LogEntryList rtn = new LogEntryList(filterName);

    // Deal with the trivial case.
    if (logs.length == 0 || keys.logs.length == 0) {
      return rtn;
    }

    // Normalize the mockNameFilter and logFilter values.
    if (mockNameFilter == null) {
      mockNameFilter = anything;
    } else {
      mockNameFilter = wrapMatcher(mockNameFilter);
    }
    logFilter = _makePredicate(logFilter);

    // The scratch list is used to hold matching entries when we
    // are doing preceding neighbors. The remainingCount is used to
    // keep track of how many matching entries we can still add in the
    // current segment (0 if we are doing doing following neighbors, until
    // we get our first key match).
    List scratch = null;
    int remainingCount = 0;
    if (isPreceding) {
      scratch = new List();
      remainingCount = logs.length;
    }

    var keyIterator = keys.logs.iterator();
    LogEntry keyEntry = keyIterator.next();
    MatchState matchState = new MatchState();

    for (LogEntry logEntry in logs) {
      // If we have a log entry match, copy the saved matches from the
      // scratch buffer into the return list, as well as the matching entry,
      // if appropriate, and reset the scratch buffer. Continue processing
      // from the next key entry.
      if (keyEntry == logEntry) {
        if (scratch != null) {
          int numToCopy = scratch.length;
          if (distance > 0 && distance < numToCopy) {
            numToCopy = distance;
          }
          for (var i = scratch.length - numToCopy; i < scratch.length; i++) {
            rtn.logs.add(scratch[i]);
          }
          scratch.clear();
        } else {
          remainingCount = distance > 0 ? distance : logs.length;
        }
        if (includeKeys) {
          rtn.logs.add(keyEntry);
        }
        if (keyIterator.hasNext()) {
          keyEntry = keyIterator.next();
        } else if (isPreceding) { // We're done.
          break;
        }
      } else if (remainingCount > 0 &&
                 mockNameFilter.matches(logEntry.mockName, matchState) &&
                 logFilter(logEntry)) {
        if (scratch != null) {
          scratch.add(logEntry);
        } else {
          rtn.logs.add(logEntry);
          --remainingCount;
        }
      }
    }
    return rtn;
  }

  /**
   * Iterate through the LogEntryList looking for matches to the entries
   * in [keys]; for each match found the closest [distance] prior log entries
   * that match [mocknameFilter] and [logFilter] will be included in the result.
   * If [includeKeys] is true then the entries in [keys] that resulted in
   * entries in the output list are themselves included in the output list. If
   * [distance] is zero then all matches are included.
   *
   * The idea here is that you could find log entries that are related to
   * other logs entries in some temporal sense. For example, say we have a
   * method commit() that returns -1 on failure. Before commit() gets called
   * the value being committed is created by process(). We may want to find
   * the calls to process() that preceded calls to commit() that failed.
   * We could do this with:
   *
   *      print(log.preceding(log.getLogs(callsTo('commit'), returning(-1)),
   *          logFilter: callsTo('process')).toString());
   *
   * We might want to include the details of the failing calls to commit()
   * to see what parameters were passed in, in which case we would set
   * [includeKeys].
   *
   * As another simple example, say we wanted to know the three method
   * calls that immediately preceded each failing call to commit():
   *
   *     print(log.preceding(log.getLogs(callsTo('commit'), returning(-1)),
   *         distance: 3).toString());
   */
  LogEntryList preceding(LogEntryList keys,
                         {mockNameFilter: null,
                         logFilter: null,
                         int distance: 1,
                         bool includeKeys: false}) =>
      _neighboring(true, keys, mockNameFilter, logFilter,
          distance, includeKeys);

  /**
   * Iterate through the LogEntryList looking for matches to the entries
   * in [keys]; for each match found the closest [distance] subsequent log
   * entries that match [mocknameFilter] and [logFilter] will be included in
   * the result. If [includeKeys] is true then the entries in [keys] that
   * resulted in entries in the output list are themselves included in the
   * output list. If [distance] is zero then all matches are included.
   * See [preceding] for a usage example.
   */
  LogEntryList following(LogEntryList keys,
                         {mockNameFilter: null,
                         logFilter: null,
                         int distance: 1,
                         bool includeKeys: false}) =>
      _neighboring(false, keys, mockNameFilter, logFilter,
          distance, includeKeys);
}

Constructors

new LogEntryList([String filter]) #

LogEntryList([this.filter]) {
  logs = new List<LogEntry>();
}

Properties

String filter #

String filter;

final first #

Get the first entry, or null if no entries.

get first => (logs == null || logs.length == 0) ? null : logs[0];

final last #

Get the last entry, or null if no entries.

get last => (logs == null || logs.length == 0) ? null : logs.last();

List<LogEntry> logs #

List<LogEntry> logs;

Methods

add(LogEntry entry) #

Add a LogEntry to the log.

add(LogEntry entry) => logs.add(entry);

LogEntryList after(Date when, [bool inPlace = false]) #

Returns log events that happened after when. If inPlace is true, then it returns this LogEntryList after removing the entries that happened up to when; otherwise a new list is created.

LogEntryList after(Date when, [bool inPlace = false]) =>
    _tail((e) => e.time > when, inPlace, 'after $when', logs.length);

LogEntryList afterEntry(LogEntry logEntry, [bool inPlace = false]) #

Returns log events that happened after logEntry's time. If inPlace is true, then it returns this LogEntryList after removing the entries that happened up to when; otherwise a new list is created. If logEntry is null the current time is used.

LogEntryList afterEntry(LogEntry logEntry, [bool inPlace = false]) =>
    after(logEntry == null ? new Date.now() : logEntry.time);

LogEntryList afterFirst(LogEntryList segment, [bool inPlace = false]) #

Returns log events that happened after the first event in segment. If inPlace is true, then it returns this LogEntryList after removing the entries that happened earlier; otherwise a new list is created.

LogEntryList afterFirst(LogEntryList segment, [bool inPlace = false]) =>
    afterEntry(segment.first, inPlace);

LogEntryList afterLast(LogEntryList segment, [bool inPlace = false]) #

Returns log events that happened after the last event in segment. If inPlace is true, then it returns this LogEntryList after removing the entries that happened earlier; otherwise a new list is created.

LogEntryList afterLast(LogEntryList segment, [bool inPlace = false]) =>
    afterEntry(segment.last, inPlace);

LogEntryList before(Date when, [bool inPlace = false]) #

Returns log events that happened before when. If inPlace is true, then it returns this LogEntryList after removing the entries that happened from when onwards; otherwise a new list is created.

LogEntryList before(Date when, [bool inPlace = false]) =>
    _head((e) => e.time >= when, inPlace, 'before $when', logs.length);

LogEntryList beforeEntry(LogEntry logEntry, [bool inPlace = false]) #

Returns log events that happened before logEntry's time. If inPlace is true, then it returns this LogEntryList after removing the entries that happened from when onwards; otherwise a new list is created. If logEntry is null the epoch time is used.

LogEntryList beforeEntry(LogEntry logEntry, [bool inPlace = false]) =>
    before(logEntry == null ?
        new Date.fromMillisecondsSinceEpoch(0) : logEntry.time);

LogEntryList beforeFirst(LogEntryList segment, [bool inPlace = false]) #

Returns log events that happened before the first event in segment. If inPlace is true, then it returns this LogEntryList after removing the entries that happened later; otherwise a new list is created.

LogEntryList beforeFirst(LogEntryList segment, [bool inPlace = false]) =>
    beforeEntry(segment.first, inPlace);

LogEntryList beforeLast(LogEntryList segment, [bool inPlace = false]) #

Returns log events that happened before the last event in segment. If inPlace is true, then it returns this LogEntryList after removing the entries that happened later; otherwise a new list is created.

LogEntryList beforeLast(LogEntryList segment, [bool inPlace = false]) =>
    beforeEntry(segment.last, inPlace);

int findLogEntry(logFilter, [int start = 0, int failureReturnValue = -1, skip = 1]) #

Find the first log entry that satisfies logFilter and return its position. A search start position can be provided to allow for repeated searches. logFilter can be a CallMatcher, or a predicate function that takes a LogEntry argument and returns a bool. If logFilter is null, it will match any LogEntry. If no entry is found, then failureReturnValue is returned. After each check the position is updated by skip, so using skip of -1 allows backward searches, using a skip of 2 can be used to check pairs of adjacent entries, and so on.

int findLogEntry(logFilter, [int start = 0, int failureReturnValue = -1,
    skip = 1]) {
  logFilter = _makePredicate(logFilter);
  int pos = start;
  while (pos >= 0 && pos < logs.length) {
    if (logFilter(logs[pos])) {
      return pos;
    }
    pos += skip;
  }
  return failureReturnValue;
}

LogEntryList following(LogEntryList keys, [mockNameFilter = null, logFilter = null, int distance = 1, bool includeKeys = false]) #

Iterate through the LogEntryList looking for matches to the entries in keys; for each match found the closest distance subsequent log entries that match mocknameFilter and logFilter will be included in the result. If includeKeys is true then the entries in keys that resulted in entries in the output list are themselves included in the output list. If distance is zero then all matches are included. See preceding for a usage example.

LogEntryList following(LogEntryList keys,
                       {mockNameFilter: null,
                       logFilter: null,
                       int distance: 1,
                       bool includeKeys: false}) =>
    _neighboring(false, keys, mockNameFilter, logFilter,
        distance, includeKeys);

LogEntryList from(Date when, [bool inPlace = false]) #

Returns log events that happened from when onwards. If inPlace is true, then it returns this LogEntryList after removing the entries that happened before when; otherwise a new list is created.

LogEntryList from(Date when, [bool inPlace = false]) =>
    _tail((e) => e.time >= when, inPlace, 'from $when', logs.length);

LogEntryList fromEntry(LogEntry logEntry, [bool inPlace = false]) #

Returns log events that happened from logEntry's time onwards. If inPlace is true, then it returns this LogEntryList after removing the entries that happened before when; otherwise a new list is created. If logEntry is null the current time is used.

LogEntryList fromEntry(LogEntry logEntry, [bool inPlace = false]) =>
    from(logEntry == null ? new Date.now() : logEntry.time);

LogEntryList fromFirst(LogEntryList segment, [bool inPlace = false]) #

Returns log events that happened from the time of the first event in segment onwards. If inPlace is true, then it returns this LogEntryList after removing the earlier entries; otherwise a new list is created.

LogEntryList fromFirst(LogEntryList segment, [bool inPlace = false]) =>
    fromEntry(segment.first, inPlace);

LogEntryList fromLast(LogEntryList segment, [bool inPlace = false]) #

Returns log events that happened from the time of the last event in segment onwards. If inPlace is true, then it returns this LogEntryList after removing the earlier entries; otherwise a new list is created.

LogEntryList fromLast(LogEntryList segment, [bool inPlace = false]) =>
    fromEntry(segment.last, inPlace);

LogEntryList getMatches([mockNameFilter, logFilter, Matcher actionMatcher, bool destructive = false]) #

Create a new LogEntryList consisting of LogEntrys from this list that match the specified mockNameFilter and logFilter. mockNameFilter can be null, a String, a predicate Function, or a Matcher. If mockNameFilter is null, this is the same as anything. If logFilter is null, all entries in the log will be returned. Otherwise logFilter should be a CallMatcher or predicate function that takes a LogEntry and returns a bool. If destructive is true, the log entries are removed from the original list.

LogEntryList getMatches([mockNameFilter,
                        logFilter,
                        Matcher actionMatcher,
                        bool destructive = false]) {
  if (mockNameFilter == null) {
    mockNameFilter = anything;
  } else {
    mockNameFilter = wrapMatcher(mockNameFilter);
  }
  Function entryFilter = _makePredicate(logFilter);
  String filterName = _qualifiedName(mockNameFilter, logFilter.toString());
  LogEntryList rtn = new LogEntryList(filterName);
  MatchState matchState = new MatchState();
  for (var i = 0; i < logs.length; i++) {
    LogEntry entry = logs[i];
    if (mockNameFilter.matches(entry.mockName, matchState) &&
        entryFilter(entry)) {
      if (actionMatcher == null ||
          actionMatcher.matches(entry, matchState)) {
        rtn.add(entry);
        if (destructive) {
          logs.removeRange(i--, 1);
        }
      }
    }
  }
  return rtn;
}

LogEntryList preceding(LogEntryList keys, [mockNameFilter = null, logFilter = null, int distance = 1, bool includeKeys = false]) #

Iterate through the LogEntryList looking for matches to the entries in keys; for each match found the closest distance prior log entries that match mocknameFilter and logFilter will be included in the result. If includeKeys is true then the entries in keys that resulted in entries in the output list are themselves included in the output list. If distance is zero then all matches are included.

The idea here is that you could find log entries that are related to other logs entries in some temporal sense. For example, say we have a method commit() that returns -1 on failure. Before commit() gets called the value being committed is created by process(). We may want to find the calls to process() that preceded calls to commit() that failed. We could do this with:

 print(log.preceding(log.getLogs(callsTo('commit'), returning(-1)),
     logFilter: callsTo('process')).toString());

We might want to include the details of the failing calls to commit() to see what parameters were passed in, in which case we would set includeKeys.

As another simple example, say we wanted to know the three method calls that immediately preceded each failing call to commit():

print(log.preceding(log.getLogs(callsTo('commit'), returning(-1)),
    distance: 3).toString());
LogEntryList preceding(LogEntryList keys,
                       {mockNameFilter: null,
                       logFilter: null,
                       int distance: 1,
                       bool includeKeys: false}) =>
    _neighboring(true, keys, mockNameFilter, logFilter,
        distance, includeKeys);

bool stepwiseValidate(StepValidator validator, [String reason = '']) #

Iterate through the list and call the validator function with the log List and position. The validator should return the number of positions to advance upon success, or zero upon failure. When zero is returned an error is reported. reason can be used to provide a more descriptive failure message. If a failure occurred false will be returned (unless the failure handler itself threw an exception); otherwise true is returned. The use case here is to perform more complex validations; for example we may want to assert that the return value from some function is later used as a parameter to a following function. If we filter the logs to include just these two functions we can write a simple validator to do this check.

bool stepwiseValidate(StepValidator validator, [String reason = '']) {
  if (_mockFailureHandler == null) {
    _mockFailureHandler =
        new _MockFailureHandler(getOrCreateExpectFailureHandler());
  }
  var i = 0;
  while (i < logs.length) {
    var n = validator(logs, i);
    if (n == 0) {
      if (reason.length > 0) {
        reason = ': $reason';
      }
      _mockFailureHandler.fail("Stepwise validation failed at $filter "
                               "position $i$reason");
      return false;
    } else {
      i += n;
    }
  }
  return true;
}

String toString([Date baseTime]) #

Turn the logs into human-readable text. If baseTime is specified then each entry is prefixed with the offset from that time in milliseconds; otherwise the time of day is used.

String toString([Date baseTime]) {
  String s = '';
  for (var e in logs) {
    s = '$s${e.toString(baseTime)}\n';
  }
  return s;
}

LogEntryList until(Date when, [bool inPlace = false]) #

Returns log events that happened until when. If inPlace is true, then it returns this LogEntryList after removing the entries that happened after when; otherwise a new list is created.

LogEntryList until(Date when, [bool inPlace = false]) =>
    _head((e) => e.time > when, inPlace, 'until $when', logs.length);

LogEntryList untilEntry(LogEntry logEntry, [bool inPlace = false]) #

Returns log events that happened until logEntry's time. If inPlace is true, then it returns this LogEntryList after removing the entries that happened after when; otherwise a new list is created. If logEntry is null the epoch time is used.

LogEntryList untilEntry(LogEntry logEntry, [bool inPlace = false]) =>
    until(logEntry == null ?
        new Date.fromMillisecondsSinceEpoch(0) : logEntry.time);

LogEntryList untilFirst(LogEntryList segment, [bool inPlace = false]) #

Returns log events that happened until the first event in segment. If inPlace is true, then it returns this LogEntryList after removing the entries that happened later; otherwise a new list is created.

LogEntryList untilFirst(LogEntryList segment, [bool inPlace = false]) =>
    untilEntry(segment.first, inPlace);

LogEntryList untilLast(LogEntryList segment, [bool inPlace = false]) #

Returns log events that happened until the last event in segment. If inPlace is true, then it returns this LogEntryList after removing the entries that happened later; otherwise a new list is created.

LogEntryList untilLast(LogEntryList segment, [bool inPlace = false]) =>
    untilEntry(segment.last, inPlace);

LogEntryList verify(Matcher matcher) #

Apply a unit test Matcher to the LogEntryList.

LogEntryList verify(Matcher matcher) {
  if (_mockFailureHandler == null) {
    _mockFailureHandler =
        new _MockFailureHandler(getOrCreateExpectFailureHandler());
  }
  expect(logs, matcher, filter, _mockFailureHandler);
  return this;
}