Add Git overlay, operation tools, and virtualized preview so large folders stay responsive.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Explorer.Domain;
|
||||
using Explorer.Domain.Abstractions;
|
||||
|
||||
namespace Explorer.Domain.Tests;
|
||||
|
||||
@@ -67,6 +68,21 @@ public class PathRulesTests
|
||||
}
|
||||
}
|
||||
|
||||
public class WindowsFileNamesTests
|
||||
{
|
||||
[Fact]
|
||||
public void Rejects_reserved_and_illegal_names()
|
||||
{
|
||||
Assert.False(WindowsFileNames.IsValid("CON", out _));
|
||||
Assert.False(WindowsFileNames.IsValid("a<.txt", out _));
|
||||
Assert.False(WindowsFileNames.IsValid("ends.", out _));
|
||||
Assert.True(WindowsFileNames.IsValid("Vacation.jpg", out _));
|
||||
Assert.Equal(("Vacation", "jpg"), WindowsFileNames.Split("Vacation.jpg"));
|
||||
Assert.Equal("Hello World", WindowsFileNames.ApplyCase("hello world", RenameCaseMode.Title));
|
||||
Assert.Equal("007", WindowsFileNames.FormatCounter(7, 3));
|
||||
}
|
||||
}
|
||||
|
||||
public class ArchiveFormatsTests
|
||||
{
|
||||
[Fact]
|
||||
@@ -79,6 +95,8 @@ public class ArchiveFormatsTests
|
||||
Assert.True(ArchiveFormats.IsZipFamily("photos.cbz"));
|
||||
Assert.False(ArchiveFormats.IsArchive("notes.txt"));
|
||||
Assert.False(ArchiveFormats.IsArchive("report.docx"));
|
||||
Assert.Equal("2019-backup", ArchiveFormats.Stem("2019-backup.7z"));
|
||||
Assert.Equal("bundle", ArchiveFormats.Stem("bundle.tar.gz"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -267,3 +285,156 @@ public class DragDropPolicyTests
|
||||
Assert.False(PathRules.IsSameVolume(@"\\media\movies\a", @"\\media\tv\b"));
|
||||
}
|
||||
}
|
||||
|
||||
public class DuplicateClassifierTests
|
||||
{
|
||||
[Fact]
|
||||
public void Same_source_and_file_id_is_hardlink_not_duplicate()
|
||||
{
|
||||
var entries = new[]
|
||||
{
|
||||
File(1, 10, 100),
|
||||
File(2, 10, 100)
|
||||
};
|
||||
Assert.True(DuplicateClassifier.IsHardlinkOnly(entries));
|
||||
Assert.Equal(DuplicateClass.Hardlink, DuplicateClassifier.Classify(entries, []));
|
||||
Assert.True(DuplicateClassifier.IsHiddenByDefault(DuplicateClass.Hardlink));
|
||||
Assert.Equal(0, DuplicateClassifier.ClassifyGroup(new DuplicateGroup { SizeBytes = 50, Entries = entries }, []).WastedBytes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Same_file_id_on_different_volumes_is_not_a_hardlink()
|
||||
{
|
||||
var entries = new[]
|
||||
{
|
||||
File(1, 10, 100, sourceId: 1),
|
||||
File(2, 10, 100, sourceId: 2)
|
||||
};
|
||||
Assert.False(DuplicateClassifier.IsHardlinkOnly(entries));
|
||||
Assert.Equal(DuplicateClass.Unknown, DuplicateClassifier.Classify(entries, []));
|
||||
Assert.Equal(2, DuplicateClassifier.UniqueFileCount(entries));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Marked_intentional_relation_is_hidden_by_default()
|
||||
{
|
||||
var entries = new[] { File(1, 10, null), File(2, 11, null) };
|
||||
var relations = new[]
|
||||
{
|
||||
new FileRelation
|
||||
{
|
||||
LeftEntryId = 1,
|
||||
RightEntryId = 2,
|
||||
Kind = FileRelationKind.IntentionalDuplicate,
|
||||
Origin = FileRelationOrigin.User,
|
||||
CreatedUtc = DateTimeOffset.UtcNow
|
||||
}
|
||||
};
|
||||
var classified = DuplicateClassifier.Classify(entries, relations);
|
||||
Assert.Equal(DuplicateClass.Intentional, classified);
|
||||
Assert.True(DuplicateClassifier.IsHiddenByDefault(classified));
|
||||
Assert.False(DuplicateClassifier.IsHiddenByDefault(DuplicateClass.Unknown));
|
||||
Assert.False(DuplicateClassifier.IsHiddenByDefault(DuplicateClass.Accidental));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sync_and_backup_relations_classify_ahead_of_unknown()
|
||||
{
|
||||
var entries = new[] { File(1, 10, 1), File(2, 11, 2) };
|
||||
Assert.Equal(DuplicateClass.Synchronized, DuplicateClassifier.Classify(entries,
|
||||
[
|
||||
Rel(1, 2, FileRelationKind.SyncCopy)
|
||||
]));
|
||||
Assert.Equal(DuplicateClass.Backup, DuplicateClassifier.Classify(entries,
|
||||
[
|
||||
Rel(1, 2, FileRelationKind.BackupCopy)
|
||||
]));
|
||||
}
|
||||
|
||||
private static IndexEntry File(long id, long sourceFileId, long? fileId, long sourceId = 1)
|
||||
=> new()
|
||||
{
|
||||
Id = id,
|
||||
SourceId = sourceId,
|
||||
Name = id + ".bin",
|
||||
NameNorm = id + ".bin",
|
||||
PathRel = id + ".bin",
|
||||
SizeBytes = 50,
|
||||
FileId = fileId,
|
||||
LastSeenUtc = DateTimeOffset.UtcNow
|
||||
};
|
||||
|
||||
private static FileRelation Rel(long left, long right, FileRelationKind kind)
|
||||
=> new()
|
||||
{
|
||||
LeftEntryId = left,
|
||||
RightEntryId = right,
|
||||
Kind = kind,
|
||||
Origin = FileRelationOrigin.User,
|
||||
CreatedUtc = DateTimeOffset.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
public class OperationProfileTests
|
||||
{
|
||||
[Fact]
|
||||
public void Auto_run_is_copy_only()
|
||||
{
|
||||
var copy = new OperationProfile { Name = "Copy", DoCopy = true, AutoRun = true };
|
||||
Assert.True(copy.CanAutoRun);
|
||||
|
||||
var compress = new OperationProfile { Name = "Archive", DoCopy = true, DoCompress = true, AutoRun = true };
|
||||
Assert.False(compress.CanAutoRun);
|
||||
|
||||
var rename = new OperationProfile { Name = "Rename", DoCopy = true, DoRename = true, RenamePrefix = "x_", AutoRun = true };
|
||||
Assert.False(rename.CanAutoRun);
|
||||
}
|
||||
}
|
||||
|
||||
public class FileClassifierTests
|
||||
{
|
||||
[Fact]
|
||||
public void Extension_and_archive_signals()
|
||||
{
|
||||
Assert.Equal(FileCategory.Photos, Classify("sunset.jpg"));
|
||||
Assert.Equal(FileCategory.Video, Classify("clip.mkv"));
|
||||
Assert.Equal(FileCategory.Audio, Classify("track.mp3"));
|
||||
Assert.Equal(FileCategory.Documents, Classify("notes.pdf"));
|
||||
Assert.Equal(FileCategory.Installer, Classify("Git-64-bit.exe"));
|
||||
Assert.Equal(FileCategory.Archive, Classify("pack.7z"));
|
||||
Assert.Equal(FileCategory.Backup, Classify("db.bak"));
|
||||
Assert.Equal(FileCategory.Unknown, Classify("mystery.bin"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Folder_structure_and_git()
|
||||
{
|
||||
Assert.Equal(FileCategory.CodeRepository, Classify("Explorer", isDirectory: true, isRepoRoot: true));
|
||||
Assert.Equal(FileCategory.BuildOutput, Classify("node_modules", isDirectory: true));
|
||||
Assert.Equal(FileCategory.BuildOutput, Classify("bin", isDirectory: true));
|
||||
Assert.Equal(FileCategory.Photos, Classify("DCIM", isDirectory: true));
|
||||
Assert.Equal(FileCategory.SystemData, FileClassifier.Classify(
|
||||
"$RECYCLE.BIN", @"C:\$RECYCLE.BIN", true,
|
||||
AttributeFlags.Directory | AttributeFlags.Hidden | AttributeFlags.System).Category);
|
||||
Assert.True(FileClassifier.ShouldLeave(FileCategory.Unknown));
|
||||
Assert.True(FileClassifier.ShouldLeave(FileCategory.BuildOutput));
|
||||
Assert.False(FileClassifier.ShouldLeave(FileCategory.Photos));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Majority_of_children_classifies_a_folder()
|
||||
{
|
||||
var mixed = FileClassifier.Classify(
|
||||
"Vacation", @"C:\Downloads\Vacation", true, AttributeFlags.Directory,
|
||||
childCategories: [FileCategory.Photos, FileCategory.Photos, FileCategory.Photos, FileCategory.Documents]);
|
||||
Assert.Equal(FileCategory.Photos, mixed.Category);
|
||||
|
||||
var split = FileClassifier.Classify(
|
||||
"Misc", @"C:\Downloads\Misc", true, AttributeFlags.Directory,
|
||||
childCategories: [FileCategory.Photos, FileCategory.Video]);
|
||||
Assert.Equal(FileCategory.Unknown, split.Category);
|
||||
}
|
||||
|
||||
private static FileCategory Classify(string name, bool isDirectory = false, bool isRepoRoot = false)
|
||||
=> FileClassifier.Classify(name, @"C:\Downloads\" + name, isDirectory, isDirectory ? AttributeFlags.Directory : 0, isRepoRoot).Category;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user