[![Build Status](https://github.com/dart-lang/test/actions/workflows/test_process.yaml/badge.svg)](https://github.com/dart-lang/test/actions/workflows/test_process.yaml)
[![pub package](https://img.shields.io/pub/v/test_process.svg)](https://pub.dev/packages/test_process)
[![package publisher](https://img.shields.io/pub/publisher/test_process.svg)](https://pub.dev/packages/test_process/publisher)

A package for testing subprocesses.

This exposes a [`TestProcess`][TestProcess] class that wraps `dart:io`'s
[`Process`][Process] class and makes it easy to read standard output
line-by-line. `TestProcess` works the same as `Process` in many ways, but there
are a few major differences.

[TestProcess]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess-class.html
[Process]: https://api.dart.dev/stable/dart-io/Process-class.html

## Standard Output

`Process.stdout` and `Process.stderr` are binary streams, which is the most
general API but isn't the most helpful when working with a program that produces
plain text. Instead, [`TestProcess.stdout`][stdout] and
[`TestProcess.stderr`][stderr] emit a string for each line of output the process
produces. What's more, they're [`StreamQueue`][StreamQueue]s, which means
they provide a *pull-based API*. For example:

[stdout]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/stdout.html
[stderr]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/stderr.html
[StreamQueue]: https://pub.dev/documentation/async/latest/async/StreamQueue-class.html

```dart
import 'package:test/test.dart';
import 'package:test_process/test_process.dart';

void main() {
  test('pub get gets dependencies', () async {
    // TestProcess.start() works just like Process.start() from dart:io.
    var process = await TestProcess.start('dart', ['pub', 'get']);

    // StreamQueue.next returns the next line emitted on standard out.
    var firstLine = await process.stdout.next;
    expect(firstLine, equals('Resolving dependencies...'));

    // Each call to StreamQueue.next moves one line further.
    String next;
    do {
      next = await process.stdout.next;
    } while (next != 'Got dependencies!');

    // Assert that the process exits with code 0.
    await process.shouldExit(0);
  });
}
```

The `test` package's [stream matchers][] have built-in support for
`StreamQueues`, which makes them perfect for making assertions about a process's
output. We can use this to clean up the previous example:

[stream matchers]: https://github.com/dart-lang/test#stream-matchers

```dart
import 'package:test/test.dart';
import 'package:test_process/test_process.dart';

void main() {
  test('pub get gets dependencies', () async {
    var process = await TestProcess.start('dart', ['pub', 'get']);

    // Each stream matcher will consume as many lines as it matches from a
    // StreamQueue, and no more, so it's safe to use them in sequence.
    await expectLater(process.stdout, emits('Resolving dependencies...'));

    // The emitsThrough matcher matches and consumes any number of lines, as
    // long as they end with one matching the argument.
    await expectLater(process.stdout, emitsThrough('Got dependencies!'));

    await process.shouldExit(0);
  });
}
```

If you want to access the standard output streams without consuming any values
from the queues, you can use the [`stdoutStream()`][stdoutStream] and
[`stderrStream()`][stderrStream] methods. Each time you call one of these, it
produces an entirely new stream that replays the corresponding output stream
from the beginning, regardless of what's already been produced by `stdout`,
`stderr`, or other calls to the stream method.

[stdoutStream]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/stdoutStream.html
[stderrStream]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/stderrStream.html

## Signals and Termination

The way signaling works is different from `dart:io` as well. `TestProcess` still
has a [`kill()`][kill] method, but it defaults to `SIGKILL` on Mac OS and Linux
to ensure (as best as possible) that processes die without leaving behind
zombies. If you want to send a particular signal (which is unsupported on
Windows), you can do so by explicitly calling [`signal()`][signal].

[kill]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/kill.html
[signal]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/signal.html

In addition to [`exitCode`][exitCode], which works the same as in `dart:io`,
`TestProcess` also adds a new method named [`shouldExit()`][shouldExit]. This
lets tests wait for a process to exit, and (if desired) assert what particular
exit code it produced.

[exitCode]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/exitCode.html
[shouldExit]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/shouldExit.html

## Debugging Output

When a test using `TestProcess` fails, it will print all the output produced by
that process. This makes it much easier to figure out what went wrong and why.
The debugging output uses a header based on the process's invocation by
default, but you can pass in custom `description` parameters to
[`TestProcess.start()`][start] to control the headers.

[start]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/start.html

`TestProcess` will also produce debugging output as the test runs if you pass
`forwardStdio: true` to `TestProcess.start()`. This can be particularly useful
when you're using an interactive debugger and you want to figure out what a
process is doing before the test finishes and the normal debugging output is
printed.
