Repository: dotnet/runtime
File: src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete.Windows.cs (C#)

Code before:
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text;
using Xunit;
using Microsoft.DotNet.XUnitExtensions;
using System.Security.Principal;
using System.Security.AccessControl;

namespace System.IO.Tests
{
    public partial class Directory_Delete_str_bool : Directory_Delete_str
    {
        [Fact]
        [PlatformSpecific(TestPlatforms.Windows)]
        public void RecursiveDelete_DirectoryContainingJunction()
        {
            // Junctions (NTFS directory junctions) share the IO_REPARSE_TAG_MOUNT_POINT reparse
            // tag with volume mount points, but DeleteVolumeMountPoint only works for volume mount
            // points. Ensure that recursive delete succeeds when the directory contains a junction.
            string target = GetTestFilePath();
            Directory.CreateDirectory(target);

            string linkParent = GetTestFilePath();
            Directory.CreateDirectory(linkParent);

            string junctionPath = Path.Combine(linkParent, GetTestFileName());
            Assert.True(MountHelper.CreateJunction(junctionPath, target));

            // Both the junction and the target exist before deletion
            Assert.True(Directory.Exists(junctionPath), "junction should exist before delete");
            Assert.True(Directory.Exists(target), "target should exist before delete");

            // Recursive delete of the parent should succeed and remove the junction without following it
            Delete(linkParent, recursive: true);

            Assert.False(Directory.Exists(linkParent), "parent should be deleted");
            Assert.True(Directory.Exists(target), "target should still exist after deleting junction");
        }

        [Fact]
        [PlatformSpecific(TestPlatforms.Windows)]
        public void RecursiveDelete_NoListDirectoryPermission() // https://github.com/dotnet/runtime/issues/56922
        {
            using WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent();

            string parentPath = GetTestFilePath();
            var parent = Directory.CreateDirectory(parentPath);
            var ac = parent.GetAccessControl();
            var rule = new FileSystemAccessRule(currentIdentity.User, FileSystemRights.ListDirectory, AccessControlType.Deny);
            ac.SetAccessRule(rule);
            parent.SetAccessControl(ac);

            var subDir = parent.CreateSubdirectory("subdir");
            File.Create(Path.Combine(subDir.FullName, GetTestFileName())).Dispose();
            Delete(subDir.FullName, recursive: true);
            Assert.False(subDir.Exists);

            // Cleanup
            ac.RemoveAccessRule(rule);
            parent.SetAccessControl(ac);
        }
    }
}


Review comment (suggestion):
Can we include tests for the following:
* A junction specified as the directory (as root) for `Delete`.
* Assert that if the target contains elements, they are not removed (assuming that's the expected).
* A volume mount point specified as the directory (as root) for `Delete` - not related to this PR but I see those needing 2 operations, DeleteVolumeMountPoint + RemoveDirectory when they are discovered as part of the enumeration, but when they are the root, we only call [RemoveDirectory](https://github.com/dotnet/runtime/blob/4d14f78d3f3b4e1e77da06577d75e3fd5fe3b77e/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs#L263-L268).

Code after:
            Assert.True(Directory.Exists(target), "target should still exist after deleting junction");
        }

        [ConditionalFact(nameof(IsPrivilegedAndNtfs))]
        [PlatformSpecific(TestPlatforms.Windows)]
        public void Delete_VolumeMountPoint()
        {
            string mountPoint = GetTestFilePath();
            Directory.CreateDirectory(mountPoint);
            MountHelper.Mount(Directory.GetCurrentDirectory().Substring(0, 2), mountPoint);
            try
            {
                // Deleting the volume mount point directly (as root) should succeed
                Delete(mountPoint);

                Assert.False(Directory.Exists(mountPoint), "mount point should be deleted");
            }
            finally
            {
                if (Directory.Exists(mountPoint))
                {
                    MountHelper.Unmount(mountPoint);
                    Directory.Delete(mountPoint);
                }
            }
        }

        [Fact]
        [PlatformSpecific(TestPlatforms.Windows)]
        public void RecursiveDelete_NoListDirectoryPermission() // https://github.com/dotnet/runtime/issues/56922
        {
            using WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent();

            string parentPath = GetTestFilePath();
            var parent = Directory.CreateDirectory(parentPath);
            var ac = parent.GetAccessControl();
            var rule = new FileSystemAccessRule(currentIdentity.User, FileSystemRights.ListDirectory, AccessControlType.Deny);
            ac.SetAccessRule(rule);
            parent.SetAccessControl(ac);

            var subDir = parent.CreateSubdirectory("subdir");
            File.Create(Path.Combine(subDir.FullName, GetTestFileName())).Dispose();
            Delete(subDir.FullName, recursive: true);
            Assert.False(subDir.Exists);

            // Cleanup
            ac.RemoveAccessRule(rule);
            parent.SetAccessControl(ac);
        }
    }
}