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
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ protected override async Task<OperationVeredict> PerformOperation()
$"Attempting to close all processes with name {ProcessName}...",
LineType.Information
);
bool processSurvived = false;
foreach (var proc in Process.GetProcessesByName(ProcessName.Replace(".exe", "")))
{
using (proc)
Expand Down Expand Up @@ -68,12 +69,13 @@ await Task.WhenAny(
$"{ProcessName} with pid={proc.Id} did not exit and will not be killed. You can change this from UniGetUI settings.",
LineType.Error
);
processSurvived = true;
}
}
}
}

return OperationVeredict.Success;
return processSurvived ? OperationVeredict.Failure : OperationVeredict.Success;
}
catch (OperationCanceledException) when (CancellationToken.IsCancellationRequested)
{
Expand Down
2 changes: 1 addition & 1 deletion src/UniGetUI.PackageEngine.Operations/PackageOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1473,7 +1473,7 @@ private static IReadOnlyList<InnerOperation> _getPreInstallOps(
return l;

foreach (var process in opts.KillBeforeOperation)
l.Add(new InnerOperation(new KillProcessOperation(process), mustSucceed: false));
l.Add(new InnerOperation(new KillProcessOperation(process), mustSucceed: true));

if (role is OperationType.Install && opts.PreInstallCommand.Any())
l.Add(
Expand Down
117 changes: 117 additions & 0 deletions src/UniGetUI.PackageEngine.Tests/KillProcessOperationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using System.Diagnostics;
using UniGetUI.Core.SettingsEngine;
using UniGetUI.PackageEngine.Enums;
using UniGetUI.PackageOperations;

namespace UniGetUI.PackageEngine.Tests;

[Collection(nameof(OperationOrchestrationTestCollection))]
public sealed class KillProcessOperationTests
{
[Fact]
public async Task FailsWhenAProcessRefusesToCloseAndMayNotBeKilled()
{
Settings.Set(Settings.K.KillProcessesThatRefuseToDie, false);
using var target = WindowlessProcess.Start();
using var operation = new KillProcessOperation(target.Name);

await operation.MainThread().WaitAsync(TimeSpan.FromSeconds(30));

Assert.Equal(OperationStatus.Failed, operation.Status);
Assert.False(target.Process.HasExited);
}

[Fact]
public async Task SucceedsWhenAProcessThatRefusesToCloseIsKilled()
{
Settings.Set(Settings.K.KillProcessesThatRefuseToDie, false);
using var target = WindowlessProcess.Start();
using var operation = new KillProcessOperation(target.Name, forceKill: true);

await operation.MainThread().WaitAsync(TimeSpan.FromSeconds(30));

Assert.Equal(OperationStatus.Succeeded, operation.Status);
Assert.True(target.Process.HasExited);
}

[Fact]
public async Task SucceedsWhenNoProcessHasTheName()
{
using var operation = new KillProcessOperation($"unigetui-absent-{Guid.NewGuid():N}");

await operation.MainThread().WaitAsync(TimeSpan.FromSeconds(30));

Assert.Equal(OperationStatus.Succeeded, operation.Status);
}

private sealed class WindowlessProcess : IDisposable
{
private readonly string _directory;

public string Name { get; }
public Process Process { get; }

private WindowlessProcess(string directory, string name, Process process)
{
_directory = directory;
Name = name;
Process = process;
}

public static WindowlessProcess Start()
{
string directory = Path.Combine(Path.GetTempPath(), $"unigetui-killtest-{Guid.NewGuid():N}");
Directory.CreateDirectory(directory);
string name = $"unigetui-killtest-{Guid.NewGuid():N}";

string source;
string arguments;
if (OperatingSystem.IsWindows())
{
source = Path.Combine(Environment.SystemDirectory, "cmd.exe");
arguments = "/d /c ping -n 120 127.0.0.1 >nul";
name += ".exe";
}
else
{
source = File.Exists("/bin/sleep") ? "/bin/sleep" : "/usr/bin/sleep";
arguments = "120";
}

string executable = Path.Combine(directory, name);
File.Copy(source, executable);
if (!OperatingSystem.IsWindows())
File.SetUnixFileMode(executable, File.GetUnixFileMode(source));

var process = Process.Start(
new ProcessStartInfo(executable, arguments)
{
CreateNoWindow = true,
UseShellExecute = false,
}
)!;
return new WindowlessProcess(directory, name, process);
}

public void Dispose()
{
try
{
if (!Process.HasExited)
{
Process.Kill(entireProcessTree: true);
Process.WaitForExit(10000);
}
}
catch (InvalidOperationException) { }

Process.Dispose();
try
{
Directory.Delete(_directory, recursive: true);
}
catch (IOException) { }
catch (UnauthorizedAccessException) { }
}
}
}
4 changes: 2 additions & 2 deletions src/UniGetUI.PackageEngine.Tests/PackageOperationsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,12 @@ public void InstallOperationBuildsPrerequisitesKillListAndPreCommand()
inner =>
{
Assert.IsType<KillProcessOperation>(inner.Operation);
Assert.False(inner.MustSucceed);
Assert.True(inner.MustSucceed);
},
inner =>
{
Assert.IsType<KillProcessOperation>(inner.Operation);
Assert.False(inner.MustSucceed);
Assert.True(inner.MustSucceed);
},
inner =>
{
Expand Down
Loading