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
8 changes: 7 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ Use the following shell command:
```console
$ dotnet test
```
Tests involving symlink creation require elevated privileges on Windows, as this is enforced at the OS level.

On Windows, tests that create symbolic links require either [Developer Mode][windows-developer-mode] or an elevated
process. Developer Mode lets an unelevated process create symbolic links, as documented for the
[`CreateSymbolicLinkW`][create-symbolic-link] API. When neither is available, these tests are skipped locally; CI
runners fail instead, ensuring that the tests are not silently skipped in automation.

Test Documentation
------------------
Expand Down Expand Up @@ -73,6 +77,8 @@ Then run the following shell command:
$ dotnet fsi scripts/github-actions.fsx
```

[create-symbolic-link]: https://learn.microsoft.com/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
[dotnet-sdk]: https://dotnet.microsoft.com/en-us/download
[powershell]: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell
[reuse]: https://reuse.software/
[windows-developer-mode]: https://learn.microsoft.com/windows/apps/get-started/enable-your-device-for-development
13 changes: 1 addition & 12 deletions TruePath.Tests/AbsolutePathTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,18 +135,7 @@ public void ReadKind_IsSymlink()

var tempDirectoryInfo = Path.GetTempPath();

try
{
Directory.CreateSymbolicLink(currentDirectory, tempDirectoryInfo);
}
catch (IOException e) when (OperatingSystem.IsWindows() && !Utils.RunsOnCi())
{
if (e.Message.Contains("privilege") && (e.StackTrace?.Contains("CreateSymbolicLink") ?? false))
{
Assert.Skip("Tests involving symlink creation require elevated privileges on Windows, as this is enforced at the OS level.");
}
else throw;
}
Utils.CreateSymbolicLinkOrSkip(() => Directory.CreateSymbolicLink(currentDirectory, tempDirectoryInfo));

// Act
var kind = localPath.ReadKind();
Expand Down
20 changes: 20 additions & 0 deletions TruePath.Tests/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
//
// SPDX-License-Identifier: MIT

using Windows.Win32;
using Windows.Win32.Foundation;
using Xunit.v3;

namespace TruePath.Tests;
Expand Down Expand Up @@ -44,6 +46,24 @@ internal static bool RunsOnCi()
return Environment.GetEnvironmentVariable("CI") is not null;
}

internal static void CreateSymbolicLinkOrSkip(Action createSymbolicLink)
{
try
{
createSymbolicLink();
}
catch (IOException e) when (OperatingSystem.IsWindows()
&& e.HResult == PInvoke.HRESULT_FROM_WIN32(WIN32_ERROR.ERROR_PRIVILEGE_NOT_HELD).Value)
{
Assert.False(
RunsOnCi(),
"CI runners must allow symbolic link creation so these tests are not skipped silently.");
Assert.Skip(
"Creating symbolic links on Windows requires Developer Mode or an elevated process. " +
"See CONTRIBUTING.md for setup instructions.");
}
}

/// <summary>
/// Changes the current directory to <paramref name="newDirectory"/>, and restores the previous one on disposal.
/// </summary>
Expand Down
2 changes: 2 additions & 0 deletions TruePath/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ FILE_ACCESS_RIGHTS
FILE_ATTRIBUTE_TAG_INFO
GetFileInformationByHandleEx
GetFinalPathNameByHandle
HRESULT_FROM_WIN32
IO_REPARSE_TAG_MOUNT_POINT
WIN32_ERROR
Loading