Skip to content

Commit afd534b

Browse files
committed
attempt to remove all entries if one fails, have test cleanup target potential leftover symlinks
1 parent 7b502c8 commit afd534b

2 files changed

Lines changed: 45 additions & 4 deletions

File tree

src/AppInstallerCLICore/PortableInstaller.cpp

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,27 @@ namespace AppInstaller::CLI::Portable
216216
{
217217
PortableIndex existingIndex = PortableIndex::Open(existingIndexPath.u8string(), SQLiteStorageBase::OpenDisposition::ReadWrite);
218218

219+
std::exception_ptr firstRemoveException;
219220
for (auto expectedEntry : m_expectedEntries)
220221
{
221-
RemoveFile(expectedEntry);
222-
existingIndex.RemovePortableFile(expectedEntry);
222+
try
223+
{
224+
RemoveFile(expectedEntry);
225+
existingIndex.RemovePortableFile(expectedEntry);
226+
}
227+
catch (...)
228+
{
229+
// Capture first failure but continue so that all entries (especially symlinks)
230+
// are cleaned up, preventing cascade failures in shared state like PATH.
231+
if (!firstRemoveException)
232+
{
233+
firstRemoveException = std::current_exception();
234+
}
235+
}
236+
}
237+
if (firstRemoveException)
238+
{
239+
std::rethrow_exception(firstRemoveException);
223240
}
224241

225242
deleteIndex = existingIndex.IsEmpty();
@@ -233,9 +250,24 @@ namespace AppInstaller::CLI::Portable
233250
}
234251
else
235252
{
253+
std::exception_ptr firstRemoveException;
236254
for (auto expectedEntry : m_expectedEntries)
237255
{
238-
RemoveFile(expectedEntry);
256+
try
257+
{
258+
RemoveFile(expectedEntry);
259+
}
260+
catch (...)
261+
{
262+
if (!firstRemoveException)
263+
{
264+
firstRemoveException = std::current_exception();
265+
}
266+
}
267+
}
268+
if (firstRemoveException)
269+
{
270+
std::rethrow_exception(firstRemoveException);
239271
}
240272
}
241273

src/AppInstallerCLIE2ETests/Helpers/TestCommon.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,16 @@ public static void VerifyPortablePackage(
502502
}
503503

504504
// Always clean up as best effort.
505-
RunAICLICommand("uninstall", $"--product-code {productCode} --force");
505+
var cleanupResult = RunAICLICommand("uninstall", $"--product-code {productCode} --force");
506+
507+
// If the uninstall cleanup failed (e.g., the exe was still in use), manually remove the symlink
508+
// to prevent cascade failures in other parallel tests that check the shared Links directory.
509+
if (cleanupResult.ExitCode != 0 && File.Exists(symlinkPath))
510+
{
511+
TestContext.Out.WriteLine($"WARNING: Cleanup uninstall failed with exit code {cleanupResult.ExitCode}. Manually removing symlink to prevent cascade: {symlinkPath}");
512+
try { File.Delete(symlinkPath); }
513+
catch (Exception ex) { TestContext.Out.WriteLine($"WARNING: Failed to manually remove symlink: {ex.Message}"); }
514+
}
506515

507516
Assert.AreEqual(shouldExist, exeExists, $"Expected portable exe path: {exePath}");
508517
Assert.AreEqual(shouldExist && !installDirectoryAddedToPath, symlinkExists, $"Expected portable symlink path: {symlinkPath}");

0 commit comments

Comments
 (0)