blob: b45de5b6daac97a77a919288212361864977ccf3 [file] [log] [blame]
// Copyright (c) 2017, 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.
import 'dart:async';
import 'package:build/src/builder/logging.dart';
import 'package:matcher/matcher.dart';
import 'package:logging/logging.dart';
/// Executes [run] with a new [Logger], returning the resulting log records.
///
/// The returned [Stream] is _closed_ after the [run] function is executed. If
/// [run] returns a [Future], that future is awaited _before_ the stream is
/// closed.
///
/// ```dart
/// test('should log "uh oh!"', () async {
/// final logs = recordLogs(() => runBuilder());
/// expect(logs, emitsInOrder([
/// anyLogOf('uh oh!'),
/// ]);
/// });
/// ```
Stream<LogRecord> recordLogs(dynamic Function() run, {String name = ''}) {
final logger = Logger(name);
Timer.run(() async {
await scopeLogAsync(() => Future.value(run()), logger);
logger.clearListeners();
});
return logger.onRecord;
}
/// Matches [LogRecord] of any level whose message is [messageOrMatcher].
///
/// ```dart
/// anyLogOf('Hello World)'; // Exactly match 'Hello World'.
/// anyLogOf(contains('ERROR')); // Contains the sub-string 'ERROR'.
/// ```
Matcher anyLogOf(dynamic messageOrMatcher) =>
_LogRecordMatcher(anything, messageOrMatcher);
/// Matches [LogRecord] of [Level.INFO] where message is [messageOrMatcher].
Matcher infoLogOf(dynamic messageOrMatcher) =>
_LogRecordMatcher(Level.INFO, messageOrMatcher);
/// Matches [LogRecord] of [Level.WARNING] where message is [messageOrMatcher].
Matcher warningLogOf(dynamic messageOrMatcher) =>
_LogRecordMatcher(Level.WARNING, messageOrMatcher);
/// Matches [LogRecord] of [Level.SEVERE] where message is [messageOrMatcher].
Matcher severeLogOf(dynamic messageOrMatcher) =>
_LogRecordMatcher(Level.SEVERE, messageOrMatcher);
class _LogRecordMatcher extends Matcher {
final Matcher _level;
final Matcher _message;
factory _LogRecordMatcher(dynamic levelOr, dynamic messageOr) =>
_LogRecordMatcher._(levelOr is Matcher ? levelOr : equals(levelOr),
messageOr is Matcher ? messageOr : equals(messageOr));
_LogRecordMatcher._(this._level, this._message);
@override
Description describe(Description description) {
description.add('level: ');
_level.describe(description);
description.add(', message: ');
_message.describe(description);
return description;
}
@override
Description describeMismatch(
covariant LogRecord item, Description description, _, __) {
if (!_level.matches(item.level, {})) {
_level.describeMismatch(item.level, description, {}, false);
}
if (!_message.matches(item.message, {})) {
_message.describeMismatch(item.message, description, {}, false);
}
return description;
}
@override
bool matches(item, _) =>
item is LogRecord &&
_level.matches(item.level, {}) &&
_message.matches(item.message, {});
}