Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/UniGetUI.Core.Settings.Tests/SettingsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,51 @@ string[] strArray
);
}

[Fact]
public void ADictionaryLoadPublishedAfterAWriteKeepsTheWrite()
{
Settings.ResetSettings();
string file = Path.Join(
CoreData.UniGetUIUserConfigurationDirectory,
$"{Settings.ResolveKey(Settings.K.DisabledManagers)}.json"
);
var staleLoad = new Dictionary<object, object?> { ["Stale"] = true };

Settings.SetDictionary(
Settings.K.DisabledManagers,
new Dictionary<string, bool> { ["Written"] = true }
);

Assert.False(Settings._publishLoadedDictionary(Settings.K.DisabledManagers, staleLoad));
Assert.True(
Settings.DictionaryContainsKey<string, bool>(Settings.K.DisabledManagers, "Written")
);
Assert.False(
Settings.DictionaryContainsKey<string, bool>(Settings.K.DisabledManagers, "Stale")
);

Settings.SetDictionaryItem(Settings.K.DisabledManagers, "Other", false);
string onDisk = File.ReadAllText(file);
Assert.Contains("Written", onDisk);
Assert.DoesNotContain("Stale", onDisk);

Settings.ResetSettings();
}

[Fact]
public void ADictionaryLoadIntoAColdCacheIsPublished()
{
Settings.ResetSettings();
var load = new Dictionary<object, object?> { ["Loaded"] = true };

Assert.True(Settings._publishLoadedDictionary(Settings.K.DisabledManagers, load));
Assert.True(
Settings.DictionaryContainsKey<string, bool>(Settings.K.DisabledManagers, "Loaded")
);

Settings.ResetSettings();
}

[Fact]
public static void EnsureAllKeysResolve()
{
Expand Down
3 changes: 3 additions & 0 deletions src/UniGetUI.Core.Settings/InternalsVisibleTo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("UniGetUI.Core.Settings.Tests")]
12 changes: 10 additions & 2 deletions src/UniGetUI.Core.Settings/SettingsEngine_Dictionaries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,14 @@ private static readonly ConcurrentDictionary<
}
}

_dictionarySettings[key] = value.ToDictionary(
Dictionary<object, object?> loaded = value.ToDictionary(
kvp => (object)kvp.Key,
kvp => (object?)kvp.Value
);
return value;
if (_publishLoadedDictionary(key, loaded))
return value;

return _getDictionary<KeyT, ValueT>(key);
}
catch (InvalidOperationException)
{
Expand All @@ -89,6 +92,11 @@ private static readonly ConcurrentDictionary<
}
}

internal static bool _publishLoadedDictionary(K key, Dictionary<object, object?> loaded)
{
return ReferenceEquals(_dictionarySettings.GetOrAdd(key, loaded), loaded);
}

// Returns an empty dictionary if the setting doesn't exist and null if the types are invalid
public static IReadOnlyDictionary<KeyT, ValueT?>? GetDictionary<KeyT, ValueT>(K settingsKey)
where KeyT : notnull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ public virtual void Initialize()
out string path,
out string callArguments
);

if (_recordDefaultEnabledState(found))
{
Status = new() { Version = CoreTools.Translate("{0} is disabled", DisplayName) };
Logger.ImportantInfo(
$"{Name} was not found on the system and has been disabled by default"
);
return;
}

Status = new ManagerStatus();
Status.Found = found;
Status.ExecutablePath = path;
Expand Down Expand Up @@ -305,6 +315,20 @@ public bool IsEnabled()
return !Settings.GetDictionaryItem<string, bool>(Settings.K.DisabledManagers, Name);
}

private static readonly object _defaultEnabledStateLock = new();

private bool _recordDefaultEnabledState(bool found)
{
lock (_defaultEnabledStateLock)
{
if (Settings.DictionaryContainsKey<string, bool>(Settings.K.DisabledManagers, Name))
return false;

Settings.SetDictionaryItem(Settings.K.DisabledManagers, Name, !found);
Comment thread
GabrielDuf marked this conversation as resolved.
Resolved
return !found;
}
}

/// <summary>
/// Returns true if the manager is enabled and available (the required executable files were found). Returns false otherwise
/// </summary>
Expand Down
61 changes: 61 additions & 0 deletions src/UniGetUI.PackageEngine.Tests/PackageManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public void InitializeEnabledManagerWithoutExecutableStaysNotReady()
var manager = CreateManager();
manager.ExecutableFound = false;
manager.ExecutablePath = CreateExecutable("missing-manager.exe");
Settings.SetDictionaryItem<string, bool>(Settings.K.DisabledManagers, manager.Name, false);

manager.Initialize();

Expand All @@ -94,6 +95,66 @@ public void InitializeEnabledManagerWithoutExecutableStaysNotReady()
);
}

[Fact]
public void FirstInitializeDisablesManagerWhoseExecutableIsMissing()
{
var manager = CreateManager();
manager.ExecutableFound = false;
manager.ExecutablePath = CreateExecutable("never-installed-manager.exe");

manager.Initialize();

Assert.False(manager.IsEnabled());
Assert.False(manager.IsReady());
Assert.False(manager.Status.Found);
Assert.True(Settings.GetDictionaryItem<string, bool>(Settings.K.DisabledManagers, manager.Name));
Assert.Equal(
CoreTools.Translate("{0} is disabled", manager.DisplayName),
manager.Status.Version
);
}

[Fact]
public void FirstInitializeKeepsFoundManagerEnabledAfterItsExecutableDisappears()
{
var manager = CreateManager();
manager.ExecutablePath = CreateExecutable("installed-manager.exe");

manager.Initialize();

Assert.True(manager.IsReady());
Assert.True(Settings.DictionaryContainsKey<string, bool>(Settings.K.DisabledManagers, manager.Name));
Assert.False(Settings.GetDictionaryItem<string, bool>(Settings.K.DisabledManagers, manager.Name));

manager.ExecutableFound = false;
manager.Initialize();

Assert.True(manager.IsEnabled());
Assert.False(manager.IsReady());
Assert.False(manager.Status.Found);
}

[Fact]
public void FirstInitializeDisablesOnlyMissingManagersWhenInitializedConcurrently()
{
var managers = Enumerable.Range(0, 16).Select(i =>
{
var manager = CreateManager();
manager.ExecutableFound = i % 2 == 0;
manager.ExecutablePath = CreateExecutable($"{manager.Name}.exe");
return manager;
}).ToList();

Parallel.ForEach(managers, manager => manager.Initialize());

foreach (var manager in managers)
{
Assert.True(Settings.DictionaryContainsKey<string, bool>(Settings.K.DisabledManagers, manager.Name));
Assert.Equal(manager.ExecutableFound, manager.IsEnabled());
Assert.Equal(manager.ExecutableFound, manager.IsReady());
}
}

[Fact]
public void InitializeEnabledManagerWithExecutableBecomesReady()
{
Expand Down
6 changes: 6 additions & 0 deletions src/UniGetUI.Tui/FakeData/FakeDataEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ public static FakeDataEnvironment Activate(string? sandboxDirectory = null)
// The Devolutions Agent broker is a real out-of-process service; never route fake operations there.
Settings.Set(Settings.K.UseAgentBroker, false);

foreach (FakeManagerProfile profile in FakeCatalog.Managers.Where(p => !p.Found))
{
if (!Settings.DictionaryContainsKey<string, bool>(Settings.K.DisabledManagers, profile.Name))
Settings.SetDictionaryItem(Settings.K.DisabledManagers, profile.Name, false);
}

env.Managers = FakeCatalog.Managers.Select(p => (IPackageManager)new FakePackageManager(p, env)).ToArray();
Current = env;
return env;
Expand Down
Loading