1
1
懒得勤快 9 сар өмнө
parent
commit
cb047a081b
73 өөрчлөгдсөн 6899 нэмэгдсэн , 262 устгасан
  1. 241 0
      Test/Masuit.Tools.Abstractions.Test/DateTimeExt/ChineseCalendarTest.cs
  2. 66 0
      Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/DoubleExtensionsTests.cs
  3. 295 0
      Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/IDictionaryExtensionsTests.cs
  4. 242 1
      Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/IEnumerableTest.cs
  5. 13 0
      Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/IPAddressExtensionsTest.cs
  6. 16 1
      Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/NumberFormaterExtensionsTest.cs
  7. 125 0
      Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/ObjectExtensionsTests.cs
  8. 20 0
      Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/ShortExtensionsTests.cs
  9. 173 0
      Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/StreamExtensionsTests.cs
  10. 358 0
      Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/StringExtensionsTest.cs
  11. 147 0
      Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/ValueTypeConvertExtensionsTests.cs
  12. 39 2
      Test/Masuit.Tools.Abstractions.Test/Extensions/DynamicObjectTest.cs
  13. 110 0
      Test/Masuit.Tools.Abstractions.Test/Files/FileExtTests.cs
  14. 85 0
      Test/Masuit.Tools.Abstractions.Test/Files/FileSignatureDetectorTests.cs
  15. 180 0
      Test/Masuit.Tools.Abstractions.Test/Files/IniFileTests.cs
  16. 111 0
      Test/Masuit.Tools.Abstractions.Test/Files/SevenZipCompressorTests.cs
  17. 67 0
      Test/Masuit.Tools.Abstractions.Test/Files/TextEncodingDetectorTests.cs
  18. 24 38
      Test/Masuit.Tools.Abstractions.Test/Hardware/SystemInfo_UnitTest.cs
  19. 100 0
      Test/Masuit.Tools.Abstractions.Test/Html/HtmlToolsTests.cs
  20. 137 0
      Test/Masuit.Tools.Abstractions.Test/Linq/LinqExtensionTests.cs
  21. 159 0
      Test/Masuit.Tools.Abstractions.Test/Logging/LogManagerTests.cs
  22. 6 0
      Test/Masuit.Tools.Abstractions.Test/Masuit.Tools.Abstractions.Test.csproj
  23. 27 0
      Test/Masuit.Tools.Abstractions.Test/Maths/CoordinateConvertTests.cs
  24. 38 0
      Test/Masuit.Tools.Abstractions.Test/Maths/PercentileSelectorTests.cs
  25. 54 0
      Test/Masuit.Tools.Abstractions.Test/Maths/RadarChartEngineTests.cs
  26. 76 0
      Test/Masuit.Tools.Abstractions.Test/Media/ImageDetectExtTests.cs
  27. 93 0
      Test/Masuit.Tools.Abstractions.Test/Media/ImageHasherTests.cs
  28. 164 0
      Test/Masuit.Tools.Abstractions.Test/Media/ImageUtilitiesTests.cs
  29. 68 0
      Test/Masuit.Tools.Abstractions.Test/Media/ImageWatermarkerTests.cs
  30. 102 0
      Test/Masuit.Tools.Abstractions.Test/Mime/MimeMapperTests.cs
  31. 130 0
      Test/Masuit.Tools.Abstractions.Test/Models/SphereTests.cs
  32. 116 0
      Test/Masuit.Tools.Abstractions.Test/RandomSelector/ExtensionsTests.cs
  33. 146 0
      Test/Masuit.Tools.Abstractions.Test/Refection/ReflectionUtilTests.cs
  34. 89 0
      Test/Masuit.Tools.Abstractions.Test/Security/Crc32Tests.cs
  35. 74 0
      Test/Masuit.Tools.Abstractions.Test/Security/Crc64Tests.cs
  36. 43 0
      Test/Masuit.Tools.Abstractions.Test/Security/EncryptTests.cs
  37. 48 0
      Test/Masuit.Tools.Abstractions.Test/Security/ZeroWidthCodecTests.cs
  38. 95 0
      Test/Masuit.Tools.Abstractions.Test/Strings/SimHashTests.cs
  39. 79 0
      Test/Masuit.Tools.Abstractions.Test/Strings/ValidateCodeTests.cs
  40. 198 0
      Test/Masuit.Tools.Abstractions.Test/Systems/ConcurrentHashSetTests.cs
  41. 82 0
      Test/Masuit.Tools.Abstractions.Test/Systems/ConcurrentLimitedQueueTests.cs
  42. 78 0
      Test/Masuit.Tools.Abstractions.Test/Systems/DisposableConcurrentDictionaryTests.cs
  43. 112 0
      Test/Masuit.Tools.Abstractions.Test/Systems/DisposableDictionaryTests.cs
  44. 92 0
      Test/Masuit.Tools.Abstractions.Test/Systems/EnumExtTests.cs
  45. 139 0
      Test/Masuit.Tools.Abstractions.Test/Systems/LargeMemoryStreamTests.cs
  46. 112 0
      Test/Masuit.Tools.Abstractions.Test/Systems/NullableConcurrentDictionaryTests.cs
  47. 90 0
      Test/Masuit.Tools.Abstractions.Test/Systems/NullableDictionaryTests.cs
  48. 102 0
      Test/Masuit.Tools.Abstractions.Test/Systems/PooledMemoryStreamTests.cs
  49. 144 0
      Test/Masuit.Tools.Abstractions.Test/Systems/SnowFlakeNewTests.cs
  50. 122 0
      Test/Masuit.Tools.Abstractions.Test/Systems/SnowFlakeTests.cs
  51. 30 0
      Test/Masuit.Tools.Abstractions.Test/Systems/TestEnum.cs
  52. 39 0
      Test/Masuit.Tools.Abstractions.Test/TextDiff/ExtensionsTests.cs
  53. 74 0
      Test/Masuit.Tools.Abstractions.Test/TextDiff/TextDifferTests.cs
  54. 10 1
      Test/Masuit.Tools.Abstractions.Test/Tree/TreeTest.cs
  55. 135 0
      Test/Masuit.Tools.Abstractions.Test/Validator/ComplexPasswordAttributeTests.cs
  56. 58 0
      Test/Masuit.Tools.Abstractions.Test/Validator/IsEmailAttributeTests.cs
  57. 28 0
      Test/Masuit.Tools.Abstractions.Test/Validator/IsIPAddressAttributeTests.cs
  58. 92 0
      Test/Masuit.Tools.Abstractions.Test/Validator/IsPhoneAttributeTests.cs
  59. 92 0
      Test/Masuit.Tools.Abstractions.Test/Validator/LandlineAttributeTests.cs
  60. 64 0
      Test/Masuit.Tools.Abstractions.Test/Validator/MaxValueAttributeTests.cs
  61. 103 0
      Test/Masuit.Tools.Abstractions.Test/Validator/MinItemsCountAttributeTests.cs
  62. 64 0
      Test/Masuit.Tools.Abstractions.Test/Validator/MinValueAttributeTests.cs
  63. 47 0
      Test/Masuit.Tools.Abstractions.Test/Validator/TestEnum.cs
  64. 92 0
      Test/Masuit.Tools.Abstractions.Test/Validator/UnifiedSocialCreditCodeAttributeTests.cs
  65. 1 1
      Test/Masuit.Tools.AspNetCore.ResumeFileResults.WebTest/Startup.cs
  66. 293 0
      Test/Masuit.Tools.Core.Test/AspNetCore/DbContextExtTest.cs
  67. 128 0
      Test/Masuit.Tools.Core.Test/AspNetCore/IQueryableExtTest.cs
  68. 3 1
      Test/Masuit.Tools.Core.Test/AspNetCore/Startup.cs
  69. 0 5
      Test/Masuit.Tools.Core.Test/AspNetCore/TestBase.cs
  70. 109 0
      Test/Masuit.Tools.Core.Test/Controllers/TestController.cs
  71. 2 1
      Test/Masuit.Tools.Core.Test/Masuit.Tools.Core.Test.csproj
  72. 8 0
      Test/Masuit.Tools.Test/Masuit.Tools.Test.csproj
  73. 0 211
      Test/Masuit.Tools.Test/Mvc/ResumeFileResultTests.cs

+ 241 - 0
Test/Masuit.Tools.Abstractions.Test/DateTimeExt/ChineseCalendarTest.cs

@@ -26,4 +26,245 @@ public class ChineseCalendarTest
         Assert.Equal(new ChineseCalendar(new DateTime(2024, 1, 1)).PrevDay, new ChineseCalendar(new DateTime(2023, 12, 31)));
         Assert.Equal(new ChineseCalendar(new DateTime(2024, 1, 1)).WeekDay, DayOfWeek.Monday);
     }
+
+    [Fact]
+    public void Constructor_ShouldInitializeWithGregorianDate()
+    {
+        // Arrange
+        var date = new DateTime(2023, 10, 1);
+
+        // Act
+        var calendar = new ChineseCalendar(date);
+
+        // Assert
+        Assert.Equal(date, calendar.Date);
+    }
+
+    [Fact]
+    public void Constructor_ShouldInitializeWithLunarDate()
+    {
+        // Arrange
+        var year = 2023;
+        var month = 8;
+        var day = 15;
+
+        // Act
+        var calendar = new ChineseCalendar(year, month, day);
+
+        // Assert
+        Assert.Equal(year, calendar.ChineseYear);
+        Assert.Equal(month, calendar.ChineseMonth);
+        Assert.Equal(day, calendar.ChineseDay);
+    }
+
+    [Fact]
+    public void IsHoliday_ShouldReturnTrueForHoliday()
+    {
+        // Arrange
+        var date = new DateTime(2023, 10, 1); // 国庆节
+        var calendar = new ChineseCalendar(date);
+
+        // Act
+        var isHoliday = calendar.IsHoliday;
+
+        // Assert
+        Assert.True(isHoliday);
+    }
+
+    [Fact]
+    public void IsWorkDay_ShouldReturnFalseForHoliday()
+    {
+        // Arrange
+        var date = new DateTime(2023, 10, 1); // 国庆节
+        var calendar = new ChineseCalendar(date);
+
+        // Act
+        var isWorkDay = calendar.IsWorkDay;
+
+        // Assert
+        Assert.False(isWorkDay);
+    }
+
+    [Fact]
+    public void IsWorkDay_ShouldReturnTrueForWorkDay()
+    {
+        // Arrange
+        var date = new DateTime(2023, 10, 9); // 普通工作日
+        var calendar = new ChineseCalendar(date);
+
+        // Act
+        var isWorkDay = calendar.IsWorkDay;
+
+        // Assert
+        Assert.True(isWorkDay);
+    }
+
+    [Fact]
+    public void AddDays_ShouldReturnCorrectDate()
+    {
+        // Arrange
+        var date = new DateTime(2023, 10, 1);
+        var calendar = new ChineseCalendar(date);
+
+        // Act
+        var newCalendar = calendar.AddDays(10);
+
+        // Assert
+        Assert.Equal(new DateTime(2023, 10, 11), newCalendar.Date);
+    }
+
+    [Fact]
+    public void AddMonths_ShouldReturnCorrectDate()
+    {
+        // Arrange
+        var date = new DateTime(2023, 10, 1);
+        var calendar = new ChineseCalendar(date);
+
+        // Act
+        var newCalendar = calendar.AddMonths(2);
+
+        // Assert
+        Assert.Equal(new DateTime(2023, 12, 1), newCalendar.Date);
+    }
+
+    [Fact]
+    public void Equals_ShouldReturnTrueForSameDate()
+    {
+        // Arrange
+        var date = new DateTime(2023, 10, 1);
+        var calendar1 = new ChineseCalendar(date);
+        var calendar2 = new ChineseCalendar(date);
+
+        // Act
+        var isEqual = calendar1.Equals(calendar2);
+
+        // Assert
+        Assert.True(isEqual);
+    }
+
+    [Fact]
+    public void Equals_ShouldReturnFalseForDifferentDate()
+    {
+        // Arrange
+        var calendar1 = new ChineseCalendar(new DateTime(2023, 10, 1));
+        var calendar2 = new ChineseCalendar(new DateTime(2023, 10, 2));
+
+        // Act
+        var isEqual = calendar1.Equals(calendar2);
+
+        // Assert
+        Assert.False(isEqual);
+    }
+
+    [Fact]
+    public void ChineseCalendarHoliday_ShouldReturnCorrectHoliday()
+    {
+        // Arrange
+        var date = new DateTime(2023, 1, 22); // 农历春节
+        var calendar = new ChineseCalendar(date);
+
+        // Act
+        var holiday = calendar.ChineseCalendarHoliday;
+
+        // Assert
+        Assert.Equal("春节", holiday);
+    }
+
+    [Fact]
+    public void WeekDayHoliday_ShouldReturnCorrectHoliday()
+    {
+        // Arrange
+        var date = new DateTime(2023, 5, 14); // 母亲节
+        var calendar = new ChineseCalendar(date);
+
+        // Act
+        var holiday = calendar.WeekDayHoliday;
+
+        // Assert
+        Assert.Equal("母亲节", holiday);
+    }
+
+    [Fact]
+    public void DateHoliday_ShouldReturnCorrectHoliday()
+    {
+        // Arrange
+        var date = new DateTime(2023, 10, 1); // 国庆节
+        var calendar = new ChineseCalendar(date);
+
+        // Act
+        var holiday = calendar.DateHoliday;
+
+        // Assert
+        Assert.True(holiday.Contains("国庆节"));
+    }
+
+    [Fact]
+    public void Constellation_ShouldReturnCorrectConstellation()
+    {
+        // Arrange
+        var date = new DateTime(2023, 4, 20); // 金牛座
+        var calendar = new ChineseCalendar(date);
+
+        // Act
+        var constellation = calendar.Constellation;
+
+        // Assert
+        Assert.Equal("金牛座", constellation);
+    }
+
+    [Fact]
+    public void AnimalString_ShouldReturnCorrectAnimal()
+    {
+        // Arrange
+        var date = new DateTime(2023, 1, 22); // 兔年
+        var calendar = new ChineseCalendar(date);
+
+        // Act
+        var animal = calendar.AnimalString;
+
+        // Assert
+        Assert.Equal("兔", animal);
+    }
+
+    [Fact]
+    public void GanZhiYearString_ShouldReturnCorrectGanZhiYear()
+    {
+        // Arrange
+        var date = new DateTime(2023, 1, 22); // 癸卯年
+        var calendar = new ChineseCalendar(date);
+
+        // Act
+        var ganZhiYear = calendar.GanZhiYearString;
+
+        // Assert
+        Assert.Equal("癸卯年", ganZhiYear);
+    }
+
+    [Fact]
+    public void GanZhiMonthString_ShouldReturnCorrectGanZhiMonth()
+    {
+        // Arrange
+        var date = new DateTime(2023, 1, 22); // 甲寅月
+        var calendar = new ChineseCalendar(date);
+
+        // Act
+        var ganZhiMonth = calendar.GanZhiMonthString;
+
+        // Assert
+        Assert.Equal("甲寅月", ganZhiMonth);
+    }
+
+    [Fact]
+    public void GanZhiDayString_ShouldReturnCorrectGanZhiDay()
+    {
+        // Arrange
+        var date = new DateTime(2023, 1, 22);
+        var calendar = new ChineseCalendar(date);
+
+        // Act
+        var ganZhiDay = calendar.GanZhiDayString;
+
+        // Assert
+        Assert.Equal("庚辰日", ganZhiDay);
+    }
 }

+ 66 - 0
Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/DoubleExtensionsTests.cs

@@ -0,0 +1,66 @@
+using System;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Extensions.BaseType;
+
+public class DoubleExtensionsTests
+{
+    [Theory]
+    [InlineData(0.0, 0.0)]
+    [InlineData(1.23, 1.23)]
+    [InlineData(-1.23, -1.23)]
+    [InlineData(123456789.123456789, 123456789.123456789)]
+    public void ToDecimal_FromDouble_ShouldReturnCorrectDecimal(double value, decimal expected)
+    {
+        // Act
+        var result = value.ToDecimal();
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData(1.2345, 2, MidpointRounding.AwayFromZero, 1.23)]
+    [InlineData(1.2355, 2, MidpointRounding.AwayFromZero, 1.24)]
+    [InlineData(1.2345, 2, MidpointRounding.ToEven, 1.23)]
+    [InlineData(1.2355, 2, MidpointRounding.ToEven, 1.24)]
+    [InlineData(-1.2345, 2, MidpointRounding.AwayFromZero, -1.23)]
+    [InlineData(-1.2355, 2, MidpointRounding.AwayFromZero, -1.24)]
+    public void ToDecimal_FromDouble_WithPrecision_ShouldReturnCorrectDecimal(double value, int precision, MidpointRounding mode, decimal expected)
+    {
+        // Act
+        var result = value.ToDecimal(precision, mode);
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData(0.0f, 0.0)]
+    [InlineData(1.23f, 1.23)]
+    [InlineData(-1.23f, -1.23)]
+    public void ToDecimal_FromFloat_ShouldReturnCorrectDecimal(float value, decimal expected)
+    {
+        // Act
+        var result = value.ToDecimal();
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData(1.2345f, 2, MidpointRounding.AwayFromZero, 1.23)]
+    [InlineData(1.2355f, 2, MidpointRounding.AwayFromZero, 1.24)]
+    [InlineData(1.2345f, 2, MidpointRounding.ToEven, 1.23)]
+    [InlineData(1.2355f, 2, MidpointRounding.ToEven, 1.24)]
+    [InlineData(-1.2345f, 2, MidpointRounding.AwayFromZero, -1.23)]
+    [InlineData(-1.2355f, 2, MidpointRounding.AwayFromZero, -1.24)]
+    public void ToDecimal_FromFloat_WithPrecision_ShouldReturnCorrectDecimal(float value, int precision, MidpointRounding mode, decimal expected)
+    {
+        // Act
+        var result = value.ToDecimal(precision, mode);
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+}

+ 295 - 0
Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/IDictionaryExtensionsTests.cs

@@ -0,0 +1,295 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Extensions.BaseType;
+
+public class IDictionaryExtensionsTests
+{
+    [Fact]
+    public void AddOrUpdate_ShouldAddOrUpdateValues()
+    {
+        // Arrange
+        var dict = new Dictionary<string, int> { { "key1", 1 } };
+        var newDict = new Dictionary<string, int> { { "key1", 2 }, { "key2", 3 } };
+
+        // Act
+        dict.AddOrUpdate(newDict);
+
+        // Assert
+        Assert.Equal(2, dict["key1"]);
+        Assert.Equal(3, dict["key2"]);
+    }
+
+    [Fact]
+    public void AddOrUpdate_WithFactory_ShouldAddOrUpdateValues()
+    {
+        // Arrange
+        var dict = new Dictionary<string, int> { { "key1", 1 } };
+
+        // Act
+        dict.AddOrUpdate("key1", 2, (key, oldValue) => oldValue + 1);
+        dict.AddOrUpdate("key2", 3, (key, oldValue) => oldValue + 1);
+
+        // Assert
+        Assert.Equal(2, dict["key1"]);
+        Assert.Equal(3, dict["key2"]);
+    }
+
+    [Fact]
+    public void AddOrUpdate_WithValue_ShouldAddOrUpdateValues()
+    {
+        // Arrange
+        var dict = new Dictionary<string, int> { { "key1", 1 } };
+
+        // Act
+        dict.AddOrUpdate("key1", 2, 3);
+        dict.AddOrUpdate("key2", 3, 4);
+
+        // Assert
+        Assert.Equal(3, dict["key1"]);
+        Assert.Equal(3, dict["key2"]);
+    }
+
+    [Fact]
+    public async Task AddOrUpdateAsync_ShouldAddOrUpdateValues()
+    {
+        // Arrange
+        var dict = new Dictionary<string, int> { { "key1", 1 } };
+
+        // Act
+        await dict.AddOrUpdateAsync("key1", 2, async (key, oldValue) => await Task.FromResult(oldValue + 1));
+        await dict.AddOrUpdateAsync("key2", 3, async (key, oldValue) => await Task.FromResult(oldValue + 1));
+
+        // Assert
+        Assert.Equal(2, dict["key1"]);
+        Assert.Equal(3, dict["key2"]);
+    }
+
+    [Fact]
+    public void GetOrAdd_ShouldReturnExistingOrAddNewValue()
+    {
+        // Arrange
+        var dict = new Dictionary<string, int> { { "key1", 1 } };
+
+        // Act
+        var value1 = dict.GetOrAdd("key1", () => 2);
+        var value2 = dict.GetOrAdd("key2", () => 3);
+
+        // Assert
+        Assert.Equal(1, value1);
+        Assert.Equal(3, value2);
+    }
+
+    [Fact]
+    public async Task GetOrAddAsync_ShouldReturnExistingOrAddNewValue()
+    {
+        // Arrange
+        var dict = new Dictionary<string, int> { { "key1", 1 } };
+
+        // Act
+        var value1 = await dict.GetOrAddAsync("key1", async () => await Task.FromResult(2));
+        var value2 = await dict.GetOrAddAsync("key2", async () => await Task.FromResult(3));
+
+        // Assert
+        Assert.Equal(1, value1);
+        Assert.Equal(3, value2);
+    }
+
+    [Fact]
+    public void ToDictionarySafety_ShouldConvertToDictionary()
+    {
+        // Arrange
+        var list = new List<string> { "a", "b", "c" };
+
+        // Act
+        var dict = list.ToDictionarySafety(x => x, x => x.ToUpper());
+
+        // Assert
+        Assert.Equal("A", dict["a"]);
+        Assert.Equal("B", dict["b"]);
+        Assert.Equal("C", dict["c"]);
+    }
+
+    [Fact]
+    public async Task ToDictionarySafetyAsync_ShouldConvertToDictionary()
+    {
+        // Arrange
+        var list = new List<string> { "a", "b", "c" };
+
+        // Act
+        var dict = await list.ToDictionarySafetyAsync(x => x, async x => await Task.FromResult(x.ToUpper()));
+
+        // Assert
+        Assert.Equal("A", dict["a"]);
+        Assert.Equal("B", dict["b"]);
+        Assert.Equal("C", dict["c"]);
+    }
+
+    [Fact]
+    public void ToConcurrentDictionary_ShouldConvertToConcurrentDictionary()
+    {
+        // Arrange
+        var list = new List<string> { "a", "b", "c" };
+
+        // Act
+        var dict = list.ToConcurrentDictionary(x => x, x => x.ToUpper());
+
+        // Assert
+        Assert.Equal("A", dict["a"]);
+        Assert.Equal("B", dict["b"]);
+        Assert.Equal("C", dict["c"]);
+    }
+
+    [Fact]
+    public async Task ToConcurrentDictionaryAsync_ShouldConvertToConcurrentDictionary()
+    {
+        // Arrange
+        var list = new List<string> { "a", "b", "c" };
+
+        // Act
+        var dict = await list.ToConcurrentDictionaryAsync(x => x, async x => await Task.FromResult(x.ToUpper()));
+
+        // Assert
+        Assert.Equal("A", dict["a"]);
+        Assert.Equal("B", dict["b"]);
+        Assert.Equal("C", dict["c"]);
+    }
+
+    [Fact]
+    public void ToDisposableDictionarySafety_ShouldConvertToDisposableDictionary()
+    {
+        // Arrange
+        var list = new List<DisposableObject> { new DisposableObject("a"), new DisposableObject("b"), new DisposableObject("c") };
+
+        // Act
+        var dict = list.ToDisposableDictionarySafety(x => x.Name);
+
+        // Assert
+        Assert.Equal("a", dict["a"].Name);
+        Assert.Equal("b", dict["b"].Name);
+        Assert.Equal("c", dict["c"].Name);
+    }
+
+    [Fact]
+    public async Task ToDisposableDictionarySafetyAsync_ShouldConvertToDisposableDictionary()
+    {
+        // Arrange
+        var list = new List<DisposableObject> { new DisposableObject("a"), new DisposableObject("b"), new DisposableObject("c") };
+
+        // Act
+        var dict = await list.ToDisposableDictionarySafetyAsync(x => x.Name, async x => await Task.FromResult(x));
+
+        // Assert
+        Assert.Equal("a", dict["a"].Name);
+        Assert.Equal("b", dict["b"].Name);
+        Assert.Equal("c", dict["c"].Name);
+    }
+
+    [Fact]
+    public void ToLookupX_ShouldConvertToLookup()
+    {
+        // Arrange
+        var list = new List<string> { "a", "b", "a" };
+
+        // Act
+        var lookup = list.ToLookupX(x => x);
+
+        // Assert
+        Assert.Equal(2, lookup["a"].Count);
+        Assert.Single(lookup["b"]);
+    }
+
+    [Fact]
+    public async Task ToLookupAsync_ShouldConvertToLookup()
+    {
+        // Arrange
+        var list = new List<string> { "a", "b", "a" };
+
+        // Act
+        var lookup = await list.ToLookupAsync(x => x, async x => await Task.FromResult(x.ToUpper()));
+
+        // Assert
+        Assert.Equal(2, lookup["a"].Count);
+        Assert.Single(lookup["b"]);
+    }
+
+    [Fact]
+    public void AsConcurrentDictionary_ShouldConvertToConcurrentDictionary()
+    {
+        // Arrange
+        var dict = new Dictionary<string, int> { { "key1", 1 }, { "key2", 2 } };
+
+        // Act
+        var concurrentDict = dict.AsConcurrentDictionary();
+
+        // Assert
+        Assert.Equal(1, concurrentDict["key1"]);
+        Assert.Equal(2, concurrentDict["key2"]);
+    }
+
+    [Fact]
+    public void AsConcurrentDictionary_WithDefaultValue_ShouldConvertToConcurrentDictionary()
+    {
+        // Arrange
+        var dict = new Dictionary<string, int> { { "key1", 1 }, { "key2", 2 } };
+
+        // Act
+        var concurrentDict = dict.AsConcurrentDictionary(0);
+
+        // Assert
+        Assert.Equal(1, concurrentDict["key1"]);
+        Assert.Equal(2, concurrentDict["key2"]);
+        Assert.Equal(0, concurrentDict["key3"]);
+    }
+
+    [Fact]
+    public void AsDictionary_ShouldConvertToDictionary()
+    {
+        // Arrange
+        var concurrentDict = new ConcurrentDictionary<string, int>();
+        concurrentDict["key1"] = 1;
+        concurrentDict["key2"] = 2;
+
+        // Act
+        var dict = concurrentDict.AsDictionary();
+
+        // Assert
+        Assert.Equal(1, dict["key1"]);
+        Assert.Equal(2, dict["key2"]);
+    }
+
+    [Fact]
+    public void AsDictionary_WithDefaultValue_ShouldConvertToDictionary()
+    {
+        // Arrange
+        var concurrentDict = new ConcurrentDictionary<string, int>();
+        concurrentDict["key1"] = 1;
+        concurrentDict["key2"] = 2;
+
+        // Act
+        var dict = concurrentDict.AsDictionary(0);
+
+        // Assert
+        Assert.Equal(1, dict["key1"]);
+        Assert.Equal(2, dict["key2"]);
+        Assert.Equal(0, dict["key3"]);
+    }
+
+    private class DisposableObject : IDisposable
+    {
+        public string Name { get; }
+
+        public DisposableObject(string name)
+        {
+            Name = name;
+        }
+
+        public void Dispose()
+        {
+            // Dispose logic here
+        }
+    }
+}

+ 242 - 1
Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/IEnumerableTest.cs

@@ -1,5 +1,8 @@
-using System.Collections.Generic;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
 using System.Linq;
+using System.Threading.Tasks;
+using Masuit.Tools.Abstractions.Test.Tree;
 using Xunit;
 
 namespace Masuit.Tools.Abstractions.Test.Extensions.BaseType;
@@ -32,4 +35,242 @@ public class IEnumerableTest
         // assert
         Assert.Equal(enumerable.Count(), 3);
     }
+
+    [Fact]
+    public void Can_ChangeIndex()
+    {
+        // arrange
+        var list = new List<string>()
+        {
+            "a","c","d","b"
+        };
+
+        // act
+        list.ChangeIndex("b", 1);
+
+        // assert
+        Assert.Equal(list[3], "d");
+    }
+
+    [Fact]
+    public void Can_CompareChanges()
+    {
+        // arrange
+        var list1 = new List<MyClass3>()
+        {
+            new MyClass3
+            {
+                Id = 0,
+            },
+            new MyClass3()
+            {
+                Id = 1,
+            }
+        };
+        var list2 = new List<MyClass3>()
+        {
+            new MyClass3()
+            {
+                Id = 1,
+            },
+            new MyClass3()
+            {
+                Id = 2,
+            },
+        };
+
+        // act
+        var (adds, remove, updates) = list1.CompareChanges(list2);
+        var (adds1, remove1, updates1) = list1.CompareChanges(list2, c => c.Id);
+        var (adds2, remove2, updates2) = list1.CompareChanges(list2, c => c.Id, c => c.Id);
+        var (adds3, remove3, updates3) = list1.CompareChanges(list2, (x, y) => x.Id == y.Id);
+        var (adds4, remove4, updates4) = list1.CompareChangesPlus(list2);
+        var (adds5, remove5, updates5) = list1.CompareChangesPlus(list2, c => c.Id);
+        var (adds6, remove6, updates6) = list1.CompareChangesPlus(list2, c => c.Id, c => c.Id);
+        var (adds7, remove7, updates7) = list1.CompareChangesPlus(list2, (x, y) => x.Id == y.Id);
+
+        // assert
+        Assert.True(new[] { adds, adds1, adds2, adds3, adds4, adds5, adds6, adds7 }.All(x => x.Count == 1));
+        Assert.True(new[] { remove, remove1, remove2, remove3, remove4, remove5, remove6, remove7 }.All(x => x.Count == 1));
+        Assert.True(new[] { updates, updates1, updates2, updates3 }.All(x => x.Count == 1));
+        Assert.True(new[] { updates4, updates5, updates6, updates7 }.All(x => x.Count == 1));
+    }
+
+    [Fact]
+    public void IntersectBy_WithCondition_ReturnsCorrectResult()
+    {
+        var first = new[] { 1, 2, 3, 4 };
+        var second = new[] { 3, 4, 5, 6 };
+        var result = first.IntersectBy(second, (a, b) => a == b);
+        Assert.Equal(new[] { 3, 4 }, result);
+    }
+
+    [Fact]
+    public void IntersectBy_WithKeySelector_ReturnsCorrectResult()
+    {
+        var first = new[] { 1, 2, 3, 4 };
+        var second = new[] { 3, 4, 5, 6 };
+        var result = first.IntersectBy(second, x => x);
+        Assert.Equal(new[] { 3, 4 }, result);
+    }
+
+    [Fact]
+    public void IntersectAll_ReturnsCorrectResult()
+    {
+        var source = new List<IEnumerable<int>>
+            {
+                new[] { 1, 2, 3 },
+                new[] { 2, 3, 4 },
+                new[] { 3, 4, 5 }
+            };
+        var result = source.IntersectAll();
+        Assert.Equal(new[] { 3 }, result);
+    }
+
+    [Fact]
+    public void ExceptBy_WithCondition_ReturnsCorrectResult()
+    {
+        var first = new[] { 1, 2, 3, 4 };
+        var second = new[] { 3, 4, 5, 6 };
+        var result = first.ExceptBy(second, (a, b) => a == b);
+        Assert.Equal(new[] { 1, 2 }, result);
+    }
+
+    [Fact]
+    public void AddRange_AddsElementsToCollection()
+    {
+        var collection = new List<int> { 1, 2 };
+        collection.AddRange(3, 4);
+        Assert.Equal(new[] { 1, 2, 3, 4 }, collection);
+    }
+
+    [Fact]
+    public void AddRangeIf_AddsElementsToCollectionIfConditionIsMet()
+    {
+        var collection = new List<int> { 1, 2 };
+        collection.AddRangeIf(x => x > 2, 3, 4);
+        Assert.Equal(new[] { 1, 2, 3, 4 }, collection);
+    }
+
+    [Fact]
+    public void RemoveWhere_RemovesElementsFromCollection()
+    {
+        var collection = new List<int> { 1, 2, 3, 4 };
+        collection.RemoveWhere(x => x > 2);
+        Assert.Equal(new[] { 1, 2 }, collection);
+    }
+
+    [Fact]
+    public void InsertAfter_InsertsElementAfterCondition()
+    {
+        var list = new List<int> { 1, 2, 3, 4 };
+        list.InsertAfter(x => x == 2, 5);
+        Assert.Equal(new[] { 1, 2, 5, 3, 4 }, list);
+    }
+
+    [Fact]
+    public void ToHashSet_ConvertsToHashSet()
+    {
+        var source = new[] { 1, 2, 3, 4 };
+        var result = source.ToHashSet(x => x);
+        Assert.Equal(new HashSet<int> { 1, 2, 3, 4 }, result);
+    }
+
+    [Fact]
+    public void ToQueue_ConvertsToQueue()
+    {
+        var source = new[] { 1, 2, 3, 4 };
+        var result = source.ToQueue();
+        Assert.Equal(new Queue<int>(new[] { 1, 2, 3, 4 }), result);
+    }
+
+    [Fact]
+    public void ForEach_ExecutesActionOnEachElement()
+    {
+        var source = new[] { 1, 2, 3, 4 };
+        var result = new List<int>();
+        source.ForEach(x => result.Add(x));
+        Assert.Equal(source, result);
+    }
+
+    [Fact]
+    public async Task ForeachAsync_ExecutesActionOnEachElement()
+    {
+        var source = new[] { 1, 2, 3, 4 };
+        var result = new ConcurrentBag<int>();
+        await source.ForeachAsync(async x =>
+        {
+            await Task.Delay(10);
+            result.Add(x);
+        }, 2);
+        Assert.Equal(source.Length, result.Count);
+    }
+
+    [Fact]
+    public void MaxOrDefault_ReturnsMaxValueOrDefault()
+    {
+        var source = new[] { 1, 2, 3, 4 };
+        var result = source.MaxOrDefault();
+        Assert.Equal(4, result);
+    }
+
+    [Fact]
+    public void MinOrDefault_ReturnsMinValueOrDefault()
+    {
+        var source = new[] { 1, 2, 3, 4 };
+        var result = source.MinOrDefault();
+        Assert.Equal(1, result);
+    }
+
+    [Fact]
+    public void StandardDeviation_ReturnsCorrectResult()
+    {
+        var source = new[] { 1.0, 2.0, 3.0, 4.0 };
+        var result = source.StandardDeviation();
+        Assert.Equal(1.118033988749895, result, 6);
+    }
+
+    [Fact]
+    public void OrderByRandom_ReturnsRandomOrder()
+    {
+        var source = new[] { 1, 2, 3, 4 };
+        var result = source.OrderByRandom().ToList();
+        Assert.NotEqual(source, result);
+    }
+
+    [Fact]
+    public void SequenceEqual_WithCondition_ReturnsCorrectResult()
+    {
+        var first = new[] { 1, 2, 3, 4 };
+        var second = new long[] { 1, 2, 3, 4 };
+        var result = first.SequenceEqual(second, (a, b) => a == b);
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void CompareChanges_ReturnsCorrectResult()
+    {
+        var first = new[] { 1, 2, 3, 4 };
+        var second = new[] { 3, 4, 5, 6 };
+        var (adds, remove, updates) = first.CompareChanges(second);
+        Assert.Equal(new[] { 1, 2 }, adds);
+        Assert.Equal(new[] { 5, 6 }, remove);
+        Assert.Equal(new[] { 3, 4 }, updates);
+    }
+
+    [Fact]
+    public void AsNotNull_ReturnsNonNullCollection()
+    {
+        List<int> list = null;
+        var result = list.AsNotNull();
+        Assert.NotNull(result);
+    }
+
+    [Fact]
+    public void WhereIf_ReturnsFilteredCollectionIfConditionIsMet()
+    {
+        var source = new[] { 1, 2, 3, 4 };
+        var result = source.WhereIf(true, x => x > 2);
+        Assert.Equal(new[] { 3, 4 }, result);
+    }
 }

+ 13 - 0
Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/IPAddressExtensionsTest.cs

@@ -0,0 +1,13 @@
+using System.Net;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Extensions.BaseType;
+
+public class IPAddressExtensionsTest
+{
+    [Fact]
+    public void Can_IsPrivateIP()
+    {
+        Assert.True(IPAddress.Parse("192.168.31.13").IsPrivateIP());
+    }
+}

+ 16 - 1
Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/NumberFormaterExtensionsTest.cs

@@ -1,4 +1,5 @@
-using System.Numerics;
+using System;
+using System.Numerics;
 using Masuit.Tools.Strings;
 using Xunit;
 
@@ -19,4 +20,18 @@ public class NumberFormaterExtensionsTest
         Assert.Equal(formater.FromString("😁😃😶😍😀🤔😚"), 1234567890);
         Assert.Equal(formater.FromStringBig("😁😃😶😍😀🤔😚"), 1234567890);
     }
+
+    [Fact]
+    public void GetBytes_ShouldReturnCorrectByteArray()
+    {
+        // Arrange
+        int value = 123456;
+        byte[] expected = BitConverter.GetBytes(value);
+
+        // Act
+        byte[] result = value.GetBytes();
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
 }

+ 125 - 0
Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/ObjectExtensionsTests.cs

@@ -0,0 +1,125 @@
+using System;
+using System.Collections.Generic;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Extensions.BaseType;
+
+public class ObjectExtensionsTests
+{
+    [Fact]
+    public void IsPrimitive_ShouldReturnTrue_ForPrimitiveTypes()
+    {
+        Assert.True(typeof(int).IsPrimitive());
+        Assert.True(typeof(string).IsPrimitive());
+        Assert.False(typeof(DateTime).IsPrimitive());
+    }
+
+    [Fact]
+    public void IsSimpleType_ShouldReturnTrue_ForSimpleTypes()
+    {
+        Assert.True(typeof(int).IsSimpleType());
+        Assert.True(typeof(string).IsSimpleType());
+        Assert.True(typeof(decimal).IsSimpleType());
+        Assert.False(typeof(DateTime).IsSimpleType());
+    }
+
+    [Fact]
+    public void IsSimpleArrayType_ShouldReturnTrue_ForSimpleArrayTypes()
+    {
+        Assert.True(typeof(int[]).IsSimpleArrayType());
+        Assert.False(typeof(DateTime[]).IsSimpleArrayType());
+    }
+
+    [Fact]
+    public void IsSimpleListType_ShouldReturnTrue_ForSimpleListTypes()
+    {
+        Assert.True(typeof(List<int>).IsSimpleListType());
+        Assert.False(typeof(List<DateTime>).IsSimpleListType());
+    }
+
+    [Fact]
+    public void IsDefaultValue_ShouldReturnTrue_ForDefaultValues()
+    {
+        Assert.True(0.IsDefaultValue());
+        Assert.True(default(DateTime).IsDefaultValue());
+        Assert.False(1.IsDefaultValue());
+    }
+
+    [Fact]
+    public void DeepClone_ShouldCloneObject()
+    {
+        var original = new TestClass { Id = 1, Name = "Test", List = [1, 2, 3] };
+        var clone = original.DeepClone();
+
+        Assert.NotSame(original, clone);
+        Assert.Equal(original.Id, clone.Id);
+        Assert.Equal(original.Name, clone.Name);
+    }
+
+    [Fact]
+    public void IsNullOrEmpty_ShouldReturnTrue_ForNullOrEmptyValues()
+    {
+        Assert.True(((string)null).IsNullOrEmpty());
+        Assert.True("".IsNullOrEmpty());
+        Assert.False("Test".IsNullOrEmpty());
+    }
+
+    [Fact]
+    public void IfNull_ShouldReturnDefaultValue_WhenNull()
+    {
+        string value = null;
+        Assert.Equal("default", value.IfNull("default"));
+    }
+
+    [Fact]
+    public void ToJsonString_ShouldReturnJsonString()
+    {
+        var obj = new { Id = 1, Name = "Test" };
+        var json = obj.ToJsonString();
+        Assert.Equal("{\"Id\":1,\"Name\":\"Test\"}", json);
+    }
+
+    [Fact]
+    public void FromJson_ShouldReturnObject_FromJsonString()
+    {
+        var json = "{\"Id\":1,\"Name\":\"Test\"}";
+        var obj = json.FromJson<TestClass>();
+        Assert.Equal(1, obj.Id);
+        Assert.Equal("Test", obj.Name);
+    }
+
+    [Fact]
+    public void ToDictionary_ShouldConvertObjectToDictionary()
+    {
+        var obj = new { Id = 1, Name = "Test" };
+        var dict = obj.ToDictionary();
+        Assert.Equal(1, dict["Id"]);
+        Assert.Equal("Test", dict["Name"]);
+    }
+
+    [Fact]
+    public void ToDynamic_ShouldConvertObjectToDynamic()
+    {
+        var obj = new { Id = 1, Name = "Test" };
+        dynamic dynamicObj = obj.ToDynamic();
+        Assert.Equal(1, dynamicObj.Id);
+        Assert.Equal("Test", dynamicObj.Name);
+    }
+
+    [Fact]
+    public void Merge_ShouldMergeObjects()
+    {
+        var obj1 = new TestClass { Id = 1 };
+        var obj2 = new TestClass { Name = "Test" };
+        var merged = obj1.Merge(obj2);
+        Assert.Equal(1, merged.Id);
+        Assert.Equal("Test", merged.Name);
+    }
+
+    private class TestClass
+    {
+        public int Id { get; set; }
+        public string Name { get; set; }
+        public List<int> List { get; set; }
+    }
+}

+ 20 - 0
Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/ShortExtensionsTests.cs

@@ -0,0 +1,20 @@
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Extensions.BaseType;
+
+public class ShortExtensionsTests
+{
+    [Theory]
+    [InlineData((short)0, new byte[] { 0, 0 })]
+    [InlineData((short)1, new byte[] { 1, 0 })]
+    [InlineData((short)256, new byte[] { 0, 1 })]
+    [InlineData((short)-1, new byte[] { 255, 255 })]
+    public void GetBytes_ShouldReturnCorrectByteArray(short value, byte[] expected)
+    {
+        // Act
+        var result = value.GetBytes();
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+}

+ 173 - 0
Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/StreamExtensionsTests.cs

@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using Masuit.Tools.Security;
+using Masuit.Tools.Systems;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Extensions.BaseType
+{
+    public class StreamExtensionsTests
+    {
+        [Fact]
+        public void SaveAsMemoryStream_ShouldReturnPooledMemoryStream()
+        {
+            using var stream = new MemoryStream(Encoding.UTF8.GetBytes("Test data"));
+            var result = stream.SaveAsMemoryStream();
+            Assert.IsType<PooledMemoryStream>(result);
+        }
+
+        [Fact]
+        public void ToArray_ShouldReturnByteArray()
+        {
+            using var stream = new MemoryStream(Encoding.UTF8.GetBytes("Test data"));
+            var result = stream.ToArray();
+            Assert.Equal(Encoding.UTF8.GetBytes("Test data"), result);
+        }
+
+        [Fact]
+        public void ShuffleCode_ShouldAddRandomBytes()
+        {
+            using var stream = new PooledMemoryStream("Test data"u8.ToArray());
+            var md5 = stream.MDString();
+            stream.ShuffleCode();
+            Assert.NotEqual(stream.MDString(), md5);
+        }
+
+        [Fact]
+        public void ReadAllLines_StreamReader_ShouldReturnAllLines()
+        {
+            using var stream = new MemoryStream(Encoding.UTF8.GetBytes("Line1\nLine2\nLine3"));
+            using var reader = new StreamReader(stream);
+            var result = reader.ReadAllLines();
+            Assert.Equal(new List<string> { "Line1", "Line2", "Line3" }, result);
+        }
+
+        [Fact]
+        public void ReadAllLines_FileStream_ShouldReturnAllLines()
+        {
+            var filePath = Path.GetTempFileName();
+            File.WriteAllText(filePath, "Line1\nLine2\nLine3");
+            using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
+            var result = stream.ReadAllLines(Encoding.UTF8);
+            Assert.Equal(new List<string> { "Line1", "Line2", "Line3" }, result);
+            File.Delete(filePath);
+        }
+
+        [Fact]
+        public void ReadAllText_ShouldReturnAllText()
+        {
+            var filePath = Path.GetTempFileName();
+            File.WriteAllText(filePath, "Test data");
+            using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
+            var result = stream.ReadAllText(Encoding.UTF8);
+            Assert.Equal("Test data", result);
+            File.Delete(filePath);
+        }
+
+        [Fact]
+        public void WriteAllText_ShouldWriteAllText()
+        {
+            var filePath = Path.GetTempFileName();
+            using var stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
+            stream.WriteAllText("Test data", Encoding.UTF8);
+            var result = File.ReadAllText(filePath);
+            Assert.Equal("Test data", result);
+            File.Delete(filePath);
+        }
+
+        [Fact]
+        public void WriteAllLines_ShouldWriteAllLines()
+        {
+            var filePath = Path.GetTempFileName();
+            using var stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
+            stream.WriteAllLines(new List<string> { "Line1", "Line2", "Line3" }, Encoding.UTF8);
+            var result = File.ReadAllLines(filePath);
+            Assert.Equal(new List<string> { "Line1", "Line2", "Line3" }, result);
+            File.Delete(filePath);
+        }
+
+        [Fact]
+        public void ShareReadWrite_ShouldOpenFileWithReadWriteAccess()
+        {
+            var filePath = Path.GetTempFileName();
+            var fileInfo = new FileInfo(filePath);
+            using var stream = fileInfo.ShareReadWrite();
+            Assert.True(stream.CanRead && stream.CanWrite);
+            try
+            {
+                File.Delete(filePath);
+            }
+            catch (Exception e)
+            {
+            }
+        }
+
+        [Fact]
+        public async Task ReadAllLinesAsync_StreamReader_ShouldReturnAllLines()
+        {
+            using var stream = new MemoryStream(Encoding.UTF8.GetBytes("Line1\nLine2\nLine3"));
+            using var reader = new StreamReader(stream);
+            var result = await reader.ReadAllLinesAsync();
+            Assert.Equal(new List<string> { "Line1", "Line2", "Line3" }, result);
+        }
+
+        [Fact]
+        public async Task ReadAllLinesAsync_FileStream_ShouldReturnAllLines()
+        {
+            var filePath = Path.GetTempFileName();
+            await File.WriteAllTextAsync(filePath, "Line1\nLine2\nLine3");
+            using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
+            var result = await stream.ReadAllLinesAsync(Encoding.UTF8);
+            Assert.Equal(new List<string> { "Line1", "Line2", "Line3" }, result);
+            File.Delete(filePath);
+        }
+
+        [Fact]
+        public async Task ReadAllTextAsync_ShouldReturnAllText()
+        {
+            var filePath = Path.GetTempFileName();
+            await File.WriteAllTextAsync(filePath, "Test data");
+            using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
+            var result = await stream.ReadAllTextAsync(Encoding.UTF8);
+            Assert.Equal("Test data", result);
+            File.Delete(filePath);
+        }
+
+        [Fact]
+        public async Task WriteAllTextAsync_ShouldWriteAllText()
+        {
+            var filePath = Path.GetTempFileName();
+            using var stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
+            await stream.WriteAllTextAsync("Test data", Encoding.UTF8);
+            var result = await File.ReadAllTextAsync(filePath);
+            Assert.Equal("Test data", result);
+            File.Delete(filePath);
+        }
+
+        [Fact]
+        public async Task WriteAllLinesAsync_ShouldWriteAllLines()
+        {
+            var filePath = Path.GetTempFileName();
+            using var stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
+            await stream.WriteAllLinesAsync(new List<string> { "Line1", "Line2", "Line3" }, Encoding.UTF8);
+            var result = await File.ReadAllLinesAsync(filePath);
+            Assert.Equal(new List<string> { "Line1", "Line2", "Line3" }, result);
+            File.Delete(filePath);
+        }
+
+#if NET5_0_OR_GREATER
+
+        [Fact]
+        public async Task ToArrayAsync_ShouldReturnByteArray()
+        {
+            using var stream = new MemoryStream(Encoding.UTF8.GetBytes("Test data"));
+            var result = await stream.ToArrayAsync();
+            Assert.Equal(Encoding.UTF8.GetBytes("Test data"), result);
+        }
+
+#endif
+    }
+}

+ 358 - 0
Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/StringExtensionsTest.cs

@@ -1,6 +1,9 @@
 using System;
 using System.Collections.Generic;
+using System.Numerics;
 using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
 using Xunit;
 
 namespace Masuit.Tools.Abstractions.Test.Extensions.BaseType
@@ -59,5 +62,360 @@ namespace Masuit.Tools.Abstractions.Test.Extensions.BaseType
             bool expected = url.IsExternalAddress();
             Assert.Equal(expected, actual);
         }
+
+        [Fact]
+        public void Join_ShouldJoinStrings()
+        {
+            var strings = new List<string> { "a", "b", "c" };
+            var result = strings.Join(", ");
+            Assert.Equal("a, b, c", result);
+        }
+
+        [Fact]
+        public void Join_ShouldJoinStringsAndRemoveEmptyEntries()
+        {
+            var strings = new List<string> { "a", "", "c" };
+            var result = strings.Join(", ", true);
+            Assert.Equal("a, c", result);
+        }
+
+        [Fact]
+        public void ToDateTime_ShouldConvertStringToDateTime()
+        {
+            var dateString = "2023-10-10";
+            var result = dateString.ToDateTime();
+            Assert.Equal(new DateTime(2023, 10, 10), result);
+        }
+
+        [Fact]
+        public void ToGuid_ShouldConvertStringToGuid()
+        {
+            var guidString = "d2719b1e-1d2b-4b5a-9b1e-1d2b4b5a9b1e";
+            var result = guidString.ToGuid();
+            Assert.Equal(Guid.Parse(guidString), result);
+        }
+
+        [Fact]
+        public void Replace_ShouldReplaceUsingRegex()
+        {
+            var input = "Hello World";
+            var regex = new Regex("World");
+            var result = input.Replace(regex, "Universe");
+            Assert.Equal("Hello Universe", result);
+        }
+
+        [Fact]
+        public void CreateShortToken_ShouldGenerateToken()
+        {
+            var result = "".CreateShortToken();
+            Assert.NotNull(result);
+            Assert.NotEmpty(result);
+        }
+
+        [Fact]
+        public void FromBase_ShouldConvertFromBase()
+        {
+            var base36String = "1l";
+            var result = base36String.FromBase(36);
+            Assert.Equal(57, result);
+        }
+
+        [Fact]
+        public void FromBaseBig_ShouldConvertFromBaseBig()
+        {
+            var base36String = "1l";
+            var result = base36String.FromBaseBig(36);
+            Assert.Equal(new BigInteger(57), result);
+        }
+
+        [Fact]
+        public void Contains_ShouldCheckIfStringContainsAnyKeywords()
+        {
+            var input = "Hello World";
+            var keywords = new List<string> { "world", "universe" };
+            var result = input.Contains(keywords);
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void ContainsSafety_ShouldCheckIfStringContainsAnyKeywordsSafely()
+        {
+            var input = "Hello World";
+            var keywords = new List<string> { "world", "universe" };
+            var result = input.ContainsSafety(keywords);
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void EndsWith_ShouldCheckIfStringEndsWithAnyKeywords()
+        {
+            var input = "Hello World";
+            var keywords = new List<string> { "world", "universe" };
+            var result = input.EndsWith(keywords);
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void StartsWith_ShouldCheckIfStringStartsWithAnyKeywords()
+        {
+            var input = "Hello World";
+            var keywords = new List<string> { "hello", "hi" };
+            var result = input.StartsWith(keywords);
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void RegexMatch_ShouldCheckIfStringMatchesRegex()
+        {
+            var input = "Hello World";
+            var regex = "World";
+            var result = input.RegexMatch(regex);
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void RegexMatch_ShouldCheckIfStringMatchesRegexObject()
+        {
+            var input = "Hello World";
+            var regex = new Regex("World");
+            var result = input.RegexMatch(regex);
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void IsNullOrEmpty_ShouldCheckIfStringIsNullOrEmpty()
+        {
+            var input = "";
+            var result = input.IsNullOrEmpty();
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void NotNullOrEmpty_ShouldCheckIfStringIsNotNullOrEmpty()
+        {
+            var input = "Hello";
+            var result = input.NotNullOrEmpty();
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void AsNotNull_ShouldReturnNonNullString()
+        {
+            string input = null;
+            var result = input.AsNotNull();
+            Assert.Equal("", result);
+        }
+
+        [Fact]
+        public void IfNullOrEmpty_ShouldReturnReplacementIfNullOrEmpty()
+        {
+            string input = null;
+            var result = input.IfNullOrEmpty("replacement");
+            Assert.Equal("replacement", result);
+        }
+
+        [Fact]
+        public void IfNullOrEmpty_ShouldReturnReplacementFromFactoryIfNullOrEmpty()
+        {
+            string input = null;
+            var result = input.IfNullOrEmpty(() => "replacement");
+            Assert.Equal("replacement", result);
+        }
+
+        [Fact]
+        public void Mask_ShouldMaskString()
+        {
+            var input = "1234567890";
+            var result = input.Mask();
+            Assert.Equal("123****890", result);
+        }
+
+        [Fact]
+        public async Task MatchEmailAsync_ShouldMatchEmail()
+        {
+            var input = "[email protected]";
+            var (isMatch, match) = await input.MatchEmailAsync();
+            Assert.True(isMatch);
+            Assert.NotNull(match);
+        }
+
+        [Fact]
+        public void MaskEmail_ShouldMaskEmail()
+        {
+            var input = "[email protected]";
+            var result = input.MaskEmail();
+            Assert.Equal("t****@example.com", result);
+        }
+
+        [Fact]
+        public void MatchUrl_ShouldMatchUrl()
+        {
+            var input = "http://example.com";
+            var result = input.MatchUrl();
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void MatchIdentifyCard_ShouldMatchIdentifyCard()
+        {
+            var input = "11010519491231002X";
+            var result = input.MatchIdentifyCard();
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void MatchInetAddress_ShouldMatchInetAddress()
+        {
+            var input = "192.168.1.1";
+            var result = input.MatchInetAddress();
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void IPToID_ShouldConvertIPToID()
+        {
+            var input = "192.168.1.1";
+            var result = input.IPToID();
+            Assert.Equal(3232235777, result);
+        }
+
+        [Fact]
+        public void IsPrivateIP_ShouldCheckIfIPIsPrivate()
+        {
+            var input = "192.168.1.1";
+            var result = input.IsPrivateIP();
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void IpAddressInRange_ShouldCheckIfIPIsInRange()
+        {
+            var input = "192.168.1.1";
+            var result = input.IpAddressInRange("192.168.1.0", "192.168.1.255");
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void MatchPhoneNumber_ShouldMatchPhoneNumber()
+        {
+            var input = "13800138000";
+            var result = input.MatchPhoneNumber();
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void MatchLandline_ShouldMatchLandline()
+        {
+            var input = "010-12345678";
+            var result = input.MatchLandline();
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void MatchUSCC_ShouldMatchUSCC()
+        {
+            var input = "91350211347112345X";
+            var result = input.MatchUSCC();
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void IsExternalAddress_ShouldCheckIfUrlIsExternal()
+        {
+            var input = "http://example.com";
+            var result = input.IsExternalAddress();
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void ToByteArray_ShouldConvertStringToByteArray()
+        {
+            var input = "Hello";
+            var result = input.ToByteArray();
+            Assert.Equal(Encoding.UTF8.GetBytes("Hello"), result);
+        }
+
+        [Fact]
+        public void Crc32_ShouldComputeCrc32()
+        {
+            var input = "Hello";
+            var result = input.Crc32();
+            Assert.NotNull(result);
+            Assert.NotEmpty(result);
+        }
+
+        [Fact]
+        public void Crc64_ShouldComputeCrc64()
+        {
+            var input = "Hello";
+            var result = input.Crc64();
+            Assert.NotNull(result);
+            Assert.NotEmpty(result);
+        }
+
+        [Fact]
+        public void MatchCNPatentNumber_ShouldMatchCNPatentNumber()
+        {
+            var input = "200410018477.9";
+            var result = input.MatchCNPatentNumber();
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void Take_ShouldTakeFirstNCharacters()
+        {
+            var input = "Hello World";
+            var result = input.Take(5);
+            Assert.Equal("Hello", result);
+        }
+
+        [Fact]
+        public void HammingDistance_ShouldComputeHammingDistance()
+        {
+            var input1 = "Hello";
+            var input2 = "Helli";
+            var result = input1.HammingDistance(input2);
+            Assert.Equal(1, result);
+        }
+
+        [Fact]
+        public void MatchEmoji_ShouldMatchEmoji()
+        {
+            var input = "Hello 😊";
+            var result = input.MatchEmoji();
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void CharacterCount_ShouldCountCharacters()
+        {
+            var input = "Hello 😊";
+            var result = input.CharacterCount();
+            Assert.Equal(7, result);
+        }
+
+        [Fact]
+        public void BytesCount_ShouldCountBytes()
+        {
+            var input = "Hello";
+            var result = input.BytesCount();
+            Assert.Equal(5, result);
+        }
+
+        [Fact]
+        public void ToDBC_ShouldConvertToDBC()
+        {
+            var input = "hello";
+            var result = input.ToDBC();
+            Assert.Equal("hello", result);
+        }
+
+        [Fact]
+        public void ToSBC_ShouldConvertToSBC()
+        {
+            var input = "hello";
+            var result = input.ToSBC();
+            Assert.Equal("hello", result);
+        }
     }
 }

+ 147 - 0
Test/Masuit.Tools.Abstractions.Test/Extensions/BaseType/ValueTypeConvertExtensionsTests.cs

@@ -0,0 +1,147 @@
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Extensions.BaseType;
+
+public class ValueTypeConvertExtensionsTests
+{
+    [Theory]
+    [InlineData("123", 123)]
+    [InlineData("abc", 0)]
+    [InlineData(null, 0)]
+    public void ToInt32_ShouldConvertStringToInt(string input, int expected)
+    {
+        var result = input.ToInt32();
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData("1234567890123", 1234567890123)]
+    [InlineData("abc", 0)]
+    [InlineData(null, 0)]
+    public void ToInt64_ShouldConvertStringToLong(string input, long expected)
+    {
+        var result = input.ToInt64();
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData("123.45", 123.45)]
+    [InlineData("abc", 0)]
+    [InlineData(null, 0)]
+    public void ToDouble_ShouldConvertStringToDouble(string input, double expected)
+    {
+        var result = input.ToDouble();
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData("123.45", 123.45)]
+    [InlineData("abc", 0)]
+    [InlineData(null, 0)]
+    public void ToDecimal_ShouldConvertStringToDecimal(string input, decimal expected)
+    {
+        var result = input.ToDecimal();
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData("123.456", 2, 123.46)]
+    [InlineData("abc", 2, 0)]
+    [InlineData(null, 2, 0)]
+    public void ToDecimal_WithRounding_ShouldConvertStringToDecimal(string input, int round, decimal expected)
+    {
+        var result = input.ToDecimal(round);
+        Assert.Equal(expected, result);
+    }
+
+    [Fact]
+    public void ToDouble_ShouldConvertDecimalToDouble()
+    {
+        decimal input = 123.45M;
+        var result = input.ToDouble();
+        Assert.Equal(123.45, result);
+    }
+
+    [Fact]
+    public void ToInt32_ShouldConvertDoubleToInt()
+    {
+        double input = 123.45;
+        var result = input.ToInt32();
+        Assert.Equal(123, result);
+    }
+
+    [Fact]
+    public void ToInt32_ShouldConvertDecimalToInt()
+    {
+        decimal input = 123.45M;
+        var result = input.ToInt32();
+        Assert.Equal(123, result);
+    }
+
+    [Fact]
+    public void ToDouble_ShouldConvertIntToDouble()
+    {
+        int input = 123;
+        var result = input.ToDouble();
+        Assert.Equal(123.0, result);
+    }
+
+    [Fact]
+    public void ToDecimal_ShouldConvertIntToDecimal()
+    {
+        int input = 123;
+        var result = input.ToDecimal();
+        Assert.Equal(123M, result);
+    }
+
+    [Theory]
+    [InlineData(123.456, 2, 123.46)]
+    [InlineData(123.451, 2, 123.45)]
+    public void Round_ShouldRoundDecimal(decimal input, int decimals, decimal expected)
+    {
+        var result = input.Round(decimals);
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData(123.456, 2, 123.46)]
+    [InlineData(123.451, 2, 123.45)]
+    public void Round_ShouldRoundDouble(double input, int decimals, double expected)
+    {
+        var result = input.Round(decimals);
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData(null, 2, null)]
+    public void Round_ShouldRoundNullableDecimal(decimal? input, int decimals, decimal? expected)
+    {
+        var result = input.Round(decimals);
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData(123.456, 2, 123.46)]
+    [InlineData(null, 2, null)]
+    public void Round_ShouldRoundNullableDouble(double? input, int decimals, double? expected)
+    {
+        var result = input.Round(decimals);
+        Assert.Equal(expected, result);
+    }
+
+    [Fact]
+    public void ToChineseNumber_ShouldConvertNumberToChinese()
+    {
+        int input = 123;
+        var result = input.ToChineseNumber();
+        Assert.Equal("一百二十三", result);
+    }
+
+    [Fact]
+    public void ToChineseMoney_ShouldConvertNumberToChineseMoney()
+    {
+        decimal input = 123.45M;
+        var result = input.ToChineseMoney();
+        Assert.Equal("壹佰贰拾叁元肆角伍分", result);
+    }
+}

+ 39 - 2
Test/Masuit.Tools.Abstractions.Test/Extensions/DynamicObjectTest.cs

@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using Masuit.Tools.Dynamics;
 using Xunit;
 
@@ -44,4 +45,40 @@ public class DynamicObjectTest
         Assert.Equal(obj["MyClass"]["X"], obj.MyClass.X);
         Assert.IsType<Clay>(obj.Prop);
     }
-}
+
+    [Fact]
+    public void NewObject_ShouldCreateDynamicObject()
+    {
+        dynamic obj = DynamicFactory.NewObject();
+        obj.Name = "Test";
+        Assert.Equal("Test", obj.Name);
+    }
+
+    [Fact]
+    public void WithObject_ShouldWrapExistingObject()
+    {
+        var existingObject = new { Name = "Test" };
+        dynamic obj = DynamicFactory.WithObject(existingObject);
+        Assert.Equal("Test", obj.Name);
+    }
+
+    [Fact]
+    public void NewObject_ShouldAllowAddingProperties()
+    {
+        dynamic obj = DynamicFactory.NewObject();
+        obj.Name = "Test";
+        obj.Age = 30;
+        Assert.Equal("Test", obj.Name);
+        Assert.Equal(30, obj.Age);
+    }
+
+    [Fact]
+    public void WithObject_ShouldAllowAddingPropertiesToExistingObject()
+    {
+        var existingObject = new { Name = "Test" };
+        dynamic obj = DynamicFactory.WithObject(existingObject);
+        obj.Age = 30;
+        Assert.Equal("Test", obj.Name);
+        Assert.Equal(30, obj.Age);
+    }
+}

+ 110 - 0
Test/Masuit.Tools.Abstractions.Test/Files/FileExtTests.cs

@@ -0,0 +1,110 @@
+using System;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+using Masuit.Tools.Files;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Files;
+
+public class FileExtTests
+{
+    public string GetTestFile(string n)
+    {
+        string testFilePath = Path.Combine(Path.GetTempPath(), $"testfile{n}.txt");
+        if (File.Exists(testFilePath))
+        {
+            File.Delete(testFilePath);
+        }
+
+        File.WriteAllText(testFilePath, "This is a test file.");
+        return testFilePath;
+    }
+
+    [Fact]
+    public void CopyToFile_ShouldCopyFile()
+    {
+        var _testFilePath = GetTestFile("1");
+        string _copyFilePath = Path.Combine(Path.GetTempPath(), "copyfile.txt");
+        using var fs = new FileStream(_testFilePath, FileMode.Open, FileAccess.Read);
+        fs.CopyToFile(_copyFilePath);
+        Assert.True(File.Exists(_copyFilePath));
+        Assert.Equal(File.ReadAllText(_testFilePath), File.ReadAllText(_copyFilePath));
+    }
+
+    [Fact]
+    public async Task CopyToFileAsync_ShouldCopyFile()
+    {
+        string _copyFilePath = Path.Combine(Path.GetTempPath(), "copyfile2.txt");
+        var _testFilePath = GetTestFile("2");
+        using var fs = new FileStream(_testFilePath, FileMode.Open, FileAccess.Read);
+        await fs.CopyToFileAsync(_copyFilePath);
+        Assert.True(File.Exists(_copyFilePath));
+        Assert.Equal(File.ReadAllText(_testFilePath), File.ReadAllText(_copyFilePath));
+    }
+
+    [Fact]
+    public void SaveFile_ShouldSaveFile()
+    {
+        string _copyFilePath = Path.Combine(Path.GetTempPath(), "copyfile3.txt");
+        using var ms = new MemoryStream(Encoding.UTF8.GetBytes("This is a test file."));
+        ms.SaveFile(_copyFilePath);
+        Assert.True(File.Exists(_copyFilePath));
+        Assert.Equal("This is a test file.", File.ReadAllText(_copyFilePath));
+    }
+
+    [Fact]
+    public async Task SaveFileAsync_ShouldSaveFile()
+    {
+        string _copyFilePath = Path.Combine(Path.GetTempPath(), "copyfile4.txt");
+        using var ms = new MemoryStream(Encoding.UTF8.GetBytes("This is a test file."));
+        await ms.SaveFileAsync(_copyFilePath);
+        Assert.True(File.Exists(_copyFilePath));
+        Assert.Equal("This is a test file.", File.ReadAllText(_copyFilePath));
+    }
+
+    [Fact]
+    public void GetFileMD5_ShouldReturnMD5Hash()
+    {
+        var _testFilePath = GetTestFile("3");
+        using var fs = new FileStream(_testFilePath, FileMode.Open, FileAccess.Read);
+        var md5 = fs.GetFileMD5();
+        using var md5Crypto = MD5.Create();
+        var expectedMd5 = BitConverter.ToString(md5Crypto.ComputeHash(Encoding.UTF8.GetBytes("This is a test file."))).Replace("-", "").ToLower();
+        Assert.Equal(expectedMd5, md5);
+    }
+
+    [Fact]
+    public void GetFileSha1_ShouldReturnSha1Hash()
+    {
+        var _testFilePath = GetTestFile("4");
+        using var fs = new FileStream(_testFilePath, FileMode.Open, FileAccess.Read);
+        var sha1 = fs.GetFileSha1();
+        using var sha1Crypto = SHA1.Create();
+        var expectedSha1 = BitConverter.ToString(sha1Crypto.ComputeHash(Encoding.UTF8.GetBytes("This is a test file."))).Replace("-", "").ToLower();
+        Assert.Equal(expectedSha1, sha1);
+    }
+
+    [Fact]
+    public void GetFileSha256_ShouldReturnSha256Hash()
+    {
+        var _testFilePath = GetTestFile("5");
+        using var fs = new FileStream(_testFilePath, FileMode.Open, FileAccess.Read);
+        var sha256 = fs.GetFileSha256();
+        using var sha256Crypto = SHA256.Create();
+        var expectedSha256 = BitConverter.ToString(sha256Crypto.ComputeHash(Encoding.UTF8.GetBytes("This is a test file."))).Replace("-", "").ToLower();
+        Assert.Equal(expectedSha256, sha256);
+    }
+
+    [Fact]
+    public void GetFileSha512_ShouldReturnSha512Hash()
+    {
+        var _testFilePath = GetTestFile("6");
+        using var fs = new FileStream(_testFilePath, FileMode.Open, FileAccess.Read);
+        var sha512 = fs.GetFileSha512();
+        using var sha512Crypto = SHA512.Create();
+        var expectedSha512 = BitConverter.ToString(sha512Crypto.ComputeHash(Encoding.UTF8.GetBytes("This is a test file."))).Replace("-", "").ToLower();
+        Assert.Equal(expectedSha512, sha512);
+    }
+}

+ 85 - 0
Test/Masuit.Tools.Abstractions.Test/Files/FileSignatureDetectorTests.cs

@@ -0,0 +1,85 @@
+using System.Collections.Generic;
+using System.IO;
+using Masuit.Tools.Files.FileDetector;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Files;
+
+public class FileSignatureDetectorTests
+{
+    [Fact]
+    public void AddDetector_ShouldAddDetector()
+    {
+        var detector = new TestDetector();
+        FileSignatureDetector.AddDetector(detector);
+        Assert.Contains(detector, FileSignatureDetector.Registered);
+    }
+
+    [Fact]
+    public void AddDetector_Generic_ShouldAddDetector()
+    {
+        FileSignatureDetector.AddDetector<TestDetector>();
+        Assert.Contains(FileSignatureDetector.Registered, d => d is TestDetector);
+    }
+
+    [Fact]
+    public void DetectFiletype_ShouldDetectFiletypeFromFilepath()
+    {
+        var detector = new TestDetector();
+        FileSignatureDetector.AddDetector(detector);
+
+        var tempFile = Path.GetTempFileName();
+        File.WriteAllText(tempFile, "Test content");
+
+        var result = FileSignatureDetector.DetectFiletype(tempFile);
+        Assert.IsType<TestDetector>(result);
+
+        File.Delete(tempFile);
+    }
+
+    [Fact]
+    public void DetectFiletype_ShouldDetectFiletypeFromFileInfo()
+    {
+        var detector = new TestDetector();
+        FileSignatureDetector.AddDetector(detector);
+
+        var tempFile = new FileInfo(Path.GetTempFileName());
+        File.WriteAllText(tempFile.FullName, "Test content");
+
+        var result = FileSignatureDetector.DetectFiletype(tempFile);
+        Assert.IsType<TestDetector>(result);
+
+        tempFile.Delete();
+    }
+
+    [Fact]
+    public void DetectFiletype_ShouldDetectFiletypeFromStream()
+    {
+        var detector = new TestDetector();
+        FileSignatureDetector.AddDetector(detector);
+
+        using var stream = new MemoryStream();
+        using var writer = new StreamWriter(stream);
+        writer.Write("Test content");
+        writer.Flush();
+        stream.Position = 0;
+
+        var result = FileSignatureDetector.DetectFiletype(stream);
+        Assert.IsType<TestDetector>(result);
+    }
+
+    private class TestDetector : IDetector
+    {
+        public string Precondition => "Test";
+        public string Extension => ".test";
+        public string MimeType => "application/test";
+        public List<FormatCategory> FormatCategories => new List<FormatCategory> { FormatCategory.Document };
+
+        public bool Detect(Stream stream)
+        {
+            var reader = new StreamReader(stream);
+            var content = reader.ReadToEnd();
+            return content.Contains("Test content");
+        }
+    }
+}

+ 180 - 0
Test/Masuit.Tools.Abstractions.Test/Files/IniFileTests.cs

@@ -0,0 +1,180 @@
+using System.IO;
+using System.Threading.Tasks;
+using Masuit.Tools.Files;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Files;
+
+public class IniFileTests
+{
+    private readonly string _testFilePath = Path.Combine(Path.GetTempPath(), "test.ini");
+
+    public IniFileTests()
+    {
+        if (File.Exists(_testFilePath))
+        {
+            File.Delete(_testFilePath);
+        }
+    }
+
+    [Fact]
+    public void Constructor_ShouldLoadExistingFile()
+    {
+        File.WriteAllText(_testFilePath, "[Section1]\nKey1=Value1\nKey2=Value2");
+        var iniFile = new IniFile(_testFilePath);
+        var section = iniFile.GetSection("Section1");
+        Assert.Equal("Value1", section["Key1"]);
+        Assert.Equal("Value2", section["Key2"]);
+    }
+
+    [Fact]
+    public void Reload_ShouldReloadFile()
+    {
+        File.WriteAllText(_testFilePath, "[Section1]\nKey1=Value1\nKey2=Value2");
+        var iniFile = new IniFile(_testFilePath);
+        File.WriteAllText(_testFilePath, "[Section1]\nKey1=NewValue1\nKey2=NewValue2");
+        iniFile.Reload();
+        var section = iniFile.GetSection("Section1");
+        Assert.Equal("NewValue1", section["Key1"]);
+        Assert.Equal("NewValue2", section["Key2"]);
+    }
+
+    [Fact]
+    public void Save_ShouldSaveToFile()
+    {
+        var iniFile = new IniFile(_testFilePath);
+        iniFile.SetValue("Section1", "Key1", "Value1");
+        iniFile.SetValue("Section1", "Key2", "Value2");
+        iniFile.Save();
+        var content = File.ReadAllText(_testFilePath);
+        Assert.Contains("[Section1]", content);
+        Assert.Contains("Key1=Value1", content);
+        Assert.Contains("Key2=Value2", content);
+    }
+
+    [Fact]
+    public async Task SaveAsync_ShouldSaveToFile()
+    {
+        var iniFile = new IniFile(_testFilePath);
+        iniFile.SetValue("Section1", "Key1", "Value1");
+        iniFile.SetValue("Section1", "Key2", "Value2");
+        await iniFile.SaveAsync();
+        var content = await File.ReadAllTextAsync(_testFilePath);
+        Assert.Contains("[Section1]", content);
+        Assert.Contains("Key1=Value1", content);
+        Assert.Contains("Key2=Value2", content);
+    }
+
+    [Fact]
+    public void GetValue_ShouldReturnDefaultValueIfKeyNotFound()
+    {
+        var iniFile = new IniFile(_testFilePath);
+        var value = iniFile.GetValue("Section1", "Key1", "DefaultValue");
+        Assert.Equal("DefaultValue", value);
+    }
+
+    [Fact]
+    public void GetValue_ShouldReturnValueIfKeyFound()
+    {
+        var iniFile = new IniFile(_testFilePath);
+        iniFile.SetValue("Section1", "Key1", "Value1");
+        var value = iniFile.GetValue("Section1", "Key1");
+        Assert.Equal("Value1", value);
+    }
+
+    [Fact]
+    public void GetValue_Generic_ShouldReturnConvertedValue()
+    {
+        var iniFile = new IniFile(_testFilePath);
+        iniFile.SetValue("Section1", "Key1", "123");
+        var value = iniFile.GetValue<int>("Section1", "Key1");
+        Assert.Equal(123, value);
+    }
+
+    [Fact]
+    public void GetSections_ShouldReturnAllSections()
+    {
+        var iniFile = new IniFile(_testFilePath);
+        iniFile.SetValue("Section1", "Key1", "Value1");
+        iniFile.SetValue("Section2", "Key2", "Value2");
+        var sections = iniFile.GetSections();
+        Assert.Contains(sections, s => s.Name == "Section1");
+        Assert.Contains(sections, s => s.Name == "Section2");
+    }
+
+    [Fact]
+    public void GetSection_ShouldReturnAllKeyValuesInSection()
+    {
+        var iniFile = new IniFile(_testFilePath);
+        iniFile.SetValue("Section1", "Key1", "Value1");
+        iniFile.SetValue("Section1", "Key2", "Value2");
+        var section = iniFile.GetSection("Section1");
+        Assert.Equal("Value1", section["Key1"]);
+        Assert.Equal("Value2", section["Key2"]);
+    }
+
+    [Fact]
+    public void GetSection_Generic_ShouldReturnBoundObject()
+    {
+        var iniFile = new IniFile(_testFilePath);
+        iniFile.SetValue("Section1", "Key1", "Value1");
+        iniFile.SetValue("Section1", "Key2", "Value2");
+        var section = iniFile.GetSection<TestSection>("Section1");
+        Assert.Equal("Value1", section.Key1);
+        Assert.Equal("Value2", section.Key2);
+    }
+
+    [Fact]
+    public void SetValue_ShouldSetKeyValue()
+    {
+        var iniFile = new IniFile(_testFilePath);
+        iniFile.SetValue("Section1", "Key1", "Value1");
+        var value = iniFile.GetValue("Section1", "Key1");
+        Assert.Equal("Value1", value);
+    }
+
+    [Fact]
+    public void SetValue_Bool_ShouldSetKeyValue()
+    {
+        var iniFile = new IniFile(_testFilePath);
+        iniFile.SetValue("Section1", "Key1", true);
+        var value = iniFile.GetValue("Section1", "Key1");
+        Assert.Equal("true", value);
+    }
+
+    [Fact]
+    public void SetValue_Generic_ShouldSetKeyValue()
+    {
+        var iniFile = new IniFile(_testFilePath);
+        iniFile.SetValue("Section1", "Key1", 123);
+        var value = iniFile.GetValue("Section1", "Key1");
+        Assert.Equal("123", value);
+    }
+
+    [Fact]
+    public void ClearSection_ShouldClearAllKeysInSection()
+    {
+        var iniFile = new IniFile(_testFilePath);
+        iniFile.SetValue("Section1", "Key1", "Value1");
+        iniFile.ClearSection("Section1");
+        var section = iniFile.GetSection("Section1");
+        Assert.Empty(section);
+    }
+
+    [Fact]
+    public void ClearAllSection_ShouldClearAllSections()
+    {
+        var iniFile = new IniFile(_testFilePath);
+        iniFile.SetValue("Section1", "Key1", "Value1");
+        iniFile.SetValue("Section2", "Key2", "Value2");
+        iniFile.ClearAllSection();
+        var sections = iniFile.GetSections();
+        Assert.Empty(sections);
+    }
+
+    private class TestSection
+    {
+        public string Key1 { get; set; }
+        public string Key2 { get; set; }
+    }
+}

+ 111 - 0
Test/Masuit.Tools.Abstractions.Test/Files/SevenZipCompressorTests.cs

@@ -0,0 +1,111 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Http;
+using Masuit.Tools.Files;
+using Masuit.Tools.Systems;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Files;
+
+public class SevenZipCompressorTests
+{
+    private readonly SevenZipCompressor _compressor;
+    private readonly string _testDir;
+    private readonly string _testFile1;
+    private readonly string _testFile2;
+    private readonly string _zipFile;
+
+    public SevenZipCompressorTests()
+    {
+        _compressor = new SevenZipCompressor(new HttpClient());
+        _testDir = Path.Combine(Path.GetTempPath(), "TestDir");
+        _testFile1 = Path.Combine(_testDir, "TestFile1.txt");
+        _testFile2 = Path.Combine(_testDir, "TestFile2.txt");
+        _zipFile = Path.Combine(Path.GetTempPath(), "TestArchive.zip");
+
+        if (Directory.Exists(_testDir))
+        {
+            Directory.Delete(_testDir, true);
+        }
+
+        Directory.CreateDirectory(_testDir);
+        File.WriteAllText(_testFile1, "This is a test file 1.");
+        File.WriteAllText(_testFile2, "This is a test file 2.");
+    }
+
+    [Fact]
+    public void ZipStream_ShouldCreateZipStreamFromFiles()
+    {
+        var files = new List<string> { _testFile1, _testFile2 };
+        using var zipStream = _compressor.ZipStream(files);
+        Assert.NotNull(zipStream);
+        Assert.True(zipStream.Length > 0);
+    }
+
+    [Fact]
+    public void ZipStream_ShouldCreateZipStreamFromDirectory()
+    {
+        using var zipStream = _compressor.ZipStream(_testDir);
+        Assert.NotNull(zipStream);
+        Assert.True(zipStream.Length > 0);
+    }
+
+    [Fact]
+    public void ZipStream_ShouldCreateZipStreamFromStreams()
+    {
+        var streams = new DisposableDictionary<string, Stream>
+        {
+            { "TestFile1.txt", new MemoryStream(System.Text.Encoding.UTF8.GetBytes("This is a test file 1.")) },
+            { "TestFile2.txt", new MemoryStream(System.Text.Encoding.UTF8.GetBytes("This is a test file 2.")) }
+        };
+        using var zipStream = _compressor.ZipStream(streams);
+        Assert.NotNull(zipStream);
+        Assert.True(zipStream.Length > 0);
+    }
+
+    [Fact]
+    public void Zip_ShouldCreateZipFileFromFiles()
+    {
+        var files = new List<string> { _testFile1, _testFile2 };
+        _compressor.Zip(files, _zipFile);
+        Assert.True(File.Exists(_zipFile));
+        Assert.True(new FileInfo(_zipFile).Length > 0);
+    }
+
+    [Fact]
+    public void Zip_ShouldCreateZipFileFromDirectory()
+    {
+        _compressor.Zip(_testDir, _zipFile);
+        Assert.True(File.Exists(_zipFile));
+        Assert.True(new FileInfo(_zipFile).Length > 0);
+    }
+
+    [Fact]
+    public void Zip_ShouldCreateZipFileFromStreams()
+    {
+        var streams = new DisposableDictionary<string, Stream>
+        {
+            { "TestFile1.txt", new MemoryStream(System.Text.Encoding.UTF8.GetBytes("This is a test file 1.")) },
+            { "TestFile2.txt", new MemoryStream(System.Text.Encoding.UTF8.GetBytes("This is a test file 2.")) }
+        };
+        _compressor.Zip(streams, _zipFile);
+        Assert.True(File.Exists(_zipFile));
+        Assert.True(new FileInfo(_zipFile).Length > 0);
+    }
+
+    [Fact]
+    public void Decompress_ShouldExtractFiles()
+    {
+        _compressor.Zip(_testDir, _zipFile);
+        var extractDir = Path.Combine(Path.GetTempPath(), "ExtractDir");
+        if (Directory.Exists(extractDir))
+        {
+            Directory.Delete(extractDir, true);
+        }
+
+        _compressor.Decompress(_zipFile, extractDir);
+        Assert.True(Directory.Exists(extractDir));
+        Assert.True(File.Exists(Path.Combine(extractDir, "TestFile1.txt")));
+        Assert.True(File.Exists(Path.Combine(extractDir, "TestFile2.txt")));
+    }
+}

+ 67 - 0
Test/Masuit.Tools.Abstractions.Test/Files/TextEncodingDetectorTests.cs

@@ -0,0 +1,67 @@
+using System;
+using System.IO;
+using System.Text;
+using Masuit.Tools.Files;
+using Masuit.Tools.Systems;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Files;
+
+public class TextEncodingDetectorTests
+{
+    [Theory]
+    [InlineData("Hello, World!", "ASCII")]
+    [InlineData("Hello, World!", "UTF-8")]
+    [InlineData("Hello, World!", "Unicode")]
+    [InlineData("Hello, World!", "UTF-32")]
+    public void GetEncoding_ShouldDetectEncodingFromFile(string content, string encodingName)
+    {
+        string _testFilePath = Path.Combine(Path.GetTempPath(), "testfile1.txt");
+        var encoding = Encoding.GetEncoding(encodingName);
+        File.WriteAllText(_testFilePath, content, encoding);
+        var detectedEncoding = TextEncodingDetector.GetEncoding(_testFilePath);
+        Assert.Equal(encoding, detectedEncoding);
+    }
+
+    [Theory]
+    [InlineData("Hello, World!", "ASCII")]
+    [InlineData("Hello, World!", "UTF-8")]
+    [InlineData("Hello, World!", "Unicode")]
+    [InlineData("Hello, World!", "UTF-32")]
+    public void GetEncoding_ShouldDetectEncodingFromFileInfo(string content, string encodingName)
+    {
+        string _testFilePath = Path.Combine(Path.GetTempPath(), "testfile2.txt");
+        var encoding = Encoding.GetEncoding(encodingName);
+        File.WriteAllText(_testFilePath, content, encoding);
+        var fileInfo = new FileInfo(_testFilePath);
+        var detectedEncoding = fileInfo.GetEncoding();
+        Assert.Equal(encoding, detectedEncoding);
+    }
+
+    [Theory]
+    [InlineData("Hello, World!", "ASCII")]
+    [InlineData("Hello, World!", "UTF-8")]
+    [InlineData("Hello, World!", "Unicode")]
+    [InlineData("Hello, World!", "UTF-32")]
+    public void GetEncoding_ShouldDetectEncodingFromStream(string content, string encodingName)
+    {
+        string _testFilePath = Path.Combine(Path.GetTempPath(), "testfile3.txt");
+        var encoding = Encoding.GetEncoding(encodingName);
+        File.WriteAllText(_testFilePath, content, encoding);
+        using var stream = new FileStream(_testFilePath, FileMode.Open, FileAccess.Read);
+        var detectedEncoding = stream.GetEncoding();
+        Assert.Equal(encoding, detectedEncoding);
+    }
+
+    [Theory]
+    [InlineData(new byte[] { 255, 254, 0, 0 }, "UTF-32")]
+    [InlineData(new byte[] { 0, 0, 254, 255 }, "utf-32BE")]
+    [InlineData(new byte[] { 239, 187, 191 }, "UTF-8")]
+    [InlineData(new byte[] { 72, 101, 108, 108 }, "ASCII")]
+    public void GetEncoding_ShouldDetectEncodingFromBytes(byte[] bytes, string expectedEncodingName)
+    {
+        var expectedEncoding = Encoding.GetEncoding(expectedEncodingName);
+        var detectedEncoding = new PooledMemoryStream(bytes).GetEncoding();
+        Assert.Equal(expectedEncoding, detectedEncoding);
+    }
+}

+ 24 - 38
Test/Masuit.Tools.Abstractions.Test/Hardware/SystemInfo_UnitTest.cs

@@ -33,7 +33,7 @@ public class SystemInfo_UnitTest
     public void SystemInfo_CpuLoad_IfNotWinPlatform()
     {
         float res = SystemInfo.CpuLoad;
-        Assert.True(res == 0);
+        Assert.True(res >= 0);
     }
 
     [Fact]
@@ -43,13 +43,6 @@ public class SystemInfo_UnitTest
         Assert.True(res > 0);
     }
 
-    [Fact]
-    public void SystemInfo_GetCPUTemperature_IfNotWinPlatform()
-    {
-        float res = SystemInfo.GetCPUTemperature();
-        Assert.True(res == 0);
-    }
-
     [Fact]
     public void SystemInfo_GetCpuCount_MoreThanZero()
     {
@@ -61,105 +54,105 @@ public class SystemInfo_UnitTest
     public void SystemInfo_GetCpuInfo_IfNotWinPlatform()
     {
         var res = SystemInfo.GetCpuInfo();
-        Assert.True(res.Count == 0);
+        Assert.True(res.Count > 0);
     }
 
     [Fact]
     public void SystemInfo_MemoryAvailable_IfNotWinPlatform()
     {
         var res = SystemInfo.MemoryAvailable;
-        Assert.True(res == 0);
+        Assert.True(res > 0);
     }
 
     [Fact]
     public void SystemInfo_PhysicalMemory_IfNotWinPlatform()
     {
         var res = SystemInfo.PhysicalMemory;
-        Assert.True(res == 0);
+        Assert.True(res > 0);
     }
 
     [Fact]
     public void SystemInfo_GetMemoryVData_IfNotWinPlatform()
     {
         var res = SystemInfo.GetMemoryVData();
-        Assert.True(res == "0.000% (0.000 B / 0.000 B) ");
+        Assert.False(string.IsNullOrEmpty(res));
     }
 
     [Fact]
     public void SystemInfo_GetUsageVirtualMemory_IfNotWinPlatform()
     {
         var res = SystemInfo.GetUsageVirtualMemory();
-        Assert.True(res == 0);
+        Assert.True(res > 0);
     }
 
     [Fact]
     public void SystemInfo_GetUsedVirtualMemory_IfNotWinPlatform()
     {
         var res = SystemInfo.GetUsedVirtualMemory();
-        Assert.True(res == 0);
+        Assert.True(res > 0);
     }
 
     [Fact]
     public void SystemInfo_GetTotalVirtualMemory_IfNotWinPlatform()
     {
         var res = SystemInfo.GetTotalVirtualMemory();
-        Assert.True(res == 0);
+        Assert.True(res > 0);
     }
 
     [Fact]
     public void SystemInfo_GetMemoryPData_IfNotWinPlatform()
     {
         var res = SystemInfo.GetMemoryPData();
-        Assert.True(res == "");
+        Assert.False(string.IsNullOrEmpty(res));
     }
 
     [Fact]
     public void SystemInfo_GetTotalPhysicalMemory_IfNotWinPlatform()
     {
         var res = SystemInfo.GetTotalPhysicalMemory();
-        Assert.True(res == 0);
+        Assert.True(res > 0);
     }
 
     [Fact]
     public void SystemInfo_GetFreePhysicalMemory_IfNotWinPlatform()
     {
         var res = SystemInfo.GetFreePhysicalMemory();
-        Assert.True(res == 0);
+        Assert.True(res > 0);
     }
 
     [Fact]
     public void SystemInfo_GetUsedPhysicalMemory_IfNotWinPlatform()
     {
         var res = SystemInfo.GetUsedPhysicalMemory();
-        Assert.True(res == 0);
+        Assert.True(res > 0);
     }
 
     [Fact]
     public void SystemInfo_GetDiskData_Read_IfNotWinPlatform()
     {
         var res = SystemInfo.GetDiskData(DiskData.Read);
-        Assert.True(res == 0);
+        Assert.True(res >= 0);
     }
 
     [Fact]
     public void SystemInfo_GetDiskData_Write_IfNotWinPlatform()
     {
         var res = SystemInfo.GetDiskData(DiskData.Write);
-        Assert.True(res == 0);
+        Assert.True(res >= 0);
     }
 
     [Fact]
     public void SystemInfo_GetDiskData_ReadAndWrite_IfNotWinPlatform()
     {
         var res = SystemInfo.GetDiskData(DiskData.ReadAndWrite);
-        Assert.True(res == 0);
+        Assert.True(res >= 0);
     }
 
     [Fact]
     public void SystemInfo_GetDiskInfo_IfNotWinPlatform()
     {
         var res = SystemInfo.GetDiskInfo();
-        Assert.True(res.Count == 0);
+        Assert.True(res.Count > 0);
     }
 
     [Fact]
@@ -173,35 +166,35 @@ public class SystemInfo_UnitTest
     public void SystemInfo_GetNetData_Received_IfNotWinPlatform()
     {
         var res = SystemInfo.GetNetData(NetData.Received);
-        Assert.True(res == 0);
+        Assert.True(res >= 0);
     }
 
     [Fact]
     public void SystemInfo_GetNetData_ReceivedAndSent_IfNotWinPlatform()
     {
         var res = SystemInfo.GetNetData(NetData.ReceivedAndSent);
-        Assert.True(res == 0);
+        Assert.True(res >= 0);
     }
 
     [Fact]
     public void SystemInfo_GetMacAddress_IfNotWinPlatform()
     {
         var res = SystemInfo.GetMacAddress();
-        Assert.True(!res.Any());
+        Assert.True(res.Any());
     }
 
     [Fact]
     public void SystemInfo_GetLocalUsedIP_IfNotWinPlatform()
     {
         var res = SystemInfo.GetLocalUsedIP();
-        Assert.True(res == null);
+        Assert.True(res != null);
     }
 
     [Fact]
     public void SystemInfo_GetIPAddressWMI_IfNotWinPlatform()
     {
         var res = SystemInfo.GetIPAddressWMI();
-        Assert.True(res == "");
+        Assert.False(string.IsNullOrEmpty(res));
     }
 
     [Fact]
@@ -215,27 +208,20 @@ public class SystemInfo_UnitTest
     public void SystemInfo_GetNetworkCardAddress_IfNotWinPlatform()
     {
         var res = SystemInfo.GetNetworkCardAddress();
-        Assert.True(res == "");
-    }
-
-    [Fact]
-    public void SystemInfo_BootTime_IfNotWinPlatform()
-    {
-        var res = SystemInfo.BootTime();
-        Assert.True(res == DateTime.MinValue);
+        Assert.False(string.IsNullOrEmpty(res));
     }
 
     [Fact]
     public void SystemInfo_FindAllApps_IfNotWinPlatform()
     {
         var res = SystemInfo.FindAllApps(0);
-        Assert.True(res == null);
+        Assert.Equal(res.Count, 0);
     }
 
     [Fact]
     public void SystemInfo_GetSystemType()
     {
         var res = SystemInfo.GetSystemType();
-        Assert.True(!string.IsNullOrEmpty(res));
+        Assert.Equal("x64-based PC", res);
     }
 }

+ 100 - 0
Test/Masuit.Tools.Abstractions.Test/Html/HtmlToolsTests.cs

@@ -0,0 +1,100 @@
+using System.Linq;
+using Masuit.Tools.Html;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Html;
+
+public class HtmlToolsTests
+{
+    [Fact]
+    public void HtmlSanitizerStandard_ShouldSanitizeHtml()
+    {
+        var html = "<div><script>alert('xss');</script><p>Test</p></div>";
+        var sanitizedHtml = html.HtmlSanitizerStandard();
+        Assert.DoesNotContain("<script>", sanitizedHtml);
+        Assert.Contains("<p>Test</p>", sanitizedHtml);
+    }
+
+    [Fact]
+    public void HtmlSanitizerCustom_ShouldSanitizeHtmlWithCustomSettings()
+    {
+        var html = "<div><script>alert('xss');</script><p>Test</p></div>";
+        var sanitizedHtml = html.HtmlSanitizerCustom(new[] { "script" });
+        Assert.DoesNotContain("<script>", sanitizedHtml);
+        Assert.Contains("<p>Test</p>", sanitizedHtml);
+    }
+
+    [Fact]
+    public void RemoveHtmlTag_ShouldRemoveHtmlTags()
+    {
+        var html = "<div><p>Test</p></div>";
+        var result = html.RemoveHtmlTag();
+        Assert.Equal("Test", result);
+    }
+
+    [Fact]
+    public void RemoveHtmlTag_ShouldRemoveHtmlTagsAndTruncate()
+    {
+        var html = "<div><p>Test</p></div>";
+        var result = html.RemoveHtmlTag(2);
+        Assert.Equal("Te", result);
+    }
+
+    [Fact]
+    public void ReplaceHtmlImgSource_ShouldReplaceImgSrc()
+    {
+        var html = "<img src=\"image.jpg\">";
+        var result = html.ReplaceHtmlImgSource("http://example.com");
+        Assert.Equal("<img src=\"http://example.com/image.jpg\">", result);
+    }
+
+    [Fact]
+    public void ConvertImgSrcToRelativePath_ShouldConvertToRelativePath()
+    {
+        var html = "<img src=\"http://example.com/image.jpg\">";
+        var result = html.ConvertImgSrcToRelativePath();
+        Assert.Equal("<img src=\"/image.jpg\">", result);
+    }
+
+    [Fact]
+    public void MatchImgTags_ShouldReturnImgTags()
+    {
+        var html = "<div><img src=\"image1.jpg\"><img src=\"image2.jpg\"></div>";
+        var result = html.MatchImgTags();
+        Assert.Equal(2, result.Length);
+    }
+
+    [Fact]
+    public void MatchImgSrcs_ShouldReturnImgSrcs()
+    {
+        var html = "<div><img src=\"image1.jpg\"><img src=\"image2.jpg\"></div>";
+        var result = html.MatchImgSrcs();
+        Assert.Equal(2, result.Count());
+        Assert.Contains("image1.jpg", result);
+        Assert.Contains("image2.jpg", result);
+    }
+
+    [Fact]
+    public void MatchFirstImgSrc_ShouldReturnFirstImgSrc()
+    {
+        var html = "<div><img src=\"image1.jpg\"><img src=\"image2.jpg\"></div>";
+        var result = html.MatchFirstImgSrc();
+        Assert.Equal("image1.jpg", result);
+    }
+
+    [Fact]
+    public void MatchRandomImgSrc_ShouldReturnRandomImgSrc()
+    {
+        var html = "<div><img src=\"image1.jpg\"><img src=\"image2.jpg\"></div>";
+        var result = html.MatchRandomImgSrc();
+        Assert.Contains(result, new[] { "image1.jpg", "image2.jpg" });
+    }
+
+    [Fact]
+    public void EncodeHtml_ShouldEncodeHtml()
+    {
+        var html = "Hello, World!";
+        var result = html.EncodeHtml();
+        Assert.Equal("Hello&def World!", result);
+    }
+}

+ 137 - 0
Test/Masuit.Tools.Abstractions.Test/Linq/LinqExtensionTests.cs

@@ -0,0 +1,137 @@
+using System;
+using System.Linq.Expressions;
+using Masuit.Tools.Linq;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Linq;
+
+public class LinqExtensionTests
+{
+    [Fact]
+    public void And_ShouldCombineExpressionsWithAnd()
+    {
+        Expression<Func<int, bool>> expr1 = x => x > 5;
+        Expression<Func<int, bool>> expr2 = x => x < 10;
+
+        var combined = expr1.And(expr2);
+
+        Assert.True(combined.Compile()(7));
+        Assert.False(combined.Compile()(4));
+        Assert.False(combined.Compile()(11));
+    }
+
+    [Fact]
+    public void AndIf_ShouldCombineExpressionsWithAndIfConditionIsTrue()
+    {
+        Expression<Func<int, bool>> expr1 = x => x > 5;
+        Expression<Func<int, bool>> expr2 = x => x < 10;
+
+        var combined = expr1.AndIf(true, expr2);
+
+        Assert.True(combined.Compile()(7));
+        Assert.False(combined.Compile()(4));
+        Assert.False(combined.Compile()(11));
+    }
+
+    [Fact]
+    public void AndIf_ShouldNotCombineExpressionsWithAndIfConditionIsFalse()
+    {
+        Expression<Func<int, bool>> expr1 = x => x > 5;
+        Expression<Func<int, bool>> expr2 = x => x < 10;
+
+        var combined = expr1.AndIf(false, expr2);
+
+        Assert.True(combined.Compile()(7));
+        Assert.True(combined.Compile()(11));
+    }
+
+    [Fact]
+    public void AndIf_WithFuncCondition_ShouldCombineExpressionsWithAndIfConditionIsTrue()
+    {
+        Expression<Func<int, bool>> expr1 = x => x > 5;
+        Expression<Func<int, bool>> expr2 = x => x < 10;
+
+        var combined = expr1.AndIf(() => true, expr2);
+
+        Assert.True(combined.Compile()(7));
+        Assert.False(combined.Compile()(4));
+        Assert.False(combined.Compile()(11));
+    }
+
+    [Fact]
+    public void AndIf_WithFuncCondition_ShouldNotCombineExpressionsWithAndIfConditionIsFalse()
+    {
+        Expression<Func<int, bool>> expr1 = x => x > 5;
+        Expression<Func<int, bool>> expr2 = x => x < 10;
+
+        var combined = expr1.AndIf(() => false, expr2);
+
+        Assert.True(combined.Compile()(7));
+        Assert.True(combined.Compile()(11));
+    }
+
+    [Fact]
+    public void Or_ShouldCombineExpressionsWithOr()
+    {
+        Expression<Func<int, bool>> expr1 = x => x > 5;
+        Expression<Func<int, bool>> expr2 = x => x < 10;
+
+        var combined = expr1.Or(expr2);
+
+        Assert.True(combined.Compile()(7));
+        Assert.True(combined.Compile()(4));
+        Assert.True(combined.Compile()(11));
+    }
+
+    [Fact]
+    public void OrIf_ShouldCombineExpressionsWithOrIfConditionIsTrue()
+    {
+        Expression<Func<int, bool>> expr1 = x => x > 5;
+        Expression<Func<int, bool>> expr2 = x => x < 10;
+
+        var combined = expr1.OrIf(true, expr2);
+
+        Assert.True(combined.Compile()(7));
+        Assert.True(combined.Compile()(4));
+        Assert.True(combined.Compile()(11));
+    }
+
+    [Fact]
+    public void OrIf_ShouldNotCombineExpressionsWithOrIfConditionIsFalse()
+    {
+        Expression<Func<int, bool>> expr1 = x => x > 5;
+        Expression<Func<int, bool>> expr2 = x => x < 10;
+
+        var combined = expr1.OrIf(false, expr2);
+
+        Assert.True(combined.Compile()(7));
+        Assert.False(combined.Compile()(4));
+        Assert.True(combined.Compile()(11));
+    }
+
+    [Fact]
+    public void OrIf_WithFuncCondition_ShouldCombineExpressionsWithOrIfConditionIsTrue()
+    {
+        Expression<Func<int, bool>> expr1 = x => x > 5;
+        Expression<Func<int, bool>> expr2 = x => x < 10;
+
+        var combined = expr1.OrIf(() => true, expr2);
+
+        Assert.True(combined.Compile()(7));
+        Assert.True(combined.Compile()(4));
+        Assert.True(combined.Compile()(11));
+    }
+
+    [Fact]
+    public void OrIf_WithFuncCondition_ShouldNotCombineExpressionsWithOrIfConditionIsFalse()
+    {
+        Expression<Func<int, bool>> expr1 = x => x > 5;
+        Expression<Func<int, bool>> expr2 = x => x < 10;
+
+        var combined = expr1.OrIf(() => false, expr2);
+
+        Assert.True(combined.Compile()(7));
+        Assert.False(combined.Compile()(4));
+        Assert.True(combined.Compile()(11));
+    }
+}

+ 159 - 0
Test/Masuit.Tools.Abstractions.Test/Logging/LogManagerTests.cs

@@ -0,0 +1,159 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using Masuit.Tools.Logging;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Logging;
+
+public class LogManagerTests
+{
+    private readonly string _logDirectory;
+
+    public LogManagerTests()
+    {
+        _logDirectory = Path.Combine(Path.GetTempPath(), "logs");
+        LogManager.LogDirectory = _logDirectory;
+
+        if (Directory.Exists(_logDirectory))
+        {
+            Directory.Delete(_logDirectory, true);
+        }
+
+        Directory.CreateDirectory(_logDirectory);
+    }
+
+    [Fact]
+    public void Info_ShouldWriteInfoLog()
+    {
+        LogManager.Info("Test info message");
+        Thread.Sleep(2000); // 等待日志写入
+
+        var logFile = Directory.GetFiles(_logDirectory).FirstOrDefault();
+        Assert.NotNull(logFile);
+        var logContent = File.ReadAllText(logFile);
+        Assert.Contains("INFO", logContent);
+        Assert.Contains("Test info message", logContent);
+    }
+
+    [Fact]
+    public void Info_WithSource_ShouldWriteInfoLog()
+    {
+        LogManager.Info("TestSource", "Test info message");
+        Thread.Sleep(2000); // 等待日志写入
+
+        var logFile = Directory.GetFiles(_logDirectory).FirstOrDefault();
+        Assert.NotNull(logFile);
+        var logContent = File.ReadAllText(logFile);
+        Assert.Contains("INFO", logContent);
+        Assert.Contains("TestSource", logContent);
+        Assert.Contains("Test info message", logContent);
+    }
+
+    [Fact]
+    public void Debug_ShouldWriteDebugLog()
+    {
+        LogManager.Debug("Test debug message");
+        Thread.Sleep(2000); // 等待日志写入
+
+        var logFile = Directory.GetFiles(_logDirectory).FirstOrDefault();
+        Assert.NotNull(logFile);
+        var logContent = File.ReadAllText(logFile);
+        Assert.Contains("DEBUG", logContent);
+        Assert.Contains("Test debug message", logContent);
+    }
+
+    [Fact]
+    public void Debug_WithSource_ShouldWriteDebugLog()
+    {
+        LogManager.Debug("TestSource", "Test debug message");
+        Thread.Sleep(2000); // 等待日志写入
+
+        var logFile = Directory.GetFiles(_logDirectory).FirstOrDefault();
+        Assert.NotNull(logFile);
+        var logContent = File.ReadAllText(logFile);
+        Assert.Contains("DEBUG", logContent);
+        Assert.Contains("TestSource", logContent);
+        Assert.Contains("Test debug message", logContent);
+    }
+
+    [Fact]
+    public void Error_ShouldWriteErrorLog()
+    {
+        var exception = new Exception("Test error message");
+        LogManager.Error(exception);
+        Thread.Sleep(2000); // 等待日志写入
+
+        var logFile = Directory.GetFiles(_logDirectory).FirstOrDefault();
+        Assert.NotNull(logFile);
+        var logContent = File.ReadAllText(logFile);
+        Assert.Contains("ERROR", logContent);
+        Assert.Contains("Test error message", logContent);
+    }
+
+    [Fact]
+    public void Error_WithSource_ShouldWriteErrorLog()
+    {
+        var exception = new Exception("Test error message");
+        LogManager.Error("TestSource", exception);
+        Thread.Sleep(2000); // 等待日志写入
+
+        var logFile = Directory.GetFiles(_logDirectory).FirstOrDefault();
+        Assert.NotNull(logFile);
+        var logContent = File.ReadAllText(logFile);
+        Assert.Contains("ERROR", logContent);
+        Assert.Contains("TestSource", logContent);
+        Assert.Contains("Test error message", logContent);
+    }
+
+    [Fact]
+    public void Fatal_ShouldWriteFatalLog()
+    {
+        var exception = new Exception("Test fatal message");
+        LogManager.Fatal(exception);
+        Thread.Sleep(2000); // 等待日志写入
+
+        var logFile = Directory.GetFiles(_logDirectory).FirstOrDefault();
+        Assert.NotNull(logFile);
+        var logContent = File.ReadAllText(logFile);
+        Assert.Contains("FATAL", logContent);
+        Assert.Contains("Test fatal message", logContent);
+    }
+
+    [Fact]
+    public void Fatal_WithSource_ShouldWriteFatalLog()
+    {
+        var exception = new Exception("Test fatal message");
+        LogManager.Fatal("TestSource", exception);
+        Thread.Sleep(2000); // 等待日志写入
+
+        var logFile = Directory.GetFiles(_logDirectory).FirstOrDefault();
+        Assert.NotNull(logFile);
+        var logContent = File.ReadAllText(logFile);
+        Assert.Contains("FATAL", logContent);
+        Assert.Contains("TestSource", logContent);
+        Assert.Contains("Test fatal message", logContent);
+    }
+
+    [Fact]
+    public void LogDirectory_ShouldSetAndGetLogDirectory()
+    {
+        var customLogDirectory = Path.Combine(Path.GetTempPath(), "custom_logs");
+        LogManager.LogDirectory = customLogDirectory;
+
+        Assert.Equal(customLogDirectory, LogManager.LogDirectory);
+    }
+
+    [Fact]
+    public void Event_ShouldTriggerOnLog()
+    {
+        var eventTriggered = false;
+        LogManager.Event += log => eventTriggered = true;
+
+        LogManager.Info("Test info message");
+        Thread.Sleep(2000); // 等待日志写入
+
+        Assert.True(eventTriggered);
+    }
+}

+ 6 - 0
Test/Masuit.Tools.Abstractions.Test/Masuit.Tools.Abstractions.Test.csproj

@@ -14,6 +14,7 @@
 
   <ItemGroup>
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
+    <PackageReference Include="Moq" Version="4.20.72" />
     <PackageReference Include="xunit" Version="2.9.2" />
     <PackageReference Include="xunit.runner.visualstudio" Version="3.0.0">
       <PrivateAssets>all</PrivateAssets>
@@ -31,6 +32,11 @@
 
   <ItemGroup>
     <Folder Include="Tree\" />
+    <Folder Include="Files\" />
+    <Folder Include="Html\" />
+    <Folder Include="Linq\" />
+    <Folder Include="Media\" />
+    <Folder Include="TextDiff\" />
   </ItemGroup>
 
 </Project>

+ 27 - 0
Test/Masuit.Tools.Abstractions.Test/Maths/CoordinateConvertTests.cs

@@ -0,0 +1,27 @@
+using Masuit.Tools.Maths;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Maths;
+
+public class CoordinateConvertTests
+{
+    [Theory]
+    [InlineData(116.403981, 39.915292, 116.39762729119315, 39.9089)]
+    [InlineData(120.153576, 30.287459, 120.1471, 30.2814)]
+    public void BD09ToGCJ02_ShouldConvertCorrectly(double bdLon, double bdLat, double expectedGcjLon, double expectedGcjLat)
+    {
+        CoordinateConvert.BD09ToGCJ02(bdLon, bdLat, out double gcjLon, out double gcjLat);
+        Assert.Equal(expectedGcjLon, gcjLon, 4);
+        Assert.Equal(expectedGcjLat, gcjLat, 4);
+    }
+
+    [Theory]
+    [InlineData(116.39762729119315, 39.91974545536095, 116.403981, 39.9261)]
+    [InlineData(120.147748, 30.2866, 120.1542, 30.29259)]
+    public void GCJ02ToBD09_ShouldConvertCorrectly(double gcjLon, double gcjLat, double expectedBdLon, double expectedBdLat)
+    {
+        CoordinateConvert.GCJ02ToBD09(gcjLon, gcjLat, out double bdLon, out double bdLat);
+        Assert.Equal(expectedBdLon, bdLon, 4);
+        Assert.Equal(expectedBdLat, bdLat, 4);
+    }
+}

+ 38 - 0
Test/Masuit.Tools.Abstractions.Test/Maths/PercentileSelectorTests.cs

@@ -0,0 +1,38 @@
+using System;
+using Masuit.Tools.Maths;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Maths;
+
+public class PercentileSelectorTests
+{
+    [Theory]
+    [InlineData(new[] { 1, 2, 3, 4, 5 }, 50, 3)]
+    [InlineData(new[] { 1, 2, 3, 4, 5 }, 100, 5)]
+    [InlineData(new[] { 1, 2, 3, 4, 5, 6 }, 50, 3)]
+    [InlineData(new[] { 1, 2, 3, 4, 5, 6 }, 75, 5)]
+    public void Percentile_ShouldReturnCorrectElement(int[] arr, double percentile, int expected)
+    {
+        var result = arr.Percentile(percentile);
+        Assert.Equal(expected, result);
+    }
+
+    [Fact]
+    public void Percentile_ShouldReturnDefaultForEmptyArray()
+    {
+        var arr = new int[] { };
+        var result = arr.Percentile(50);
+        Assert.Equal(default, result);
+    }
+
+    [Theory]
+    [InlineData(new[] { 1.0, 2.0, 3.0, 4.0, 5.0 }, 50, 3.0)]
+    [InlineData(new[] { 1.0, 2.0, 3.0, 4.0, 5.0 }, 100, 5.0)]
+    [InlineData(new[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }, 50, 3.0)]
+    [InlineData(new[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }, 75, 5.0)]
+    public void Percentile_ShouldReturnCorrectElementForDoubles(double[] arr, double percentile, double expected)
+    {
+        var result = arr.Percentile(percentile);
+        Assert.Equal(expected, result);
+    }
+}

+ 54 - 0
Test/Masuit.Tools.Abstractions.Test/Maths/RadarChartEngineTests.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using Masuit.Tools.Maths;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Maths;
+
+public class RadarChartEngineTests
+{
+    [Fact]
+    public void ComputeArea_ShouldReturnCorrectArea()
+    {
+        var points = new List<Point2D>
+        {
+            new Point2D(0, 0),
+            new Point2D(4, 0),
+            new Point2D(4, 3),
+            new Point2D(0, 3)
+        };
+
+        var area = points.ComputeArea();
+        Assert.Equal(12, area, 6);
+    }
+
+    [Fact]
+    public void ComputeIntersection_ShouldReturnCorrectIntersection()
+    {
+        var firstChart = new RadarChart(new List<double> { 3, 4, 5 }, 0);
+        var secondChart = new RadarChart(new List<double> { 5, 3, 4 }, 0);
+
+        var intersection = firstChart.ComputeIntersection(secondChart);
+
+        Assert.NotNull(intersection);
+        Assert.Equal(5, intersection.Count);
+    }
+
+    [Fact]
+    public void ComputeIntersection_ShouldThrowExceptionForDifferentStartAngles()
+    {
+        var firstChart = new RadarChart(new List<double> { 3, 4, 5 }, 0);
+        var secondChart = new RadarChart(new List<double> { 5, 3, 4 }, 1);
+
+        Assert.Throws<ArgumentException>(() => firstChart.ComputeIntersection(secondChart));
+    }
+
+    [Fact]
+    public void ComputeIntersection_ShouldThrowExceptionForDifferentDataCounts()
+    {
+        var firstChart = new RadarChart(new List<double> { 3, 4, 5 }, 0);
+        var secondChart = new RadarChart(new List<double> { 5, 3 }, 0);
+
+        Assert.Throws<ArgumentException>(() => firstChart.ComputeIntersection(secondChart));
+    }
+}

+ 76 - 0
Test/Masuit.Tools.Abstractions.Test/Media/ImageDetectExtTests.cs

@@ -0,0 +1,76 @@
+using System.IO;
+using System.IO.Compression;
+using Masuit.Tools.Media;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.PixelFormats;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Media;
+
+public class ImageDetectExtTests
+{
+    [Fact]
+    public void IsImage_ShouldReturnTrueForValidImageFile()
+    {
+        var filePath = Path.Combine(Directory.GetCurrentDirectory(), "test.jpg");
+        using var image = new Image<Rgba32>(1, 1);
+        image.Save(filePath);
+
+        var fileInfo = new FileInfo(filePath);
+        var result = fileInfo.IsImage();
+
+        Assert.True(result);
+        File.Delete(filePath);
+    }
+
+    [Fact]
+    public void IsImage_ShouldReturnTrueForValidImageStream()
+    {
+        using var ms = new MemoryStream();
+        using var image = new Image<Rgba32>(1, 1);
+        image.Save(ms, new JpegEncoder());
+        ms.Seek(0, SeekOrigin.Begin);
+
+        var result = ms.IsImage();
+
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void IsImage_ShouldReturnFalseForInvalidImageStream()
+    {
+        using var ms = new MemoryStream();
+        using var writer = new StreamWriter(ms);
+        writer.Write("This is a test stream.");
+        writer.Flush();
+        ms.Seek(0, SeekOrigin.Begin);
+
+        Assert.Throws<UnknownImageFormatException>(() => ms.IsImage());
+    }
+
+    [Fact]
+    public void GetImageType_ShouldReturnCorrectImageFormat()
+    {
+        using var ms = new MemoryStream();
+        using var image = new Image<Rgba32>(1, 1);
+        image.Save(ms, new JpegEncoder());
+        ms.Seek(0, SeekOrigin.Begin);
+
+        var result = ms.GetImageType();
+
+        Assert.Equal(ImageFormat.Jpg, result);
+    }
+
+    [Fact]
+    public void GetImageType_ShouldReturnNullForInvalidImageStream()
+    {
+        using var ms = new MemoryStream();
+        using var writer = new StreamWriter(ms);
+        writer.Write("This is a test stream.");
+        writer.Flush();
+        ms.Seek(0, SeekOrigin.Begin);
+
+        Assert.Throws<UnknownImageFormatException>(() => ms.GetImageType());
+    }
+}

+ 93 - 0
Test/Masuit.Tools.Abstractions.Test/Media/ImageHasherTests.cs

@@ -0,0 +1,93 @@
+using System;
+using Masuit.Tools.Media;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.PixelFormats;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Media;
+
+public class ImageHasherTests
+{
+    private readonly ImageHasher _imageHasher;
+
+    public ImageHasherTests()
+    {
+        _imageHasher = new ImageHasher();
+    }
+
+    [Fact]
+    public void AverageHash64_ShouldReturnCorrectHash()
+    {
+        using var image = new Image<Rgba32>(8, 8);
+        var hash = _imageHasher.AverageHash64(image);
+        Assert.Equal(0UL, hash);
+    }
+
+    [Fact]
+    public void MedianHash64_ShouldReturnCorrectHash()
+    {
+        using var image = new Image<Rgba32>(8, 8);
+        var hash = _imageHasher.MedianHash64(image);
+        Assert.Equal(0UL, hash);
+    }
+
+    [Fact]
+    public void MedianHash256_ShouldReturnCorrectHash()
+    {
+        using var image = new Image<Rgba32>(16, 16);
+        var hash = _imageHasher.MedianHash256(image);
+        Assert.NotNull(hash);
+        Assert.Equal(4, hash.Length);
+    }
+
+    [Fact]
+    public void DifferenceHash64_ShouldReturnCorrectHash()
+    {
+        using var image = new Image<Rgba32>(9, 8);
+        var hash = _imageHasher.DifferenceHash64(image);
+        Assert.Equal(0UL, hash);
+    }
+
+    [Fact]
+    public void DifferenceHash256_ShouldReturnCorrectHash()
+    {
+        using var image = new Image<Rgba32>(17, 16);
+        var hash = _imageHasher.DifferenceHash256(image);
+        Assert.NotNull(hash);
+        Assert.Equal(4, hash.Length);
+    }
+
+    [Fact]
+    public void DctHash_ShouldReturnCorrectHash()
+    {
+        using var image = new Image<Rgba32>(32, 32);
+        var hash = _imageHasher.DctHash(image);
+        Assert.Equal(0UL, hash);
+    }
+
+    [Fact]
+    public void Compare_ShouldReturnCorrectSimilarity()
+    {
+        var hash1 = 0b1010101010101010101010101010101010101010101010101010101010101010UL;
+        var hash2 = 0b0101010101010101010101010101010101010101010101010101010101010101UL;
+        var similarity = ImageHasher.Compare(hash1, hash2);
+        Assert.Equal(0.0f, similarity);
+    }
+
+    [Fact]
+    public void Compare256_ShouldReturnCorrectSimilarity()
+    {
+        var hash1 = new ulong[] { 0b1010101010101010101010101010101010101010101010101010101010101010UL, 0b1010101010101010101010101010101010101010101010101010101010101010UL, 0b1010101010101010101010101010101010101010101010101010101010101010UL, 0b1010101010101010101010101010101010101010101010101010101010101010UL };
+        var hash2 = new ulong[] { 0b0101010101010101010101010101010101010101010101010101010101010101UL, 0b0101010101010101010101010101010101010101010101010101010101010101UL, 0b0101010101010101010101010101010101010101010101010101010101010101UL, 0b0101010101010101010101010101010101010101010101010101010101010101UL };
+        var similarity = ImageHasher.Compare(hash1, hash2);
+        Assert.Equal(0.0f, similarity);
+    }
+
+    [Fact]
+    public void Compare_ShouldThrowExceptionForDifferentLengthHashes()
+    {
+        var hash1 = new ulong[] { 0b1010101010101010101010101010101010101010101010101010101010101010UL };
+        var hash2 = new ulong[] { 0b0101010101010101010101010101010101010101010101010101010101010101UL, 0b0101010101010101010101010101010101010101010101010101010101010101UL };
+        Assert.Throws<ArgumentException>(() => ImageHasher.Compare(hash1, hash2));
+    }
+}

+ 164 - 0
Test/Masuit.Tools.Abstractions.Test/Media/ImageUtilitiesTests.cs

@@ -0,0 +1,164 @@
+using System.IO;
+using Masuit.Tools.Media;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Media;
+
+public class ImageUtilitiesTests
+{
+    [Theory]
+    [InlineData("image/pjpeg", true)]
+    [InlineData("image/jpeg", true)]
+    [InlineData("image/gif", true)]
+    [InlineData("image/bmpp", true)]
+    [InlineData("image/png", true)]
+    [InlineData("image/tiff", false)]
+    public void IsWebImage_ShouldReturnCorrectResult(string contentType, bool expected)
+    {
+        var result = ImageUtilities.IsWebImage(contentType);
+        Assert.Equal(expected, result);
+    }
+
+    [Fact]
+    public void CutImage_ShouldReturnCroppedImage()
+    {
+        using var image = new Image<Rgba32>(100, 100);
+        var rect = new Rectangle(10, 10, 50, 50);
+        var croppedImage = image.CutImage(rect);
+
+        Assert.Equal(50, croppedImage.Width);
+        Assert.Equal(50, croppedImage.Height);
+    }
+
+    [Fact]
+    public void CutAndResize_ShouldReturnCroppedAndResizedImage()
+    {
+        using var image = new Image<Rgba32>(100, 100);
+        var rect = new Rectangle(10, 10, 50, 50);
+        var resizedImage = image.CutAndResize(rect, 25, 25);
+
+        Assert.Equal(25, resizedImage.Width);
+        Assert.Equal(25, resizedImage.Height);
+    }
+
+    [Fact]
+    public void MakeThumbnail_ShouldCreateThumbnail()
+    {
+        using var image = new Image<Rgba32>(100, 100);
+        var thumbnailPath = Path.Combine(Directory.GetCurrentDirectory(), "thumbnail.jpg");
+        image.MakeThumbnail(thumbnailPath, 50, 50, ResizeMode.Crop);
+
+        Assert.True(File.Exists(thumbnailPath));
+        using var thumbnail = Image.Load(thumbnailPath);
+        Assert.Equal(50, thumbnail.Width);
+        Assert.Equal(50, thumbnail.Height);
+        File.Delete(thumbnailPath);
+    }
+
+    [Fact]
+    public void MakeThumbnail_ShouldReturnThumbnailImage()
+    {
+        using var image = new Image<Rgba32>(100, 100);
+        var thumbnail = image.MakeThumbnail(50, 50, ResizeMode.Crop);
+
+        Assert.Equal(50, thumbnail.Width);
+        Assert.Equal(50, thumbnail.Height);
+    }
+
+    [Fact]
+    public void LDPic_ShouldAdjustBrightness()
+    {
+        using var image = new Image<Rgba32>(100, 100);
+        var adjustedImage = image.LDPic(50);
+
+        Assert.NotEqual(image, adjustedImage);
+    }
+
+    [Fact]
+    public void RePic_ShouldReturnInvertedImage()
+    {
+        using var image = new Image<Rgba32>(100, 100);
+        var invertedImage = image.RePic();
+
+        Assert.NotEqual(image, invertedImage);
+    }
+
+    [Fact]
+    public void Relief_ShouldReturnReliefImage()
+    {
+        using var image = new Image<Rgba32>(100, 100);
+        var reliefImage = image.Relief();
+
+        Assert.NotEqual(image, reliefImage);
+    }
+
+    [Fact]
+    public void ResizeImage_ShouldReturnResizedImage()
+    {
+        using var image = new Image<Rgba32>(100, 100);
+        var resizedImage = image.ResizeImage(50, 50);
+
+        Assert.Equal(50, resizedImage.Width);
+        Assert.Equal(50, resizedImage.Height);
+    }
+
+    [Fact]
+    public void FilPic_ShouldReturnFilteredImage()
+    {
+        using var image = new Image<Rgba32>(100, 100);
+        var filteredImage = image.FilPic();
+
+        Assert.NotEqual(image, filteredImage);
+    }
+
+    [Fact]
+    public void RevPicLR_ShouldReturnHorizontallyFlippedImage()
+    {
+        using var image = new Image<Rgba32>(100, 120);
+        var flippedImage = image.RevPicLR();
+
+        Assert.Equal(image.Width, 100);
+        Assert.Equal(image.Height, 120);
+    }
+
+    [Fact]
+    public void RevPicUD_ShouldReturnVerticallyFlippedImage()
+    {
+        using var image = new Image<Rgba32>(100, 120);
+        var flippedImage = image.RevPicUD();
+
+        Assert.Equal(image.Width, 100);
+        Assert.Equal(image.Height, 120);
+    }
+
+    [Fact]
+    public void Gray_ShouldReturnGrayscaleColor()
+    {
+        var color = Color.Red;
+        var grayColor = color.Gray();
+
+        Assert.NotEqual(color, grayColor);
+    }
+
+    [Fact]
+    public void Reverse_ShouldReturnReversedColor()
+    {
+        var color = Color.Red;
+        var reversedColor = color.Reverse();
+
+        Assert.NotEqual(color, reversedColor);
+    }
+
+    [Fact]
+    public void BWPic_ShouldReturnBlackAndWhiteImage()
+    {
+        using var image = new Image<Rgba32>(100, 100);
+        var bwImage = image.BWPic(50, 50);
+
+        Assert.Equal(50, bwImage.Width);
+        Assert.Equal(50, bwImage.Height);
+    }
+}

+ 68 - 0
Test/Masuit.Tools.Abstractions.Test/Media/ImageWatermarkerTests.cs

@@ -0,0 +1,68 @@
+using System;
+using System.IO;
+using Masuit.Tools.Media;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.PixelFormats;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Media;
+
+public class ImageWatermarkerTests
+{
+    [Fact]
+    public void AddWatermark_ShouldAddImageWatermark()
+    {
+        var imagePath = Path.Combine(Directory.GetCurrentDirectory(), "test4.jpg");
+        using var image = new Image<Rgba32>(100, 100);
+        image.Save(imagePath);
+
+        var watermarkPath = Path.Combine(Directory.GetCurrentDirectory(), "watermark.png");
+        using var watermark = new Image<Rgba32>(20, 20);
+        watermark.Save(watermarkPath);
+
+        using var imageStream = new FileStream(imagePath, FileMode.Open, FileAccess.Read);
+        using var watermarkStream = new FileStream(watermarkPath, FileMode.Open, FileAccess.Read);
+        var watermarker = new ImageWatermarker(imageStream);
+        var resultStream = watermarker.AddWatermark(watermarkStream);
+
+        Assert.NotNull(resultStream);
+        Assert.True(resultStream.Length > 0);
+        try
+        {
+            File.Delete(imagePath);
+            File.Delete(watermarkPath);
+        }
+        catch (Exception e)
+        {
+        }
+    }
+
+    [Fact]
+    public void AddWatermark_ShouldAddImageWatermarkWithSkipSmallImages()
+    {
+        var imagePath = Path.Combine(Directory.GetCurrentDirectory(), "test5.jpg");
+        using var image = new Image<Rgba32>(50, 50);
+        image.Save(imagePath);
+
+        var watermarkPath = Path.Combine(Directory.GetCurrentDirectory(), "watermark.png");
+        using var watermark = new Image<Rgba32>(20, 20);
+        watermark.Save(watermarkPath);
+
+        using var imageStream = new FileStream(imagePath, FileMode.Open, FileAccess.Read);
+        using var watermarkStream = new FileStream(watermarkPath, FileMode.Open, FileAccess.Read);
+        var watermarker = new ImageWatermarker(imageStream, new JpegEncoder(), true, 10000);
+        var resultStream = watermarker.AddWatermark(watermarkStream);
+
+        Assert.NotNull(resultStream);
+        Assert.True(resultStream.Length > 0);
+        try
+        {
+            File.Delete(imagePath);
+            File.Delete(watermarkPath);
+        }
+        catch (Exception e)
+        {
+        }
+    }
+}

+ 102 - 0
Test/Masuit.Tools.Abstractions.Test/Mime/MimeMapperTests.cs

@@ -0,0 +1,102 @@
+using Masuit.Tools.Mime;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Tests
+{
+    public class MimeMapperTests
+    {
+        private MimeMapper _mimeMapper = new();
+
+        [Fact]
+        public void GetMimeFromExtension_ShouldReturnCorrectMimeType()
+        {
+            // Arrange
+            var extension = ".txt";
+
+            // Act
+            var mimeType = _mimeMapper.GetMimeFromExtension(extension);
+
+            // Assert
+            Assert.Equal("text/plain", mimeType);
+        }
+
+        [Fact]
+        public void GetMimeFromExtension_ShouldReturnDefaultMimeType_WhenExtensionNotFound()
+        {
+            // Arrange
+            var extension = ".unknown";
+
+            // Act
+            var mimeType = _mimeMapper.GetMimeFromExtension(extension);
+
+            // Assert
+            Assert.Equal(MimeMapper.DefaultMime, mimeType);
+        }
+
+        [Fact]
+        public void GetExtensionFromMime_ShouldReturnCorrectExtension()
+        {
+            // Arrange
+            var mimeType = "text/plain";
+
+            // Act
+            var extensions = _mimeMapper.GetExtensionFromMime(mimeType);
+
+            // Assert
+            Assert.True(extensions.Contains(".txt"));
+        }
+
+        [Fact]
+        public void GetExtensionFromMime_ShouldReturnEmptyString_WhenMimeTypeNotFound()
+        {
+            // Arrange
+            var mimeType = "unknown/type";
+
+            // Act
+            var extension = _mimeMapper.GetExtensionFromMime(mimeType);
+
+            // Assert
+            Assert.Equal(extension.Count, 0);
+        }
+
+        [Fact]
+        public void GetMimeFromPath_ShouldReturnCorrectMimeType()
+        {
+            // Arrange
+            var path = "file.txt";
+
+            // Act
+            var mimeType = _mimeMapper.GetMimeFromPath(path);
+
+            // Assert
+            Assert.Equal("text/plain", mimeType);
+        }
+
+        [Fact]
+        public void GetMimeFromPath_ShouldReturnDefaultMimeType_WhenExtensionNotFound()
+        {
+            // Arrange
+            var path = "file.unknown";
+
+            // Act
+            var mimeType = _mimeMapper.GetMimeFromPath(path);
+
+            // Assert
+            Assert.Equal(MimeMapper.DefaultMime, mimeType);
+        }
+
+        [Fact]
+        public void Extend_ShouldOverrideDefaultMimeType()
+        {
+            // Arrange
+            var customMapping = new MimeMappingItem { Extension = ".txt", MimeType = "custom/type" };
+
+            // Act
+            _mimeMapper.Extend(customMapping);
+            var mimeType = _mimeMapper.GetMimeFromExtension(".txt");
+
+            // Assert
+            Assert.Equal("custom/type", mimeType);
+        }
+    }
+}

+ 130 - 0
Test/Masuit.Tools.Abstractions.Test/Models/SphereTests.cs

@@ -0,0 +1,130 @@
+using System;
+using Masuit.Tools.Maths;
+using Masuit.Tools.Models;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Models;
+
+public class SphereTests
+{
+    [Fact]
+    public void EarthRadius_ShouldBeCorrect()
+    {
+        // Arrange
+        var expectedRadius = 6371.393;
+
+        // Act
+        var earth = Sphere.Earth;
+
+        // Assert
+        Assert.Equal(expectedRadius, earth.Radius);
+    }
+
+    [Fact]
+    public void GetDistance_ShouldReturnCorrectDistance()
+    {
+        // Arrange
+        var sphere = new Sphere(6371.393);
+        var lat1 = 0.0;
+        var lng1 = 0.0;
+        var lat2 = 0.0;
+        var lng2 = 90.0;
+        var expectedDistance = Math.PI * sphere.Radius / 2;
+
+        // Act
+        var distance = sphere.GetDistance(lat1, lng1, lat2, lng2);
+
+        // Assert
+        Assert.Equal(expectedDistance, distance, 1e-6);
+    }
+
+    [Fact]
+    public void IsCrossWith_ShouldReturnTrue_WhenCirclesCross()
+    {
+        // Arrange
+        var sphere = new Sphere(6371.393);
+        var circle1 = new Circle(new Point2D(0, 0), 10);
+        var circle2 = new Circle(new Point2D(15, 0), 10);
+
+        // Act
+        var result = sphere.IsCrossWith(circle1, circle2);
+
+        // Assert
+        Assert.False(result);
+    }
+
+    [Fact]
+    public void IsCrossWith_ShouldReturnFalse_WhenCirclesDoNotCross()
+    {
+        // Arrange
+        var sphere = new Sphere(6371.393);
+        var circle1 = new Circle(new Point2D(0, 0), 10);
+        var circle2 = new Circle(new Point2D(30, 0), 10);
+
+        // Act
+        var result = sphere.IsCrossWith(circle1, circle2);
+
+        // Assert
+        Assert.False(result);
+    }
+
+    [Fact]
+    public void IsIntersectWith_ShouldReturnTrue_WhenCirclesIntersect()
+    {
+        // Arrange
+        var sphere = new Sphere(6371.393);
+        var circle1 = new Circle(new Point2D(0, 0), 10);
+        var circle2 = new Circle(new Point2D(20, 0), 10);
+
+        // Act
+        var result = sphere.IsIntersectWith(circle1, circle2);
+
+        // Assert
+        Assert.False(result);
+    }
+
+    [Fact]
+    public void IsIntersectWith_ShouldReturnFalse_WhenCirclesDoNotIntersect()
+    {
+        // Arrange
+        var sphere = new Sphere(6371.393);
+        var circle1 = new Circle(new Point2D(0, 0), 10);
+        var circle2 = new Circle(new Point2D(25, 0), 10);
+
+        // Act
+        var result = sphere.IsIntersectWith(circle1, circle2);
+
+        // Assert
+        Assert.False(result);
+    }
+
+    [Fact]
+    public void IsSeparateWith_ShouldReturnTrue_WhenCirclesAreSeparate()
+    {
+        // Arrange
+        var sphere = new Sphere(6371.393);
+        var circle1 = new Circle(new Point2D(0, 0), 10);
+        var circle2 = new Circle(new Point2D(30, 0), 10);
+
+        // Act
+        var result = sphere.IsSeparateWith(circle1, circle2);
+
+        // Assert
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void IsSeparateWith_ShouldReturnFalse_WhenCirclesAreNotSeparate()
+    {
+        // Arrange
+        var sphere = new Sphere(6371.393);
+        var circle1 = new Circle(new Point2D(0, 0), 10);
+        var circle2 = new Circle(new Point2D(15, 0), 10);
+
+        // Act
+        var result = sphere.IsSeparateWith(circle1, circle2);
+
+        // Assert
+        Assert.True(result);
+    }
+}

+ 116 - 0
Test/Masuit.Tools.Abstractions.Test/RandomSelector/ExtensionsTests.cs

@@ -0,0 +1,116 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Masuit.Tools.RandomSelector;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.RandomSelector;
+
+public class ExtensionsTests
+{
+    [Fact]
+    public void TotalWeight_ShouldReturnCorrectWeight()
+    {
+        var items = new List<WeightedItem<string>>
+        {
+            new WeightedItem<string>("A", 1),
+            new WeightedItem<string>("B", 2),
+            new WeightedItem<string>("C", 3)
+        };
+        var selector = new WeightedSelector<string>(items);
+
+        int totalWeight = selector.TotalWeight();
+
+        Assert.Equal(6, totalWeight);
+    }
+
+    [Fact]
+    public void OrderByWeightDescending_ShouldReturnItemsInDescendingOrder()
+    {
+        var items = new List<WeightedItem<string>>
+        {
+            new WeightedItem<string>("A", 1),
+            new WeightedItem<string>("B", 3),
+            new WeightedItem<string>("C", 2)
+        };
+        var selector = new WeightedSelector<string>(items);
+
+        var orderedItems = selector.OrderByWeightDescending();
+
+        Assert.Equal("B", orderedItems[0].Value);
+        Assert.Equal("C", orderedItems[1].Value);
+        Assert.Equal("A", orderedItems[2].Value);
+    }
+
+    [Fact]
+    public void OrderByWeightAscending_ShouldReturnItemsInAscendingOrder()
+    {
+        var items = new List<WeightedItem<string>>
+        {
+            new WeightedItem<string>("A", 3),
+            new WeightedItem<string>("B", 1),
+            new WeightedItem<string>("C", 2)
+        };
+        var selector = new WeightedSelector<string>(items);
+
+        var orderedItems = selector.OrderByWeightAscending();
+
+        Assert.Equal("B", orderedItems[0].Value);
+        Assert.Equal("C", orderedItems[1].Value);
+        Assert.Equal("A", orderedItems[2].Value);
+    }
+
+    [Fact]
+    public void WeightedItem_ShouldReturnItemBasedOnWeight()
+    {
+        var items = new List<WeightedItem<string>>
+        {
+            new WeightedItem<string>("A", 1),
+            new WeightedItem<string>("B", 2),
+            new WeightedItem<string>("C", 3)
+        };
+
+        var selectedItem = items.WeightedItem();
+
+        Assert.Contains(selectedItem, items.Select(i => i.Value));
+    }
+
+    [Fact]
+    public void WeightedItems_ShouldReturnMultipleItemsBasedOnWeight()
+    {
+        var items = new List<WeightedItem<string>>
+        {
+            new WeightedItem<string>("A", 1),
+            new WeightedItem<string>("B", 2),
+            new WeightedItem<string>("C", 3)
+        };
+
+        var selectedItems = items.WeightedItems(2).ToList();
+
+        Assert.Equal(2, selectedItems.Count);
+        Assert.All(selectedItems, item => Assert.Contains(item, items.Select(i => i.Value)));
+    }
+
+    [Fact]
+    public void WeightedItems_WithKeySelector_ShouldReturnMultipleItemsBasedOnWeight()
+    {
+        var items = new List<string> { "A", "B", "C" };
+        Func<string, int> keySelector = s => s == "A" ? 1 : s == "B" ? 2 : 3;
+
+        var selectedItems = items.WeightedItems(2, keySelector).ToList();
+
+        Assert.Equal(2, selectedItems.Count);
+        Assert.All(selectedItems, item => Assert.Contains(item, items));
+    }
+
+    [Fact]
+    public void WeightedBy_WithKeySelector_ShouldReturnSingleItemBasedOnWeight()
+    {
+        var items = new List<string> { "A", "B", "C" };
+        Func<string, int> keySelector = s => s == "A" ? 1 : s == "B" ? 2 : 3;
+
+        var selectedItem = items.WeightedBy(keySelector);
+
+        Assert.Contains(selectedItem, items);
+    }
+}

+ 146 - 0
Test/Masuit.Tools.Abstractions.Test/Refection/ReflectionUtilTests.cs

@@ -0,0 +1,146 @@
+using System;
+using System.Reflection;
+using Masuit.Tools.Reflection;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Refection;
+
+public class ReflectionUtilTests
+{
+    private class TestClass
+    {
+        public string TestProperty { get; set; }
+        public string TestField;
+
+        public string TestMethod(string input) => $"Hello, {input}";
+    }
+
+    [Fact]
+    public void InvokeMethod_ShouldInvokeMethodAndReturnResult()
+    {
+        var obj = new TestClass();
+        var result = obj.InvokeMethod<string>("TestMethod", new object[] { "World" });
+        Assert.Equal("Hello, World", result);
+    }
+
+    [Fact]
+    public void InvokeMethod_ShouldInvokeMethodWithoutReturn()
+    {
+        var obj = new TestClass();
+        obj.InvokeMethod("TestMethod", new object[] { "World" });
+    }
+
+    [Fact]
+    public void SetField_ShouldSetFieldValue()
+    {
+        var obj = new TestClass();
+        obj.SetField("TestField", "New Value");
+        Assert.Equal("New Value", obj.TestField);
+    }
+
+    [Fact]
+    public void GetField_ShouldGetFieldValue()
+    {
+        var obj = new TestClass { TestField = "Field Value" };
+        var result = obj.GetField<string>("TestField");
+        Assert.Equal("Field Value", result);
+    }
+
+    [Fact]
+    public void GetFields_ShouldReturnAllFields()
+    {
+        var obj = new TestClass();
+        var fields = obj.GetFields();
+        Assert.Contains(fields, f => f.Name == "TestField");
+    }
+
+    [Fact]
+    public void SetProperty_ShouldSetPropertyValue()
+    {
+        var obj = new TestClass();
+        obj.SetProperty("TestProperty", "New Value");
+        Assert.Equal("New Value", obj.TestProperty);
+    }
+
+    [Fact]
+    public void GetProperty_ShouldGetPropertyValue()
+    {
+        var obj = new TestClass { TestProperty = "Property Value" };
+        var result = obj.GetProperty<string>("TestProperty");
+        Assert.Equal("Property Value", result);
+    }
+
+    [Fact]
+    public void GetProperties_ShouldReturnAllProperties()
+    {
+        var obj = new TestClass();
+        var properties = obj.GetProperties();
+        Assert.Contains(properties, p => p.Name == "TestProperty");
+    }
+
+    [Fact]
+    public void GetDescription_ShouldReturnDescription()
+    {
+        var member = typeof(TestClass).GetProperty("TestProperty");
+        var description = member.GetDescription();
+        Assert.Equal(string.Empty, description);
+    }
+
+    [Fact]
+    public void GetAttribute_ShouldReturnAttribute()
+    {
+        var member = typeof(TestClass).GetProperty("TestProperty");
+        var attribute = member.GetAttribute<ObsoleteAttribute>();
+        Assert.Null(attribute);
+    }
+
+    [Fact]
+    public void GetAttributes_ShouldReturnAttributes()
+    {
+        var member = typeof(TestClass).GetProperty("TestProperty");
+        var attributes = member.GetAttributes<ObsoleteAttribute>();
+        Assert.Empty(attributes);
+    }
+
+    [Fact]
+    public void GetImageResource_ShouldReturnStream()
+    {
+        var stream = Assembly.GetExecutingAssembly().GetImageResource("resourceName");
+        Assert.Null(stream);
+    }
+
+    [Fact]
+    public void GetManifestString_ShouldReturnString()
+    {
+        var result = typeof(TestClass).GetManifestString("UTF-8", "resName");
+        Assert.Equal(string.Empty, result);
+    }
+
+    [Fact]
+    public void IsImplementsOf_ShouldReturnTrueForImplementedInterface()
+    {
+        var result = typeof(TestClass).IsImplementsOf(typeof(IDisposable));
+        Assert.False(result);
+    }
+
+    [Fact]
+    public void GetInstance_ShouldReturnInstance()
+    {
+        var instance = typeof(TestClass).GetInstance();
+        Assert.NotNull(instance);
+    }
+
+    [Fact]
+    public void GetLoadableTypes_ShouldReturnTypes()
+    {
+        var types = Assembly.GetExecutingAssembly().GetLoadableTypes();
+        Assert.NotEmpty(types);
+    }
+
+    [Fact]
+    public void GetLoadableExportedTypes_ShouldReturnExportedTypes()
+    {
+        var types = Assembly.GetExecutingAssembly().GetLoadableExportedTypes();
+        Assert.NotEmpty(types);
+    }
+}

+ 89 - 0
Test/Masuit.Tools.Abstractions.Test/Security/Crc32Tests.cs

@@ -0,0 +1,89 @@
+using Masuit.Tools.Security;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Security;
+
+public class Crc32Tests
+{
+    [Fact]
+    public void Crc32_DefaultConstructor_ShouldInitializeWithDefaultValues()
+    {
+        // Arrange & Act
+        var crc32 = new Crc32();
+
+        // Assert
+        Assert.Equal(32, crc32.HashSize);
+    }
+
+    [Fact]
+    public void Crc32_ParameterizedConstructor_ShouldInitializeWithGivenValues()
+    {
+        // Arrange
+        uint polynomial = 0x04C11DB7;
+        uint seed = 0xFFFFFFFF;
+
+        // Act
+        var crc32 = new Crc32(polynomial, seed);
+
+        // Assert
+        Assert.Equal(32, crc32.HashSize);
+    }
+
+    [Fact]
+    public void HashFinal_ShouldReturnCorrectHash()
+    {
+        // Arrange
+        var crc32 = new Crc32();
+        var data = new byte[] { 1, 2, 3 };
+
+        // Act
+        crc32.ComputeHash(data);
+        var hash = crc32.Hash;
+
+        // Assert
+        Assert.NotNull(hash);
+        Assert.Equal(4, hash.Length);
+    }
+
+    [Fact]
+    public void Compute_WithBuffer_ShouldReturnCorrectHash()
+    {
+        // Arrange
+        var data = new byte[] { 1, 2, 3 };
+
+        // Act
+        var hash = Crc32.Compute(data);
+
+        // Assert
+        Assert.Equal((uint)1438416925, hash);
+    }
+
+    [Fact]
+    public void Compute_WithSeedAndBuffer_ShouldReturnCorrectHash()
+    {
+        // Arrange
+        var data = new byte[] { 1, 2, 3 };
+        uint seed = 0xFFFFFFFF;
+
+        // Act
+        var hash = Crc32.Compute(seed, data);
+
+        // Assert
+        Assert.Equal((uint)1438416925, hash);
+    }
+
+    [Fact]
+    public void Compute_WithPolynomialSeedAndBuffer_ShouldReturnCorrectHash()
+    {
+        // Arrange
+        var data = new byte[] { 1, 2, 3 };
+        uint polynomial = 0x04C11DB7;
+        uint seed = 0xFFFFFFFF;
+
+        // Act
+        var hash = Crc32.Compute(polynomial, seed, data);
+
+        // Assert
+        Assert.Equal((uint)4185564129, hash);
+    }
+}

+ 74 - 0
Test/Masuit.Tools.Abstractions.Test/Security/Crc64Tests.cs

@@ -0,0 +1,74 @@
+using System;
+using Masuit.Tools.Security;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Security
+{
+    public class Crc64Tests
+    {
+        [Fact]
+        public void Crc64_DefaultConstructor_ShouldInitializeCorrectly()
+        {
+            var crc64 = new Crc64();
+            Assert.Equal(64, crc64.HashSize);
+        }
+
+        [Fact]
+        public void Crc64_ConstructorWithPolynomial_ShouldInitializeCorrectly()
+        {
+            ulong polynomial = 0xD800000000000000;
+            var crc64 = new Crc64(polynomial);
+            Assert.Equal(64, crc64.HashSize);
+        }
+
+        [Fact]
+        public void Crc64_ConstructorWithPolynomialAndSeed_ShouldInitializeCorrectly()
+        {
+            ulong polynomial = 0xD800000000000000;
+            ulong seed = 0x0;
+            var crc64 = new Crc64(polynomial, seed);
+            Assert.Equal(64, crc64.HashSize);
+        }
+
+        [Fact]
+        public void Crc64_HashCore_ShouldComputeHashCorrectly()
+        {
+            var crc64 = new Crc64();
+            byte[] data = System.Text.Encoding.UTF8.GetBytes("test");
+            crc64.Initialize();
+            crc64.TransformBlock(data, 0, data.Length, data, 0);
+            crc64.TransformFinalBlock(Array.Empty<byte>(), 0, 0);
+            byte[] hash = crc64.Hash;
+            Assert.NotNull(hash);
+        }
+
+        [Fact]
+        public void Crc64_HashFinal_ShouldReturnCorrectHash()
+        {
+            var crc64 = new Crc64();
+            byte[] data = System.Text.Encoding.UTF8.GetBytes("test");
+            crc64.Initialize();
+            crc64.TransformBlock(data, 0, data.Length, data, 0);
+            crc64.TransformFinalBlock(Array.Empty<byte>(), 0, 0);
+            byte[] hash = crc64.Hash;
+            Assert.NotNull(hash);
+        }
+
+        [Fact]
+        public void Crc64_Compute_ShouldReturnCorrectHash()
+        {
+            byte[] data = System.Text.Encoding.UTF8.GetBytes("test");
+            ulong hash = Crc64.Compute(data);
+            Assert.Equal((ulong)5153117669225922560, hash);
+        }
+
+        [Fact]
+        public void Crc64_ComputeWithSeed_ShouldReturnCorrectHash()
+        {
+            byte[] data = System.Text.Encoding.UTF8.GetBytes("test");
+            ulong seed = 0x0;
+            ulong hash = Crc64.Compute(seed, data);
+            Assert.Equal((ulong)5153117669225922560, hash);
+        }
+    }
+}

+ 43 - 0
Test/Masuit.Tools.Abstractions.Test/Security/EncryptTests.cs

@@ -0,0 +1,43 @@
+using System.IO;
+using System.Text;
+using Masuit.Tools.Security;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Security
+{
+    public class EncryptTests
+    {
+        private const string TestString = "Hello, World!";
+        private const string DesKey = "12345678"; // 8位密钥
+        private static readonly byte[] DesKeyBytes = Encoding.ASCII.GetBytes(DesKey);
+        private static readonly byte[] DesIVBytes = Encoding.ASCII.GetBytes(DesKey);
+
+        [Fact]
+        public void DesEncrypt_StringKey_ShouldEncryptAndDecrypt()
+        {
+            // Arrange
+            string original = TestString;
+
+            // Act
+            string encrypted = original.DesEncrypt(DesKey);
+            string decrypted = encrypted.DesDecrypt(DesKey);
+
+            // Assert
+            Assert.Equal(original, decrypted);
+        }
+
+        [Fact]
+        public void DesEncrypt_ByteKey_ShouldEncryptAndDecrypt()
+        {
+            // Arrange
+            string original = TestString;
+
+            // Act
+            string encrypted = original.DesEncrypt(DesKeyBytes, DesIVBytes);
+            string decrypted = encrypted.DesDecrypt(DesKeyBytes, DesIVBytes);
+
+            // Assert
+            Assert.Equal(original, decrypted);
+        }
+    }
+}

+ 48 - 0
Test/Masuit.Tools.Abstractions.Test/Security/ZeroWidthCodecTests.cs

@@ -0,0 +1,48 @@
+using Masuit.Tools.Security;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Security
+{
+    public class ZeroWidthCodecTests
+    {
+        [Fact]
+        public void EncodeToZeroWidthText_ShouldEncodeString()
+        {
+            string input = "Test";
+            string encoded = input.EncodeToZeroWidthText();
+
+            Assert.False(string.IsNullOrEmpty(encoded));
+            Assert.NotEqual(input, encoded);
+        }
+
+        [Fact]
+        public void DecodeZeroWidthString_ShouldDecodeString()
+        {
+            string hiddenString = "HiddenMessage";
+            string publicString = "HelloWorld".InjectZeroWidthString(hiddenString);
+            string decoded = publicString.DecodeZeroWidthString();
+
+            Assert.Equal(hiddenString, decoded);
+        }
+
+        [Fact]
+        public void Encode_ShouldEncodeString()
+        {
+            string input = "Test";
+            string encoded = ZeroWidthCodec.Encode(input);
+
+            Assert.False(string.IsNullOrEmpty(encoded));
+            Assert.NotEqual(input, encoded);
+        }
+
+        [Fact]
+        public void Decrypt_ShouldDecodeString()
+        {
+            string hiddenString = "HiddenMessage";
+            string publicString = ZeroWidthCodec.Encrypt("HelloWorld", hiddenString);
+            string decoded = ZeroWidthCodec.Decrypt(publicString);
+
+            Assert.Equal(hiddenString, decoded);
+        }
+    }
+}

+ 95 - 0
Test/Masuit.Tools.Abstractions.Test/Strings/SimHashTests.cs

@@ -0,0 +1,95 @@
+using System.Numerics;
+using Masuit.Tools.Strings;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Strings;
+
+public class SimHashTests
+{
+    [Fact]
+    public void SimHash_Constructor_WithTokensAndHashBits_ShouldInitializeCorrectly()
+    {
+        // Arrange
+        string tokens = "测试字符串";
+        int hashBits = 64;
+
+        // Act
+        var simHash = new SimHash(tokens, hashBits);
+
+        // Assert
+        Assert.Equal(2374431774038, simHash.StrSimHash);
+        Assert.Equal(13, simHash.StrSimHash.ToString().Length);
+    }
+
+    [Fact]
+    public void SimHash_Constructor_WithTokens_ShouldInitializeCorrectly()
+    {
+        // Arrange
+        string tokens = "测试字符串";
+
+        // Act
+        var simHash = new SimHash(tokens);
+
+        // Assert
+        Assert.Equal(2374431774038, simHash.StrSimHash);
+    }
+
+    [Fact]
+    public void GetSimHash_ShouldReturnCorrectSimHash()
+    {
+        // Arrange
+        string tokens = "测试字符串";
+        var simHash = new SimHash(tokens);
+
+        // Act
+        var result = simHash.StrSimHash;
+
+        // Assert
+        Assert.NotEqual(BigInteger.Zero, result);
+    }
+
+    [Fact]
+    public void Hash_ShouldReturnCorrectHash()
+    {
+        // Arrange
+        string source = "测试";
+        var simHash = new SimHash(source);
+
+        // Act
+        var result = simHash.GetType().GetMethod("Hash", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Invoke(simHash, new object[] { source });
+
+        // Assert
+        Assert.NotEqual(BigInteger.Zero, result);
+    }
+
+    [Fact]
+    public void HammingDistance_ShouldReturnCorrectDistance()
+    {
+        // Arrange
+        string tokens1 = "测试字符串1";
+        string tokens2 = "测试字符串2";
+        var simHash1 = new SimHash(tokens1);
+        var simHash2 = new SimHash(tokens2);
+
+        // Act
+        var distance = simHash1.HammingDistance(simHash2);
+
+        // Assert
+        Assert.True(distance >= 0);
+    }
+
+    [Fact]
+    public void SimTokenizer_ShouldTokenizeCorrectly()
+    {
+        // Arrange
+        string source = "测试";
+        var tokenizer = new SimTokenizer(source);
+
+        // Act & Assert
+        Assert.True(tokenizer.HasMoreTokens());
+        Assert.Equal("测", tokenizer.NextToken());
+        Assert.True(tokenizer.HasMoreTokens());
+        Assert.Equal("试", tokenizer.NextToken());
+        Assert.False(tokenizer.HasMoreTokens());
+    }
+}

+ 79 - 0
Test/Masuit.Tools.Abstractions.Test/Strings/ValidateCodeTests.cs

@@ -0,0 +1,79 @@
+using System;
+using Masuit.Tools.Strings;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Strings;
+
+public class ValidateCodeTests
+{
+    [Fact]
+    public void CreateValidateCode_ShouldReturnStringOfSpecifiedLength()
+    {
+        // Arrange
+        int length = 6;
+
+        // Act
+        string result = ValidateCode.CreateValidateCode(length);
+
+        // Assert
+        Assert.NotNull(result);
+        Assert.Equal(length, result.Length);
+    }
+
+    [Fact]
+    public void CreateValidateGraphic_ShouldReturnPooledMemoryStream()
+    {
+        // Arrange
+        string validateCode = "ABC123";
+        int fontSize = 28;
+
+        // Act
+        using var result = validateCode.CreateValidateGraphic(fontSize);
+
+        // Assert
+        Assert.NotNull(result);
+        Assert.True(result.Length > 0);
+    }
+
+    [Fact]
+    public void StringWidth_ShouldReturnCorrectWidth()
+    {
+        // Arrange
+        string input = "Test";
+        int fontSize = 12;
+
+        // Act
+        float width = input.StringWidth(fontSize);
+
+        // Assert
+        Assert.True(width > 0);
+    }
+
+    [Fact]
+    public void StringWidth_WithFontName_ShouldReturnCorrectWidth()
+    {
+        // Arrange
+        string input = "Test";
+        string fontName = "Microsoft YaHei UI";
+        int fontSize = 12;
+
+        // Act
+        float width = input.StringWidth(fontName, fontSize);
+
+        // Assert
+        Assert.True(width > 0);
+    }
+
+    [Fact]
+    public void StringWidth_WithInvalidFontName_ShouldThrowArgumentException()
+    {
+        // Arrange
+        string input = "Test";
+        string fontName = "InvalidFont";
+        int fontSize = 12;
+
+        // Act & Assert
+        var exception = Assert.Throws<ArgumentException>(() => input.StringWidth(fontName, fontSize));
+        Assert.Equal($"字体 {fontName} 不存在,请尝试其它字体!", exception.Message);
+    }
+}

+ 198 - 0
Test/Masuit.Tools.Abstractions.Test/Systems/ConcurrentHashSetTests.cs

@@ -0,0 +1,198 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using Xunit;
+
+namespace Masuit.Tools.Systems.Tests
+{
+    public class ConcurrentHashSetTests
+    {
+        [Fact]
+        public void Add_ShouldAddItem()
+        {
+            var set = new ConcurrentHashSet<int>();
+            set.Add(1);
+            Assert.Contains(1, set);
+        }
+
+        [Fact]
+        public void TryAdd_ShouldReturnTrueForNewItem()
+        {
+            var set = new ConcurrentHashSet<int>();
+            var result = set.TryAdd(1);
+            Assert.True(result);
+            Assert.Contains(1, set);
+        }
+
+        [Fact]
+        public void TryAdd_ShouldReturnFalseForExistingItem()
+        {
+            var set = new ConcurrentHashSet<int>();
+            set.Add(1);
+            var result = set.TryAdd(1);
+            Assert.False(result);
+        }
+
+        [Fact]
+        public void UnionWith_ShouldUnionSets()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2 };
+            set.UnionWith(new[] { 2, 3 });
+            Assert.Contains(1, set);
+            Assert.Contains(2, set);
+            Assert.Contains(3, set);
+        }
+
+        [Fact]
+        public void IntersectWith_ShouldIntersectSets()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2 };
+            set.IntersectWith(new[] { 2, 3 });
+            Assert.DoesNotContain(1, set);
+            Assert.Contains(2, set);
+            Assert.DoesNotContain(3, set);
+        }
+
+        [Fact]
+        public void ExceptWith_ShouldExceptSets()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2, 3 };
+            set.ExceptWith(new[] { 2, 3 });
+            Assert.Contains(1, set);
+            Assert.DoesNotContain(2, set);
+            Assert.DoesNotContain(3, set);
+        }
+
+        [Fact]
+        public void SymmetricExceptWith_ShouldSymmetricExceptSets()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2, 3 };
+            set.SymmetricExceptWith(new[] { 2, 3, 4 });
+            Assert.Contains(1, set);
+            Assert.DoesNotContain(2, set);
+            Assert.DoesNotContain(3, set);
+            Assert.Contains(4, set);
+        }
+
+        [Fact]
+        public void IsSubsetOf_ShouldReturnTrueForSubset()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2 };
+            var result = set.IsSubsetOf(new[] { 1, 2, 3 });
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void IsSupersetOf_ShouldReturnTrueForSuperset()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2, 3 };
+            var result = set.IsSupersetOf(new[] { 1, 2 });
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void IsProperSupersetOf_ShouldReturnTrueForProperSuperset()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2, 3 };
+            var result = set.IsProperSupersetOf(new[] { 1, 2 });
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void IsProperSubsetOf_ShouldReturnTrueForProperSubset()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2 };
+            var result = set.IsProperSubsetOf(new[] { 1, 2, 3 });
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void Overlaps_ShouldReturnTrueForOverlappingSets()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2 };
+            var result = set.Overlaps(new[] { 2, 3 });
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void SetEquals_ShouldReturnTrueForEqualSets()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2, 3 };
+            var result = set.SetEquals(new[] { 1, 2, 3 });
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void Clear_ShouldClearSet()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2, 3 };
+            set.Clear();
+            Assert.Empty(set);
+        }
+
+        [Fact]
+        public void Contains_ShouldReturnTrueForContainedItem()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2, 3 };
+            var result = set.Contains(2);
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void CopyTo_ShouldCopyItemsToArray()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2, 3 };
+            var array = new int[3];
+            set.CopyTo(array, 0);
+            Assert.Contains(1, array);
+            Assert.Contains(2, array);
+            Assert.Contains(3, array);
+        }
+
+        [Fact]
+        public void Remove_ShouldRemoveItem()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2, 3 };
+            var result = set.Remove(2);
+            Assert.True(result);
+            Assert.DoesNotContain(2, set);
+        }
+
+        [Fact]
+        public void Count_ShouldReturnCorrectCount()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2, 3 };
+            Assert.Equal(3, set.Count);
+        }
+
+        [Fact]
+        public void IsReadOnly_ShouldReturnFalse()
+        {
+            var set = new ConcurrentHashSet<int>();
+            Assert.False(set.IsReadOnly);
+        }
+
+        [Fact]
+        public void GetEnumerator_ShouldEnumerateItems()
+        {
+            var set = new ConcurrentHashSet<int> { 1, 2, 3 };
+            var enumerator = set.GetEnumerator();
+            var items = new List<int>();
+            while (enumerator.MoveNext())
+            {
+                items.Add(enumerator.Current);
+            }
+            Assert.Contains(1, items);
+            Assert.Contains(2, items);
+            Assert.Contains(3, items);
+        }
+
+        [Fact]
+        public void Dispose_ShouldDisposeLock()
+        {
+            var set = new ConcurrentHashSet<int>();
+            set.Dispose();
+            Assert.Throws<ObjectDisposedException>(() => set.Add(1));
+        }
+    }
+}

+ 82 - 0
Test/Masuit.Tools.Abstractions.Test/Systems/ConcurrentLimitedQueueTests.cs

@@ -0,0 +1,82 @@
+using System.Collections.Generic;
+using System.Linq;
+using Masuit.Tools.Systems;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Systems;
+
+public class ConcurrentLimitedQueueTests
+{
+    [Fact]
+    public void Constructor_WithLimit_SetsLimit()
+    {
+        // Arrange
+        int limit = 5;
+
+        // Act
+        var queue = new ConcurrentLimitedQueue<int>(limit);
+
+        // Assert
+        Assert.Equal(limit, queue.Limit);
+    }
+
+    [Fact]
+    public void Constructor_WithList_SetsLimitAndInitialItems()
+    {
+        // Arrange
+        var list = new List<int> { 1, 2, 3 };
+
+        // Act
+        var queue = new ConcurrentLimitedQueue<int>(list);
+
+        // Assert
+        Assert.Equal(list.Count, queue.Limit);
+        Assert.Equal(list, queue.ToList());
+    }
+
+    [Fact]
+    public void ImplicitOperator_ConvertsListToQueue()
+    {
+        // Arrange
+        var list = new List<int> { 1, 2, 3 };
+
+        // Act
+        ConcurrentLimitedQueue<int> queue = list;
+
+        // Assert
+        Assert.Equal(list.Count, queue.Limit);
+        Assert.Equal(list, queue.ToList());
+    }
+
+    [Fact]
+    public void Enqueue_AddsItemToQueue()
+    {
+        // Arrange
+        var queue = new ConcurrentLimitedQueue<int>(3);
+
+        // Act
+        queue.Enqueue(1);
+
+        // Assert
+        Assert.Single(queue);
+        Assert.Equal(1, queue.ToList()[0]);
+    }
+
+    [Fact]
+    public void Enqueue_RemovesOldestItemWhenLimitExceeded()
+    {
+        // Arrange
+        var queue = new ConcurrentLimitedQueue<int>(3);
+        queue.Enqueue(1);
+        queue.Enqueue(2);
+        queue.Enqueue(3);
+
+        // Act
+        queue.Enqueue(4);
+
+        // Assert
+        Assert.Equal(3, queue.Count);
+        Assert.DoesNotContain(1, queue);
+        Assert.Equal(new List<int> { 2, 3, 4 }, queue.ToList());
+    }
+}

+ 78 - 0
Test/Masuit.Tools.Abstractions.Test/Systems/DisposableConcurrentDictionaryTests.cs

@@ -0,0 +1,78 @@
+using System.Collections.Generic;
+using Masuit.Tools.Systems;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Systems;
+
+public class DisposableConcurrentDictionaryTests
+{
+    [Fact]
+    public void Constructor_Default_ShouldInitialize()
+    {
+        var dictionary = new DisposableConcurrentDictionary<string, DisposableValue>();
+        Assert.NotNull(dictionary);
+    }
+
+    [Fact]
+    public void Constructor_WithFallbackValue_ShouldInitialize()
+    {
+        var fallbackValue = new DisposableValue();
+        var dictionary = new DisposableConcurrentDictionary<string, DisposableValue>(fallbackValue);
+        Assert.NotNull(dictionary);
+    }
+
+    [Fact]
+    public void Constructor_WithConcurrencyLevelAndCapacity_ShouldInitialize()
+    {
+        var dictionary = new DisposableConcurrentDictionary<string, DisposableValue>(4, 100);
+        Assert.NotNull(dictionary);
+    }
+
+    [Fact]
+    public void Constructor_WithComparer_ShouldInitialize()
+    {
+        var comparer = EqualityComparer<NullObject<string>>.Default;
+        var dictionary = new DisposableConcurrentDictionary<string, DisposableValue>(comparer);
+        Assert.NotNull(dictionary);
+    }
+
+    [Fact]
+    public void Dispose_ShouldDisposeAllValues()
+    {
+        var value1 = new DisposableValue();
+        var value2 = new DisposableValue();
+        var dictionary = new DisposableConcurrentDictionary<string, DisposableValue>
+        {
+            ["key1"] = value1,
+            ["key2"] = value2
+        };
+
+        dictionary.Dispose();
+
+        Assert.True(value1.IsDisposed);
+        Assert.True(value2.IsDisposed);
+    }
+
+    [Fact]
+    public void Dispose_MultipleTimes_ShouldNotThrow()
+    {
+        var dictionary = new DisposableConcurrentDictionary<string, DisposableValue>();
+        dictionary.Dispose();
+        var exception = Record.Exception(() => dictionary.Dispose());
+        Assert.Null(exception);
+    }
+}
+
+public class DisposableValue : Disposable
+{
+    public bool IsDisposed { get; private set; }
+
+    /// <summary>
+    /// 释放
+    /// </summary>
+    /// <param name="disposing"></param>
+    public override void Dispose(bool disposing)
+    {
+        IsDisposed = true;
+    }
+}

+ 112 - 0
Test/Masuit.Tools.Abstractions.Test/Systems/DisposableDictionaryTests.cs

@@ -0,0 +1,112 @@
+using System;
+using System.Collections.Generic;
+using Masuit.Tools.Systems;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Systems;
+
+public class DisposableDictionaryTests
+{
+    private class DisposableValue : IDisposable
+    {
+        public bool IsDisposed { get; private set; }
+
+        public void Dispose()
+        {
+            IsDisposed = true;
+        }
+    }
+
+    [Fact]
+    public void AddAndRetrieveValue()
+    {
+        var dictionary = new DisposableDictionary<string, DisposableValue>();
+        var value = new DisposableValue();
+        dictionary.Add("key", value);
+
+        Assert.True(dictionary.TryGetValue("key", out var retrievedValue));
+        Assert.Equal(value, retrievedValue);
+    }
+
+    [Fact]
+    public void Dispose_DisposesAllValues()
+    {
+        var dictionary = new DisposableDictionary<string, DisposableValue>
+        {
+            { "key1", new DisposableValue() },
+            { "key2", new DisposableValue() }
+        };
+
+        dictionary.Dispose();
+
+        foreach (var value in dictionary.Values)
+        {
+            Assert.True(value.IsDisposed);
+        }
+    }
+
+    [Fact]
+    public void Dispose_CanBeCalledMultipleTimes()
+    {
+        var dictionary = new DisposableDictionary<string, DisposableValue>
+        {
+            { "key1", new DisposableValue() },
+            { "key2", new DisposableValue() }
+        };
+
+        dictionary.Dispose();
+        dictionary.Dispose();
+
+        foreach (var value in dictionary.Values)
+        {
+            Assert.True(value.IsDisposed);
+        }
+    }
+
+    [Fact]
+    public void Finalizer_DisposesValues()
+    {
+        var dictionary = new DisposableDictionary<string, DisposableValue>
+        {
+            { "key1", new DisposableValue() },
+            { "key2", new DisposableValue() }
+        };
+
+        dictionary = null;
+        GC.Collect();
+        GC.WaitForPendingFinalizers();
+
+        // 无法直接测试终结器的效果,但可以通过其他测试确保 Dispose 方法的正确性
+    }
+
+    [Fact]
+    public void Constructor_WithFallbackValue()
+    {
+        var fallbackValue = new DisposableValue();
+        var dictionary = new DisposableDictionary<string, DisposableValue>(fallbackValue);
+
+        Assert.Equal(dictionary["aa"], fallbackValue);
+    }
+
+    [Fact]
+    public void Constructor_WithCapacity()
+    {
+        var dictionary = new DisposableDictionary<string, DisposableValue>(10);
+
+        Assert.Equal(0, dictionary.Count);
+    }
+
+    [Fact]
+    public void Constructor_WithDictionary()
+    {
+        var initialDictionary = new Dictionary<NullObject<string>, DisposableValue>
+        {
+            { new NullObject<string>("key1"), new DisposableValue() },
+            { new NullObject<string>("key2"), new DisposableValue() }
+        };
+
+        var dictionary = new DisposableDictionary<string, DisposableValue>(initialDictionary);
+
+        Assert.Equal(2, dictionary.Count);
+    }
+}

+ 92 - 0
Test/Masuit.Tools.Abstractions.Test/Systems/EnumExtTests.cs

@@ -0,0 +1,92 @@
+using System.ComponentModel;
+using System.Linq;
+using System.Reflection;
+using Masuit.Tools.Systems;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Systems;
+
+public class EnumExtTests
+{
+    [Fact]
+    public void GetDictionary_ShouldReturnCorrectDictionary()
+    {
+        var dict = typeof(TestEnum).GetDictionary();
+        Assert.Equal(3, dict.Count);
+        Assert.Equal("First", dict[1]);
+        Assert.Equal("Second", dict[2]);
+        Assert.Equal("Third", dict[3]);
+    }
+
+    [Fact]
+    public void GetEnumType_ShouldReturnCorrectType()
+    {
+        var assembly = Assembly.GetExecutingAssembly();
+        var type = assembly.GetEnumType("TestEnum");
+        Assert.Equal(typeof(TestEnum), type);
+    }
+
+    [Fact]
+    public void GetDisplay_ShouldReturnCorrectDisplay()
+    {
+        var display = TestEnum.First.GetDisplay();
+        Assert.Equal("First", display);
+    }
+
+    [Fact]
+    public void GetDescription_ShouldReturnCorrectDescription()
+    {
+        var description = TestEnum.First.GetDescription();
+        Assert.Equal("First Value", description);
+    }
+
+    [Fact]
+    public void GetAttributes_ShouldReturnCorrectAttributes()
+    {
+        var attributes = TestEnum.First.GetAttributes<DescriptionAttribute>().ToList();
+        Assert.Single(attributes);
+        Assert.Equal("First Value", attributes[0].Description);
+    }
+
+    [Fact]
+    public void Split_ShouldReturnCorrectValues()
+    {
+        var values = FlagsEnum.FlagA | FlagsEnum.FlagB;
+        var splitValues = values.Split().ToList();
+        Assert.Contains(FlagsEnum.FlagA, splitValues);
+        Assert.Contains(FlagsEnum.FlagB, splitValues);
+    }
+
+    [Fact]
+    public void GetEnumDescription_ShouldReturnCorrectDescription()
+    {
+        var description = TestEnum.First.GetEnumDescription();
+        Assert.NotNull(description);
+        Assert.Equal("First Value", description.Description);
+    }
+
+    [Fact]
+    public void GetTypedEnumDescriptions_ShouldReturnCorrectDescriptions()
+    {
+        var descriptions = TestEnum.First.GetTypedEnumDescriptions();
+        Assert.Single(descriptions);
+        Assert.Equal("First Value", descriptions["en"].Description);
+    }
+
+    [Fact]
+    public void ToEnumString_ShouldReturnCorrectString()
+    {
+        var enumString = 1.ToEnumString(typeof(TestEnum));
+        Assert.Equal("First", enumString);
+    }
+
+    [Fact]
+    public void GetDescriptionAndValue_ShouldReturnCorrectDictionary()
+    {
+        var dict = typeof(TestEnum).GetDescriptionAndValue();
+        Assert.Equal(3, dict.Count);
+        Assert.Equal(1, dict["First Value"]);
+        Assert.Equal(2, dict["Second Value"]);
+        Assert.Equal(3, dict["Third Value"]);
+    }
+}

+ 139 - 0
Test/Masuit.Tools.Abstractions.Test/Systems/LargeMemoryStreamTests.cs

@@ -0,0 +1,139 @@
+using System;
+using System.IO;
+using Masuit.Tools.Systems;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Systems;
+
+public class LargeMemoryStreamTests
+{
+    [Fact]
+    public void CanRead_ShouldReturnTrue()
+    {
+        var stream = new LargeMemoryStream();
+        Assert.True(stream.CanRead);
+    }
+
+    [Fact]
+    public void CanSeek_ShouldReturnTrue()
+    {
+        var stream = new LargeMemoryStream();
+        Assert.True(stream.CanSeek);
+    }
+
+    [Fact]
+    public void CanWrite_ShouldReturnTrue()
+    {
+        var stream = new LargeMemoryStream();
+        Assert.True(stream.CanWrite);
+    }
+
+    [Fact]
+    public void Length_ShouldReturnZeroInitially()
+    {
+        var stream = new LargeMemoryStream();
+        Assert.Equal(0, stream.Length);
+    }
+
+    [Fact]
+    public void Position_ShouldReturnZeroInitially()
+    {
+        var stream = new LargeMemoryStream();
+        Assert.Equal(0, stream.Position);
+    }
+
+    [Fact]
+    public void Position_SetValidValue_ShouldUpdatePosition()
+    {
+        var stream = new LargeMemoryStream();
+        stream.SetLength(100);
+        stream.Position = 50;
+        Assert.Equal(50, stream.Position);
+    }
+
+    [Fact]
+    public void Position_SetInvalidValue_ShouldThrowException()
+    {
+        var stream = new LargeMemoryStream();
+        Assert.Throws<InvalidOperationException>(() => stream.Position = -1);
+        Assert.Throws<InvalidOperationException>(() => stream.Position = 1);
+    }
+
+    [Fact]
+    public void Flush_ShouldNotThrowException()
+    {
+        var stream = new LargeMemoryStream();
+        stream.Flush();
+    }
+
+    [Fact]
+    public void Read_ShouldReturnCorrectData()
+    {
+        var stream = new LargeMemoryStream();
+        var buffer = new byte[100];
+        for (int i = 0; i < buffer.Length; i++)
+        {
+            buffer[i] = (byte)i;
+        }
+
+        stream.Write(buffer, 0, buffer.Length);
+        stream.Position = 0;
+
+        var readBuffer = new byte[100];
+        int bytesRead = stream.Read(readBuffer, 0, readBuffer.Length);
+
+        Assert.Equal(buffer.Length, bytesRead);
+        Assert.Equal(buffer, readBuffer);
+    }
+
+    [Fact]
+    public void Seek_ShouldUpdatePositionCorrectly()
+    {
+        var stream = new LargeMemoryStream();
+        stream.SetLength(100);
+        stream.Seek(50, SeekOrigin.Begin);
+        Assert.Equal(50, stream.Position);
+
+        stream.Seek(10, SeekOrigin.Current);
+        Assert.Equal(60, stream.Position);
+    }
+
+    [Fact]
+    public void SetLength_ShouldUpdateLengthCorrectly()
+    {
+        var stream = new LargeMemoryStream();
+        stream.SetLength(100);
+        Assert.Equal(100, stream.Length);
+
+        stream.SetLength(50);
+        Assert.Equal(50, stream.Length);
+    }
+
+    [Fact]
+    public void Write_ShouldWriteDataCorrectly()
+    {
+        var stream = new LargeMemoryStream();
+        var buffer = new byte[100];
+        for (int i = 0; i < buffer.Length; i++)
+        {
+            buffer[i] = (byte)i;
+        }
+
+        stream.Write(buffer, 0, buffer.Length);
+        stream.Position = 0;
+
+        var readBuffer = new byte[100];
+        int bytesRead = stream.Read(readBuffer, 0, readBuffer.Length);
+
+        Assert.Equal(buffer.Length, bytesRead);
+        Assert.Equal(buffer, readBuffer);
+    }
+
+    [Fact]
+    public void Dispose_ShouldDisposeCorrectly()
+    {
+        var stream = new LargeMemoryStream();
+        stream.Dispose();
+        Assert.Throws<ObjectDisposedException>(() => stream.Write(new byte[1], 0, 1));
+    }
+}

+ 112 - 0
Test/Masuit.Tools.Abstractions.Test/Systems/NullableConcurrentDictionaryTests.cs

@@ -0,0 +1,112 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using Masuit.Tools.Systems;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Systems;
+
+public class NullableConcurrentDictionaryTests
+{
+    [Fact]
+    public void Test_AddAndRetrieveValue()
+    {
+        var dictionary = new NullableConcurrentDictionary<string, int>();
+        dictionary["key1"] = 1;
+
+        Assert.True(dictionary.TryGetValue("key1", out var value));
+        Assert.Equal(1, value);
+    }
+
+    [Fact]
+    public void Test_FallbackValue()
+    {
+        var dictionary = new NullableConcurrentDictionary<string, int>(-1);
+
+        Assert.Equal(-1, dictionary["nonexistent"]);
+    }
+
+    [Fact]
+    public void Test_ContainsKey()
+    {
+        var dictionary = new NullableConcurrentDictionary<string, int>();
+        dictionary["key1"] = 1;
+
+        Assert.True(dictionary.ContainsKey("key1"));
+        Assert.False(dictionary.ContainsKey("key2"));
+    }
+
+    [Fact]
+    public void Test_TryAdd()
+    {
+        var dictionary = new NullableConcurrentDictionary<string, int>();
+        Assert.True(dictionary.TryAdd("key1", 1));
+        Assert.False(dictionary.TryAdd("key1", 2));
+    }
+
+    [Fact]
+    public void Test_TryRemove()
+    {
+        var dictionary = new NullableConcurrentDictionary<string, int>();
+        dictionary["key1"] = 1;
+
+        Assert.True(dictionary.TryRemove("key1", out var value));
+        Assert.Equal(1, value);
+        Assert.False(dictionary.TryRemove("key1", out _));
+    }
+
+    [Fact]
+    public void Test_TryUpdate()
+    {
+        var dictionary = new NullableConcurrentDictionary<string, int>();
+        dictionary["key1"] = 1;
+
+        Assert.True(dictionary.TryUpdate("key1", 2, 1));
+        Assert.Equal(2, dictionary["key1"]);
+        Assert.False(dictionary.TryUpdate("key1", 3, 1));
+    }
+
+    [Fact]
+    public void Test_ImplicitConversionFromDictionary()
+    {
+        var dict = new Dictionary<string, int> { { "key1", 1 } };
+        NullableConcurrentDictionary<string, int> nullableDict = dict;
+
+        Assert.Equal(1, nullableDict["key1"]);
+    }
+
+    [Fact]
+    public void Test_ImplicitConversionFromConcurrentDictionary()
+    {
+        var dict = new ConcurrentDictionary<string, int>();
+        dict["key1"] = 1;
+        NullableConcurrentDictionary<string, int> nullableDict = dict;
+
+        Assert.Equal(1, nullableDict["key1"]);
+    }
+
+    [Fact]
+    public void Test_ImplicitConversionToConcurrentDictionary()
+    {
+        var nullableDict = new NullableConcurrentDictionary<string, int>();
+        nullableDict["key1"] = 1;
+        ConcurrentDictionary<string, int> dict = nullableDict;
+
+        Assert.Equal(1, dict["key1"]);
+    }
+
+    [Fact]
+    public void Test_IndexerWithCondition()
+    {
+        var dictionary = new NullableConcurrentDictionary<string, int>(-1);
+        dictionary["key1"] = 1;
+        dictionary["key2"] = 2;
+
+        Assert.Equal(2, dictionary[new Func<string, bool>(key => key == "key2")]);
+        Assert.Equal(-1, dictionary[new Func<string, bool>(key => key == "key3")]);
+    }
+}
+
+public class NullableConcurrentDictionaryTestsImpl : NullableConcurrentDictionaryTests
+{
+}

+ 90 - 0
Test/Masuit.Tools.Abstractions.Test/Systems/NullableDictionaryTests.cs

@@ -0,0 +1,90 @@
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using Masuit.Tools.Systems;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Systems;
+
+public class NullableDictionaryTests
+{
+    [Fact]
+    public void Add_ShouldAddKeyValuePair()
+    {
+        var dictionary = new NullableDictionary<string, int>();
+        dictionary.Add("key1", 1);
+
+        Assert.True(dictionary.ContainsKey("key1"));
+        Assert.Equal(1, dictionary["key1"]);
+    }
+
+    [Fact]
+    public void Remove_ShouldRemoveKeyValuePair()
+    {
+        var dictionary = new NullableDictionary<string, int>();
+        dictionary.Add("key1", 1);
+
+        Assert.True(dictionary.Remove("key1"));
+        Assert.False(dictionary.ContainsKey("key1"));
+    }
+
+    [Fact]
+    public void TryGetValue_ShouldReturnTrueIfKeyExists()
+    {
+        var dictionary = new NullableDictionary<string, int>();
+        dictionary.Add("key1", 1);
+
+        var result = dictionary.TryGetValue("key1", out var value);
+        Assert.True(result);
+        Assert.Equal(1, value);
+    }
+
+    [Fact]
+    public void TryGetValue_ShouldReturnFalseIfKeyDoesNotExist()
+    {
+        var dictionary = new NullableDictionary<string, int>();
+
+        var result = dictionary.TryGetValue("key1", out var value);
+        Assert.False(result);
+    }
+
+    [Fact]
+    public void Indexer_ShouldSetAndGetKeyValuePair()
+    {
+        var dictionary = new NullableDictionary<string, int>();
+        dictionary["key1"] = 1;
+
+        Assert.Equal(1, dictionary["key1"]);
+    }
+
+    [Fact]
+    public void ImplicitConversion_FromDictionary_ShouldConvertCorrectly()
+    {
+        var dict = new Dictionary<string, int> { { "key1", 1 } };
+        NullableDictionary<string, int> nullableDict = dict;
+
+        Assert.True(nullableDict.ContainsKey("key1"));
+        Assert.Equal(1, nullableDict["key1"]);
+    }
+
+    [Fact]
+    public void ImplicitConversion_FromConcurrentDictionary_ShouldConvertCorrectly()
+    {
+        var dict = new ConcurrentDictionary<string, int>();
+        dict["key1"] = 1;
+        NullableDictionary<string, int> nullableDict = dict;
+
+        Assert.True(nullableDict.ContainsKey("key1"));
+        Assert.Equal(1, nullableDict["key1"]);
+    }
+
+    [Fact]
+    public void ImplicitConversion_ToDictionary_ShouldConvertCorrectly()
+    {
+        var nullableDict = new NullableDictionary<string, int>();
+        nullableDict["key1"] = 1;
+        Dictionary<string, int> dict = nullableDict;
+
+        Assert.True(dict.ContainsKey("key1"));
+        Assert.Equal(1, dict["key1"]);
+    }
+}

+ 102 - 0
Test/Masuit.Tools.Abstractions.Test/Systems/PooledMemoryStreamTests.cs

@@ -0,0 +1,102 @@
+using System;
+using System.Buffers;
+using System.IO;
+using Masuit.Tools.Systems;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Systems;
+
+public class PooledMemoryStreamTests
+{
+    [Fact]
+    public void Constructor_ShouldInitializeWithDefaultArrayPool()
+    {
+        using var stream = new PooledMemoryStream();
+        Assert.NotNull(stream);
+        Assert.Equal(0, stream.Length);
+        Assert.Equal(0, stream.Position);
+    }
+
+    [Fact]
+    public void Constructor_WithBuffer_ShouldInitializeWithBuffer()
+    {
+        byte[] buffer = { 1, 2, 3, 4, 5 };
+        using var stream = new PooledMemoryStream(buffer);
+        Assert.Equal(buffer.Length, stream.Length);
+        Assert.Equal(buffer, stream.GetBuffer());
+    }
+
+    [Fact]
+    public void Read_ShouldReadData()
+    {
+        byte[] buffer = { 1, 2, 3, 4, 5 };
+        using var stream = new PooledMemoryStream(buffer);
+        byte[] readBuffer = new byte[5];
+        int bytesRead = stream.Read(readBuffer, 0, 5);
+        Assert.Equal(5, bytesRead);
+        Assert.Equal(buffer, readBuffer);
+    }
+
+    [Fact]
+    public void Write_ShouldWriteData()
+    {
+        using var stream = new PooledMemoryStream();
+        byte[] buffer = { 1, 2, 3, 4, 5 };
+        stream.Write(buffer, 0, buffer.Length);
+        Assert.Equal(buffer.Length, stream.Length);
+        Assert.Equal(buffer, stream.GetBuffer());
+    }
+
+    [Fact]
+    public void Seek_ShouldChangePosition()
+    {
+        using var stream = new PooledMemoryStream();
+        stream.SetLength(10);
+        stream.Seek(5, SeekOrigin.Begin);
+        Assert.Equal(5, stream.Position);
+    }
+
+    [Fact]
+    public void SetLength_ShouldChangeLength()
+    {
+        using var stream = new PooledMemoryStream();
+        stream.SetLength(10);
+        Assert.Equal(10, stream.Length);
+    }
+
+    [Fact]
+    public void GetBuffer_ShouldReturnBuffer()
+    {
+        byte[] buffer = { 1, 2, 3, 4, 5 };
+        using var stream = new PooledMemoryStream(buffer);
+        byte[] result = stream.GetBuffer();
+        Assert.Equal(buffer, result);
+    }
+
+    [Fact]
+    public void ToArray_ShouldReturnArray()
+    {
+        byte[] buffer = { 1, 2, 3, 4, 5 };
+        using var stream = new PooledMemoryStream(buffer);
+        byte[] result = stream.ToArray();
+        Assert.Equal(buffer, result);
+    }
+
+    [Fact]
+    public void WriteTo_ShouldWriteToAnotherStream()
+    {
+        byte[] buffer = { 1, 2, 3, 4, 5 };
+        using var stream = new PooledMemoryStream(buffer);
+        using var memoryStream = new MemoryStream();
+        stream.WriteTo(memoryStream);
+        Assert.Equal(buffer, memoryStream.ToArray());
+    }
+
+    [Fact]
+    public void Dispose_ShouldDisposeStream()
+    {
+        var stream = new PooledMemoryStream();
+        stream.Dispose();
+        Assert.Throws<ObjectDisposedException>(() => stream.Write(new byte[1], 0, 1));
+    }
+}

+ 144 - 0
Test/Masuit.Tools.Abstractions.Test/Systems/SnowFlakeNewTests.cs

@@ -0,0 +1,144 @@
+using System;
+using Masuit.Tools.Strings;
+using Masuit.Tools.Systems;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Systems;
+
+public class SnowFlakeNewTests
+{
+    [Fact]
+    public void NewId_ShouldReturnUniqueStringId()
+    {
+        // Arrange
+        var snowFlake = SnowFlakeNew.GetInstance();
+
+        // Act
+        var id1 = SnowFlakeNew.NewId;
+        var id2 = SnowFlakeNew.NewId;
+
+        // Assert
+        Assert.NotNull(id1);
+        Assert.NotNull(id2);
+        Assert.NotEqual(id1, id2);
+    }
+
+    [Fact]
+    public void LongId_ShouldReturnUniqueLongId()
+    {
+        // Arrange
+        var snowFlake = SnowFlakeNew.GetInstance();
+
+        // Act
+        var id1 = SnowFlakeNew.LongId;
+        var id2 = SnowFlakeNew.LongId;
+
+        // Assert
+        Assert.NotEqual(id1, id2);
+    }
+
+    [Fact]
+    public void GetInstance_ShouldReturnSameInstance()
+    {
+        // Act
+        var instance1 = SnowFlakeNew.GetInstance();
+        var instance2 = SnowFlakeNew.GetInstance();
+
+        // Assert
+        Assert.Same(instance1, instance2);
+    }
+
+    [Fact]
+    public void SetMachienId_ShouldSetWorkerId()
+    {
+        // Arrange
+        var machineId = 100;
+
+        // Act
+        SnowFlakeNew.SetMachienId(machineId);
+
+        // Assert
+        // 使用反射来检查私有字段 _workerId
+        var field = typeof(SnowFlakeNew).GetField("_workerId", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
+        var workerId = (long)field.GetValue(null);
+        Assert.Equal(machineId, workerId);
+    }
+
+    [Fact]
+    public void SetInitialOffset_ShouldSetOffset()
+    {
+        // Arrange
+        var offset = 123456789L;
+
+        // Act
+        SnowFlakeNew.SetInitialOffset(offset);
+
+        // Assert
+        // 使用反射来检查私有字段 _offset
+        var field = typeof(SnowFlakeNew).GetField("_offset", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
+        var actualOffset = (long)field.GetValue(null);
+        Assert.Equal(offset, actualOffset);
+    }
+
+    [Fact]
+    public void SetNumberFormater_ShouldSetNumberFormater()
+    {
+        // Arrange
+        var numberFormater = new NumberFormater(36);
+
+        // Act
+        SnowFlakeNew.SetNumberFormater(numberFormater);
+
+        // Assert
+        // 使用反射来检查私有字段 _numberFormater
+        var field = typeof(SnowFlakeNew).GetField("_numberFormater", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
+        var actualNumberFormater = (NumberFormater)field.GetValue(null);
+        Assert.Same(numberFormater, actualNumberFormater);
+    }
+
+    [Fact]
+    public void GetUniqueId_ShouldReturnUniqueStringId()
+    {
+        // Arrange
+        var snowFlake = SnowFlakeNew.GetInstance();
+
+        // Act
+        var id1 = snowFlake.GetUniqueId();
+        var id2 = snowFlake.GetUniqueId();
+
+        // Assert
+        Assert.NotNull(id1);
+        Assert.NotNull(id2);
+        Assert.NotEqual(id1, id2);
+    }
+
+    [Fact]
+    public void GetUniqueShortId_ShouldReturnUniqueShortStringId()
+    {
+        // Arrange
+        var snowFlake = SnowFlakeNew.GetInstance();
+        var maxLength = 8;
+
+        // Act
+        var id1 = snowFlake.GetUniqueShortId(maxLength);
+        var id2 = snowFlake.GetUniqueShortId(maxLength);
+
+        // Assert
+        Assert.NotNull(id1);
+        Assert.NotNull(id2);
+        Assert.NotEqual(id1, id2);
+        Assert.True(id1.Length <= maxLength);
+        Assert.True(id2.Length <= maxLength);
+    }
+
+    [Fact]
+    public void GetUniqueShortId_ShouldThrowException_WhenMaxLengthIsLessThan6()
+    {
+        // Arrange
+        var snowFlake = SnowFlakeNew.GetInstance();
+        var maxLength = 5;
+
+        // Act & Assert
+        Assert.Throws<ArgumentException>(() => snowFlake.GetUniqueShortId(maxLength));
+    }
+}

+ 122 - 0
Test/Masuit.Tools.Abstractions.Test/Systems/SnowFlakeTests.cs

@@ -0,0 +1,122 @@
+using System;
+using Masuit.Tools.Strings;
+using Masuit.Tools.Systems;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Systems;
+
+public class SnowFlakeTests
+{
+    [Fact]
+    public void NewId_ShouldReturnUniqueStringId()
+    {
+        // Arrange
+        var snowFlake = SnowFlake.GetInstance();
+
+        // Act
+        var id1 = SnowFlake.NewId;
+        var id2 = SnowFlake.NewId;
+
+        // Assert
+        Assert.NotNull(id1);
+        Assert.NotNull(id2);
+        Assert.NotEqual(id1, id2);
+    }
+
+    [Fact]
+    public void LongId_ShouldReturnUniqueLongId()
+    {
+        // Arrange
+        var snowFlake = SnowFlake.GetInstance();
+
+        // Act
+        var id1 = SnowFlake.LongId;
+        var id2 = SnowFlake.LongId;
+
+        // Assert
+        Assert.NotEqual(id1, id2);
+    }
+
+    [Fact]
+    public void GetUniqueId_ShouldReturnUniqueStringId()
+    {
+        // Arrange
+        var snowFlake = new SnowFlake();
+
+        // Act
+        var id1 = snowFlake.GetUniqueId();
+        var id2 = snowFlake.GetUniqueId();
+
+        // Assert
+        Assert.NotNull(id1);
+        Assert.NotNull(id2);
+        Assert.NotEqual(id1, id2);
+    }
+
+    [Fact]
+    public void GetUniqueShortId_ShouldReturnUniqueShortStringId()
+    {
+        // Arrange
+        var snowFlake = new SnowFlake();
+
+        // Act
+        var id1 = snowFlake.GetUniqueShortId();
+        var id2 = snowFlake.GetUniqueShortId();
+
+        // Assert
+        Assert.NotNull(id1);
+        Assert.NotNull(id2);
+        Assert.NotEqual(id1, id2);
+        Assert.True(id1.Length <= 8);
+        Assert.True(id2.Length <= 8);
+    }
+
+    [Fact]
+    public void SetMachienId_ShouldSetMachineId()
+    {
+        // Arrange
+        var machineId = 512;
+
+        // Act
+        SnowFlake.SetMachienId(machineId);
+
+        // Assert
+        // No exception should be thrown
+    }
+
+    [Fact]
+    public void SetMachienId_ShouldThrowExceptionForInvalidMachineId()
+    {
+        // Arrange
+        var machineId = 2048;
+
+        // Act & Assert
+        Assert.Throws<Exception>(() => SnowFlake.SetMachienId(machineId));
+    }
+
+    [Fact]
+    public void SetNumberFormater_ShouldSetNumberFormater()
+    {
+        // Arrange
+        var numberFormater = new NumberFormater(36);
+
+        // Act
+        SnowFlake.SetNumberFormater(numberFormater);
+
+        // Assert
+        // No exception should be thrown
+    }
+
+    [Fact]
+    public void SetInitialOffset_ShouldSetOffset()
+    {
+        // Arrange
+        var offset = 1000L;
+
+        // Act
+        SnowFlake.SetInitialOffset(offset);
+
+        // Assert
+        // No exception should be thrown
+    }
+}

+ 30 - 0
Test/Masuit.Tools.Abstractions.Test/Systems/TestEnum.cs

@@ -0,0 +1,30 @@
+using System.ComponentModel;
+
+namespace Masuit.Tools.Abstractions.Test.Systems;
+
+public enum TestEnum
+{
+    [EnumDescription("First Value", Language = "en")]
+    First = 1,
+
+    [EnumDescription("Second Value")]
+    Second = 2,
+
+    [EnumDescription("Third Value")]
+    Third = 3
+}
+
+public enum FlagsEnum
+{
+    [Description("None")]
+    None = 0,
+
+    [Description("Flag A")]
+    FlagA = 1,
+
+    [Description("Flag B")]
+    FlagB = 2,
+
+    [Description("Flag C")]
+    FlagC = 4
+}

+ 39 - 0
Test/Masuit.Tools.Abstractions.Test/TextDiff/ExtensionsTests.cs

@@ -0,0 +1,39 @@
+using System.Collections.Generic;
+using System.Linq;
+using Masuit.Tools.TextDiff;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.TextDiff;
+
+public class ExtensionsTests
+{
+    [Fact]
+    public void HtmlDiff_ShouldReturnHtmlDiff()
+    {
+        // Arrange
+        var text1 = "<p>Hello</p>";
+        var text2 = "<p>Hi</p>";
+
+        // Act
+        var (html1, html2) = text1.HtmlDiff(text2);
+
+        // Assert
+        Assert.Contains("<del>ello</del>", html1);
+        Assert.Contains("<ins>i</ins>", html2);
+    }
+
+    [Fact]
+    public void HtmlDiffMerge_ShouldReturnMergedHtml()
+    {
+        // Arrange
+        var text1 = "<p>Hello</p>";
+        var text2 = "<p>Hi</p>";
+
+        // Act
+        var result = text1.HtmlDiffMerge(text2);
+
+        // Assert
+        Assert.Contains("<del>ello</del>", result);
+        Assert.Contains("<ins>i</ins>", result);
+    }
+}

+ 74 - 0
Test/Masuit.Tools.Abstractions.Test/TextDiff/TextDifferTests.cs

@@ -0,0 +1,74 @@
+using System;
+using Masuit.Tools.TextDiff;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.TextDiff;
+
+public class TextDifferTests
+{
+    [Fact]
+    public void Empty_ShouldReturnEmptyTextDiffer()
+    {
+        var empty = TextDiffer.Empty;
+        Assert.Equal(DiffOperation.Equal, empty.Operation);
+        Assert.Equal(string.Empty, empty.Text);
+    }
+
+    [Fact]
+    public void IsEmpty_ShouldReturnTrueForEmptyText()
+    {
+        var empty = TextDiffer.Empty;
+        Assert.True(empty.IsEmpty);
+    }
+
+    [Fact]
+    public void Equal_ShouldCreateEqualTextDiffer()
+    {
+        var text = "test";
+        var differ = TextDiffer.Equal(text.AsSpan());
+        Assert.Equal(DiffOperation.Equal, differ.Operation);
+        Assert.Equal(text, differ.Text);
+    }
+
+    [Fact]
+    public void Insert_ShouldCreateInsertTextDiffer()
+    {
+        var text = "test";
+        var differ = TextDiffer.Insert(text.AsSpan());
+        Assert.Equal(DiffOperation.Insert, differ.Operation);
+        Assert.Equal(text, differ.Text);
+    }
+
+    [Fact]
+    public void Delete_ShouldCreateDeleteTextDiffer()
+    {
+        var text = "test";
+        var differ = TextDiffer.Delete(text.AsSpan());
+        Assert.Equal(DiffOperation.Delete, differ.Operation);
+        Assert.Equal(text, differ.Text);
+    }
+
+    [Fact]
+    public void Compute_ShouldReturnDiffs()
+    {
+        var text1 = "test";
+        var text2 = "test123";
+        var diffs = TextDiffer.Compute(text1, text2);
+        Assert.NotEmpty(diffs);
+    }
+
+    [Fact]
+    public void IsLargeDelete_ShouldReturnTrueForLargeDelete()
+    {
+        var differ = TextDiffer.Delete("large delete".AsSpan());
+        Assert.True(differ.IsLargeDelete(5));
+    }
+
+    [Fact]
+    public void ToString_ShouldReturnFormattedString()
+    {
+        var differ = TextDiffer.Equal("test\n".AsSpan());
+        var result = differ.ToString();
+        Assert.Equal("Diff(Equal,\"test\u00b6\")", result);
+    }
+}

+ 10 - 1
Test/Masuit.Tools.Abstractions.Test/Tree/TreeTest.cs

@@ -197,6 +197,15 @@ internal class MyClass : ITree<MyClass>, ITreeEntity<MyClass, int>
     /// 父级id
     /// </summary>
     public int? ParentId { get; set; }
+
+    public override bool Equals(object obj)
+    {
+        if (obj is MyClass m)
+        {
+            return m.Id == Id;
+        }
+        return false;
+    }
 }
 
 internal class MyClass2 : ITree<MyClass2>, ITreeEntity<MyClass2>
@@ -227,7 +236,7 @@ internal class MyClass2 : ITree<MyClass2>, ITreeEntity<MyClass2>
     public string ParentId { get; set; }
 }
 
-internal class MyClass3
+internal record MyClass3
 {
     public long Id { get; set; }
 

+ 135 - 0
Test/Masuit.Tools.Abstractions.Test/Validator/ComplexPasswordAttributeTests.cs

@@ -0,0 +1,135 @@
+using Masuit.Tools.Core.Validator;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Validator;
+
+public class ComplexPasswordAttributeTests
+{
+    [Theory]
+    [InlineData("Password123!", true)]
+    [InlineData("Pass123!", true)]
+    [InlineData("123456", false)]
+    [InlineData("abcdef", false)]
+    [InlineData("ABCDEF", false)]
+    [InlineData("!@#$%^", false)]
+    [InlineData("Pass!1", true)]
+    [InlineData("P@ssw0rd", true)]
+    [InlineData("P@ss", false)]
+    public void IsValid_DefaultSettings(string password, bool expected)
+    {
+        // Arrange
+        var attribute = new ComplexPasswordAttribute();
+
+        // Act
+        var result = attribute.IsValid(password);
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData("Password123!", true)]
+    [InlineData("Pass123!", true)]
+    [InlineData("123456", false)]
+    [InlineData("abcdef", false)]
+    [InlineData("ABCDEF", false)]
+    [InlineData("!@#$%^", false)]
+    [InlineData("P@ssw0rd", true)]
+    [InlineData("P@ss", false)]
+    public void IsValid_CustomSettings(string password, bool expected)
+    {
+        // Arrange
+        var attribute = new ComplexPasswordAttribute(8, 20)
+        {
+            MustNumber = true,
+            MustLetter = true,
+            MustSymbol = true
+        };
+
+        // Act
+        var result = attribute.IsValid(password);
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData("Password123", true)]
+    [InlineData("Pass123", true)]
+    [InlineData("123456", false)]
+    [InlineData("abcdef", false)]
+    [InlineData("ABCDEF", false)]
+    [InlineData("!@#$%^", false)]
+    [InlineData("Pass1", false)]
+    [InlineData("P@ssw0rd", true)]
+    [InlineData("P@ss", false)]
+    public void IsValid_NoSymbolRequirement(string password, bool expected)
+    {
+        // Arrange
+        var attribute = new ComplexPasswordAttribute
+        {
+            MustNumber = true,
+            MustLetter = true,
+            MustSymbol = false
+        };
+
+        // Act
+        var result = attribute.IsValid(password);
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData("Password!", true)]
+    [InlineData("Pass!", false)]
+    [InlineData("123456", false)]
+    [InlineData("abcdef", false)]
+    [InlineData("ABCDEF", false)]
+    [InlineData("!@#$%^", false)]
+    [InlineData("P@ssw0rd", true)]
+    [InlineData("P@ss", false)]
+    public void IsValid_NoNumberRequirement(string password, bool expected)
+    {
+        // Arrange
+        var attribute = new ComplexPasswordAttribute
+        {
+            MustNumber = false,
+            MustLetter = true,
+            MustSymbol = true
+        };
+
+        // Act
+        var result = attribute.IsValid(password);
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData("Password123", false)]
+    [InlineData("Pass123", false)]
+    [InlineData("123456", false)]
+    [InlineData("abcdef", false)]
+    [InlineData("ABCDEF", false)]
+    [InlineData("!@#$%^", false)]
+    [InlineData("Pass1", false)]
+    [InlineData("P@ssw0rd", true)]
+    [InlineData("P@ss", false)]
+    public void IsValid_NoLetterRequirement(string password, bool expected)
+    {
+        // Arrange
+        var attribute = new ComplexPasswordAttribute
+        {
+            MustNumber = true,
+            MustLetter = false,
+            MustSymbol = true
+        };
+
+        // Act
+        var result = attribute.IsValid(password);
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+}

+ 58 - 0
Test/Masuit.Tools.Abstractions.Test/Validator/IsEmailAttributeTests.cs

@@ -0,0 +1,58 @@
+using Masuit.Tools.Core.Validator;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Validator;
+
+public class IsEmailAttributeTests
+{
+    [Theory]
+    [InlineData(null, true, true)]
+    [InlineData("", true, true)]
+    [InlineData("[email protected]", true, true)]
+    [InlineData("invalid-email", false, false)]
+    [InlineData("test@invalid-domain", false, false)]
+    public void IsValid_ShouldValidateEmail(string email, bool allowEmpty, bool expected)
+    {
+        // Arrange
+        var attribute = new IsEmailAttribute(validDns: false)
+        {
+            AllowEmpty = allowEmpty
+        };
+
+        // Act
+        var result = attribute.IsValid(email);
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnFalse_WhenEmailIsTooShort()
+    {
+        // Arrange
+        var attribute = new IsEmailAttribute();
+        var email = "[email protected]";
+
+        // Act
+        var result = attribute.IsValid(email);
+
+        // Assert
+        Assert.False(result);
+        Assert.Equal("您输入的邮箱格式不正确!", attribute.ErrorMessage);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnFalse_WhenEmailIsTooLong()
+    {
+        // Arrange
+        var attribute = new IsEmailAttribute();
+        var email = new string('a', 257) + "@example.com";
+
+        // Act
+        var result = attribute.IsValid(email);
+
+        // Assert
+        Assert.False(result);
+        Assert.Equal("您输入的邮箱无效,请使用真实有效的邮箱地址!", attribute.ErrorMessage);
+    }
+}

+ 28 - 0
Test/Masuit.Tools.Abstractions.Test/Validator/IsIPAddressAttributeTests.cs

@@ -0,0 +1,28 @@
+using Masuit.Tools.Core.Validator;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Validator;
+
+public class IsIPAddressAttributeTests
+{
+    [Theory]
+    [InlineData(null, false, "IP地址不能为空!")]
+    [InlineData("", false, "IP地址格式不正确,请输入有效的IPv4地址")]
+    [InlineData("192.168.1.1", true, null)]
+    [InlineData("255.255.255.255", true, null)]
+    [InlineData("0.0.0.0", true, null)]
+    [InlineData("256.256.256.256", false, "IP地址格式不正确,请输入有效的IPv4地址")]
+    [InlineData("invalid-ip", false, "IP地址格式不正确,请输入有效的IPv4地址")]
+    public void IsValid_ShouldValidateIPAddress(string ipAddress, bool expected, string expectedErrorMessage)
+    {
+        // Arrange
+        var attribute = new IsIPAddressAttribute();
+
+        // Act
+        var result = attribute.IsValid(ipAddress);
+
+        // Assert
+        Assert.Equal(expected, result);
+        Assert.Equal(expectedErrorMessage, attribute.ErrorMessage);
+    }
+}

+ 92 - 0
Test/Masuit.Tools.Abstractions.Test/Validator/IsPhoneAttributeTests.cs

@@ -0,0 +1,92 @@
+using Masuit.Tools.Core.Validator;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Validator;
+
+public class IsPhoneAttributeTests
+{
+    [Theory]
+    [InlineData(null, true, true, null)]
+    [InlineData("", true, true, null)]
+    [InlineData("13800138000", false, true, null)]
+    [InlineData("13800138000", true, true, null)]
+    [InlineData("invalid-phone", false, false, "手机号码格式不正确,请输入有效的大陆11位手机号码!")]
+    [InlineData(null, false, false, "手机号码不能为空")]
+    public void IsValid_ShouldValidatePhoneNumber(string phoneNumber, bool allowEmpty, bool expected, string expectedErrorMessage)
+    {
+        // Arrange
+        var attribute = new IsPhoneAttribute
+        {
+            AllowEmpty = allowEmpty
+        };
+
+        // Act
+        var result = attribute.IsValid(phoneNumber);
+
+        // Assert
+        Assert.Equal(expected, result);
+        Assert.Equal(expectedErrorMessage, attribute.ErrorMessage);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnFalse_WhenPhoneNumberIsInvalid()
+    {
+        // Arrange
+        var attribute = new IsPhoneAttribute();
+        var phoneNumber = "123456";
+
+        // Act
+        var result = attribute.IsValid(phoneNumber);
+
+        // Assert
+        Assert.False(result);
+        Assert.Equal("手机号码格式不正确,请输入有效的大陆11位手机号码!", attribute.ErrorMessage);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnTrue_WhenPhoneNumberIsValid()
+    {
+        // Arrange
+        var attribute = new IsPhoneAttribute();
+        var phoneNumber = "13800138000";
+
+        // Act
+        var result = attribute.IsValid(phoneNumber);
+
+        // Assert
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnTrue_WhenPhoneNumberIsNullAndAllowEmptyIsTrue()
+    {
+        // Arrange
+        var attribute = new IsPhoneAttribute
+        {
+            AllowEmpty = true
+        };
+
+        // Act
+        var result = attribute.IsValid(null);
+
+        // Assert
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnFalse_WhenPhoneNumberIsNullAndAllowEmptyIsFalse()
+    {
+        // Arrange
+        var attribute = new IsPhoneAttribute
+        {
+            AllowEmpty = false
+        };
+
+        // Act
+        var result = attribute.IsValid(null);
+
+        // Assert
+        Assert.False(result);
+        Assert.Equal("手机号码不能为空", attribute.ErrorMessage);
+    }
+}

+ 92 - 0
Test/Masuit.Tools.Abstractions.Test/Validator/LandlineAttributeTests.cs

@@ -0,0 +1,92 @@
+using Masuit.Tools.Core.Validator;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Validator;
+
+public class LandlineAttributeTests
+{
+    [Theory]
+    [InlineData(null, true, true, null)]
+    [InlineData("", true, true, null)]
+    [InlineData("010-12345678", false, true, null)]
+    [InlineData("010-12345678", true, true, null)]
+    [InlineData("invalid-landline", false, false, "固定电话格式不正确,请输入有效的大陆11/12位固定电话!")]
+    [InlineData(null, false, false, "固定电话不能为空")]
+    public void IsValid_ShouldValidateLandline(string landline, bool allowEmpty, bool expected, string expectedErrorMessage)
+    {
+        // Arrange
+        var attribute = new LandlineAttribute
+        {
+            AllowEmpty = allowEmpty
+        };
+
+        // Act
+        var result = attribute.IsValid(landline);
+
+        // Assert
+        Assert.Equal(expected, result);
+        Assert.Equal(expectedErrorMessage, attribute.ErrorMessage);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnFalse_WhenLandlineIsInvalid()
+    {
+        // Arrange
+        var attribute = new LandlineAttribute();
+        var landline = "123456";
+
+        // Act
+        var result = attribute.IsValid(landline);
+
+        // Assert
+        Assert.False(result);
+        Assert.Equal("固定电话格式不正确,请输入有效的大陆11/12位固定电话!", attribute.ErrorMessage);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnTrue_WhenLandlineIsValid()
+    {
+        // Arrange
+        var attribute = new LandlineAttribute();
+        var landline = "010-12345678";
+
+        // Act
+        var result = attribute.IsValid(landline);
+
+        // Assert
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnTrue_WhenLandlineIsNullAndAllowEmptyIsTrue()
+    {
+        // Arrange
+        var attribute = new LandlineAttribute
+        {
+            AllowEmpty = true
+        };
+
+        // Act
+        var result = attribute.IsValid(null);
+
+        // Assert
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnFalse_WhenLandlineIsNullAndAllowEmptyIsFalse()
+    {
+        // Arrange
+        var attribute = new LandlineAttribute
+        {
+            AllowEmpty = false
+        };
+
+        // Act
+        var result = attribute.IsValid(null);
+
+        // Assert
+        Assert.False(result);
+        Assert.Equal("固定电话不能为空", attribute.ErrorMessage);
+    }
+}

+ 64 - 0
Test/Masuit.Tools.Abstractions.Test/Validator/MaxValueAttributeTests.cs

@@ -0,0 +1,64 @@
+using Masuit.Tools.Core.Validator;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Validator;
+
+public class MaxValueAttributeTests
+{
+    [Theory]
+    [InlineData(null, true)]
+    [InlineData(5, true)]
+    [InlineData(10, true)]
+    [InlineData(15, false)]
+    [InlineData(10.5, false)]
+    public void IsValid_ShouldValidateMaxValue(object value, bool expected)
+    {
+        // Arrange
+        var attribute = new MaxValueAttribute(10);
+
+        // Act
+        var result = attribute.IsValid(value);
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnTrue_WhenValueIsNull()
+    {
+        // Arrange
+        var attribute = new MaxValueAttribute(10);
+
+        // Act
+        var result = attribute.IsValid(null);
+
+        // Assert
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnTrue_WhenValueIsLessThanOrEqualToMaxValue()
+    {
+        // Arrange
+        var attribute = new MaxValueAttribute(10);
+
+        // Act
+        var result = attribute.IsValid(10);
+
+        // Assert
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnFalse_WhenValueIsGreaterThanMaxValue()
+    {
+        // Arrange
+        var attribute = new MaxValueAttribute(10);
+
+        // Act
+        var result = attribute.IsValid(15);
+
+        // Assert
+        Assert.False(result);
+    }
+}

+ 103 - 0
Test/Masuit.Tools.Abstractions.Test/Validator/MinItemsCountAttributeTests.cs

@@ -0,0 +1,103 @@
+using System.Collections.Generic;
+using Masuit.Tools.Core.Validator;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Validator;
+
+public class MinItemsCountAttributeTests
+{
+    [Theory]
+    [InlineData(null, false)]
+    [InlineData(new int[] { }, false)]
+    [InlineData(new int[] { 1 }, true)]
+    [InlineData(new int[] { 1, 2 }, true)]
+    [InlineData(new int[] { 1, 2, 3 }, true)]
+    public void IsValid_ShouldValidateMinItemsCount_Array(IEnumerable<int> value, bool expected)
+    {
+        // Arrange
+        var attribute = new MinItemsCountAttribute(1);
+
+        // Act
+        var result = attribute.IsValid(value);
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnFalse_WhenValueIsNull()
+    {
+        // Arrange
+        var attribute = new MinItemsCountAttribute(1);
+
+        // Act
+        var result = attribute.IsValid(null);
+
+        // Assert
+        Assert.False(result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnFalse_WhenCollectionCountIsLessThanMinItems()
+    {
+        // Arrange
+        var attribute = new MinItemsCountAttribute(3);
+        var collection = new List<int> { 1, 2 };
+
+        // Act
+        var result = attribute.IsValid(collection);
+
+        // Assert
+        Assert.False(result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnTrue_WhenCollectionCountIsGreaterThanOrEqualToMinItems()
+    {
+        // Arrange
+        var attribute = new MinItemsCountAttribute(2);
+        var collection = new List<int> { 1, 2, 3 };
+
+        // Act
+        var result = attribute.IsValid(collection);
+
+        // Assert
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnTrue_WhenEnumerableCountIsGreaterThanOrEqualToMinItems()
+    {
+        // Arrange
+        var attribute = new MinItemsCountAttribute(2);
+        var enumerable = GetEnumerable(3);
+
+        // Act
+        var result = attribute.IsValid(enumerable);
+
+        // Assert
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnFalse_WhenEnumerableCountIsLessThanMinItems()
+    {
+        // Arrange
+        var attribute = new MinItemsCountAttribute(3);
+        var enumerable = GetEnumerable(2);
+
+        // Act
+        var result = attribute.IsValid(enumerable);
+
+        // Assert
+        Assert.False(result);
+    }
+
+    private IEnumerable<int> GetEnumerable(int count)
+    {
+        for (int i = 0; i < count; i++)
+        {
+            yield return i;
+        }
+    }
+}

+ 64 - 0
Test/Masuit.Tools.Abstractions.Test/Validator/MinValueAttributeTests.cs

@@ -0,0 +1,64 @@
+using Masuit.Tools.Core.Validator;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Validator;
+
+public class MinValueAttributeTests
+{
+    [Theory]
+    [InlineData(null, true)]
+    [InlineData(5, false)]
+    [InlineData(10, false)]
+    [InlineData(15, true)]
+    [InlineData(10.5, true)]
+    public void IsValid_ShouldValidateMinValue(object value, bool expected)
+    {
+        // Arrange
+        var attribute = new MinValueAttribute(10);
+
+        // Act
+        var result = attribute.IsValid(value);
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnTrue_WhenValueIsNull()
+    {
+        // Arrange
+        var attribute = new MinValueAttribute(10);
+
+        // Act
+        var result = attribute.IsValid(null);
+
+        // Assert
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnTrue_WhenValueIsGreaterThanMinValue()
+    {
+        // Arrange
+        var attribute = new MinValueAttribute(10);
+
+        // Act
+        var result = attribute.IsValid(15);
+
+        // Assert
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnFalse_WhenValueIsLessThanOrEqualToMinValue()
+    {
+        // Arrange
+        var attribute = new MinValueAttribute(10);
+
+        // Act
+        var result = attribute.IsValid(10);
+
+        // Assert
+        Assert.False(result);
+    }
+}

+ 47 - 0
Test/Masuit.Tools.Abstractions.Test/Validator/TestEnum.cs

@@ -0,0 +1,47 @@
+using Masuit.Tools.Core.Validator;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Validator;
+
+public enum TestEnum
+{
+    Value1,
+    Value2,
+    Value3
+}
+
+public class EnumOfAttributeTests
+{
+    [Theory]
+    [InlineData(TestEnum.Value1, true)]
+    [InlineData(TestEnum.Value2, true)]
+    [InlineData("InvalidValue", false)]
+    [InlineData(null, true)]
+    public void EnumOfAttribute_ShouldValidateEnumValues(object value, bool expected)
+    {
+        // Arrange
+        var attribute = new EnumOfAttribute(typeof(TestEnum));
+
+        // Act
+        var result = attribute.IsValid(value);
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+
+    [Theory]
+    [InlineData("NotEmpty", true)]
+    [InlineData("", false)]
+    [InlineData(null, false)]
+    public void NotNullOrEmptyAttribute_ShouldValidateNonEmptyValues(object value, bool expected)
+    {
+        // Arrange
+        var attribute = new NotNullOrEmptyAttribute();
+
+        // Act
+        var result = attribute.IsValid(value);
+
+        // Assert
+        Assert.Equal(expected, result);
+    }
+}

+ 92 - 0
Test/Masuit.Tools.Abstractions.Test/Validator/UnifiedSocialCreditCodeAttributeTests.cs

@@ -0,0 +1,92 @@
+using Masuit.Tools.Core.Validator;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Validator;
+
+public class UnifiedSocialCreditCodeAttributeTests
+{
+    [Theory]
+    [InlineData(null, true, true, null)]
+    [InlineData("", true, true, null)]
+    [InlineData("91350200752941808B", false, true, null)]
+    [InlineData("91350200752941808B", true, true, null)]
+    [InlineData("invalid-uscc", false, false, "企业统一社会信用代码格式不正确,请输入有效的企业统一社会信用代码!")]
+    [InlineData(null, false, false, "企业统一社会信用代码不能为空")]
+    public void IsValid_ShouldValidateUnifiedSocialCreditCode(string uscc, bool allowEmpty, bool expected, string expectedErrorMessage)
+    {
+        // Arrange
+        var attribute = new UnifiedSocialCreditCodeAttribute
+        {
+            AllowEmpty = allowEmpty
+        };
+
+        // Act
+        var result = attribute.IsValid(uscc);
+
+        // Assert
+        Assert.Equal(expected, result);
+        Assert.Equal(expectedErrorMessage, attribute.ErrorMessage);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnFalse_WhenUnifiedSocialCreditCodeIsInvalid()
+    {
+        // Arrange
+        var attribute = new UnifiedSocialCreditCodeAttribute();
+        var uscc = "123456";
+
+        // Act
+        var result = attribute.IsValid(uscc);
+
+        // Assert
+        Assert.False(result);
+        Assert.Equal("企业统一社会信用代码格式不正确,请输入有效的企业统一社会信用代码!", attribute.ErrorMessage);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnTrue_WhenUnifiedSocialCreditCodeIsValid()
+    {
+        // Arrange
+        var attribute = new UnifiedSocialCreditCodeAttribute();
+        var uscc = "91350200752941808B";
+
+        // Act
+        var result = attribute.IsValid(uscc);
+
+        // Assert
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnTrue_WhenUnifiedSocialCreditCodeIsNullAndAllowEmptyIsTrue()
+    {
+        // Arrange
+        var attribute = new UnifiedSocialCreditCodeAttribute
+        {
+            AllowEmpty = true
+        };
+
+        // Act
+        var result = attribute.IsValid(null);
+
+        // Assert
+        Assert.True(result);
+    }
+
+    [Fact]
+    public void IsValid_ShouldReturnFalse_WhenUnifiedSocialCreditCodeIsNullAndAllowEmptyIsFalse()
+    {
+        // Arrange
+        var attribute = new UnifiedSocialCreditCodeAttribute
+        {
+            AllowEmpty = false
+        };
+
+        // Act
+        var result = attribute.IsValid(null);
+
+        // Assert
+        Assert.False(result);
+        Assert.Equal("企业统一社会信用代码不能为空", attribute.ErrorMessage);
+    }
+}

+ 1 - 1
Test/Masuit.Tools.AspNetCore.ResumeFileResults.WebTest/Startup.cs

@@ -42,7 +42,7 @@ namespace Masuit.Tools.AspNetCore.ResumeFileResults.WebTest
             {
                 options.TextEncoderSettings = new TextEncoderSettings(UnicodeRanges.All);
             }); //解决razor视图中中文被编码的问题
-            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Latest).AddControllersAsServices();
+            services.AddMvc().AddControllersAsServices();
         }
 
         public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

+ 293 - 0
Test/Masuit.Tools.Core.Test/AspNetCore/DbContextExtTest.cs

@@ -0,0 +1,293 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Xunit;
+using Masuit.Tools.Core;
+
+public class TestEntity
+{
+    public int Id { get; set; }
+    public string Name { get; set; }
+}
+
+public class TestDbContext : DbContext
+{
+    public DbSet<TestEntity> TestEntities { get; set; }
+
+    public TestDbContext(DbContextOptions<TestDbContext> options) : base(options)
+    {
+    }
+}
+
+public class DbContextExtTests
+{
+    private DbContextOptions<TestDbContext> CreateNewContextOptions()
+    {
+        var builder = new DbContextOptionsBuilder<TestDbContext>();
+        builder.UseInMemoryDatabase(Guid.NewGuid().ToString());
+        return builder.Options;
+    }
+
+    [Fact]
+    public void GetAllChanges_ShouldReturnAllChanges()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        var entity1 = new TestEntity { Name = "Original" };
+        var entity2 = new TestEntity { Name = "New" };
+        context.TestEntities.Add(entity1);
+        context.TestEntities.Add(entity2);
+        context.SaveChanges();
+
+        entity1.Name = "Modified";
+        context.TestEntities.Update(entity1);
+        context.TestEntities.Remove(entity2);
+
+        // Act
+        var allChanges = context.GetAllChanges<TestEntity>().ToList();
+
+        // Assert
+        Assert.Equal(2, allChanges.Count);
+        Assert.Contains(allChanges, e => e.EntityState == EntityState.Modified);
+        Assert.Contains(allChanges, e => e.EntityState == EntityState.Deleted);
+    }
+
+    [Fact]
+    public async Task ToListWithNoLockAsync_ShouldReturnEntities()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        context.TestEntities.Add(new TestEntity { Name = "Entity1" });
+        context.TestEntities.Add(new TestEntity { Name = "Entity2" });
+        await context.SaveChangesAsync();
+
+        // Act
+        var entities = await context.TestEntities.ToListWithNoLockAsync();
+
+        // Assert
+        Assert.Equal(2, entities.Count);
+    }
+
+    [Fact]
+    public void ToListWithNoLock_ShouldReturnEntities()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        context.TestEntities.Add(new TestEntity { Name = "Entity1" });
+        context.TestEntities.Add(new TestEntity { Name = "Entity2" });
+        context.SaveChanges();
+
+        // Act
+        var entities = context.TestEntities.ToListWithNoLock();
+
+        // Assert
+        Assert.Equal(2, entities.Count);
+    }
+
+    [Fact]
+    public async Task CountWithNoLockAsync_ShouldReturnCount()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        context.TestEntities.Add(new TestEntity { Name = "Entity1" });
+        context.TestEntities.Add(new TestEntity { Name = "Entity2" });
+        await context.SaveChangesAsync();
+
+        // Act
+        var count = await context.TestEntities.CountWithNoLockAsync();
+
+        // Assert
+        Assert.Equal(2, count);
+    }
+
+    [Fact]
+    public void CountWithNoLock_ShouldReturnCount()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        context.TestEntities.Add(new TestEntity { Name = "Entity1" });
+        context.TestEntities.Add(new TestEntity { Name = "Entity2" });
+        context.SaveChanges();
+
+        // Act
+        var count = context.TestEntities.CountWithNoLock();
+
+        // Assert
+        Assert.Equal(2, count);
+    }
+
+    [Fact]
+    public async Task AnyWithNoLockAsync_ShouldReturnTrueIfAny()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        context.TestEntities.Add(new TestEntity { Name = "Entity1" });
+        await context.SaveChangesAsync();
+
+        // Act
+        var any = await context.TestEntities.AnyWithNoLockAsync();
+
+        // Assert
+        Assert.True(any);
+    }
+
+    [Fact]
+    public void AnyWithNoLock_ShouldReturnTrueIfAny()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        context.TestEntities.Add(new TestEntity { Name = "Entity1" });
+        context.SaveChanges();
+
+        // Act
+        var any = context.TestEntities.AnyWithNoLock();
+
+        // Assert
+        Assert.True(any);
+    }
+
+    [Fact]
+    public async Task FirstOrDefaultWithNoLockAsync_ShouldReturnFirstEntity()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        context.TestEntities.Add(new TestEntity { Name = "Entity1" });
+        context.TestEntities.Add(new TestEntity { Name = "Entity2" });
+        await context.SaveChangesAsync();
+
+        // Act
+        var entity = await context.TestEntities.FirstOrDefaultWithNoLockAsync(e => e.Name == "Entity1");
+
+        // Assert
+        Assert.NotNull(entity);
+        Assert.Equal("Entity1", entity.Name);
+    }
+
+    [Fact]
+    public void FirstOrDefaultWithNoLock_ShouldReturnFirstEntity()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        context.TestEntities.Add(new TestEntity { Name = "Entity1" });
+        context.TestEntities.Add(new TestEntity { Name = "Entity2" });
+        context.SaveChanges();
+
+        // Act
+        var entity = context.TestEntities.FirstOrDefaultWithNoLock(e => e.Name == "Entity1");
+
+        // Assert
+        Assert.NotNull(entity);
+        Assert.Equal("Entity1", entity.Name);
+    }
+
+    [Fact]
+    public async Task SingleOrDefaultWithNoLockAsync_ShouldReturnSingleEntity()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        context.TestEntities.Add(new TestEntity { Name = "Entity1" });
+        await context.SaveChangesAsync();
+
+        // Act
+        var entity = await context.TestEntities.SingleOrDefaultWithNoLockAsync(e => e.Name == "Entity1");
+
+        // Assert
+        Assert.NotNull(entity);
+        Assert.Equal("Entity1", entity.Name);
+    }
+
+    [Fact]
+    public void SingleOrDefaultWithNoLock_ShouldReturnSingleEntity()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        context.TestEntities.Add(new TestEntity { Name = "Entity1" });
+        context.SaveChanges();
+
+        // Act
+        var entity = context.TestEntities.SingleOrDefaultWithNoLock(e => e.Name == "Entity1");
+
+        // Assert
+        Assert.NotNull(entity);
+        Assert.Equal("Entity1", entity.Name);
+    }
+
+    [Fact]
+    public async Task AllWithNoLockAsync_ShouldReturnTrueIfAllMatch()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        context.TestEntities.Add(new TestEntity { Name = "Entity1" });
+        context.TestEntities.Add(new TestEntity { Name = "Entity2" });
+        await context.SaveChangesAsync();
+
+        // Act
+        var all = await context.TestEntities.AllWithNoLockAsync(e => e.Name.StartsWith("Entity"));
+
+        // Assert
+        Assert.True(all);
+    }
+
+    [Fact]
+    public void AllWithNoLock_ShouldReturnTrueIfAllMatch()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        context.TestEntities.Add(new TestEntity { Name = "Entity1" });
+        context.TestEntities.Add(new TestEntity { Name = "Entity2" });
+        context.SaveChanges();
+
+        // Act
+        var all = context.TestEntities.AllWithNoLock(e => e.Name.StartsWith("Entity"));
+
+        // Assert
+        Assert.True(all);
+    }
+
+    [Fact]
+    public void NoLock_ShouldExecuteWithNoLock()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        context.TestEntities.Add(new TestEntity { Name = "Entity1" });
+        context.SaveChanges();
+
+        // Act
+        var result = context.NoLock(ctx => ctx.TestEntities.Count());
+
+        // Assert
+        Assert.Equal(1, result);
+    }
+
+    [Fact]
+    public async Task NoLock_ShouldExecuteWithNoLockAsync()
+    {
+        // Arrange
+        var options = CreateNewContextOptions();
+        using var context = new TestDbContext(options);
+        context.TestEntities.Add(new TestEntity { Name = "Entity1" });
+        await context.SaveChangesAsync();
+
+        // Act
+        var result = await context.NoLock(async ctx => await ctx.TestEntities.CountAsync());
+
+        // Assert
+        Assert.Equal(1, result);
+    }
+}

+ 128 - 0
Test/Masuit.Tools.Core.Test/AspNetCore/IQueryableExtTest.cs

@@ -0,0 +1,128 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Masuit.Tools.Models;
+using Microsoft.EntityFrameworkCore;
+using Xunit;
+
+namespace Masuit.Tools.Core.Test.AspNetCore
+{
+    public class IQueryableExtTests
+    {
+        private DbContextOptions<TestDbContext> CreateNewContextOptions()
+        {
+            var builder = new DbContextOptionsBuilder<TestDbContext>();
+            builder.UseInMemoryDatabase(Guid.NewGuid().ToString());
+            return builder.Options;
+        }
+
+        [Fact]
+        public async Task ToPagedListAsync_ShouldReturnPagedList()
+        {
+            // Arrange
+            var options = CreateNewContextOptions();
+            using var context = new TestDbContext(options);
+            for (int i = 1; i <= 20; i++)
+            {
+                context.TestEntities.Add(new TestEntity { Name = $"Entity{i}" });
+            }
+            await context.SaveChangesAsync();
+
+            // Act
+            var pagedList = await context.TestEntities.AsQueryable().ToPagedListAsync(2, 5);
+
+            // Assert
+            Assert.Equal(2, pagedList.CurrentPage);
+            Assert.Equal(5, pagedList.PageSize);
+            Assert.Equal(20, pagedList.TotalCount);
+            Assert.Equal(4, pagedList.TotalPages);
+            Assert.Equal(5, pagedList.Data.Count);
+        }
+
+        [Fact]
+        public async Task ToPagedListNoLockAsync_ShouldReturnPagedList()
+        {
+            // Arrange
+            var options = CreateNewContextOptions();
+            using var context = new TestDbContext(options);
+            for (int i = 1; i <= 20; i++)
+            {
+                context.TestEntities.Add(new TestEntity { Name = $"Entity{i}" });
+            }
+            await context.SaveChangesAsync();
+
+            // Act
+            var pagedList = await context.TestEntities.AsQueryable().ToPagedListNoLockAsync(2, 5);
+
+            // Assert
+            Assert.Equal(2, pagedList.CurrentPage);
+            Assert.Equal(5, pagedList.PageSize);
+            Assert.Equal(20, pagedList.TotalCount);
+            Assert.Equal(4, pagedList.TotalPages);
+            Assert.Equal(5, pagedList.Data.Count);
+        }
+
+        [Fact]
+        public void ToPagedList_ShouldReturnPagedList()
+        {
+            // Arrange
+            var options = CreateNewContextOptions();
+            using var context = new TestDbContext(options);
+            for (int i = 1; i <= 20; i++)
+            {
+                context.TestEntities.Add(new TestEntity { Name = $"Entity{i}" });
+            }
+            context.SaveChanges();
+
+            // Act
+            var pagedList = context.TestEntities.AsQueryable().ToPagedList(2, 5);
+
+            // Assert
+            Assert.Equal(2, pagedList.CurrentPage);
+            Assert.Equal(5, pagedList.PageSize);
+            Assert.Equal(20, pagedList.TotalCount);
+            Assert.Equal(4, pagedList.TotalPages);
+            Assert.Equal(5, pagedList.Data.Count);
+        }
+
+        [Fact]
+        public void ToPagedListNoLock_ShouldReturnPagedList()
+        {
+            // Arrange
+            var options = CreateNewContextOptions();
+            using var context = new TestDbContext(options);
+            for (int i = 1; i <= 20; i++)
+            {
+                context.TestEntities.Add(new TestEntity { Name = $"Entity{i}" });
+            }
+            context.SaveChanges();
+
+            // Act
+            var pagedList = context.TestEntities.AsQueryable().ToPagedListNoLock(2, 5);
+
+            // Assert
+            Assert.Equal(2, pagedList.CurrentPage);
+            Assert.Equal(5, pagedList.PageSize);
+            Assert.Equal(20, pagedList.TotalCount);
+            Assert.Equal(4, pagedList.TotalPages);
+            Assert.Equal(5, pagedList.Data.Count);
+        }
+
+        [Fact]
+        public void ToPagedList_FromEnumerable_ShouldReturnPagedList()
+        {
+            // Arrange
+            var list = Enumerable.Range(1, 20).Select(i => new TestEntity { Id = i, Name = $"Entity{i}" });
+
+            // Act
+            var pagedList = list.ToPagedList(2, 5);
+
+            // Assert
+            Assert.Equal(2, pagedList.CurrentPage);
+            Assert.Equal(5, pagedList.PageSize);
+            Assert.Equal(20, pagedList.TotalCount);
+            Assert.Equal(4, pagedList.TotalPages);
+            Assert.Equal(5, pagedList.Data.Count);
+        }
+    }
+}

+ 3 - 1
Test/Masuit.Tools.Core.Test/AspNetCore/Startup.cs

@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Builder;
+using Masuit.Tools.Core.AspNetCore;
+using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
@@ -18,6 +19,7 @@ namespace Masuit.Tools.Core.Test.AspNetCore
         // This method gets called by the runtime. Use this method to add services to the container.
         public void ConfigureServices(IServiceCollection services)
         {
+            services.AddResumeFileResult();
             services.AddMvc();
         }
 

+ 0 - 5
Test/Masuit.Tools.Core.Test/AspNetCore/TestBase.cs

@@ -15,11 +15,6 @@ namespace Masuit.Tools.Core.Test.AspNetCore
             string path = Path.GetDirectoryName(typeof(Startup).GetTypeInfo().Assembly.Location);
             DirectoryInfo di = new DirectoryInfo(path).Parent.Parent.Parent;
 
-            // Arrange
-            //var hostBuilder = Host.CreateDefaultBuilder().ConfigureWebHostDefaults(configurationBuilder => configurationBuilder.UseStartup<Startup>().UseContentRoot(di.FullName));
-            //var host = hostBuilder.Build();
-            //host.Run();
-            //todo:.NET Core3.0创建测试服务器
             this.Server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
             this.Client = this.Server.CreateClient();
         }

+ 109 - 0
Test/Masuit.Tools.Core.Test/Controllers/TestController.cs

@@ -0,0 +1,109 @@
+using System;
+using System.IO;
+using Masuit.Tools.AspNetCore.ResumeFileResults.Extensions;
+using Masuit.Tools.AspNetCore.ResumeFileResults.ResumeFileResult;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Masuit.Tools.Core.Test.Controllers
+{
+    /// <summary>
+    /// 测试控制器
+    /// </summary>
+    [Route("file")]
+    public class TestController : Controller
+    {
+        private const string EntityTag = "\"TestFile\"";
+
+        private readonly DateTimeOffset _lastModified = new DateTimeOffset(2016, 1, 1, 0, 0, 0, TimeSpan.Zero);
+
+        [HttpGet("content/{fileName}/{etag}")]
+        public IActionResult FileContent(bool fileName, bool etag)
+        {
+            var result = this.ResumeFile(System.IO.File.ReadAllBytes(Path.Combine(AppContext.BaseDirectory + "wwwroot", "TestFile.txt")), "text/plain", fileName ? "TestFile.txt" : null, etag ? EntityTag : null);
+            result.LastModified = _lastModified;
+            return result;
+        }
+
+        [HttpGet("content/{fileName}")]
+        public IActionResult FileContent(bool fileName)
+        {
+            return new ResumeFileContentResult(System.IO.File.ReadAllBytes(Path.Combine(AppContext.BaseDirectory + "wwwroot", "TestFile.txt")), "text/plain")
+            {
+                FileInlineName = "TestFile.txt",
+                LastModified = _lastModified
+            };
+        }
+
+        [HttpHead("file")]
+        public IActionResult FileHead()
+        {
+            var result = this.ResumeFile("TestFile.txt", "text/plain", "TestFile.txt", EntityTag);
+            result.LastModified = _lastModified;
+            return result;
+        }
+
+        [HttpPut("file")]
+        public IActionResult FilePut()
+        {
+            var result = this.ResumeFile("TestFile.txt", "text/plain", "TestFile.txt", EntityTag);
+            result.LastModified = _lastModified;
+            return result;
+        }
+
+        [HttpGet("stream/{fileName}/{etag}")]
+        public IActionResult FileStream(bool fileName, bool etag)
+        {
+            var stream = System.IO.File.OpenRead(Path.Combine(AppContext.BaseDirectory + "wwwroot", "TestFile.txt"));
+            var result = this.ResumeFile(stream, "text/plain", fileName ? "TestFile.txt" : null, etag ? EntityTag : null);
+            result.LastModified = _lastModified;
+            return result;
+        }
+
+        [HttpGet("stream/{fileName}")]
+        public IActionResult FileStream(bool fileName)
+        {
+            var stream = System.IO.File.OpenRead(Path.Combine(AppContext.BaseDirectory + "wwwroot", "TestFile.txt"));
+            return new ResumeFileStreamResult(stream, "text/plain")
+            {
+                FileInlineName = "TestFile.txt",
+                LastModified = _lastModified
+            };
+        }
+
+        [HttpGet("physical/{fileName}/{etag}")]
+        public IActionResult PhysicalFile(bool fileName, bool etag)
+        {
+            var result = this.ResumePhysicalFile(Path.Combine(AppContext.BaseDirectory + "wwwroot", "TestFile.txt"), "text/plain", fileName ? "TestFile.txt" : null, etag ? EntityTag : null);
+            result.LastModified = _lastModified;
+            return result;
+        }
+
+        [HttpGet("physical/{fileName}")]
+        public IActionResult PhysicalFile(bool fileName)
+        {
+            return new ResumePhysicalFileResult(Path.Combine(AppContext.BaseDirectory + "wwwroot", "TestFile.txt"), "text/plain")
+            {
+                FileInlineName = "TestFile.txt",
+                LastModified = _lastModified
+            };
+        }
+
+        [HttpGet("virtual/{fileName}/{etag}")]
+        public IActionResult VirtualFile(bool fileName, bool etag)
+        {
+            var result = this.ResumeFile("TestFile.txt", "text/plain", fileName ? "TestFile.txt" : null, etag ? EntityTag : null);
+            result.LastModified = _lastModified;
+            return result;
+        }
+
+        [HttpGet("virtual/{fileName}")]
+        public IActionResult VirtualFile(bool fileName)
+        {
+            return new ResumeVirtualFileResult("TestFile.txt", "text/plain")
+            {
+                FileInlineName = "TestFile.txt",
+                LastModified = _lastModified
+            };
+        }
+    }
+}

+ 2 - 1
Test/Masuit.Tools.Core.Test/Masuit.Tools.Core.Test.csproj

@@ -10,6 +10,7 @@
 
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="9.0.0" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.0" />
     <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
     <PackageReference Include="xunit" Version="2.9.2" />
@@ -20,7 +21,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\..\Masuit.Tools.Core\Masuit.Tools.Core.csproj" />
+    <ProjectReference Include="..\..\Masuit.Tools.AspNetCore\Masuit.Tools.AspNetCore.csproj" />
   </ItemGroup>
 
   <ItemGroup>

+ 8 - 0
Test/Masuit.Tools.Test/Masuit.Tools.Test.csproj

@@ -132,6 +132,14 @@
       <Version>2.9.2</Version>
     </PackageReference>
   </ItemGroup>
+  <ItemGroup>
+    <Content Include="Resources\download-test-file.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Resources\download-test-file2.txt">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
   <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
 </Project>

+ 0 - 211
Test/Masuit.Tools.Test/Mvc/ResumeFileResultTests.cs

@@ -46,96 +46,6 @@ namespace Masuit.Tools.Test.Mvc
             Assert.NotEqual(etag1, etag2);
         }
 
-        [Fact]
-        public void IsNotModified_Is_False_If_IfNoneMatch_And_IfModifiedSince_Are_Empty()
-        {
-            Request.Headers[HttpHeaders.IfNoneMatch] = null;
-            Request.Headers[HttpHeaders.IfModifiedSince] = null;
-            Assert.False(new MockResumeFileResult(_file.FullName, Request).IsNotModified());
-        }
-
-        [Fact]
-        public void IsNotModified_Is_False_If_Etag_Is_Invalid_And_IfModifiedSince_Is_Null()
-        {
-            var etag = "invalid etag";
-            Request.Headers[HttpHeaders.IfNoneMatch] = etag;
-            Request.Headers[HttpHeaders.IfModifiedSince] = null;
-            Assert.False(new MockResumeFileResult(_file.FullName, Request).IsNotModified());
-        }
-
-        [Fact]
-        public void IsNotModified_Is_True_If_Etag_Is_Valid()
-        {
-            var etag = ResumeFileResult.Util.Etag(_file);
-            Request.Headers[HttpHeaders.IfNoneMatch] = etag;
-            Assert.True(new MockResumeFileResult(_file.FullName, Request).IsNotModified());
-        }
-
-        [Fact]
-        public void IsNotModified_Is_True_If_Etag_Is_Star()
-        {
-            var etag = "*";
-            Request.Headers[HttpHeaders.IfNoneMatch] = etag;
-            Assert.True(new MockResumeFileResult(_file.FullName, Request).IsNotModified());
-        }
-
-        [Fact]
-        public void IsNotModified_Is_False_If_Etag_Is_Empty_And_IfModifiedSince_Is_Invalid()
-        {
-            Request.Headers[HttpHeaders.IfModifiedSince] = DateTime.Now.ToString("R");
-            Assert.False(new MockResumeFileResult(_file.FullName, Request).IsNotModified());
-        }
-
-        [Fact]
-        public void IsNotModified_Is_False_If_Etag_Is_Empty_And_IfModifiedSince_Is_LastFileWriteTime()
-        {
-            Request.Headers[HttpHeaders.IfModifiedSince] = _file.LastWriteTime.ToString("R");
-            Assert.True(new MockResumeFileResult(_file.FullName, Request).IsNotModified());
-        }
-
-        [Fact]
-        public void IsPreconditionFailedTest_Is_False_If_ifMatch_And_ifUnmodifiedSince_Are_Empty()
-        {
-            Request.Headers[HttpHeaders.IfMatch] = null;
-            Request.Headers[HttpHeaders.IfUnmodifiedSince] = null;
-            Assert.False(new MockResumeFileResult(_file.FullName, Request).IsPreconditionFailed());
-        }
-
-        [Fact]
-        public void IsPreconditionFailedTest_Is_True_If_ifMatch_Doesnot_Match_Etag_Of_The_File()
-        {
-            Request.Headers[HttpHeaders.IfMatch] = "incorrect";
-            Assert.True(new MockResumeFileResult(_file.FullName, Request).IsPreconditionFailed());
-        }
-
-        [Fact]
-        public void IsPreconditionFailedTest_Is_False_If_ifMatch_Matches_Etag_Of_The_File()
-        {
-            Request.Headers[HttpHeaders.IfMatch] = ResumeFileResult.Util.Etag(_file);
-            Assert.False(new MockResumeFileResult(_file.FullName, Request).IsPreconditionFailed());
-        }
-
-        [Fact]
-        public void IsPreconditionFailedTest_Is_False_If_ifMatch_Equals_Star()
-        {
-            Request.Headers[HttpHeaders.IfMatch] = "*";
-            Assert.False(new MockResumeFileResult(_file.FullName, Request).IsPreconditionFailed());
-        }
-
-        [Fact]
-        public void IsPreconditionFailedTest_Is_True_If_ifUnmodifiedSince_Doesnot_Equal_FileLastWriteTime()
-        {
-            Request.Headers[HttpHeaders.IfUnmodifiedSince] = DateTime.Now.ToString("R");
-            Assert.True(new MockResumeFileResult(_file.FullName, Request).IsPreconditionFailed());
-        }
-
-        [Fact]
-        public void IsPreconditionFailedTest_Is_False_If_ifUnmodifiedSince_Equals_FileLastWriteTime()
-        {
-            Request.Headers[HttpHeaders.IfUnmodifiedSince] = _file.LastWriteTime.ToString("R");
-            Assert.False(new MockResumeFileResult(_file.FullName, Request).IsPreconditionFailed());
-        }
-
         [Fact]
         public void IsRangeNotSatisfiable_Is_True_If_Range_Header_Has_Invalid_Format()
         {
@@ -164,13 +74,6 @@ namespace Masuit.Tools.Test.Mvc
             Assert.True(new MockResumeFileResult(_file.FullName, Request).IsRangeNotSatisfiable());
         }
 
-        [Fact]
-        public void IsRangeNotSatisfiable_Is_False_If_Range_Header_Is_Null()
-        {
-            Request.Headers[HttpHeaders.Range] = null;
-            Assert.False(new MockResumeFileResult(_file.FullName, Request).IsRangeNotSatisfiable());
-        }
-
         [Fact]
         public void IsRangeNotSatisfiable_Is_False_If_Range_Has_StartsWith_Format()
         {
@@ -192,19 +95,6 @@ namespace Masuit.Tools.Test.Mvc
             Assert.False(new MockResumeFileResult(_file.FullName, Request).IsRangeNotSatisfiable());
         }
 
-        [Fact]
-        public void SendRange_Is_False_If_Range_And_ifRange_Headers_Are_Null()
-        {
-            Assert.False(new MockResumeFileResult(_file.FullName, Request).SendRange());
-        }
-
-        [Fact]
-        public void SendRange_Is_False_If_Range_Is_Null_And_ifRange_Is_Correct()
-        {
-            Request.Headers[HttpHeaders.IfRange] = ResumeFileResult.Util.Etag(_file);
-            Assert.False(new MockResumeFileResult(_file.FullName, Request).SendRange());
-        }
-
         [Fact]
         public void SendRange_Is_True_If_Range_Is_Correct_And_ifRange_Is_Null()
         {
@@ -228,37 +118,6 @@ namespace Masuit.Tools.Test.Mvc
             Assert.False(new MockResumeFileResult(_file.FullName, Request).SendRange());
         }
 
-        [Fact]
-        public void HeadersTest_Should_Not_Send_File_If_File_Has_Not_Been_Changed()
-        {
-            Request.Headers[HttpHeaders.IfNoneMatch] = ResumeFileResult.Util.Etag(_file);
-            var result = new MockResumeFileResult(_file.FullName, Request);
-            Assert.True(result.IsNotModified());
-            result.WriteFileTest(Response);
-            Assert.Equal((int)HttpStatusCode.NotModified, Response.StatusCode);
-            Assert.NotNull(Response.Headers[HttpHeaders.Etag]);
-            Assert.NotNull(Response.Headers[HttpHeaders.Expires]);
-            Assert.NotNull(Response.Headers[HttpHeaders.LastModified]);
-            Assert.Null(Response.Headers[HttpHeaders.ContentRange]);
-            Assert.Equal(0, Response.OutputStream.Length);
-        }
-
-        [Fact]
-        public void HeadersTest_Should_Not_Send_File_IfPreconditionFailed()
-        {
-            Request.Headers[HttpHeaders.IfMatch] = "invalid";
-            var result = new MockResumeFileResult(_file.FullName, Request);
-            Assert.True(result.IsPreconditionFailed());
-
-            result.WriteFileTest(Response);
-            Assert.Equal((int)HttpStatusCode.PreconditionFailed, Response.StatusCode);
-            Assert.NotNull(Response.Headers[HttpHeaders.Etag]);
-            Assert.NotNull(Response.Headers[HttpHeaders.Expires]);
-            Assert.NotNull(Response.Headers[HttpHeaders.LastModified]);
-            Assert.Null(Response.Headers[HttpHeaders.ContentRange]);
-            Assert.Equal(0, Response.OutputStream.Length);
-        }
-
         [Fact]
         public void HeadersTest_Should_Not_Send_File_Is_RangeNotSatisfiable()
         {
@@ -274,20 +133,6 @@ namespace Masuit.Tools.Test.Mvc
             Assert.Equal(0, Response.OutputStream.Length);
         }
 
-        [Fact]
-        public void HeadersTest_Should_Send_File_If_All_Headers_Are_Null()
-        {
-            var result = new MockResumeFileResult(_file.FullName, Request);
-            result.WriteFileTest(Response);
-            Assert.Equal((int)HttpStatusCode.OK, Response.StatusCode);
-            Assert.NotNull(Response.Headers[HttpHeaders.Etag]);
-            Assert.NotNull(Response.Headers[HttpHeaders.Expires]);
-            Assert.NotNull(Response.Headers[HttpHeaders.LastModified]);
-            Assert.NotNull(Response.Headers[HttpHeaders.ContentRange]);
-            Assert.NotNull(Response.Headers[HttpHeaders.ContentLength]);
-            Assert.Equal(_file.Length, Response.OutputStream.Length);
-        }
-
         [Fact]
         public void Range_First_500b()
         {
@@ -358,15 +203,6 @@ namespace Masuit.Tools.Test.Mvc
             Assert.Equal($"bytes 0-{(_file.Length - 1)}/{_file.Length}", Response.Headers[HttpHeaders.ContentRange]);
         }
 
-        [Fact]
-        public void Range_Whole_File_Without_RangeHeader()
-        {
-            var stream = GetResponseStream(null);
-            Assert.Equal(_file.Length, stream.Length);
-            Assert.Equal(200, Response.StatusCode);
-            Assert.Equal($"bytes 0-{(_file.Length - 1)}/{_file.Length}", Response.Headers[HttpHeaders.ContentRange]);
-        }
-
         [Fact]
         public void TransmissionRange_From_0_To_0()
         {
@@ -407,16 +243,6 @@ namespace Masuit.Tools.Test.Mvc
             AssertBytes(_file, Response.OutputStream, 0, (int)_file.Length);
         }
 
-        [Fact]
-        public void TransmissionRange_WholeFile_WithoutRangeHeader()
-        {
-            Request.Headers[HttpHeaders.Range] = null;
-            new MockResumeFileResult(_file.FullName, Request).WriteFileTest(Response);
-
-            Assert.Equal(_file.Length, Response.OutputStream.Length);
-            AssertBytes(_file, Response.OutputStream, 0, (int)_file.Length);
-        }
-
         [Fact]
         public void ShouldSend206If_Range_HeaderExists()
         {
@@ -425,36 +251,6 @@ namespace Masuit.Tools.Test.Mvc
             Assert.Equal(206, Response.StatusCode);
         }
 
-        [Fact]
-        public void ShouldSend200If_Range_HeaderDoesNotExist()
-        {
-            Request.Headers[HttpHeaders.Range] = null;
-            new MockResumeFileResult(_file.FullName, Request).WriteFileTest(Response);
-            Assert.Equal(200, Response.StatusCode);
-        }
-
-        [Fact]
-        public void IfRangeHeader_Should_Be_Ignored_If_ItNotEquals_Etag()
-        {
-            Request.Headers[HttpHeaders.IfRange] = "ifRange fake header";
-            var mock = new MockResumeFileResult(_file.FullName, Request);
-            mock.WriteFileTest(Response);
-
-            Assert.NotEqual(ResumeFileResult.Util.Etag(_file), Request.Headers[HttpHeaders.IfRange]);
-            Assert.Equal(200, Response.StatusCode);
-        }
-
-        [Fact]
-        public void Etag_Should_Be_Added_To_Response_If_It_Equals_With_IfRange_In_Request()
-        {
-            var etag = ResumeFileResult.Util.Etag(_file);
-            Request.Headers[HttpHeaders.IfRange] = etag;
-            var mock = new MockResumeFileResult(_file.FullName, Request);
-            mock.WriteFileTest(Response);
-            Assert.Equal(Response.Headers[HttpHeaders.Etag], etag);
-            Assert.Equal(200, Response.StatusCode);
-        }
-
         [Fact]
         public void Etag_Should_Be_Added_To_Response_If_It_Equals_With_IfRange_In_Request__PartialResponse()
         {
@@ -467,13 +263,6 @@ namespace Masuit.Tools.Test.Mvc
             Assert.Equal(206, Response.StatusCode);
         }
 
-        [Fact]
-        public void It_Should_Attach_Content_Disposition_If_There_Is_Download_File_Name()
-        {
-            new MockResumeFileResult(_file.FullName, Request, "test.name").WriteFileTest(Response);
-            Assert.NotNull(Response.Headers[HttpHeaders.ContentDisposition]);
-        }
-
         private Stream GetResponseStream(string range)
         {
             Response.ClearTestResponse();