| // Copyright 2013 The Flutter Authors. 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 'dart:io' show stdout; | |
| import 'dart:isolate'; | |
| import 'package:async_helper/async_minitest.dart' as m; | |
| import 'package:expect/expect.dart' as e; | |
| /// The state that each [Test] may be in. | |
| enum TestState { | |
| /// Initial state of a [Test] after it has been allocated. | |
| allocated, | |
| /// State of a [Test] when it is on the queue of a [TestSuite]. | |
| queued, | |
| /// State of a [Test] when it has been started. | |
| started, | |
| /// State of a [Test] when it has succeeded. | |
| succeeded, | |
| /// State of a [Test] when it has failed. | |
| failed, | |
| } | |
| /// A test that a [TestSuite] can enqueue to run. | |
| class Test { | |
| /// Creates a [Test] with the given name, body, logger, and lifecycle. | |
| Test( | |
| this.name, | |
| this.body, { | |
| StringSink? logger, | |
| TestLifecycle? lifecycle, | |
| }) : | |
| _logger = logger ?? stdout, | |
| _lifecycle = lifecycle ?? _DefaultTestLifecycle(name); | |
| /// The name of the test. | |
| final String name; | |
| /// The body of the test. | |
| final dynamic Function() body; | |
| /// The logger that records information about the test. | |
| final StringSink _logger; | |
| /// The state that the test is in. | |
| TestState state = TestState.allocated; | |
| final TestLifecycle _lifecycle; | |
| /// Runs the test. | |
| /// | |
| /// Also signals the test's progress to the [TestLifecycle] object | |
| /// that was provided when the [Test] was constructed, which will eventually | |
| /// call the provided [onDone] callback. | |
| void run({ | |
| void Function()? onDone, | |
| }) { | |
| m.test(name, () async { | |
| await Future<void>(() async { | |
| state = TestState.started; | |
| _logger.writeln('Test "$name": Started'); | |
| try { | |
| await body(); | |
| state = TestState.succeeded; | |
| _logger.writeln('Test "$name": Passed'); | |
| } on e.ExpectException catch (e, st) { | |
| state = TestState.failed; | |
| _logger.writeln('Test "$name": Failed\n$e\n$st'); | |
| } finally { | |
| _lifecycle.onDone(cleanup: onDone); | |
| } | |
| }); | |
| }); | |
| _lifecycle.onStart(); | |
| } | |
| } | |
| /// Callbacks for the lifecycle of a test. | |
| abstract class TestLifecycle { | |
| /// Called after a test has started. | |
| void onStart(); | |
| /// Called when a test is finished. | |
| /// | |
| /// The callback should ensure that the [cleanup] function should run | |
| /// eventually if provided. | |
| void onDone({void Function()? cleanup}); | |
| } | |
| class _DefaultTestLifecycle implements TestLifecycle { | |
| _DefaultTestLifecycle(String name) : _port = ReceivePort(name); | |
| final ReceivePort _port; | |
| void Function()? _cleanup; | |
| @override | |
| void onStart() { | |
| _port.listen((dynamic msg) { | |
| _port.close(); | |
| if (_cleanup != null) { | |
| _cleanup!(); | |
| } | |
| }); | |
| } | |
| @override | |
| void onDone({void Function()? cleanup}) { | |
| _port.sendPort.send(null); | |
| _cleanup = cleanup; | |
| } | |
| } |