Skip to content

Commit 9a69980

Browse files
authored
Disable package managers whose tool is missing on first run (#5573)
1 parent c410dec commit 9a69980

6 files changed

Lines changed: 149 additions & 2 deletions

File tree

‎src/UniGetUI.Core.Settings.Tests/SettingsTest.cs‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,51 @@ string[] strArray
388388
);
389389
}
390390

391+
[Fact]
392+
public void ADictionaryLoadPublishedAfterAWriteKeepsTheWrite()
393+
{
394+
Settings.ResetSettings();
395+
string file = Path.Join(
396+
CoreData.UniGetUIUserConfigurationDirectory,
397+
$"{Settings.ResolveKey(Settings.K.DisabledManagers)}.json"
398+
);
399+
var staleLoad = new Dictionary<object, object?> { ["Stale"] = true };
400+
401+
Settings.SetDictionary(
402+
Settings.K.DisabledManagers,
403+
new Dictionary<string, bool> { ["Written"] = true }
404+
);
405+
406+
Assert.False(Settings._publishLoadedDictionary(Settings.K.DisabledManagers, staleLoad));
407+
Assert.True(
408+
Settings.DictionaryContainsKey<string, bool>(Settings.K.DisabledManagers, "Written")
409+
);
410+
Assert.False(
411+
Settings.DictionaryContainsKey<string, bool>(Settings.K.DisabledManagers, "Stale")
412+
);
413+
414+
Settings.SetDictionaryItem(Settings.K.DisabledManagers, "Other", false);
415+
string onDisk = File.ReadAllText(file);
416+
Assert.Contains("Written", onDisk);
417+
Assert.DoesNotContain("Stale", onDisk);
418+
419+
Settings.ResetSettings();
420+
}
421+
422+
[Fact]
423+
public void ADictionaryLoadIntoAColdCacheIsPublished()
424+
{
425+
Settings.ResetSettings();
426+
var load = new Dictionary<object, object?> { ["Loaded"] = true };
427+
428+
Assert.True(Settings._publishLoadedDictionary(Settings.K.DisabledManagers, load));
429+
Assert.True(
430+
Settings.DictionaryContainsKey<string, bool>(Settings.K.DisabledManagers, "Loaded")
431+
);
432+
433+
Settings.ResetSettings();
434+
}
435+
391436
[Fact]
392437
public static void EnsureAllKeysResolve()
393438
{
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using System.Runtime.CompilerServices;
2+
3+
[assembly: InternalsVisibleTo("UniGetUI.Core.Settings.Tests")]

‎src/UniGetUI.Core.Settings/SettingsEngine_Dictionaries.cs‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,14 @@ private static readonly ConcurrentDictionary<
7171
}
7272
}
7373

74-
_dictionarySettings[key] = value.ToDictionary(
74+
Dictionary<object, object?> loaded = value.ToDictionary(
7575
kvp => (object)kvp.Key,
7676
kvp => (object?)kvp.Value
7777
);
78-
return value;
78+
if (_publishLoadedDictionary(key, loaded))
79+
return value;
80+
81+
return _getDictionary<KeyT, ValueT>(key);
7982
}
8083
catch (InvalidOperationException)
8184
{
@@ -89,6 +92,11 @@ private static readonly ConcurrentDictionary<
8992
}
9093
}
9194

95+
internal static bool _publishLoadedDictionary(K key, Dictionary<object, object?> loaded)
96+
{
97+
return ReferenceEquals(_dictionarySettings.GetOrAdd(key, loaded), loaded);
98+
}
99+
92100
// Returns an empty dictionary if the setting doesn't exist and null if the types are invalid
93101
public static IReadOnlyDictionary<KeyT, ValueT?>? GetDictionary<KeyT, ValueT>(K settingsKey)
94102
where KeyT : notnull

‎src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/PackageManager.cs‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,16 @@ public virtual void Initialize()
118118
out string path,
119119
out string callArguments
120120
);
121+
122+
if (_recordDefaultEnabledState(found))
123+
{
124+
Status = new() { Version = CoreTools.Translate("{0} is disabled", DisplayName) };
125+
Logger.ImportantInfo(
126+
$"{Name} was not found on the system and has been disabled by default"
127+
);
128+
return;
129+
}
130+
121131
Status = new ManagerStatus();
122132
Status.Found = found;
123133
Status.ExecutablePath = path;
@@ -305,6 +315,20 @@ public bool IsEnabled()
305315
return !Settings.GetDictionaryItem<string, bool>(Settings.K.DisabledManagers, Name);
306316
}
307317

318+
private static readonly object _defaultEnabledStateLock = new();
319+
320+
private bool _recordDefaultEnabledState(bool found)
321+
{
322+
lock (_defaultEnabledStateLock)
323+
{
324+
if (Settings.DictionaryContainsKey<string, bool>(Settings.K.DisabledManagers, Name))
325+
return false;
326+
327+
Settings.SetDictionaryItem(Settings.K.DisabledManagers, Name, !found);
328+
return !found;
329+
}
330+
}
331+
308332
/// <summary>
309333
/// Returns true if the manager is enabled and available (the required executable files were found). Returns false otherwise
310334
/// </summary>

‎src/UniGetUI.PackageEngine.Tests/PackageManagerTests.cs‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public void InitializeEnabledManagerWithoutExecutableStaysNotReady()
8181
var manager = CreateManager();
8282
manager.ExecutableFound = false;
8383
manager.ExecutablePath = CreateExecutable("missing-manager.exe");
84+
Settings.SetDictionaryItem<string, bool>(Settings.K.DisabledManagers, manager.Name, false);
8485

8586
manager.Initialize();
8687

@@ -94,6 +95,66 @@ public void InitializeEnabledManagerWithoutExecutableStaysNotReady()
9495
);
9596
}
9697

98+
[Fact]
99+
public void FirstInitializeDisablesManagerWhoseExecutableIsMissing()
100+
{
101+
var manager = CreateManager();
102+
manager.ExecutableFound = false;
103+
manager.ExecutablePath = CreateExecutable("never-installed-manager.exe");
104+
105+
manager.Initialize();
106+
107+
Assert.False(manager.IsEnabled());
108+
Assert.False(manager.IsReady());
109+
Assert.False(manager.Status.Found);
110+
Assert.True(Settings.GetDictionaryItem<string, bool>(Settings.K.DisabledManagers, manager.Name));
111+
Assert.Equal(
112+
CoreTools.Translate("{0} is disabled", manager.DisplayName),
113+
manager.Status.Version
114+
);
115+
}
116+
117+
[Fact]
118+
public void FirstInitializeKeepsFoundManagerEnabledAfterItsExecutableDisappears()
119+
{
120+
var manager = CreateManager();
121+
manager.ExecutablePath = CreateExecutable("installed-manager.exe");
122+
123+
manager.Initialize();
124+
125+
Assert.True(manager.IsReady());
126+
Assert.True(Settings.DictionaryContainsKey<string, bool>(Settings.K.DisabledManagers, manager.Name));
127+
Assert.False(Settings.GetDictionaryItem<string, bool>(Settings.K.DisabledManagers, manager.Name));
128+
129+
manager.ExecutableFound = false;
130+
manager.Initialize();
131+
132+
Assert.True(manager.IsEnabled());
133+
Assert.False(manager.IsReady());
134+
Assert.False(manager.Status.Found);
135+
}
136+
137+
[Fact]
138+
public void FirstInitializeDisablesOnlyMissingManagersWhenInitializedConcurrently()
139+
{
140+
var managers = Enumerable.Range(0, 16).Select(i =>
141+
{
142+
var manager = CreateManager();
143+
manager.ExecutableFound = i % 2 == 0;
144+
manager.ExecutablePath = CreateExecutable($"{manager.Name}.exe");
145+
return manager;
146+
}).ToList();
147+
148+
Parallel.ForEach(managers, manager => manager.Initialize());
149+
150+
foreach (var manager in managers)
151+
{
152+
Assert.True(Settings.DictionaryContainsKey<string, bool>(Settings.K.DisabledManagers, manager.Name));
153+
Assert.Equal(manager.ExecutableFound, manager.IsEnabled());
154+
Assert.Equal(manager.ExecutableFound, manager.IsReady());
155+
}
156+
}
157+
97158
[Fact]
98159
public void InitializeEnabledManagerWithExecutableBecomesReady()
99160
{

‎src/UniGetUI.Tui/FakeData/FakeDataEnvironment.cs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ public static FakeDataEnvironment Activate(string? sandboxDirectory = null)
8686
// The Devolutions Agent broker is a real out-of-process service; never route fake operations there.
8787
Settings.Set(Settings.K.UseAgentBroker, false);
8888

89+
foreach (FakeManagerProfile profile in FakeCatalog.Managers.Where(p => !p.Found))
90+
{
91+
if (!Settings.DictionaryContainsKey<string, bool>(Settings.K.DisabledManagers, profile.Name))
92+
Settings.SetDictionaryItem(Settings.K.DisabledManagers, profile.Name, false);
93+
}
94+
8995
env.Managers = FakeCatalog.Managers.Select(p => (IPackageManager)new FakePackageManager(p, env)).ToArray();
9096
Current = env;
9197
return env;

0 commit comments

Comments
 (0)