| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182 |
- using System.Text;
- using Serilog;
- using YZWater.Core.Utils;
- namespace YZWater.Core.Tests.Services;
- /// <summary>
- /// 文本文件编码的回归测试。
- /// 背景:日志和 CSV 导出原先写成无 BOM 的 UTF-8,中文 Windows 上记事本、Excel、
- /// PowerShell 5.1 的 Get-Content 都会按 GBK 解释,取证时看到的是乱码。
- /// </summary>
- public class FileEncodingTests : IDisposable
- {
- private readonly string _dir = Path.Combine(
- Path.GetTempPath(), "yzwater-encoding-" + Guid.NewGuid().ToString("N"));
- public FileEncodingTests() => Directory.CreateDirectory(_dir);
- public void Dispose()
- {
- try { Directory.Delete(_dir, true); } catch (IOException) { }
- }
- [Fact]
- public void Utf8WithBom_Preamble_IsTheThreeUtf8SignatureBytes()
- {
- Assert.Equal(new byte[] { 0xEF, 0xBB, 0xBF }, FileTextEncoding.Utf8WithBom.Preamble.ToArray());
- }
- [Fact]
- public void SerilogFileSink_NewFile_StartsWithBomAndKeepsChineseReadable()
- {
- var previous = Serilog.Log.Logger;
- var path = Path.Combine(_dir, "log-encoding.txt");
- try
- {
- Log.Logger = new LoggerConfiguration()
- .WriteTo.File(path, encoding: FileTextEncoding.Utf8WithBom)
- .CreateLogger();
- Log.Information("集水池 {Level:F2} 米,泵已停止", 3.5f);
- Log.CloseAndFlush();
- var bytes = File.ReadAllBytes(path);
- Assert.Equal(new byte[] { 0xEF, 0xBB, 0xBF }, bytes.Take(3).ToArray());
- // 去 BOM 后必须是合法 UTF-8 且中文原样可读(乱码时这两条都会失败)
- var text = File.ReadAllText(path);
- Assert.Contains("集水池 3.50 米,泵已停止", text);
- Assert.DoesNotContain("", text);
- }
- finally
- {
- Log.Logger = previous;
- }
- }
- [Fact]
- public void AppendAllText_WritesBomOnlyOnce()
- {
- var path = Path.Combine(_dir, "crash-sim.log");
- File.AppendAllText(path, "第一段:集水池溢出\n", FileTextEncoding.Utf8WithBom);
- File.AppendAllText(path, "第二段:泵故障\n", FileTextEncoding.Utf8WithBom);
- var bytes = File.ReadAllBytes(path);
- Assert.Equal(1, CountOccurrences(bytes, new byte[] { 0xEF, 0xBB, 0xBF }));
- Assert.Contains("第二段:泵故障", File.ReadAllText(path));
- }
- private static int CountOccurrences(byte[] haystack, byte[] needle)
- {
- var count = 0;
- for (var i = 0; i <= haystack.Length - needle.Length; i++)
- {
- var match = true;
- for (var j = 0; j < needle.Length; j++)
- if (haystack[i + j] != needle[j]) { match = false; break; }
- if (match) count++;
- }
- return count;
- }
- }
|