Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Commit

Permalink
Run windows integration tests with cmd /c to fix random test hangs
Browse files Browse the repository at this point in the history
  • Loading branch information
caesay committed Dec 26, 2023
1 parent a4dcfbe commit 1ab77e0
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 54 deletions.
2 changes: 1 addition & 1 deletion src/Rust/src/commands/uninstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub fn uninstall(log_file: &PathBuf) -> Result<()> {

let dead_path = root_path.join(".dead");
let _ = File::create(dead_path);
if let Err(e) = windows::register_intent_to_delete_self(5, &root_path) {
if let Err(e) = windows::register_intent_to_delete_self(3, &root_path) {
warn!("Unable to schedule self delete ({}).", e);
}

Expand Down
8 changes: 4 additions & 4 deletions src/Squirrel/Internal/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,12 @@ public static async Task CopyToAsync(string from, string to)
await Task.Run(() => File.Copy(from, to, true)).ConfigureAwait(false);
}

public static void Retry(this Action block, int retries = 4, int retryDelay = 250)
public static void Retry(this Action block, int retries = 4, int retryDelay = 250, ILogger logger = null)
{
Retry(() => {
block();
return true;
}, retries, retryDelay);
}, retries, retryDelay, logger);
}

public static T Retry<T>(this Func<T> block, int retries = 4, int retryDelay = 250, ILogger logger = null)
Expand All @@ -215,12 +215,12 @@ public static T Retry<T>(this Func<T> block, int retries = 4, int retryDelay = 2
}
}

public static Task RetryAsync(this Func<Task> block, int retries = 4, int retryDelay = 250)
public static Task RetryAsync(this Func<Task> block, int retries = 4, int retryDelay = 250, ILogger logger = null)
{
return RetryAsync(async () => {
await block().ConfigureAwait(false);
return true;
}, retries, retryDelay);
}, retries, retryDelay, logger);
}

public static async Task<T> RetryAsync<T>(this Func<Task<T>> block, int retries = 4, int retryDelay = 250, ILogger logger = null)
Expand Down
137 changes: 88 additions & 49 deletions test/Squirrel.Packaging.Tests/WindowsPackTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
using System.Runtime.Versioning;
using System.Text;
using System.Xml.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestPlatform.Utilities;
using Microsoft.Win32;
using NuGet.Packaging;
using Squirrel.Compression;
Expand Down Expand Up @@ -195,7 +193,7 @@ public void PackBuildRefuseChannelMultipleRids()
Assert.Throws<ArgumentException>(() => runner.Pack(options));
}

[SkippableFact(Skip = "not working")]
[SkippableFact]
public void PackBuildsPackageWhichIsInstallable()
{
Skip.IfNot(SquirrelRuntimeInfo.IsWindows);
Expand Down Expand Up @@ -255,7 +253,8 @@ public void PackBuildsPackageWhichIsInstallable()
var date = DateTime.Now.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
Assert.Equal(date, installDate.Trim('\0'));

RunNoCoverage(updatePath, new string[] { "--nocolor", "--silent", "--uninstall" }, Environment.CurrentDirectory, logger);
var uninstOutput = RunNoCoverage(updatePath, new string[] { "--nocolor", "--silent", "--uninstall" }, Environment.CurrentDirectory, logger);
Assert.EndsWith(Environment.NewLine + "Y", uninstOutput); // this checks that the self-delete succeeded

Assert.False(File.Exists(shortcutPath));
Assert.False(File.Exists(appPath));
Expand All @@ -266,7 +265,7 @@ public void PackBuildsPackageWhichIsInstallable()
}
}

[SkippableFact(Skip = "not working")]
[SkippableFact]
public void TestPackedAppCanDeltaUpdateToLatest()
{
Skip.IfNot(SquirrelRuntimeInfo.IsWindows);
Expand Down Expand Up @@ -346,53 +345,93 @@ public void TestPackedAppCanDeltaUpdateToLatest()
logger.Info("TEST: uninstalled / complete");
}

private string RunCoveredRust(string binName, string[] args, string workingDir, ILogger logger, int? exitCode = 0)
{
var outputfile = GetPath($"coverage.runrust.{RandomString(8)}.xml");
var manifestFile = GetPath("..", "src", "Rust", "Cargo.toml");

var psi = new ProcessStartInfo("cargo");
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.WorkingDirectory = workingDir;

psi.ArgumentList.Add("llvm-cov");
psi.ArgumentList.Add("run");
psi.ArgumentList.Add("--cobertura");
psi.ArgumentList.Add("--manifest-path");
psi.ArgumentList.Add(manifestFile);
psi.ArgumentList.Add("--output");
psi.ArgumentList.Add(outputfile);
psi.ArgumentList.Add("--bin");
psi.ArgumentList.Add(binName);
psi.ArgumentList.Add("--");
psi.ArgumentList.AddRange(args);

return RunImpl(psi, logger, exitCode);
}
//private string RunCoveredRust(string binName, string[] args, string workingDir, ILogger logger, int? exitCode = 0)
//{
// var outputfile = GetPath($"coverage.runrust.{RandomString(8)}.xml");
// var manifestFile = GetPath("..", "src", "Rust", "Cargo.toml");

// var psi = new ProcessStartInfo("cargo");
// psi.CreateNoWindow = true;
// psi.RedirectStandardOutput = true;
// psi.RedirectStandardError = true;
// psi.WorkingDirectory = workingDir;

// psi.ArgumentList.Add("llvm-cov");
// psi.ArgumentList.Add("run");
// psi.ArgumentList.Add("--cobertura");
// psi.ArgumentList.Add("--manifest-path");
// psi.ArgumentList.Add(manifestFile);
// psi.ArgumentList.Add("--output");
// psi.ArgumentList.Add(outputfile);
// psi.ArgumentList.Add("--bin");
// psi.ArgumentList.Add(binName);
// psi.ArgumentList.Add("--");
// psi.ArgumentList.AddRange(args);

// return RunImpl(psi, logger, exitCode);
//}

private string RunImpl(ProcessStartInfo psi, ILogger logger, int? exitCode = 0)
{
logger.Info($"TEST: Running {psi.FileName} {psi.ArgumentList.Aggregate((a, b) => $"{a} {b}")}");
var p = Process.Start(psi);

StringBuilder sb = new StringBuilder();
p.BeginErrorReadLine();
p.BeginOutputReadLine();
p.OutputDataReceived += (s, e) => { sb.AppendLine(e.Data); logger.Debug(e.Data); };
p.ErrorDataReceived += (s, e) => { sb.AppendLine(e.Data); logger.Debug(e.Data); };
p.WaitForExit();

if (exitCode != null)
Assert.Equal(exitCode, p.ExitCode);

return String.Join(Environment.NewLine,
sb.ToString()
.Split('\n')
.Where(l => !l.Contains("Code coverage results"))
.Select(l => l.Trim())
).Trim();
//logger.Info($"TEST: Running {psi.FileName} {psi.ArgumentList.Aggregate((a, b) => $"{a} {b}")}");
//using var p = Process.Start(psi);

var outputfile = GetPath($"run.{RandomString(8)}.log");

try {
// this is a huge hack, but WaitForProcess hangs in the test runner when the output is redirected
// so for now, we use cmd.exe to redirect output to file.
var args = new string[psi.ArgumentList.Count];
psi.ArgumentList.CopyTo(args, 0);
new ProcessStartInfo().AppendArgumentListSafe(args, out var debug);

var fix = new ProcessStartInfo("cmd.exe");
fix.CreateNoWindow = true;
fix.WorkingDirectory = psi.WorkingDirectory;
fix.Arguments = $"/c \"{psi.FileName}\" {debug} > {outputfile} 2>&1";

Stopwatch sw = new Stopwatch();
sw.Start();

logger.Info($"TEST: Running {fix.FileName} {fix.Arguments}");
using var p = Process.Start(fix);

var timeout = TimeSpan.FromMinutes(1);
if (!p.WaitForExit(timeout))
throw new TimeoutException($"Process did not exit within {timeout.TotalSeconds}s.");

var elapsed = sw.Elapsed;
sw.Stop();

logger.Info($"TEST: Process exited with code {p.ExitCode} in {elapsed.TotalSeconds}s");

if (exitCode != null)
Assert.Equal(exitCode, p.ExitCode);

using var fs = Utility.Retry(() => {
return File.Open(outputfile, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}, 10, 1000, logger);

using var reader = new StreamReader(fs);
var output = reader.ReadToEnd();

if (String.IsNullOrWhiteSpace(output)) {
logger.Warn($"TEST: Process output was empty");
} else {
logger.Info($"TEST: Process output: {Environment.NewLine}{output.Trim()}{Environment.NewLine}");
}

return String.Join(Environment.NewLine,
output
.Split('\n')
.Where(l => !l.Contains("Code coverage results"))
.Select(l => l.Trim())
).Trim();
} finally {
try {
File.Delete(outputfile);
} catch { }
}
}

private string RunCoveredDotnet(string exe, string[] args, string workingDir, ILogger logger, int? exitCode = 0)
Expand Down

0 comments on commit 1ab77e0

Please sign in to comment.