| // 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<void>.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<TypeError>()); | |
| /// A matcher that matches a callback or future that throws a [TypeError]. | |
| final throwsTypeError = throwsA(isA<TypeError>()); | |
| /// 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<T> extends Stream<T> { | |
| @override | |
| StreamSubscription<T> 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<T> implements StreamSink<T> { | |
| final completer = Completer<void>(); | |
| @override | |
| Future get done => completer.future; | |
| @override | |
| void add(T event) {} | |
| @override | |
| void addError(Object error, [StackTrace? stackTrace]) {} | |
| @override | |
| Future addStream(Stream<T> 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<T> implements StreamSink<T> { | |
| /// The results corresponding to events that have been added to the sink. | |
| final results = <Result<T>>[]; | |
| /// Whether [close] has been called. | |
| bool get isClosed => _isClosed; | |
| var _isClosed = false; | |
| @override | |
| Future get done => _doneCompleter.future; | |
| final _doneCompleter = Completer<void>(); | |
| 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<T>.value(event)); | |
| } | |
| @override | |
| void addError(Object error, [StackTrace? stackTrace]) { | |
| results.add(Result<T>.error(error, stackTrace)); | |
| } | |
| @override | |
| Future addStream(Stream<T> 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; | |
| } | |
| } |