Skip to content

Commit 44196b9

Browse files
hueifengclaude
andcommitted
test(IE.Tests): 简化 DrawingsFlush_Tests 的跨 TFM SHA1 实现
原 ImageByteConsistency_Tests 用 zip-level CRC32 验图片字节一致: - net471 上 ZipArchiveEntry.Crc32 不存在,需手写 IEEE 802.3 CRC32 (poly=0xEDB88320, init/xorout=0xFFFFFFFF) — ~70 行 #if + 表生成 + 流循环 - net6+ 直接用 `imageEntry.Crc32` 属性 两套实现都需要并发维护,且 CRC32 选型本身是 over-spec — 目标只是验图片字节没被改动,SHA1 等任意 hash 都够。 简化为全 TFM 走 SHA1.Create + BitConverter.ToString,删除 net471 #if 块 及手写 CRC32 实现。同时清理 4 段 verbose XML doc / inline 注释。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent fdfd544 commit 44196b9

1 file changed

Lines changed: 13 additions & 69 deletions

File tree

src/Magicodes.ExporterAndImporter.Tests/DrawingsFlush_Tests.cs

Lines changed: 13 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.IO;
44
using System.IO.Compression;
55
using System.Linq;
6+
using System.Security.Cryptography;
67
using System.Threading.Tasks;
78
using Magicodes.ExporterAndImporter.Core;
89
using Magicodes.ExporterAndImporter.Excel;
@@ -139,9 +140,8 @@ public class OnePicRowDto
139140
}
140141

141142
/// <summary>
142-
/// 验证 SHA1 改动后图片字节内容完全一致:
143-
/// 1 张图 vs 100 张同图的 xlsx (两个文件都走 IExporter.Export 生成),
144-
/// 提取 /xl/media/image1.* 的 zip-level CRC32, 应完全一致.
143+
/// 验证 SHA1 改动后图片字节内容一致:
144+
/// 1 张图 vs 100 张同图的 xlsx, /xl/media/image1.* 原始字节 SHA1 应完全一致.
145145
/// </summary>
146146
public class ImageByteConsistency_Tests : TestBase
147147
{
@@ -158,11 +158,7 @@ private static string GetSamplePngPath() =>
158158
[Fact(DisplayName = "ImageByteConsistency_SingleVsRepeatedDrawings_ImageBytesIdentical")]
159159
public async Task SingleVsRepeatedDrawings_ImageBytesIdentical()
160160
{
161-
// baseline: 1 行 1 列 = 1 张图
162-
// repeat: 100 行 1 列 = 100 张同图 (走 IE 同图去重路径)
163-
// 两个 xlsx 都走 IExporter.Export 生成 (与生产路径一致),
164-
// 用 ZipArchive 提取 /xl/media/image1.*, 对比 zip-level CRC32,
165-
// 应完全一致 → 确认 SHA1 改动未改变图片字节内容.
161+
// baseline: 1 张图, repeat: 100 张同图 (走 IE 同图去重路径)
166162
var singlePath = GetTestFilePath($"{nameof(SingleVsRepeatedDrawings_ImageBytesIdentical)}_single.xlsx");
167163
var repeatPath = GetTestFilePath($"{nameof(SingleVsRepeatedDrawings_ImageBytesIdentical)}_repeat.xlsx");
168164
DeleteFile(singlePath);
@@ -176,78 +172,26 @@ public async Task SingleVsRepeatedDrawings_ImageBytesIdentical()
176172
var repeatData = DrawingsFlush_Tests.BuildOnePicRowDto(100, GetSamplePngPath());
177173
await exporter.Export(repeatPath, repeatData);
178174

179-
var singleCrc = ExtractFirstImageCrc32(singlePath);
180-
var repeatCrc = ExtractFirstImageCrc32(repeatPath);
175+
var singleHash = ComputeFirstImageHash(singlePath);
176+
var repeatHash = ComputeFirstImageHash(repeatPath);
181177

182-
singleCrc.ShouldBe(repeatCrc);
183-
_output.WriteLine($"single (1 行) image1 CRC32 = 0x{singleCrc:X8}, repeat (100 行) image1 CRC32 = 0x{repeatCrc:X8}");
178+
singleHash.ShouldBe(repeatHash);
179+
_output.WriteLine($"single (1 行) image1 SHA1 = {singleHash}, repeat (100 行) image1 SHA1 = {repeatHash}");
184180
}
185181

186182
/// <summary>
187-
/// 从 xlsx zip 中提取第一个 /xl/media/image* 条目的 CRC32 (zip-level).
188-
/// net471 上 ZipArchiveEntry.Crc32 不存在, 走流手算; 其他 target 直接用原生属性.
183+
/// 从 xlsx zip 读取第一个 /xl/media/image* 条目的全部字节, 返回 SHA1 十六进制.
189184
/// </summary>
190-
private static uint ExtractFirstImageCrc32(string xlsxPath)
185+
private static string ComputeFirstImageHash(string xlsxPath)
191186
{
192187
using var archive = ZipFile.OpenRead(xlsxPath);
193188
var imageEntry = archive.Entries
194189
.Where(e => e.FullName.StartsWith("xl/media/", StringComparison.OrdinalIgnoreCase))
195190
.OrderBy(e => e.FullName, StringComparer.Ordinal)
196191
.First();
197-
#if NET471
198-
using (var stream = imageEntry.Open())
199-
{
200-
return ComputeZipEntryCrc32(stream);
201-
}
202-
#else
203-
return imageEntry.Crc32;
204-
#endif
205-
}
206-
207-
#if NET471
208-
/// <summary>
209-
/// IEEE 802.3 CRC32 (zip 规范): poly=0xEDB88320 (reflected), init=0xFFFFFFFF, xorout=0xFFFFFFFF.
210-
/// 与 ZipArchiveEntry.Crc32 (NET6+) 字节级一致 — 已用 System.IO.Hashing.Crc32 在 11 个样本
211-
/// (含 png 实测数据) + 真实 zip entry 四路对比验证. 表在类型加载时按 poly 运行时生成,
212-
/// 避免硬编码 256 项引入抄写错误 (CLR 保证 static readonly 初始化线程安全).
213-
/// 循环风格对齐 dotnet/runtime 的 Crc32ParameterSet.UpdateScalar.
214-
/// </summary>
215-
private static readonly uint[] ZipCrc32LookupTable = BuildZipCrc32LookupTable();
216-
217-
private static uint[] BuildZipCrc32LookupTable()
218-
{
219-
const uint polynomial = 0xEDB88320u;
220-
var lookupTable = new uint[256];
221-
for (uint i = 0; i < 256; i++)
222-
{
223-
uint c = i;
224-
for (int k = 0; k < 8; k++)
225-
{
226-
c = (c & 1u) != 0 ? (c >> 1) ^ polynomial : c >> 1;
227-
}
228-
lookupTable[i] = c;
229-
}
230-
return lookupTable;
231-
}
232-
233-
private static uint ComputeZipEntryCrc32(Stream stream)
234-
{
235-
uint[] lookupTable = ZipCrc32LookupTable;
236-
System.Diagnostics.Debug.Assert(lookupTable.Length == 256);
237-
uint crc = 0xFFFFFFFFu;
238-
// net471 没有 Stream.Read(Span<byte>), 用 byte[] + int 偏移.
239-
var buffer = new byte[4096];
240-
int read;
241-
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
242-
{
243-
for (int i = 0; i < read; i++)
244-
{
245-
byte idx = (byte)(crc ^ buffer[i]);
246-
crc = lookupTable[idx] ^ (crc >> 8);
247-
}
248-
}
249-
return crc ^ 0xFFFFFFFFu;
192+
using var stream = imageEntry.Open();
193+
using var sha1 = SHA1.Create();
194+
return BitConverter.ToString(sha1.ComputeHash(stream)).Replace("-", string.Empty);
250195
}
251-
#endif
252196
}
253197
}

0 commit comments

Comments
 (0)