From f1268d49936a26e5530d2fc3fc7146a941d79437 Mon Sep 17 00:00:00 2001 From: Andrei Errapart Date: Wed, 26 Aug 2026 12:55:44 +0200 Subject: [PATCH] Tests: leave the working tree clean after a full run Every test run dirtied two tracked files: tests/COVERAGE.md (only its "Generated:" timestamp line changed) and TestData/Generated/Individual/ PCB/BODY_3D_STEP_CHECKSUM0.PcbLib (rewritten with only the OLE directory FILETIMEs differing). Drop the timestamp line from the coverage report and rewrite the file only when its content actually changed. Save the zero-checksum PcbLib to the system temp directory instead of TestData, make FileComparisonTest regenerate that variant in memory rather than reading the file from disk, and untrack the generated fixture. --- .../PCB/BODY_3D_STEP_CHECKSUM0.PcbLib | Bin 118272 -> 0 bytes tests/COVERAGE.md | 2 -- .../PropertyCoverage/CoverageReportTests.cs | 9 ++++--- .../RoundTrip/FileComparisonTest.cs | 24 +++++++++++++----- .../RoundTrip/RawDataPreservationTest.cs | 5 ++-- 5 files changed, 26 insertions(+), 14 deletions(-) delete mode 100644 TestData/Generated/Individual/PCB/BODY_3D_STEP_CHECKSUM0.PcbLib diff --git a/TestData/Generated/Individual/PCB/BODY_3D_STEP_CHECKSUM0.PcbLib b/TestData/Generated/Individual/PCB/BODY_3D_STEP_CHECKSUM0.PcbLib deleted file mode 100644 index ef8181d3146b20999299f41ed2357311e30ba7cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118272 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 &JsV+Rrjld}RM88} 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 diff --git a/tests/COVERAGE.md b/tests/COVERAGE.md index e4161d2..68697af 100644 --- a/tests/COVERAGE.md +++ b/tests/COVERAGE.md @@ -1,7 +1,5 @@ # Property Coverage Report -Generated: 2026-06-19 09:47 UTC - ## PCB Primitive Types | Type | Files | Primitives | JSON Props | Mapped | Missing | Coverage | diff --git a/tests/OriginalCircuit.Altium.Tests/PropertyCoverage/CoverageReportTests.cs b/tests/OriginalCircuit.Altium.Tests/PropertyCoverage/CoverageReportTests.cs index e163a2b..8b21304 100644 --- a/tests/OriginalCircuit.Altium.Tests/PropertyCoverage/CoverageReportTests.cs +++ b/tests/OriginalCircuit.Altium.Tests/PropertyCoverage/CoverageReportTests.cs @@ -17,8 +17,6 @@ public void GenerateCoverageReport() var sb = new StringBuilder(); sb.AppendLine("# Property Coverage Report"); sb.AppendLine(); - sb.AppendLine($"Generated: {DateTime.UtcNow:yyyy-MM-dd HH:mm} UTC"); - sb.AppendLine(); // PCB types sb.AppendLine("## PCB Primitive Types"); @@ -121,10 +119,13 @@ public void GenerateCoverageReport() sb.AppendLine(); } - // Write the report + // Write the report only when its content changed, so a plain test run + // leaves the working tree clean. var reportPath = Path.Combine(GetTestDataPath(), "..", "tests", "COVERAGE.md"); reportPath = Path.GetFullPath(reportPath); - File.WriteAllText(reportPath, sb.ToString()); + var report = sb.ToString(); + if (!File.Exists(reportPath) || File.ReadAllText(reportPath) != report) + File.WriteAllText(reportPath, report); // Also output to test console Assert.True(true, sb.ToString()); diff --git a/tests/OriginalCircuit.Altium.Tests/RoundTrip/FileComparisonTest.cs b/tests/OriginalCircuit.Altium.Tests/RoundTrip/FileComparisonTest.cs index fb27e22..4090b2b 100644 --- a/tests/OriginalCircuit.Altium.Tests/RoundTrip/FileComparisonTest.cs +++ b/tests/OriginalCircuit.Altium.Tests/RoundTrip/FileComparisonTest.cs @@ -1,6 +1,7 @@ using System.IO.Compression; using System.Text; using OpenMcdf; +using OriginalCircuit.Altium.Serialization.Readers; using Xunit; using Xunit.Abstractions; @@ -16,24 +17,35 @@ public FileComparisonTest(ITestOutputHelper output) } [SkippableFact] - public void CompareOriginalAndWrittenPcbLib() + public async Task CompareOriginalAndWrittenPcbLib() { var origPath = GetDataPath("TestData", "Generated", "Individual", "PCB", "BODY_3D_STEP.PcbLib"); - var writtenPath = GetDataPath("TestData", "Generated", "Individual", "PCB", "BODY_3D_STEP_CHECKSUM0.PcbLib"); - if (!File.Exists(origPath) || !File.Exists(writtenPath)) { Skip.If(true, "Test data not available"); return; } + if (!File.Exists(origPath)) { Skip.If(true, "Test data not available"); return; } + + // Regenerate the written variant in memory (zeroed model checksums, as + // RawDataPreservationTest saves it) instead of depending on a file on disk. + await using var origRead = File.OpenRead(origPath); + var library = new PcbLibReader().Read(origRead); + foreach (var model in library.Models) + model.Checksum = 0; + using var writtenMs = new MemoryStream(); + await library.SaveAsync(writtenMs); _output.WriteLine("=== ORIGINAL ==="); DumpCompoundFile(origPath); _output.WriteLine("\n\n=== WRITTEN ==="); - DumpCompoundFile(writtenPath); + _output.WriteLine($"In-memory zero-checksum variant ({writtenMs.Length} bytes)"); + writtenMs.Position = 0; + using (var writtenDump = RootStorage.Open(writtenMs, StorageModeFlags.LeaveOpen)) + DumpStorage(writtenDump, ""); // Now do a detailed byte comparison of each stream _output.WriteLine("\n\n=== STREAM-BY-STREAM COMPARISON ==="); using var origFs = File.OpenRead(origPath); using var origCf = RootStorage.Open(origFs, StorageModeFlags.LeaveOpen); - using var writtenFs = File.OpenRead(writtenPath); - using var writtenCf = RootStorage.Open(writtenFs, StorageModeFlags.LeaveOpen); + writtenMs.Position = 0; + using var writtenCf = RootStorage.Open(writtenMs, StorageModeFlags.LeaveOpen); CompareStorage(origCf, writtenCf, ""); } diff --git a/tests/OriginalCircuit.Altium.Tests/RoundTrip/RawDataPreservationTest.cs b/tests/OriginalCircuit.Altium.Tests/RoundTrip/RawDataPreservationTest.cs index 1a8881e..7e8c47f 100644 --- a/tests/OriginalCircuit.Altium.Tests/RoundTrip/RawDataPreservationTest.cs +++ b/tests/OriginalCircuit.Altium.Tests/RoundTrip/RawDataPreservationTest.cs @@ -30,8 +30,9 @@ public async Task PcbLib_SaveWithZeroChecksum() model.Checksum = 0; } - // Save to a new file next to the original - var outPath = GetDataPath("TestData", "Generated", "Individual", "PCB", "BODY_3D_STEP_CHECKSUM0.PcbLib"); + // Save outside the repository — writing into TestData would dirty the working + // tree on every run (the OLE directory FILETIMEs differ between writes). + var outPath = Path.Combine(Path.GetTempPath(), "BODY_3D_STEP_CHECKSUM0.PcbLib"); await library.SaveAsync(outPath, new OriginalCircuit.Eda.Models.SaveOptions()); _output.WriteLine($"Saved to: {outPath}"); }