-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ability to create temp mapped drive for integration tests #8366
Merged
JaynieBai
merged 9 commits into
dotnet:main
from
JanKrivanek:proto/path-enumeration-tests
Feb 8, 2023
Merged
Changes from 7 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
9d62041
Add ability to create temp mapped drive for integration tests
JanKrivanek e498cc2
Fix analyzer findings
JanKrivanek 908b674
Remove the unix warning
JanKrivanek f782273
Guard platform specific calls
JanKrivanek f6f1998
Failing unix testcase
JanKrivanek 52f1ef3
Style checks
JanKrivanek 334dece
Merge branch 'main' into proto/path-enumeration-tests
JanKrivanek bc7179b
Fix review comments
JanKrivanek 0fbd8de
Use drive placeholder
JanKrivanek File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
#nullable enable | ||
using System; | ||
using System.Runtime.InteropServices; | ||
using System.Runtime.Versioning; | ||
using System.Text; | ||
|
||
namespace Microsoft.Build.UnitTests.Shared; | ||
|
||
internal static class DriveMapping | ||
{ | ||
private const int ERROR_FILE_NOT_FOUND = 2; | ||
// private const int ERROR_INSUFFICIENT_BUFFER = 122; | ||
private const int DDD_REMOVE_DEFINITION = 2; | ||
private const int DDD_NO_FLAG = 0; | ||
// extra space for '\??\'. Not counting for long paths support in tests. | ||
private const int MAX_PATH = 259; | ||
|
||
/// <summary> | ||
/// Windows specific. Maps path to a requested drive. | ||
/// </summary> | ||
/// <param name="letter">Drive letter</param> | ||
/// <param name="path">Path to be mapped</param> | ||
[SupportedOSPlatform("windows")] | ||
public static void MapDrive(char letter, string path) | ||
JanKrivanek marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
if (!DefineDosDevice(DDD_NO_FLAG, ToDeviceName(letter), path)) | ||
{ | ||
NativeMethodsShared.ThrowExceptionForErrorCode(Marshal.GetLastWin32Error()); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Windows specific. Unmaps drive mapping. | ||
/// </summary> | ||
/// <param name="letter">Drive letter.</param> | ||
[SupportedOSPlatform("windows")] | ||
public static void UnmapDrive(char letter) | ||
{ | ||
if (!DefineDosDevice(DDD_REMOVE_DEFINITION, ToDeviceName(letter), null)) | ||
{ | ||
NativeMethodsShared.ThrowExceptionForErrorCode(Marshal.GetLastWin32Error()); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Windows specific. Fetches path mapped under specific drive letter. | ||
/// </summary> | ||
/// <param name="letter">Drive letter.</param> | ||
/// <returns>Path mapped under specified letter. Empty string if mapping not found.</returns> | ||
[SupportedOSPlatform("windows")] | ||
public static string GetDriveMapping(char letter) | ||
{ | ||
// since this is just for test purposes - let's not overcomplicate with long paths support | ||
JanKrivanek marked this conversation as resolved.
Show resolved
Hide resolved
|
||
char[] buffer = new char[MAX_PATH]; | ||
if (QueryDosDevice(ToDeviceName(letter), buffer, buffer.Length) == 0) | ||
{ | ||
// Return empty string if the drive is not mapped | ||
int err = Marshal.GetLastWin32Error(); | ||
if (err == ERROR_FILE_NOT_FOUND) | ||
{ | ||
return string.Empty; | ||
} | ||
NativeMethodsShared.ThrowExceptionForErrorCode(err); | ||
} | ||
// Translate from the native path semantic - starting with '\??\' | ||
return new string(buffer, 4, buffer.Length - 4); | ||
} | ||
|
||
private static string ToDeviceName(char letter) | ||
{ | ||
return new string(char.ToUpper(letter), 1) + ":"; | ||
JanKrivanek marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] | ||
[SupportedOSPlatform("windows")] | ||
private static extern bool DefineDosDevice([In] int flags, [In] string deviceName, [In] string? path); | ||
|
||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] | ||
[SupportedOSPlatform("windows")] | ||
private static extern int QueryDosDevice([In] string deviceName, [Out] char[] buffer, [In] int bufSize); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
#nullable enable | ||
using System; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Runtime.Versioning; | ||
|
||
namespace Microsoft.Build.UnitTests.Shared; | ||
|
||
/// <summary> | ||
/// Windows specific. Class managing system resource - temporary local path mapped to available drive letter. | ||
/// </summary> | ||
public class DummyMappedDrive : IDisposable | ||
{ | ||
public char MappedDriveLetter { get; init; } = 'z'; | ||
private readonly string _mappedPath; | ||
private readonly bool _mapped; | ||
|
||
public DummyMappedDrive() | ||
{ | ||
_mappedPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); | ||
|
||
if (!NativeMethodsShared.IsWindows) | ||
{ | ||
return; | ||
} | ||
|
||
Directory.CreateDirectory(_mappedPath); | ||
File.Create(Path.Combine(_mappedPath, "x")).Dispose(); | ||
|
||
for (char driveLetter = 'z'; driveLetter >= 'a'; driveLetter--) | ||
{ | ||
if (DriveMapping.GetDriveMapping(driveLetter) == string.Empty) | ||
{ | ||
DriveMapping.MapDrive(driveLetter, _mappedPath); | ||
MappedDriveLetter = driveLetter; | ||
_mapped = true; | ||
return; | ||
} | ||
} | ||
} | ||
|
||
private void ReleaseUnmanagedResources(bool disposing) | ||
{ | ||
Exception? e = null; | ||
if (Directory.Exists(_mappedPath)) | ||
{ | ||
try | ||
{ | ||
Directory.Delete(_mappedPath, true); | ||
JanKrivanek marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
catch (Exception exc) | ||
{ | ||
e = exc; | ||
Debug.Fail("Exception in DummyMappedDrive finalizer: " + e.ToString()); | ||
} | ||
} | ||
|
||
if (_mapped && NativeMethodsShared.IsWindows) | ||
{ | ||
try | ||
{ | ||
DriveMapping.UnmapDrive(MappedDriveLetter); | ||
} | ||
catch (Exception exc) | ||
{ | ||
e = e == null ? exc : new AggregateException(e, exc); | ||
Debug.Fail("Exception in DummyMappedDrive finalizer: " + e.ToString()); | ||
} | ||
} | ||
|
||
if (disposing && e != null) | ||
{ | ||
throw e; | ||
} | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
ReleaseUnmanagedResources(true); | ||
GC.SuppressFinalize(this); | ||
} | ||
|
||
~DummyMappedDrive() => ReleaseUnmanagedResources(false); | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If path is empty, should we return "" or driveLetter + ":"? I'm curious if this could artificially make the drive enumeration tests pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Empty paths are valid test case scenarios (e.g. unspecified exclude pattern). So it intentionaly leaves unspecified or unrooted paths unaffected