Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 83ac6fa

Browse files
AnipikDan Moseley
authored andcommitted
[release/2.1] Fix Granularity for copy operation on unix (#31046)
* Fix Granualarity on unix * Maintaining the consistency of tests among master and release branch (#31164) * Feedback Addressed * Fixing original tests and for appContainers * comment corrected
1 parent d0975af commit 83ac6fa

3 files changed

Lines changed: 52 additions & 44 deletions

File tree

src/Native/Unix/System.Native/pal_io.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,18 +1193,18 @@ int32_t SystemNative_CopyFile(intptr_t sourceFd, intptr_t destinationFd)
11931193
#if HAVE_FUTIMES
11941194
struct timeval origTimes[2];
11951195
origTimes[0].tv_sec = sourceStat.st_atime;
1196-
origTimes[0].tv_usec = 0;
1196+
origTimes[0].tv_usec = ST_ATIME_NSEC(&sourceStat) / 1000;
11971197
origTimes[1].tv_sec = sourceStat.st_mtime;
1198-
origTimes[1].tv_usec = 0;
1198+
origTimes[1].tv_usec = ST_MTIME_NSEC(&sourceStat) / 1000;
11991199
while ((ret = futimes(outFd, origTimes)) < 0 && errno == EINTR);
12001200
#elif HAVE_FUTIMENS
12011201
// futimes is not a POSIX function, and not available on Android,
12021202
// but futimens is
12031203
struct timespec origTimes[2];
12041204
origTimes[0].tv_sec = (time_t)sourceStat.st_atime;
1205-
origTimes[0].tv_nsec = 0;
1205+
origTimes[0].tv_nsec = ST_ATIME_NSEC(&sourceStat);
12061206
origTimes[1].tv_sec = (time_t)sourceStat.st_mtime;
1207-
origTimes[1].tv_nsec = 0;
1207+
origTimes[1].tv_nsec = ST_MTIME_NSEC(&sourceStat);
12081208
while ((ret = futimens(outFd, origTimes)) < 0 && errno == EINTR);
12091209
#endif
12101210
}

src/System.IO.FileSystem/tests/Base/BaseGetSetTimes.cs

Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,14 @@ namespace System.IO.Tests
1010
{
1111
public abstract class BaseGetSetTimes<T> : FileSystemTest
1212
{
13+
protected const string HFS = "hfs";
1314
public delegate void SetTime(T item, DateTime time);
1415
public delegate DateTime GetTime(T item);
16+
// AppContainer restricts access to DriveFormat (::GetVolumeInformation)
17+
private static string driveFormat = PlatformDetection.IsInAppContainer ? string.Empty : new DriveInfo(Path.GetTempPath()).DriveFormat;
18+
19+
protected static bool isHFS => driveFormat.Equals(HFS, StringComparison.InvariantCultureIgnoreCase);
20+
protected static bool isNotHFS => !isHFS;
1521

1622
public abstract T GetExistingItem();
1723
public abstract T GetMissingItem();
@@ -70,22 +76,17 @@ public void CanGetAllTimesAfterCreation()
7076
ValidateSetTimes(item, beforeTime, afterTime);
7177
}
7278

73-
[Fact]
74-
[PlatformSpecific(TestPlatforms.Linux)] // Windows tested below, and OSX does not currently support millisec granularity
75-
public void TimesIncludeMillisecondPart_Linux()
79+
[ConditionalFact(nameof(isNotHFS))] // OSX HFS driver format does not support millisec granularity
80+
public void TimesIncludeMillisecondPart_Unix()
7681
{
7782
T item = GetExistingItem();
78-
79-
string driveFormat = new DriveInfo(GetItemPath(item)).DriveFormat;
80-
8183
Assert.All(TimeFunctions(), (function) =>
8284
{
8385
var msec = 0;
8486
for (int i = 0; i < 5; i++)
8587
{
8688
DateTime time = function.Getter(item);
8789
msec = time.Millisecond;
88-
8990
if (msec != 0)
9091
break;
9192

@@ -107,42 +108,11 @@ public void TimesIncludeMillisecondPart_Linux()
107108
});
108109
}
109110

110-
111-
[Fact]
112-
[PlatformSpecific(TestPlatforms.Windows)] // Breaking out Windows as it passes no problem there
113-
public void TimesIncludeMillisecondPart_Windows()
114-
{
115-
T item = GetExistingItem();
116-
Assert.All(TimeFunctions(), (function) =>
117-
{
118-
var msec = 0;
119-
for (int i = 0; i < 5; i++)
120-
{
121-
DateTime time = function.Getter(item);
122-
msec = time.Millisecond;
123-
if (msec != 0)
124-
break;
125-
126-
// This case should only happen 1/1000 times, unless the OS/Filesystem does
127-
// not support millisecond granularity.
128-
129-
// If it's 1/1000, or low granularity, this may help:
130-
Thread.Sleep(1234);
131-
132-
item = GetExistingItem(); // try a new file/directory
133-
}
134-
135-
Assert.NotEqual(0, msec);
136-
});
137-
}
138-
139-
[Fact]
140-
// OSX does not currently support millisec granularity: use this test as a canary to flag
141-
// if this ever changes so we can enable the actual test
142-
[PlatformSpecific(TestPlatforms.OSX)]
111+
[ConditionalFact(nameof(isHFS))]
143112
public void TimesIncludeMillisecondPart_OSX()
144113
{
145114
T item = GetExistingItem();
115+
// OSX HFS driver format does not support millisec granularity
146116
Assert.All(TimeFunctions(), (function) =>
147117
{
148118
DateTime time = function.Getter(item);

src/System.IO.FileSystem/tests/FileInfo/GetSetTimes.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System.Collections.Generic;
66
using System.Linq;
7+
using System.Threading;
78
using Xunit;
89

910
namespace System.IO.Tests
@@ -66,6 +67,43 @@ public override IEnumerable<TimeFunction> TimeFunctions(bool requiresRoundtrippi
6667
DateTimeKind.Utc);
6768
}
6869

70+
[ConditionalFact(nameof(isNotHFS))]
71+
public void CopyToMillisecondPresent()
72+
{
73+
FileInfo input = new FileInfo(GetTestFilePath());
74+
for (int i = 0; i < 5; i++)
75+
{
76+
input.Create().Dispose();
77+
if (input.LastWriteTime.Millisecond != 0)
78+
break;
79+
80+
// This case should only happen 1/1000 times, unless the OS/Filesystem does
81+
// not support millisecond granularity.
82+
83+
// If it's 1/1000, or low granularity, this may help:
84+
Thread.Sleep(1234);
85+
}
86+
87+
FileInfo output = new FileInfo(Path.Combine(GetTestFilePath(), input.Name));
88+
Assert.Equal(0, output.LastWriteTime.Millisecond);
89+
output.Directory.Create();
90+
output = input.CopyTo(output.FullName, true);
91+
Assert.NotEqual(0, input.LastWriteTime.Millisecond);
92+
Assert.NotEqual(0, output.LastWriteTime.Millisecond);
93+
}
94+
95+
[ConditionalFact(nameof(isHFS))]
96+
public void CopyToMillisecondPresent_HFS()
97+
{
98+
FileInfo input = new FileInfo(GetTestFilePath());
99+
input.Create().Dispose();
100+
FileInfo output = new FileInfo(Path.Combine(GetTestFilePath(), input.Name));
101+
output.Directory.Create();
102+
output = input.CopyTo(output.FullName, true);
103+
Assert.Equal(0, input.LastWriteTime.Millisecond);
104+
Assert.Equal(0, output.LastWriteTime.Millisecond);
105+
}
106+
69107
[Fact]
70108
public void DeleteAfterEnumerate_TimesStillSet()
71109
{

0 commit comments

Comments
 (0)