258 lines
7.8 KiB
Dart
258 lines
7.8 KiB
Dart
// ignore_for_file: depend_on_referenced_packages, library_private_types_in_public_api
|
|
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:dart_framework/platform/process.dart';
|
|
import 'package:oto/cli/commands/install/regist_path.dart';
|
|
import 'package:oto/cli/printer.dart';
|
|
import 'package:oto/oto/core/system_runtime.dart';
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:test/test.dart';
|
|
|
|
// Fake classes to avoid duplicate definition problems
|
|
class _ShellCall {
|
|
final String shell;
|
|
final String? workspace;
|
|
final bool printStdout;
|
|
final bool printStderr;
|
|
final Converter<List<int>, String>? decoder;
|
|
_ShellCall(this.shell, this.workspace, this.printStdout, this.printStderr,
|
|
this.decoder);
|
|
}
|
|
|
|
class _RunExecutableCall {
|
|
final String exe;
|
|
final List<String> args;
|
|
final String? workingDirectory;
|
|
final Encoding? stdoutEncoding;
|
|
final Encoding? stderrEncoding;
|
|
_RunExecutableCall(this.exe, this.args, this.workingDirectory,
|
|
this.stdoutEncoding, this.stderrEncoding);
|
|
}
|
|
|
|
class FakeProcessData extends ProcessData {
|
|
FakeProcessData() : super(false, false) {
|
|
exitCode = 0;
|
|
}
|
|
|
|
@override
|
|
Future waitForExit() async {}
|
|
|
|
@override
|
|
Process get process =>
|
|
throw UnsupportedError('FakeProcessData has no real process');
|
|
}
|
|
|
|
class FakeSystemRuntime implements SystemRuntime {
|
|
final List<_ShellCall> startShellCalls = [];
|
|
final List<_ShellCall> runShellCalls = [];
|
|
final List<_RunExecutableCall> runExecutableCalls = [];
|
|
final List<int> killedPids = [];
|
|
|
|
final bool _isWindows;
|
|
final bool _isMacOS;
|
|
final bool _isLinux;
|
|
final Map<String, String> _environment;
|
|
|
|
FakeSystemRuntime({
|
|
bool isWindows = false,
|
|
bool isMacOS = false,
|
|
bool isLinux = true,
|
|
Map<String, String> environment = const {},
|
|
}) : _isWindows = isWindows,
|
|
_isMacOS = isMacOS,
|
|
_isLinux = isLinux,
|
|
_environment = environment;
|
|
|
|
@override
|
|
bool get isWindows => _isWindows;
|
|
@override
|
|
bool get isMacOS => _isMacOS;
|
|
@override
|
|
bool get isLinux => _isLinux;
|
|
@override
|
|
Map<String, String> get environment => _environment;
|
|
|
|
@override
|
|
Future<ProcessData> startShell(
|
|
StringBuffer shell, {
|
|
String? workspace,
|
|
bool printStdout = true,
|
|
bool printStderr = true,
|
|
Converter<List<int>, String>? decoder,
|
|
LogHandler? logHandler,
|
|
}) async {
|
|
startShellCalls.add(_ShellCall(
|
|
shell.toString(), workspace, printStdout, printStderr, decoder));
|
|
return FakeProcessData();
|
|
}
|
|
|
|
@override
|
|
Future<ProcessResult> runShell(
|
|
StringBuffer shell, {
|
|
String? workspace,
|
|
bool printStdout = true,
|
|
bool printStderr = true,
|
|
Converter<List<int>, String>? decoder,
|
|
LogHandler? logHandler,
|
|
}) async {
|
|
runShellCalls.add(_ShellCall(
|
|
shell.toString(), workspace, printStdout, printStderr, decoder));
|
|
return ProcessResult(0, 0, '', '');
|
|
}
|
|
|
|
@override
|
|
Future<ProcessResult> runExecutable(
|
|
String exe,
|
|
List<String> args, {
|
|
String? workingDirectory,
|
|
Encoding? stdoutEncoding,
|
|
Encoding? stderrEncoding,
|
|
}) async {
|
|
runExecutableCalls.add(_RunExecutableCall(
|
|
exe, args, workingDirectory, stdoutEncoding, stderrEncoding));
|
|
return ProcessResult(0, 0, '', '');
|
|
}
|
|
|
|
@override
|
|
Future<Process> startExecutable(
|
|
String program,
|
|
List<String> args, {
|
|
String? workingDirectory,
|
|
ProcessStartMode mode = ProcessStartMode.normal,
|
|
}) async {
|
|
throw UnsupportedError('FakeSystemRuntime does not return a real Process');
|
|
}
|
|
|
|
@override
|
|
bool killPid(int pid, [ProcessSignal signal = ProcessSignal.sigterm]) {
|
|
killedPids.add(pid);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
group('Printer Runtime Rollout Tests', () {
|
|
test('unix printer delegates script execution to system runtime', () async {
|
|
final fakeRuntime = FakeSystemRuntime(
|
|
isWindows: false,
|
|
isMacOS: false,
|
|
isLinux: true,
|
|
);
|
|
|
|
final printer = Printer.unix(runtime: fakeRuntime);
|
|
await printer.printCLI(['hello', 'world']);
|
|
|
|
expect(fakeRuntime.runExecutableCalls, hasLength(1));
|
|
final call = fakeRuntime.runExecutableCalls.first;
|
|
expect(call.exe, 'bash');
|
|
expect(call.args, hasLength(1));
|
|
expect(call.stdoutEncoding, utf8);
|
|
expect(call.stderrEncoding, utf8);
|
|
final tempFilePath = call.args.first;
|
|
expect(tempFilePath, contains('temp_'));
|
|
expect(tempFilePath, endsWith('.sh'));
|
|
|
|
final tempFile = File(tempFilePath);
|
|
expect(tempFile.existsSync(), isFalse); // printer deletes it
|
|
});
|
|
|
|
test('windows printer delegates script execution to system runtime',
|
|
() async {
|
|
final fakeRuntime = FakeSystemRuntime(
|
|
isWindows: true,
|
|
isMacOS: false,
|
|
isLinux: false,
|
|
);
|
|
|
|
final printer = Printer.windows(runtime: fakeRuntime);
|
|
await printer.printCLI(['hello', 'world']);
|
|
|
|
expect(fakeRuntime.runExecutableCalls, hasLength(1));
|
|
final call = fakeRuntime.runExecutableCalls.first;
|
|
expect(call.exe, 'cmd');
|
|
expect(call.args, ['/C', contains('temp_')]);
|
|
final tempFilePath = call.args[1];
|
|
expect(tempFilePath, endsWith('.bat'));
|
|
|
|
final tempFile = File(tempFilePath);
|
|
expect(tempFile.existsSync(), isFalse); // printer deletes it
|
|
});
|
|
});
|
|
|
|
group('RegistPath Runtime Rollout Tests', () {
|
|
late Directory tempDir;
|
|
|
|
setUp(() async {
|
|
tempDir = await Directory.systemTemp.createTemp('regist_path_test');
|
|
});
|
|
|
|
tearDown(() async {
|
|
await tempDir.delete(recursive: true);
|
|
});
|
|
|
|
test('regist_path osx sources profiles through system runtime', () async {
|
|
final fakeRuntime = FakeSystemRuntime(
|
|
isWindows: false,
|
|
isMacOS: true,
|
|
isLinux: false,
|
|
environment: {'HOME': tempDir.path},
|
|
);
|
|
|
|
final registPath = RegistPath.create(runtime: fakeRuntime);
|
|
|
|
final zprofileFile = File(path.join(tempDir.path, '.zprofile'));
|
|
final bashProfileFile = File(path.join(tempDir.path, '.bash_profile'));
|
|
|
|
await registPath.add('/path/to/my/bin');
|
|
|
|
expect(zprofileFile.existsSync(), isTrue);
|
|
expect(bashProfileFile.existsSync(), isTrue);
|
|
|
|
final zprofileContent = await zprofileFile.readAsString();
|
|
expect(zprofileContent, contains('export PATH=\${PATH}:/path/to/my/bin'));
|
|
|
|
expect(fakeRuntime.runShellCalls, hasLength(1));
|
|
expect(fakeRuntime.runShellCalls.first.shell, contains('source'));
|
|
|
|
// remove test
|
|
await registPath.remove('/path/to/my/bin');
|
|
final zprofileContentAfter = await zprofileFile.readAsString();
|
|
expect(zprofileContentAfter, isNot(contains('/path/to/my/bin')));
|
|
});
|
|
|
|
test('regist_path windows delegates setx through system runtime', () async {
|
|
final fakeRuntime = FakeSystemRuntime(
|
|
isWindows: true,
|
|
isMacOS: false,
|
|
isLinux: false,
|
|
environment: {'UserProfile': tempDir.path},
|
|
);
|
|
|
|
final registPath = RegistPath.create(runtime: fakeRuntime);
|
|
|
|
await registPath.add('/path/to/win/bin');
|
|
|
|
expect(fakeRuntime.runShellCalls, hasLength(1));
|
|
expect(fakeRuntime.runShellCalls.first.shell,
|
|
contains('setx path "%PathUser%;/path/to/win/bin'));
|
|
|
|
fakeRuntime.runShellCalls.clear();
|
|
|
|
// remove test (windows path removal creates a temporary path.txt in user profile)
|
|
final pathTxtFile = File(path.join(tempDir.path, 'path.txt'));
|
|
await pathTxtFile.writeAsString('some_old_path;/path/to/win/bin');
|
|
|
|
await registPath.remove('/path/to/win/bin');
|
|
|
|
// The temporary file pathTxt should be deleted after processing
|
|
expect(pathTxtFile.existsSync(), isFalse);
|
|
|
|
// There should be two shell calls: first to query/write path.txt, second to setx the new path.
|
|
expect(fakeRuntime.runShellCalls, hasLength(2));
|
|
expect(fakeRuntime.runShellCalls[1].shell,
|
|
contains('setx path "some_old_path"'));
|
|
});
|
|
});
|
|
}
|