Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
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 @@ -35,6 +35,11 @@ public static void ThrowsAny<TFirstExceptionType, TSecondExceptionType>(System.A
where TFirstExceptionType : System.Exception
where TSecondExceptionType : System.Exception
{ }
public static void ThrowsAny<TFirstExceptionType, TSecondExceptionType, TThirdExceptionType>(Action action)
where TFirstExceptionType : Exception
where TSecondExceptionType : Exception
where TThirdExceptionType : Exception
{ }
public static void ThrowsIf<T>(bool condition, System.Action action) where T : System.Exception { }
public static void GreaterThan<T>(T actual, T greaterThan, string userMessage = null) where T : System.IComparable { }
public static void LessThan<T>(T actual, T lessThan, string userMessage = null) where T : System.IComparable { }
Expand Down
27 changes: 21 additions & 6 deletions src/CoreFx.Private.TestUtilities/src/System/AssertExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading.Tasks;
using Xunit;
using Xunit.Sdk;
using System.Linq;

namespace System
{
Expand Down Expand Up @@ -93,27 +94,41 @@ public static void Throws<TNetCoreExceptionType, TNetFxExceptionType>(string net
}

public static void ThrowsAny(Type firstExceptionType, Type secondExceptionType, Action action)
{
ThrowsAnyInternal(action, firstExceptionType, secondExceptionType);
}

private static void ThrowsAnyInternal(Action action, params Type[] exceptionTypes)
{
try
{
action();
}
catch (Exception e)
{
if (e.GetType().Equals(firstExceptionType) || e.GetType().Equals(secondExceptionType))
{
Type exceptionType = e.GetType();
if (exceptionTypes.Any(t => t.Equals(exceptionType)))
return;
}
throw new XunitException($"Expected: ({firstExceptionType}) or ({secondExceptionType}) -> Actual: ({e.GetType()})");

throw new XunitException($"Expected one of: ({string.Join<Type>(", ", exceptionTypes)}) -> Actual: ({e.GetType()})");
}
throw new XunitException("AssertExtensions.ThrowsAny<firstExceptionType, secondExceptionType> didn't throw any exception");

throw new XunitException($"Expected one of: ({string.Join<Type>(", ", exceptionTypes)}) -> Actual: No exception thrown");
}

public static void ThrowsAny<TFirstExceptionType, TSecondExceptionType>(Action action)
where TFirstExceptionType : Exception
where TSecondExceptionType : Exception
{
ThrowsAny(typeof(TFirstExceptionType), typeof(TSecondExceptionType), action);
ThrowsAnyInternal(action, typeof(TFirstExceptionType), typeof(TSecondExceptionType));
}

public static void ThrowsAny<TFirstExceptionType, TSecondExceptionType, TThirdExceptionType>(Action action)
where TFirstExceptionType : Exception
where TSecondExceptionType : Exception
where TThirdExceptionType : Exception
{
ThrowsAnyInternal(action, typeof(TFirstExceptionType), typeof(TSecondExceptionType), typeof(TThirdExceptionType));
}

public static void ThrowsIf<T>(bool condition, Action action)
Expand Down
136 changes: 136 additions & 0 deletions src/CoreFx.Private.TestUtilities/tests/AssertExtensionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using Xunit.Sdk;

namespace CoreFx.Private.TestUtilities.Tests
{
public class AssertExtensionTests
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.

Are these unit tests for the functionality of our test helpers? That shouldn't be necessary, and if it is then our helpers are too complex.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Test helpers should always be tested- it is part of our infrastructure. What you don't need to do is test test methods.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

To be completely clear, where plausible and particularly in shared helpers.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The regression risk should also be considered. In these extensions it is plausible for an error in logic to not be found immediately, potentially masking test failures for extended periods of time. We have a history of our test infrastructure doing just that.

{
public class OneException : Exception { }

public class TwoException : Exception { }

public class ThreeException : Exception { }

public class NotMyException : Exception { }

[Fact]
public void ThrowsAny_Two_ThrowsExpected()
{
Action throwExpected = () => { throw new OneException(); };
try
{
AssertExtensions.ThrowsAny<OneException, TwoException>(throwExpected);
}
catch (Exception e)
{
Assert.False(true, $"Should not have thrown for first, threw {e}");
}

throwExpected = () => { throw new TwoException(); };
try
{
AssertExtensions.ThrowsAny<OneException, TwoException>(throwExpected);
}
catch (Exception e)
{
Assert.False(true, $"Should not have thrown for second, threw {e}");
}
}

[Fact]
public void ThrowsAny_Two_ThrowsUnexpected()
{
Action throwUnexpected = () => { throw new NotMyException(); };
try
{
AssertExtensions.ThrowsAny<OneException, TwoException>(throwUnexpected);
}
catch (XunitException e)
{
Assert.Equal(
"Expected one of: (CoreFx.Private.TestUtilities.Tests.AssertExtensionTests+OneException, CoreFx.Private.TestUtilities.Tests.AssertExtensionTests+TwoException) -> Actual: (CoreFx.Private.TestUtilities.Tests.AssertExtensionTests+NotMyException)",
e.Message);
return;
}

Assert.False(true, "Should have thrown");
}

[Fact]
public void ThrowsAny_Two_DoesNotThrow()
{
Action doNothing = () => { };
try
{
AssertExtensions.ThrowsAny<OneException, TwoException>(doNothing);
}
catch (XunitException e)
{
Assert.Equal(
"Expected one of: (CoreFx.Private.TestUtilities.Tests.AssertExtensionTests+OneException, CoreFx.Private.TestUtilities.Tests.AssertExtensionTests+TwoException) -> Actual: No exception thrown",
e.Message);
return;
}

Assert.False(true, "Should have thrown");
}


[Fact]
public void ThrowsAny_Three_ThrowsExpected()
{
Action throwExpected = () => { throw new ThreeException(); };
try
{
AssertExtensions.ThrowsAny<OneException, TwoException, ThreeException>(throwExpected);
}
catch (Exception e)
{
Assert.False(true, $"Should not have thrown for first, threw {e}");
}
}

[Fact]
public void ThrowsAny_Three_ThrowsUnexpected()
{
Action throwUnexpected = () => { throw new NotMyException(); };
try
{
AssertExtensions.ThrowsAny<OneException, TwoException, ThreeException>(throwUnexpected);
}
catch (XunitException)
{
// We're ok
return;
}

Assert.False(true, "Should have thrown");
}

[Fact]
public void ThrowsAny_Three_DoesNotThrow()
{
Action doNothing = () => { };
try
{
AssertExtensions.ThrowsAny<OneException, TwoException, ThreeException>(doNothing);
}
catch (XunitException)
{
// We're ok
return;
}

Assert.False(true, "Should have thrown");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<RootNamespace>CoreFx.Private.TestUtilities.Tests</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="AssertExtensionTests.cs" />
<Compile Include="TheoryExtensionTests.cs" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
Expand Down
7 changes: 3 additions & 4 deletions src/System.IO.FileSystem/tests/File/Move.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,18 +206,17 @@ public void OverMaxPathWorks_Windows()
}

[Fact]
[PlatformSpecific(TestPlatforms.AnyUnix)] // Path longer than max Windows path limit throws
[PlatformSpecific(TestPlatforms.AnyUnix)]
public void LongPath()
{
//Create a destination path longer than the traditional Windows limit of 256 characters
string testFileSource = Path.Combine(TestDirectory, GetTestFileName());
File.Create(testFileSource).Dispose();

Assert.All(IOInputs.GetPathsLongerThanMaxLongPath(GetTestFilePath()), (path) =>
{
AssertExtensions.ThrowsAny<PathTooLongException, FileNotFoundException>(() => Move(testFileSource, path));
AssertExtensions.ThrowsAny<PathTooLongException, FileNotFoundException, DirectoryNotFoundException>(() => Move(testFileSource, path));
File.Delete(testFileSource);
AssertExtensions.ThrowsAny<PathTooLongException, FileNotFoundException>(() => Move(path, testFileSource));
AssertExtensions.ThrowsAny<PathTooLongException, FileNotFoundException, DirectoryNotFoundException>(() => Move(path, testFileSource));
});
}

Expand Down
39 changes: 31 additions & 8 deletions src/System.Runtime.Extensions/tests/System/IO/PathTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,28 @@ public static void ChangeExtension(string path, string newExtension, string expe
Assert.Equal(expected, Path.ChangeExtension(path, newExtension));
}

[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData("\r\n")]
public static void GetDirectoryName_EmptyOrWhitespace_Throws(string path)
[Fact]
public static void GetDirectoryName_EmptyThrows()
{
AssertExtensions.Throws<ArgumentException>("path", null, () => Path.GetDirectoryName(string.Empty));
}

[Theory,
InlineData(" "),
InlineData("\r\n")]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I prefer having separate attributes, but it seems we aren't consisetnt...

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.

ditto

[ActiveIssue(21358)] // Pending CoreCLR behavior change
public static void GetDirectoryName_SpaceOrControlCharsThrowOnWindows(string path)
{
AssertExtensions.Throws<ArgumentException>("path", null, () => Path.GetDirectoryName(path));
Action action = () => Path.GetDirectoryName(path);
if (PlatformDetection.IsWindows)
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.

This should be using PlatformSpecific attributes instead.

Also the test name has "Windows" in it but runs on Unix.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't agree in this case. This particular case is saying "throws on Windows but not Unix". Same input data. I could make the test name longer, but I think the fact that it isn't Windows only makes it implicit that it is "WindowsAndNotUnix".

I could split this into two entry points and shared test data property, but that seems like over-kill to me in this case. Same goes for duplicating the method. I think it is more coherent to have both sides of the coin together here.

{
AssertExtensions.Throws<ArgumentException>("path", null, () => Path.GetDirectoryName(path));
}
else
{
// These are valid paths on Unix
action();
}
}

[Theory]
Expand Down Expand Up @@ -201,12 +216,20 @@ public static void GetFileNameWithoutExtension(string path, string expected)
}

[Fact]
public static void GetPathRoot()
public static void GetPathRoot_NullReturnsNull()
{
Assert.Null(Path.GetPathRoot(null));
}

[Fact]
public static void GetPathRoot_EmptyThrows()
{
AssertExtensions.Throws<ArgumentException>("path", null, () => Path.GetPathRoot(string.Empty));
AssertExtensions.Throws<ArgumentException>("path", null, () => Path.GetPathRoot("\r\n"));
}

[Fact]
public static void GetPathRoot_Basic()
{
string cwd = Directory.GetCurrentDirectory();
Assert.Equal(cwd.Substring(0, cwd.IndexOf(Path.DirectorySeparatorChar) + 1), Path.GetPathRoot(cwd));
Assert.True(Path.IsPathRooted(cwd));
Expand Down
21 changes: 0 additions & 21 deletions src/System.Runtime/tests/System/IO/PathTooLongExceptionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,5 @@ public static void Ctor_String_Exception()
var exception = new PathTooLongException(message, innerException);
ExceptionUtility.ValidateExceptionProperties(exception, hResult: HResults.COR_E_PATHTOOLONG, innerException: innerException, message: message);
}

[Fact]
public static void OverlyLongPath_ThrowsPathTooLongException()
{
// This test case ensures that the PathTooLongException defined in System.IO.Primitives is the same that
// is thrown by Path. The S.IO.FS.P implementation forwards to the core assembly to ensure this is true.

// Build up a path until GetFullPath throws, and verify that the right exception type
// emerges from it and related APIs.
var builder = new StringBuilder("directoryNameHere" + Path.DirectorySeparatorChar);
string path = null;
Assert.Throws<PathTooLongException>(new Action(() =>
{
while (true)
{
path = builder.ToString();
Path.GetFullPath(path); // will eventually throw when path is too long
builder.Append(path); // double the number of directories for the next time
}
}));
}
}
}