Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions FileDeduplicator.Test/VerbOutputTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,54 @@ public void StatsReportsTotalsForTheTree()
Assert.IsTrue(doomed.TrueForAll(TempTree.Exists), "Stats deleted a file.");
}

/// <summary>
/// A file Stats cannot hash is reported as unreadable, not counted as a duplicate or in the size.
/// </summary>
/// <remarks>
/// Held open with <see cref="FileShare.None"/> rather than stripped of its permissions, because a
/// process running as root reads through permissions but .NET enforces the share lock on every
/// platform. Before ktsu-dev/FileDeduplicator#137 this tree reported one duplicate file and zero
/// duplicate groups.
/// </remarks>
[TestMethod]
public void StatsReportsAFileItCannotHashAsUnreadable()
{
// Arrange
using TempTree tree = new();
_ = tree.Write("a.txt", "alpha");
_ = tree.Write("b.txt", "beta");
AbsoluteFilePath locked = tree.Write("locked.txt", "gamma");
using FileStream holder = new(locked.WeakString, FileMode.Open, FileAccess.ReadWrite, FileShare.None);

// Act
string output = ConsoleCapture.Normalize(ConsoleCapture.Run(new Stats { PathString = tree.Root.WeakString }));

// Assert -- the two readable files are 9 B and unique
Assert.Contains("Total files: 3", output);
Assert.Contains("Unreadable files: 1", output);
Assert.Contains("Total size: 9 B", output);
Assert.Contains("Unique files: 2", output);
Assert.Contains("Duplicate files: 0", output);
Assert.Contains("Duplicate groups: 0", output);
}

/// <summary>
/// A tree where every file hashes has no unreadable line at all.
/// </summary>
[TestMethod]
public void StatsOmitsTheUnreadableLineWhenEveryFileHashes()
{
// Arrange
using TempTree tree = new();
WriteOneGroup(tree, out _, out _);

// Act
string output = ConsoleCapture.Normalize(ConsoleCapture.Run(new Stats { PathString = tree.Root.WeakString }));

// Assert
Assert.DoesNotContain("Unreadable files:", output);
}

/// <summary>
/// A tree with nothing duplicated says so, in every verb that looks for duplicates.
/// </summary>
Expand Down
13 changes: 11 additions & 2 deletions FileDeduplicator/Verbs/Stats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,22 @@
Dictionary<string, List<AbsoluteFilePath>> hashGroups = Deduplicator.GroupByHash(fileHashes);
IReadOnlyList<DuplicateGroup> duplicates = Deduplicator.FindDuplicates(hashGroups);

long totalSize = files.Sum(f => new FileInfo(f.WeakString).Length);
// Every count below is taken over the files that hashed. HashFiles drops a file it cannot
// read, so counting against the scanned list would report each one as a duplicate of nothing,
// and sizing it would throw if it had vanished since the scan.
long totalSize = fileHashes.Keys.Sum(f => new FileInfo(f.WeakString).Length);
int uniqueFiles = hashGroups.Count;
int duplicateFiles = files.Count - uniqueFiles;
int duplicateFiles = fileHashes.Count - uniqueFiles;
int unreadableFiles = files.Count - fileHashes.Count;

Console.WriteLine("=== FileDeduplicator Statistics ===");
Console.WriteLine();
Console.WriteLine($"Total files: {files.Count}");
if (unreadableFiles > 0)
{
Console.WriteLine($"Unreadable files: {unreadableFiles}");
}

Console.WriteLine($"Total size: {DuplicateReport.FormatBytes(totalSize)}");
Console.WriteLine($"Unique files: {uniqueFiles}");
Console.WriteLine($"Duplicate files: {duplicateFiles}");
Expand All @@ -75,7 +84,7 @@

// Extension breakdown
Dictionary<string, int> extensionCounts = [];
foreach (DuplicateGroup group in duplicates)

Check warning on line 87 in FileDeduplicator/Verbs/Stats.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Loop should be simplified by calling Select(group => group.Files))

Check warning on line 87 in FileDeduplicator/Verbs/Stats.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Loop should be simplified by calling Select(group => group.Files))

Check warning on line 87 in FileDeduplicator/Verbs/Stats.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Loop should be simplified by calling Select(group => group.Files))

Check warning on line 87 in FileDeduplicator/Verbs/Stats.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Loop should be simplified by calling Select(group => group.Files))
{
string ext = System.IO.Path.GetExtension(group.Files[0].WeakString);
if (string.IsNullOrEmpty(ext))
Expand Down
Loading