Line data Source code
1 : // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file 2 : // for details. All rights reserved. Use of this source code is governed by a 3 : // BSD-style license that can be found in the LICENSE file. 4 : 5 : import 'package:matcher/matcher.dart'; 6 : import 'package:test_api/hooks.dart'; 7 : 8 : import 'expect.dart'; 9 : 10 : /// A matcher that does asynchronous computation. 11 : /// 12 : /// Rather than implementing [matches], subclasses implement [matchAsync]. 13 : /// [AsyncMatcher.matches] ensures that the test doesn't complete until the 14 : /// returned future completes, and [expect] returns a future that completes when 15 : /// the returned future completes so that tests can wait for it. 16 : abstract class AsyncMatcher extends Matcher { 17 38 : const AsyncMatcher(); 18 : 19 : /// Returns `null` if this matches [item], or a [String] description of the 20 : /// failure if it doesn't match. 21 : /// 22 : /// This can return a [Future] or a synchronous value. If it returns a 23 : /// [Future], neither [expect] nor the test will complete until that [Future] 24 : /// completes. 25 : /// 26 : /// If this returns a [String] synchronously, [expect] will synchronously 27 : /// throw a [TestFailure] and [matches] will synchronusly return `false`. 28 : dynamic /*FutureOr<String>*/ matchAsync(item); 29 : 30 0 : @override 31 : bool matches(item, Map matchState) { 32 0 : final result = matchAsync(item); 33 0 : expect(result, 34 0 : anyOf([equals(null), TypeMatcher<Future>(), TypeMatcher<String>()]), 35 : reason: 'matchAsync() may only return a String, a Future, or null.'); 36 : 37 0 : if (result is Future) { 38 0 : final outstandingWork = TestHandle.current.markPending(); 39 0 : result.then((realResult) { 40 : if (realResult != null) { 41 0 : fail(formatFailure(this, item, realResult as String)); 42 : } 43 0 : outstandingWork.complete(); 44 : }); 45 0 : } else if (result is String) { 46 0 : matchState[this] = result; 47 : return false; 48 : } 49 : 50 : return true; 51 : } 52 : 53 0 : @override 54 : Description describeMismatch(item, Description mismatchDescription, 55 : Map matchState, bool verbose) => 56 0 : StringDescription(matchState[this] as String); 57 : }