Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 51 additions & 43 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,51 +1,59 @@
name: CLI Tools CI

on:
push:
branches:
- main
pull_request:
branches:
- main
push:
branches:
- main
pull_request:
branches:
- main

jobs:
dart_format:
name: Dart format check
strategy:
fail-fast: false
matrix:
dart: [3.3, 3.9]
package: [cli_tools, config]
runs-on: ubuntu-latest
defaults:
run:
working-directory: packages/${{ matrix.package }}
steps:
- uses: actions/checkout@v4
- uses: dart-lang/setup-dart@v1.7.1
with:
sdk: ${{ matrix.dart }}
dart_format:
name: Dart format check
strategy:
fail-fast: false
matrix:
dart: [3.3, 3.9]
package: [cli_tools, config]
runs-on: ubuntu-latest
defaults:
run:
working-directory: packages/${{ matrix.package }}
steps:
- uses: actions/checkout@v4
- uses: dart-lang/setup-dart@v1.7.1
with:
sdk: ${{ matrix.dart }}

- run: dart pub get
- run: dart format --output=none --set-exit-if-changed .
- run: dart pub get
- run: dart format --output=none --set-exit-if-changed .

dart_analyze_test:
name: Dart Analyze and Test
strategy:
fail-fast: false
matrix:
dart: [3.3, 3.9]
package: [cli_tools, config]
runs-on: ubuntu-latest
defaults:
run:
working-directory: packages/${{ matrix.package }}
steps:
- uses: actions/checkout@v4
- uses: dart-lang/setup-dart@v1.7.1
with:
sdk: ${{ matrix.dart }}
dart_analyze_test:
name: Dart Analyze and Test
strategy:
fail-fast: false
matrix:
dart: [3.3, 3.9]
package: [cli_tools, config]
platform: [ubuntu-latest]
include:
- package: cli_tools
platform: windows-latest
dart: 3.3
- package: cli_tools
platform: macos-latest
dart: 3.3
runs-on: ${{ matrix.platform }}
defaults:
run:
working-directory: packages/${{ matrix.package }}
steps:
- uses: actions/checkout@v4
- uses: dart-lang/setup-dart@v1.7.1
with:
sdk: ${{ matrix.dart }}

- run: dart pub get
- run: dart analyze --fatal-infos
- run: dart test
- run: dart pub get
- run: dart analyze --fatal-infos
- run: dart test
110 changes: 81 additions & 29 deletions packages/cli_tools/test/execute_test.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// These test depends on bash and unix specific tools (trap, exit, echo)
@TestOn('!windows')
library;

import 'dart:io';

import 'package:cli_tools/execute.dart';
import 'package:path/path.dart' as p;
import 'package:test/test.dart';
import 'package:test_descriptor/test_descriptor.dart' as d;

import 'test_utils/mock_stdout.dart';

Future<String> compileDriver() async {
final driverExe = p.join(Directory.systemTemp.path, 'execute_driver.exe');
Expand All @@ -29,7 +28,7 @@ void main() {
test(
'when running a command that succeeds, then the effect is expected',
() async {
final result = await runDriver('echo "Hello world!"');
final result = await runDriver('echo Hello world!');
expect(result.exitCode, 0);
expect(result.stdout, contains('Hello world!'));
},
Expand All @@ -47,32 +46,85 @@ void main() {
() async {
final result = await runDriver('fhasjkhfs');
expect(result.exitCode, isNot(0));
expect(result.stderr, contains('not found'));
expect(result.stderr,
contains(Platform.isWindows ? 'not recognized' : 'not found'));
},
);

test('when sending SIGINT, then it is forwarded to the child process',
() async {
// Use trap to catch signal in child
final process = await startDriver(
'trap "echo SIGINT; exit 0" INT; echo "Running"; while :; do sleep 0.1; done');

// Collect stdout incrementally
final stdoutBuffer = StringBuffer();
process.stdout.transform(systemEncoding.decoder).listen((final data) {
stdoutBuffer.write(data);
});

// Wait for the script to start (look for "Running" message)
while (!stdoutBuffer.toString().contains('Running')) {
await Future<void>.delayed(const Duration(milliseconds: 100));
}

// Send SIGINT to driver
process.kill(ProcessSignal.sigint);

expect(await process.exitCode, 0);
expect(stdoutBuffer.toString(), contains('SIGINT'));
});
test(
'when sending SIGINT, then it is forwarded to the child process',
() async {
// Use trap to catch signal in child
final process = await startDriver(
'trap "echo SIGINT; exit 0" INT; echo "Running"; while :; do sleep 0.1; done');

// Collect stdout incrementally
final stdoutBuffer = StringBuffer();
process.stdout.transform(systemEncoding.decoder).listen((final data) {
stdoutBuffer.write(data);
});

// Wait for the script to start (look for "Running" message)
while (!stdoutBuffer.toString().contains('Running')) {
await Future<void>.delayed(const Duration(milliseconds: 100));
}

// Send SIGINT to driver
process.kill(ProcessSignal.sigint);

expect(await process.exitCode, 0);
expect(stdoutBuffer.toString(), contains('SIGINT'));
},
skip: Platform.isWindows ? 'No trap equivalent on Windows' : null,
);

test(
'when specifying a working directory, '
'then the command runs in that directory',
() async {
await d.dir('test_workdir', []).create();
final testDir = Directory(p.join(d.sandbox, 'test_workdir'));

final stdout = MockStdout();
final exitCode = await execute(
Platform.isWindows ? 'cd' : 'pwd',
workingDirectory: testDir,
stdout: stdout,
);

expect(exitCode, 0);
expect(stdout.output.trim(), testDir.path);
},
);

test(
'when the command outputs to stderr, then stderr is streamed',
() async {
final stderr = MockStdout();
final exitCode = await execute(
'echo error output 1>&2',
stderr: stderr,
);

expect(exitCode, 0);
expect(stderr.output, contains('error output'));
},
);

test(
'when running a complex command with shell features, '
'then it handles them correctly',
() async {
final stdout = MockStdout();
final exitCode = await execute(
'echo hello && echo world',
stdout: stdout,
);

expect(exitCode, 0);
expect(stdout.output, contains('hello'));
expect(stdout.output, contains('world'));
},
);
});
}
2 changes: 1 addition & 1 deletion packages/cli_tools/test/test_utils/mock_stdout.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class MockStdout implements Stdout {

@override
Future addStream(final Stream<List<int>> stream) {
throw UnimplementedError();
return stream.forEach(add);
}

@override
Expand Down