Add a couple DesktopController tests

Many more to add as we've set up the outline
This commit is contained in:
Ben Olden-Cooligan 2022-06-26 22:10:09 -07:00
parent c77f5b30f9
commit 7544aff995
22 changed files with 205 additions and 67 deletions

View File

@ -38,7 +38,7 @@ public static class InternalDefaults
HasCheckedForUpdates = false,
LastUpdateCheckDate = DateTime.MinValue,
HasBeenRun = false,
FirstRunDate = DateTime.MinValue,
FirstRunDate = null,
HasBeenPromptedForDonation = false,
LastDonatePromptDate = DateTime.MinValue,
DeleteAfterSaving = false,

View File

@ -61,8 +61,9 @@ public class ThumbnailRenderQueue : IDisposable
private void RenderThumbnails()
{
// TODO: Make this run as async?
// TODO: Verify WorkerFactory is not null? Or handle this better for tests?
bool useWorker = PlatformCompat.Runtime.UseWorker;
var worker = useWorker ? _scanningContext.WorkerFactory.Create() : null;
var worker = useWorker ? _scanningContext.WorkerFactory?.Create() : null;
var fallback = new ExpFallback(100, 60 * 1000);
while (true)
{

View File

@ -1,40 +0,0 @@
using NAPS2.Config.Model;
using NAPS2.Sdk.Tests;
using Xunit;
namespace NAPS2.Lib.Tests.Config;
public class InternalDefaultsTests
{
[Fact]
public void NotNullProps()
{
var config = InternalDefaults.GetCommonConfig();
AssertPropNullOrNotNull(config, false, "");
}
private static void AssertPropNullOrNotNull(object config, bool shouldBeNull, string path)
{
Assert.True(config != null, path);
foreach (var prop in config.GetType().GetProperties())
{
var value = prop.GetValue(config);
if (ConfigLookup.HasConfigAttribute(prop))
{
// Child, so recurse
AssertPropNullOrNotNull(value, shouldBeNull, $"{path}{prop.Name}.");
}
else
{
if (shouldBeNull)
{
Assert.True(value == null, $"{prop.DeclaringType?.Name}.{prop.Name} == null");
}
else
{
Assert.True(value != null, $"{prop.DeclaringType?.Name}.{prop.Name} != null");
}
}
}
}
}

View File

@ -0,0 +1,107 @@
using Moq;
using NAPS2.ImportExport.Images;
using NAPS2.Platform.Windows;
using NAPS2.Recovery;
using NAPS2.Update;
using NAPS2.WinForms;
using Xunit;
namespace NAPS2.Sdk.Tests.WinForms;
public class DesktopControllerTests : ContextualTexts
{
// TODO: We should create individual unit tests for everything that's mocked here.
// TODO: We should also make some things more testable:
// - Change DesktopFormProvider to e.g. DesktopFormActivator or something else with a mockable interface
private readonly DesktopController _desktopController;
private readonly UiImageList _imageList;
private readonly RecoveryStorageManager _recoveryStorageManager;
private readonly ThumbnailRenderQueue _thumbnailRenderQueue;
private readonly Mock<OperationProgress> _operationProgress;
private readonly Naps2Config _config;
private readonly Mock<IOperationFactory> _operationFactory;
private readonly Mock<StillImage> _stillImage;
private readonly Mock<IUpdateChecker> _updateChecker;
private readonly Mock<INotificationManager> _notifcationManager;
private readonly ImageTransfer _imageTransfer;
private readonly ImageClipboard _imageClipboard;
private readonly Mock<IWinFormsExportHelper> _exportHelper;
private readonly DesktopImagesController _desktopImagesController;
private readonly Mock<IDesktopScanController> _desktopScanController;
private readonly DesktopFormProvider _desktopFormProvider;
public DesktopControllerTests()
{
ScanningContext.RecoveryPath = Path.Combine(FolderPath, "recovery");
ScanningContext.FileStorageManager = new FileStorageManager(ScanningContext.RecoveryPath);
_recoveryStorageManager = RecoveryStorageManager.CreateFolder(ScanningContext.RecoveryPath);
_imageList = new UiImageList(_recoveryStorageManager);
_thumbnailRenderQueue = new ThumbnailRenderQueue(ScanningContext, new ThumbnailRenderer(ImageContext));
_operationProgress = new Mock<OperationProgress>();
_config = Naps2Config.Stub();
_operationFactory = new Mock<IOperationFactory>();
_stillImage = new Mock<StillImage>();
_updateChecker = new Mock<IUpdateChecker>();
_notifcationManager = new Mock<INotificationManager>();
_imageTransfer = new ImageTransfer(ImageContext);
_imageClipboard = new ImageClipboard(ImageContext, _imageTransfer);
_exportHelper = new Mock<IWinFormsExportHelper>();
_desktopImagesController = new DesktopImagesController(_imageList);
_desktopScanController = new Mock<IDesktopScanController>();
_desktopFormProvider = new DesktopFormProvider();
_desktopController = new DesktopController(
ScanningContext,
_imageList,
_recoveryStorageManager,
_thumbnailRenderQueue,
_operationProgress.Object,
_config,
_operationFactory.Object,
_stillImage.Object,
_updateChecker.Object,
_notifcationManager.Object,
_imageTransfer,
_imageClipboard,
new ImageListActions(ImageContext, _imageList),
_exportHelper.Object,
_desktopImagesController,
_desktopScanController.Object,
_desktopFormProvider
);
_operationFactory.Setup(x => x.Create<RecoveryOperation>()).Returns(
new RecoveryOperation(new Mock<IFormFactory>().Object, new RecoveryManager(ScanningContext)));
}
public override void Dispose()
{
base.Dispose();
_desktopController.Cleanup();
}
[Fact]
public async Task Initialize_SetsFirstRunDate_IfNotRunBefore()
{
Assert.False(_config.Get(c => c.HasBeenRun));
Assert.Null(_config.Get(c => c.FirstRunDate));
await _desktopController.Initialize();
Assert.True(_config.Get(c => c.HasBeenRun));
Assert.InRange(_config.Get(c => c.FirstRunDate) ?? DateTime.MinValue,
DateTime.Now - TimeSpan.FromMilliseconds(10), DateTime.Now);
}
[Fact]
public async Task Initialize_DoesntSetFirstRunDate_IfAlreadyRun()
{
var firstRunDate = DateTime.Now - TimeSpan.FromDays(1);
_config.User.Set(c => c.HasBeenRun, true);
_config.User.Set(c => c.FirstRunDate, firstRunDate);
await _desktopController.Initialize();
Assert.True(_config.Get(c => c.HasBeenRun));
Assert.Equal(firstRunDate, _config.Get(c => c.FirstRunDate));
}
}

View File

@ -5,6 +5,7 @@ using NAPS2.ImportExport;
using NAPS2.ImportExport.Pdf;
using NAPS2.Scan;
using NAPS2.Scan.Batch;
using NAPS2.Update;
using NAPS2.WinForms;
using Ninject;
using Ninject.Modules;
@ -23,10 +24,13 @@ public class WinFormsModule : NinjectModule
Bind<IComponentInstallPrompt>().To<WinFormsComponentInstallPrompt>();
Bind<DialogHelper>().To<WinFormsDialogHelper>();
Bind<IEtoPlatform>().To<WinFormsEtoPlatform>();
Bind<NotificationManager>().ToSelf().InSingletonScope();
Bind<ISaveNotify>().ToMethod(ctx => ctx.Kernel.Get<NotificationManager>());
Bind<INotificationManager>().To<NotificationManager>().InSingletonScope();
Bind<ISaveNotify>().ToMethod(ctx => ctx.Kernel.Get<INotificationManager>());
Bind<IScannedImagePrinter>().To<PrintDocumentPrinter>();
Bind<IDevicePrompt>().To<WinFormsDevicePrompt>();
Bind<DesktopController>().ToSelf().InSingletonScope();
Bind<IUpdateChecker>().To<UpdateChecker>();
Bind<IWinFormsExportHelper>().To<WinFormsExportHelper>();
Bind<IDesktopScanController>().To<DesktopScanController>();
}
}

View File

@ -0,0 +1,8 @@
namespace NAPS2.Update;
public interface IUpdateChecker
{
TimeSpan CheckInterval { get; }
Task<UpdateInfo> CheckForUpdates();
UpdateOperation StartUpdate(UpdateInfo update);
}

View File

@ -4,7 +4,7 @@ using Newtonsoft.Json.Linq;
namespace NAPS2.Update;
public class UpdateChecker
public class UpdateChecker : IUpdateChecker
{
private const string UPDATE_CHECK_ENDPOINT = "https://www.naps2.com/api/v1/update";
#if STANDALONE

View File

@ -26,14 +26,14 @@ public class DesktopController
private readonly Naps2Config _config;
private readonly IOperationFactory _operationFactory;
private readonly StillImage _stillImage;
private readonly UpdateChecker _updateChecker;
private readonly NotificationManager _notify;
private readonly IUpdateChecker _updateChecker;
private readonly INotificationManager _notify;
private readonly ImageTransfer _imageTransfer;
private readonly ImageClipboard _imageClipboard;
private readonly ImageListActions _imageListActions;
private readonly WinFormsExportHelper _exportHelper;
private readonly IWinFormsExportHelper _exportHelper;
private readonly DesktopImagesController _desktopImagesController;
private readonly DesktopScanController _desktopScanController;
private readonly IDesktopScanController _desktopScanController;
private readonly DesktopFormProvider _desktopFormProvider;
private bool _closed;
@ -42,9 +42,9 @@ public class DesktopController
RecoveryStorageManager recoveryStorageManager, ThumbnailRenderQueue thumbnailRenderQueue,
OperationProgress operationProgress, Naps2Config config, IOperationFactory operationFactory,
StillImage stillImage,
UpdateChecker updateChecker, NotificationManager notify, ImageTransfer imageTransfer,
ImageClipboard imageClipboard, ImageListActions imageListActions, WinFormsExportHelper exportHelper,
DesktopImagesController desktopImagesController, DesktopScanController desktopScanController,
IUpdateChecker updateChecker, INotificationManager notify, ImageTransfer imageTransfer,
ImageClipboard imageClipboard, ImageListActions imageListActions, IWinFormsExportHelper exportHelper,
DesktopImagesController desktopImagesController, IDesktopScanController desktopScanController,
DesktopFormProvider desktopFormProvider)
{
_scanningContext = scanningContext;

View File

@ -3,7 +3,7 @@ using NAPS2.Wia;
namespace NAPS2.WinForms;
public class DesktopScanController
public class DesktopScanController : IDesktopScanController
{
private readonly Naps2Config _config;
private readonly IProfileManager _profileManager;

View File

@ -21,7 +21,7 @@ namespace NAPS2.WinForms
private readonly IScannedImagePrinter _scannedImagePrinter;
private readonly KeyboardShortcutManager _ksm;
private readonly ThumbnailRenderer _thumbnailRenderer;
private readonly NotificationManager _notify;
private readonly INotificationManager _notify;
private readonly CultureHelper _cultureHelper;
private readonly IProfileManager _profileManager;
private readonly UiImageList _imageList;
@ -29,7 +29,7 @@ namespace NAPS2.WinForms
private readonly ThumbnailRenderQueue _thumbnailRenderQueue;
private readonly UiThumbnailProvider _thumbnailProvider;
private readonly DesktopController _desktopController;
private readonly DesktopScanController _desktopScanController;
private readonly IDesktopScanController _desktopScanController;
private readonly ImageListActions _imageListActions;
private readonly DesktopFormProvider _desktopFormProvider;
private readonly DesktopSubFormController _desktopSubFormController;
@ -46,7 +46,7 @@ namespace NAPS2.WinForms
IScannedImagePrinter scannedImagePrinter,
KeyboardShortcutManager ksm,
ThumbnailRenderer thumbnailRenderer,
NotificationManager notify,
INotificationManager notify,
CultureHelper cultureHelper,
IProfileManager profileManager,
UiImageList imageList,
@ -54,7 +54,7 @@ namespace NAPS2.WinForms
ThumbnailRenderQueue thumbnailRenderQueue,
UiThumbnailProvider thumbnailProvider,
DesktopController desktopController,
DesktopScanController desktopScanController,
IDesktopScanController desktopScanController,
ImageListActions imageListActions,
DesktopFormProvider desktopFormProvider, DesktopSubFormController desktopSubFormController)
{

View File

@ -30,7 +30,7 @@ namespace NAPS2.WinForms
private ToolStripSeparator _toolStripSeparator2;
private ToolStripButton _tsSaveImage;
private readonly IOperationFactory _operationFactory;
private readonly WinFormsExportHelper _exportHelper;
private readonly IWinFormsExportHelper _exportHelper;
private ToolStripButton _tsHueSaturation;
private ToolStripButton _tsBlackWhite;
private ToolStripButton _tsSharpen;
@ -38,7 +38,7 @@ namespace NAPS2.WinForms
private readonly OperationProgress _operationProgress;
private readonly ImageContext _imageContext;
public FViewer(IOperationFactory operationFactory, WinFormsExportHelper exportHelper, KeyboardShortcutManager ksm, OperationProgress operationProgress, ImageContext imageContext)
public FViewer(IOperationFactory operationFactory, IWinFormsExportHelper exportHelper, KeyboardShortcutManager ksm, OperationProgress operationProgress, ImageContext imageContext)
{
_operationFactory = operationFactory;
_exportHelper = exportHelper;

View File

@ -0,0 +1,11 @@
using NAPS2.Scan;
namespace NAPS2.WinForms;
public interface IDesktopScanController
{
Task ScanWithDevice(string deviceID);
Task ScanDefault();
Task ScanWithNewProfile();
Task ScanWithProfile(ScanProfile profile);
}

View File

@ -0,0 +1,12 @@
using NAPS2.Update;
namespace NAPS2.WinForms;
public interface INotificationManager : ISaveNotify
{
FormBase ParentForm { get; set; }
void DonatePrompt();
void OperationProgress(OperationProgress opModalProgress, IOperation op);
void UpdateAvailable(IUpdateChecker updateChecker, UpdateInfo update);
void Rebuild();
}

View File

@ -0,0 +1,11 @@
using NAPS2.ImportExport.Email;
namespace NAPS2.WinForms;
public interface IWinFormsExportHelper
{
Task<bool> SavePDF(IList<ProcessedImage> images, ISaveNotify notify);
Task<bool> ExportPDF(string filename, IList<ProcessedImage> images, bool email, EmailMessage emailMessage);
Task<bool> SaveImages(IList<ProcessedImage> images, ISaveNotify notify);
Task<bool> EmailPDF(IList<ProcessedImage> images);
}

View File

@ -3,7 +3,7 @@ using NAPS2.Update;
namespace NAPS2.WinForms;
public class NotificationManager : ISaveNotify
public class NotificationManager : INotificationManager
{
private const int PADDING_X = 25, PADDING_Y = 25;
private const int SPACING_Y = 20;
@ -54,7 +54,7 @@ public class NotificationManager : ISaveNotify
Show(new OperationProgressNotifyWidget(opModalProgress, op));
}
public void UpdateAvailable(UpdateChecker updateChecker, UpdateInfo update)
public void UpdateAvailable(IUpdateChecker updateChecker, UpdateInfo update)
{
Show(new UpdateAvailableNotifyWidget(updateChecker, update));
}

View File

@ -5,10 +5,10 @@ namespace NAPS2.WinForms;
public class UpdateAvailableNotifyWidget : NotifyWidget
{
private readonly UpdateChecker _updateChecker;
private readonly IUpdateChecker _updateChecker;
private readonly UpdateInfo _update;
public UpdateAvailableNotifyWidget(UpdateChecker updateChecker, UpdateInfo update)
public UpdateAvailableNotifyWidget(IUpdateChecker updateChecker, UpdateInfo update)
: base(MiscResources.UpdateAvailable, string.Format(MiscResources.Install, update.Name), null, null)
{
_updateChecker = updateChecker;

View File

@ -7,7 +7,7 @@ using NAPS2.ImportExport.Pdf;
namespace NAPS2.WinForms;
// TODO: Rename this ImageExportController or something
public class WinFormsExportHelper
public class WinFormsExportHelper : IWinFormsExportHelper
{
private readonly DialogHelper _dialogHelper;
private readonly IOperationFactory _operationFactory;

View File

@ -6,12 +6,12 @@ namespace NAPS2.WinForms;
public class WinFormsOperationProgress : OperationProgress
{
private readonly IFormFactory _formFactory;
private readonly NotificationManager _notificationManager;
private readonly INotificationManager _notificationManager;
private readonly Naps2Config _config;
private readonly HashSet<IOperation> _activeOperations = new HashSet<IOperation>();
public WinFormsOperationProgress(IFormFactory formFactory, NotificationManager notificationManager, Naps2Config config)
public WinFormsOperationProgress(IFormFactory formFactory, INotificationManager notificationManager, Naps2Config config)
{
_formFactory = formFactory;
_notificationManager = notificationManager;

View File

@ -0,0 +1,11 @@
using Xunit;
namespace NAPS2.Sdk.Tests.Images;
public class UiImageListTests
{
[Fact]
public void Something()
{
}
}

View File

@ -0,0 +1,11 @@
using Xunit;
namespace NAPS2.Sdk.Tests.Images;
public class UiImageTests
{
[Fact]
public void Something()
{
}
}

View File

@ -23,6 +23,7 @@ public class UiImageList
_selection = ListSelection.Empty<UiImage>();
}
// TODO: Can we inject this?
public ThumbnailRenderer? ThumbnailRenderer { get; set; }
// TODO: Make this immutable?
@ -105,6 +106,7 @@ public class UiImageList
private void UpdateSelectionOnUiThread(ListSelection<UiImage> currentSelection)
{
// TODO: This won't work right as SyncContext.Current is only set on the UI thread anyway
var syncContext = SynchronizationContext.Current;
if (syncContext != null)
{

View File

@ -38,7 +38,7 @@ public class ScanningContext : IDisposable
public string? RecoveryPath { get; set; }
public IWorkerFactory WorkerFactory { get; set; }
public IWorkerFactory? WorkerFactory { get; set; }
public OcrRequestQueue OcrRequestQueue { get; } = new();