Fixing bugs in full integration test

This commit is contained in:
Caelan Sayler
2023-12-23 20:35:56 +00:00
parent 565fd7eddb
commit ace4cadeb9
6 changed files with 151 additions and 20 deletions

View File

@@ -139,9 +139,9 @@ impl BundleInfo<'_> {
None
}
pub fn extract_zip_idx_to_path<T: AsRef<str>>(&self, index: usize, path: T) -> Result<()> {
pub fn extract_zip_idx_to_path<T: AsRef<Path>>(&self, index: usize, path: T) -> Result<()> {
let path = path.as_ref();
debug!("Extracting zip file to path: {}", path);
debug!("Extracting zip file to path: {}", path.to_string_lossy());
let p = PathBuf::from(path);
let parent = p.parent().unwrap();
@@ -160,7 +160,7 @@ impl BundleInfo<'_> {
Ok(())
}
pub fn extract_zip_predicate_to_path<F, T: AsRef<str>>(&self, predicate: F, path: T) -> Result<usize>
pub fn extract_zip_predicate_to_path<F, T: AsRef<Path>>(&self, predicate: F, path: T) -> Result<usize>
where
F: Fn(&str) -> bool,
{
@@ -183,6 +183,11 @@ impl BundleInfo<'_> {
let stub_regex = Regex::new("_ExecutionStub.exe$").unwrap();
let updater_idx = self.find_zip_file(|name| name.ends_with("Squirrel.exe"));
let nuspec_path = current_path.join("sq.version");
let _ = self
.extract_zip_predicate_to_path(|name| name.ends_with(".nuspec"), nuspec_path)
.map_err(|_| anyhow!("This package is missing a nuspec manifest."))?;
for (i, key) in files.iter().enumerate() {
if Some(i) == updater_idx || !re.is_match(key) || key.ends_with("/") || key.ends_with("\\") {
info!(" {} Skipped '{}'", i, key);

View File

@@ -185,6 +185,10 @@ fn apply<'a>(matches: &ArgMatches) -> Result<()> {
info!(" Exe Name: {:?}", exe_name);
info!(" Exe Args: {:?}", exe_args);
if wait_for_parent {
let _ = platform::wait_for_parent_to_exit(60_000); // 1 minute
}
if let Err(e) = apply_package(package) {
error!("Error applying package: {}", e);
if !restart {
@@ -193,7 +197,7 @@ fn apply<'a>(matches: &ArgMatches) -> Result<()> {
}
if restart {
_start(wait_for_parent, exe_name, exe_args, None)?;
_start(false, exe_name, exe_args, None)?;
}
Ok(())
@@ -240,6 +244,8 @@ fn apply_package<'a>(package: Option<&PathBuf>) -> Result<()> {
bail!("Latest package found is {}, which is not newer than current version {}.", found_version, app.version);
}
info!("Applying package to current: {}", found_version);
let current_dir = app.get_current_path(&root_path);
replace_dir_with_rollback(current_dir.clone(), || {
if let Some(bundle) = package_bundle.take() {

View File

@@ -0,0 +1,115 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace Squirrel
{
internal static class ProcessArgumentListPolyfill
{
#if NET5_0_OR_GREATER
public static void AppendArgumentListSafe(this ProcessStartInfo psi, IEnumerable<string> args, out string debug)
{
foreach (var a in args) {
psi.ArgumentList.Add(a);
}
var sb = new StringBuilder();
AppendArgumentsTo(sb, args);
debug = sb.ToString();
}
#else
public static void AppendArgumentListSafe(this ProcessStartInfo psi, IEnumerable<string> args, out string debug)
{
var sb = new StringBuilder();
AppendArgumentsTo(sb, args);
psi.Arguments = sb.ToString();
debug = psi.Arguments;
}
#endif
// https://source.dot.net/#System.Diagnostics.Process/System/Diagnostics/ProcessStartInfo.cs,204
private static void AppendArgumentsTo(StringBuilder stringBuilder, IEnumerable<string> args)
{
if (args != null && args.Any()) {
foreach (string argument in args) {
AppendArgument(stringBuilder, argument);
}
}
}
// https://source.dot.net/#System.Diagnostics.Process/src/libraries/System.Private.CoreLib/src/System/PasteArguments.cs,624678ba1465e776
private static void AppendArgument(StringBuilder stringBuilder, string argument)
{
if (stringBuilder.Length != 0) {
stringBuilder.Append(' ');
}
// Parsing rules for non-argv[0] arguments:
// - Backslash is a normal character except followed by a quote.
// - 2N backslashes followed by a quote ==> N literal backslashes followed by unescaped quote
// - 2N+1 backslashes followed by a quote ==> N literal backslashes followed by a literal quote
// - Parsing stops at first whitespace outside of quoted region.
// - (post 2008 rule): A closing quote followed by another quote ==> literal quote, and parsing remains in quoting mode.
if (argument.Length != 0 && ContainsNoWhitespaceOrQuotes(argument)) {
// Simple case - no quoting or changes needed.
stringBuilder.Append(argument);
} else {
stringBuilder.Append(Quote);
int idx = 0;
while (idx < argument.Length) {
char c = argument[idx++];
if (c == Backslash) {
int numBackSlash = 1;
while (idx < argument.Length && argument[idx] == Backslash) {
idx++;
numBackSlash++;
}
if (idx == argument.Length) {
// We'll emit an end quote after this so must double the number of backslashes.
stringBuilder.Append(Backslash, numBackSlash * 2);
} else if (argument[idx] == Quote) {
// Backslashes will be followed by a quote. Must double the number of backslashes.
stringBuilder.Append(Backslash, numBackSlash * 2 + 1);
stringBuilder.Append(Quote);
idx++;
} else {
// Backslash will not be followed by a quote, so emit as normal characters.
stringBuilder.Append(Backslash, numBackSlash);
}
continue;
}
if (c == Quote) {
// Escape the quote so it appears as a literal. This also guarantees that we won't end up generating a closing quote followed
// by another quote (which parses differently pre-2008 vs. post-2008.)
stringBuilder.Append(Backslash);
stringBuilder.Append(Quote);
continue;
}
stringBuilder.Append(c);
}
stringBuilder.Append(Quote);
}
}
private static bool ContainsNoWhitespaceOrQuotes(string s)
{
for (int i = 0; i < s.Length; i++) {
char c = s[i];
if (char.IsWhiteSpace(c) || c == Quote) {
return false;
}
}
return true;
}
private const char Quote = '\"';
private const char Backslash = '\\';
}
}

View File

@@ -215,12 +215,7 @@ namespace Squirrel
WorkingDirectory = Path.GetDirectoryName(Locator.UpdateExePath),
};
#if NET5_0_OR_GREATER
var args = psi.ArgumentList;
#else
var args = new List<string>();
#endif
if (silent) args.Add("--silent");
args.Add("apply");
args.Add("--wait");
@@ -239,9 +234,8 @@ namespace Squirrel
}
}
#if !NET5_0_OR_GREATER
psi.Arguments = String.Join(" ", args);
#endif
psi.AppendArgumentListSafe(args, out var debugArgs);
Log.Debug($"Restarting app to apply updates. Running: {psi.FileName} {debugArgs}");
var p = Process.Start(psi);
Thread.Sleep(300);
@@ -251,6 +245,7 @@ namespace Squirrel
if (p.HasExited) {
throw new Exception($"Update.exe process exited too soon ({p.ExitCode}).");
}
Log.Info("Update.exe apply triggered successfully.");
}
protected virtual async Task DownloadAndApplyDeltaUpdates(string extractedBasePackage, UpdateInfo updates, Action<int> progress)

View File

@@ -280,10 +280,11 @@ public class WindowsPackTests
// check app installed correctly
var appPath = Path.Combine(installDir, "current", "TestApp.exe");
Assert.True(File.Exists(appPath));
var argsPath = Path.Combine(installDir, "current", "args.txt");
var argsPath = Path.Combine(installDir, "args.txt");
Assert.True(File.Exists(argsPath));
var argsContent = File.ReadAllText(argsPath).Trim();
Assert.Equal("--squirrel-install 1.0.0", argsContent);
logger.Info("TEST: v1 installed");
// check app output
var chk1test = RunProcess(appPath, new string[] { "test" }, installDir, logger);
@@ -292,6 +293,7 @@ public class WindowsPackTests
Assert.EndsWith(Environment.NewLine + "1.0.0", chk1version);
var chk1check = RunProcess(appPath, new string[] { "check", releaseDir }, installDir, logger);
Assert.EndsWith(Environment.NewLine + "no updates", chk1check);
logger.Info("TEST: v1 output verified");
// pack v2
PackTestApp("2.0.0", "version 2 test", releaseDir, logger);
@@ -299,15 +301,17 @@ public class WindowsPackTests
// check can find v2 update
var chk2check = RunProcess(appPath, new string[] { "check", releaseDir }, installDir, logger);
Assert.EndsWith(Environment.NewLine + "update: 2.0.0", chk2check);
logger.Info("TEST: found v2 update");
// pack v3
PackTestApp("3.0.0", "version 3 test", releaseDir, logger);
// perform full update, check that we get v3
// apply should fail if there's not an update downloaded
RunProcess(appPath, new string[] { "apply", releaseDir }, installDir, logger, -1);
RunProcess(appPath, new string[] { "apply", releaseDir }, installDir, logger, exitCode: -1);
RunProcess(appPath, new string[] { "download", releaseDir }, installDir, logger);
RunProcess(appPath, new string[] { "apply", releaseDir }, installDir, logger);
RunProcess(appPath, new string[] { "apply", releaseDir }, installDir, logger, exitCode: null);
logger.Info("TEST: v3 applied");
// check app output
var chk3test = RunProcess(appPath, new string[] { "test" }, installDir, logger);
@@ -316,12 +320,15 @@ public class WindowsPackTests
Assert.EndsWith(Environment.NewLine + "3.0.0", chk3version);
var ch3check2 = RunProcess(appPath, new string[] { "check", releaseDir }, installDir, logger);
Assert.EndsWith(Environment.NewLine + "no updates", ch3check2);
logger.Info("TEST: v3 output verified");
// check new obsoleted/updated hooks have run
var argsContentv3 = File.ReadAllText(argsPath).Trim();
Assert.Contains("--squirrel-install 1.0.0", argsContent);
Assert.Contains("--squirrel-obsoleted 1.0.0", argsContent);
Assert.Contains("--squirrel-updated 3.0.0", argsContent);
logger.Info("TEST: hooks verified");
@@ -339,11 +346,12 @@ public class WindowsPackTests
// uninstall
var updatePath = Path.Combine(installDir, "Update.exe");
RunProcess(updatePath, new string[] { "--nocolor", "--silent", "--uninstall" }, Environment.CurrentDirectory, logger);
logger.Info("TEST: uninstalled / complete");
}
const string TEST_APP_ID = "Test.Squirrel-App";
private string RunProcess(string exe, string[] args, string workingDir, ILogger logger, int exitCode = 0)
private string RunProcess(string exe, string[] args, string workingDir, ILogger logger, int? exitCode = 0)
{
var psi = new ProcessStartInfo(exe);
psi.WorkingDirectory = workingDir;
@@ -360,7 +368,9 @@ public class WindowsPackTests
p.ErrorDataReceived += (s, e) => { sb.AppendLine(e.Data); logger.Debug(e.Data); };
p.WaitForExit();
Assert.Equal(exitCode, p.ExitCode);
if (exitCode != null)
Assert.Equal(exitCode, p.ExitCode);
return sb.ToString().Trim();
}

View File

@@ -4,18 +4,18 @@ using Squirrel.Locators;
try {
if (args.Length >= 1 && args[0].StartsWith("--squirrel")) {
// squirrel hooks
File.AppendAllText(Path.Combine(AppContext.BaseDirectory, "args.txt"), String.Join(" ", args) + Environment.NewLine);
File.AppendAllText(Path.Combine(AppContext.BaseDirectory, "..", "args.txt"), String.Join(" ", args) + Environment.NewLine);
return 0;
}
if (args.Length == 1 && args[0] == "version") {
var locator = SquirrelLocator.GetDefault(new ConsoleLogger());
Console.WriteLine(locator.CurrentlyInstalledVersion);
Console.WriteLine(locator.CurrentlyInstalledVersion?.ToString() ?? "unknown_version");
return 0;
}
if (args.Length == 1 && args[0] == "test") {
Console.WriteLine(Const.TEST_STRING);
Console.WriteLine(Const.TEST_STRING ?? "no_test_string");
return 0;
}