Skip to content

Commit 659b158

Browse files
rolfbjarneCopilot
andauthored
[tests] Fix CreateAndBuildProjectTemplate hang/flakiness for Mac Catalyst (#26564)
Two issues in the 'Execute'/'DeleteSavedState' helpers used by the Mac Catalyst 'CreateAndBuildProjectTemplate' tests could cause the test process to hang for 30 seconds (or longer) waiting on an unattended 'Do you want to try to reopen its windows again?' AppKit modal dialog: * 'DeleteSavedState' only deleted '<bundleid>.savedState', but macOS stores Mac Catalyst apps' saved state under '<bundleid>~iosmac.savedState' (the 'iosmac' personality suffix), so the saved state was never actually being cleaned up. * Even with a clean Saved Application State folder, AppKit also tracks a separate persistent 'crash history' per bundle identifier that isn't stored in that folder and isn't cleared by deleting it. After many crashed/killed test runs sharing the same default bundle identifier, this alone could still trigger the modal. Fix both: delete the '~iosmac' saved-state variant too, and set the application default 'ApplePersistenceIgnoreState' to 'YES', which tells AppKit to skip the restore-prompt flow entirely regardless of any tracked crash history. Copilot-Session: 0d41380a-e6ee-470e-b837-68411050a4f6 --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent cf9b1bc commit 659b158

1 file changed

Lines changed: 42 additions & 27 deletions

File tree

tests/dotnet/UnitTests/TestBaseClass.cs

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -488,8 +488,6 @@ protected Execution Execute (string executable, out string output, out string ma
488488
if (!File.Exists (executable))
489489
throw new FileNotFoundException ($"The executable '{executable}' does not exists.");
490490

491-
DeleteSavedState (executable);
492-
493491
magicWord = Guid.NewGuid ().ToString ();
494492
var env = new Dictionary<string, string?> {
495493
{ "MAGIC_WORD", magicWord },
@@ -500,54 +498,71 @@ protected Execution Execute (string executable, out string output, out string ma
500498
env [kvp.Key] = kvp.Value;
501499
}
502500

503-
var rv = Execution.RunAsync (executable, Array.Empty<string> (), environment: env, timeout: TimeSpan.FromSeconds (30)).Result;
504-
output = rv.Output.MergedOutput;
505-
506-
DeleteSavedState (executable);
507-
508-
return rv;
501+
DeleteSavedState (executable, false);
502+
try {
503+
var rv = Execution.RunAsync (executable, Array.Empty<string> (), environment: env, timeout: TimeSpan.FromSeconds (30)).Result;
504+
output = rv.Output.MergedOutput;
505+
return rv;
506+
} finally {
507+
// Remove the override so it doesn't affect a later test using the same bundle identifier.
508+
DeleteSavedState (executable, true);
509+
}
509510
}
510511

511512
// Delete the saved application state for the app being launched, to prevent
512513
// the "Do you want to try to reopen its windows again?" dialog from showing
513514
// if the app crashed during a previous test run. See https://github.com/dotnet/macios/issues/25922
514-
static void DeleteSavedState (string executable)
515+
static void DeleteSavedState (string executable, bool cleanup)
516+
{
517+
var bundleIdentifier = GetBundleIdentifier (executable);
518+
if (string.IsNullOrEmpty (bundleIdentifier))
519+
return;
520+
521+
var savedStateParentDir = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile), "Library", "Saved Application State");
522+
// Mac Catalyst apps run under the "iosmac" personality, and macOS stores their saved state
523+
// with a "~iosmac" suffix added to the bundle identifier, so delete both variants.
524+
foreach (var identifier in new [] { bundleIdentifier, $"{bundleIdentifier}~iosmac" }) {
525+
var savedStateDir = Path.Combine (savedStateParentDir, $"{identifier}.savedState");
526+
try {
527+
if (Directory.Exists (savedStateDir)) {
528+
Directory.Delete (savedStateDir, true);
529+
Console.WriteLine ($"Deleted saved application state: {savedStateDir}");
530+
}
531+
532+
if (cleanup) {
533+
Execution.RunAsync ("/usr/bin/defaults", new [] { "delete", bundleIdentifier, "ApplePersistenceIgnoreState" }, timeout: TimeSpan.FromSeconds (30)).Wait ();
534+
} else {
535+
Execution.RunAsync ("/usr/bin/defaults", new [] { "write", bundleIdentifier, "ApplePersistenceIgnoreState", "-bool", "YES" }, timeout: TimeSpan.FromSeconds (30)).Wait ();
536+
}
537+
} catch (Exception e) {
538+
Console.WriteLine ($"Could not delete saved application state '{savedStateDir}': {e.Message}");
539+
}
540+
}
541+
}
542+
543+
static string? GetBundleIdentifier (string executable)
515544
{
516545
// Find the .app bundle directory from the executable path
517546
var dir = Path.GetDirectoryName (executable);
518547
while (!string.IsNullOrEmpty (dir) && !dir.EndsWith (".app", StringComparison.OrdinalIgnoreCase))
519548
dir = Path.GetDirectoryName (dir);
520549

521550
if (string.IsNullOrEmpty (dir))
522-
return;
551+
return null;
523552

524553
// Read the bundle identifier from Info.plist
525-
string? bundleIdentifier = null;
526554
var infoPlistPath = Path.Combine (dir, "Contents", "Info.plist");
527555
if (!File.Exists (infoPlistPath))
528556
infoPlistPath = Path.Combine (dir, "Info.plist");
529557
if (!File.Exists (infoPlistPath))
530-
return;
558+
return null;
531559

532560
try {
533561
var infoPlist = PDictionary.OpenFile (infoPlistPath);
534-
bundleIdentifier = infoPlist.GetString ("CFBundleIdentifier")?.Value;
562+
return infoPlist.GetString ("CFBundleIdentifier")?.Value;
535563
} catch (Exception e) {
536564
Console.WriteLine ($"Could not read bundle identifier from '{infoPlistPath}': {e.Message}");
537-
return;
538-
}
539-
540-
if (string.IsNullOrEmpty (bundleIdentifier))
541-
return;
542-
543-
var savedStateDir = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile), "Library", "Saved Application State", $"{bundleIdentifier}.savedState");
544-
try {
545-
if (Directory.Exists (savedStateDir)) {
546-
Directory.Delete (savedStateDir, true);
547-
Console.WriteLine ($"Deleted saved application state: {savedStateDir}");
548-
}
549-
} catch (Exception e) {
550-
Console.WriteLine ($"Could not delete saved application state '{savedStateDir}': {e.Message}");
565+
return null;
551566
}
552567
}
553568

0 commit comments

Comments
 (0)