using System.Reactive.Linq; using System.Reactive.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using NSubstitute; using PLib.Application.Library; using PLib.Desktop.Services; using PLib.Desktop.Settings; using PLib.Desktop.ViewModels; using Shouldly; namespace PLib.Tests.Settings; public sealed class SettingsViewModelTests { private readonly IAppSettingsStore _store = Substitute.For(); [Fact] public async Task Opening_and_saving_without_touching_anything_changes_nothing() { // Both of these are displayed in a lossier unit than they are stored in: 65 536 bytes // shows as 0,06 MB. Converting the untouched field back used to yield 62 915, which // silently rewrote the setting and forced a full rescan on every visit to the panel. var options = new LibraryOptions { Folders = [@"C:\videos"], MinimumFileSizeInBytes = 65_536, ThumbnailPositionRatio = 0.15, }; var (viewModel, outcome) = await SaveAsync(options); outcome.RescanRequired.ShouldBeFalse(); Written.MinimumFileSizeInBytes.ShouldBe(65_536); Written.ThumbnailPositionRatio.ShouldBe(0.15); viewModel.HasFolders.ShouldBeTrue(); } [Fact] public async Task Editing_the_minimum_size_does_write_the_new_value() { var (_, outcome) = await SaveAsync( new LibraryOptions { Folders = [@"C:\videos"], MinimumFileSizeInBytes = 65_536 }, viewModel => viewModel.MinimumFileSizeMegabytes = 4); Written.MinimumFileSizeInBytes.ShouldBe(4 * 1024 * 1024); outcome.RescanRequired.ShouldBeTrue(); } [Fact] public async Task Removing_a_folder_is_carried_into_the_saved_settings() { var (_, outcome) = await SaveAsync( new LibraryOptions { Folders = [@"C:\a", @"C:\b"] }, viewModel => viewModel.Folders[0].RemoveCommand.Execute().Subscribe()); Written.Folders.ShouldBe([@"C:\b"]); outcome.RescanRequired.ShouldBeTrue(); } [Fact] public async Task Cancelling_writes_nothing() { var viewModel = Create(new LibraryOptions { Folders = [@"C:\videos"] }); var closed = viewModel.Closed.FirstAsync().ToTask(); viewModel.MinimumFileSizeMegabytes = 999; await viewModel.CancelCommand.Execute().FirstAsync().ToTask(TestContext.Current.CancellationToken); (await closed).Saved.ShouldBeFalse(); _store.ReceivedCalls().ShouldBeEmpty(); } private AppSettings Written => (AppSettings)_store.ReceivedCalls().Single(call => call.GetMethodInfo().Name == nameof(IAppSettingsStore.SaveAsync)) .GetArguments()[0]!; private async Task<(SettingsViewModel ViewModel, SettingsDialogOutcome Outcome)> SaveAsync( LibraryOptions options, Action? edit = null) { var viewModel = Create(options); var closed = viewModel.Closed.FirstAsync().ToTask(); edit?.Invoke(viewModel); await viewModel.SaveCommand.Execute().FirstAsync().ToTask(TestContext.Current.CancellationToken); return (viewModel, await closed); } private SettingsViewModel Create(LibraryOptions options) => new( Substitute.For(), Monitor(options), Monitor(new AppearanceOptions()), _store, Substitute.For(), Substitute.For(), NullLogger.Instance); private static IOptionsMonitor Monitor(T value) { var monitor = Substitute.For>(); monitor.CurrentValue.Returns(value); return monitor; } }