EncodingTests.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  1. using System.Text;
  2. using Apq.Cfg.EncodingSupport;
  3. using Apq.Cfg.Sources.File;
  4. using static Apq.Cfg.EncodingSupport.EncodingDetectionResult;
  5. namespace Apq.Cfg.Tests;
  6. /// <summary>
  7. /// 编码检测相关测试
  8. /// </summary>
  9. public class EncodingTests : IDisposable
  10. {
  11. private readonly string _testDir;
  12. public EncodingTests()
  13. {
  14. _testDir = Path.Combine(Path.GetTempPath(), $"ApqCfgEncodingTests_{Guid.NewGuid():N}");
  15. Directory.CreateDirectory(_testDir);
  16. }
  17. public void Dispose()
  18. {
  19. if (Directory.Exists(_testDir))
  20. {
  21. try { Directory.Delete(_testDir, true); }
  22. catch { }
  23. }
  24. }
  25. // ========== EncodingOptions 测试 ==========
  26. [Fact]
  27. public void EncodingOptions_Default_HasCorrectValues()
  28. {
  29. // Arrange & Act
  30. var options = Apq.Cfg.EncodingSupport.EncodingOptions.Default;
  31. // Assert
  32. Assert.Equal(Apq.Cfg.EncodingSupport.EncodingReadStrategy.AutoDetect, options.ReadStrategy);
  33. Assert.Equal(Apq.Cfg.EncodingSupport.EncodingWriteStrategy.Utf8NoBom, options.WriteStrategy);
  34. Assert.Equal(0.6f, options.ConfidenceThreshold);
  35. Assert.True(options.EnableCache);
  36. Assert.False(options.EnableLogging);
  37. }
  38. [Fact]
  39. public void EncodingOptions_PowerShell_HasUtf8WithBom()
  40. {
  41. // Arrange & Act
  42. var options = Apq.Cfg.EncodingSupport.EncodingOptions.PowerShell;
  43. // Assert
  44. Assert.Equal(Apq.Cfg.EncodingSupport.EncodingWriteStrategy.Utf8WithBom, options.WriteStrategy);
  45. }
  46. [Fact]
  47. public void EncodingOptions_GetWriteEncoding_Utf8NoBom()
  48. {
  49. // Arrange
  50. var options = new Apq.Cfg.EncodingSupport.EncodingOptions { WriteStrategy = Apq.Cfg.EncodingSupport.EncodingWriteStrategy.Utf8NoBom };
  51. // Act
  52. var encoding = options.GetWriteEncoding();
  53. // Assert
  54. Assert.IsType<UTF8Encoding>(encoding);
  55. var utf8 = (UTF8Encoding)encoding;
  56. Assert.Empty(utf8.GetPreamble()); // 无 BOM
  57. }
  58. [Fact]
  59. public void EncodingOptions_GetWriteEncoding_Utf8WithBom()
  60. {
  61. // Arrange
  62. var options = new Apq.Cfg.EncodingSupport.EncodingOptions { WriteStrategy = Apq.Cfg.EncodingSupport.EncodingWriteStrategy.Utf8WithBom };
  63. // Act
  64. var encoding = options.GetWriteEncoding();
  65. // Assert
  66. Assert.IsType<UTF8Encoding>(encoding);
  67. var utf8 = (UTF8Encoding)encoding;
  68. Assert.Equal(3, utf8.GetPreamble().Length); // 有 BOM
  69. }
  70. [Fact]
  71. public void EncodingOptions_GetWriteEncoding_Preserve()
  72. {
  73. // Arrange
  74. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  75. var options = new Apq.Cfg.EncodingSupport.EncodingOptions { WriteStrategy = Apq.Cfg.EncodingSupport.EncodingWriteStrategy.Preserve };
  76. var detectedEncoding = Encoding.GetEncoding("GB2312");
  77. // Act
  78. var encoding = options.GetWriteEncoding(detectedEncoding);
  79. // Assert
  80. Assert.Equal(detectedEncoding, encoding);
  81. }
  82. [Fact]
  83. public void EncodingOptions_GetWriteEncoding_Specified()
  84. {
  85. // Arrange
  86. var specifiedEncoding = Encoding.Unicode;
  87. var options = new Apq.Cfg.EncodingSupport.EncodingOptions
  88. {
  89. WriteStrategy = Apq.Cfg.EncodingSupport.EncodingWriteStrategy.Specified,
  90. WriteEncoding = specifiedEncoding
  91. };
  92. // Act
  93. var encoding = options.GetWriteEncoding();
  94. // Assert
  95. Assert.Equal(specifiedEncoding, encoding);
  96. }
  97. // ========== EncodingDetectionResult 测试 ==========
  98. [Fact]
  99. public void EncodingDetectionResult_ToString_FormatsCorrectly()
  100. {
  101. // Arrange
  102. var result = new Apq.Cfg.EncodingSupport.EncodingDetectionResult(
  103. Encoding.UTF8,
  104. 0.95f,
  105. Apq.Cfg.EncodingSupport.EncodingDetectionMethod.Bom,
  106. true,
  107. "UTF-8",
  108. "/test/file.json");
  109. // Act
  110. var str = result.ToString();
  111. // Assert
  112. Assert.Contains("Bom", str);
  113. Assert.Contains("BOM", str);
  114. Assert.Contains("95", str); // 95%
  115. }
  116. // ========== EncodingDetector 测试 ==========
  117. [Fact]
  118. public void EncodingDetector_Detect_Utf8NoBom()
  119. {
  120. // Arrange
  121. var filePath = Path.Combine(_testDir, "utf8_no_bom.json");
  122. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(false));
  123. // Act
  124. var result = Apq.Cfg.EncodingSupport.EncodingDetector.Default.Detect(filePath);
  125. // Assert
  126. Assert.NotNull(result);
  127. Assert.Equal(Apq.Cfg.EncodingSupport.EncodingDetectionMethod.UtfUnknown, result.Method);
  128. Assert.False(result.HasBom);
  129. }
  130. [Fact]
  131. public void EncodingDetector_Detect_Utf8WithBom()
  132. {
  133. // Arrange
  134. var filePath = Path.Combine(_testDir, "utf8_with_bom.json");
  135. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(true));
  136. // Act
  137. var result = Apq.Cfg.EncodingSupport.EncodingDetector.Default.Detect(filePath);
  138. // Assert
  139. Assert.NotNull(result);
  140. Assert.Equal(Apq.Cfg.EncodingSupport.EncodingDetectionMethod.Bom, result.Method);
  141. Assert.True(result.HasBom);
  142. }
  143. [Fact]
  144. public void EncodingDetector_Detect_Utf16Le()
  145. {
  146. // Arrange
  147. var filePath = Path.Combine(_testDir, "utf16_le.json");
  148. File.WriteAllText(filePath, """{"key": "value"}""", Encoding.Unicode);
  149. // Act
  150. var result = Apq.Cfg.EncodingSupport.EncodingDetector.Default.Detect(filePath);
  151. // Assert
  152. Assert.NotNull(result);
  153. Assert.Equal(Apq.Cfg.EncodingSupport.EncodingDetectionMethod.Bom, result.Method);
  154. Assert.True(result.HasBom);
  155. }
  156. [Fact]
  157. public void EncodingDetector_Detect_NonExistentFile_ReturnsFallback()
  158. {
  159. // Arrange
  160. var filePath = Path.Combine(_testDir, "non_existent.json");
  161. // Act
  162. var result = Apq.Cfg.EncodingSupport.EncodingDetector.Default.Detect(filePath);
  163. // Assert
  164. Assert.NotNull(result);
  165. Assert.Equal(Apq.Cfg.EncodingSupport.EncodingDetectionMethod.Fallback, result.Method);
  166. Assert.Equal(Encoding.UTF8, result.Encoding);
  167. }
  168. [Fact]
  169. public void EncodingDetector_CustomReadMapping_OverridesDetection()
  170. {
  171. // Arrange
  172. var detector = new Apq.Cfg.EncodingSupport.EncodingDetector();
  173. var filePath = Path.Combine(_testDir, "custom_mapping.json");
  174. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(false));
  175. var customEncoding = Encoding.Unicode;
  176. detector.MappingConfig.AddReadMapping(filePath, EncodingMappingType.ExactPath, customEncoding, priority: 100);
  177. // Act
  178. var result = detector.Detect(filePath);
  179. // Assert
  180. Assert.Equal(Apq.Cfg.EncodingSupport.EncodingDetectionMethod.UserSpecified, result.Method);
  181. Assert.Equal(customEncoding, result.Encoding);
  182. // Cleanup
  183. detector.MappingConfig.RemoveReadMapping(filePath);
  184. }
  185. [Fact]
  186. public void EncodingDetector_Cache_ReturnsFromCache()
  187. {
  188. // Arrange
  189. var detector = new Apq.Cfg.EncodingSupport.EncodingDetector();
  190. var filePath = Path.Combine(_testDir, "cached.json");
  191. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(true));
  192. var options = new Apq.Cfg.EncodingSupport.EncodingOptions { EnableCache = true };
  193. // Act - 第一次检测
  194. var result1 = detector.Detect(filePath, options);
  195. // Act - 第二次检测(应该从缓存获取)
  196. var result2 = detector.Detect(filePath, options);
  197. // Assert
  198. Assert.Equal(Apq.Cfg.EncodingSupport.EncodingDetectionMethod.Bom, result1.Method);
  199. Assert.Equal(Apq.Cfg.EncodingSupport.EncodingDetectionMethod.Cached, result2.Method);
  200. }
  201. [Fact]
  202. public void EncodingDetector_Cache_InvalidatesOnFileChange()
  203. {
  204. // Arrange
  205. var detector = new Apq.Cfg.EncodingSupport.EncodingDetector();
  206. var filePath = Path.Combine(_testDir, "invalidate.json");
  207. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(true));
  208. var options = new Apq.Cfg.EncodingSupport.EncodingOptions { EnableCache = true };
  209. // Act - 第一次检测
  210. var result1 = detector.Detect(filePath, options);
  211. // 修改文件
  212. Thread.Sleep(100); // 确保时间戳不同
  213. File.WriteAllText(filePath, """{"key": "new_value"}""", new UTF8Encoding(true));
  214. // Act - 第二次检测(缓存应该失效)
  215. var result2 = detector.Detect(filePath, options);
  216. // Assert
  217. Assert.Equal(Apq.Cfg.EncodingSupport.EncodingDetectionMethod.Bom, result1.Method);
  218. Assert.Equal(Apq.Cfg.EncodingSupport.EncodingDetectionMethod.Bom, result2.Method); // 不是 Cached
  219. }
  220. [Fact]
  221. public void EncodingDetector_ExtensionMapping_ReturnsCorrectEncoding()
  222. {
  223. // Arrange
  224. var detector = new Apq.Cfg.EncodingSupport.EncodingDetector();
  225. var ps1Path = Path.Combine(_testDir, "script.ps1");
  226. // Act
  227. var encoding = detector.MappingConfig.GetWriteEncoding(ps1Path);
  228. // Assert
  229. Assert.NotNull(encoding);
  230. Assert.IsType<UTF8Encoding>(encoding);
  231. Assert.Equal(3, encoding.GetPreamble().Length); // UTF-8 BOM
  232. }
  233. [Fact]
  234. public void EncodingDetector_Logging_InvokesHandler()
  235. {
  236. // Arrange
  237. var detector = new Apq.Cfg.EncodingSupport.EncodingDetector();
  238. var filePath = Path.Combine(_testDir, "logging.json");
  239. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(true));
  240. Apq.Cfg.EncodingSupport.EncodingDetectionResult? loggedResult = null;
  241. detector.OnEncodingDetected += result => loggedResult = result;
  242. var options = new Apq.Cfg.EncodingSupport.EncodingOptions { EnableLogging = true };
  243. // Act
  244. detector.Detect(filePath, options);
  245. // Assert
  246. Assert.NotNull(loggedResult);
  247. Assert.Equal(filePath, Path.GetFullPath(loggedResult.FilePath));
  248. }
  249. [Fact]
  250. public void EncodingDetector_ClearCache_RemovesAllEntries()
  251. {
  252. // Arrange
  253. var detector = new Apq.Cfg.EncodingSupport.EncodingDetector();
  254. var filePath = Path.Combine(_testDir, "clear_cache.json");
  255. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(true));
  256. var options = new Apq.Cfg.EncodingSupport.EncodingOptions { EnableCache = true };
  257. detector.Detect(filePath, options);
  258. // Act
  259. detector.ClearCache();
  260. var result = detector.Detect(filePath, options);
  261. // Assert
  262. Assert.NotEqual(Apq.Cfg.EncodingSupport.EncodingDetectionMethod.Cached, result.Method);
  263. }
  264. // ========== CfgBuilder 编码选项测试 ==========
  265. [Fact]
  266. public async Task CfgBuilder_WithEncodingOptions_WritesWithCorrectEncoding()
  267. {
  268. // Arrange
  269. var jsonPath = Path.Combine(_testDir, "encoding_test.json");
  270. File.WriteAllText(jsonPath, """{}""");
  271. var encodingOptions = new Apq.Cfg.EncodingSupport.EncodingOptions
  272. {
  273. WriteStrategy = Apq.Cfg.EncodingSupport.EncodingWriteStrategy.Utf8WithBom
  274. };
  275. using var cfg = new CfgBuilder()
  276. .AddJson(jsonPath, level: 0, writeable: true, encoding: encodingOptions)
  277. .Build();
  278. // Act
  279. cfg.Set("Key", "Value");
  280. await cfg.SaveAsync();
  281. // Assert - 检查文件是否有 BOM
  282. var bytes = File.ReadAllBytes(jsonPath);
  283. Assert.True(bytes.Length >= 3);
  284. Assert.Equal(0xEF, bytes[0]);
  285. Assert.Equal(0xBB, bytes[1]);
  286. Assert.Equal(0xBF, bytes[2]);
  287. }
  288. [Fact]
  289. public void CfgBuilder_AddReadEncodingMapping_SetsReadMapping()
  290. {
  291. // Arrange
  292. var filePath = Path.Combine(_testDir, "custom_read.json");
  293. var customEncoding = Encoding.Unicode;
  294. // Act
  295. new CfgBuilder()
  296. .AddReadEncodingMapping(filePath, customEncoding);
  297. // Assert
  298. var result = FileCfgSourceBase.EncodingDetector.Detect(filePath);
  299. Assert.Equal(Apq.Cfg.EncodingSupport.EncodingDetectionMethod.UserSpecified, result.Method);
  300. Assert.Equal(customEncoding, result.Encoding);
  301. // Cleanup
  302. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveReadMapping(filePath);
  303. }
  304. [Fact]
  305. public void CfgBuilder_AddWriteEncodingMapping_SetsWriteMapping()
  306. {
  307. // Arrange
  308. var filePath = Path.Combine(_testDir, "custom_write.json");
  309. var customEncoding = Encoding.Unicode;
  310. // Act
  311. new CfgBuilder()
  312. .AddWriteEncodingMapping(filePath, customEncoding);
  313. // Assert
  314. var writeEncoding = FileCfgSourceBase.EncodingDetector.MappingConfig.GetWriteEncoding(filePath);
  315. Assert.NotNull(writeEncoding);
  316. Assert.Equal(customEncoding, writeEncoding);
  317. // Cleanup
  318. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveWriteMapping(filePath);
  319. }
  320. [Fact]
  321. public void EncodingDetector_SeparateReadWriteMappings_WorkIndependently()
  322. {
  323. // Arrange
  324. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  325. var detector = new Apq.Cfg.EncodingSupport.EncodingDetector();
  326. var filePath = Path.Combine(_testDir, "separate_rw.json");
  327. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(false));
  328. var readEncoding = Encoding.GetEncoding("GB2312");
  329. var writeEncoding = Encoding.Unicode;
  330. // Act
  331. detector.MappingConfig.AddReadMapping(filePath, EncodingMappingType.ExactPath, readEncoding, priority: 100);
  332. detector.MappingConfig.AddWriteMapping(filePath, EncodingMappingType.ExactPath, writeEncoding, priority: 100);
  333. // Assert - 读取映射
  334. var detectResult = detector.Detect(filePath);
  335. Assert.Equal(readEncoding, detectResult.Encoding);
  336. // Assert - 写入映射
  337. var customWriteEncoding = detector.MappingConfig.GetWriteEncoding(filePath);
  338. Assert.Equal(writeEncoding, customWriteEncoding);
  339. // Cleanup
  340. detector.MappingConfig.RemoveReadMapping(filePath);
  341. detector.MappingConfig.RemoveWriteMapping(filePath);
  342. }
  343. [Fact]
  344. public void CfgBuilder_WithEncodingDetectionLogging_RegistersHandler()
  345. {
  346. // Arrange
  347. var logged = false;
  348. // Act
  349. new CfgBuilder()
  350. .WithEncodingDetectionLogging(_ => logged = true);
  351. var filePath = Path.Combine(_testDir, "log_test.json");
  352. File.WriteAllText(filePath, """{}""");
  353. var options = new Apq.Cfg.EncodingSupport.EncodingOptions { EnableLogging = true };
  354. FileCfgSourceBase.EncodingDetector.Detect(filePath, options);
  355. // Assert
  356. Assert.True(logged);
  357. }
  358. // ========== 编码映射通配符测试 ==========
  359. [Fact]
  360. public void CfgBuilder_AddReadEncodingMappingWildcard_MatchesPattern()
  361. {
  362. // Arrange
  363. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  364. var customEncoding = Encoding.GetEncoding("GB2312");
  365. var pattern = "*.legacy.json";
  366. // Act
  367. new CfgBuilder()
  368. .AddReadEncodingMappingWildcard(pattern, customEncoding, priority: 100);
  369. var filePath = Path.Combine(_testDir, "config.legacy.json");
  370. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(false));
  371. // Assert
  372. var result = FileCfgSourceBase.EncodingDetector.Detect(filePath);
  373. Assert.Equal(EncodingDetectionMethod.UserSpecified, result.Method);
  374. Assert.Equal(customEncoding.CodePage, result.Encoding.CodePage);
  375. // Cleanup
  376. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveReadMapping(pattern);
  377. }
  378. [Fact]
  379. public void CfgBuilder_AddReadEncodingMappingWildcard_DoesNotMatchNonMatchingFile()
  380. {
  381. // Arrange
  382. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  383. var customEncoding = Encoding.GetEncoding("GB2312");
  384. var pattern = "*.legacy.json";
  385. // Act
  386. new CfgBuilder()
  387. .AddReadEncodingMappingWildcard(pattern, customEncoding, priority: 100);
  388. var filePath = Path.Combine(_testDir, "config.normal.json");
  389. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(true));
  390. // Assert - 不匹配的文件应该使用正常检测
  391. var result = FileCfgSourceBase.EncodingDetector.Detect(filePath);
  392. Assert.NotEqual(customEncoding.CodePage, result.Encoding.CodePage);
  393. // Cleanup
  394. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveReadMapping(pattern);
  395. }
  396. [Fact]
  397. public void CfgBuilder_AddWriteEncodingMappingWildcard_MatchesPattern()
  398. {
  399. // Arrange
  400. var customEncoding = new UTF8Encoding(true); // UTF-8 with BOM
  401. var pattern = "*.ps1";
  402. // Act
  403. new CfgBuilder()
  404. .AddWriteEncodingMappingWildcard(pattern, customEncoding, priority: 100);
  405. var filePath = Path.Combine(_testDir, "script.ps1");
  406. // Assert
  407. var writeEncoding = FileCfgSourceBase.EncodingDetector.MappingConfig.GetWriteEncoding(filePath);
  408. Assert.NotNull(writeEncoding);
  409. Assert.Equal(3, writeEncoding.GetPreamble().Length); // UTF-8 BOM
  410. // Cleanup
  411. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveWriteMapping(pattern);
  412. }
  413. // ========== 编码映射正则表达式测试 ==========
  414. [Fact]
  415. public void CfgBuilder_AddReadEncodingMappingRegex_MatchesPattern()
  416. {
  417. // Arrange
  418. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  419. var customEncoding = Encoding.GetEncoding("GB2312");
  420. var regexPattern = @"config\d+\.json$";
  421. // Act
  422. new CfgBuilder()
  423. .AddReadEncodingMappingRegex(regexPattern, customEncoding, priority: 100);
  424. var filePath = Path.Combine(_testDir, "config123.json");
  425. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(false));
  426. // Assert
  427. var result = FileCfgSourceBase.EncodingDetector.Detect(filePath);
  428. Assert.Equal(EncodingDetectionMethod.UserSpecified, result.Method);
  429. Assert.Equal(customEncoding.CodePage, result.Encoding.CodePage);
  430. // Cleanup
  431. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveReadMapping(regexPattern);
  432. }
  433. [Fact]
  434. public void CfgBuilder_AddReadEncodingMappingRegex_DoesNotMatchNonMatchingFile()
  435. {
  436. // Arrange
  437. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  438. var customEncoding = Encoding.GetEncoding("GB2312");
  439. var regexPattern = @"config\d+\.json$";
  440. // Act
  441. new CfgBuilder()
  442. .AddReadEncodingMappingRegex(regexPattern, customEncoding, priority: 100);
  443. var filePath = Path.Combine(_testDir, "configABC.json");
  444. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(true));
  445. // Assert - 不匹配的文件应该使用正常检测
  446. var result = FileCfgSourceBase.EncodingDetector.Detect(filePath);
  447. Assert.NotEqual(customEncoding.CodePage, result.Encoding.CodePage);
  448. // Cleanup
  449. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveReadMapping(regexPattern);
  450. }
  451. [Fact]
  452. public void CfgBuilder_AddWriteEncodingMappingRegex_MatchesPattern()
  453. {
  454. // Arrange
  455. var customEncoding = Encoding.Unicode;
  456. var regexPattern = @"logs[/\\].*\.log$";
  457. // Act
  458. new CfgBuilder()
  459. .AddWriteEncodingMappingRegex(regexPattern, customEncoding, priority: 100);
  460. var filePath = Path.Combine(_testDir, "logs", "app.log");
  461. // Assert
  462. var writeEncoding = FileCfgSourceBase.EncodingDetector.MappingConfig.GetWriteEncoding(filePath);
  463. Assert.NotNull(writeEncoding);
  464. Assert.Equal(customEncoding.CodePage, writeEncoding.CodePage);
  465. // Cleanup
  466. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveWriteMapping(regexPattern);
  467. }
  468. // ========== ConfigureEncodingMapping 委托测试 ==========
  469. [Fact]
  470. public void CfgBuilder_ConfigureEncodingMapping_AllowsCustomConfiguration()
  471. {
  472. // Arrange
  473. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  474. var customReadEncoding = Encoding.GetEncoding("GB2312");
  475. var customWriteEncoding = Encoding.Unicode;
  476. var filePath = Path.Combine(_testDir, "delegate_config.json");
  477. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(false));
  478. // Act
  479. new CfgBuilder()
  480. .ConfigureEncodingMapping(config =>
  481. {
  482. config.AddReadMapping(filePath, EncodingMappingType.ExactPath, customReadEncoding, priority: 100);
  483. config.AddWriteMapping(filePath, EncodingMappingType.ExactPath, customWriteEncoding, priority: 100);
  484. });
  485. // Assert - 读取映射
  486. var readResult = FileCfgSourceBase.EncodingDetector.Detect(filePath);
  487. Assert.Equal(EncodingDetectionMethod.UserSpecified, readResult.Method);
  488. Assert.Equal(customReadEncoding.CodePage, readResult.Encoding.CodePage);
  489. // Assert - 写入映射
  490. var writeEncoding = FileCfgSourceBase.EncodingDetector.MappingConfig.GetWriteEncoding(filePath);
  491. Assert.NotNull(writeEncoding);
  492. Assert.Equal(customWriteEncoding.CodePage, writeEncoding.CodePage);
  493. // Cleanup
  494. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveReadMapping(filePath);
  495. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveWriteMapping(filePath);
  496. }
  497. [Fact]
  498. public void CfgBuilder_ConfigureEncodingMapping_SupportsWildcardInDelegate()
  499. {
  500. // Arrange
  501. var customEncoding = new UTF8Encoding(true);
  502. var pattern = "*.script.ps1";
  503. // Act
  504. new CfgBuilder()
  505. .ConfigureEncodingMapping(config =>
  506. {
  507. config.AddWriteMapping(pattern, EncodingMappingType.Wildcard, customEncoding, priority: 100);
  508. });
  509. var filePath = Path.Combine(_testDir, "test.script.ps1");
  510. // Assert
  511. var writeEncoding = FileCfgSourceBase.EncodingDetector.MappingConfig.GetWriteEncoding(filePath);
  512. Assert.NotNull(writeEncoding);
  513. Assert.Equal(3, writeEncoding.GetPreamble().Length);
  514. // Cleanup
  515. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveWriteMapping(pattern);
  516. }
  517. // ========== WithEncodingConfidenceThreshold 测试 ==========
  518. [Fact]
  519. public void CfgBuilder_WithEncodingConfidenceThreshold_SetsThreshold()
  520. {
  521. // Arrange
  522. var filePath = Path.Combine(_testDir, "threshold_test.json");
  523. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(false));
  524. // Act
  525. var builder = new CfgBuilder()
  526. .WithEncodingConfidenceThreshold(0.9f);
  527. // Assert - 通过检测结果验证阈值设置
  528. // 由于阈值设置是全局的,我们只能验证方法调用不抛出异常
  529. Assert.NotNull(builder);
  530. }
  531. // ========== 编码映射优先级测试 ==========
  532. [Fact]
  533. public void EncodingMapping_HigherPriority_TakesPrecedence()
  534. {
  535. // Arrange
  536. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  537. var lowPriorityEncoding = Encoding.GetEncoding("GB2312");
  538. var highPriorityEncoding = Encoding.Unicode;
  539. var filePath = Path.Combine(_testDir, "priority_test.json");
  540. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(false));
  541. // Act - 先添加低优先级,再添加高优先级
  542. new CfgBuilder()
  543. .ConfigureEncodingMapping(config =>
  544. {
  545. config.AddReadMapping(filePath, EncodingMappingType.ExactPath, lowPriorityEncoding, priority: 10);
  546. config.AddReadMapping(filePath + "_high", EncodingMappingType.ExactPath, highPriorityEncoding, priority: 100);
  547. });
  548. // 使用通配符测试优先级
  549. var wildcardPattern = "*.priority.json";
  550. new CfgBuilder()
  551. .AddReadEncodingMappingWildcard(wildcardPattern, highPriorityEncoding, priority: 100);
  552. var testFile = Path.Combine(_testDir, "test.priority.json");
  553. File.WriteAllText(testFile, """{"key": "value"}""", new UTF8Encoding(false));
  554. // Assert
  555. var result = FileCfgSourceBase.EncodingDetector.Detect(testFile);
  556. Assert.Equal(highPriorityEncoding.CodePage, result.Encoding.CodePage);
  557. // Cleanup
  558. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveReadMapping(filePath);
  559. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveReadMapping(filePath + "_high");
  560. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveReadMapping(wildcardPattern);
  561. }
  562. // ========== 编码映射移除测试 ==========
  563. [Fact]
  564. public void EncodingMapping_RemoveMapping_RestoresDefaultBehavior()
  565. {
  566. // Arrange
  567. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
  568. var customEncoding = Encoding.GetEncoding("GB2312");
  569. var filePath = Path.Combine(_testDir, "remove_test.json");
  570. File.WriteAllText(filePath, """{"key": "value"}""", new UTF8Encoding(true));
  571. // Act - 添加映射
  572. new CfgBuilder()
  573. .AddReadEncodingMapping(filePath, customEncoding);
  574. var resultWithMapping = FileCfgSourceBase.EncodingDetector.Detect(filePath);
  575. Assert.Equal(customEncoding.CodePage, resultWithMapping.Encoding.CodePage);
  576. // Act - 移除映射
  577. FileCfgSourceBase.EncodingDetector.MappingConfig.RemoveReadMapping(filePath);
  578. // Assert - 应该恢复默认检测行为
  579. var resultWithoutMapping = FileCfgSourceBase.EncodingDetector.Detect(filePath);
  580. Assert.NotEqual(customEncoding.CodePage, resultWithoutMapping.Encoding.CodePage);
  581. Assert.Equal(EncodingDetectionMethod.Bom, resultWithoutMapping.Method); // UTF-8 with BOM
  582. }
  583. }