import 'dart:async'; import 'dart:io'; import 'package:code_work_spawner/src/core/process_launcher.dart'; import 'package:mocktail/mocktail.dart'; typedef FakeCommandHandler = Future Function(List arguments, {Duration? timeout}); typedef FakeStartedCommandHandler = Future Function(List arguments); final Map fakeCommandHandlers = {}; final Map fakeStartedCommandHandlers = {}; class MockExternalCommandExecutor extends Mock implements ExternalCommandExecutor {} ExternalCommandExecutor fakeExternalCommandExecutor() { _registerMocktailFallbackValues(); final executor = MockExternalCommandExecutor(); when( () => executor.run( any(), any>(), workingDirectory: any(named: 'workingDirectory'), environment: any(named: 'environment'), includeParentEnvironment: any(named: 'includeParentEnvironment'), timeout: any(named: 'timeout'), ), ).thenAnswer((invocation) { final command = invocation.positionalArguments[0] as String; final arguments = invocation.positionalArguments[1] as List; final timeout = invocation.namedArguments[#timeout] as Duration?; final handler = fakeCommandHandlers[command]; if (handler == null) { throw StateError('No fake command registered for $command.'); } return handler(arguments, timeout: timeout); }); when( () => executor.start( any(), any>(), workingDirectory: any(named: 'workingDirectory'), environment: any(named: 'environment'), includeParentEnvironment: any(named: 'includeParentEnvironment'), runInShell: any(named: 'runInShell'), mode: any(named: 'mode'), ), ).thenAnswer((invocation) { final command = invocation.positionalArguments[0] as String; final arguments = invocation.positionalArguments[1] as List; final handler = fakeStartedCommandHandlers[command]; if (handler == null) { throw StateError('No fake started command registered for $command.'); } return handler(arguments); }); return executor; } class FakeStartedExternalCommand implements StartedExternalCommand { final StreamController> _stdoutController = StreamController(); final StreamController> _stderrController = StreamController(); final Completer _exitCodeCompleter = Completer(); @override Stream> get stdout => _stdoutController.stream; @override Stream> get stderr => _stderrController.stream; @override Future get exitCode => _exitCodeCompleter.future; void addStdout(String text) { if (!_stdoutController.isClosed) { _stdoutController.add(systemEncoding.encode(text)); } } void addStderr(String text) { if (!_stderrController.isClosed) { _stderrController.add(systemEncoding.encode(text)); } } void complete([int exitCode = 0]) { if (!_exitCodeCompleter.isCompleted) { _exitCodeCompleter.complete(exitCode); } _stdoutController.close(); _stderrController.close(); } @override bool kill([ProcessSignal signal = ProcessSignal.sigterm]) { complete(); return true; } } bool _fallbackValuesRegistered = false; void _registerMocktailFallbackValues() { if (_fallbackValuesRegistered) { return; } _fallbackValuesRegistered = true; registerFallbackValue([]); registerFallbackValue({}); registerFallbackValue(Duration.zero); registerFallbackValue(ProcessStartMode.normal); } Future makeScriptExecutable(File script) async { if (Platform.isWindows) { return; } final result = await Process.run('chmod', ['+x', script.path]); if (result.exitCode != 0) { throw ProcessException( 'chmod', ['+x', script.path], '${result.stdout}\n${result.stderr}', result.exitCode, ); } }