Skip to content
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
58 changes: 37 additions & 21 deletions src/Framework.UnitTests/AbsolutePath_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Shouldly;
using Xunit;
using Xunit.NetCore.Extensions;
Expand All @@ -30,7 +29,7 @@ private static void ValidatePathAcceptance(string path, bool shouldBeAccepted)
else
{
// Should throw ArgumentException for any non-absolute path
Should.Throw<System.ArgumentException>(() => new AbsolutePath(path, ignoreRootedCheck: false),
Should.Throw<ArgumentException>(() => new AbsolutePath(path, ignoreRootedCheck: false),
$"Path '{path}' should be rejected as it's not a true absolute path");
}
}
Expand All @@ -45,25 +44,42 @@ public void AbsolutePath_FromAbsolutePath_ShouldPreservePath()
Path.IsPathRooted(absolutePath.Value).ShouldBeTrue();
}

[Theory]
[InlineData(null)]
[InlineData("")]
[Fact]
public void AbsolutePath_NullOrEmpty_ShouldThrowOnNull()
{
string? path = null;

Should.Throw<ArgumentNullException>(() => new AbsolutePath(path!));
}

[Fact]
[UseInvariantCulture]
public void AbsolutePath_NullOrEmpty_ShouldThrow(string? path)
public void AbsolutePath_NullOrEmpty_ShouldThrowOnEmpty()
{
var exception = Should.Throw<ArgumentException>(() => new AbsolutePath(path!));
exception.Message.ShouldContain("Path must not be null or empty");
string path = "";

var exception = Should.Throw<ArgumentException>(() => new AbsolutePath(path));
exception.Message.ShouldStartWith("The value cannot be an empty string.");
}

[Theory]
[InlineData(null)]
[InlineData("")]
[Fact]
public void AbsolutePath_NullOrEmptyWithBasePath_ShouldThrowOnNull()
{
string? path = null;
var basePath = GetTestBasePath();

Should.Throw<ArgumentNullException>(() => new AbsolutePath(path!, basePath));
}

[Fact]
[UseInvariantCulture]
public void AbsolutePath_NullOrEmptyWithBasePath_ShouldThrow(string? path)
public void AbsolutePath_NullOrEmptyWithBasePath_ShouldThrowOnEmpty()
{
string path = "";
var basePath = GetTestBasePath();
var exception = Should.Throw<ArgumentException>(() => new AbsolutePath(path!, basePath));
exception.Message.ShouldContain("Path must not be null or empty");

var exception = Should.Throw<ArgumentException>(() => new AbsolutePath(path, basePath));
exception.Message.ShouldStartWith("The value cannot be an empty string.");
}

[Theory]
Expand All @@ -78,7 +94,7 @@ public void AbsolutePath_FromRelativePath_ShouldResolveAgainstBase(string relati
var absolutePath = new AbsolutePath(relativePath, basePath);

Path.IsPathRooted(absolutePath.Value).ShouldBeTrue();

string expectedPath = Path.Combine(baseDirectory, relativePath);
absolutePath.Value.ShouldBe(expectedPath);
}
Expand Down Expand Up @@ -165,7 +181,7 @@ public void AbsolutePath_RootedValidation_ShouldBehaveProperly(string path, bool
{
if (shouldThrow)
{
Should.Throw<System.ArgumentException>(() => new AbsolutePath(path, ignoreRootedCheck: ignoreRootedCheck));
Should.Throw<ArgumentException>(() => new AbsolutePath(path, ignoreRootedCheck: ignoreRootedCheck));
}
else
{
Expand Down Expand Up @@ -224,7 +240,7 @@ public void GetCanonicalForm_NullPath_ShouldReturnSameInstance()
{
var absolutePath = new AbsolutePath(null!, null!, ignoreRootedCheck: true);
var result = absolutePath.GetCanonicalForm();

// Should return the same struct values when no normalization is needed
result.ShouldBe(absolutePath);
}
Expand All @@ -249,20 +265,20 @@ public void GetCanonicalForm_UnixPathNormalization_ShouldMatchPathGetFullPath(st
{
ValidateGetCanonicalFormMatchesSystem(inputPath);
}

private static void ValidateGetCanonicalFormMatchesSystem(string inputPath)
{
var absolutePath = new AbsolutePath(inputPath, ignoreRootedCheck: true);
var result = absolutePath.GetCanonicalForm();
var systemResult = Path.GetFullPath(inputPath);

// Should match Path.GetFullPath behavior exactly
result.Value.ShouldBe(systemResult);

// Should preserve original value
result.OriginalValue.ShouldBe(absolutePath.OriginalValue);
}

[WindowsOnlyFact]
[UseInvariantCulture]
public void AbsolutePath_NotRooted_ShouldThrowWithLocalizedMessage()
Expand Down
9 changes: 6 additions & 3 deletions src/Framework/Microsoft.Build.Framework.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
<ApplyNgenOptimization Condition="'$(TargetFramework)' == '$(FullFrameworkTFM)'">full</ApplyNgenOptimization>
<EnablePackageValidation>true</EnablePackageValidation>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateResxSource>true</GenerateResxSource>
Comment thread
JanProvaznik marked this conversation as resolved.
<GenerateResxSourceEmitFormatMethods>true</GenerateResxSourceEmitFormatMethods>
</PropertyGroup>

<ItemGroup>
Expand Down Expand Up @@ -75,9 +77,10 @@
</ItemGroup>

<ItemGroup>
<EmbeddedResource Update="Resources\Strings.resx">
<LogicalName>$(AssemblyName).Strings.resources</LogicalName>
<SubType>Designer</SubType>
<EmbeddedResource Update="Resources\SR.resx">
<GenerateSource>true</GenerateSource>
</EmbeddedResource>

<Using Include="$(RootNamespace).Resources"/>
</ItemGroup>
</Project>
14 changes: 4 additions & 10 deletions src/Framework/PathHelpers/AbsolutePath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ namespace Microsoft.Build.Framework
/// The normalized string representation of this path.
/// </summary>
public string Value { get; }

/// <summary>
/// The original string used to create this path.
/// </summary>
Expand Down Expand Up @@ -84,17 +84,14 @@ internal AbsolutePath(string path, string original, bool ignoreRootedCheck)
/// <exception cref="ArgumentException">Thrown if <paramref name="path"/> is null, empty, or not a rooted path.</exception>
private static void ValidatePath(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException(FrameworkResources.GetString("PathMustNotBeNullOrEmpty"), nameof(path));
}
ArgumentException.ThrowIfNullOrEmpty(path);

// Path.IsPathFullyQualified is not available in .NET Standard 2.0
// in .NET Framework it's provided by package and in .NET it's built-in
#if NETFRAMEWORK || NET
if (!Path.IsPathFullyQualified(path))
{
throw new ArgumentException(FrameworkResources.GetString("PathMustBeRooted"), nameof(path));
throw new ArgumentException(SR.PathMustBeRooted, nameof(path));
}
#endif
}
Expand All @@ -107,10 +104,7 @@ private static void ValidatePath(string path)
/// <exception cref="ArgumentException">Thrown if <paramref name="path"/> is null or empty.</exception>
public AbsolutePath(string path, AbsolutePath basePath)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException(FrameworkResources.GetString("PathMustNotBeNullOrEmpty"), nameof(path));
}
ArgumentException.ThrowIfNullOrEmpty(path);

// This function should not throw when path has illegal characters.
// For .NET Framework, Microsoft.IO.Path.Combine should be used instead of System.IO.Path.Combine to achieve it.
Expand Down
Loading