Skip to content
This repository has been archived by the owner on Oct 6, 2020. It is now read-only.

Commit

Permalink
feat: Core functionality to scan for images
Browse files Browse the repository at this point in the history
  • Loading branch information
StanleyGoldman committed Sep 8, 2019
1 parent d00323d commit 7dfe7e8
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
using Autofac.Extras.NSubstitute;
using SonOfPicasso.Data.Repository;
using SonOfPicasso.Data.Tests;
using SonOfPicasso.Testing.Common;
using Xunit;
using Xunit.Abstractions;

namespace SonOfPicasso.Core.Tests.IntegrationTests.Services
namespace SonOfPicasso.Core.IntegrationTests.Services
{
public class ImageManagementServiceTests : DataTestsBase, IDisposable
{
Expand All @@ -19,7 +20,7 @@ public ImageManagementServiceTests(ITestOutputHelper testOutputHelper)
}

[Fact]
public void Blah()
public void CanInitialize()
{
var imageManagementService = _autoSubstitute.Resolve<Core.Services.ImageManagementService>();
}
Expand Down
110 changes: 46 additions & 64 deletions src/SonOfPicasso.Core.Tests/Services/ImageManagementServiceTests.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Abstractions;
using System.IO.Abstractions.TestingHelpers;
using System.Reactive.Linq;
using System.Threading;
using Autofac.Extras.NSubstitute;
using FluentAssertions;
using NSubstitute;
using SonOfPicasso.Core.Interfaces;
using SonOfPicasso.Core.Scheduling;
using SonOfPicasso.Core.Services;
using SonOfPicasso.Data.Model;
using SonOfPicasso.Data.Repository;
using SonOfPicasso.Testing.Common;
using SonOfPicasso.Testing.Common.Extensions;
using SonOfPicasso.Testing.Common.Scheduling;
using Xunit;
using Xunit.Abstractions;
using Directory = SonOfPicasso.Data.Model.Directory;
Expand All @@ -15,104 +24,77 @@ namespace SonOfPicasso.Core.Tests.Services
{
public class ImageManagementServiceTests : TestsBase, IDisposable
{
private readonly AutoSubstitute _autoSubstitute;
private readonly Queue<IUnitOfWork> _unitOfWorkQueue;

public ImageManagementServiceTests(ITestOutputHelper testOutputHelper)
: base(testOutputHelper)
{
_autoSubstitute = new AutoSubstitute();

_testSchedulerProvider = new TestSchedulerProvider();
_autoSubstitute.Provide<ISchedulerProvider>(_testSchedulerProvider);

_unitOfWorkQueue = new Queue<IUnitOfWork>();
_autoSubstitute.Provide<Func<IUnitOfWork>>(() => _unitOfWorkQueue.Dequeue());

_mockFileSystem = new MockFileSystem();
_autoSubstitute.Provide<IFileSystem>(_mockFileSystem);
}

[Fact]
public void ShouldAddDirectory()
public void Dispose()
{
var directoryPath = Faker.System.DirectoryPathWindows();

var unitOfWork = Substitute.For<IUnitOfWork>();
unitOfWork.DirectoryRepository.Get().Returns(new Directory[0]);

_unitOfWorkQueue.Enqueue(unitOfWork);

var imageManagementService = _autoSubstitute.Resolve<Core.Services.ImageManagementService>();
imageManagementService.AddFolder(directoryPath);

unitOfWork.DirectoryRepository
.ReceivedWithAnyArgs(1)
.Insert(Arg.Any<Directory>());

unitOfWork.Received(1)
.Save();

unitOfWork.Received(1)
.Dispose();
_autoSubstitute.Dispose();
}

private readonly AutoSubstitute _autoSubstitute;
private readonly Queue<IUnitOfWork> _unitOfWorkQueue;
private readonly MockFileSystem _mockFileSystem;
private readonly TestSchedulerProvider _testSchedulerProvider;

[Fact]
public void ShouldSkipAddingDirectoryIfExists()
public void ScanFolder()
{
var directoryPath = Faker.System.DirectoryPathWindows();
var imagePath = Path.Join(directoryPath, Faker.System.FileName(".jpg"));

var unitOfWork = Substitute.For<IUnitOfWork>();
unitOfWork.DirectoryRepository.Get().Returns(new Directory[1]{new Directory()
var directory = new Directory
{
Id = Faker.Random.Int(1),
Path = directoryPath,
Images = new List<Image>()
}});
};

var unitOfWork = Substitute.For<IUnitOfWork>();
unitOfWork.DirectoryRepository.Get()
.ReturnsForAnyArgs(new[] {directory, FakerProfiles.FakeNewDirectory});

_unitOfWorkQueue.Enqueue(unitOfWork);

var imageManagementService = _autoSubstitute.Resolve<Core.Services.ImageManagementService>();
imageManagementService.AddFolder(directoryPath);
_mockFileSystem.AddDirectory(directoryPath);
_mockFileSystem.AddFile(imagePath, new MockFileData(new byte[0]));

unitOfWork.DirectoryRepository
.DidNotReceiveWithAnyArgs()
.Insert(Arg.Any<Directory>());
var autoResetEvent = new AutoResetEvent(false);

unitOfWork.DidNotReceive()
.Save();
_autoSubstitute.Resolve<IImageLocationService>()
.GetImages(Arg.Any<string>())
.Returns(Observable.Return(new[] {imagePath}));

unitOfWork.Received(1)
.Dispose();
}
var imageManagementService = _autoSubstitute.Resolve<ImageManagementService>();
imageManagementService.ScanFolder(directoryPath)
.Subscribe(unit => autoResetEvent.Set());

[Fact]
public void ShouldSkipAddingDirectoryIfChildOfExisting()
{
var directoryPath = Faker.System.DirectoryPathWindows();
var parentDirectoryPath = new DirectoryInfo(directoryPath).Parent.ToString();
_testSchedulerProvider.TaskPool.AdvanceBy(1);

var unitOfWork = Substitute.For<IUnitOfWork>();
unitOfWork.DirectoryRepository.Get().Returns(new Directory[1]{new Directory()
{
Id = Faker.Random.Int(1),
Path = parentDirectoryPath,
Images = new List<Image>()
}});

_unitOfWorkQueue.Enqueue(unitOfWork);

var imageManagementService = _autoSubstitute.Resolve<Core.Services.ImageManagementService>();
imageManagementService.AddFolder(directoryPath);
autoResetEvent.WaitOne(10).Should().BeTrue();

unitOfWork.DirectoryRepository
.DidNotReceiveWithAnyArgs()
.Insert(Arg.Any<Directory>());
unitOfWork.ImageRepository.Received(1)
.Insert(Arg.Any<Image>());

unitOfWork.DidNotReceive()
unitOfWork.Received(1)
.Save();

unitOfWork.Received(1)
.Dispose();
}

public void Dispose()
{
_autoSubstitute.Dispose();
directory.Images.Count.Should().Be(1);
}
}
}
3 changes: 2 additions & 1 deletion src/SonOfPicasso.Core/Interfaces/IImageManagementService.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System;
using System.Reactive;
using SonOfPicasso.Core.Models;
using SonOfPicasso.Data.Model;

namespace SonOfPicasso.Core.Interfaces
{
public interface IImageManagementService
{
void AddFolder(string path);
IObservable<Image[]> ScanFolder(string path);
}
}
4 changes: 3 additions & 1 deletion src/SonOfPicasso.Core/Services/ImageLocationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ public class ImageLocationService: IImageLocationService
private readonly ISchedulerProvider _schedulerProvider;
private readonly ILogger _logger;

public ImageLocationService(ILogger logger, IFileSystem fileSystem, ISchedulerProvider schedulerProvider)
public ImageLocationService(ILogger logger,
IFileSystem fileSystem,
ISchedulerProvider schedulerProvider)
{
_logger = logger;
_fileSystem = fileSystem;
Expand Down
88 changes: 46 additions & 42 deletions src/SonOfPicasso.Core/Services/ImageManagementService.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Serilog;
using SonOfPicasso.Core.Interfaces;
using SonOfPicasso.Data;
using SonOfPicasso.Core.Scheduling;
using SonOfPicasso.Data.Model;
using SonOfPicasso.Data.Repository;
using Directory = SonOfPicasso.Data.Model.Directory;
Expand All @@ -14,67 +18,67 @@ namespace SonOfPicasso.Core.Services
{
public class ImageManagementService : IImageManagementService
{
private readonly Func<IUnitOfWork> _unitOfWorkFactory;
private readonly IFileSystem _fileSystem;
private readonly IImageLocationService _imageLocationService;
private readonly ILogger _logger;
private readonly ISchedulerProvider _schedulerProvider;
private readonly Func<IUnitOfWork> _unitOfWorkFactory;

public ImageManagementService(ILogger logger,
IFileSystem fileSystem,
IImageLocationService imageLocationService,
Func<IUnitOfWork> unitOfWorkFactory)
Func<IUnitOfWork> unitOfWorkFactory,
ISchedulerProvider schedulerProvider)
{
_logger = logger;
_fileSystem = fileSystem;
_imageLocationService = imageLocationService;
_unitOfWorkFactory = unitOfWorkFactory;
_schedulerProvider = schedulerProvider;
}

public void AddFolder(string path)
public IObservable<Image[]> ScanFolder(string path)
{
using var unitOfWork = _unitOfWorkFactory();
var paths = unitOfWork.DirectoryRepository.Get()
.Select(directory => directory.Path)
.ToArray();

if (paths.Any(s => path == s || path.IsSubDirectoryOf(s)))
return Observable.StartAsync<Image[]>(async task =>
{
return;
}
using var unitOfWork = _unitOfWorkFactory();
if (!_fileSystem.Directory.Exists(path))
throw new SonOfPicassoException($"Path: `{path}` does not exist");
unitOfWork.DirectoryRepository.Insert(new Directory { Path = path });
unitOfWork.Save();
}
}
var images = await _imageLocationService.GetImages(path)
.SelectMany(locatedImages => locatedImages)
.Where(s => !unitOfWork.ImageRepository.Get(image => image.Path == path).Any())
.GroupBy(s => _fileSystem.FileInfo.FromFileName(s).DirectoryName)
.SelectMany(groupedObservable =>
{
var directory = unitOfWork.DirectoryRepository.Get(directory => directory.Path == groupedObservable.Key)
.FirstOrDefault();
public static class StringExtensions
{
// https://stackoverflow.com/a/23354773/104877
public static bool IsSubDirectoryOf(this string candidate, string other)
{
var isChild = false;
try
{
var candidateInfo = new DirectoryInfo(candidate);
var otherInfo = new DirectoryInfo(other);
if (directory == null)
{
directory = new Directory { Path = groupedObservable.Key, Images = new List<Image>()};
unitOfWork.DirectoryRepository.Insert(directory);
}
while (candidateInfo.Parent != null)
{
if (candidateInfo.Parent.FullName == otherInfo.FullName)
{
isChild = true;
break;
}
else candidateInfo = candidateInfo.Parent;
}
}
catch (Exception error)
{
var message = String.Format("Unable to check directories {0} and {1}: {2}", candidate, other, error);
Trace.WriteLine(message);
}
return groupedObservable.Select(imagePath =>
{
var image = new Image()
{
Path = imagePath
};
unitOfWork.ImageRepository.Insert(image);
directory.Images.Add(image);
return image;
});
}).ToArray();
unitOfWork.Save();
return isChild;
return images;
}, _schedulerProvider.TaskPool);
}
}
}
1 change: 1 addition & 0 deletions src/SonOfPicasso.Data.Tests/SonOfPicasso.Data.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
</ItemGroup>

<ItemGroup>
<Folder Include="Common\" />
<Folder Include="Properties\" />
</ItemGroup>

Expand Down

0 comments on commit 7dfe7e8

Please sign in to comment.