quorate/packages/fsharp-runner/hostexec-tests/Program.cs

110 lines
4.9 KiB
C#

using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Quorate.HostExec;
namespace Quorate.HostExec.Tests;
[TestClass]
[SupportedOSPlatform("linux")]
public sealed class HostExecTests
{
[TestMethod]
public async Task InstallsAndRunsGnuProxy()
{
string root = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
try
{
Directory.CreateDirectory(Path.Combine(root, "usr/bin"));
string probe = Path.Combine(AppContext.BaseDirectory, "HostExec.Probe");
File.CreateSymbolicLink(Path.Combine(root, "usr/bin/gcc"), probe);
ToolRoot toolRoot = ToolRoot.Open(root);
string proxy = GnuProxy.Install(toolRoot, GnuTool.Gcc);
Assert.AreEqual(
"#!/usr/share/dotnet/dotnet /opt/fsi/packages/HostExec.dll\n",
await File.ReadAllTextAsync(proxy).ConfigureAwait(false));
Assert.IsTrue(File.GetUnixFileMode(proxy).HasFlag(UnixFileMode.UserExecute));
Assert.IsTrue(File.Exists($"{proxy}.json"));
IReadOnlyList<string> supplied = Proxy.WithSysroot(root, ["-c", "source.c"]);
CollectionAssert.AreEqual(new[] { $"--sysroot={root}", "-c", "source.c" }, supplied.ToArray());
IReadOnlyList<string> retained = Proxy.WithSysroot(root, [$"--sysroot={root}", "source.c"]);
Assert.HasCount(2, retained);
Assert.ThrowsExactly<ArgumentException>(() => Proxy.WithSysroot(root, ["--sysroot=/different", "source.c"]));
Assert.ThrowsExactly<ArgumentException>(() => Proxy.WithSysroot(root, ["--sysroot"]));
Assert.ThrowsExactly<ArgumentException>(() => ToolRoot.Open("relative"));
string assembly = typeof(ToolRoot).Assembly.Location;
string runtime = Path.GetDirectoryName(typeof(object).Assembly.Location)!;
string dotnet = Path.Combine(Path.GetFullPath(Path.Combine(runtime, "../../..")), "dotnet");
await File.WriteAllTextAsync(proxy, $"#!{dotnet} {assembly}\n").ConfigureAwait(false);
var start = new ProcessStartInfo(proxy)
{
RedirectStandardOutput = true,
UseShellExecute = false
};
start.ArgumentList.Add("probe.c");
using Process child = Process.Start(start)
?? throw new InvalidOperationException("proxy did not start");
string output = await child.StandardOutput.ReadToEndAsync().ConfigureAwait(false);
await child.WaitForExitAsync().ConfigureAwait(false);
Assert.AreEqual(0, child.ExitCode);
Assert.AreEqual($"--sysroot={root} probe.c\n", output);
}
finally
{
if (Directory.Exists(root))
{
Directory.Delete(root, true);
}
}
}
[TestMethod]
public async Task ForwardsTermination()
{
string root = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
try
{
Directory.CreateDirectory(Path.Combine(root, "usr/bin"));
string probe = Path.Combine(AppContext.BaseDirectory, "HostExec.Probe");
File.CreateSymbolicLink(Path.Combine(root, "usr/bin/gcc"), probe);
string proxy = GnuProxy.Install(ToolRoot.Open(root), GnuTool.Gcc);
string assembly = typeof(ToolRoot).Assembly.Location;
string runtime = Path.GetDirectoryName(typeof(object).Assembly.Location)!;
string dotnet = Path.Combine(Path.GetFullPath(Path.Combine(runtime, "../../..")), "dotnet");
await File.WriteAllTextAsync(proxy, $"#!{dotnet} {assembly}\n").ConfigureAwait(false);
var start = new ProcessStartInfo(proxy)
{
RedirectStandardOutput = true,
UseShellExecute = false
};
start.ArgumentList.Add("--wait");
using Process child = Process.Start(start)
?? throw new InvalidOperationException("proxy did not start");
string childLine = await child.StandardOutput.ReadLineAsync()
.WaitAsync(TimeSpan.FromSeconds(5)).ConfigureAwait(false)
?? throw new InvalidDataException("probe did not report its process identity");
int childId = int.Parse(childLine, CultureInfo.InvariantCulture);
Assert.AreEqual(0, Kill(child.Id, 15));
await child.WaitForExitAsync().WaitAsync(TimeSpan.FromSeconds(5)).ConfigureAwait(false);
Assert.AreEqual(143, child.ExitCode);
Assert.AreEqual(-1, Kill(childId, 0));
}
finally
{
if (Directory.Exists(root))
{
Directory.Delete(root, true);
}
}
}
[DllImport("libc", EntryPoint = "kill", SetLastError = true)]
[DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)]
private static extern int Kill(int processId, int signal);
}