Skip to content

Commit b2bcc1d

Browse files
[fix] Always scan selected root even if hidden/system (#256)
1 parent a274442 commit b2bcc1d

File tree

3 files changed

+209
-9
lines changed

3 files changed

+209
-9
lines changed

src/ByteSync.Client/Services/Inventories/InventoryBuilder.cs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,24 @@ private void AddInaccessibleDirectoryAndLog(InventoryPart inventoryPart, Directo
305305
AddFileSystemDescription(inventoryPart, subDirectoryDescription);
306306
_logger.LogWarning(ex, message, directoryInfo.FullName);
307307
}
308+
309+
private bool IsRootPath(InventoryPart inventoryPart, FileSystemInfo fileSystemInfo)
310+
{
311+
var rootPath = NormalizePath(inventoryPart.RootPath);
312+
var currentPath = NormalizePath(fileSystemInfo.FullName);
313+
var comparison = OSPlatform == OSPlatforms.Windows
314+
? StringComparison.OrdinalIgnoreCase
315+
: StringComparison.Ordinal;
316+
317+
return string.Equals(rootPath, currentPath, comparison);
318+
}
319+
320+
private static string NormalizePath(string path)
321+
{
322+
var fullPath = Path.GetFullPath(path);
323+
324+
return Path.TrimEndingDirectorySeparator(fullPath);
325+
}
308326

309327
private bool ShouldIgnoreHiddenDirectory(DirectoryInfo directoryInfo)
310328
{
@@ -339,12 +357,14 @@ private bool ShouldIgnoreHiddenDirectory(DirectoryInfo directoryInfo)
339357

340358
try
341359
{
342-
if (ShouldIgnoreHiddenFile(fileInfo))
360+
var isRoot = IsRootPath(inventoryPart, fileInfo);
361+
362+
if (!isRoot && ShouldIgnoreHiddenFile(fileInfo))
343363
{
344364
return;
345365
}
346366

347-
if (ShouldIgnoreSystemFile(fileInfo))
367+
if (!isRoot && ShouldIgnoreSystemFile(fileInfo))
348368
{
349369
return;
350370
}
@@ -397,7 +417,7 @@ private void DoAnalyze(InventoryPart inventoryPart, DirectoryInfo directoryInfo,
397417
return;
398418
}
399419

400-
if (ShouldIgnoreHiddenDirectory(directoryInfo))
420+
if (!IsRootPath(inventoryPart, directoryInfo) && ShouldIgnoreHiddenDirectory(directoryInfo))
401421
{
402422
return;
403423
}

tests/ByteSync.Client.IntegrationTests/Services/Inventories/TestInventoryBuilder.cs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,93 @@ public async Task Test_HiddenFiles_Windows(bool excludeHiddenFiles, int expected
343343
inventory.InventoryParts[0].FileDescriptions.Count.Should().Be(2 + expectedHiddenFiles);
344344
}
345345

346+
[Test]
347+
[Platform(Exclude = "Linux")]
348+
public async Task Test_HiddenRootDirectory_Windows()
349+
{
350+
InventoryBuilder inventoryBuilder;
351+
Inventory inventory;
352+
353+
DirectoryInfo sourceA, unzipDir;
354+
FileInfo fileInfo;
355+
356+
sourceA = new DirectoryInfo(IOUtils.Combine(_testDirectoryService.TestDirectory.FullName, "sourceA"));
357+
sourceA.Create();
358+
var rootAttributes = File.GetAttributes(sourceA.FullName);
359+
File.SetAttributes(sourceA.FullName, rootAttributes | FileAttributes.Hidden);
360+
361+
fileInfo = new FileInfo(_testDirectoryService.CreateFileInDirectory(sourceA.FullName, "fileA.txt", "file1Content").FullName);
362+
fileInfo = new FileInfo(_testDirectoryService.CreateFileInDirectory(sourceA.FullName, "fileA_hidden.txt", "file1Content").FullName);
363+
File.SetAttributes(fileInfo.FullName, FileAttributes.Hidden);
364+
365+
var inventoryAFilePath = IOUtils.Combine(_testDirectoryService.TestDirectory.FullName, $"inventoryA.zip");
366+
367+
var sessionSettings = SessionSettings.BuildDefault();
368+
sessionSettings.ExcludeHiddenFiles = true;
369+
370+
inventoryBuilder = BuildInventoryBuilder(sessionSettings);
371+
inventoryBuilder.AddInventoryPart(sourceA.FullName);
372+
await inventoryBuilder.BuildBaseInventoryAsync(inventoryAFilePath);
373+
374+
File.Exists(inventoryAFilePath).Should().BeTrue();
375+
376+
unzipDir = new DirectoryInfo(IOUtils.Combine(_testDirectoryService.TestDirectory.FullName, "unzip"));
377+
unzipDir.Create();
378+
379+
var fastZip = new FastZip();
380+
fastZip.ExtractZip(inventoryAFilePath, unzipDir.FullName, null);
381+
382+
unzipDir.GetFiles("*", SearchOption.AllDirectories).Length.Should().Be(1);
383+
File.Exists(IOUtils.Combine(unzipDir.FullName, $"inventory.json")).Should().BeTrue();
384+
385+
inventory = inventoryBuilder.Inventory!;
386+
inventory.InventoryParts.Count.Should().Be(1);
387+
inventory.InventoryParts[0].FileDescriptions.Count.Should().Be(1);
388+
inventory.InventoryParts[0].FileDescriptions[0].Name.Should().Be("fileA.txt");
389+
}
390+
391+
[Test]
392+
[Platform(Include = "Linux,MacOsX")]
393+
public async Task Test_HiddenRootDirectory_Linux_Mac()
394+
{
395+
InventoryBuilder inventoryBuilder;
396+
Inventory inventory;
397+
398+
DirectoryInfo sourceA, unzipDir;
399+
FileInfo fileInfo;
400+
401+
sourceA = new DirectoryInfo(IOUtils.Combine(_testDirectoryService.TestDirectory.FullName, ".sourceA"));
402+
sourceA.Create();
403+
404+
fileInfo = new FileInfo(_testDirectoryService.CreateFileInDirectory(sourceA.FullName, "fileA.txt", "file1Content").FullName);
405+
fileInfo = new FileInfo(_testDirectoryService.CreateFileInDirectory(sourceA.FullName, ".fileA_hidden.txt", "file1Content").FullName);
406+
407+
var inventoryAFilePath = IOUtils.Combine(_testDirectoryService.TestDirectory.FullName, $"inventoryA.zip");
408+
409+
var sessionSettings = SessionSettings.BuildDefault();
410+
sessionSettings.ExcludeHiddenFiles = true;
411+
412+
inventoryBuilder = BuildInventoryBuilder(sessionSettings);
413+
inventoryBuilder.AddInventoryPart(sourceA.FullName);
414+
await inventoryBuilder.BuildBaseInventoryAsync(inventoryAFilePath);
415+
416+
File.Exists(inventoryAFilePath).Should().BeTrue();
417+
418+
unzipDir = new DirectoryInfo(IOUtils.Combine(_testDirectoryService.TestDirectory.FullName, "unzip"));
419+
unzipDir.Create();
420+
421+
var fastZip = new FastZip();
422+
fastZip.ExtractZip(inventoryAFilePath, unzipDir.FullName, null);
423+
424+
unzipDir.GetFiles("*", SearchOption.AllDirectories).Length.Should().Be(1);
425+
File.Exists(IOUtils.Combine(unzipDir.FullName, $"inventory.json")).Should().BeTrue();
426+
427+
inventory = inventoryBuilder.Inventory!;
428+
inventory.InventoryParts.Count.Should().Be(1);
429+
inventory.InventoryParts[0].FileDescriptions.Count.Should().Be(1);
430+
inventory.InventoryParts[0].FileDescriptions[0].Name.Should().Be("fileA.txt");
431+
}
432+
346433
[Test]
347434
[Platform(Exclude = "Win")]
348435
[TestCase(true, 0)]
@@ -786,4 +873,4 @@ private InventoryBuilder BuildInventoryBuilder(SessionSettings? sessionSettings
786873
saver,
787874
new InventoryIndexer());
788875
}
789-
}
876+
}

tests/ByteSync.Client.UnitTests/Services/Inventories/InventoryBuilderInspectorTests.cs

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,15 @@ private InventoryBuilder CreateBuilder(IFileSystemInspector inspector)
7575
}
7676

7777
[Test]
78-
public async Task Hidden_File_Is_Ignored()
78+
public async Task Hidden_Root_File_Is_Analyzed()
7979
{
8080
var insp = new Mock<IFileSystemInspector>(MockBehavior.Strict);
8181
insp.Setup(i => i.IsHidden(It.IsAny<FileSystemInfo>(), It.IsAny<OSPlatforms>())).Returns(true);
82+
insp.Setup(i => i.IsSystem(It.IsAny<FileInfo>())).Returns(false);
83+
insp.Setup(i => i.IsReparsePoint(It.IsAny<FileSystemInfo>())).Returns(false);
84+
insp.Setup(i => i.Exists(It.IsAny<FileInfo>())).Returns(true);
85+
insp.Setup(i => i.IsOffline(It.IsAny<FileInfo>())).Returns(false);
86+
insp.Setup(i => i.IsRecallOnDataAccess(It.IsAny<FileInfo>())).Returns(false);
8287
var builder = CreateBuilder(insp.Object);
8388

8489
var filePath = Path.Combine(TestDirectory.FullName, "a.txt");
@@ -89,15 +94,19 @@ public async Task Hidden_File_Is_Ignored()
8994
await builder.BuildBaseInventoryAsync(invPath);
9095

9196
var part = builder.Inventory.InventoryParts.Single();
92-
part.FileDescriptions.Should().BeEmpty();
97+
part.FileDescriptions.Should().ContainSingle();
9398
}
9499

95100
[Test]
96-
public async Task System_File_Is_Ignored()
101+
public async Task System_Root_File_Is_Analyzed()
97102
{
98103
var insp = new Mock<IFileSystemInspector>(MockBehavior.Strict);
99104
insp.Setup(i => i.IsHidden(It.IsAny<FileSystemInfo>(), It.IsAny<OSPlatforms>())).Returns(false);
100105
insp.Setup(i => i.IsSystem(It.IsAny<FileInfo>())).Returns(true);
106+
insp.Setup(i => i.IsReparsePoint(It.IsAny<FileSystemInfo>())).Returns(false);
107+
insp.Setup(i => i.Exists(It.IsAny<FileInfo>())).Returns(true);
108+
insp.Setup(i => i.IsOffline(It.IsAny<FileInfo>())).Returns(false);
109+
insp.Setup(i => i.IsRecallOnDataAccess(It.IsAny<FileInfo>())).Returns(false);
101110
var builder = CreateBuilder(insp.Object);
102111

103112
var filePath = Path.Combine(TestDirectory.FullName, "b.txt");
@@ -108,7 +117,91 @@ public async Task System_File_Is_Ignored()
108117
await builder.BuildBaseInventoryAsync(invPath);
109118

110119
var part = builder.Inventory.InventoryParts.Single();
111-
part.FileDescriptions.Should().BeEmpty();
120+
part.FileDescriptions.Should().ContainSingle();
121+
}
122+
123+
[Test]
124+
public async Task Hidden_Root_Directory_Is_Analyzed()
125+
{
126+
var insp = new Mock<IFileSystemInspector>(MockBehavior.Strict);
127+
insp.Setup(i => i.IsHidden(It.IsAny<DirectoryInfo>(), It.IsAny<OSPlatforms>())).Returns(true);
128+
insp.Setup(i => i.IsHidden(It.IsAny<FileInfo>(), It.IsAny<OSPlatforms>())).Returns(false);
129+
insp.Setup(i => i.IsSystem(It.IsAny<FileInfo>())).Returns(false);
130+
insp.Setup(i => i.IsReparsePoint(It.IsAny<FileSystemInfo>())).Returns(false);
131+
insp.Setup(i => i.Exists(It.IsAny<FileInfo>())).Returns(true);
132+
insp.Setup(i => i.IsOffline(It.IsAny<FileInfo>())).Returns(false);
133+
insp.Setup(i => i.IsRecallOnDataAccess(It.IsAny<FileInfo>())).Returns(false);
134+
var builder = CreateBuilder(insp.Object);
135+
136+
var root = Directory.CreateDirectory(Path.Combine(TestDirectory.FullName, "root_hidden"));
137+
var filePath = Path.Combine(root.FullName, "f.txt");
138+
await File.WriteAllTextAsync(filePath, "x");
139+
140+
builder.AddInventoryPart(root.FullName);
141+
var invPath = Path.Combine(TestDirectory.FullName, "inv_hidden_root.zip");
142+
await builder.BuildBaseInventoryAsync(invPath);
143+
144+
var part = builder.Inventory.InventoryParts.Single();
145+
part.FileDescriptions.Should().ContainSingle();
146+
part.FileDescriptions[0].RelativePath.Should().Be("/f.txt");
147+
}
148+
149+
[Test]
150+
public async Task Hidden_Child_File_Is_Ignored()
151+
{
152+
var insp = new Mock<IFileSystemInspector>(MockBehavior.Strict);
153+
insp.Setup(i => i.IsHidden(It.IsAny<DirectoryInfo>(), It.IsAny<OSPlatforms>())).Returns(false);
154+
insp.Setup(i => i.IsHidden(It.Is<FileInfo>(fi => fi.Name == "hidden.txt"), It.IsAny<OSPlatforms>()))
155+
.Returns(true);
156+
insp.Setup(i => i.IsHidden(It.Is<FileInfo>(fi => fi.Name != "hidden.txt"), It.IsAny<OSPlatforms>()))
157+
.Returns(false);
158+
insp.Setup(i => i.IsSystem(It.IsAny<FileInfo>())).Returns(false);
159+
insp.Setup(i => i.IsReparsePoint(It.IsAny<FileSystemInfo>())).Returns(false);
160+
insp.Setup(i => i.Exists(It.IsAny<FileInfo>())).Returns(true);
161+
insp.Setup(i => i.IsOffline(It.IsAny<FileInfo>())).Returns(false);
162+
insp.Setup(i => i.IsRecallOnDataAccess(It.IsAny<FileInfo>())).Returns(false);
163+
var builder = CreateBuilder(insp.Object);
164+
165+
var root = Directory.CreateDirectory(Path.Combine(TestDirectory.FullName, "root_hidden_child"));
166+
var visiblePath = Path.Combine(root.FullName, "visible.txt");
167+
var hiddenPath = Path.Combine(root.FullName, "hidden.txt");
168+
await File.WriteAllTextAsync(visiblePath, "x");
169+
await File.WriteAllTextAsync(hiddenPath, "x");
170+
171+
builder.AddInventoryPart(root.FullName);
172+
var invPath = Path.Combine(TestDirectory.FullName, "inv_hidden_child.zip");
173+
await builder.BuildBaseInventoryAsync(invPath);
174+
175+
var part = builder.Inventory.InventoryParts.Single();
176+
part.FileDescriptions.Should().ContainSingle(fd => fd.Name == "visible.txt");
177+
}
178+
179+
[Test]
180+
public async Task System_Child_File_Is_Ignored()
181+
{
182+
var insp = new Mock<IFileSystemInspector>(MockBehavior.Strict);
183+
insp.Setup(i => i.IsHidden(It.IsAny<DirectoryInfo>(), It.IsAny<OSPlatforms>())).Returns(false);
184+
insp.Setup(i => i.IsHidden(It.IsAny<FileInfo>(), It.IsAny<OSPlatforms>())).Returns(false);
185+
insp.Setup(i => i.IsSystem(It.Is<FileInfo>(fi => fi.Name == "system.txt"))).Returns(true);
186+
insp.Setup(i => i.IsSystem(It.Is<FileInfo>(fi => fi.Name != "system.txt"))).Returns(false);
187+
insp.Setup(i => i.IsReparsePoint(It.IsAny<FileSystemInfo>())).Returns(false);
188+
insp.Setup(i => i.Exists(It.IsAny<FileInfo>())).Returns(true);
189+
insp.Setup(i => i.IsOffline(It.IsAny<FileInfo>())).Returns(false);
190+
insp.Setup(i => i.IsRecallOnDataAccess(It.IsAny<FileInfo>())).Returns(false);
191+
var builder = CreateBuilder(insp.Object);
192+
193+
var root = Directory.CreateDirectory(Path.Combine(TestDirectory.FullName, "root_system_child"));
194+
var visiblePath = Path.Combine(root.FullName, "visible.txt");
195+
var systemPath = Path.Combine(root.FullName, "system.txt");
196+
await File.WriteAllTextAsync(visiblePath, "x");
197+
await File.WriteAllTextAsync(systemPath, "x");
198+
199+
builder.AddInventoryPart(root.FullName);
200+
var invPath = Path.Combine(TestDirectory.FullName, "inv_system_child.zip");
201+
await builder.BuildBaseInventoryAsync(invPath);
202+
203+
var part = builder.Inventory.InventoryParts.Single();
204+
part.FileDescriptions.Should().ContainSingle(fd => fd.Name == "visible.txt");
112205
}
113206

114207
[Test]
@@ -320,4 +413,4 @@ public async Task Directory_ReparsePoint_Is_Skipped()
320413
part.FileDescriptions.Should().ContainSingle();
321414
part.FileDescriptions[0].RelativePath.Should().Be("/ok.txt");
322415
}
323-
}
416+
}

0 commit comments

Comments
 (0)