EPUB Support (#178)

* Added book filetype detection and reorganized tests due to size of file

* Added ability to get basic Parse Info from Book and Pages.

* We can now scan books and get them in a library with cover images.

* Take the first image in the epub if the cover isn't set.

* Implemented the ability to unzip the ebup to cache. Implemented a test api to load html files.

* Just some test code to figure out how to approach this.

* Fixed some merge conflicts

* Removed some dead code from merge

* Snapshot: I can now load everything properly into the UI by rewriting the urls before I send them back. I don't notice any lag from this method. It can be optimized further.

* Implemented a way to load the content in the browser not via an iframe.

* Added a note

* Anchor mappings is complete. New anchors are updated so references now resolve to javascript:void() for UI to take care of internally loading and the appropriate page is mapped to it. Anchors that are external have target="_blank" added so they don't force you out of the app and styles are of course inlined.

* Oops i need this

* Table of contents api implemented (rough) and some small enhancements to codebase for books.

* GetBookPageResources now only loads files from within the book. Nested chapter list support and images now use html parsing instead of string parsing.

* Fonts now are remapped to load from endpoint.

* book-resources now uses a key, ensuring the file is in proper format for lookup. Changed chapter list based on structure with one HEADER and nested chapters.

* Properly handle svg resource requests and when there are part anchors that are clickable, make sure we handle them in the UI by adding a kavita-page handler.

* Add Chapter group page even if one isn't set by using first page (without part) from nestedChildren.

* Added extra debug code for issue #163.

* Added new user preferences for books and updated the css so we scope it to our reading section.

* Cleaned up style code

* Implemented ability to save book preferences and some cleanup on existing apis.

* Added an api for checking if a user has read something in a library type before.

* Forgot to make sure the has reading progress is against a user lol.

* Remove cacheservice code for books, sine we use an in-memory method

* Handle svg images as well

* Enhanced cover image extraction to check for a "cover" image if the cover image wasn't set in OPF before falling back to the first image.

* Fixed an issue with special books not properly generating metadata due to not having filename set.

* Cleanup, removed warmup task code from statup/program and changed taskscheduler to schedule tasks on startup only (or if tasks are changed from UI).

* Code cleanup

* Code cleanup

* So much code. Lots of refactors to try to test scanner service. Moved a lot of the queries into Extensions to allow to easier test, even though it's hacky. Support @font-face src:url swaps with ' and ". Source summary information from epubs.

* Well...baseURL needs to come from BE and not from UI lol.

* Adjusted migrations so default values match Entity

* Removed comment

* I think I finally fixed #163! The issue was that when i checked if it had a parserInfo, i wasn't considering that the chapter range might have a - in it (0-6) and so when the code to check if range could parse out a number failed, it treated it like a special and checked range against info's filename.

* Some bugfixes

* Lots of testing, extracting code to make it easier to test. This code is buggy, but fixed a bug where 1) If we changed the normalization code, we would remove the whole db during a scan and 2) We weren't actually removing series properly.

Other than that, code is being extracted to remove duplication and centralize logic.

* More code cleanup and test cleanup to ensure scan loop is working as expected and matches expectaions from tests.

* Cleaned up the code and made it so if I change normalization, which I do in this branch, it wont break existing DBs.

* Some comic parser changes for partial chapter support.

* Added some code for directory service and scanner service along with python code to generate test files (not used yet). Fixed up all the tests.

* Code smells
This commit is contained in:
Joseph Milazzo 2021-04-28 16:16:22 -05:00 committed by GitHub
parent 2b99c8abfa
commit a01613f80f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
103 changed files with 5017 additions and 2480 deletions

View file

@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="NSubstitute" Version="4.2.2" />
<PackageReference Include="xunit" Version="2.4.1" />
@ -26,6 +27,7 @@
<ItemGroup>
<Folder Include="Services\Test Data\ArchiveService\ComicInfos" />
<Folder Include="Services\Test Data\ScannerService\Manga" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,29 @@
using System;
using API.Data;
using API.Tests.Helpers;
using Xunit;
namespace API.Tests.Entities
{
/// <summary>
/// Tests for <see cref="API.Entities.Series"/>
/// </summary>
public class SeriesTest
{
[Theory]
[InlineData("Darker than Black")]
public void CreateSeries(string name)
{
var key = API.Parser.Parser.Normalize(name);
var series = DbFactory.Series(name);
Assert.Equal(0, series.Id);
Assert.Equal(0, series.Pages);
Assert.Equal(name, series.Name);
Assert.Null(series.CoverImage);
Assert.Equal(name, series.LocalizedName);
Assert.Equal(name, series.SortName);
Assert.Equal(name, series.OriginalName);
Assert.Equal(key, series.NormalizedName);
}
}
}

View file

@ -0,0 +1,86 @@
using System.Collections.Generic;
using API.Entities;
using API.Entities.Enums;
using API.Extensions;
using API.Parser;
using Xunit;
namespace API.Tests.Extensions
{
public class ChapterListExtensionsTests
{
private Chapter CreateChapter(string range, string number, MangaFile file, bool isSpecial)
{
return new Chapter()
{
Range = range,
Number = number,
Files = new List<MangaFile>() {file},
IsSpecial = isSpecial
};
}
private MangaFile CreateFile(string file, MangaFormat format)
{
return new MangaFile()
{
FilePath = file,
Format = format
};
}
[Fact]
public void GetAnyChapterByRange_Test_ShouldBeNull()
{
var info = new ParserInfo()
{
Chapters = "0",
Edition = "",
Format = MangaFormat.Archive,
FullFilePath = "/manga/darker than black.cbz",
Filename = "darker than black.cbz",
IsSpecial = false,
Series = "darker than black",
Title = "darker than black",
Volumes = "0"
};
var chapterList = new List<Chapter>()
{
CreateChapter("darker than black - Some special", "0", CreateFile("/manga/darker than black - special.cbz", MangaFormat.Archive), true)
};
var actualChapter = chapterList.GetChapterByRange(info);
Assert.NotEqual(chapterList[0], actualChapter);
}
[Fact]
public void GetAnyChapterByRange_Test_ShouldBeNotNull()
{
var info = new ParserInfo()
{
Chapters = "0",
Edition = "",
Format = MangaFormat.Archive,
FullFilePath = "/manga/darker than black.cbz",
Filename = "darker than black.cbz",
IsSpecial = true,
Series = "darker than black",
Title = "darker than black",
Volumes = "0"
};
var chapterList = new List<Chapter>()
{
CreateChapter("darker than black", "0", CreateFile("/manga/darker than black.cbz", MangaFormat.Archive), true)
};
var actualChapter = chapterList.GetChapterByRange(info);
Assert.Equal(chapterList[0], actualChapter);
}
}
}

View file

@ -0,0 +1,27 @@
using System;
using System.IO;
using API.Extensions;
using NSubstitute;
using Xunit;
namespace API.Tests.Extensions
{
public class FileInfoExtensionsTests
{
// [Fact]
// public void DoesLastWriteMatchTest()
// {
// var fi = Substitute.For<FileInfo>();
// fi.LastWriteTime = DateTime.Now;
//
// var deltaTime = DateTime.Today.Subtract(TimeSpan.FromDays(1));
// Assert.False(fi.DoesLastWriteMatch(deltaTime));
// }
//
// [Fact]
// public void IsLastWriteLessThanTest()
// {
//
// }
}
}

View file

@ -0,0 +1,42 @@
using System.Collections.Generic;
using System.Linq;
using API.Entities;
using API.Entities.Enums;
using API.Extensions;
using API.Parser;
using API.Tests.Helpers;
using Xunit;
namespace API.Tests.Extensions
{
public class ParserInfoListExtensions
{
[Theory]
[InlineData(new string[] {"1", "1", "3-5", "5", "8", "0", "0"}, new string[] {"1", "3-5", "5", "8", "0"})]
public void DistinctVolumesTest(string[] volumeNumbers, string[] expectedNumbers)
{
var infos = volumeNumbers.Select(n => new ParserInfo() {Volumes = n}).ToList();
Assert.Equal(expectedNumbers, infos.DistinctVolumes());
}
[Theory]
[InlineData(new string[] {@"Cynthia The Mission - c000-006 (v06) [Desudesu&Brolen].zip"}, new string[] {@"E:\Manga\Cynthia the Mission\Cynthia The Mission - c000-006 (v06) [Desudesu&Brolen].zip"}, true)]
[InlineData(new string[] {@"Cynthia The Mission - c000-006 (v06-07) [Desudesu&Brolen].zip"}, new string[] {@"E:\Manga\Cynthia the Mission\Cynthia The Mission - c000-006 (v06) [Desudesu&Brolen].zip"}, true)]
[InlineData(new string[] {@"Cynthia The Mission v20 c12-20 [Desudesu&Brolen].zip"}, new string[] {@"E:\Manga\Cynthia the Mission\Cynthia The Mission - c000-006 (v06) [Desudesu&Brolen].zip"}, false)]
public void HasInfoTest(string[] inputInfos, string[] inputChapters, bool expectedHasInfo)
{
var infos = new List<ParserInfo>();
foreach (var filename in inputInfos)
{
infos.Add(API.Parser.Parser.Parse(
filename,
string.Empty));
}
var files = inputChapters.Select(s => EntityFactory.CreateMangaFile(s, MangaFormat.Archive, 199)).ToList();
var chapter = EntityFactory.CreateChapter("0-6", false, files);
Assert.Equal(expectedHasInfo, infos.HasInfo(chapter));
}
}
}

View file

@ -1,4 +1,5 @@
using API.Entities;
using System;
using API.Entities;
using API.Extensions;
using Xunit;
@ -10,6 +11,11 @@ namespace API.Tests.Extensions
[InlineData(new [] {"Darker than Black", "Darker Than Black", "Darker than Black"}, new [] {"Darker than Black"}, true)]
[InlineData(new [] {"Darker than Black", "Darker Than Black", "Darker than Black"}, new [] {"Darker_than_Black"}, true)]
[InlineData(new [] {"Darker than Black", "Darker Than Black", "Darker than Black"}, new [] {"Darker then Black!"}, false)]
[InlineData(new [] {"Salem's Lot", "Salem's Lot", "Salem's Lot"}, new [] {"Salem's Lot"}, true)]
[InlineData(new [] {"Salem's Lot", "Salem's Lot", "Salem's Lot"}, new [] {"salems lot"}, true)]
[InlineData(new [] {"Salem's Lot", "Salem's Lot", "Salem's Lot"}, new [] {"salem's lot"}, true)]
// Different normalizations pass as we check normalization against an on-the-fly calculation so we don't delete series just because we change how normalization works
[InlineData(new [] {"Salem's Lot", "Salem's Lot", "Salem's Lot", "salems lot"}, new [] {"salem's lot"}, true)]
public void NameInListTest(string[] seriesInput, string[] list, bool expected)
{
var series = new Series()
@ -17,7 +23,7 @@ namespace API.Tests.Extensions
Name = seriesInput[0],
LocalizedName = seriesInput[1],
OriginalName = seriesInput[2],
NormalizedName = Parser.Parser.Normalize(seriesInput[0])
NormalizedName = seriesInput.Length == 4 ? seriesInput[3] : API.Parser.Parser.Normalize(seriesInput[0])
};
Assert.Equal(expected, series.NameInList(list));

View file

@ -0,0 +1,57 @@
using System.Collections.Generic;
using API.Entities;
using API.Entities.Enums;
namespace API.Tests.Helpers
{
/// <summary>
/// Used to help quickly create DB entities for Unit Testing
/// </summary>
public static class EntityFactory
{
public static Series CreateSeries(string name)
{
return new Series()
{
Name = name,
SortName = name,
LocalizedName = name,
NormalizedName = API.Parser.Parser.Normalize(name),
Volumes = new List<Volume>()
};
}
public static Volume CreateVolume(string volumeNumber, List<Chapter> chapters = null)
{
return new Volume()
{
Name = volumeNumber,
Pages = 0,
Chapters = chapters ?? new List<Chapter>()
};
}
public static Chapter CreateChapter(string range, bool isSpecial, List<MangaFile> files = null)
{
return new Chapter()
{
IsSpecial = isSpecial,
Range = range,
Number = API.Parser.Parser.MinimumNumberFromRange(range) + string.Empty,
Files = files ?? new List<MangaFile>(),
Pages = 0,
};
}
public static MangaFile CreateMangaFile(string filename, MangaFormat format, int pages)
{
return new MangaFile()
{
FilePath = filename,
Format = format,
Pages = pages
};
}
}
}

View file

@ -0,0 +1,25 @@
using System.IO;
using API.Entities.Enums;
using API.Parser;
namespace API.Tests.Helpers
{
public static class ParserInfoFactory
{
public static ParserInfo CreateParsedInfo(string series, string volumes, string chapters, string filename, bool isSpecial)
{
return new ParserInfo()
{
Chapters = chapters,
Edition = "",
Format = MangaFormat.Archive,
FullFilePath = Path.Join(@"/manga/", filename),
Filename = filename,
IsSpecial = isSpecial,
Title = Path.GetFileNameWithoutExtension(filename),
Series = series,
Volumes = volumes
};
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.IO;
using API.Services;
namespace API.Tests.Helpers
{
/// <summary>
/// Given a -testcase.txt file, will generate a folder with fake archive or book files. These files are just renamed txt files.
/// <remarks>This currently is broken - you cannot create files from a unit test it seems</remarks>
/// </summary>
public static class TestCaseGenerator
{
public static string GenerateFiles(string directory, string fileToExpand)
{
//var files = Directory.GetFiles(directory, fileToExpand);
var file = new FileInfo(fileToExpand);
if (!file.Exists && file.Name.EndsWith("-testcase.txt")) return string.Empty;
var baseDirectory = TestCaseGenerator.CreateTestBase(fileToExpand, directory);
var filesToCreate = File.ReadLines(file.FullName);
foreach (var fileToCreate in filesToCreate)
{
// var folders = DirectoryService.GetFoldersTillRoot(directory, fileToCreate);
// foreach (var VARIABLE in COLLECTION)
// {
//
// }
File.Create(fileToCreate);
}
return baseDirectory;
}
/// <summary>
/// Creates and returns a new base directory for data creation for a given testcase
/// </summary>
/// <param name="file"></param>
/// <param name="rootDirectory"></param>
/// <returns></returns>
private static string CreateTestBase(string file, string rootDirectory)
{
var baseDir = file.Split("-testcase.txt")[0];
var newDirectory = Path.Join(rootDirectory, baseDir);
if (!Directory.Exists(newDirectory))
{
new DirectoryInfo(newDirectory).Create();
}
return newDirectory;
}
}
}

View file

@ -0,0 +1,15 @@
using API.Services;
using Xunit;
namespace API.Tests.Parser
{
public class BookParserTests
{
[Theory]
[InlineData("Gifting The Wonderful World With Blessings! - 3 Side Stories [yuNS][Unknown]", "Gifting The Wonderful World With Blessings!")]
public void ParseSeriesTest(string filename, string expected)
{
Assert.Equal(expected, API.Parser.Parser.ParseSeries(filename));
}
}
}

View file

@ -0,0 +1,69 @@
using Xunit;
namespace API.Tests.Parser
{
public class ComicParserTests
{
[Theory]
[InlineData("01 Spider-Man & Wolverine 01.cbr", "Spider-Man & Wolverine")]
[InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", "Asterix the Gladiator")]
[InlineData("The First Asterix Frieze (WebP by Doc MaKS)", "The First Asterix Frieze")]
[InlineData("Batman & Catwoman - Trail of the Gun 01", "Batman & Catwoman - Trail of the Gun")]
[InlineData("Batman & Daredevil - King of New York", "Batman & Daredevil - King of New York")]
[InlineData("Batman & Grendel (1996) 01 - Devil's Bones", "Batman & Grendel")]
[InlineData("Batman & Robin the Teen Wonder #0", "Batman & Robin the Teen Wonder")]
[InlineData("Batman & Wildcat (1 of 3)", "Batman & Wildcat")]
[InlineData("Batman And Superman World's Finest #01", "Batman And Superman World's Finest")]
[InlineData("Babe 01", "Babe")]
[InlineData("Scott Pilgrim 01 - Scott Pilgrim's Precious Little Life (2004)", "Scott Pilgrim")]
[InlineData("Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)", "Teen Titans")]
[InlineData("Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)", "Scott Pilgrim")]
[InlineData("Wolverine - Origins 003 (2006) (digital) (Minutemen-PhD)", "Wolverine - Origins")]
[InlineData("Invincible Vol 01 Family matters (2005) (Digital).cbr", "Invincible")]
public void ParseComicSeriesTest(string filename, string expected)
{
Assert.Equal(expected, API.Parser.Parser.ParseComicSeries(filename));
}
[Theory]
[InlineData("01 Spider-Man & Wolverine 01.cbr", "1")]
[InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", "4")]
[InlineData("The First Asterix Frieze (WebP by Doc MaKS)", "0")]
[InlineData("Batman & Catwoman - Trail of the Gun 01", "1")]
[InlineData("Batman & Daredevil - King of New York", "0")]
[InlineData("Batman & Grendel (1996) 01 - Devil's Bones", "1")]
[InlineData("Batman & Robin the Teen Wonder #0", "0")]
[InlineData("Batman & Wildcat (1 of 3)", "0")]
[InlineData("Batman And Superman World's Finest #01", "1")]
[InlineData("Babe 01", "1")]
[InlineData("Scott Pilgrim 01 - Scott Pilgrim's Precious Little Life (2004)", "1")]
[InlineData("Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)", "1")]
[InlineData("Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)", "2")]
[InlineData("Superman v1 024 (09-10 1943)", "1")]
public void ParseComicVolumeTest(string filename, string expected)
{
Assert.Equal(expected, API.Parser.Parser.ParseComicVolume(filename));
}
[Theory]
[InlineData("01 Spider-Man & Wolverine 01.cbr", "0")]
[InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", "0")]
[InlineData("The First Asterix Frieze (WebP by Doc MaKS)", "0")]
[InlineData("Batman & Catwoman - Trail of the Gun 01", "0")]
[InlineData("Batman & Daredevil - King of New York", "0")]
[InlineData("Batman & Grendel (1996) 01 - Devil's Bones", "1")]
[InlineData("Batman & Robin the Teen Wonder #0", "0")]
[InlineData("Batman & Wildcat (1 of 3)", "1")]
[InlineData("Batman & Wildcat (2 of 3)", "2")]
[InlineData("Batman And Superman World's Finest #01", "0")]
[InlineData("Babe 01", "0")]
[InlineData("Scott Pilgrim 01 - Scott Pilgrim's Precious Little Life (2004)", "1")]
[InlineData("Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)", "1")]
[InlineData("Superman v1 024 (09-10 1943)", "24")]
[InlineData("Invincible 070.5 - Invincible Returns 1 (2010) (digital) (Minutemen-InnerDemons).cbr", "70.5")]
public void ParseComicChapterTest(string filename, string expected)
{
Assert.Equal(expected, API.Parser.Parser.ParseComicChapter(filename));
}
}
}

View file

@ -1,18 +1,16 @@
using System.Collections.Generic;
using System.Collections.Generic;
using API.Entities.Enums;
using API.Parser;
using Xunit;
using Xunit.Abstractions;
using static API.Parser.Parser;
namespace API.Tests
namespace API.Tests.Parser
{
public class ParserTests
public class MangaParserTests
{
private readonly ITestOutputHelper _testOutputHelper;
public ParserTests(ITestOutputHelper testOutputHelper)
public MangaParserTests(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
@ -61,9 +59,10 @@ namespace API.Tests
[InlineData("Gantz.V26.cbz", "26")]
[InlineData("NEEDLESS_Vol.4_-Simeon_6_v2[SugoiSugoi].rar", "4")]
[InlineData("[Hidoi]_Amaenaideyo_MS_vol01_chp02.rar", "1")]
[InlineData("NEEDLESS_Vol.4_-_Simeon_6_v2_[SugoiSugoi].rar", "4")]
public void ParseVolumeTest(string filename, string expected)
{
Assert.Equal(expected, ParseVolume(filename));
Assert.Equal(expected, API.Parser.Parser.ParseVolume(filename));
}
[Theory]
@ -132,9 +131,10 @@ namespace API.Tests
[InlineData("Umineko no Naku Koro ni - Episode 1 - Legend of the Golden Witch #1", "Umineko no Naku Koro ni")]
[InlineData("Kimetsu no Yaiba - Digital Colored Comics c162 Three Victorious Stars.cbz", "Kimetsu no Yaiba - Digital Colored Comics")]
[InlineData("[Hidoi]_Amaenaideyo_MS_vol01_chp02.rar", "Amaenaideyo MS")]
[InlineData("NEEDLESS_Vol.4_-_Simeon_6_v2_[SugoiSugoi].rar", "NEEDLESS")]
public void ParseSeriesTest(string filename, string expected)
{
Assert.Equal(expected, ParseSeries(filename));
Assert.Equal(expected, API.Parser.Parser.ParseSeries(filename));
}
[Theory]
@ -193,51 +193,9 @@ namespace API.Tests
[InlineData("[Hidoi]_Amaenaideyo_MS_vol01_chp02.rar", "2")]
public void ParseChaptersTest(string filename, string expected)
{
Assert.Equal(expected, ParseChapter(filename));
}
[Theory]
[InlineData("0001", "1")]
[InlineData("1", "1")]
[InlineData("0013", "13")]
public void RemoveLeadingZeroesTest(string input, string expected)
{
Assert.Equal(expected, RemoveLeadingZeroes(input));
Assert.Equal(expected, API.Parser.Parser.ParseChapter(filename));
}
[Theory]
[InlineData("1", "001")]
[InlineData("10", "010")]
[InlineData("100", "100")]
[InlineData("4-8", "004-008")]
public void PadZerosTest(string input, string expected)
{
Assert.Equal(expected, PadZeros(input));
}
[Theory]
[InlineData("Hello_I_am_here", "Hello I am here")]
[InlineData("Hello_I_am_here ", "Hello I am here")]
[InlineData("[ReleaseGroup] The Title", "The Title")]
[InlineData("[ReleaseGroup]_The_Title", "The Title")]
[InlineData("[Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1", "Kasumi Otoko no Ko v1.1")]
public void CleanTitleTest(string input, string expected)
{
Assert.Equal(expected, CleanTitle(input));
}
[Theory]
[InlineData("test.cbz", true)]
[InlineData("test.cbr", true)]
[InlineData("test.zip", true)]
[InlineData("test.rar", true)]
[InlineData("test.rar.!qb", false)]
[InlineData("[shf-ma-khs-aqs]negi_pa_vol15007.jpg", false)]
public void IsArchiveTest(string input, bool expected)
{
Assert.Equal(expected, IsArchive(input));
}
[Theory]
[InlineData("Tenjou Tenge Omnibus", "Omnibus")]
@ -250,7 +208,7 @@ namespace API.Tests
[InlineData("AKIRA - c003 (v01) [Full Color] [Darkhorse].cbz", "Full Color")]
public void ParseEditionTest(string input, string expected)
{
Assert.Equal(expected, ParseEdition(input));
Assert.Equal(expected, API.Parser.Parser.ParseEdition(input));
}
[Theory]
[InlineData("Beelzebub Special OneShot - Minna no Kochikame x Beelzebub (2016) [Mangastream].cbz", true)]
@ -260,151 +218,26 @@ namespace API.Tests
[InlineData("Darker than Black Shikkoku no Hana Fanbook Extra [Simple Scans].zip", true)]
[InlineData("Corpse Party -The Anthology- Sachikos game of love Hysteric Birthday 2U Extra Chapter", true)]
[InlineData("Ani-Hina Art Collection.cbz", true)]
[InlineData("Gifting The Wonderful World With Blessings! - 3 Side Stories [yuNS][Unknown]", true)]
public void ParseMangaSpecialTest(string input, bool expected)
{
Assert.Equal(expected, ParseMangaSpecial(input) != "");
Assert.Equal(expected, !string.IsNullOrEmpty(API.Parser.Parser.ParseMangaSpecial(input)));
}
[Theory]
[InlineData("12-14", 12)]
[InlineData("24", 24)]
[InlineData("18-04", 4)]
[InlineData("18-04.5", 4.5)]
[InlineData("40", 40)]
public void MinimumNumberFromRangeTest(string input, float expected)
{
Assert.Equal(expected, MinimumNumberFromRange(input));
}
[Theory]
[InlineData("Darker Than Black", "darkerthanblack")]
[InlineData("Darker Than Black - Something", "darkerthanblacksomething")]
[InlineData("Darker Than_Black", "darkerthanblack")]
[InlineData("", "")]
public void NormalizeTest(string input, string expected)
{
Assert.Equal(expected, Normalize(input));
}
[Theory]
[InlineData("01 Spider-Man & Wolverine 01.cbr", "Spider-Man & Wolverine")]
[InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", "Asterix the Gladiator")]
[InlineData("The First Asterix Frieze (WebP by Doc MaKS)", "The First Asterix Frieze")]
[InlineData("Batman & Catwoman - Trail of the Gun 01", "Batman & Catwoman - Trail of the Gun")]
[InlineData("Batman & Daredevil - King of New York", "Batman & Daredevil - King of New York")]
[InlineData("Batman & Grendel (1996) 01 - Devil's Bones", "Batman & Grendel")]
[InlineData("Batman & Robin the Teen Wonder #0", "Batman & Robin the Teen Wonder")]
[InlineData("Batman & Wildcat (1 of 3)", "Batman & Wildcat")]
[InlineData("Batman And Superman World's Finest #01", "Batman And Superman World's Finest")]
[InlineData("Babe 01", "Babe")]
[InlineData("Scott Pilgrim 01 - Scott Pilgrim's Precious Little Life (2004)", "Scott Pilgrim")]
[InlineData("Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)", "Teen Titans")]
[InlineData("Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)", "Scott Pilgrim")]
[InlineData("Wolverine - Origins 003 (2006) (digital) (Minutemen-PhD)", "Wolverine - Origins")]
[InlineData("Invincible Vol 01 Family matters (2005) (Digital).cbr", "Invincible")]
public void ParseComicSeriesTest(string filename, string expected)
{
Assert.Equal(expected, ParseComicSeries(filename));
}
[Theory]
[InlineData("01 Spider-Man & Wolverine 01.cbr", "1")]
[InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", "4")]
[InlineData("The First Asterix Frieze (WebP by Doc MaKS)", "0")]
[InlineData("Batman & Catwoman - Trail of the Gun 01", "1")]
[InlineData("Batman & Daredevil - King of New York", "0")]
[InlineData("Batman & Grendel (1996) 01 - Devil's Bones", "1")]
[InlineData("Batman & Robin the Teen Wonder #0", "0")]
[InlineData("Batman & Wildcat (1 of 3)", "0")]
[InlineData("Batman And Superman World's Finest #01", "1")]
[InlineData("Babe 01", "1")]
[InlineData("Scott Pilgrim 01 - Scott Pilgrim's Precious Little Life (2004)", "1")]
[InlineData("Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)", "1")]
[InlineData("Scott Pilgrim 02 - Scott Pilgrim vs. The World (2005)", "2")]
[InlineData("Superman v1 024 (09-10 1943)", "1")]
public void ParseComicVolumeTest(string filename, string expected)
{
Assert.Equal(expected, ParseComicVolume(filename));
}
[Theory]
[InlineData("01 Spider-Man & Wolverine 01.cbr", "0")]
[InlineData("04 - Asterix the Gladiator (1964) (Digital-Empire) (WebP by Doc MaKS)", "0")]
[InlineData("The First Asterix Frieze (WebP by Doc MaKS)", "0")]
[InlineData("Batman & Catwoman - Trail of the Gun 01", "0")]
[InlineData("Batman & Daredevil - King of New York", "0")]
[InlineData("Batman & Grendel (1996) 01 - Devil's Bones", "0")]
[InlineData("Batman & Robin the Teen Wonder #0", "0")]
[InlineData("Batman & Wildcat (1 of 3)", "1")]
[InlineData("Batman & Wildcat (2 of 3)", "2")]
[InlineData("Batman And Superman World's Finest #01", "0")]
[InlineData("Babe 01", "0")]
[InlineData("Scott Pilgrim 01 - Scott Pilgrim's Precious Little Life (2004)", "0")]
[InlineData("Teen Titans v1 001 (1966-02) (digital) (OkC.O.M.P.U.T.O.-Novus)", "1")]
[InlineData("Superman v1 024 (09-10 1943)", "24")]
public void ParseComicChapterTest(string filename, string expected)
{
Assert.Equal(expected, ParseComicChapter(filename));
}
[Theory]
[InlineData("test.jpg", true)]
[InlineData("test.jpeg", true)]
[InlineData("test.png", true)]
[InlineData(".test.jpg", false)]
[InlineData("!test.jpg", false)]
public void IsImageTest(string filename, bool expected)
{
Assert.Equal(expected, IsImage(filename));
}
[Theory]
[InlineData("C:/", "C:/Love Hina/Love Hina - Special.cbz", "Love Hina")]
[InlineData("C:/", "C:/Love Hina/Specials/Ani-Hina Art Collection.cbz", "Love Hina")]
[InlineData("C:/", "C:/Mujaki no Rakuen Something/Mujaki no Rakuen Vol12 ch76.cbz", "Mujaki no Rakuen")]
public void FallbackTest(string rootDir, string inputPath, string expectedSeries)
{
var actual = Parse(inputPath, rootDir);
if (actual == null)
{
Assert.NotNull(actual);
return;
}
Assert.Equal(expectedSeries, actual.Series);
}
[Theory]
[InlineData("Love Hina - Special.jpg", false)]
[InlineData("folder.jpg", true)]
[InlineData("DearS_v01_cover.jpg", true)]
[InlineData("DearS_v01_covers.jpg", false)]
[InlineData("!cover.jpg", true)]
[InlineData("cover.jpg", true)]
[InlineData("cover.png", true)]
[InlineData("ch1/cover.png", true)]
public void IsCoverImageTest(string inputPath, bool expected)
{
Assert.Equal(expected, IsCoverImage(inputPath));
}
[Theory]
[InlineData("__MACOSX/Love Hina - Special.jpg", true)]
[InlineData("TEST/Love Hina - Special.jpg", false)]
[InlineData("__macosx/Love Hina/", false)]
[InlineData("MACOSX/Love Hina/", false)]
public void HasBlacklistedFolderInPathTest(string inputPath, bool expected)
{
Assert.Equal(expected, HasBlacklistedFolderInPath(inputPath));
}
[Theory]
[InlineData("image.png", MangaFormat.Image)]
[InlineData("image.cbz", MangaFormat.Archive)]
[InlineData("image.txt", MangaFormat.Unknown)]
public void ParseFormatTest(string inputFile, MangaFormat expected)
{
Assert.Equal(expected, ParseFormat(inputFile));
Assert.Equal(expected, API.Parser.Parser.ParseFormat(inputFile));
}
[Theory]
[InlineData("Gifting The Wonderful World With Blessings! - 3 Side Stories [yuNS][Unknown].epub", "Side Stories")]
public void ParseSpecialTest(string inputFile, string expected)
{
Assert.Equal(expected, API.Parser.Parser.ParseMangaSpecial(inputFile));
}
[Fact]
@ -496,7 +329,7 @@ namespace API.Tests
foreach (var file in expected.Keys)
{
var expectedInfo = expected[file];
var actual = Parse(file, rootPath);
var actual = API.Parser.Parser.Parse(file, rootPath);
if (expectedInfo == null)
{
Assert.Null(actual);

View file

@ -0,0 +1,110 @@
using API.Entities.Enums;
using API.Parser;
using Xunit;
namespace API.Tests.Parser
{
public class ParserInfoTests
{
[Fact]
public void MergeFromTest()
{
var p1 = new ParserInfo()
{
Chapters = "0",
Edition = "",
Format = MangaFormat.Archive,
FullFilePath = "/manga/darker than black.cbz",
IsSpecial = false,
Series = "darker than black",
Title = "darker than black",
Volumes = "0"
};
var p2 = new ParserInfo()
{
Chapters = "1",
Edition = "",
Format = MangaFormat.Archive,
FullFilePath = "/manga/darker than black.cbz",
IsSpecial = false,
Series = "darker than black",
Title = "Darker Than Black",
Volumes = "0"
};
var expected = new ParserInfo()
{
Chapters = "1",
Edition = "",
Format = MangaFormat.Archive,
FullFilePath = "/manga/darker than black.cbz",
IsSpecial = false,
Series = "darker than black",
Title = "darker than black",
Volumes = "0"
};
p1.Merge(p2);
AssertSame(expected, p1);
}
[Fact]
public void MergeFromTest2()
{
var p1 = new ParserInfo()
{
Chapters = "1",
Edition = "",
Format = MangaFormat.Archive,
FullFilePath = "/manga/darker than black.cbz",
IsSpecial = true,
Series = "darker than black",
Title = "darker than black",
Volumes = "0"
};
var p2 = new ParserInfo()
{
Chapters = "0",
Edition = "",
Format = MangaFormat.Archive,
FullFilePath = "/manga/darker than black.cbz",
IsSpecial = false,
Series = "darker than black",
Title = "Darker Than Black",
Volumes = "1"
};
var expected = new ParserInfo()
{
Chapters = "1",
Edition = "",
Format = MangaFormat.Archive,
FullFilePath = "/manga/darker than black.cbz",
IsSpecial = true,
Series = "darker than black",
Title = "darker than black",
Volumes = "1"
};
p1.Merge(p2);
AssertSame(expected, p1);
}
private void AssertSame(ParserInfo expected, ParserInfo actual)
{
Assert.Equal(expected.Chapters, actual.Chapters);
Assert.Equal(expected.Volumes, actual.Volumes);
Assert.Equal(expected.Edition, actual.Edition);
Assert.Equal(expected.Filename, actual.Filename);
Assert.Equal(expected.Format, actual.Format);
Assert.Equal(expected.Series, actual.Series);
Assert.Equal(expected.IsSpecial, actual.IsSpecial);
Assert.Equal(expected.FullFilePath, actual.FullFilePath);
}
}
}

View file

@ -0,0 +1,192 @@
using Xunit;
using static API.Parser.Parser;
namespace API.Tests.Parser
{
public class ParserTests
{
[Theory]
[InlineData("0001", "1")]
[InlineData("1", "1")]
[InlineData("0013", "13")]
public void RemoveLeadingZeroesTest(string input, string expected)
{
Assert.Equal(expected, RemoveLeadingZeroes(input));
}
[Theory]
[InlineData("1", "001")]
[InlineData("10", "010")]
[InlineData("100", "100")]
public void PadZerosTest(string input, string expected)
{
Assert.Equal(expected, PadZeros(input));
}
[Theory]
[InlineData("Hello_I_am_here", "Hello I am here")]
[InlineData("Hello_I_am_here ", "Hello I am here")]
[InlineData("[ReleaseGroup] The Title", "The Title")]
[InlineData("[ReleaseGroup]_The_Title", "The Title")]
[InlineData("[Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1", "Kasumi Otoko no Ko v1.1")]
public void CleanTitleTest(string input, string expected)
{
Assert.Equal(expected, CleanTitle(input));
}
// [Theory]
// //[InlineData("@font-face{font-family:\"PaytoneOne\";src:url(\"..\\/Fonts\\/PaytoneOne.ttf\")}", "@font-face{font-family:\"PaytoneOne\";src:url(\"PaytoneOne.ttf\")}")]
// [InlineData("@font-face{font-family:\"PaytoneOne\";src:url(\"..\\/Fonts\\/PaytoneOne.ttf\")}", "..\\/Fonts\\/PaytoneOne.ttf")]
// //[InlineData("@font-face{font-family:'PaytoneOne';src:url('..\\/Fonts\\/PaytoneOne.ttf')}", "@font-face{font-family:'PaytoneOne';src:url('PaytoneOne.ttf')}")]
// //[InlineData("@font-face{\r\nfont-family:'PaytoneOne';\r\nsrc:url('..\\/Fonts\\/PaytoneOne.ttf')\r\n}", "@font-face{font-family:'PaytoneOne';src:url('PaytoneOne.ttf')}")]
// public void ReplaceStyleUrlTest(string input, string expected)
// {
// var replacementStr = "PaytoneOne.ttf";
// // TODO: Use Match to validate since replace is weird
// //Assert.Equal(expected, FontSrcUrlRegex.Replace(input, "$1" + replacementStr + "$2" + "$3"));
// var match = FontSrcUrlRegex.Match(input);
// Assert.Equal(!string.IsNullOrEmpty(expected), FontSrcUrlRegex.Match(input).Success);
// }
[Theory]
[InlineData("test.cbz", true)]
[InlineData("test.cbr", true)]
[InlineData("test.zip", true)]
[InlineData("test.rar", true)]
[InlineData("test.rar.!qb", false)]
[InlineData("[shf-ma-khs-aqs]negi_pa_vol15007.jpg", false)]
public void IsArchiveTest(string input, bool expected)
{
Assert.Equal(expected, IsArchive(input));
}
[Theory]
[InlineData("test.epub", true)]
[InlineData("test.pdf", false)]
[InlineData("test.mobi", false)]
[InlineData("test.djvu", false)]
[InlineData("test.zip", false)]
[InlineData("test.rar", false)]
[InlineData("test.epub.!qb", false)]
[InlineData("[shf-ma-khs-aqs]negi_pa_vol15007.ebub", false)]
public void IsBookTest(string input, bool expected)
{
Assert.Equal(expected, IsBook(input));
}
[Theory]
[InlineData("test.epub", true)]
[InlineData("test.EPUB", true)]
[InlineData("test.mobi", false)]
[InlineData("test.epub.!qb", false)]
[InlineData("[shf-ma-khs-aqs]negi_pa_vol15007.ebub", false)]
public void IsEpubTest(string input, bool expected)
{
Assert.Equal(expected, IsEpub(input));
}
// [Theory]
// [InlineData("Tenjou Tenge Omnibus", "Omnibus")]
// [InlineData("Tenjou Tenge {Full Contact Edition}", "Full Contact Edition")]
// [InlineData("Tenjo Tenge {Full Contact Edition} v01 (2011) (Digital) (ASTC).cbz", "Full Contact Edition")]
// [InlineData("Wotakoi - Love is Hard for Otaku Omnibus v01 (2018) (Digital) (danke-Empire)", "Omnibus")]
// [InlineData("To Love Ru v01 Uncensored (Ch.001-007)", "Uncensored")]
// [InlineData("Chobits Omnibus Edition v01 [Dark Horse]", "Omnibus Edition")]
// [InlineData("[dmntsf.net] One Piece - Digital Colored Comics Vol. 20 Ch. 177 - 30 Million vs 81 Million.cbz", "Digital Colored Comics")]
// [InlineData("AKIRA - c003 (v01) [Full Color] [Darkhorse].cbz", "Full Color")]
// public void ParseEditionTest(string input, string expected)
// {
// Assert.Equal(expected, ParseEdition(input));
// }
// [Theory]
// [InlineData("Beelzebub Special OneShot - Minna no Kochikame x Beelzebub (2016) [Mangastream].cbz", true)]
// [InlineData("Beelzebub_Omake_June_2012_RHS", true)]
// [InlineData("Beelzebub_Side_Story_02_RHS.zip", false)]
// [InlineData("Darker than Black Shikkoku no Hana Special [Simple Scans].zip", true)]
// [InlineData("Darker than Black Shikkoku no Hana Fanbook Extra [Simple Scans].zip", true)]
// [InlineData("Corpse Party -The Anthology- Sachikos game of love Hysteric Birthday 2U Extra Chapter", true)]
// [InlineData("Ani-Hina Art Collection.cbz", true)]
// public void ParseMangaSpecialTest(string input, bool expected)
// {
// Assert.Equal(expected, ParseMangaSpecial(input) != "");
// }
[Theory]
[InlineData("12-14", 12)]
[InlineData("24", 24)]
[InlineData("18-04", 4)]
[InlineData("18-04.5", 4.5)]
[InlineData("40", 40)]
public void MinimumNumberFromRangeTest(string input, float expected)
{
Assert.Equal(expected, MinimumNumberFromRange(input));
}
[Theory]
[InlineData("Darker Than Black", "darkerthanblack")]
[InlineData("Darker Than Black - Something", "darkerthanblacksomething")]
[InlineData("Darker Than_Black", "darkerthanblack")]
[InlineData("", "")]
public void NormalizeTest(string input, string expected)
{
Assert.Equal(expected, Normalize(input));
}
[Theory]
[InlineData("test.jpg", true)]
[InlineData("test.jpeg", true)]
[InlineData("test.png", true)]
[InlineData(".test.jpg", false)]
[InlineData("!test.jpg", false)]
public void IsImageTest(string filename, bool expected)
{
Assert.Equal(expected, IsImage(filename));
}
[Theory]
[InlineData("C:/", "C:/Love Hina/Love Hina - Special.cbz", "Love Hina")]
[InlineData("C:/", "C:/Love Hina/Specials/Ani-Hina Art Collection.cbz", "Love Hina")]
[InlineData("C:/", "C:/Mujaki no Rakuen Something/Mujaki no Rakuen Vol12 ch76.cbz", "Mujaki no Rakuen")]
public void FallbackTest(string rootDir, string inputPath, string expectedSeries)
{
var actual = Parse(inputPath, rootDir);
if (actual == null)
{
Assert.NotNull(actual);
return;
}
Assert.Equal(expectedSeries, actual.Series);
}
[Theory]
[InlineData("Love Hina - Special.jpg", false)]
[InlineData("folder.jpg", true)]
[InlineData("DearS_v01_cover.jpg", true)]
[InlineData("DearS_v01_covers.jpg", false)]
[InlineData("!cover.jpg", true)]
[InlineData("cover.jpg", true)]
[InlineData("cover.png", true)]
[InlineData("ch1/cover.png", true)]
public void IsCoverImageTest(string inputPath, bool expected)
{
Assert.Equal(expected, IsCoverImage(inputPath));
}
[Theory]
[InlineData("__MACOSX/Love Hina - Special.jpg", true)]
[InlineData("TEST/Love Hina - Special.jpg", false)]
[InlineData("__macosx/Love Hina/", false)]
[InlineData("MACOSX/Love Hina/", false)]
public void HasBlacklistedFolderInPathTest(string inputPath, bool expected)
{
Assert.Equal(expected, HasBlacklistedFolderInPath(inputPath));
}
}
}

View file

@ -1,9 +1,7 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using API.Archive;
using API.Interfaces.Services;
using API.Services;
using Microsoft.Extensions.Logging;
using NSubstitute;

View file

@ -1,47 +0,0 @@
using API.Interfaces;
using API.Services;
using API.Services.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using NSubstitute;
namespace API.Tests.Services
{
public class BackupServiceTests
{
private readonly DirectoryService _directoryService;
private readonly BackupService _backupService;
private readonly IUnitOfWork _unitOfWork = Substitute.For<IUnitOfWork>();
private readonly ILogger<DirectoryService> _directoryLogger = Substitute.For<ILogger<DirectoryService>>();
private readonly ILogger<BackupService> _logger = Substitute.For<ILogger<BackupService>>();
private readonly IConfiguration _config;
// public BackupServiceTests()
// {
// var inMemorySettings = new Dictionary<string, string> {
// {"Logging:File:MaxRollingFiles", "0"},
// {"Logging:File:Path", "file.log"},
// };
//
// _config = new ConfigurationBuilder()
// .AddInMemoryCollection(inMemorySettings)
// .Build();
//
// //_config.GetMaxRollingFiles().Returns(0);
// //_config.GetLoggingFileName().Returns("file.log");
// //var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/BackupService/");
// //Directory.GetCurrentDirectory().Returns(testDirectory);
//
// _directoryService = new DirectoryService(_directoryLogger);
// _backupService = new BackupService(_unitOfWork, _logger, _directoryService, _config);
// }
//
// [Fact]
// public void Test()
// {
// _backupService.BackupDatabase();
// }
}
}

View file

@ -0,0 +1,32 @@
using System.IO;
using API.Entities.Interfaces;
using API.Interfaces;
using API.Services;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
namespace API.Tests.Services
{
public class BookServiceTests
{
private readonly IBookService _bookService;
private readonly ILogger<BookService> _logger = Substitute.For<ILogger<BookService>>();
public BookServiceTests()
{
_bookService = new BookService(_logger);
}
[Theory]
[InlineData("The Golden Harpoon; Or, Lost Among the Floes A Story of the Whaling Grounds.epub", 16)]
[InlineData("Non-existent file.epub", 0)]
[InlineData("Non an ebub.pdf", 0)]
public void GetNumberOfPagesTest(string filePath, int expectedPages)
{
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/BookService/EPUB");
Assert.Equal(expectedPages, _bookService.GetNumberOfPages(Path.Join(testDirectory, filePath)));
}
}
}

View file

@ -41,7 +41,7 @@
// //[InlineData("", 0, "")]
// public void GetCachedPagePathTest_Should()
// {
// // TODO: Figure out how to test this
//
// // string archivePath = "flat file.zip";
// // int pageNum = 0;
// // string expected = "cache/1/pexels-photo-6551949.jpg";

View file

@ -1,6 +1,8 @@
using System.IO;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using API.Services;
using API.Tests.Helpers;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
@ -18,6 +20,18 @@ namespace API.Tests.Services
_directoryService = new DirectoryService(_logger);
}
[Theory]
[InlineData("Manga-testcase.txt", 28)]
public void GetFilesTest(string file, int expectedFileCount)
{
var testDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ScannerService/Manga");
var files = new List<string>();
var fileCount = DirectoryService.TraverseTreeParallelForEach(testDirectory, s => files.Add(s),
API.Parser.Parser.ArchiveFileExtensions, _logger);
Assert.Equal(expectedFileCount, fileCount);
}
[Fact]
public void GetFiles_WithCustomRegex_ShouldPass_Test()
{

View file

@ -1,69 +1,102 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.Entities;
using API.Extensions;
using API.Interfaces;
using API.Interfaces.Services;
using API.Parser;
using API.Services;
using API.Services.Tasks;
using API.Tests.Helpers;
using AutoMapper;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.Logging;
using NSubstitute;
using NSubstitute.Extensions;
using Xunit;
using Xunit.Abstractions;
namespace API.Tests.Services
{
public class ScannerServiceTests
public class ScannerServiceTests : IDisposable
{
private readonly ITestOutputHelper _testOutputHelper;
private readonly ScannerService _scannerService;
private readonly ILogger<ScannerService> _logger = Substitute.For<ILogger<ScannerService>>();
private readonly IUnitOfWork _unitOfWork = Substitute.For<IUnitOfWork>();
private readonly IUnitOfWork _unitOfWork;
private readonly IArchiveService _archiveService = Substitute.For<IArchiveService>();
private readonly IBookService _bookService = Substitute.For<IBookService>();
private readonly IMetadataService _metadataService;
private readonly ILogger<MetadataService> _metadataLogger = Substitute.For<ILogger<MetadataService>>();
private Library _libraryMock;
private readonly DbConnection _connection;
private readonly DataContext _context;
public ScannerServiceTests(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
_scannerService = new ScannerService(_unitOfWork, _logger, _archiveService, _metadataService);
_metadataService= Substitute.For<MetadataService>(_unitOfWork, _metadataLogger, _archiveService);
// _libraryMock = new Library()
// {
// Id = 1,
// Name = "Manga",
// Folders = new List<FolderPath>()
// {
// new FolderPath()
// {
// Id = 1,
// LastScanned = DateTime.Now,
// LibraryId = 1,
// Path = "E:/Manga"
// }
// },
// LastModified = DateTime.Now,
// Series = new List<Series>()
// {
// new Series()
// {
// Id = 0,
// Name = "Darker Than Black"
// }
// }
// };
var contextOptions = new DbContextOptionsBuilder()
.UseSqlite(CreateInMemoryDatabase())
.Options;
_connection = RelationalOptionsExtension.Extract(contextOptions).Connection;
_context = new DataContext(contextOptions);
Task.Run(SeedDb).GetAwaiter().GetResult();
//BackgroundJob.Enqueue is what I need to mock or something (it's static...)
// ICacheService cacheService, ILogger<TaskScheduler> logger, IScannerService scannerService,
// IUnitOfWork unitOfWork, IMetadataService metadataService, IBackupService backupService, ICleanupService cleanupService,
// IBackgroundJobClient jobClient
//var taskScheduler = new TaskScheduler(Substitute.For<ICacheService>(), Substitute.For<ILogger<TaskScheduler>>(), Substitute.For<)
// Substitute.For<UserManager<AppUser>>() - Not needed because only for UserService
_unitOfWork = new UnitOfWork(_context, Substitute.For<IMapper>(), null,
Substitute.For<ILogger<UnitOfWork>>());
_testOutputHelper = testOutputHelper;
_metadataService= Substitute.For<MetadataService>(_unitOfWork, _metadataLogger, _archiveService, _bookService);
_scannerService = new ScannerService(_unitOfWork, _logger, _archiveService, _metadataService, _bookService);
}
private async Task<bool> SeedDb()
{
await _context.Database.MigrateAsync();
await Seed.SeedSettings(_context);
_context.Library.Add(new Library()
{
Name = "Manga",
Folders = new List<FolderPath>()
{
new FolderPath()
{
Path = Path.Join(Directory.GetCurrentDirectory(), "../../../Services/Test Data/ScannerService/Manga")
}
}
});
return await _context.SaveChangesAsync() > 0;
}
// [Fact]
// public void Test()
// {
// _scannerService.ScanLibrary(1, false);
//
// var series = _unitOfWork.LibraryRepository.GetLibraryForIdAsync(1).Result.Series;
// }
[Fact]
public void FindSeriesNotOnDisk_Should_RemoveNothing_Test()
{
var scannerService = new ScannerService(_unitOfWork, _logger, _archiveService, _metadataService);
var infos = new Dictionary<string, List<ParserInfo>>();
AddToParsedInfo(infos, new ParserInfo() {Series = "Darker than Black"});
@ -76,38 +109,36 @@ namespace API.Tests.Services
Name = "Cage of Eden",
LocalizedName = "Cage of Eden",
OriginalName = "Cage of Eden",
NormalizedName = Parser.Parser.Normalize("Cage of Eden")
NormalizedName = API.Parser.Parser.Normalize("Cage of Eden")
});
existingSeries.Add(new Series()
{
Name = "Darker Than Black",
LocalizedName = "Darker Than Black",
OriginalName = "Darker Than Black",
NormalizedName = Parser.Parser.Normalize("Darker Than Black")
NormalizedName = API.Parser.Parser.Normalize("Darker Than Black")
});
var expectedSeries = new List<Series>();
Assert.Empty(scannerService.FindSeriesNotOnDisk(existingSeries, infos));
Assert.Empty(_scannerService.FindSeriesNotOnDisk(existingSeries, infos));
}
[Theory]
[InlineData(new [] {"Darker than Black"}, "Darker than Black", "Darker than Black")]
[InlineData(new [] {"Darker than Black"}, "Darker Than Black", "Darker than Black")]
[InlineData(new [] {"Darker than Black"}, "Darker Than Black!", "Darker Than Black!")]
[InlineData(new [] {"Darker than Black"}, "Darker Than Black!", "Darker than Black")]
[InlineData(new [] {""}, "Runaway Jack", "Runaway Jack")]
public void MergeNameTest(string[] existingSeriesNames, string parsedInfoName, string expected)
{
var scannerService = new ScannerService(_unitOfWork, _logger, _archiveService, _metadataService);
var collectedSeries = new ConcurrentDictionary<string, List<ParserInfo>>();
foreach (var seriesName in existingSeriesNames)
{
AddToParsedInfo(collectedSeries, new ParserInfo() {Series = seriesName});
}
var actualName = scannerService.MergeName(collectedSeries, new ParserInfo()
var actualName = _scannerService.MergeName(collectedSeries, new ParserInfo()
{
Series = parsedInfoName
});
@ -115,6 +146,25 @@ namespace API.Tests.Services
Assert.Equal(expected, actualName);
}
[Fact]
public void RemoveMissingSeries_Should_RemoveSeries()
{
var existingSeries = new List<Series>()
{
EntityFactory.CreateSeries("Darker than Black Vol 1"),
EntityFactory.CreateSeries("Darker than Black"),
EntityFactory.CreateSeries("Beastars"),
};
var missingSeries = new List<Series>()
{
EntityFactory.CreateSeries("Darker than Black Vol 1"),
};
existingSeries = ScannerService.RemoveMissingSeries(existingSeries, missingSeries, out var removeCount).ToList();
Assert.DoesNotContain(missingSeries[0].Name, existingSeries.Select(s => s.Name));
Assert.Equal(missingSeries.Count, removeCount);
}
private void AddToParsedInfo(IDictionary<string, List<ParserInfo>> collectedSeries, ParserInfo info)
{
if (collectedSeries.GetType() == typeof(ConcurrentDictionary<,>))
@ -209,5 +259,16 @@ namespace API.Tests.Services
// _testOutputHelper.WriteLine(_libraryMock.ToString());
Assert.True(true);
}
private static DbConnection CreateInMemoryDatabase()
{
var connection = new SqliteConnection("Filename=:memory:");
connection.Open();
return connection;
}
public void Dispose() => _connection.Dispose();
}
}

View file

@ -0,0 +1,153 @@
\A Town Where You Live\A Town Where You Live Vol. 01.zip
\A Town Where You Live\A Town Where You Live Vol. 02.zip
\A Town Where You Live\A Town Where You Live Vol. 03.zip
\A Town Where You Live\A Town Where You Live Vol. 04.zip
\A Town Where You Live\A Town Where You Live Vol. 05.zip
\A Town Where You Live\A Town Where You Live Vol. 06.zip
\A Town Where You Live\A Town Where You Live Vol. 07.zip
\A Town Where You Live\A Town Where You Live Vol. 08.zip
\A Town Where You Live\A Town Where You Live Vol. 09.zip
\A Town Where You Live\A Town Where You Live Vol. 10.zip
\A Town Where You Live\A Town Where You Live Vol. 11.zip
\A Town Where You Live\A Town Where You Live Vol. 12.zip
\A Town Where You Live\A Town Where You Live Vol. 13.zip
\A Town Where You Live\A Town Where You Live Vol. 14.zip
\A Town Where You Live\A Town Where You Live Vol. 15.zip
\A Town Where You Live\A Town Where You Live Vol. 16.zip
\A Town Where You Live\A Town Where You Live Vol. 17.zip
\A Town Where You Live\A Town Where You Live Vol. 18.zip
\A Town Where You Live\A Town Where You Live Vol. 19.zip
\A Town Where You Live\A Town Where You Live Vol. 20.zip
\A Town Where You Live\A Town Where You Live Vol. 21.zip
\A Town Where You Live\A Town Where You Live Vol. 22.zip
\A Town Where You Live\A Town Where You Live Vol. 23.zip
\A Town Where You Live\A Town Where You Live Vol. 24.zip
\A Town Where You Live\A Town Where You Live Vol. 25.zip
\A Town Where You Live\A Town Where You Live Vol. 26.zip
\A Town Where You Live\A Town Where You Live Vol. 27.zip
\A Town Where You Live\A Town Where You Live - Post Volume 27\A Town Where You Live - Bonus Chapter.zip
\A Town Where You Live\A Town Where You Live - Post Volume 27\A Town Where You Live - Princess Lucia Collaboration.zip
\A Town Where You Live\A Town Where You Live - Post Volume 27\A Town Where You Live - Special Fantasy.zip
\A Town Where You Live\A Town Where You Live - Post Volume 27\A Town Where You Live - Special Youth's Acne.zip
\Accomplishments of the Duke's Daughter\Accomplishments of the Duke's Daughter v01 (2018) (Digital) (danke-Empire).cbz
\Accomplishments of the Duke's Daughter\Accomplishments of the Duke's Daughter v02 (2018) (Digital) (danke-Empire).cbz
\Accomplishments of the Duke's Daughter\Accomplishments of the Duke's Daughter v03 (2019) (Digital) (danke-Empire).cbz
\Accomplishments of the Duke's Daughter\Accomplishments of the Duke's Daughter v04 (2019) (Digital) (danke-Empire).cbz
\Accomplishments of the Duke's Daughter\Accomplishments of the Duke's Daughter v05 (2019) (Digital) (danke-Empire).cbz
\Aiki\Aiki V01.cbz
\Aiki\Aiki V02.cbz
\Aiki\Aiki V03.cbz
\Aiki\Aiki V04.cbz
\Aiki\Aiki V05.cbz
\Aiki\Aiki V06.cbz
\Aiki\Aiki V07.cbz
\Aiki\Aiki V08.cbz
\Aiki\Aiki V09.cbz
\Aiki\Aiki V10.cbz
\Aiki\Aiki V11.cbz
\Aiki\Aiki V12.cbz
\Aiki\Aiki V13.cbz
\Aiki\Aiki V14.cbz
\Ajin - Demi-Human\Ajin - Demi-Human 074 (2019) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 074.5 (2019) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 075 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 075.5 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 076 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 077 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 078 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 079 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 080 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 081 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 082 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 083 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 083.5 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 084 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 085 (2021) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 086 (2021) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v01 (2014) (Digital) (LostNerevarine-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v02 (2014) (Digital) (LostNerevarine-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v03 (2015) (Digital) (LostNerevarine-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v04 (2015) (Digital) (LostNerevarine-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v05 (2015) (Digital) (LostNerevarine-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v06 (2015) (Digital) (LostNerevarine-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v07 (2016) (Digital) (Hexer-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v08 (2016) (Digital) (Hexer-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v09 (2017) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v10 (2017) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v11 (2018) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v12 (2019) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v13 (2019) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v14 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v15 (2020) (Digital) (danke-Empire).cbz
\Akame ga KILL!\Akame ga KILL! v01 (2015) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v02 (2015) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v03 (2015) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v04 (2015) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v05 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v06 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v07 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v08 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v09 (2017) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v10 (2017) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v11 (2017) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v12 (2017) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v13 (2018) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v14 (2018) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v15 (2018) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v01 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v02 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v03 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v04 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v05 (2017) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v06 (2017) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v07 (2018) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v08 (2018) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v09 (2019) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v10 (2019) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v01 (2019) (F) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v02 (2019) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v03 (2019) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v04 (2020) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v05 (2020) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v06 (2020) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v07 (2020) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v08 (2020) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v09.cbz
\Beastars\BEASTARS v10.cbz
\Beastars\BEASTARS v11.cbz
\Beastars\BEASTARS v12.cbz
\Beastars\BEASTARS v13.cbz
\Beastars\BEASTARS v14.cbz
\Beastars\BEASTARS v15.cbz
\Beastars\BEASTARS v16.cbz
\Beastars\BEASTARS v17.cbz
\Beastars\BEASTARS v18.cbz
\Beastars\BEASTARS v19.cbz
\Beastars\BEASTARS v20.cbz
\Beastars\BEASTARS v21.cbz
\Black Bullet\Black Bullet - v4 c17 [batoto].zip
\Black Bullet\Black Bullet - v4 c17.5 [batoto].zip
\Black Bullet\Black Bullet - v4 c18 [batoto].zip
\Black Bullet\Black Bullet - v4 c18.5 [batoto].zip
\Black Bullet\Black Bullet - v4 c19 [batoto].zip
\Black Bullet\Black Bullet - v4 c19.5 [batoto].zip
\Black Bullet\Black Bullet - v4 c20 [batoto].zip
\Black Bullet\Black Bullet - v4 c20.5 [batoto].zip
\Black Bullet\Black Bullet v01 c01.rar
\Black Bullet\Black Bullet v01 c02.rar
\Black Bullet\Black Bullet v01 c03.rar
\Black Bullet\Black Bullet v01 c04.rar
\Black Bullet\Black Bullet v01 c05.rar
\Black Bullet\Black Bullet v01 c06.rar
\Black Bullet\Black Bullet v01 c07.rar
\Black Bullet\Black Bullet v01 c08.rar
\Black Bullet\Black Bullet v01 c09.5.rar
\Black Bullet\Black Bullet v01 c09.rar
\Black Bullet\Black Bullet v01 c10.rar
\Black Bullet\Black Bullet v01 c11.zip
\Black Bullet\Black Bullet v01 c12.5.rar
\Black Bullet\Black Bullet v01 c12.rar
\Black Bullet\Black Bullet v01 c13.rar
\Black Bullet\Black Bullet v01 c14.rar
\Black Bullet\Black Bullet v01 c15.rar
\Black Bullet\Black Bullet v01 c16.rar

View file

@ -0,0 +1,153 @@
\A Town Where You Live\A Town Where You Live Vol. 01.zip
\A Town Where You Live\A Town Where You Live Vol. 02.zip
\A Town Where You Live\A Town Where You Live Vol. 03.zip
\A Town Where You Live\A Town Where You Live Vol. 04.zip
\A Town Where You Live\A Town Where You Live Vol. 05.zip
\A Town Where You Live\A Town Where You Live Vol. 06.zip
\A Town Where You Live\A Town Where You Live Vol. 07.zip
\A Town Where You Live\A Town Where You Live Vol. 08.zip
\A Town Where You Live\A Town Where You Live Vol. 09.zip
\A Town Where You Live\A Town Where You Live Vol. 10.zip
\A Town Where You Live\A Town Where You Live Vol. 11.zip
\A Town Where You Live\A Town Where You Live Vol. 12.zip
\A Town Where You Live\A Town Where You Live Vol. 13.zip
\A Town Where You Live\A Town Where You Live Vol. 14.zip
\A Town Where You Live\A Town Where You Live Vol. 15.zip
\A Town Where You Live\A Town Where You Live Vol. 16.zip
\A Town Where You Live\A Town Where You Live Vol. 17.zip
\A Town Where You Live\A Town Where You Live Vol. 18.zip
\A Town Where You Live\A Town Where You Live Vol. 19.zip
\A Town Where You Live\A Town Where You Live Vol. 20.zip
\A Town Where You Live\A Town Where You Live Vol. 21.zip
\A Town Where You Live\A Town Where You Live Vol. 22.zip
\A Town Where You Live\A Town Where You Live Vol. 23.zip
\A Town Where You Live\A Town Where You Live Vol. 24.zip
\A Town Where You Live\A Town Where You Live Vol. 25.zip
\A Town Where You Live\A Town Where You Live Vol. 26.zip
\A Town Where You Live\A Town Where You Live Vol. 27.zip
\A Town Where You Live\A Town Where You Live - Post Volume 27\A Town Where You Live - Bonus Chapter.zip
\A Town Where You Live\A Town Where You Live - Post Volume 27\A Town Where You Live - Princess Lucia Collaboration.zip
\A Town Where You Live\A Town Where You Live - Post Volume 27\A Town Where You Live - Special Fantasy.zip
\A Town Where You Live\A Town Where You Live - Post Volume 27\A Town Where You Live - Special Youth's Acne.zip
\Accomplishments of the Duke's Daughter\Accomplishments of the Duke's Daughter v01 (2018) (Digital) (danke-Empire).cbz
\Accomplishments of the Duke's Daughter\Accomplishments of the Duke's Daughter v02 (2018) (Digital) (danke-Empire).cbz
\Accomplishments of the Duke's Daughter\Accomplishments of the Duke's Daughter v03 (2019) (Digital) (danke-Empire).cbz
\Accomplishments of the Duke's Daughter\Accomplishments of the Duke's Daughter v04 (2019) (Digital) (danke-Empire).cbz
\Accomplishments of the Duke's Daughter\Accomplishments of the Duke's Daughter v05 (2019) (Digital) (danke-Empire).cbz
\Aiki\Aiki V01.cbz
\Aiki\Aiki V02.cbz
\Aiki\Aiki V03.cbz
\Aiki\Aiki V04.cbz
\Aiki\Aiki V05.cbz
\Aiki\Aiki V06.cbz
\Aiki\Aiki V07.cbz
\Aiki\Aiki V08.cbz
\Aiki\Aiki V09.cbz
\Aiki\Aiki V10.cbz
\Aiki\Aiki V11.cbz
\Aiki\Aiki V12.cbz
\Aiki\Aiki V13.cbz
\Aiki\Aiki V14.cbz
\Ajin - Demi-Human\Ajin - Demi-Human 074 (2019) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 074.5 (2019) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 075 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 075.5 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 076 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 077 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 078 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 079 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 080 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 081 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 082 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 083 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 083.5 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 084 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 085 (2021) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human 086 (2021) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v01 (2014) (Digital) (LostNerevarine-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v02 (2014) (Digital) (LostNerevarine-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v03 (2015) (Digital) (LostNerevarine-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v04 (2015) (Digital) (LostNerevarine-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v05 (2015) (Digital) (LostNerevarine-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v06 (2015) (Digital) (LostNerevarine-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v07 (2016) (Digital) (Hexer-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v08 (2016) (Digital) (Hexer-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v09 (2017) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v10 (2017) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v11 (2018) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v12 (2019) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v13 (2019) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v14 (2020) (Digital) (danke-Empire).cbz
\Ajin - Demi-Human\Ajin - Demi-Human v15 (2020) (Digital) (danke-Empire).cbz
\Akame ga KILL!\Akame ga KILL! v01 (2015) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v02 (2015) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v03 (2015) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v04 (2015) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v05 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v06 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v07 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v08 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v09 (2017) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v10 (2017) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v11 (2017) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v12 (2017) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v13 (2018) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v14 (2018) (Digital) (LuCaZ).cbz
\Akame ga KILL!\Akame ga KILL! v15 (2018) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v01 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v02 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v03 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v04 (2016) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v05 (2017) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v06 (2017) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v07 (2018) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v08 (2018) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v09 (2019) (Digital) (LuCaZ).cbz
\Akame ga KILL! ZERO (2016-2019) (Digital) (LuCaZ)\Akame ga KILL! ZERO v10 (2019) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v01 (2019) (F) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v02 (2019) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v03 (2019) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v04 (2020) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v05 (2020) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v06 (2020) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v07 (2020) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v08 (2020) (Digital) (LuCaZ).cbz
\Beastars\BEASTARS v09.cbz
\Beastars\BEASTARS v10.cbz
\Beastars\BEASTARS v11.cbz
\Beastars\BEASTARS v12.cbz
\Beastars\BEASTARS v13.cbz
\Beastars\BEASTARS v14.cbz
\Beastars\BEASTARS v15.cbz
\Beastars\BEASTARS v16.cbz
\Beastars\BEASTARS v17.cbz
\Beastars\BEASTARS v18.cbz
\Beastars\BEASTARS v19.cbz
\Beastars\BEASTARS v20.cbz
\Beastars\BEASTARS v21.cbz
\Black Bullet\Black Bullet - v4 c17 [batoto].zip
\Black Bullet\Black Bullet - v4 c17.5 [batoto].zip
\Black Bullet\Black Bullet - v4 c18 [batoto].zip
\Black Bullet\Black Bullet - v4 c18.5 [batoto].zip
\Black Bullet\Black Bullet - v4 c19 [batoto].zip
\Black Bullet\Black Bullet - v4 c19.5 [batoto].zip
\Black Bullet\Black Bullet - v4 c20 [batoto].zip
\Black Bullet\Black Bullet - v4 c20.5 [batoto].zip
\Black Bullet\Black Bullet v01 c01.rar
\Black Bullet\Black Bullet v01 c02.rar
\Black Bullet\Black Bullet v01 c03.rar
\Black Bullet\Black Bullet v01 c04.rar
\Black Bullet\Black Bullet v01 c05.rar
\Black Bullet\Black Bullet v01 c06.rar
\Black Bullet\Black Bullet v01 c07.rar
\Black Bullet\Black Bullet v01 c08.rar
\Black Bullet\Black Bullet v01 c09.5.rar
\Black Bullet\Black Bullet v01 c09.rar
\Black Bullet\Black Bullet v01 c10.rar
\Black Bullet\Black Bullet v01 c11.zip
\Black Bullet\Black Bullet v01 c12.5.rar
\Black Bullet\Black Bullet v01 c12.rar
\Black Bullet\Black Bullet v01 c13.rar
\Black Bullet\Black Bullet v01 c14.rar
\Black Bullet\Black Bullet v01 c15.rar
\Black Bullet\Black Bullet v01 c16.rar

View file

@ -0,0 +1,80 @@
""" This script should be run on a directory which will generate a test case file
that can be loaded into the renametest.py"""
import os
from pathlib import Path
import shutil
verbose = False
def print_log(val):
if verbose:
print(val)
def create_test_base(file, root_dir):
""" Creates and returns a new base directory for data creation for a given testcase."""
base_dir = os.path.split(file.split('-testcase.txt')[0])[-1]
print_log('base_dir: {0}'.format(base_dir))
new_dir = os.path.join(root_dir, base_dir)
print_log('new dir: {0}'.format(new_dir))
p = Path(new_dir)
if not p.exists():
os.mkdir(new_dir)
return new_dir
def generate_data(file, root_dir):
''' Generates directories and fake files for testing against '''
base_dir = ''
if file.endswith('-testcase.txt'):
base_dir = create_test_base(file, root_dir)
files_to_create = []
with open(file, 'r') as in_file:
files_to_create = in_file.read().splitlines()
for filepath in files_to_create:
for part in os.path.split(filepath):
part_path = os.path.join(base_dir, part)
print_log('Checking if {0} exists '.format(part_path))
p = Path(part_path)
if not p.exists():
print_log('Creating: {0}'.format(part))
if p.suffix != '':
with open(os.path.join(root_dir, base_dir + '/' + filepath), 'w+') as f:
f.write('')
else:
os.mkdir(part_path)
def clean_up_generated_data(root_dir):
for root, dirs, files in os.walk(root_dir):
for dir in dirs:
shutil.rmtree(os.path.join(root, dir))
for file in files:
if not file.endswith('-testcase.txt'):
print_log('Removing {0}'.format(os.path.join(root, file)))
os.remove(os.path.join(root, file))
def generate_test_file():
root_dir = os.path.abspath('.')
current_folder = os.path.split(root_dir)[-1]
out_files = []
for root, _, files in os.walk(root_dir):
for file in files:
if not file.endswith('-testcase.txt'):
filename = os.path.join(root.replace(root_dir, ''), file) # root_dir or root_dir + '//'?
out_files.append(filename)
with open(os.path.join(root_dir, current_folder + '-testcase.txt'), 'w+') as f:
for filename in out_files:
f.write(filename + '\n')
if __name__ == '__main__':
verbose = True
generate_test_file()