// ignore_for_file: depend_on_referenced_packages, library_private_types_in_public_api import 'dart:async'; import 'dart:io'; import 'package:dart_framework/platform/process.dart'; import 'package:oto/oto/application.dart'; import 'package:oto/oto/commands/command_runtime.dart'; import 'package:oto/oto/commands/git/git.dart'; import 'package:oto/oto/commands/process/process.dart'; import 'package:oto/oto/commands/shell/shell.dart'; import 'package:oto/oto/core/execution_context.dart'; import 'package:oto/oto/data/command_data.dart'; import 'package:test/test.dart'; class _StartCall { final String shell; final bool printStderr; _StartCall(this.shell, this.printStderr); } class _DetachedCall { final String shell; final String workspace; _DetachedCall(this.shell, this.workspace); } class FakeProcessData extends ProcessData { FakeProcessData( {int exitCode = 0, String stdoutText = '', String stderrText = ''}) : super(false, false) { this.exitCode = exitCode; this.stdout = StringBuffer(stdoutText); this.stderr = StringBuffer(stderrText); } @override Future waitForExit() async {} @override Process get process => throw UnsupportedError('FakeProcessData has no real process'); } class FakeRuntime implements CommandRuntime { final List<_StartCall> startCalls = []; final List runCalls = []; final List<_DetachedCall> detachedCalls = []; int exitCode; String stdoutText; FakeRuntime({this.exitCode = 0, this.stdoutText = ''}); @override Future start(StringBuffer shell, {bool printStderr = true, LogHandler? logHandler}) async { startCalls.add(_StartCall(shell.toString(), printStderr)); return FakeProcessData(exitCode: exitCode, stdoutText: stdoutText); } @override Future run(StringBuffer shell) async { runCalls.add(shell.toString()); return ProcessResult(0, exitCode, stdoutText, ''); } @override Future startDetached(StringBuffer shell, {required String workspace}) async { detachedCalls.add(_DetachedCall(shell.toString(), workspace)); } } void main() { setUp(() { Application.instance.context = ExecutionContext(); Application.instance.property['workspace'] = '/tmp/oto_runtime_test'; }); DataCommand commandWith(Map param) { return DataCommand.fromJson({ 'command': 'Shell', 'id': 'fake', 'param': param, }); } test('shell command delegates process start to runtime', () async { final fake = FakeRuntime(); final shell = Shell()..runtime = fake; final command = commandWith({ 'commands': ['echo hello'], }); await shell.execute(command); expect(fake.startCalls, hasLength(1)); expect(fake.startCalls.single.shell, contains('echo hello')); expect(fake.startCalls.single.shell, contains('/tmp/oto_runtime_test')); expect(fake.runCalls, isEmpty); expect(fake.detachedCalls, isEmpty); }); test('git command delegates command buffer to runtime', () async { final fake = FakeRuntime(); final git = Git()..runtime = fake; final command = commandWith({ 'commands': ['status', 'fetch origin'], }); await git.execute(command); expect(fake.startCalls, hasLength(1)); final buffer = fake.startCalls.single.shell; expect(buffer, contains('git status')); expect(buffer, contains('git fetch origin')); expect(fake.detachedCalls, isEmpty); }); test('process run delegates detached start to runtime', () async { final fake = FakeRuntime(); final processRun = ProcessRun()..runtime = fake; final command = commandWith({ 'executable': 'my_server', 'param': '--port 8080', 'workspace': '/tmp/oto_runtime_test', }); await processRun.execute(command); expect(fake.detachedCalls, hasLength(1)); expect(fake.detachedCalls.single.workspace, '/tmp/oto_runtime_test'); expect(fake.detachedCalls.single.shell, contains('my_server')); expect(fake.detachedCalls.single.shell, contains('--port 8080')); expect(fake.detachedCalls.single.shell, contains('dontKillMe')); expect(fake.startCalls, isEmpty); }); }