Skip to content

Commit e2eefbc

Browse files
timcassellclaude
andauthored
Fix the inverted reparse point filter in the netstandard2.0 cleanup path (#3255)
FileCleanupHelper's netstandard2.0 enumeration kept only the entries whose ReparsePoint attribute was set, the opposite of the AttributesToSkip the other branch passes. On a .NET Framework host that left every regular file in place, so the recursive Delete then threw into TryExecute's empty catch and the work directory survived - the cleanup #3175 added never ran there. A stale directory is not inert: the generated project imports files it finds beside itself, so leftovers from an earlier run can be picked up by the next one. The tests cover both the file and the directory entry points; the two directory cases fail on net472 without this fix. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 36a5a06 commit e2eefbc

2 files changed

Lines changed: 74 additions & 2 deletions

File tree

‎src/BenchmarkDotNet/Helpers/FileCleanupHelper.cs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ private static IEnumerable<FileInfo> EnumerateFiles(DirectoryInfo directory)
6161
try
6262
{
6363
return directory.EnumerateFiles("*", SearchOption.TopDirectoryOnly)
64-
.Where(x => (x.Attributes & FileAttributes.ReparsePoint) != 0);
64+
.Where(x => (x.Attributes & FileAttributes.ReparsePoint) == 0);
6565
}
6666
catch
6767
{
@@ -75,7 +75,7 @@ private static IEnumerable<DirectoryInfo> EnumerateSubDirectories(DirectoryInfo
7575
try
7676
{
7777
return directory.EnumerateDirectories()
78-
.Where(x => (x.Attributes & FileAttributes.ReparsePoint) != 0);
78+
.Where(x => (x.Attributes & FileAttributes.ReparsePoint) == 0);
7979
}
8080
catch
8181
{
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using AwesomeAssertions;
2+
using BenchmarkDotNet.Helpers;
3+
4+
namespace BenchmarkDotNet.Tests.Helpers;
5+
6+
public class FileCleanupHelperTests : IDisposable
7+
{
8+
private readonly string rootPath = Path.Combine(Path.GetTempPath(), $"BDN.FileCleanupHelperTests.{Guid.NewGuid():N}");
9+
10+
public void Dispose()
11+
{
12+
if (!Directory.Exists(rootPath))
13+
return;
14+
15+
foreach (var file in Directory.EnumerateFiles(rootPath, "*", SearchOption.AllDirectories))
16+
File.SetAttributes(file, FileAttributes.Normal);
17+
18+
Directory.Delete(rootPath, recursive: true);
19+
}
20+
21+
[Fact]
22+
public void CleanupRemovesFile()
23+
{
24+
var filePath = CreateFile(rootPath, "program.notcs");
25+
26+
FileCleanupHelper.Cleanup(filePath);
27+
28+
File.Exists(filePath).Should().BeFalse();
29+
}
30+
31+
[Fact]
32+
public void CleanupRemovesReadOnlyFile()
33+
{
34+
var filePath = CreateFile(rootPath, "readonly.txt");
35+
File.SetAttributes(filePath, FileAttributes.ReadOnly);
36+
37+
FileCleanupHelper.Cleanup(filePath);
38+
39+
File.Exists(filePath).Should().BeFalse();
40+
}
41+
42+
[Fact]
43+
public void CleanupRemovesDirectoryWithItsContents()
44+
{
45+
CreateFile(rootPath, "BenchmarkDotNet.Autogenerated.csproj");
46+
CreateFile(Path.Combine(rootPath, "obj"), "project.assets.json");
47+
CreateFile(Path.Combine(rootPath, "obj", "release_net10.0"), "program.dll");
48+
49+
FileCleanupHelper.Cleanup(rootPath);
50+
51+
Directory.Exists(rootPath).Should().BeFalse();
52+
}
53+
54+
[Fact]
55+
public void CleanupRemovesDirectoryContainingReadOnlyFile()
56+
{
57+
File.SetAttributes(CreateFile(rootPath, "readonly.txt"), FileAttributes.ReadOnly);
58+
59+
FileCleanupHelper.Cleanup(rootPath);
60+
61+
Directory.Exists(rootPath).Should().BeFalse();
62+
}
63+
64+
private static string CreateFile(string directoryPath, string fileName)
65+
{
66+
Directory.CreateDirectory(directoryPath);
67+
68+
var filePath = Path.Combine(directoryPath, fileName);
69+
File.WriteAllText(filePath, "content");
70+
return filePath;
71+
}
72+
}

0 commit comments

Comments
 (0)