Skip to content
Open
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
29 changes: 26 additions & 3 deletions src/libraries/System.Private.CoreLib/src/System/Version.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace System

[Serializable]
[TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public sealed class Version : ICloneable, IComparable, IComparable<Version?>, IEquatable<Version?>, ISpanFormattable, IUtf8SpanFormattable, IUtf8SpanParsable<Version>
public sealed class Version : ICloneable, IComparable, IComparable<Version?>, IEquatable<Version?>, ISpanFormattable, IUtf8SpanFormattable, IUtf8SpanParsable<Version>, ISpanParsable<Version>
{
// AssemblyName depends on the order staying the same
private readonly int _Major; // Do not rename (binary serialization)
Expand Down Expand Up @@ -288,13 +288,28 @@ public static Version Parse(string input)
return ParseVersion(input.AsSpan(), throwOnFailure: true)!;
}

/// <inheritdoc cref="IParsable{TSelf}.Parse(string, IFormatProvider?)"/>
static Version IParsable<Version>.Parse(string s, IFormatProvider? provider)
{
ArgumentNullException.ThrowIfNull(s);
return ParseHelper(s.AsSpan());
}

public static Version Parse(ReadOnlySpan<char> input) =>
ParseVersion(input, throwOnFailure: true)!;

/// <inheritdoc cref="ISpanParsable{TSelf}.Parse(ReadOnlySpan{char}, IFormatProvider?)"/>
static Version ISpanParsable<Version>.Parse(ReadOnlySpan<char> s, IFormatProvider? provider) =>
ParseHelper(s);

/// <inheritdoc cref="IUtf8SpanParsable{TSelf}.Parse(ReadOnlySpan{byte}, IFormatProvider?)"/>
static Version IUtf8SpanParsable<Version>.Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider)
static Version IUtf8SpanParsable<Version>.Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider) =>
ParseHelper(utf8Text);

private static Version ParseHelper<TChar>(ReadOnlySpan<TChar> input)
where TChar : unmanaged, IUtfChar<TChar>
{
Version? result = ParseVersion(utf8Text, throwOnFailure: false);
Version? result = ParseVersion(input, throwOnFailure: false);
// Required to throw FormatException for invalid input according to contract.
if (result == null)
{
Expand Down Expand Up @@ -327,12 +342,20 @@ public static bool TryParse([NotNullWhen(true)] string? input, [NotNullWhen(true
return result is not null;
}

/// <inheritdoc cref="IParsable{TSelf}.TryParse(string?, IFormatProvider?, out TSelf)"/>
static bool IParsable<Version>.TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [NotNullWhen(true)] out Version? result) =>
TryParse(s, out result);

public static bool TryParse(ReadOnlySpan<char> input, [NotNullWhen(true)] out Version? result)
{
result = ParseVersion(input, throwOnFailure: false);
return result is not null;
}

/// <inheritdoc cref="ISpanParsable{TSelf}.TryParse(ReadOnlySpan{char}, IFormatProvider?, out TSelf)"/>
static bool ISpanParsable<Version>.TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, [NotNullWhen(true)] out Version? result) =>
TryParse(s, out result);

/// <summary>
/// Tries to convert the UTF-8 representation of a version number to an equivalent Version object, and returns a value that indicates whether the conversion succeeded.
/// </summary>
Expand Down
6 changes: 5 additions & 1 deletion src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7895,7 +7895,7 @@ protected ValueType() { }
public override int GetHashCode() { throw null; }
public override string? ToString() { throw null; }
}
public sealed partial class Version : System.ICloneable, System.IComparable, System.IComparable<System.Version?>, System.IEquatable<System.Version?>, System.IFormattable, System.ISpanFormattable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable<System.Version>
public sealed partial class Version : System.ICloneable, System.IComparable, System.IComparable<System.Version?>, System.IEquatable<System.Version?>, System.IFormattable, System.IParsable<System.Version>, System.ISpanFormattable, System.ISpanParsable<System.Version>, System.IUtf8SpanFormattable, System.IUtf8SpanParsable<System.Version>
{
public Version() { }
public Version(int major, int minor) { }
Expand Down Expand Up @@ -7924,7 +7924,11 @@ public Version(string version) { }
public static System.Version Parse(System.ReadOnlySpan<char> input) { throw null; }
public static System.Version Parse(string input) { throw null; }
string System.IFormattable.ToString(string? format, System.IFormatProvider? formatProvider) { throw null; }
static System.Version System.IParsable<System.Version>.Parse(string s, System.IFormatProvider? provider) { throw null; }
static bool System.IParsable<System.Version>.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Version result) { throw null; }
bool System.ISpanFormattable.TryFormat(System.Span<char> destination, out int charsWritten, System.ReadOnlySpan<char> format, System.IFormatProvider? provider) { throw null; }
static System.Version System.ISpanParsable<System.Version>.Parse(System.ReadOnlySpan<char> s, System.IFormatProvider? provider) { throw null; }
static bool System.ISpanParsable<System.Version>.TryParse(System.ReadOnlySpan<char> s, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Version result) { throw null; }
bool System.IUtf8SpanFormattable.TryFormat(System.Span<byte> utf8Destination, out int bytesWritten, System.ReadOnlySpan<char> format, System.IFormatProvider? provider) { throw null; }
static System.Version System.IUtf8SpanParsable<System.Version>.Parse(System.ReadOnlySpan<byte> utf8Text, System.IFormatProvider? provider) { throw null; }
static bool System.IUtf8SpanParsable<System.Version>.TryParse(System.ReadOnlySpan<byte> utf8Text, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Version result) { throw null; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Xunit;

Expand Down Expand Up @@ -502,5 +503,124 @@ public static void TryFormat_Invoke_WritesExpected(Version version, string[] exp
AssertExtensions.Throws<ArgumentException>("fieldCount", () => version.TryFormat(dest, maxFieldCount + 1, out bytesWritten)); // Index > version.fieldCount
}
}

public static IEnumerable<object[]> Parse_Invalid_NonNull_TestData()
{
foreach (object[] data in Parse_Invalid_TestData())
{
if (data[0] != null)
{
yield return new object[] { data[0] };
}
}
}

[Theory]
[MemberData(nameof(Parse_Valid_TestData))]
public static void IParsable_Parse_ValidInput_ReturnsExpected(string input, Version expected)
{
Assert.Equal(expected, ParsableHelper<Version>.Parse(input, null));
Assert.Equal(expected, ParsableHelper<Version>.Parse(input, CultureInfo.InvariantCulture));

Assert.True(ParsableHelper<Version>.TryParse(input, null, out Version? result));
Assert.Equal(expected, result);
Assert.True(ParsableHelper<Version>.TryParse(input, CultureInfo.InvariantCulture, out result));
Assert.Equal(expected, result);
}

[Theory]
[MemberData(nameof(Parse_Valid_TestData))]
public static void ISpanParsable_Parse_ValidInput_ReturnsExpected(string input, Version expected)
{
Assert.Equal(expected, SpanParsableHelper<Version>.Parse(input.AsSpan(), null));
Assert.Equal(expected, SpanParsableHelper<Version>.Parse(input.AsSpan(), CultureInfo.InvariantCulture));

Assert.True(SpanParsableHelper<Version>.TryParse(input.AsSpan(), null, out Version? result));
Assert.Equal(expected, result);
Assert.True(SpanParsableHelper<Version>.TryParse(input.AsSpan(), CultureInfo.InvariantCulture, out result));
Assert.Equal(expected, result);
}

[Theory]
[MemberData(nameof(Parse_Valid_TestData))]
public static void IUtf8SpanParsable_Parse_ValidInput_ReturnsExpected(string input, Version expected)
{
byte[] utf8 = Encoding.UTF8.GetBytes(input);
Assert.Equal(expected, Utf8SpanParsableHelper<Version>.Parse(utf8, null));
Assert.Equal(expected, Utf8SpanParsableHelper<Version>.Parse(utf8, CultureInfo.InvariantCulture));

Assert.True(Utf8SpanParsableHelper<Version>.TryParse(utf8, null, out Version? result));
Assert.Equal(expected, result);
Assert.True(Utf8SpanParsableHelper<Version>.TryParse(utf8, CultureInfo.InvariantCulture, out result));
Assert.Equal(expected, result);
}

[Fact]
public static void IParsable_Parse_NullInput_ThrowsArgumentNullException()
{
AssertExtensions.Throws<ArgumentNullException>("s", () => ParsableHelper<Version>.Parse(null!, null));
AssertExtensions.Throws<ArgumentNullException>("s", () => ParsableHelper<Version>.Parse(null!, CultureInfo.InvariantCulture));

Assert.False(ParsableHelper<Version>.TryParse(null, null, out Version? result));
Assert.Null(result);
Assert.False(ParsableHelper<Version>.TryParse(null, CultureInfo.InvariantCulture, out result));
Assert.Null(result);
}

[Theory]
[MemberData(nameof(Parse_Invalid_NonNull_TestData))]
public static void IParsable_Parse_InvalidInput_ThrowsFormatException(string input)
{
Assert.Throws<FormatException>(() => ParsableHelper<Version>.Parse(input, null));
Assert.Throws<FormatException>(() => ParsableHelper<Version>.Parse(input, CultureInfo.InvariantCulture));

Assert.False(ParsableHelper<Version>.TryParse(input, null, out Version? result));
Assert.Null(result);
Assert.False(ParsableHelper<Version>.TryParse(input, CultureInfo.InvariantCulture, out result));
Assert.Null(result);
}

[Theory]
[MemberData(nameof(Parse_Invalid_NonNull_TestData))]
public static void ISpanParsable_Parse_InvalidInput_ThrowsFormatException(string input)
{
Assert.Throws<FormatException>(() => SpanParsableHelper<Version>.Parse(input.AsSpan(), null));
Assert.Throws<FormatException>(() => SpanParsableHelper<Version>.Parse(input.AsSpan(), CultureInfo.InvariantCulture));

Assert.False(SpanParsableHelper<Version>.TryParse(input.AsSpan(), null, out Version? result));
Assert.Null(result);
Assert.False(SpanParsableHelper<Version>.TryParse(input.AsSpan(), CultureInfo.InvariantCulture, out result));
Assert.Null(result);
}

[Theory]
[MemberData(nameof(Parse_Invalid_NonNull_TestData))]
public static void IUtf8SpanParsable_Parse_InvalidInput_ThrowsFormatException(string input)
{
byte[] utf8 = Encoding.UTF8.GetBytes(input);
Assert.Throws<FormatException>(() => Utf8SpanParsableHelper<Version>.Parse(utf8, null));
Assert.Throws<FormatException>(() => Utf8SpanParsableHelper<Version>.Parse(utf8, CultureInfo.InvariantCulture));

Assert.False(Utf8SpanParsableHelper<Version>.TryParse(utf8, null, out Version? result));
Assert.Null(result);
Assert.False(Utf8SpanParsableHelper<Version>.TryParse(utf8, CultureInfo.InvariantCulture, out result));
Assert.Null(result);
}

[Theory]
[InlineData(".")]
[InlineData("1.")]
[InlineData("1.0.")]
[InlineData("1.0.0.")]
public static void IParsable_ISpanParsable_Parse_TrailingDot_ThrowsFormatException(string input)
{
Assert.Throws<FormatException>(() => ParsableHelper<Version>.Parse(input, null));
Assert.Throws<FormatException>(() => SpanParsableHelper<Version>.Parse(input.AsSpan(), null));

Assert.False(ParsableHelper<Version>.TryParse(input, null, out Version? result));
Assert.Null(result);
Assert.False(SpanParsableHelper<Version>.TryParse(input.AsSpan(), null, out result));
Assert.Null(result);
}
}
}
Loading