Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ public abstract class BaseGetSetTimes<T> : FileSystemTest
public delegate void SetTime(T item, DateTime time);
public delegate DateTime GetTime(T item);
// AppContainer restricts access to DriveFormat (::GetVolumeInformation)
private static string driveFormat = PlatformDetection.IsInAppContainer ? string.Empty : new DriveInfo(Path.GetTempPath()).DriveFormat;
private static string driveFormat = TempDriveFormat;

private static bool isHFS => driveFormat != null && driveFormat.Equals("hfs", StringComparison.InvariantCultureIgnoreCase);
private static bool isFat32 => IsTempPathOnFat32;
private static int TemporalResolutionSafeSecond => isFat32 ? 4 : 3;

protected static bool SecondTemporalResolution => true;
protected static bool MilliSecondTemporalResolution => SecondTemporalResolution && !isHFS; // HFS only supports temporal resolution of 1 second
protected static bool MilliSecondTemporalResolution => SecondTemporalResolution && !isHFS && !isFat32; // HFS and FAT32 have low temporal resolution
protected static bool NanoSecondTemporalResolution => MilliSecondTemporalResolution && !PlatformDetection.IsBrowser; // Browser does not support nanosecond resolution
protected static bool NotMilliSecondTemporalResolution => !MilliSecondTemporalResolution;
protected static bool NotNanoSecondTemporalResolution => !NanoSecondTemporalResolution;
Expand Down Expand Up @@ -73,7 +75,7 @@ private void SettingUpdatesPropertiesCore(T item, T? linkTarget = default)
bool isLink = linkTarget is not null;

// Checking that milliseconds are not dropped after setter.
DateTime dt = new DateTime(2014, 12, 1, 12, 3, 3, NotMilliSecondTemporalResolution ? 0 : 321, function.Kind);
DateTime dt = new DateTime(2014, 12, 1, 12, 3, TemporalResolutionSafeSecond, NotMilliSecondTemporalResolution ? 0 : 321, function.Kind);
function.Setter(item, dt);

T getTarget = !isLink || ApiTargetsLink ? item : linkTarget;
Expand Down Expand Up @@ -207,9 +209,9 @@ public void SettingUpdatesPropertiesAfterAnother()
bool reverse = functions.reverse;

// Checking that milliseconds are not dropped after setter.
DateTime dt1 = new DateTime(2002, 12, 1, 12, 3, 3, NotMilliSecondTemporalResolution ? 0 : 321, DateTimeKind.Utc);
DateTime dt2 = new DateTime(2001, 12, 1, 12, 3, 3, NotMilliSecondTemporalResolution ? 0 : 321, DateTimeKind.Utc);
DateTime dt3 = new DateTime(2000, 12, 1, 12, 3, 3, NotMilliSecondTemporalResolution ? 0 : 321, DateTimeKind.Utc);
DateTime dt1 = new DateTime(2002, 12, 1, 12, 3, TemporalResolutionSafeSecond, NotMilliSecondTemporalResolution ? 0 : 321, DateTimeKind.Utc);
DateTime dt2 = new DateTime(2001, 12, 1, 12, 3, TemporalResolutionSafeSecond, NotMilliSecondTemporalResolution ? 0 : 321, DateTimeKind.Utc);
DateTime dt3 = new DateTime(2000, 12, 1, 12, 3, TemporalResolutionSafeSecond, NotMilliSecondTemporalResolution ? 0 : 321, DateTimeKind.Utc);
if (reverse) //reverse the order of setting dates
{
(dt1, dt3) = (dt3, dt1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ private void AssertSettingInvalidAttributes(string path, FileAttributes attribut
Assert.Equal(FileAttributes.Normal, GetAttributes(path));
}

[Theory,
InlineData(":bar"),
InlineData(":bar:$DATA")]
[ConditionalTheory(typeof(FileSystemTest), nameof(FileSystemTest.SupportsAlternateDataStreams))]
[InlineData(":bar")]
[InlineData(":bar:$DATA")]
[PlatformSpecific(TestPlatforms.Windows)]
public void GettingAndSettingAttributes_AlternateDataStream_Windows(string streamName)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,18 @@ public void InvalidPath_Core()
{
case '/':
case '\\':
case ':':
Assert.Throws<DirectoryNotFoundException>(() => GetEntries(badPath));
break;
case ':':
if (SupportsAlternateDataStreams)
{
Assert.Throws<DirectoryNotFoundException>(() => GetEntries(badPath));
}
else
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert.Throws<IOException> matches only the exact IOException type, not derived types like DirectoryNotFoundException. That is fine here given the observed FAT32 behavior (a plain IOException "...syntax is incorrect"), but it makes the assertion sensitive to the exact runtime exception type. Since SupportsAlternateDataStreams is a temp-volume probe rather than a strict "is FAT32" check, a non-FAT32 Windows configuration that happens to lack ADS support would also take this branch and would fail if it threw DirectoryNotFoundException instead. Consider Assert.ThrowsAny<IOException> to be robust to derived types, or gate specifically on IsTempPathOnFat32 to mirror the failure mode described in the issue.

Assert.Throws<IOException>(() => GetEntries(badPath));
}
break;
case '\0':
Assert.Throws<ArgumentException>(() => GetEntries(badPath));
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,12 @@ public void CopyFileWithData(char[] data, bool readOnly)
AssertExtensions.Equal(data, readData);
}

// Ensure last write/access time on the new file is appropriate
Assert.InRange(File.GetLastWriteTimeUtc(testFileDest), lastWriteTime.AddSeconds(-1), lastWriteTime.AddSeconds(1));
// Ensure last write/access time on the new file is appropriate.
TimeSpan tolerance = IsTempPathOnFat32 ? TimeSpan.FromSeconds(3) : TimeSpan.FromSeconds(1);
Assert.InRange(
File.GetLastWriteTimeUtc(testFileDest),
lastWriteTime.Subtract(tolerance),
lastWriteTime.Add(tolerance));

Assert.Equal(readOnly, (File.GetAttributes(testFileDest) & FileAttributes.ReadOnly) != 0);
if (readOnly)
Expand Down Expand Up @@ -203,11 +207,11 @@ public void UnixInvalidWindowsPaths(string valid)
Assert.True(File.Exists(Path.Combine(TestDirectory, valid)));
}

[Theory,
InlineData("", ":bar"),
InlineData("", ":bar:$DATA"),
InlineData("::$DATA", ":bar"),
InlineData("::$DATA", ":bar:$DATA")]
[ConditionalTheory(typeof(FileSystemTest), nameof(FileSystemTest.SupportsAlternateDataStreams))]
[InlineData("", ":bar")]
[InlineData("", ":bar:$DATA")]
[InlineData("::$DATA", ":bar")]
[InlineData("::$DATA", ":bar:$DATA")]
[PlatformSpecific(TestPlatforms.Windows)]
public void WindowsAlternateDataStream(string defaultStream, string alternateStream)
{
Expand Down Expand Up @@ -347,11 +351,11 @@ public void OverwriteFalse()
}
}

[Theory,
InlineData("", ":bar"),
InlineData("", ":bar:$DATA"),
InlineData("::$DATA", ":bar"),
InlineData("::$DATA", ":bar:$DATA")]
[ConditionalTheory(typeof(FileSystemTest), nameof(FileSystemTest.SupportsAlternateDataStreams))]
[InlineData("", ":bar")]
[InlineData("", ":bar:$DATA")]
[InlineData("::$DATA", ":bar")]
[InlineData("::$DATA", ":bar:$DATA")]
[PlatformSpecific(TestPlatforms.Windows)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/83659", typeof(PlatformDetection), nameof(PlatformDetection.IsWindows), nameof(PlatformDetection.IsArm64Process))]
public void WindowsAlternateDataStreamOverwrite(string defaultStream, string alternateStream)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,10 +301,10 @@ public void UnixWhitespacePath(string path)
}
}

[Theory,
InlineData(":bar"),
InlineData(":bar:$DATA"),
InlineData("::$DATA")]
[ConditionalTheory(typeof(FileSystemTest), nameof(FileSystemTest.SupportsAlternateDataStreams))]
[InlineData(":bar")]
[InlineData(":bar:$DATA")]
[InlineData("::$DATA")]
[PlatformSpecific(TestPlatforms.Windows)]
public void WindowsAlternateDataStream(string streamName)
{
Expand All @@ -316,9 +316,9 @@ public void WindowsAlternateDataStream(string streamName)
}
}

[Theory,
InlineData(":bar"),
InlineData(":bar:$DATA")]
[ConditionalTheory(typeof(FileSystemTest), nameof(FileSystemTest.SupportsAlternateDataStreams))]
[InlineData(":bar")]
[InlineData(":bar:$DATA")]
[PlatformSpecific(TestPlatforms.Windows)]
public void WindowsAlternateDataStream_OnExisting(string streamName)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,9 @@ public void UnixDeleteReadOnlyFile()
Assert.False(testFile.Exists);
}

[Theory,
InlineData(":bar"),
InlineData(":bar:$DATA")]
[ConditionalTheory(typeof(FileSystemTest), nameof(FileSystemTest.SupportsAlternateDataStreams))]
[InlineData(":bar")]
[InlineData(":bar:$DATA")]
[PlatformSpecific(TestPlatforms.Windows)]
public void WindowsDeleteAlternateDataStream(string streamName)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ public void GetAttributes_MissingFile(char trailingChar)
}

// Getting only throws for File, not FileInfo
[Theory,
InlineData(":bar"),
InlineData(":bar:$DATA")]
[ConditionalTheory(typeof(FileSystemTest), nameof(FileSystemTest.SupportsAlternateDataStreams))]
[InlineData(":bar")]
[InlineData(":bar:$DATA")]
[PlatformSpecific(TestPlatforms.Windows)]
public void GetAttributes_MissingAlternateDataStream_Windows(string streamName)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ public abstract class File_GetSetTimes : StaticGetSetTimes
{
// OSX has the limitation of setting upto 2262-04-11T23:47:16 (long.Max) date.
// 32bit Unix has time_t up to ~ 2038.
protected static bool SupportsLongMaxDateTime => PlatformDetection.IsWindows || (!PlatformDetection.Is32BitProcess && !PlatformDetection.IsApplePlatform);
protected static bool SupportsLongMaxDateTime =>
!IsTempPathOnFat32 &&
(PlatformDetection.IsWindows || (!PlatformDetection.Is32BitProcess && !PlatformDetection.IsApplePlatform));

protected override bool CanBeReadOnly => true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,11 +325,11 @@ public void UnixWhitespacePath(string whitespace)

}

[Theory,
InlineData("", ":bar"),
InlineData("", ":bar:$DATA"),
InlineData("::$DATA", ":bar"),
InlineData("::$DATA", ":bar:$DATA")]
[ConditionalTheory(typeof(FileSystemTest), nameof(FileSystemTest.SupportsAlternateDataStreams))]
[InlineData("", ":bar")]
[InlineData("", ":bar:$DATA")]
[InlineData("::$DATA", ":bar")]
[InlineData("::$DATA", ":bar:$DATA")]
[PlatformSpecific(TestPlatforms.Windows)]
public void WindowsAlternateDataStreamMove(string defaultStream, string alternateStream)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.IO.Pipes;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Xunit;

Expand Down Expand Up @@ -41,7 +39,7 @@ public void ExtendedPathsAreSupported(string prefix)
}
}

[ConditionalTheory(typeof(FileStream_ctor_options), nameof(IsFat32))]
[ConditionalTheory(typeof(FileSystemTest), nameof(FileSystemTest.IsTempPathOnFat32))]
[InlineData(FileMode.Create)]
[InlineData(FileMode.CreateNew)]
public void WhenFileIsTooLargeTheErrorMessageContainsAllDetails(FileMode mode)
Expand All @@ -57,43 +55,5 @@ public void WhenFileIsTooLargeTheErrorMessageContainsAllDetails(FileMode mode)

Assert.False(File.Exists(filePath)); // ensure it was NOT created
}

public static bool IsFat32
{
get
{
string testDirectory = Path.GetTempPath(); // logic taken from FileCleanupTestBase, can't call the property here as it's not static

var volumeNameBuffer = new StringBuilder(250);
var fileSystemNameBuffer = new StringBuilder(250);

if (GetVolumeInformation(
Path.GetPathRoot(testDirectory),
volumeNameBuffer,
volumeNameBuffer.Capacity,
out uint _,
out uint _,
out uint _,
fileSystemNameBuffer,
fileSystemNameBuffer.Capacity
))
{
return fileSystemNameBuffer.ToString().Equals("FAT32", StringComparison.OrdinalIgnoreCase);
}

return false;
}
}

[DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool GetVolumeInformation(
string rootPathName,
StringBuilder volumeNameBuffer,
int volumeNameSize,
out uint volumeSerialNumber,
out uint maximumComponentLength,
out uint fileSystemFlags,
StringBuilder fileSystemNameBuffer,
int fileSystemNameSize);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public static TheoryData<string> StreamSpecifiers
TheoryData<string> data = new TheoryData<string>();
data.Add("");

if (PlatformDetection.IsWindows)
if (SupportsAlternateDataStreams)
{
data.Add("::$DATA"); // Same as default stream (e.g. main file)
data.Add(":bar"); // $DATA isn't necessary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void FileShareDeleteNew()
if (OperatingSystem.IsWindows())
{
// Prior to 1903 Windows would not delete the filename until the last file handle is closed.
Assert.Equal(PlatformDetection.IsWindows10Version1903OrGreater, !File.Exists(fileName));
Assert.Equal(DeletesOpenFileNameImmediately, !File.Exists(fileName));
}
}

Expand Down Expand Up @@ -58,7 +58,7 @@ public void FileShareDeleteExisting()
if (OperatingSystem.IsWindows())
{
// Prior to 1903 Windows would not delete the filename until the last file handle is closed.
Assert.Equal(PlatformDetection.IsWindows10Version1903OrGreater, !File.Exists(fileName));
Assert.Equal(DeletesOpenFileNameImmediately, !File.Exists(fileName));
}
}

Expand Down Expand Up @@ -106,13 +106,13 @@ public void FileShareDeleteExistingMultipleClients()
Assert.Equal(0, fs2.ReadByte());

// Prior to 1903 Windows would not delete the filename until the last file handle is closed.
Assert.Equal(PlatformDetection.IsWindows10Version1903OrGreater, !File.Exists(fileName));
Assert.Equal(DeletesOpenFileNameImmediately, !File.Exists(fileName));
}

Assert.Equal(0, fs1.ReadByte());
fs1.WriteByte(0xFF);

if (PlatformDetection.IsWindows10Version1903OrGreater)
if (DeletesOpenFileNameImmediately)
{
// On 1903 the filename is immediately released after delete is called
Assert.Throws<FileNotFoundException>(() => CreateFileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,51 @@ public abstract partial class FileSystemTest : FileCleanupTestBase

public static bool IsAsyncIoSupportedForRegularFiles => PlatformDetection.IsWindows;

private static readonly Lazy<string> s_tempDriveFormat = new Lazy<string>(() =>
PlatformDetection.IsInAppContainer ? string.Empty : new DriveInfo(Path.GetTempPath()).DriveFormat);

private static readonly Lazy<bool> s_supportsAlternateDataStreams =
new Lazy<bool>(GetSupportsAlternateDataStreams);

public static string TempDriveFormat => s_tempDriveFormat.Value;

public static bool IsTempPathOnFat32 =>
string.Equals(TempDriveFormat, "FAT32", StringComparison.OrdinalIgnoreCase);

public static bool TempPathSupportsLargeFiles => !IsTempPathOnFat32;

public static bool DeletesOpenFileNameImmediately =>
PlatformDetection.IsWindows10Version1903OrGreater && !IsTempPathOnFat32;

public static bool SupportsAlternateDataStreams => s_supportsAlternateDataStreams.Value;

private static bool GetSupportsAlternateDataStreams()
{
if (!PlatformDetection.IsWindows)
{
return false;
}

string path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
string streamPath = path + ":stream";

try
{
File.WriteAllText(path, string.Empty);
File.WriteAllText(streamPath, "stream");

return File.ReadAllText(streamPath) == "stream";
}
catch (Exception ex) when (ex is IOException || ex is NotSupportedException || ex is UnauthorizedAccessException)
{
return false;
}
finally
{
try { File.Delete(path); } catch { }
}
}

public static TheoryData<string> PathsWithInvalidColons = TestData.PathsWithInvalidColons;
public static TheoryData<string> PathsWithInvalidCharacters = TestData.PathsWithInvalidCharacters;
public static TheoryData<char> TrailingCharacters = TestData.TrailingCharacters;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace System.IO.FileSystem.Tests
[Collection(nameof(DisableParallelization))] // don't create multiple large files at the same time
public class LargeFileTests : FileSystemTest
{
[Fact]
[ConditionalFact(typeof(FileSystemTest), nameof(FileSystemTest.TempPathSupportsLargeFiles))]
public async Task ReadAllBytesOverLimit()
{
using FileStream fs = new (GetTestFilePath(), FileMode.Create, FileAccess.Write, FileShare.Read, 4096, FileOptions.DeleteOnClose);
Expand All @@ -26,7 +26,7 @@ public async Task ReadAllBytesOverLimit()
}
}

[Fact]
[ConditionalFact(typeof(FileSystemTest), nameof(FileSystemTest.TempPathSupportsLargeFiles))]
public void NoInt32OverflowInTheBufferingLogic()
{
const long position1 = 10;
Expand Down
Loading