// Copyright (c) 2015, 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. /// Helper utilities for testing. library; import 'dart:async'; import 'package:async/async.dart'; import 'package:test/test.dart'; /// A zero-millisecond timer should wait until after all microtasks. Future flushMicrotasks() => Future.delayed(Duration.zero); typedef OptionalArgAction = void Function([dynamic a, dynamic b]); /// A generic unreachable callback function. /// /// Returns a function that fails the test if it is ever called. OptionalArgAction unreachable(String name) => ([a, b]) => fail('Unreachable: $name'); /// A matcher that runs a callback in its own zone and asserts that that zone /// emits an error that matches [matcher]. Matcher throwsZoned(Matcher matcher) => predicate((void Function() callback) { var firstError = true; runZonedGuarded( callback, expectAsync2((error, stackTrace) { if (firstError) { expect(error, matcher); firstError = false; } else { registerException(error, stackTrace); } }, max: -1)); return true; }); /// A matcher that runs a callback in its own zone and asserts that that zone /// emits a [TypeError]. final throwsZonedTypeError = throwsZoned(isA()); /// A matcher that matches a callback or future that throws a [TypeError]. final throwsTypeError = throwsA(isA()); /// A badly behaved stream which throws if it's ever listened to. /// /// Can be used to test cases where a stream should not be used. class UnusableStream extends Stream { @override StreamSubscription listen(void Function(T event)? onData, {Function? onError, void Function()? onDone, bool? cancelOnError}) { throw UnimplementedError('Gotcha!'); } } /// A dummy [StreamSink] for testing the routing of the [done] and [close] /// futures. /// /// The [completer] field allows the user to control the future returned by /// [done] and [close]. class CompleterStreamSink implements StreamSink { final completer = Completer(); @override Future get done => completer.future; @override void add(T event) {} @override void addError(Object error, [StackTrace? stackTrace]) {} @override Future addStream(Stream stream) async {} @override Future close() => completer.future; } /// A [StreamSink] that collects all events added to it as results. /// /// This is used for testing code that interacts with sinks. class TestSink implements StreamSink { /// The results corresponding to events that have been added to the sink. final results = >[]; /// Whether [close] has been called. bool get isClosed => _isClosed; var _isClosed = false; @override Future get done => _doneCompleter.future; final _doneCompleter = Completer(); final void Function() _onDone; /// Creates a new sink. /// /// If [onDone] is passed, it's called when the user calls [close]. Its result /// is piped to the [done] future. TestSink({void Function()? onDone}) : _onDone = onDone ?? (() {}); @override void add(T event) { results.add(Result.value(event)); } @override void addError(Object error, [StackTrace? stackTrace]) { results.add(Result.error(error, stackTrace)); } @override Future addStream(Stream stream) { var completer = Completer.sync(); stream.listen(add, onError: addError, onDone: completer.complete); return completer.future; } @override Future close() { _isClosed = true; _doneCompleter.complete(Future.microtask(_onDone)); return done; } }