From ebd915240c5ea59645d5cbe9c42b82a62caf6abd Mon Sep 17 00:00:00 2001 From: FB2Toolbox <54662093+FB2Toolbox@users.noreply.github.com> Date: Thu, 6 Nov 2025 11:59:35 +0100 Subject: [PATCH 1/2] Refactor test suite: Migrate from MSTest to xUnit - Deleted old test file `FileUtilsTests.cs` and replaced with modular test files for better organization. - Introduced new test files: `CharacterSubstitutionElementTests.cs`, `CommandElementTests.cs`, `EncodingElementTests.cs`, `FB2EncoderFallbackBufferTests.cs`, `FBEncoderFallbackTests.cs`, `FileMetadataTests.cs`, `FileOperationResultTests.cs`, `FilePropertiesTests.cs`, `GenreSubstitutionElementTests.cs`, `GenresCollectionTests.cs`, `RenameProfileElementTests.cs`. - Updated test attributes from MSTest to xUnit equivalents, replacing `[TestClass]` and `[TestMethod]` with `[Fact]` and `[Trait]`. - Enhanced assertions using AwesomeAssertions for improved readability and maintainability. - Added `packages.config` for managing NuGet dependencies, including xUnit and AwesomeAssertions. - Updated GitHub Actions workflow for .NET Framework tests to accommodate new test framework and structure. - Documented changes in `RELEASE_v1.9.1.md` for version 1.9.1 release. --- .github/workflows/dotnet-tests.yml | 69 ++++ docs/RELEASE_v1.9.1.md | 36 ++ .../CharacterSubstitutionElementTests.cs | 29 ++ .../FB2ToolboxUnitTest/CommandElementTests.cs | 35 ++ tests/FB2ToolboxUnitTest/ConfigTests.cs | 251 ------------- .../EncodingElementTests.cs | 25 ++ tests/FB2ToolboxUnitTest/FB2BookTests.cs | 83 +++-- .../FB2EncoderFallbackBufferTests.cs | 118 ++++++ .../FB2FileAdvancedTests.cs | 103 +++--- tests/FB2ToolboxUnitTest/FB2Tests.cs | 97 ++--- .../FB2ToolboxUnitTest.csproj | 73 +++- .../FBEncoderFallbackTests.cs | 41 ++ tests/FB2ToolboxUnitTest/FileMetadataTests.cs | 122 ++++++ .../FileOperationResultTests.cs | 43 +++ .../FB2ToolboxUnitTest/FilePropertiesTests.cs | 49 +++ tests/FB2ToolboxUnitTest/FileUtilsTests.cs | 349 ------------------ .../GenreSubstitutionElementTests.cs | 90 +++++ .../GenresCollectionTests.cs | 55 +++ .../RenameProfileElementTests.cs | 44 +++ tests/FB2ToolboxUnitTest/packages.config | 18 + 20 files changed, 988 insertions(+), 742 deletions(-) create mode 100644 .github/workflows/dotnet-tests.yml create mode 100644 docs/RELEASE_v1.9.1.md create mode 100644 tests/FB2ToolboxUnitTest/CharacterSubstitutionElementTests.cs create mode 100644 tests/FB2ToolboxUnitTest/CommandElementTests.cs delete mode 100644 tests/FB2ToolboxUnitTest/ConfigTests.cs create mode 100644 tests/FB2ToolboxUnitTest/EncodingElementTests.cs create mode 100644 tests/FB2ToolboxUnitTest/FB2EncoderFallbackBufferTests.cs create mode 100644 tests/FB2ToolboxUnitTest/FBEncoderFallbackTests.cs create mode 100644 tests/FB2ToolboxUnitTest/FileMetadataTests.cs create mode 100644 tests/FB2ToolboxUnitTest/FileOperationResultTests.cs create mode 100644 tests/FB2ToolboxUnitTest/FilePropertiesTests.cs delete mode 100644 tests/FB2ToolboxUnitTest/FileUtilsTests.cs create mode 100644 tests/FB2ToolboxUnitTest/GenreSubstitutionElementTests.cs create mode 100644 tests/FB2ToolboxUnitTest/GenresCollectionTests.cs create mode 100644 tests/FB2ToolboxUnitTest/RenameProfileElementTests.cs create mode 100644 tests/FB2ToolboxUnitTest/packages.config diff --git a/.github/workflows/dotnet-tests.yml b/.github/workflows/dotnet-tests.yml new file mode 100644 index 0000000..3c880d6 --- /dev/null +++ b/.github/workflows/dotnet-tests.yml @@ -0,0 +1,69 @@ +name: .NET Framework Tests + +on: + push: + paths: + - 'src/**' + - 'tests/**' + - 'FB2Toolbox.sln' + - '**/*.csproj' + - '.github/workflows/dotnet-tests.yml' + pull_request: + paths: + - 'src/**' + - 'tests/**' + - 'FB2Toolbox.sln' + - '**/*.csproj' + - '.github/workflows/dotnet-tests.yml' + +jobs: + build-and-test: + # .NET Framework projects (TargetFramework v4.8) require Windows/MSBuild + runs-on: windows-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v2 + + - name: Install NuGet + uses: NuGet/setup-nuget@v1 + + - name: Restore NuGet packages + run: nuget restore FB2Toolbox.sln + shell: pwsh + + - name: Build solution with MSBuild + run: msbuild FB2Toolbox.sln /p:Configuration=Release /m + shell: pwsh + + - name: Run unit tests with xUnit + run: | + $testDll = "tests\FB2ToolboxUnitTest\bin\x86\Release\FB2ToolboxUnitTest.dll" + + if (-Not (Test-Path $testDll)) { + Write-Error "Test assembly not found at: $testDll" + exit 1 + } + + Write-Host "Running xUnit tests from: $testDll" + + # Use VSTest with xUnit adapter (from xunit.runner.visualstudio package) + # VSTest is available on windows-latest runners + $vstestPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" + + if (-Not (Test-Path $vstestPath)) { + # Try alternative paths for different VS editions + $vstestPath = Get-ChildItem "C:\Program Files\Microsoft Visual Studio\2022" -Recurse -Filter "vstest.console.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty FullName + } + + if (-Not $vstestPath) { + Write-Error "vstest.console.exe not found" + exit 1 + } + + Write-Host "Using VSTest from: $vstestPath" + & $vstestPath $testDll /logger:"console;verbosity=normal" + shell: pwsh diff --git a/docs/RELEASE_v1.9.1.md b/docs/RELEASE_v1.9.1.md new file mode 100644 index 0000000..b8f2b23 --- /dev/null +++ b/docs/RELEASE_v1.9.1.md @@ -0,0 +1,36 @@ +Релиз 1.9.1 - 6 ноября 2025 года + +Исправлены мелкие ошибки и внесены улучшения в тестовый код. + +### Изменено +- Миграция тестов с MSTest на xUnit +- Замена фреймворка тестирования на xUnit с использованием AwesomeAssertions для более удобных и читаемых утверждений +- Рефакторинг больших тестовых файлов на более мелкие и сфокусированные модули +- Обновление атрибутов тестов (`[TestClass]`, `[TestMethod]`) на эквиваленты xUnit (`[Fact]`, `[Trait]`) +- Модернизация методов утверждений с использованием fluent-синтаксиса (например, `Should().Be`, `Should().Throw`) +- Улучшена читаемость и поддерживаемость тестового кода +- Обновление зависимостей проекта с добавлением xUnit и AwesomeAssertions + +### Добавлено +- Новые тестовые файлы для лучшей организации: + - `CharacterSubstitutionElementTests.cs` + - `CommandElementTests.cs` + - `EncodingElementTests.cs` + - `FB2EncoderFallbackBufferTests.cs` + - `FBEncoderFallbackTests.cs` + - `FileMetadataTests.cs` + - `FileOperationResultTests.cs` + - `FilePropertiesTests.cs` + - `GenreSubstitutionElementTests.cs` + - `GenresCollectionTests.cs` + - `RenameProfileElementTests.cs` +- Файл `packages.config` для управления зависимостями NuGet +- Расширенное покрытие тестами с дополнительными тестовыми случаями + +### Удалено +- Старые монолитные тестовые файлы (`ConfigTests.cs`, `FileUtilsTests.cs`) в пользу модульной структуры + +### Техническая информация +- Рефакторинг структуры тестов улучшает поддерживаемость и масштабируемость проекта +- Использование современных паттернов тестирования для повышения качества кода +- Улучшена обработка исключений в тестах diff --git a/tests/FB2ToolboxUnitTest/CharacterSubstitutionElementTests.cs b/tests/FB2ToolboxUnitTest/CharacterSubstitutionElementTests.cs new file mode 100644 index 0000000..539127a --- /dev/null +++ b/tests/FB2ToolboxUnitTest/CharacterSubstitutionElementTests.cs @@ -0,0 +1,29 @@ +using System.ComponentModel; +using AwesomeAssertions; +using FB2Toolbox; +using Xunit; + +namespace FB2ToolboxUnitTest +{ + public class CharacterSubstitutionElementTests + { + [Fact] + [Trait("Category", "Config")] + [Description("Test CharacterSubstitutionElement properties")] + public void CharacterSubstitutionElement_Properties_Work() + { + // Arrange + var element = new CharacterSubstitutionElement(); + + // Act + element.From = "?"; + element.To = "_"; + element.Repeat = 2; + + // Assert + element.From.Should().Be("?"); + element.To.Should().Be("_"); + element.Repeat.Should().Be(2); + } + } +} diff --git a/tests/FB2ToolboxUnitTest/CommandElementTests.cs b/tests/FB2ToolboxUnitTest/CommandElementTests.cs new file mode 100644 index 0000000..22460ce --- /dev/null +++ b/tests/FB2ToolboxUnitTest/CommandElementTests.cs @@ -0,0 +1,35 @@ +using System.ComponentModel; +using AwesomeAssertions; +using FB2Toolbox; +using Xunit; + +namespace FB2ToolboxUnitTest +{ + public class CommandElementTests + { + [Fact] + [Trait("Category", "Config")] + [Description("Test CommandElement properties")] + public void CommandElement_Properties_Work() + { + // Arrange + var element = new CommandElement(); + + // Act + element.Name = "Open with Notepad"; + element.FileName = "notepad.exe"; + element.Arguments = "{0}"; + element.CreateNoWindow = false; + element.OnlyWithExtension = ".fb2"; + element.WaitAndReload = true; + + // Assert + element.Name.Should().Be("Open with Notepad"); + element.FileName.Should().Be("notepad.exe"); + element.Arguments.Should().Be("{0}"); + element.CreateNoWindow.Should().BeFalse(); + element.OnlyWithExtension.Should().Be(".fb2"); + element.WaitAndReload.Should().BeTrue(); + } + } +} diff --git a/tests/FB2ToolboxUnitTest/ConfigTests.cs b/tests/FB2ToolboxUnitTest/ConfigTests.cs deleted file mode 100644 index 7956b26..0000000 --- a/tests/FB2ToolboxUnitTest/ConfigTests.cs +++ /dev/null @@ -1,251 +0,0 @@ -using FB2Toolbox; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace FB2ToolboxUnitTest -{ - [TestClass] - public class ConfigTests - { - [TestClass] - public class GenreSubstitutionElementTests - { - [TestMethod] - [TestCategory("Config")] - [Description("Test GenreSubstitutionElement properties")] - public void GenreSubstitutionElement_Properties_Work() - { - // Arrange - var element = new GenreSubstitutionElement(); - - // Act - element.From = "sf"; - element.To = "Science Fiction"; - - // Assert - Assert.AreEqual("sf", element.From); - Assert.AreEqual("Science Fiction", element.To); - } - - [TestMethod] - [TestCategory("Config")] - [Description("Test GenreSubstitutionElement ToString")] - public void GenreSubstitutionElement_ToString_ReturnsFormattedString() - { - // Arrange - var element = new GenreSubstitutionElement - { - From = "sf", - To = "Science Fiction" - }; - - // Act - string result = element.ToString(); - - // Assert - Assert.AreEqual("Science Fiction (sf)", result); - } - - [TestMethod] - [TestCategory("Config")] - [Description("Test GenreSubstitutionElement CompareTo")] - public void GenreSubstitutionElement_CompareTo_Works() - { - // Arrange - var element1 = new GenreSubstitutionElement - { - From = "sf", - To = "Science Fiction" - }; - var element2 = new GenreSubstitutionElement - { - From = "fantasy", - To = "Fantasy" - }; - - // Act - int result = element1.CompareTo(element2); - - // Assert - Assert.IsTrue(result > 0, "Science Fiction should come after Fantasy alphabetically"); - } - - [TestMethod] - [TestCategory("Config")] - [Description("Test GenreSubstitutionElement CompareTo with non-GenreSubstitutionElement")] - public void GenreSubstitutionElement_CompareTo_WithOtherType_ReturnsZero() - { - // Arrange - var element = new GenreSubstitutionElement - { - From = "sf", - To = "Science Fiction" - }; - object other = "string"; - - // Act - int result = element.CompareTo(other); - - // Assert - Assert.AreEqual(0, result); - } - } - - [TestClass] - public class EncodingElementTests - { - [TestMethod] - [TestCategory("Config")] - [Description("Test EncodingElement Name property")] - public void EncodingElement_Name_Works() - { - // Arrange - var element = new EncodingElement(); - - // Act - element.Name = "UTF-8"; - - // Assert - Assert.AreEqual("UTF-8", element.Name); - } - } - - [TestClass] - public class RenameProfileElementTests - { - [TestMethod] - [TestCategory("Config")] - [Description("Test RenameProfileElement properties")] - public void RenameProfileElement_Properties_Work() - { - // Arrange - var element = new RenameProfileElement(); - - // Act - element.Name = "Test Profile"; - element.Path = @"(AuthorLastName)\(SequenceName)"; - element.FileName = "(AuthorLastName) - (Title)"; - - // Assert - Assert.AreEqual("Test Profile", element.Name); - Assert.AreEqual(@"(AuthorLastName)\(SequenceName)", element.Path); - Assert.AreEqual("(AuthorLastName) - (Title)", element.FileName); - } - - [TestMethod] - [TestCategory("Config")] - [Description("Test RenameProfileElement CharacterSubstitution collection")] - public void RenameProfileElement_CharacterSubstitution_IsNotNull() - { - // Arrange - var element = new RenameProfileElement(); - - // Act - var substitutions = element.CharacterSubstitution; - - // Assert - Assert.IsNotNull(substitutions); - } - } - - [TestClass] - public class CharacterSubstitutionElementTests - { - [TestMethod] - [TestCategory("Config")] - [Description("Test CharacterSubstitutionElement properties")] - public void CharacterSubstitutionElement_Properties_Work() - { - // Arrange - var element = new CharacterSubstitutionElement(); - - // Act - element.From = "?"; - element.To = "_"; - element.Repeat = 2; - - // Assert - Assert.AreEqual("?", element.From); - Assert.AreEqual("_", element.To); - Assert.AreEqual(2, element.Repeat); - } - } - - [TestClass] - public class CommandElementTests - { - [TestMethod] - [TestCategory("Config")] - [Description("Test CommandElement properties")] - public void CommandElement_Properties_Work() - { - // Arrange - var element = new CommandElement(); - - // Act - element.Name = "Open with Notepad"; - element.FileName = "notepad.exe"; - element.Arguments = "{0}"; - element.CreateNoWindow = false; - element.OnlyWithExtension = ".fb2"; - element.WaitAndReload = true; - - // Assert - Assert.AreEqual("Open with Notepad", element.Name); - Assert.AreEqual("notepad.exe", element.FileName); - Assert.AreEqual("{0}", element.Arguments); - Assert.IsFalse(element.CreateNoWindow); - Assert.AreEqual(".fb2", element.OnlyWithExtension); - Assert.IsTrue(element.WaitAndReload); - } - } - - [TestClass] - public class GenresCollectionTests - { - [TestMethod] - [TestCategory("Config")] - [Description("Test GenresCollection FindSubstitution returns original if not found")] - public void GenresCollection_FindSubstitution_ReturnsOriginalWhenNotFound() - { - // Arrange - var collection = new GenresCollection(); - - // Act - string result = collection.FindSubstitution("unknown_genre"); - - // Assert - Assert.AreEqual("unknown_genre", result); - } - - [TestMethod] - [TestCategory("Config")] - [Description("Test GenresCollection FindSubstitution returns empty for null")] - public void GenresCollection_FindSubstitution_ReturnsEmptyForNull() - { - // Arrange - var collection = new GenresCollection(); - - // Act - string result = collection.FindSubstitution(null); - - // Assert - Assert.AreEqual(string.Empty, result); - } - - [TestMethod] - [TestCategory("Config")] - [Description("Test GenresCollection FindSubstitution returns empty for empty string")] - public void GenresCollection_FindSubstitution_ReturnsEmptyForEmptyString() - { - // Arrange - var collection = new GenresCollection(); - - // Act - string result = collection.FindSubstitution(string.Empty); - - // Assert - Assert.AreEqual(string.Empty, result); - } - } - } -} diff --git a/tests/FB2ToolboxUnitTest/EncodingElementTests.cs b/tests/FB2ToolboxUnitTest/EncodingElementTests.cs new file mode 100644 index 0000000..903ac5f --- /dev/null +++ b/tests/FB2ToolboxUnitTest/EncodingElementTests.cs @@ -0,0 +1,25 @@ +using System.ComponentModel; +using AwesomeAssertions; +using FB2Toolbox; +using Xunit; + +namespace FB2ToolboxUnitTest +{ + public class EncodingElementTests + { + [Fact] + [Trait("Category", "Config")] + [Description("Test EncodingElement Name property")] + public void EncodingElement_Name_Works() + { + // Arrange + var element = new EncodingElement(); + + // Act + element.Name = "UTF-8"; + + // Assert + element.Name.Should().Be("UTF-8"); + } + } +} diff --git a/tests/FB2ToolboxUnitTest/FB2BookTests.cs b/tests/FB2ToolboxUnitTest/FB2BookTests.cs index 9a7b6ea..56888a5 100644 --- a/tests/FB2ToolboxUnitTest/FB2BookTests.cs +++ b/tests/FB2ToolboxUnitTest/FB2BookTests.cs @@ -1,14 +1,15 @@ using System; +using System.ComponentModel; using FB2Toolbox.Entities; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using AwesomeAssertions; +using Xunit; namespace FB2ToolboxUnitTest { - [TestClass] public class FB2BookTests { - [TestMethod] - [TestCategory("FB2Book")] + [Fact] + [Trait("Category", "FB2Book")] [Description("Test FB2Book default constructor initializes properties correctly")] public void FB2Book_DefaultConstructor_InitializesProperties() { @@ -16,19 +17,19 @@ public void FB2Book_DefaultConstructor_InitializesProperties() var book = new FB2Book(); // Assert - Assert.AreEqual(string.Empty, book.BookFile); - Assert.AreEqual(string.Empty, book.BookAuthorFirstName); - Assert.AreEqual(string.Empty, book.BookAuthorLastName); - Assert.AreEqual(string.Empty, book.BookAuthorMiddleName); - Assert.AreEqual(string.Empty, book.BookEncoding); - Assert.AreEqual(string.Empty, book.BookTitle); - Assert.AreEqual(string.Empty, book.BookSequenceName); - Assert.IsNull(book.BookSequenceNr); - Assert.AreEqual(string.Empty, book.BookLang); + book.BookFile.Should().BeEmpty(); + book.BookAuthorFirstName.Should().BeEmpty(); + book.BookAuthorLastName.Should().BeEmpty(); + book.BookAuthorMiddleName.Should().BeEmpty(); + book.BookEncoding.Should().BeEmpty(); + book.BookTitle.Should().BeEmpty(); + book.BookSequenceName.Should().BeEmpty(); + book.BookSequenceNr.Should().BeNull(); + book.BookLang.Should().BeEmpty(); } - [TestMethod] - [TestCategory("FB2Book")] + [Fact] + [Trait("Category", "FB2Book")] [Description("Test FB2Book CompareTo by LastName")] public void FB2Book_CompareTo_ComparesByLastName() { @@ -40,11 +41,11 @@ public void FB2Book_CompareTo_ComparesByLastName() int result = book1.CompareTo(book2); // Assert - Assert.IsTrue(result < 0, "Ivanov should come before Petrov"); + result.Should().BeLessThan(0); } - [TestMethod] - [TestCategory("FB2Book")] + [Fact] + [Trait("Category", "FB2Book")] [Description("Test FB2Book CompareTo by FirstName when LastName is equal")] public void FB2Book_CompareTo_ComparesByFirstNameWhenLastNameEqual() { @@ -64,11 +65,11 @@ public void FB2Book_CompareTo_ComparesByFirstNameWhenLastNameEqual() int result = book1.CompareTo(book2); // Assert - Assert.IsTrue(result < 0, "Ivan should come before Petr"); + result.Should().BeLessThan(0); } - [TestMethod] - [TestCategory("FB2Book")] + [Fact] + [Trait("Category", "FB2Book")] [Description("Test FB2Book CompareTo by SequenceName when author names are equal")] public void FB2Book_CompareTo_ComparesBySequenceNameWhenAuthorsEqual() { @@ -90,11 +91,11 @@ public void FB2Book_CompareTo_ComparesBySequenceNameWhenAuthorsEqual() int result = book1.CompareTo(book2); // Assert - Assert.IsTrue(result < 0, "Series A should come before Series B"); + result.Should().BeLessThan(0); } - [TestMethod] - [TestCategory("FB2Book")] + [Fact] + [Trait("Category", "FB2Book")] [Description("Test FB2Book CompareTo by SequenceNr when everything else is equal")] public void FB2Book_CompareTo_ComparesBySequenceNrWhenOthersEqual() { @@ -118,11 +119,11 @@ public void FB2Book_CompareTo_ComparesBySequenceNrWhenOthersEqual() int result = book1.CompareTo(book2); // Assert - Assert.IsTrue(result < 0, "Book 1 should come before Book 2"); + result.Should().BeLessThan(0); } - [TestMethod] - [TestCategory("FB2Book")] + [Fact] + [Trait("Category", "FB2Book")] [Description("Test FB2Book CompareTo by Title when all else is equal")] public void FB2Book_CompareTo_ComparesByTitleWhenAllElseEqual() { @@ -148,11 +149,11 @@ public void FB2Book_CompareTo_ComparesByTitleWhenAllElseEqual() int result = book1.CompareTo(book2); // Assert - Assert.IsTrue(result < 0, "Book A should come before Book B"); + result.Should().BeNegative(); } - [TestMethod] - [TestCategory("FB2Book")] + [Fact] + [Trait("Category", "FB2Book")] [Description("Test FB2Book CompareTo returns 0 for equal books")] public void FB2Book_CompareTo_ReturnsZeroForEqualBooks() { @@ -178,11 +179,11 @@ public void FB2Book_CompareTo_ReturnsZeroForEqualBooks() int result = book1.CompareTo(book2); // Assert - Assert.AreEqual(0, result, "Identical books should return 0"); + result.Should().Be(0); } - [TestMethod] - [TestCategory("FB2Book")] + [Fact] + [Trait("Category", "FB2Book")] [Description("Test FB2Book CompareTo with null sequence numbers")] public void FB2Book_CompareTo_HandlesNullSequenceNumbers() { @@ -206,13 +207,12 @@ public void FB2Book_CompareTo_HandlesNullSequenceNumbers() int result = book1.CompareTo(book2); // Assert - Assert.IsTrue(result < 0, "Null sequence number should come before numbered"); + result.Should().BeNegative(); } - [TestMethod] - [TestCategory("FB2Book")] + [Fact] + [Trait("Category", "FB2Book")] [Description("Test FB2Book CompareTo throws InvalidCastException for non-IBook object")] - [ExpectedException(typeof(InvalidCastException))] public void FB2Book_CompareTo_ThrowsExceptionForInvalidType() { // Arrange @@ -220,13 +220,14 @@ public void FB2Book_CompareTo_ThrowsExceptionForInvalidType() var invalidObject = "not a book"; // Act - book.CompareTo(invalidObject); + Action act = () => book.CompareTo(invalidObject); // Assert - exception expected + act.Should().Throw(); } - [TestMethod] - [TestCategory("FB2Book")] + [Fact] + [Trait("Category", "FB2Book")] [Description("Test FB2Book CompareTo is case insensitive")] public void FB2Book_CompareTo_IsCaseInsensitive() { @@ -238,7 +239,7 @@ public void FB2Book_CompareTo_IsCaseInsensitive() int result = book1.CompareTo(book2); // Assert - Assert.AreEqual(0, result, "Comparison should be case insensitive"); + result.Should().Be(0); } } } diff --git a/tests/FB2ToolboxUnitTest/FB2EncoderFallbackBufferTests.cs b/tests/FB2ToolboxUnitTest/FB2EncoderFallbackBufferTests.cs new file mode 100644 index 0000000..4baad25 --- /dev/null +++ b/tests/FB2ToolboxUnitTest/FB2EncoderFallbackBufferTests.cs @@ -0,0 +1,118 @@ +using System; +using System.ComponentModel; +using AwesomeAssertions; +using FB2Toolbox.Utilities; +using Xunit; + +namespace FB2ToolboxUnitTest +{ + public class FB2EncoderFallbackBufferTests + { + [Fact] + [Trait("Category", "FileUtils")] + [Description("Test FB2EncoderFallbackBuffer handles unknown character")] + public void FB2EncoderFallbackBuffer_Fallback_HandlesUnknownChar() + { + // Arrange + var buffer = new FB2EncoderFallbackBuffer(); + char unknownChar = '\u2022'; // bullet point + + // Act + bool result = buffer.Fallback(unknownChar, 0); + + // Assert + result.Should().BeTrue("Fallback should return true for unknown character"); + buffer.Remaining.Should().BePositive("Should have characters to output"); + } + + [Fact] + [Trait("Category", "FileUtils")] + [Description("Test FB2EncoderFallbackBuffer GetNextChar returns correct entity")] + public void FB2EncoderFallbackBuffer_GetNextChar_ReturnsEntity() + { + // Arrange + var buffer = new FB2EncoderFallbackBuffer(); + char unknownChar = 'A'; // ASCII 65 + + // Act + buffer.Fallback(unknownChar, 0); + char firstChar = buffer.GetNextChar(); + + // Assert + firstChar.Should().Be('&', "First character should be &"); + } + + [Fact] + [Trait("Category", "FileUtils")] + [Description("Test FB2EncoderFallbackBuffer handles surrogate pair")] + public void FB2EncoderFallbackBuffer_Fallback_HandlesSurrogatePair() + { + // Arrange + var buffer = new FB2EncoderFallbackBuffer(); + char high = '\uD800'; + char low = '\uDC00'; + + // Act + bool result = buffer.Fallback(high, low, 0); + + // Assert + result.Should().BeTrue("Fallback should handle surrogate pair"); + buffer.Remaining.Should().BePositive("Should have characters to output"); + } + + [Fact] + [Trait("Category", "FileUtils")] + [Description("Test FB2EncoderFallbackBuffer throws on recursive fallback")] + public void FB2EncoderFallbackBuffer_Fallback_ThrowsOnRecursive() + { + // Arrange + var buffer = new FB2EncoderFallbackBuffer(); + + // Act + Action act = () => + { + buffer.Fallback('A', 0); + buffer.Fallback('B', 0); // Should throw + }; + + // Assert - exception expected + act.Should().Throw(); + } + + [Fact] + [Trait("Category", "FileUtils")] + [Description("Test FB2EncoderFallbackBuffer MovePrevious works")] + public void FB2EncoderFallbackBuffer_MovePrevious_Works() + { + // Arrange + var buffer = new FB2EncoderFallbackBuffer(); + buffer.Fallback('A', 0); + buffer.GetNextChar(); + buffer.GetNextChar(); + + // Act + bool result = buffer.MovePrevious(); + + // Assert + result.Should().BeTrue("MovePrevious should succeed"); + } + + [Fact] + [Trait("Category", "FileUtils")] + [Description("Test FB2EncoderFallbackBuffer Remaining property")] + public void FB2EncoderFallbackBuffer_Remaining_ReturnsCorrectCount() + { + // Arrange + var buffer = new FB2EncoderFallbackBuffer(); + buffer.Fallback('A', 0); + int initialRemaining = buffer.Remaining; + + // Act + buffer.GetNextChar(); + int afterOneChar = buffer.Remaining; + + // Assert + afterOneChar.Should().BeLessThan(initialRemaining, "Remaining should decrease"); + } + } +} diff --git a/tests/FB2ToolboxUnitTest/FB2FileAdvancedTests.cs b/tests/FB2ToolboxUnitTest/FB2FileAdvancedTests.cs index c358cce..ea3ebae 100644 --- a/tests/FB2ToolboxUnitTest/FB2FileAdvancedTests.cs +++ b/tests/FB2ToolboxUnitTest/FB2FileAdvancedTests.cs @@ -1,147 +1,148 @@ using System; +using System.ComponentModel; +using AwesomeAssertions; using FB2Toolbox.Utilities; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Xunit; namespace FB2ToolboxUnitTest { - [TestClass] public class FB2FileAdvancedTests { - [TestMethod] - [TestCategory("FB2 File")] + [Fact] + [Category("FB2 File")] [Description("Test FB2File CompareTo returns 0 for same files")] public void FB2File_CompareTo_ReturnsSameForIdenticalFiles() { // Arrange - FB2File file1 = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); - FB2File file2 = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); + var file1 = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); + var file2 = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); // Act int result = file1.CompareTo(file2); // Assert - Assert.AreEqual(0, result, "Same files should compare as equal"); + result.Should().Be(0, "Same files should compare as equal"); } - [TestMethod] - [TestCategory("FB2 File")] + [Fact] + [Category("FB2 File")] [Description("Test FB2File CompareTo throws InvalidCastException for invalid type")] - [ExpectedException(typeof(InvalidCastException))] public void FB2File_CompareTo_ThrowsExceptionForInvalidType() { // Arrange - FB2File file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); + var file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); var invalidObject = "not a file"; // Act - file.CompareTo(invalidObject); + Action act = () => file.CompareTo(invalidObject); // Assert - exception expected + act.Should().Throw("Comparing to non-FB2File should throw InvalidCastException"); } - [TestMethod] - [TestCategory("FB2 File")] + [Fact] + [Category("FB2 File")] [Description("Test FB2File ToString returns file name")] public void FB2File_ToString_ReturnsFileName() { // Arrange - FB2File file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); + var file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); // Act string result = file.ToString(); // Assert - Assert.AreEqual("Макиавелли Николо - Государь.fb2", result); + result.Should().Be("Макиавелли Николо - Государь.fb2"); } - [TestMethod] - [TestCategory("FB2 File")] + [Fact] + [Category("FB2 File")] [Description("Test FB2File IsZIP returns false for .fb2 files")] public void FB2File_IsZIP_ReturnsFalseForFB2() { // Arrange - FB2File file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); + var file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); // Act bool result = file.IsZIP(); // Assert - Assert.IsFalse(result, "FB2 file should not be identified as ZIP"); + result.Should().BeFalse("FB2 file should not be identified as ZIP"); } - [TestMethod] - [TestCategory("FB2.ZIP File")] + [Fact] + [Category("FB2 File")] [Description("Test FB2File IsZIP returns true for .fb2.zip files")] public void FB2File_IsZIP_ReturnsTrueForFB2ZIP() { // Arrange - FB2File file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2.zip"); + var file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2.zip"); // Act bool result = file.IsZIP(); // Assert - Assert.IsTrue(result, "FB2.ZIP file should be identified as ZIP"); + result.Should().BeTrue("FB2.ZIP file should be identified as ZIP"); } - [TestMethod] - [TestCategory("FB2 File")] + [Fact] + [Category("FB2 File")] [Description("Test FB2File IsZIP with filename parameter returns false for .fb2 files")] public void FB2File_IsZIP_WithFilename_ReturnsFalseForFB2() { // Arrange - FB2File file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); + var file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); string testPath = @"TestFiles\Макиавелли Николо - Государь.fb2"; // Act bool result = file.IsZIP(testPath); // Assert - Assert.IsFalse(result, "FB2 file should not be identified as ZIP"); + result.Should().BeFalse("FB2 file should not be identified as ZIP"); } - [TestMethod] - [TestCategory("FB2 File")] + [Fact] + [Category("FB2 File")] [Description("Test FB2File FileInformation property provides correct file details")] public void FB2File_FileInformation_ProvidesCorrectDetails() { // Arrange - FB2File file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); + var file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); // Act var fileInfo = file.FileInformation; // Assert - Assert.IsNotNull(fileInfo, "FileInformation should not be null"); - Assert.IsTrue(fileInfo.Exists, "File should exist"); - Assert.AreEqual("Макиавелли Николо - Государь.fb2", fileInfo.Name, "File name should match"); - Assert.IsTrue(fileInfo.Length > 0, "File should have content"); + fileInfo.Should().NotBeNull($"{nameof(file.FileInformation)} should not be null"); + fileInfo.Exists.Should().BeTrue("File should exist"); + fileInfo.Name.Should().Be("Макиавелли Николо - Государь.fb2", "File name should match"); + fileInfo.Length.Should().BePositive("File should have content"); } - [TestMethod] - [TestCategory("FB2 File")] + [Fact] + [Category("FB2 File")] [Description("Test FB2File Metadata property contains description")] public void FB2File_Metadata_ContainsDescription() { // Arrange - FB2File file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); + var file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); // Act var metadata = file.Metadata; // Assert - Assert.IsNotNull(metadata, "Metadata should not be null"); - Assert.IsNotNull(metadata.Description, "Description should not be null"); - Assert.IsTrue(metadata.Description.Length > 0, "Description should have content"); + metadata.Should().NotBeNull("Metadata should not be null"); + metadata.Description.Should().NotBeNull("Description should not be null"); + metadata.Description.Length.Should().BePositive("Description should have content"); } - [TestMethod] - [TestCategory("FB2 File")] + [Fact] + [Category("FB2 File")] [Description("Test FB2File UpdateFileInfo changes FileInformation")] public void FB2File_UpdateFileInfo_ChangesFileInformation() { // Arrange - FB2File file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); + var file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); string originalName = file.FileInformation.Name; // Act @@ -149,17 +150,17 @@ public void FB2File_UpdateFileInfo_ChangesFileInformation() string newName = file.FileInformation.Name; // Assert - Assert.AreNotEqual(originalName, newName, "File name should be different after update"); - Assert.AreEqual("Макиавелли Николо - Государь.fb2.zip", newName, "New file name should match"); + newName.Should().NotBe(originalName, "File name should be different after update"); + newName.Should().Be("Макиавелли Николо - Государь.fb2.zip", "New file name should match"); } - [TestMethod] - [TestCategory("FB2 File")] + [Fact] + [Category("FB2 File")] [Description("Test FB2File BookInternalEncoding is set via reflection")] public void FB2File_BookInternalEncoding_IsSet() { // Arrange - FB2File file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); + var file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); // Act - access via reflection since it's protected internal var type = file.GetType(); @@ -170,8 +171,8 @@ public void FB2File_BookInternalEncoding_IsSet() var value = property.GetValue(file, null) as string; // Assert - Assert.IsNotNull(value, "BookInternalEncoding should be set"); - Assert.IsTrue(value.Length > 0, "BookInternalEncoding should not be empty"); + value.Should().NotBeNull("BookInternalEncoding should be set"); + value.Length.Should().BePositive("BookInternalEncoding should not be empty"); } } } diff --git a/tests/FB2ToolboxUnitTest/FB2Tests.cs b/tests/FB2ToolboxUnitTest/FB2Tests.cs index 7538289..d61003a 100644 --- a/tests/FB2ToolboxUnitTest/FB2Tests.cs +++ b/tests/FB2ToolboxUnitTest/FB2Tests.cs @@ -1,73 +1,74 @@ -using System.Text; +using System.ComponentModel; +using System.Text; +using AwesomeAssertions; using FB2Toolbox; using FB2Toolbox.Utilities; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Xunit; namespace FB2ToolboxUnitTest { - [TestClass] public class FB2FileTest { - [TestMethod] - [TestCategory("FB2 File")] + [Fact] + [Trait("Category", "FB2 File")] [Description("Reading correct .FB2 file")] public void Read_Correct_FB2_File() { - FB2File file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); - Assert.AreEqual("Государь", file.BookTitle, "Book title must be 'Государь'"); + var file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); + file.BookTitle.Should().Be("Государь", "Book title must be 'Государь'"); - Assert.AreEqual("Николо", file.BookAuthorFirstName, "Author first name must be 'Николо'"); - Assert.AreEqual(string.Empty, file.BookAuthorMiddleName, "Author middle name must be empty string"); - Assert.AreEqual("Макиавелли", file.BookAuthorLastName, "Author last name must be 'Макиавелли'"); + file.BookAuthorFirstName.Should().Be("Николо", "Author first name must be 'Николо'"); + file.BookAuthorMiddleName.Should().Be(string.Empty, "Author middle name must be empty string"); + file.BookAuthorLastName.Should().Be("Макиавелли", "Author last name must be 'Макиавелли'"); - Assert.AreEqual(string.Empty, file.BookSequenceName, "Sequence Name must be empty string"); - Assert.IsNull(file.BookSequenceNr, "Sequence Number must be null"); + file.BookSequenceName.Should().Be(string.Empty, "Sequence Name must be empty string"); + file.BookSequenceNr.Should().BeNull("Sequence Number must be null"); - Assert.AreEqual("Европейская старинная литература", file.BookGenre, "Genre must be 'Европейская старинная литература'"); + file.BookGenre.Should().Be("Европейская старинная литература", "Genre must be 'Европейская старинная литература'"); - Assert.AreEqual("1.1", file.BookVersion, "Book version must be '1.1'"); + file.BookVersion.Should().Be("1.1", "Book version must be '1.1'"); - Assert.AreEqual("ru", file.BookLang, "Book language must be 'ru'"); + file.BookLang.Should().Be("ru", "Book language must be 'ru'"); - Encoding win1251 = Encoding.GetEncoding(1251); - Assert.AreEqual(win1251.EncodingName, file.BookEncoding, string.Format("Book encoding must be '{0}'", win1251.EncodingName)); + var win1251 = Encoding.GetEncoding(1251); + file.BookEncoding.Should().Be(win1251.EncodingName, string.Format("Book encoding must be '{0}'", win1251.EncodingName)); - Assert.AreEqual("352 Кб", file.BookSizeText, "Book size text must be '352 Кб'"); + file.BookSizeText.Should().Be("352 Кб", "Book size text must be '352 Кб'"); } - [TestMethod] - [TestCategory("FB2.ZIP File")] + [Fact] + [Trait("Category", "FB2 File")] [Description("Reading correct .FB2.ZIP file")] public void Read_Correct_FB2_ZIP_File() { - FB2File file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2.zip"); - Assert.AreEqual("Государь", file.BookTitle, "Book title must be 'Государь'"); + var file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2.zip"); + file.BookTitle.Should().Be("Государь", "Book title must be 'Государь'"); - Assert.AreEqual("Николо", file.BookAuthorFirstName, "Author first name must be 'Николо'"); - Assert.AreEqual(string.Empty, file.BookAuthorMiddleName, "Author middle name must be empty string"); - Assert.AreEqual("Макиавелли", file.BookAuthorLastName, "Author last name must be 'Макиавелли'"); + file.BookAuthorFirstName.Should().Be("Николо", "Author first name must be 'Николо'"); + file.BookAuthorMiddleName.Should().Be(string.Empty, "Author middle name must be empty string"); + file.BookAuthorLastName.Should().Be("Макиавелли", "Author last name must be 'Макиавелли'"); - Assert.AreEqual(string.Empty, file.BookSequenceName, "Sequence Name must be empty string"); - Assert.IsNull(file.BookSequenceNr, "Sequence Number must be null"); + file.BookSequenceName.Should().Be(string.Empty, "Sequence Name must be empty string"); + file.BookSequenceNr.Should().BeNull("Sequence Number must be null"); - Assert.AreEqual("Европейская старинная литература", file.BookGenre, "Genre must be 'Европейская старинная литература'"); + file.BookGenre.Should().Be("Европейская старинная литература", "Genre must be 'Европейская старинная литература'"); - Assert.AreEqual("1.1", file.BookVersion, "Book version must be '1.1'"); + file.BookVersion.Should().Be("1.1", "Book version must be '1.1'"); - Assert.AreEqual("ru", file.BookLang, "Book language must be 'ru'"); + file.BookLang.Should().Be("ru", "Book language must be 'ru'"); - Encoding win1251 = Encoding.GetEncoding(1251); - Assert.AreEqual(win1251.EncodingName, file.BookEncoding, string.Format("Book encoding must be '{0}'", win1251.EncodingName)); + var win1251 = Encoding.GetEncoding(1251); + file.BookEncoding.Should().Be(win1251.EncodingName, string.Format("Book encoding must be '{0}'", win1251.EncodingName)); - Assert.AreEqual("210 Кб", file.BookSizeText, "Book size text must be '210 Кб'"); + file.BookSizeText.Should().Be("210 Кб", "Book size text must be '210 Кб'"); } - [TestMethod] - [TestCategory("FB2 File")] + [Fact] + [Trait("Category", "FB2 File")] [Description("Renaming .FB2 file to the same name")] public void Rename_To_The_Same_Name() { - FB2File file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); + var file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); RenameProfileElement profile = null; foreach (RenameProfileElement rp in FB2Config.Current.RenameProfiles) @@ -79,37 +80,37 @@ public void Rename_To_The_Same_Name() } } - Assert.IsNotNull(profile, "Rename profile should not be null"); + profile.Should().NotBeNull("Rename profile should not be null"); if (profile != null) { var result = file.RenameTo(profile, false); - Assert.AreEqual(true, result.Skipped, "File should not be renamed"); + result.Skipped.Should().Be(true, "File should not be renamed"); } } - [TestMethod] - [TestCategory("FB2 File")] + [Fact] + [Trait("Category", "FB2 File")] [Description("Validate the .FB2 file schema")] public void Validate_Correct_FB2_File() { - FB2File file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); + var file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2"); var errors = file.ValidateSchema(); - Assert.IsNotNull(errors); - Assert.AreEqual(0, errors.Count); + errors.Should().NotBeNull(); + errors.Should().BeEmpty(); } - [TestMethod] - [TestCategory("FB2.ZIP File")] + [Fact] + [Trait("Category", "FB2 File")] [Description("Validate the .FB2.ZIP file schema")] public void Validate_Correct_FB2_ZIP_File() { - FB2File file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2.zip"); + var file = new FB2File(@"TestFiles\Макиавелли Николо - Государь.fb2.zip"); var errors = file.ValidateSchema(); - Assert.IsNotNull(errors); - Assert.AreEqual(0, errors.Count); + errors.Should().NotBeNull(); + errors.Should().BeEmpty(); } } diff --git a/tests/FB2ToolboxUnitTest/FB2ToolboxUnitTest.csproj b/tests/FB2ToolboxUnitTest/FB2ToolboxUnitTest.csproj index 49f8ece..d6e2248 100644 --- a/tests/FB2ToolboxUnitTest/FB2ToolboxUnitTest.csproj +++ b/tests/FB2ToolboxUnitTest/FB2ToolboxUnitTest.csproj @@ -1,5 +1,7 @@  + + Debug AnyCPU @@ -17,6 +19,8 @@ False UnitTest + + true @@ -57,8 +61,49 @@ MinimumRecommendedRules.ruleset + + ..\..\packages\AwesomeAssertions.9.3.0\lib\net47\AwesomeAssertions.dll + + + + ..\..\packages\Microsoft.TestPlatform.ObjectModel.17.13.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll + + + ..\..\packages\Microsoft.TestPlatform.ObjectModel.17.13.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll + + + ..\..\packages\Microsoft.TestPlatform.ObjectModel.17.13.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + + + ..\..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll + + + ..\..\packages\System.Reflection.Metadata.1.6.0\lib\netstandard2.0\System.Reflection.Metadata.dll + + + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + + + ..\..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll + + + ..\..\packages\xunit.assert.2.9.3\lib\netstandard1.1\xunit.assert.dll + + + ..\..\packages\xunit.extensibility.core.2.9.3\lib\net452\xunit.core.dll + + + ..\..\packages\xunit.extensibility.execution.2.9.3\lib\net452\xunit.execution.desktop.dll + @@ -73,12 +118,21 @@ - + + + + - + + + + + + + @@ -90,6 +144,7 @@ + @@ -97,6 +152,11 @@ FB2Toolbox + + + + + @@ -117,6 +177,15 @@ + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + +