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

PathVerifier should check specifically for invalid use of volume separator and tolerate alt dir separator #327

Merged
merged 18 commits into from Aug 27, 2018

Conversation

rkoeninger
Copy link
Contributor

@rkoeninger rkoeninger commented Jul 16, 2018

Fixes #283
Fixes #320

^ Lots of information on the issue (#320) about how things are handled in Unix, Mono and the .NET Framework reference implementation of File.Copy/File.Move.

  • PathVerifier.IsLegalAbsoluteOrRelative now throws NotSupportedException on Windows only when the path contains an invalid use of the drive separator char.
  • Renamed 8 tests to mention ArgumentException instead of NotSupportedException.
  • Added 8 tests for invalid drive separator specifically.
  • Updated 4 Windows only tests to test invalid path chars other than drive separator.

@rkoeninger rkoeninger changed the title File.Copy/Move should throw NotSupportedException on invalid use of drive separator PathVerifier should check specifically for invalid use of volume separator and tolerate alt dir separator Jul 17, 2018
@@ -191,6 +197,64 @@ public void MockFile_Copy_ShouldThrowNotSupportedExceptionWhenTargetFileNameCont
}
}

[Test]
[WindowsOnly(WindowsSpecifics.StrictPathRules + "; Mono does not raise this exception")]
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not WindowsSpecifics.Drives? This way we wouldn't need the additional text. Same for the other new tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, used Drives instead.

Copy link
Contributor

@fgreinacher fgreinacher left a comment

Choose a reason for hiding this comment

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

Thanks for this. I left a couple of comments.

var badDestinationPath = XFS.Path(@"^:\elsewhere\demo.txt");
var fileSystem = new MockFileSystem();
fileSystem.AddFile(sourcePath, new MockFileData("1"));
fileSystem.AddDirectory(destinationFolder);
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be unnecessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are you saying the tests themselves are necessary? They're the only place the NotSupportedException throws are tested, which is the impetus for the PR. There's a test for each potential issue with the volume separator (which would cause the NSE), for each of sourcePath and destinationPath, for each of Move and Copy - that's the 8 tests.

Are these 5 particular ones redundant? I thought people would complain about a lack of coverage if I didn't add of these.

Copy link
Contributor

Choose a reason for hiding this comment

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

I meant just the line above (and the variable), where you add some directory that doesn’t seem to be necessary for the test. Sorry for being unclear on this!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, Ok. I re-visited all 8 of those tests and found that creating the source file and destination folder was not necessary to produce the error, so I removed those parts. They just create a file system and attempt the move/copy and assert the NotSupportedException is raised.

[WindowsOnly(WindowsSpecifics.StrictPathRules + "; Mono does not raise this exception")]
public void MockFile_Move_ShouldThrowNotSupportedExceptionWhenSourcePathContainsInvalidUseOfDriveSeparator()
{
var sourcePath = XFS.Path(@"C:\something\demo.txt");
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be unnecessary.

[WindowsOnly(WindowsSpecifics.StrictPathRules + "; Mono does not raise this exception")]
public void MockFile_Move_ShouldThrowNotSupportedExceptionWhenSourcePathContainsInvalidDriveLetter()
{
var sourcePath = XFS.Path(@"C:\something\demo.txt");
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be unnecessary.

public void MockFile_Move_ShouldThrowNotSupportedExceptionWhenDestinationPathContainsInvalidUseOfDriveSeparator()
{
var sourcePath = XFS.Path(@"C:\something\demo.txt");
var destinationFolder = XFS.Path(@"C:\elsewhere");
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be unnecessary.

public void MockFile_Move_ShouldThrowNotSupportedExceptionWhenDestinationPathContainsInvalidDriveLetter()
{
var sourcePath = XFS.Path(@"C:\something\demo.txt");
var destinationFolder = XFS.Path(@"C:\elsewhere");
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be unnecessary.

if (path.Length > 0 && path[0] == Path.VolumeSeparatorChar
|| path.Length > 1 && path[1] == Path.VolumeSeparatorChar && !char.IsLetter(path[0])
|| path.LastIndexOf(Path.VolumeSeparatorChar) > 1)
{
Copy link
Contributor

Choose a reason for hiding this comment

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

This whole condition is a bit too dense for me. Maybe extract some intermediate variables or helper methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to private static method and simplified expression. Almost added a comment, but I think the new code doesn't need it: lastVolSepIndex == -1 || lastVolSepIndex == 1 && char.IsLetter(path[0])

foreach (var invalidChar in fileSystem.Path.GetInvalidFileNameChars()
.Where(x => x != fileSystem.Path.DirectorySeparatorChar
&& x != fileSystem.Path.AltDirectorySeparatorChar
&& x != fileSystem.Path.VolumeSeparatorChar))
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe add a comment that explains why exclude some characters here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added comments explaining why they're excluded and moved to local char[] show I could just use .Except() instead of length .Where().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also handled same case in Move tests

foreach (var invalidChar in fileSystem.Path.GetInvalidFileNameChars()
.Where(x => x != fileSystem.Path.DirectorySeparatorChar
&& x != fileSystem.Path.AltDirectorySeparatorChar
&& x != fileSystem.Path.VolumeSeparatorChar))
Copy link
Contributor

Choose a reason for hiding this comment

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

See above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Did same here

@rkoeninger
Copy link
Contributor Author

@fgreinacher Applied suggested changes. Made code simpler and easier to understand. Thanks.

Copy link
Contributor

@fgreinacher fgreinacher left a comment

Choose a reason for hiding this comment

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

👍

@@ -106,8 +106,19 @@ public void MockFile_Copy_ShouldThrowNotSupportedExceptionWhenSourceFileNameCont

var destFilePath = XFS.Path(@"c:\something\demo.txt");
var fileSystem = new MockFileSystem();
var excludeChars = new []
Copy link
Member

Choose a reason for hiding this comment

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

This list, comments and all, is introduced four times throughout the PR. I think there's value in pulling this out into a static constant helper file that we can just pull from.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a class called Shared to contain the building of the chars. There isn't a very good name for them though, as they're just chars that don't have ArgumentException to be raised. So it's called SpecialInvalidPathChars.

@@ -120,7 +131,7 @@ public void MockFile_Copy_ShouldThrowNotSupportedExceptionWhenSourceFileNameCont
}

[Test]
public void MockFile_Copy_ShouldThrowNotSupportedExceptionWhenSourcePathContainsInvalidChars_Message()
public void MockFile_Copy_ShouldThrowArgumentExceptionWhenSourcePathContainsInvalidChars_Message()
{
if (XFS.IsUnixPlatform())
Copy link
Member

Choose a reason for hiding this comment

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

While not necessarily part of the PR, we're touching the line right above it. It'd be nice to push forward and get rid of these if() checks and use the attribute.

@@ -120,7 +131,7 @@ public void MockFile_Copy_ShouldThrowNotSupportedExceptionWhenSourceFileNameCont
}

[Test]
public void MockFile_Copy_ShouldThrowNotSupportedExceptionWhenSourcePathContainsInvalidChars_Message()
public void MockFile_Copy_ShouldThrowArgumentExceptionWhenSourcePathContainsInvalidChars_Message()
Copy link
Member

Choose a reason for hiding this comment

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

While not necessarily part of the PR, we're touching the line right above it. It'd be nice to push forward and get rid of these if() checks and use the attribute.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done for this and 3 other similar tests.

}

[Test]
[WindowsOnly(WindowsSpecifics.Drives)]
Copy link
Member

Choose a reason for hiding this comment

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

Would this necessarily be a Windows only feature? I may need to look into it more, but the C:\ path will just be converted to a unix path, along the lines of /tmp/some/dir, and I would think /elsewhere:/demo.txt would be a valid case and something to test against a Unix environment.

Tests verify behavior, skipping them on an environment doesn't prevent the behavioral changes from going through, they just make us blind to the side effects.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

According to the docs for Path.VolumeSeparatorChar:

The value of this field is a colon (:) on Windows and Macintosh, and a slash (/) on UNIX operating systems. This is most useful for parsing paths such as "c:\windows" or "MacVolume:System Folder".

So on Unix, the test would just be inserting a /, which wouldn't cause any exception to be raised.

Drives isn't exactly the reason why the test is Windows-only, but Unix doesn't have an analog to the : on Windows, so there's no analog for this test on Unix.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this approach is fine. @jpreese Any objections left?


fileSystem.AddFile(XFS.Path(@"C:\test.txt"), new MockFileData("content"));

Assert.AreEqual("content", fileSystem.File.ReadAllText(XFS.Path("C:/test.txt")));
Copy link
Member

Choose a reason for hiding this comment

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

The name of the test is ReadAllBytes, but we're calling ReadAllText. It also looks like were hardcoding the forward slash. If AltDirectorySeparator were to change, this test might not make anymore sense. If you want to specifically test forward slash, I'd probably prefer calling out the way that were honestly just testing forwards and backwards slashes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Used byte array instead and built paths using fileSystem.Path.DirectorySeparatorChar and .AltDirectorySeparatorChar.

var destinationPath = XFS.Path(@"C:\elsewhere\demo.txt");
var fileSystem = new MockFileSystem();

Assert.Throws<NotSupportedException>(() => fileSystem.File.Copy(badSourcePath, destinationPath));
Copy link
Member

Choose a reason for hiding this comment

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

Small nit, but it's generally preferred to split up the Act and Assert steps.

TestDelegate action = () => fileSystem.File.Copy(badSourcePath, destinationPath);

Assert.Throws<NotSupportedException>(action);

Makes the debugging and readability story a bit better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not my style, but sure, done.

Used [WindowsOnly] instead of if (IsUnix)
Split delegates out of Assert.Throws onto separate line
@rkoeninger
Copy link
Contributor Author

@jpreese Made suggested changes, except for the volume separator one, which I don't know what to do for.

@fgreinacher
Copy link
Contributor

Merging this one. If we want to do something for the volume separator disuccuisb we can do that in a separate PR.

@rkoeninger rkoeninger deleted the copy-move-exception branch December 1, 2018 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants