Skip to content
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

62606 Return fully qualified path from FileSystemEventArgs #63051

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,15 @@ private void OnFileSystemEntryChange(string fullPath)
{
try
{
// this can happen when the fullPath matches the root directory path
// but the root path is always created with a trailing slash
// this was masked by a side effect (bug) in RenamedEventArgs that was appending the trailing slash to fullPath value
// that behavior was changed with the fix for https://github.com/dotnet/runtime/issues/62606
if (fullPath.Length < _root.Length)
{
return;
}

var fileSystemInfo = new FileInfo(fullPath);
if (FileSystemInfoHelper.IsExcluded(fileSystemInfo, _filters))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,7 @@ public FileSystemEventArgs(WatcherChangeTypes changeType, string directory, stri
{
_changeType = changeType;
_name = name;
_fullPath = Combine(directory, name);
}

/// <summary>Combines a directory path and a relative file name into a single path.</summary>
/// <param name="directoryPath">The directory path.</param>
/// <param name="name">The file name.</param>
/// <returns>The combined name.</returns>
/// <remarks>
/// This is like Path.Combine, except without argument validation,
/// and a separator is used even if the name argument is empty.
/// </remarks>
internal static string Combine(string directoryPath, string? name)
{
bool hasSeparator = false;
if (directoryPath.Length > 0)
{
char c = directoryPath[directoryPath.Length - 1];
hasSeparator = c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar;
}

return hasSeparator ?
directoryPath + name :
directoryPath + Path.DirectorySeparatorChar + name;
_fullPath = Path.Join(Path.GetFullPath(directory), name);
}

/// <devdoc>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public RenamedEventArgs(WatcherChangeTypes changeType, string directory, string?
: base(changeType, directory, name)
{
_oldName = oldName;
_oldFullPath = Combine(directory, oldName);
_oldFullPath = Path.Join(Path.GetFullPath(directory), oldName);
}

/// <devdoc>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,86 @@ namespace System.IO.Tests
public class FileSystemEventArgsTests
{
[Theory]
[PlatformSpecific(TestPlatforms.Windows)]
[InlineData(WatcherChangeTypes.Changed, "D:\\", "foo.txt", "D:\\foo.txt")]
[InlineData(WatcherChangeTypes.Changed, "E:\\bar", "foo.txt", "E:\\bar\\foo.txt")]
[InlineData(WatcherChangeTypes.All, "D:\\", "foo.txt", "D:\\foo.txt")]
[InlineData(WatcherChangeTypes.All, "E:\\bar", "foo.txt", "E:\\bar\\foo.txt")]
public static void FileSystemEventArgs_ctor_AbsolutePaths(WatcherChangeTypes changeType, string directory, string name, string expectedFullPath)
{
FileSystemEventArgs args = new FileSystemEventArgs(changeType, directory, name);

Assert.Equal(changeType, args.ChangeType);
Assert.Equal(expectedFullPath, args.FullPath);
Assert.Equal(name, args.Name);
}

[Theory]
[PlatformSpecific(TestPlatforms.Windows)]
[InlineData(WatcherChangeTypes.Changed, "C:", "foo.txt")]
[InlineData(WatcherChangeTypes.All, "C:", "foo.txt")]
[InlineData((WatcherChangeTypes)0, "", "")]
[InlineData((WatcherChangeTypes)0, "", null)]
public static void FileSystemEventArgs_ctor(WatcherChangeTypes changeType, string directory, string name)
public static void FileSystemEventArgs_ctor_RelativePathFromCurrentDirectoryInGivenDrive(WatcherChangeTypes changeType, string directory, string name)
{
FileSystemEventArgs args = new FileSystemEventArgs(changeType, directory, name);

if (!directory.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
{
directory += Path.DirectorySeparatorChar;
}
Assert.Equal(changeType, args.ChangeType);
Assert.Equal(AppendDirectorySeparator(Directory.GetCurrentDirectory()) + name, args.FullPath);
Assert.Equal(name, args.Name);
}

[Theory]
[InlineData(WatcherChangeTypes.Changed, "bar", "foo.txt")]
[InlineData(WatcherChangeTypes.Changed, "bar\\baz", "foo.txt")]
[InlineData(WatcherChangeTypes.All, "bar", "foo.txt")]
[InlineData(WatcherChangeTypes.All, "bar\\baz", "foo.txt")]
public static void FileSystemEventArgs_ctor_DirectoryIsRelativePath(WatcherChangeTypes changeType, string directory, string name)
{
FileSystemEventArgs args = new FileSystemEventArgs(changeType, directory, name);

directory = AppendDirectorySeparator(directory);

Assert.Equal(changeType, args.ChangeType);
Assert.Equal(directory + name, args.FullPath);
Assert.Equal(AppendDirectorySeparator(Directory.GetCurrentDirectory()) + directory + name, args.FullPath);
Assert.Equal(name, args.Name);
}

[Fact]
public static void FileSystemEventArgs_ctor_Invalid()
public static void FileSystemEventArgs_ctor_Invalid_EmptyDirectory()
{
Assert.Throws<NullReferenceException>(() => new FileSystemEventArgs((WatcherChangeTypes)0, null, string.Empty));
Assert.Throws<ArgumentException>(() => new FileSystemEventArgs((WatcherChangeTypes)0, "", "foo.txt"));
}

[Fact]
public static void FileSystemEventArgs_ctor_Invalid_NullDirectory()
{
Assert.Throws<ArgumentNullException>(() => new FileSystemEventArgs((WatcherChangeTypes)0, null, "foo.txt"));
}

[Theory]
[InlineData(WatcherChangeTypes.All, "bar", "")]
[InlineData(WatcherChangeTypes.All, "bar", null)]
[InlineData(WatcherChangeTypes.Changed, "bar", "")]
[InlineData(WatcherChangeTypes.Changed, "bar", null)]
public static void FileSystemEventArgs_ctor_When_EmptyFileName_Then_FullPathReturnsTheDirectoryFullPath(WatcherChangeTypes changeType, string directory, string name)
{
FileSystemEventArgs args = new FileSystemEventArgs(changeType, directory, name);

Assert.Equal(changeType, args.ChangeType);
Assert.Equal(AppendDirectorySeparator(Directory.GetCurrentDirectory()) + directory, args.FullPath);
Assert.Equal(name, args.Name);
}

#region Test Helpers

private static string AppendDirectorySeparator(string directory)
{
if (!directory.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
{
directory += Path.DirectorySeparatorChar;
}
return directory;
}

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,104 @@ namespace System.IO.Tests
public class RenamedEventArgsTests
{
[Theory]
[InlineData(WatcherChangeTypes.Changed, "C:", "foo.txt", "bar.txt")]
[InlineData(WatcherChangeTypes.All, "C:", "foo.txt", "bar.txt")]
[InlineData((WatcherChangeTypes)0, "", "", "")]
[InlineData((WatcherChangeTypes)0, "", null, null)]
public static void RenamedEventArgs_ctor(WatcherChangeTypes changeType, string directory, string name, string oldName)
[InlineData(WatcherChangeTypes.Changed, "C:\\bar", "foo.txt", "bar.txt")]
[InlineData(WatcherChangeTypes.All, "C:\\bar", "foo.txt", "bar.txt")]
[InlineData((WatcherChangeTypes)0, "C:\\bar", "", "")]
[InlineData((WatcherChangeTypes)0, "C:\\bar", null, null)]
public static void RenamedEventArgs_ctor_NonPathPropertiesAreSetCorrectly(WatcherChangeTypes changeType, string directory, string name, string oldName)
{
RenamedEventArgs args = new RenamedEventArgs(changeType, directory, name, oldName);
Assert.Equal(changeType, args.ChangeType);
Assert.Equal(directory + Path.DirectorySeparatorChar + name, args.FullPath);
Assert.Equal(name, args.Name);
Assert.Equal(oldName, args.OldName);
// FullPath is tested as part of the base class FileSystemEventArgs tests
}

[Theory]
[PlatformSpecific(TestPlatforms.Windows)]
[InlineData(WatcherChangeTypes.Changed, "D:\\", "foo.txt", "bar.txt", "D:\\bar.txt")]
[InlineData(WatcherChangeTypes.Changed, "E:\\bar", "foo.txt", "bar.txt", "E:\\bar\\bar.txt")]
[InlineData(WatcherChangeTypes.All, "D:\\", "foo.txt", "bar.txt", "D:\\bar.txt")]
[InlineData(WatcherChangeTypes.All, "E:\\bar", "foo.txt", "bar.txt", "E:\\bar\\bar.txt")]
public static void RenamedEventArgs_ctor_OldFullPath_DirectoryIsAnAbsolutePath(WatcherChangeTypes changeType, string directory, string name, string oldName, string expectedOldFullPath)
{
RenamedEventArgs args = new RenamedEventArgs(changeType, directory, name, oldName);

Assert.Equal(changeType, args.ChangeType);
Assert.Equal(expectedOldFullPath, args.OldFullPath);
Assert.Equal(name, args.Name);
Assert.Equal(oldName, args.OldName);
}

[Theory]
[PlatformSpecific(TestPlatforms.Windows)]
[InlineData(WatcherChangeTypes.Changed, "C:", "foo.txt", "bar.txt")]
[InlineData(WatcherChangeTypes.All, "C:", "foo.txt", "bar.txt")]
[InlineData((WatcherChangeTypes)0, "", "", "")]
[InlineData((WatcherChangeTypes)0, "", null, null)]
public static void RenamedEventArgs_ctor_OldFullPath(WatcherChangeTypes changeType, string directory, string name, string oldName)
public static void RenamedEventArgs_ctor_OldFullPath_DirectoryIsRelativePathFromCurrentDirectoryInGivenDrive(WatcherChangeTypes changeType, string directory, string name, string oldName)
{
RenamedEventArgs args = new RenamedEventArgs(changeType, directory, name, oldName);
Assert.Equal(directory + Path.DirectorySeparatorChar + oldName, args.OldFullPath);

Assert.Equal(changeType, args.ChangeType);
Assert.Equal(AppendDirectorySeparator(Directory.GetCurrentDirectory()) + oldName, args.OldFullPath);
Assert.Equal(name, args.Name);
}

[Theory]
[InlineData(WatcherChangeTypes.Changed, "bar", "foo.txt", "bar.txt")]
[InlineData(WatcherChangeTypes.Changed, "bar\\baz", "foo.txt", "bar.txt")]
[InlineData(WatcherChangeTypes.All, "bar", "foo.txt", "bar.txt")]
[InlineData(WatcherChangeTypes.All, "bar\\baz", "foo.txt", "bar.txt")]
public static void RenamedEventArgs_ctor_OldFullPath_DirectoryIsRelativePath(WatcherChangeTypes changeType, string directory, string name, string oldName)
{
RenamedEventArgs args = new RenamedEventArgs(changeType, directory, name, oldName);

directory = AppendDirectorySeparator(directory);

Assert.Equal(changeType, args.ChangeType);
Assert.Equal(AppendDirectorySeparator(Directory.GetCurrentDirectory()) + directory + oldName, args.OldFullPath);
Assert.Equal(name, args.Name);
Assert.Equal(oldName, args.OldName);
}

[Fact]
public static void RenamedEventArgs_ctor_Invalid_EmptyDirectory()
{
Assert.Throws<ArgumentException>(() => new RenamedEventArgs((WatcherChangeTypes)0, "", "foo.txt", "bar.txt"));
}

[Fact]
public static void RenamedEventArgs_ctor_Invalid()
public static void RenamedEventArgs_ctor_Invalid_NullDirectory()
{
Assert.Throws<ArgumentNullException>(() => new RenamedEventArgs((WatcherChangeTypes)0, null, "foo.txt", "bar.txt"));
}

[Theory]
[InlineData(WatcherChangeTypes.All, "bar", "", "")]
[InlineData(WatcherChangeTypes.All, "bar", null, null)]
[InlineData(WatcherChangeTypes.Changed, "bar", "", "")]
[InlineData(WatcherChangeTypes.Changed, "bar", null, null)]
[InlineData(WatcherChangeTypes.All, "bar", "foo.txt", "")]
[InlineData(WatcherChangeTypes.Changed, "bar", "foo.txt", null)]
public static void RenamedEventArgs_ctor_When_EmptyOldFileName_Then_OldFullPathReturnsTheDirectoryFullPath(WatcherChangeTypes changeType, string directory, string name, string oldName)
{
RenamedEventArgs args = new RenamedEventArgs(changeType, directory, name, oldName);

Assert.Equal(changeType, args.ChangeType);
Assert.Equal(AppendDirectorySeparator(Directory.GetCurrentDirectory()) + directory, args.OldFullPath);
Assert.Equal(name, args.Name);
}

#region Test Helpers

private static string AppendDirectorySeparator(string directory)
{
Assert.Throws<NullReferenceException>(() => new RenamedEventArgs((WatcherChangeTypes)0, null, string.Empty, string.Empty));
if (!directory.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
{
directory += Path.DirectorySeparatorChar;
}
return directory;
}

#endregion
}
}