diff --git a/FileDeduplicator.Test/VerbOutputTests.cs b/FileDeduplicator.Test/VerbOutputTests.cs
index f650ade..ad6db6e 100644
--- a/FileDeduplicator.Test/VerbOutputTests.cs
+++ b/FileDeduplicator.Test/VerbOutputTests.cs
@@ -152,6 +152,26 @@ public void StatsReportsAFileItCannotHashAsUnreadable()
Assert.Contains("Duplicate groups: 0", output);
}
+ ///
+ /// A file that cannot be hashed is named by its full path, so the failing copy can be found
+ /// among others sharing its file name.
+ ///
+ [TestMethod]
+ public void AHashingErrorNamesTheFullPath()
+ {
+ // Arrange
+ using TempTree tree = new();
+ _ = tree.Write("one/IMG_0001.jpg", "alpha");
+ AbsoluteFilePath locked = tree.Write("two/IMG_0001.jpg", "beta");
+ using FileStream holder = new(locked.WeakString, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
+
+ // Act
+ string output = ConsoleCapture.Run(new Stats { PathString = tree.Root.WeakString });
+
+ // Assert
+ Assert.Contains($"Error hashing {locked}:", output);
+ }
+
///
/// A tree where every file hashes has no unreadable line at all.
///
diff --git a/FileDeduplicator/FileHasher.cs b/FileDeduplicator/FileHasher.cs
index bfb5ff0..84b5ce9 100644
--- a/FileDeduplicator/FileHasher.cs
+++ b/FileDeduplicator/FileHasher.cs
@@ -51,7 +51,9 @@ private static void ReportSkipped(AbsoluteFilePath filePath, Exception ex)
{
lock (ConsoleLock)
{
- Console.WriteLine($" Error hashing {filePath.FileName}: {ex.Message}");
+ // The full path, not the file name: index.js or IMG_0001.jpg can repeat dozens of times in
+ // one tree, and this line is the only place the failing copy is ever named.
+ Console.WriteLine($" Error hashing {filePath}: {ex.Message}");
}
}