diff --git a/src/Client/IProcessLauncher.cs b/src/Client/IProcessLauncher.cs
deleted file mode 100644
index 0d2d16fd7..000000000
--- a/src/Client/IProcessLauncher.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright Bastian Eicher et al.
-// Licensed under the GNU Lesser Public License
-
-using System.Diagnostics;
-
-namespace ZeroInstall.Client;
-
-///
-/// Launches an external process.
-///
-internal interface IProcessLauncher
-{
- ///
- /// Starts a new and runs it in parallel with this one.
- ///
- /// The newly launched process.
- /// There was a problem launching the executable.
- /// The executable file could not be found.
- /// The target process requires elevation.
- Process Start(params string[] args);
-
- ///
- /// Starts a new and waits for it to complete.
- ///
- /// The exit code of the child process.
- /// There was a problem launching the executable.
- /// The executable file could not be found.
- /// The target process requires elevation.
- int Run(params string[] args);
-}
diff --git a/src/Client/ProcessLauncher.cs b/src/Client/ProcessLauncher.cs
deleted file mode 100644
index 8ccaba5b2..000000000
--- a/src/Client/ProcessLauncher.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright Bastian Eicher et al.
-// Licensed under the GNU Lesser Public License
-
-using System.Diagnostics;
-
-namespace ZeroInstall.Client;
-
-///
-/// Launches an external process.
-///
-[PrimaryConstructor]
-internal partial class ProcessLauncher : IProcessLauncher
-{
- private readonly string _commandLine;
-
- ///
- public Process Start(params string[] args)
- => ProcessUtils.FromCommandLine(_commandLine + " " + args.JoinEscapeArguments()).Start();
-
- ///
- public int Run(params string[] args)
- => ProcessUtils.FromCommandLine(_commandLine + " " + args.JoinEscapeArguments()).Run();
-}
diff --git a/src/Client/ZeroInstallClient.cs b/src/Client/ZeroInstallClient.cs
index 7fc981adb..b060fdc2f 100644
--- a/src/Client/ZeroInstallClient.cs
+++ b/src/Client/ZeroInstallClient.cs
@@ -15,20 +15,17 @@ namespace ZeroInstall.Client;
public class ZeroInstallClient : IZeroInstallClient
{
private readonly ISubProcess _subProcess;
- private readonly IProcessLauncher _launcher;
- private readonly IProcessLauncher? _guiLauncher;
+ private readonly ISubProcess? _guiSubProcess;
///
/// Creates a new Zero Install client.
///
- /// Used to launch 0install, captures its output and waits until it has terminated.
- /// Used to launch 0install as ane external process.
- /// Used to launch 0install-win as ane external process.
- internal ZeroInstallClient(ISubProcess subProcess, IProcessLauncher launcher, IProcessLauncher? guiLauncher = null)
+ /// Used to launch 0install as a child process.
+ /// Used to launch 0install-win as a child process.
+ internal ZeroInstallClient(ISubProcess subProcess, ISubProcess? guiSubProcess = null)
{
_subProcess = subProcess;
- _launcher = launcher;
- _guiLauncher = guiLauncher;
+ _guiSubProcess = guiSubProcess;
}
///
@@ -38,9 +35,8 @@ internal ZeroInstallClient(ISubProcess subProcess, IProcessLauncher launcher, IP
/// The optional command-line used to launch 0install-win. Whitespace must be properly escaped.
public ZeroInstallClient(string commandLine, string? guiCommandLine = null)
: this(
- subProcess: new SubProcess(ProcessUtils.FromCommandLine(commandLine)),
- launcher: new ProcessLauncher(commandLine),
- guiLauncher: guiCommandLine?.To(x=> new ProcessLauncher(x)))
+ subProcess: new ZeroInstallProcess(commandLine),
+ guiSubProcess: guiCommandLine?.To(x=> new ZeroInstallProcess(x)))
{}
///
@@ -81,7 +77,7 @@ public async Task DownloadAsync(Requirements requirements, bool refr
if (refresh) args.Add("--refresh");
args.AddRange(requirements.ToCommandLineArgs());
- if (_guiLauncher == null)
+ if (_guiSubProcess == null)
{
args.Add("--xml");
string output = await Task.Run(() => _subProcess.RunAndCapture(args.ToArray()));
@@ -90,7 +86,7 @@ public async Task DownloadAsync(Requirements requirements, bool refr
else
{
args.Add("--background");
- await Task.Run(() => _guiLauncher.Run(args.ToArray()));
+ await Task.Run(() => _guiSubProcess.Run(args.ToArray()));
return await SelectAsync(requirements, offline: true);
}
}
@@ -104,10 +100,8 @@ public void Run(Requirements requirements, bool refresh = false, bool needsTermi
args.AddRange(requirements.ToCommandLineArgs());
args.AddRange(arguments);
- if (needsTerminal || _guiLauncher == null)
- _launcher.Start(args.ToArray());
- else
- _guiLauncher.Start(args.ToArray());
+ var launcher = needsTerminal ? _subProcess : _guiSubProcess ?? _subProcess;
+ launcher.Start(args.ToArray());
}
///
@@ -118,9 +112,8 @@ public Process RunWithProcess(Requirements requirements, bool refresh = false, b
args.AddRange(requirements.ToCommandLineArgs());
args.AddRange(arguments);
- return needsTerminal || _guiLauncher == null
- ? _launcher.Start(args.ToArray())
- : _guiLauncher.Start(args.ToArray());
+ var launcher = needsTerminal ? _subProcess : _guiSubProcess ?? _subProcess;
+ return launcher.Start(args.ToArray());
}
///
diff --git a/src/Client/ZeroInstallProcess.cs b/src/Client/ZeroInstallProcess.cs
new file mode 100644
index 000000000..d4d379125
--- /dev/null
+++ b/src/Client/ZeroInstallProcess.cs
@@ -0,0 +1,36 @@
+// Copyright Bastian Eicher et al.
+// Licensed under the GNU Lesser Public License
+
+using NanoByte.Common.Streams;
+
+namespace ZeroInstall.Client;
+
+///
+/// Runs Zero Install as a sub/child process.
+///
+internal class ZeroInstallProcess : SubProcess
+{
+ public ZeroInstallProcess(string commandLine)
+ : base(ProcessUtils.FromCommandLine(commandLine))
+ {}
+
+ protected override void HandleExitCode(int exitCode)
+ {
+ switch (exitCode)
+ {
+ case 1: // No changes
+ break;
+ case 10: // Web error
+ throw new WebException();
+ case 11: // Access denied
+ throw new UnauthorizedAccessException();
+ case 12: // IO error
+ throw new IOException();
+ case 100: // User canceled
+ throw new OperationCanceledException();
+ default:
+ base.HandleExitCode(exitCode);
+ return;
+ }
+ }
+}
diff --git a/src/UnitTests/Client/ZeroInstallClientGuiTest.cs b/src/UnitTests/Client/ZeroInstallClientGuiTest.cs
index a2775ae54..e59703630 100644
--- a/src/UnitTests/Client/ZeroInstallClientGuiTest.cs
+++ b/src/UnitTests/Client/ZeroInstallClientGuiTest.cs
@@ -12,15 +12,14 @@ namespace ZeroInstall.Client;
///
public class ZeroInstallClientGuiTest : TestWithMocks
{
- private readonly Mock _subProcessMock;
- private readonly Mock _guiLauncherMock;
+ private readonly Mock _subProcessMock, _guiSubProcessMock;
private readonly ZeroInstallClient _client;
public ZeroInstallClientGuiTest()
{
_subProcessMock = CreateMock();
- _guiLauncherMock = CreateMock();
- _client = new(_subProcessMock.Object, CreateMock().Object, _guiLauncherMock.Object);
+ _guiSubProcessMock = CreateMock();
+ _client = new(_subProcessMock.Object, _guiSubProcessMock.Object);
}
[Fact]
@@ -28,8 +27,7 @@ public async Task Download()
{
var selections = SelectionsTest.CreateTestSelections();
- _guiLauncherMock.Setup(x => x.Run("download", "--batch", "--refresh", FeedTest.Test1Uri.ToStringRfc(), "--background"))
- .Returns(0);
+ _guiSubProcessMock.Setup(x => x.Run("download", "--batch", "--refresh", FeedTest.Test1Uri.ToStringRfc(), "--background"));
_subProcessMock.Setup(x => x.RunAndCapture(null, "select", "--batch", "--xml", "--offline", FeedTest.Test1Uri.ToStringRfc()))
.Returns(selections.ToXmlString());
@@ -41,18 +39,18 @@ public async Task Download()
[Fact]
public void Run()
{
- _guiLauncherMock.Setup(x => x.Start("run", "--no-wait", FeedTest.Test1Uri.ToStringRfc(), "--arg"))
- .Returns(new Process());
+ _guiSubProcessMock.Setup(x => x.Start("run", "--no-wait", FeedTest.Test1Uri.ToStringRfc(), "--arg"))
+ .Returns(new Process());
_client.Run(FeedTest.Test1Uri, arguments: "--arg");
}
[Fact]
- public void RunAndWait()
+ public void RunWithProcess()
{
var process = new Process();
- _guiLauncherMock.Setup(x => x.Start("run", FeedTest.Test1Uri.ToStringRfc(), "--arg"))
- .Returns(process);
+ _guiSubProcessMock.Setup(x => x.Start("run", FeedTest.Test1Uri.ToStringRfc(), "--arg"))
+ .Returns(process);
_client.RunWithProcess(FeedTest.Test1Uri, arguments: "--arg")
.Should().BeSameAs(process);
diff --git a/src/UnitTests/Client/ZeroInstallClientTest.cs b/src/UnitTests/Client/ZeroInstallClientTest.cs
index e9f99b91a..89080079e 100644
--- a/src/UnitTests/Client/ZeroInstallClientTest.cs
+++ b/src/UnitTests/Client/ZeroInstallClientTest.cs
@@ -14,14 +14,12 @@ namespace ZeroInstall.Client;
public class ZeroInstallClientTest : TestWithMocks
{
private readonly Mock _subProcessMock;
- private readonly Mock _launcherMock;
private readonly ZeroInstallClient _client;
public ZeroInstallClientTest()
{
_subProcessMock = CreateMock();
- _launcherMock = CreateMock();
- _client = new(_subProcessMock.Object, _launcherMock.Object);
+ _client = new(_subProcessMock.Object);
}
[Fact]
@@ -51,18 +49,18 @@ public async Task Download()
[Fact]
public void Run()
{
- _launcherMock.Setup(x => x.Start("run", "--no-wait", FeedTest.Test1Uri.ToStringRfc(), "--arg"))
- .Returns(new Process());
+ _subProcessMock.Setup(x => x.Start("run", "--no-wait", FeedTest.Test1Uri.ToStringRfc(), "--arg"))
+ .Returns(new Process());
_client.Run(FeedTest.Test1Uri, arguments: "--arg");
}
[Fact]
- public void RunAndWait()
+ public void RunWithProcess()
{
var process = new Process();
- _launcherMock.Setup(x => x.Start("run", FeedTest.Test1Uri.ToStringRfc(), "--arg"))
- .Returns(process);
+ _subProcessMock.Setup(x => x.Start("run", FeedTest.Test1Uri.ToStringRfc(), "--arg"))
+ .Returns(process);
_client.RunWithProcess(FeedTest.Test1Uri, arguments: "--arg")
.Should().BeSameAs(process);