From 76a14923597b77ff5c7748fda6fdd534a5a09a31 Mon Sep 17 00:00:00 2001 From: vers-one <12114169+vers-one@users.noreply.github.com> Date: Sat, 17 Sep 2022 01:05:21 -0400 Subject: [PATCH] EpubReader unit tests --- README.md | 1 + .../Unit/Comparers/Epub2NcxComparer.cs | 21 +- .../Unit/Comparers/EpubBookComparer.cs | 22 + .../Unit/Comparers/EpubBookRefComparer.cs | 18 + .../Unit/Comparers/EpubContentComparer.cs | 58 + .../Comparers/EpubNavigationItemComparer.cs | 45 + .../EpubNavigationItemRefComparer.cs | 17 +- .../Unit/Mocks/TestFileSystem.cs | 41 + .../Unit/Root/EpubReaderTests.cs | 1114 +++++++++++++++++ Source/VersOne.Epub/Entities/EpubBook.cs | 8 +- Source/VersOne.Epub/EpubReader.cs | 59 +- 11 files changed, 1359 insertions(+), 45 deletions(-) create mode 100644 Source/VersOne.Epub.Test/Unit/Comparers/EpubBookComparer.cs create mode 100644 Source/VersOne.Epub.Test/Unit/Comparers/EpubBookRefComparer.cs create mode 100644 Source/VersOne.Epub.Test/Unit/Comparers/EpubContentComparer.cs create mode 100644 Source/VersOne.Epub.Test/Unit/Comparers/EpubNavigationItemComparer.cs create mode 100644 Source/VersOne.Epub.Test/Unit/Mocks/TestFileSystem.cs create mode 100644 Source/VersOne.Epub.Test/Unit/Root/EpubReaderTests.cs diff --git a/README.md b/README.md index 531941d..0c8b206 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ [![Build](https://github.com/vers-one/EpubReader/actions/workflows/build.yml/badge.svg)](https://github.com/vers-one/EpubReader/actions/workflows/build.yml) [![Tests](https://github.com/vers-one/EpubReader/actions/workflows/test.yml/badge.svg)](https://github.com/vers-one/EpubReader/actions/workflows/test.yml) +[![Test coverage](https://codecov.io/gh/vers-one/EpubReader/branch/master/graph/badge.svg?token=19RoYjPa56)](https://codecov.io/gh/vers-one/EpubReader) [![NuGet](https://img.shields.io/nuget/v/VersOne.Epub.svg)](https://www.nuget.org/packages/VersOne.Epub) Supported EPUB standards: diff --git a/Source/VersOne.Epub.Test/Unit/Comparers/Epub2NcxComparer.cs b/Source/VersOne.Epub.Test/Unit/Comparers/Epub2NcxComparer.cs index 1e7a3c9..592a385 100644 --- a/Source/VersOne.Epub.Test/Unit/Comparers/Epub2NcxComparer.cs +++ b/Source/VersOne.Epub.Test/Unit/Comparers/Epub2NcxComparer.cs @@ -7,13 +7,20 @@ internal static class Epub2NcxComparer { public static void CompareEpub2Ncxes(Epub2Ncx expected, Epub2Ncx actual) { - Assert.NotNull(actual); - CompareEpub2NcxHeads(expected.Head, actual.Head); - Assert.Equal(expected.DocTitle, actual.DocTitle); - Assert.Equal(expected.DocAuthors, actual.DocAuthors); - CompareEpub2NcxNavigationMaps(expected.NavMap, actual.NavMap); - CompareEpub2NcxPageLists(expected.PageList, actual.PageList); - AssertUtils.CollectionsEqual(expected.NavLists, actual.NavLists, CompareEpub2NcxNavigationLists); + if (expected == null) + { + Assert.Null(actual); + } + else + { + Assert.NotNull(actual); + CompareEpub2NcxHeads(expected.Head, actual.Head); + Assert.Equal(expected.DocTitle, actual.DocTitle); + Assert.Equal(expected.DocAuthors, actual.DocAuthors); + CompareEpub2NcxNavigationMaps(expected.NavMap, actual.NavMap); + CompareEpub2NcxPageLists(expected.PageList, actual.PageList); + AssertUtils.CollectionsEqual(expected.NavLists, actual.NavLists, CompareEpub2NcxNavigationLists); + } } private static void CompareEpub2NcxHeads(Epub2NcxHead expected, Epub2NcxHead actual) diff --git a/Source/VersOne.Epub.Test/Unit/Comparers/EpubBookComparer.cs b/Source/VersOne.Epub.Test/Unit/Comparers/EpubBookComparer.cs new file mode 100644 index 0000000..c487725 --- /dev/null +++ b/Source/VersOne.Epub.Test/Unit/Comparers/EpubBookComparer.cs @@ -0,0 +1,22 @@ +using VersOne.Epub.Test.Unit.TestUtils; + +namespace VersOne.Epub.Test.Unit.Comparers +{ + internal static class EpubBookComparer + { + public static void CompareEpubBooks(EpubBook expected, EpubBook actual) + { + Assert.NotNull(actual); + Assert.Equal(expected.FilePath, actual.FilePath); + Assert.Equal(expected.Title, actual.Title); + Assert.Equal(expected.Author, actual.Author); + Assert.Equal(expected.AuthorList, actual.AuthorList); + Assert.Equal(expected.Description, actual.Description); + Assert.Equal(expected.CoverImage, actual.CoverImage); + AssertUtils.CollectionsEqual(expected.ReadingOrder, actual.ReadingOrder, EpubContentComparer.CompareEpubTextContentFiles); + EpubNavigationItemComparer.CompareNavigationItemLists(expected.Navigation, actual.Navigation); + EpubContentComparer.CompareEpubContents(expected.Content, actual.Content); + EpubSchemaComparer.CompareEpubSchemas(expected.Schema, actual.Schema); + } + } +} diff --git a/Source/VersOne.Epub.Test/Unit/Comparers/EpubBookRefComparer.cs b/Source/VersOne.Epub.Test/Unit/Comparers/EpubBookRefComparer.cs new file mode 100644 index 0000000..cdddd50 --- /dev/null +++ b/Source/VersOne.Epub.Test/Unit/Comparers/EpubBookRefComparer.cs @@ -0,0 +1,18 @@ +namespace VersOne.Epub.Test.Unit.Comparers +{ + internal static class EpubBookRefComparer + { + public static void CompareEpubBookRefs(EpubBookRef expected, EpubBookRef actual) + { + Assert.NotNull(actual); + Assert.Equal(expected.FilePath, actual.FilePath); + Assert.Equal(expected.Title, actual.Title); + Assert.Equal(expected.Author, actual.Author); + Assert.Equal(expected.AuthorList, actual.AuthorList); + Assert.Equal(expected.Description, actual.Description); + EpubSchemaComparer.CompareEpubSchemas(expected.Schema, actual.Schema); + EpubContentRefComparer.CompareEpubContentRefs(expected.Content, actual.Content); + Assert.Equal(expected.EpubFile, actual.EpubFile); + } + } +} diff --git a/Source/VersOne.Epub.Test/Unit/Comparers/EpubContentComparer.cs b/Source/VersOne.Epub.Test/Unit/Comparers/EpubContentComparer.cs new file mode 100644 index 0000000..696538d --- /dev/null +++ b/Source/VersOne.Epub.Test/Unit/Comparers/EpubContentComparer.cs @@ -0,0 +1,58 @@ +using VersOne.Epub.Test.Unit.TestUtils; + +namespace VersOne.Epub.Test.Unit.Comparers +{ + internal static class EpubContentComparer + { + public static void CompareEpubContents(EpubContent expected, EpubContent actual) + { + CompareEpubContentFiles(expected.Cover, actual.Cover); + CompareEpubContentFiles(expected.NavigationHtmlFile, actual.NavigationHtmlFile); + AssertUtils.DictionariesEqual(expected.Html, actual.Html, CompareEpubTextContentFiles); + AssertUtils.DictionariesEqual(expected.Css, actual.Css, CompareEpubTextContentFiles); + AssertUtils.DictionariesEqual(expected.Images, actual.Images, CompareEpubByteContentFiles); + AssertUtils.DictionariesEqual(expected.Fonts, actual.Fonts, CompareEpubByteContentFiles); + AssertUtils.DictionariesEqual(expected.AllFiles, actual.AllFiles, CompareEpubContentFilesWithContent); + } + + public static void CompareEpubTextContentFiles(EpubTextContentFile expected, EpubTextContentFile actual) + { + CompareEpubContentFiles(expected, actual); + Assert.Equal(expected.Content, actual.Content); + } + + public static void CompareEpubByteContentFiles(EpubByteContentFile expected, EpubByteContentFile actual) + { + CompareEpubContentFiles(expected, actual); + Assert.Equal(expected.Content, actual.Content); + } + + private static void CompareEpubContentFilesWithContent(EpubContentFile expected, EpubContentFile actual) + { + if (expected is EpubTextContentFile) + { + CompareEpubTextContentFiles(expected as EpubTextContentFile, actual as EpubTextContentFile); + } + else if (expected is EpubByteContentFile) + { + CompareEpubByteContentFiles(expected as EpubByteContentFile, actual as EpubByteContentFile); + } + } + + private static void CompareEpubContentFiles(EpubContentFile expected, EpubContentFile actual) + { + if (expected == null) + { + Assert.Null(actual); + } + else + { + Assert.NotNull(actual); + Assert.Equal(expected.FileName, actual.FileName); + Assert.Equal(expected.FilePathInEpubArchive, actual.FilePathInEpubArchive); + Assert.Equal(expected.ContentType, actual.ContentType); + Assert.Equal(expected.ContentMimeType, actual.ContentMimeType); + } + } + } +} diff --git a/Source/VersOne.Epub.Test/Unit/Comparers/EpubNavigationItemComparer.cs b/Source/VersOne.Epub.Test/Unit/Comparers/EpubNavigationItemComparer.cs new file mode 100644 index 0000000..f23cc1b --- /dev/null +++ b/Source/VersOne.Epub.Test/Unit/Comparers/EpubNavigationItemComparer.cs @@ -0,0 +1,45 @@ +using VersOne.Epub.Test.Unit.TestUtils; + +namespace VersOne.Epub.Test.Unit.Comparers +{ + internal static class EpubNavigationItemComparer + { + public static void CompareNavigationItemLists(List expected, List actual) + { + if (expected == null) + { + Assert.Null(actual); + } + else + { + Assert.NotNull(actual); + AssertUtils.CollectionsEqual(expected, actual, CompareNavigationItems); + } + } + + public static void CompareNavigationItemLinks(EpubNavigationItemLink expected, EpubNavigationItemLink actual) + { + if (expected == null) + { + Assert.Null(actual); + } + else + { + Assert.NotNull(actual); + Assert.Equal(expected.ContentFileName, actual.ContentFileName); + Assert.Equal(expected.ContentFilePathInEpubArchive, actual.ContentFilePathInEpubArchive); + Assert.Equal(expected.Anchor, actual.Anchor); + } + } + + private static void CompareNavigationItems(EpubNavigationItem expected, EpubNavigationItem actual) + { + Assert.NotNull(actual); + Assert.Equal(expected.Type, actual.Type); + Assert.Equal(expected.Title, actual.Title); + CompareNavigationItemLinks(expected.Link, actual.Link); + EpubContentComparer.CompareEpubTextContentFiles(expected.HtmlContentFile, actual.HtmlContentFile); + CompareNavigationItemLists(expected.NestedItems, actual.NestedItems); + } + } +} diff --git a/Source/VersOne.Epub.Test/Unit/Comparers/EpubNavigationItemRefComparer.cs b/Source/VersOne.Epub.Test/Unit/Comparers/EpubNavigationItemRefComparer.cs index 87d6c1e..4760ebb 100644 --- a/Source/VersOne.Epub.Test/Unit/Comparers/EpubNavigationItemRefComparer.cs +++ b/Source/VersOne.Epub.Test/Unit/Comparers/EpubNavigationItemRefComparer.cs @@ -22,24 +22,9 @@ private static void CompareNavigationItemRefs(EpubNavigationItemRef expected, Ep Assert.NotNull(actual); Assert.Equal(expected.Type, actual.Type); Assert.Equal(expected.Title, actual.Title); - CompareNavigationItemLinks(expected.Link, actual.Link); + EpubNavigationItemComparer.CompareNavigationItemLinks(expected.Link, actual.Link); Assert.Equal(expected.HtmlContentFileRef, actual.HtmlContentFileRef); CompareNavigationItemRefLists(expected.NestedItems, actual.NestedItems); } - - private static void CompareNavigationItemLinks(EpubNavigationItemLink expected, EpubNavigationItemLink actual) - { - if (expected == null) - { - Assert.Null(actual); - } - else - { - Assert.NotNull(actual); - Assert.Equal(expected.ContentFileName, actual.ContentFileName); - Assert.Equal(expected.ContentFilePathInEpubArchive, actual.ContentFilePathInEpubArchive); - Assert.Equal(expected.Anchor, actual.Anchor); - } - } } } diff --git a/Source/VersOne.Epub.Test/Unit/Mocks/TestFileSystem.cs b/Source/VersOne.Epub.Test/Unit/Mocks/TestFileSystem.cs new file mode 100644 index 0000000..d4e200e --- /dev/null +++ b/Source/VersOne.Epub.Test/Unit/Mocks/TestFileSystem.cs @@ -0,0 +1,41 @@ +using VersOne.Epub.Environment; + +namespace VersOne.Epub.Test.Unit.Mocks +{ + internal class TestFileSystem : IFileSystem + { + private readonly Dictionary testZipFilesByPath; + private readonly Dictionary testZipFilesByStream; + + public TestFileSystem() + { + testZipFilesByPath = new Dictionary(); + testZipFilesByStream = new Dictionary(); + } + + public void AddTestZipFile(string path, TestZipFile testZipFile) + { + testZipFilesByPath.Add(path, testZipFile); + } + + public void AddTestZipFile(Stream stream, TestZipFile testZipFile) + { + testZipFilesByStream.Add(stream, testZipFile); + } + + public bool FileExists(string path) + { + return testZipFilesByPath.ContainsKey(path); + } + + public IZipFile OpenZipFile(string path) + { + return testZipFilesByPath[path]; + } + + public IZipFile OpenZipFile(Stream stream) + { + return testZipFilesByStream[stream]; + } + } +} diff --git a/Source/VersOne.Epub.Test/Unit/Root/EpubReaderTests.cs b/Source/VersOne.Epub.Test/Unit/Root/EpubReaderTests.cs new file mode 100644 index 0000000..db14349 --- /dev/null +++ b/Source/VersOne.Epub.Test/Unit/Root/EpubReaderTests.cs @@ -0,0 +1,1114 @@ +using VersOne.Epub.Environment; +using VersOne.Epub.Schema; +using VersOne.Epub.Test.Unit.Comparers; +using VersOne.Epub.Test.Unit.Mocks; + +namespace VersOne.Epub.Test.Unit.Root +{ + public class EpubReaderTests + { + private const string EPUB_FILE_PATH = "test.epub"; + private const string CONTAINER_FILE_PATH = "META-INF/container.xml"; + private const string CONTENT_DIRECTORY_PATH = "Content"; + private const string OPF_FILE_PATH = $"{CONTENT_DIRECTORY_PATH}/content.opf"; + private const EpubContentType HTML_CONTENT_TYPE = EpubContentType.XHTML_1_1; + private const string HTML_CONTENT_MIME_TYPE = "application/xhtml+xml"; + private const string CHAPTER1_FILE_NAME = "chapter1.html"; + private const string CHAPTER1_FILE_PATH = $"{CONTENT_DIRECTORY_PATH}/{CHAPTER1_FILE_NAME}"; + private const string CHAPTER2_FILE_NAME = "chapter2.html"; + private const string CHAPTER2_FILE_PATH = $"{CONTENT_DIRECTORY_PATH}/{CHAPTER2_FILE_NAME}"; + private const EpubContentType CSS_CONTENT_TYPE = EpubContentType.CSS; + private const string CSS_CONTENT_MIME_TYPE = "text/css"; + private const string STYLES1_FILE_NAME = "styles1.css"; + private const string STYLES1_FILE_PATH = $"{CONTENT_DIRECTORY_PATH}/{STYLES1_FILE_NAME}"; + private const string STYLES2_FILE_NAME = "styles2.css"; + private const string STYLES2_FILE_PATH = $"{CONTENT_DIRECTORY_PATH}/{STYLES2_FILE_NAME}"; + private const EpubContentType IMAGE_CONTENT_TYPE = EpubContentType.IMAGE_JPEG; + private const string IMAGE_CONTENT_MIME_TYPE = "image/jpeg"; + private const string IMAGE1_FILE_NAME = "image1.jpg"; + private const string IMAGE1_FILE_PATH = $"{CONTENT_DIRECTORY_PATH}/{IMAGE1_FILE_NAME}"; + private const string IMAGE2_FILE_NAME = "image2.jpg"; + private const string IMAGE2_FILE_PATH = $"{CONTENT_DIRECTORY_PATH}/{IMAGE2_FILE_NAME}"; + private const EpubContentType FONT_CONTENT_TYPE = EpubContentType.FONT_TRUETYPE; + private const string FONT_CONTENT_MIME_TYPE = "font/truetype"; + private const string FONT1_FILE_NAME = "font1.ttf"; + private const string FONT1_FILE_PATH = $"{CONTENT_DIRECTORY_PATH}/{FONT1_FILE_NAME}"; + private const string FONT2_FILE_NAME = "font2.ttf"; + private const string FONT2_FILE_PATH = $"{CONTENT_DIRECTORY_PATH}/{FONT2_FILE_NAME}"; + private const string NAV_FILE_NAME = "toc.html"; + private const string NAV_FILE_PATH = $"{CONTENT_DIRECTORY_PATH}/{NAV_FILE_NAME}"; + private const string COVER_FILE_NAME = "cover.jpg"; + private const string COVER_FILE_PATH = $"{CONTENT_DIRECTORY_PATH}/{COVER_FILE_NAME}"; + private const EpubContentType NCX_CONTENT_TYPE = EpubContentType.DTBOOK_NCX; + private const string NCX_CONTENT_MIME_TYPE = "application/x-dtbncx+xml"; + private const string NCX_FILE_NAME = "toc.ncx"; + private const string NCX_FILE_PATH = $"{CONTENT_DIRECTORY_PATH}/{NCX_FILE_NAME}"; + private const EpubContentType OTHER_CONTENT_TYPE = EpubContentType.OTHER; + private const string AUDIO_MPEG_CONTENT_MIME_TYPE = "audio/mpeg"; + private const string AUDIO_FILE_NAME = "audio.mp3"; + private const string AUDIO_FILE_PATH = $"{CONTENT_DIRECTORY_PATH}/{AUDIO_FILE_NAME}"; + private const string BOOK_TITLE = "Test title"; + private const string BOOK_AUTHOR = "John Doe"; + private const string BOOK_DESCRIPTION = "Test description"; + private const string BOOK_UID = "9781234567890"; + + private const string CONTAINER_FILE_CONTENT = $""" + + + + + + + """; + + private const string MINIMAL_OPF_FILE_CONTENT = $""" + + + + + + + + + """; + + private const string MINIMAL_NAV_FILE_CONTENT = $""" + + + + """; + + private const string FULL_OPF_FILE_CONTENT = $""" + + + + {BOOK_TITLE} + {BOOK_AUTHOR} + {BOOK_DESCRIPTION} + + + + + + + + + + + + + + + + + + + + + """; + + private const string NCX_FILE_CONTENT = $""" + + + + + + + {BOOK_TITLE} + + + {BOOK_AUTHOR} + + + + + Chapter 1 + + + + + + Chapter 2 + + + + + + """; + + private const string FULL_NAV_FILE_CONTENT = $""" + + + + + + """; + + private const string CHAPTER1_FILE_CONTENT = "Chapter 1

Chapter 1

"; + + private const string CHAPTER2_FILE_CONTENT = "Chapter 2

Chapter 2

"; + + private const string STYLES1_FILE_CONTENT = ".text{color:#010101}"; + + private const string STYLES2_FILE_CONTENT = ".text{color:#020202}"; + + private static readonly byte[] IMAGE1_FILE_CONTENT = new byte[] { 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x01 }; + + private static readonly byte[] IMAGE2_FILE_CONTENT = new byte[] { 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x02 }; + + private static readonly byte[] COVER_FILE_CONTENT = new byte[] { 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0xff }; + + private static readonly byte[] FONT1_FILE_CONTENT = new byte[] { 0x00, 0x01, 0x00, 0x01 }; + + private static readonly byte[] FONT2_FILE_CONTENT = new byte[] { 0x00, 0x01, 0x00, 0x02 }; + + private static readonly byte[] AUDIO_FILE_CONTENT = new byte[] { 0x49, 0x44, 0x33, 0x03 }; + + [Fact(DisplayName = "Opening a minimal EPUB book from a file synchronously should succeed")] + public void OpenMinimalBookFromFileTest() + { + TestZipFile testEpubFile = CreateMinimalTestEpubFile(); + SetupTestFileSystem(EPUB_FILE_PATH, testEpubFile); + EpubBookRef expectedEpubBookRef = CreateMinimalExpectedEpubBookRef(testEpubFile, EPUB_FILE_PATH); + EpubBookRef actualEpubBookRef = EpubReader.OpenBook(EPUB_FILE_PATH); + EpubBookRefComparer.CompareEpubBookRefs(expectedEpubBookRef, actualEpubBookRef); + } + + [Fact(DisplayName = "Reading a minimal EPUB book from a file synchronously should succeed")] + public void ReadMinimalBookFromFileTest() + { + TestZipFile testEpubFile = CreateMinimalTestEpubFile(); + SetupTestFileSystem(EPUB_FILE_PATH, testEpubFile); + EpubBook expectedEpubBook = CreateMinimalExpectedEpubBook(EPUB_FILE_PATH); + EpubBook actualEpubBook = EpubReader.ReadBook(EPUB_FILE_PATH); + EpubBookComparer.CompareEpubBooks(expectedEpubBook, actualEpubBook); + } + + [Fact(DisplayName = "Opening a full EPUB book from a file synchronously should succeed")] + public void OpenBookFromFileTest() + { + TestZipFile testEpubFile = CreateFullTestEpubFile(); + SetupTestFileSystem(EPUB_FILE_PATH, testEpubFile); + EpubBookRef expectedEpubBookRef = CreateFullExpectedEpubBookRef(testEpubFile, EPUB_FILE_PATH); + EpubBookRef actualEpubBookRef = EpubReader.OpenBook(EPUB_FILE_PATH); + EpubBookRefComparer.CompareEpubBookRefs(expectedEpubBookRef, actualEpubBookRef); + } + + [Fact(DisplayName = "Opening a full EPUB book from a file asynchronously should succeed")] + public async void OpenBookFromFileAsyncTest() + { + TestZipFile testEpubFile = CreateFullTestEpubFile(); + SetupTestFileSystem(EPUB_FILE_PATH, testEpubFile); + EpubBookRef expectedEpubBookRef = CreateFullExpectedEpubBookRef(testEpubFile, EPUB_FILE_PATH); + EpubBookRef actualEpubBookRef = await EpubReader.OpenBookAsync(EPUB_FILE_PATH); + EpubBookRefComparer.CompareEpubBookRefs(expectedEpubBookRef, actualEpubBookRef); + } + + [Fact(DisplayName = "Opening a full EPUB book from a stream synchronously should succeed")] + public void OpenBookFromStreamTest() + { + TestZipFile testEpubFile = CreateFullTestEpubFile(); + MemoryStream epubFileStream = new(); + SetupTestFileSystem(epubFileStream, testEpubFile); + EpubBookRef expectedEpubBookRef = CreateFullExpectedEpubBookRef(testEpubFile, null); + EpubBookRef actualEpubBookRef = EpubReader.OpenBook(epubFileStream); + EpubBookRefComparer.CompareEpubBookRefs(expectedEpubBookRef, actualEpubBookRef); + } + + [Fact(DisplayName = "Opening a full EPUB book from a stream asynchronously should succeed")] + public async void OpenBookFromStreamAsyncTest() + { + TestZipFile testEpubFile = CreateFullTestEpubFile(); + MemoryStream epubFileStream = new(); + SetupTestFileSystem(epubFileStream, testEpubFile); + EpubBookRef expectedEpubBookRef = CreateFullExpectedEpubBookRef(testEpubFile, null); + EpubBookRef actualEpubBookRef = await EpubReader.OpenBookAsync(epubFileStream); + EpubBookRefComparer.CompareEpubBookRefs(expectedEpubBookRef, actualEpubBookRef); + } + + [Fact(DisplayName = "Reading a full EPUB book from a file synchronously should succeed")] + public void ReadBookFromFileTest() + { + TestZipFile testEpubFile = CreateFullTestEpubFile(); + SetupTestFileSystem(EPUB_FILE_PATH, testEpubFile); + EpubBook expectedEpubBook = CreateFullExpectedEpubBook(EPUB_FILE_PATH); + EpubBook actualEpubBook = EpubReader.ReadBook(EPUB_FILE_PATH); + EpubBookComparer.CompareEpubBooks(expectedEpubBook, actualEpubBook); + } + + [Fact(DisplayName = "Reading a full EPUB book from a file asynchronously should succeed")] + public async void ReadBookFromFileAsyncTest() + { + TestZipFile testEpubFile = CreateFullTestEpubFile(); + SetupTestFileSystem(EPUB_FILE_PATH, testEpubFile); + EpubBook expectedEpubBook = CreateFullExpectedEpubBook(EPUB_FILE_PATH); + EpubBook actualEpubBook = await EpubReader.ReadBookAsync(EPUB_FILE_PATH); + EpubBookComparer.CompareEpubBooks(expectedEpubBook, actualEpubBook); + } + + [Fact(DisplayName = "Reading a full EPUB book from a stream synchronously should succeed")] + public void ReadBookFromStreamTest() + { + TestZipFile testEpubFile = CreateFullTestEpubFile(); + MemoryStream epubFileStream = new(); + SetupTestFileSystem(epubFileStream, testEpubFile); + EpubBook expectedEpubBook = CreateFullExpectedEpubBook(null); + EpubBook actualEpubBook = EpubReader.ReadBook(epubFileStream); + EpubBookComparer.CompareEpubBooks(expectedEpubBook, actualEpubBook); + } + + [Fact(DisplayName = "Reading a full EPUB book from a stream asynchronously should succeed")] + public async void ReadBookFromStreamAsyncTest() + { + TestZipFile testEpubFile = CreateFullTestEpubFile(); + MemoryStream epubFileStream = new(); + SetupTestFileSystem(epubFileStream, testEpubFile); + EpubBook expectedEpubBook = CreateFullExpectedEpubBook(null); + EpubBook actualEpubBook = await EpubReader.ReadBookAsync(epubFileStream); + EpubBookComparer.CompareEpubBooks(expectedEpubBook, actualEpubBook); + } + + [Fact(DisplayName = "OpenBook should throw FileNotFoundException if the specified file does not exist")] + public void OpenBookFromFileWithMissingFileTest() + { + SetupEmptyTestFileSystem(); + Assert.Throws(() => EpubReader.OpenBook(EPUB_FILE_PATH)); + } + + [Fact(DisplayName = "OpenBookAsync should throw FileNotFoundException if the specified file does not exist")] + public async void OpenBookFromFileAsyncWithMissingFileTest() + { + SetupEmptyTestFileSystem(); + await Assert.ThrowsAsync(() => EpubReader.OpenBookAsync(EPUB_FILE_PATH)); + } + + [Fact(DisplayName = "ReadBook should throw FileNotFoundException if the specified file does not exist")] + public void ReadBookFromFileWithMissingFileTest() + { + SetupEmptyTestFileSystem(); + Assert.Throws(() => EpubReader.ReadBook(EPUB_FILE_PATH)); + } + + [Fact(DisplayName = "ReadBookAsync should throw FileNotFoundException if the specified file does not exist")] + public async void ReadBookFromFileAsyncWithMissingFileTest() + { + SetupEmptyTestFileSystem(); + await Assert.ThrowsAsync(() => EpubReader.ReadBookAsync(EPUB_FILE_PATH)); + } + + [Fact(DisplayName = "OpenBook should rethrow EPUB parsing exceptions")] + public void OpenBookFromFileWithIncorrectEpubFileTest() + { + TestZipFile incorrectEpubFile = new(); + SetupTestFileSystem(EPUB_FILE_PATH, incorrectEpubFile); + Assert.Throws(() => EpubReader.OpenBook(EPUB_FILE_PATH)); + } + + [Fact(DisplayName = "OpenBookAsync should rethrow EPUB parsing exceptions")] + public async void OpenBookFromFileAsyncWithIncorrectEpubTest() + { + TestZipFile incorrectEpubFile = new(); + SetupTestFileSystem(EPUB_FILE_PATH, incorrectEpubFile); + await Assert.ThrowsAsync(() => EpubReader.OpenBookAsync(EPUB_FILE_PATH)); + } + + [Fact(DisplayName = "ReadBook should rethrow EPUB parsing exceptions")] + public void ReadBookFromFileWithIncorrectEpubTest() + { + TestZipFile incorrectEpubFile = new(); + SetupTestFileSystem(EPUB_FILE_PATH, incorrectEpubFile); + Assert.Throws(() => EpubReader.ReadBook(EPUB_FILE_PATH)); + } + + [Fact(DisplayName = "ReadBookAsync should rethrow EPUB parsing exceptions")] + public async void ReadBookFromFileAsyncWithIncorrectEpubTest() + { + TestZipFile incorrectEpubFile = new(); + SetupTestFileSystem(EPUB_FILE_PATH, incorrectEpubFile); + await Assert.ThrowsAsync(() => EpubReader.ReadBookAsync(EPUB_FILE_PATH)); + } + + private TestZipFile CreateMinimalTestEpubFile() + { + TestZipFile result = new(); + result.AddEntry(CONTAINER_FILE_PATH, CONTAINER_FILE_CONTENT); + result.AddEntry(OPF_FILE_PATH, MINIMAL_OPF_FILE_CONTENT); + result.AddEntry(NAV_FILE_PATH, MINIMAL_NAV_FILE_CONTENT); + return result; + } + + private TestZipFile CreateFullTestEpubFile() + { + TestZipFile result = new(); + result.AddEntry(CONTAINER_FILE_PATH, CONTAINER_FILE_CONTENT); + result.AddEntry(OPF_FILE_PATH, FULL_OPF_FILE_CONTENT); + result.AddEntry(CHAPTER1_FILE_PATH, CHAPTER1_FILE_CONTENT); + result.AddEntry(CHAPTER2_FILE_PATH, CHAPTER2_FILE_CONTENT); + result.AddEntry(STYLES1_FILE_PATH, STYLES1_FILE_CONTENT); + result.AddEntry(STYLES2_FILE_PATH, STYLES2_FILE_CONTENT); + result.AddEntry(IMAGE1_FILE_PATH, IMAGE1_FILE_CONTENT); + result.AddEntry(IMAGE2_FILE_PATH, IMAGE2_FILE_CONTENT); + result.AddEntry(FONT1_FILE_PATH, FONT1_FILE_CONTENT); + result.AddEntry(FONT2_FILE_PATH, FONT2_FILE_CONTENT); + result.AddEntry(AUDIO_FILE_PATH, AUDIO_FILE_CONTENT); + result.AddEntry(NAV_FILE_PATH, FULL_NAV_FILE_CONTENT); + result.AddEntry(COVER_FILE_PATH, COVER_FILE_CONTENT); + result.AddEntry(NCX_FILE_PATH, NCX_FILE_CONTENT); + return result; + } + + private void SetupEmptyTestFileSystem() + { + TestFileSystem testFileSystem = new(); + EnvironmentDependencies.FileSystem = testFileSystem; + } + + private void SetupTestFileSystem(string epubFilePath, TestZipFile testEpubFile) + { + TestFileSystem testFileSystem = new(); + testFileSystem.AddTestZipFile(epubFilePath, testEpubFile); + EnvironmentDependencies.FileSystem = testFileSystem; + } + + private void SetupTestFileSystem(Stream epubFileStream, TestZipFile testEpubFile) + { + TestFileSystem testFileSystem = new(); + testFileSystem.AddTestZipFile(epubFileStream, testEpubFile); + EnvironmentDependencies.FileSystem = testFileSystem; + } + + private EpubBookRef CreateMinimalExpectedEpubBookRef(TestZipFile epubFile, string epubFilePath) + { + EpubBookRef result = new(epubFile) + { + FilePath = epubFilePath, + Title = String.Empty, + Author = String.Empty, + AuthorList = new List(), + Description = null, + Schema = CreateMinimalExpectedEpubSchema(), + Content = new EpubContentRef() + { + Html = new Dictionary(), + Css = new Dictionary(), + Images = new Dictionary(), + Fonts = new Dictionary(), + AllFiles = new Dictionary(), + Cover = null + } + }; + EpubTextContentFileRef navFileRef = new(result, NAV_FILE_NAME, HTML_CONTENT_TYPE, HTML_CONTENT_MIME_TYPE); + result.Content.Html[NAV_FILE_NAME] = navFileRef; + result.Content.AllFiles[NAV_FILE_NAME] = navFileRef; + result.Content.NavigationHtmlFile = navFileRef; + return result; + } + + private EpubBook CreateMinimalExpectedEpubBook(string epubFilePath) + { + EpubTextContentFile navFile = new() + { + FileName = NAV_FILE_NAME, + FilePathInEpubArchive = NAV_FILE_PATH, + ContentType = HTML_CONTENT_TYPE, + ContentMimeType = HTML_CONTENT_MIME_TYPE, + Content = MINIMAL_NAV_FILE_CONTENT + }; + return new() + { + FilePath = epubFilePath, + Title = String.Empty, + Author = String.Empty, + AuthorList = new List(), + Description = null, + CoverImage = null, + ReadingOrder = new List(), + Navigation = new List(), + Schema = CreateMinimalExpectedEpubSchema(), + Content = new EpubContent() + { + Html = new Dictionary() + { + { + NAV_FILE_NAME, + navFile + } + }, + Css = new Dictionary(), + Images = new Dictionary(), + Fonts = new Dictionary(), + AllFiles = new Dictionary() + { + { + NAV_FILE_NAME, + navFile + } + }, + NavigationHtmlFile = navFile, + Cover = null + } + }; + } + + private EpubSchema CreateMinimalExpectedEpubSchema() + { + return new() + { + Package = new EpubPackage() + { + EpubVersion = EpubVersion.EPUB_3, + Metadata = new EpubMetadata() + { + Titles = new List(), + Creators = new List(), + Subjects = new List(), + Description = null, + Publishers = new List(), + Contributors = new List(), + Dates = new List(), + Types = new List(), + Formats = new List(), + Identifiers = new List(), + Sources = new List(), + Languages = new List(), + Relations = new List(), + Coverages = new List(), + Rights = new List(), + Links = new List(), + MetaItems = new List() + }, + Manifest = new EpubManifest() + { + new EpubManifestItem() + { + Id = "item-toc", + Href = NAV_FILE_NAME, + MediaType = HTML_CONTENT_MIME_TYPE, + Properties = new List() + { + EpubManifestProperty.NAV + } + } + }, + Spine = new EpubSpine(), + Guide = null + }, + Epub2Ncx = null, + Epub3NavDocument = new Epub3NavDocument() + { + Navs = new List() + }, + ContentDirectoryPath = CONTENT_DIRECTORY_PATH + }; + } + + private EpubBookRef CreateFullExpectedEpubBookRef(TestZipFile epubFile, string epubFilePath) + { + EpubBookRef result = new(epubFile) + { + FilePath = epubFilePath, + Title = BOOK_TITLE, + Author = BOOK_AUTHOR, + AuthorList = new List { BOOK_AUTHOR }, + Description = BOOK_DESCRIPTION, + Schema = CreateFullExpectedEpubSchema() + }; + EpubTextContentFileRef chapter1FileRef = new(result, CHAPTER1_FILE_NAME, HTML_CONTENT_TYPE, HTML_CONTENT_MIME_TYPE); + EpubTextContentFileRef chapter2FileRef = new(result, CHAPTER2_FILE_NAME, HTML_CONTENT_TYPE, HTML_CONTENT_MIME_TYPE); + EpubTextContentFileRef styles1FileRef = new(result, STYLES1_FILE_NAME, CSS_CONTENT_TYPE, CSS_CONTENT_MIME_TYPE); + EpubTextContentFileRef styles2FileRef = new(result, STYLES2_FILE_NAME, CSS_CONTENT_TYPE, CSS_CONTENT_MIME_TYPE); + EpubByteContentFileRef image1FileRef = new(result, IMAGE1_FILE_NAME, IMAGE_CONTENT_TYPE, IMAGE_CONTENT_MIME_TYPE); + EpubByteContentFileRef image2FileRef = new(result, IMAGE2_FILE_NAME, IMAGE_CONTENT_TYPE, IMAGE_CONTENT_MIME_TYPE); + EpubByteContentFileRef font1FileRef = new(result, FONT1_FILE_NAME, FONT_CONTENT_TYPE, FONT_CONTENT_MIME_TYPE); + EpubByteContentFileRef font2FileRef = new(result, FONT2_FILE_NAME, FONT_CONTENT_TYPE, FONT_CONTENT_MIME_TYPE); + EpubByteContentFileRef audioFileRef = new(result, AUDIO_FILE_NAME, OTHER_CONTENT_TYPE, AUDIO_MPEG_CONTENT_MIME_TYPE); + EpubTextContentFileRef navFileRef = new(result, NAV_FILE_NAME, HTML_CONTENT_TYPE, HTML_CONTENT_MIME_TYPE); + EpubByteContentFileRef coverFileRef = new(result, COVER_FILE_NAME, IMAGE_CONTENT_TYPE, IMAGE_CONTENT_MIME_TYPE); + EpubTextContentFileRef ncxFileRef = new(result, NCX_FILE_NAME, NCX_CONTENT_TYPE, NCX_CONTENT_MIME_TYPE); + result.Content = new EpubContentRef() + { + Html = new Dictionary() + { + { + CHAPTER1_FILE_NAME, + chapter1FileRef + }, + { + CHAPTER2_FILE_NAME, + chapter2FileRef + }, + { + NAV_FILE_NAME, + navFileRef + } + }, + Css = new Dictionary() + { + { + STYLES1_FILE_NAME, + styles1FileRef + }, + { + STYLES2_FILE_NAME, + styles2FileRef + } + }, + Images = new Dictionary() + { + { + IMAGE1_FILE_NAME, + image1FileRef + }, + { + IMAGE2_FILE_NAME, + image2FileRef + }, + { + COVER_FILE_NAME, + coverFileRef + } + }, + Fonts = new Dictionary() + { + { + FONT1_FILE_NAME, + font1FileRef + }, + { + FONT2_FILE_NAME, + font2FileRef + } + }, + AllFiles = new Dictionary() + { + { + CHAPTER1_FILE_NAME, + chapter1FileRef + }, + { + CHAPTER2_FILE_NAME, + chapter2FileRef + }, + { + STYLES1_FILE_NAME, + styles1FileRef + }, + { + STYLES2_FILE_NAME, + styles2FileRef + }, + { + IMAGE1_FILE_NAME, + image1FileRef + }, + { + IMAGE2_FILE_NAME, + image2FileRef + }, + { + FONT1_FILE_NAME, + font1FileRef + }, + { + FONT2_FILE_NAME, + font2FileRef + }, + { + AUDIO_FILE_NAME, + audioFileRef + }, + { + NAV_FILE_NAME, + navFileRef + }, + { + COVER_FILE_NAME, + coverFileRef + }, + { + NCX_FILE_NAME, + ncxFileRef + } + }, + NavigationHtmlFile = navFileRef, + Cover = coverFileRef + }; + return result; + } + + private EpubBook CreateFullExpectedEpubBook(string epubFilePath) + { + EpubTextContentFile chapter1File = new() + { + FileName = CHAPTER1_FILE_NAME, + FilePathInEpubArchive = CHAPTER1_FILE_PATH, + ContentType = HTML_CONTENT_TYPE, + ContentMimeType = HTML_CONTENT_MIME_TYPE, + Content = CHAPTER1_FILE_CONTENT + }; + EpubTextContentFile chapter2File = new() + { + FileName = CHAPTER2_FILE_NAME, + FilePathInEpubArchive = CHAPTER2_FILE_PATH, + ContentType = HTML_CONTENT_TYPE, + ContentMimeType = HTML_CONTENT_MIME_TYPE, + Content = CHAPTER2_FILE_CONTENT + }; + EpubTextContentFile styles1File = new() + { + FileName = STYLES1_FILE_NAME, + FilePathInEpubArchive = STYLES1_FILE_PATH, + ContentType = CSS_CONTENT_TYPE, + ContentMimeType = CSS_CONTENT_MIME_TYPE, + Content = STYLES1_FILE_CONTENT + }; + EpubTextContentFile styles2File = new() + { + FileName = STYLES2_FILE_NAME, + FilePathInEpubArchive = STYLES2_FILE_PATH, + ContentType = CSS_CONTENT_TYPE, + ContentMimeType = CSS_CONTENT_MIME_TYPE, + Content = STYLES2_FILE_CONTENT + }; + EpubByteContentFile image1File = new() + { + FileName = IMAGE1_FILE_NAME, + FilePathInEpubArchive = IMAGE1_FILE_PATH, + ContentType = IMAGE_CONTENT_TYPE, + ContentMimeType = IMAGE_CONTENT_MIME_TYPE, + Content = IMAGE1_FILE_CONTENT + }; + EpubByteContentFile image2File = new() + { + FileName = IMAGE2_FILE_NAME, + FilePathInEpubArchive = IMAGE2_FILE_PATH, + ContentType = IMAGE_CONTENT_TYPE, + ContentMimeType = IMAGE_CONTENT_MIME_TYPE, + Content = IMAGE2_FILE_CONTENT + }; + EpubByteContentFile font1File = new() + { + FileName = FONT1_FILE_NAME, + FilePathInEpubArchive = FONT1_FILE_PATH, + ContentType = FONT_CONTENT_TYPE, + ContentMimeType = FONT_CONTENT_MIME_TYPE, + Content = FONT1_FILE_CONTENT + }; + EpubByteContentFile font2File = new() + { + FileName = FONT2_FILE_NAME, + FilePathInEpubArchive = FONT2_FILE_PATH, + ContentType = FONT_CONTENT_TYPE, + ContentMimeType = FONT_CONTENT_MIME_TYPE, + Content = FONT2_FILE_CONTENT + }; + EpubByteContentFile audioFile = new() + { + FileName = AUDIO_FILE_NAME, + FilePathInEpubArchive = AUDIO_FILE_PATH, + ContentType = OTHER_CONTENT_TYPE, + ContentMimeType = AUDIO_MPEG_CONTENT_MIME_TYPE, + Content = AUDIO_FILE_CONTENT + }; + EpubTextContentFile navFile = new() + { + FileName = NAV_FILE_NAME, + FilePathInEpubArchive = NAV_FILE_PATH, + ContentType = HTML_CONTENT_TYPE, + ContentMimeType = HTML_CONTENT_MIME_TYPE, + Content = FULL_NAV_FILE_CONTENT + }; + EpubByteContentFile coverFile = new() + { + FileName = COVER_FILE_NAME, + FilePathInEpubArchive = COVER_FILE_PATH, + ContentType = IMAGE_CONTENT_TYPE, + ContentMimeType = IMAGE_CONTENT_MIME_TYPE, + Content = COVER_FILE_CONTENT + }; + EpubTextContentFile ncxFile = new() + { + FileName = NCX_FILE_NAME, + FilePathInEpubArchive = NCX_FILE_PATH, + ContentType = NCX_CONTENT_TYPE, + ContentMimeType = NCX_CONTENT_MIME_TYPE, + Content = NCX_FILE_CONTENT + }; + return new() + { + FilePath = epubFilePath, + Title = BOOK_TITLE, + Author = BOOK_AUTHOR, + AuthorList = new List { BOOK_AUTHOR }, + Description = BOOK_DESCRIPTION, + CoverImage = COVER_FILE_CONTENT, + ReadingOrder = new List() + { + chapter1File, + chapter2File + }, + Navigation = new List() + { + new EpubNavigationItem(EpubNavigationItemType.LINK) + { + Title = "Chapter 1", + Link = new EpubNavigationItemLink(CHAPTER1_FILE_NAME, CONTENT_DIRECTORY_PATH), + HtmlContentFile = chapter1File, + NestedItems = new List() + }, + new EpubNavigationItem(EpubNavigationItemType.LINK) + { + Title = "Chapter 2", + Link = new EpubNavigationItemLink(CHAPTER2_FILE_NAME, CONTENT_DIRECTORY_PATH), + HtmlContentFile = chapter2File, + NestedItems = new List() + } + }, + Schema = CreateFullExpectedEpubSchema(), + Content = new EpubContent() + { + Html = new Dictionary() + { + { + CHAPTER1_FILE_NAME, + chapter1File + }, + { + CHAPTER2_FILE_NAME, + chapter2File + }, + { + NAV_FILE_NAME, + navFile + } + }, + Css = new Dictionary() + { + { + STYLES1_FILE_NAME, + styles1File + }, + { + STYLES2_FILE_NAME, + styles2File + } + }, + Images = new Dictionary() + { + { + IMAGE1_FILE_NAME, + image1File + }, + { + IMAGE2_FILE_NAME, + image2File + }, + { + COVER_FILE_NAME, + coverFile + } + }, + Fonts = new Dictionary() + { + { + FONT1_FILE_NAME, + font1File + }, + { + FONT2_FILE_NAME, + font2File + } + }, + AllFiles = new Dictionary() + { + { + CHAPTER1_FILE_NAME, + chapter1File + }, + { + CHAPTER2_FILE_NAME, + chapter2File + }, + { + STYLES1_FILE_NAME, + styles1File + }, + { + STYLES2_FILE_NAME, + styles2File + }, + { + IMAGE1_FILE_NAME, + image1File + }, + { + IMAGE2_FILE_NAME, + image2File + }, + { + FONT1_FILE_NAME, + font1File + }, + { + FONT2_FILE_NAME, + font2File + }, + { + AUDIO_FILE_NAME, + audioFile + }, + { + NAV_FILE_NAME, + navFile + }, + { + COVER_FILE_NAME, + coverFile + }, + { + NCX_FILE_NAME, + ncxFile + } + }, + NavigationHtmlFile = navFile, + Cover = coverFile + } + }; + } + + private EpubSchema CreateFullExpectedEpubSchema() + { + EpubSchema result = new() + { + Package = new EpubPackage() + { + EpubVersion = EpubVersion.EPUB_3, + Metadata = new EpubMetadata() + { + Titles = new List() + { + BOOK_TITLE + }, + Creators = new List() + { + new EpubMetadataCreator() + { + Creator = BOOK_AUTHOR + } + }, + Subjects = new List(), + Description = BOOK_DESCRIPTION, + Publishers = new List(), + Contributors = new List(), + Dates = new List(), + Types = new List(), + Formats = new List(), + Identifiers = new List(), + Sources = new List(), + Languages = new List(), + Relations = new List(), + Coverages = new List(), + Rights = new List(), + Links = new List(), + MetaItems = new List() + }, + Manifest = new EpubManifest() + { + new EpubManifestItem() + { + Id = "item-1", + Href = CHAPTER1_FILE_NAME, + MediaType = HTML_CONTENT_MIME_TYPE + }, + new EpubManifestItem() + { + Id = "item-2", + Href = CHAPTER2_FILE_NAME, + MediaType = HTML_CONTENT_MIME_TYPE + }, + new EpubManifestItem() + { + Id = "item-3", + Href = STYLES1_FILE_NAME, + MediaType = CSS_CONTENT_MIME_TYPE + }, + new EpubManifestItem() + { + Id = "item-4", + Href = STYLES2_FILE_NAME, + MediaType = CSS_CONTENT_MIME_TYPE + }, + new EpubManifestItem() + { + Id = "item-5", + Href = IMAGE1_FILE_NAME, + MediaType = IMAGE_CONTENT_MIME_TYPE + }, + new EpubManifestItem() + { + Id = "item-6", + Href = IMAGE2_FILE_NAME, + MediaType = IMAGE_CONTENT_MIME_TYPE + }, + new EpubManifestItem() + { + Id = "item-7", + Href = FONT1_FILE_NAME, + MediaType = FONT_CONTENT_MIME_TYPE + }, + new EpubManifestItem() + { + Id = "item-8", + Href = FONT2_FILE_NAME, + MediaType = FONT_CONTENT_MIME_TYPE + }, + new EpubManifestItem() + { + Id = "item-9", + Href = AUDIO_FILE_NAME, + MediaType = AUDIO_MPEG_CONTENT_MIME_TYPE + }, + new EpubManifestItem() + { + Id = "item-toc", + Href = NAV_FILE_NAME, + MediaType = HTML_CONTENT_MIME_TYPE, + Properties = new List() + { + EpubManifestProperty.NAV + } + }, + new EpubManifestItem() + { + Id = "item-cover", + Href = COVER_FILE_NAME, + MediaType = IMAGE_CONTENT_MIME_TYPE, + Properties = new List() + { + EpubManifestProperty.COVER_IMAGE + } + }, + new EpubManifestItem() + { + Id = "ncx", + Href = NCX_FILE_NAME, + MediaType = NCX_CONTENT_MIME_TYPE + } + }, + Spine = new EpubSpine() + { + new EpubSpineItemRef() + { + Id = "itemref-1", + IdRef = "item-1", + IsLinear = true + }, + new EpubSpineItemRef() + { + Id = "itemref-2", + IdRef = "item-2", + IsLinear = true + } + }, + Guide = null + }, + Epub2Ncx = new Epub2Ncx() + { + Head = new Epub2NcxHead() + { + new Epub2NcxHeadMeta() + { + Name = "dtb:uid", + Content = BOOK_UID + } + }, + DocTitle = BOOK_TITLE, + DocAuthors = new List() + { + BOOK_AUTHOR + }, + NavMap = new Epub2NcxNavigationMap() + { + new Epub2NcxNavigationPoint() + { + Id = "navpoint-1", + NavigationLabels = new List() + { + new Epub2NcxNavigationLabel() + { + Text = "Chapter 1" + } + }, + Content = new Epub2NcxContent() + { + Source = CHAPTER1_FILE_NAME + }, + ChildNavigationPoints = new List() + }, + new Epub2NcxNavigationPoint() + { + Id = "navpoint-2", + NavigationLabels = new List() + { + new Epub2NcxNavigationLabel() + { + Text = "Chapter 2" + } + }, + Content = new Epub2NcxContent() + { + Source = CHAPTER2_FILE_NAME + }, + ChildNavigationPoints = new List() + } + }, + NavLists = new List() + }, + Epub3NavDocument = new Epub3NavDocument() + { + Navs = new List() + { + new Epub3Nav() + { + Type = Epub3NavStructuralSemanticsProperty.TOC, + Ol = new Epub3NavOl() + { + Lis = new List() + { + new Epub3NavLi() + { + Anchor = new Epub3NavAnchor() + { + Href = CHAPTER1_FILE_NAME, + Text = "Chapter 1" + } + }, + new Epub3NavLi() + { + Anchor = new Epub3NavAnchor() + { + Href = CHAPTER2_FILE_NAME, + Text = "Chapter 2" + } + } + } + } + } + } + }, + ContentDirectoryPath = CONTENT_DIRECTORY_PATH + }; + result.Package.Spine.Toc = "ncx"; + return result; + } + } +} diff --git a/Source/VersOne.Epub/Entities/EpubBook.cs b/Source/VersOne.Epub/Entities/EpubBook.cs index e1a020e..de0d769 100644 --- a/Source/VersOne.Epub/Entities/EpubBook.cs +++ b/Source/VersOne.Epub/Entities/EpubBook.cs @@ -48,13 +48,13 @@ public class EpubBook public List Navigation { get; internal set; } /// - /// Gets the collection of the book's content files. + /// Gets the parsed EPUB schema of the book. /// - public EpubContent Content { get; internal set; } + public EpubSchema Schema { get; internal set; } /// - /// Gets the parsed EPUB schema of the book. + /// Gets the collection of the book's content files. /// - public EpubSchema Schema { get; internal set; } + public EpubContent Content { get; internal set; } } } diff --git a/Source/VersOne.Epub/EpubReader.cs b/Source/VersOne.Epub/EpubReader.cs index 21f1d26..20213ae 100644 --- a/Source/VersOne.Epub/EpubReader.cs +++ b/Source/VersOne.Epub/EpubReader.cs @@ -24,7 +24,7 @@ public static class EpubReader /// EPUB book reference. This object holds a handle to the EPUB file. public static EpubBookRef OpenBook(string filePath, EpubReaderOptions epubReaderOptions = null) { - return OpenBookAsync(filePath, epubReaderOptions).Result; + return ExecuteAndUnwrapAggregateException(() => OpenBookAsync(filePath, epubReaderOptions)); } /// @@ -35,7 +35,7 @@ public static EpubBookRef OpenBook(string filePath, EpubReaderOptions epubReader /// EPUB book reference. This object holds a handle to the EPUB file. public static EpubBookRef OpenBook(Stream stream, EpubReaderOptions epubReaderOptions = null) { - return OpenBookAsync(stream, epubReaderOptions).Result; + return ExecuteAndUnwrapAggregateException(() => OpenBookAsync(stream, epubReaderOptions)); } /// @@ -46,8 +46,7 @@ public static EpubBookRef OpenBook(Stream stream, EpubReaderOptions epubReaderOp /// EPUB book reference. This object holds a handle to the EPUB file. public static Task OpenBookAsync(string filePath, EpubReaderOptions epubReaderOptions = null) { - IFileSystem fileSystem = EnvironmentDependencies.FileSystem; - if (!fileSystem.FileExists(filePath)) + if (!FileSystem.FileExists(filePath)) { throw new FileNotFoundException("Specified EPUB file not found.", filePath); } @@ -73,7 +72,7 @@ public static Task OpenBookAsync(Stream stream, EpubReaderOptions e /// EPUB book with all its content. This object does not retain a handle to the EPUB file. public static EpubBook ReadBook(string filePath, EpubReaderOptions epubReaderOptions = null) { - return ReadBookAsync(filePath, epubReaderOptions).Result; + return ExecuteAndUnwrapAggregateException(() => ReadBookAsync(filePath, epubReaderOptions)); } /// @@ -84,7 +83,7 @@ public static EpubBook ReadBook(string filePath, EpubReaderOptions epubReaderOpt /// EPUB book with all its content. This object does not retain a handle to the EPUB file. public static EpubBook ReadBook(Stream stream, EpubReaderOptions epubReaderOptions = null) { - return ReadBookAsync(stream, epubReaderOptions).Result; + return ExecuteAndUnwrapAggregateException(() => ReadBookAsync(stream, epubReaderOptions)); } /// @@ -118,9 +117,9 @@ private static async Task OpenBookAsync(IZipFile zipFile, string fi { epubReaderOptions = new EpubReaderOptions(); } + result = new EpubBookRef(zipFile); try { - result = new EpubBookRef(zipFile); result.FilePath = filePath; result.Schema = await SchemaReader.ReadSchemaAsync(zipFile, epubReaderOptions).ConfigureAwait(false); result.Title = result.Schema.Package.Metadata.Titles.FirstOrDefault() ?? String.Empty; @@ -132,7 +131,7 @@ private static async Task OpenBookAsync(IZipFile zipFile, string fi } catch { - result?.Dispose(); + result.Dispose(); throw; } } @@ -188,7 +187,14 @@ private static async Task ReadContent(EpubContentRef contentRef) { if (!result.AllFiles.ContainsKey(contentFileRef.Key)) { - result.AllFiles.Add(contentFileRef.Key, await ReadByteContentFile(contentFileRef.Value).ConfigureAwait(false)); + if (contentFileRef.Value is EpubTextContentFileRef) + { + result.AllFiles.Add(contentFileRef.Key, await ReadTextContentFile(contentFileRef.Value).ConfigureAwait(false)); + } + else + { + result.AllFiles.Add(contentFileRef.Key, await ReadByteContentFile(contentFileRef.Value).ConfigureAwait(false)); + } } } if (contentRef.Cover != null) @@ -207,15 +213,7 @@ private static async Task> ReadTextConte Dictionary result = new Dictionary(); foreach (KeyValuePair textContentFileRef in textContentFileRefs) { - EpubTextContentFile textContentFile = new EpubTextContentFile - { - FileName = textContentFileRef.Value.FileName, - FilePathInEpubArchive = textContentFileRef.Value.FilePathInEpubArchive, - ContentType = textContentFileRef.Value.ContentType, - ContentMimeType = textContentFileRef.Value.ContentMimeType - }; - textContentFile.Content = await textContentFileRef.Value.ReadContentAsTextAsync().ConfigureAwait(false); - result.Add(textContentFileRef.Key, textContentFile); + result.Add(textContentFileRef.Key, await ReadTextContentFile(textContentFileRef.Value).ConfigureAwait(false)); } return result; } @@ -230,6 +228,19 @@ private static async Task> ReadByteConte return result; } + private static async Task ReadTextContentFile(EpubContentFileRef contentFileRef) + { + EpubTextContentFile result = new EpubTextContentFile + { + FileName = contentFileRef.FileName, + FilePathInEpubArchive = contentFileRef.FilePathInEpubArchive, + ContentType = contentFileRef.ContentType, + ContentMimeType = contentFileRef.ContentMimeType + }; + result.Content = await contentFileRef.ReadContentAsTextAsync().ConfigureAwait(false); + return result; + } + private static async Task ReadByteContentFile(EpubContentFileRef contentFileRef) { EpubByteContentFile result = new EpubByteContentFile @@ -267,5 +278,17 @@ private static List ReadNavigation(EpubBook epubBook, List(Func> asyncOperation) + { + try + { + return asyncOperation().Result; + } + catch (AggregateException aggregateException) + { + throw aggregateException.InnerException; + } + } } }