Skip to content

Commit 2e6fa66

Browse files
author
Kormos Tamás
committed
Implemented ProductVersionParser to mimick the behavior of the AssemblyInformationalVersion attribute.
Added Version.TryParse to the FileVersion parsing for a similar reason.
1 parent dd68ff2 commit 2e6fa66

File tree

4 files changed

+223
-55
lines changed

4 files changed

+223
-55
lines changed

src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileVersionInfo.cs

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -52,23 +52,25 @@ public MockFileVersionInfo(
5252
PrivateBuild = privateBuild;
5353
SpecialBuild = specialBuild;
5454

55-
if (fileVersion != null)
55+
if (Version.TryParse(fileVersion, out Version version))
5656
{
57-
ParseVersion(fileVersion, out int fileMajor, out int fileMinor, out int fileBuild, out int filePrivate);
58-
FileMajorPart = fileMajor;
59-
FileMinorPart = fileMinor;
60-
FileBuildPart = fileBuild;
61-
FilePrivatePart = filePrivate;
57+
FileMajorPart = version.Major;
58+
FileMinorPart = version.Minor;
59+
FileBuildPart = version.Build;
60+
FilePrivatePart = version.Revision;
6261
}
6362

64-
if (productVersion != null)
65-
{
66-
ParseVersion(productVersion, out int productMajor, out int productMinor, out int productBuild, out int productPrivate);
67-
ProductMajorPart = productMajor;
68-
ProductMinorPart = productMinor;
69-
ProductBuildPart = productBuild;
70-
ProductPrivatePart = productPrivate;
71-
}
63+
ProductVersionParser.Parse(
64+
productVersion,
65+
out int productMajor,
66+
out int productMinor,
67+
out int productBuild,
68+
out int productPrivate);
69+
70+
ProductMajorPart = productMajor;
71+
ProductMinorPart = productMinor;
72+
ProductBuildPart = productBuild;
73+
ProductPrivatePart = productPrivate;
7274
}
7375

7476
/// <inheritdoc/>
@@ -186,22 +188,5 @@ public override string ToString()
186188
sb.Append("Language: ").AppendLine(Language);
187189
return sb.ToString();
188190
}
189-
190-
private static void ParseVersion(string version, out int major, out int minor, out int build, out int revision)
191-
{
192-
var parts = version.Split('.');
193-
if (parts.Length != 4)
194-
{
195-
throw new FormatException("Version string must have the format 'major.minor.build.revision'.");
196-
}
197-
198-
if (!int.TryParse(parts[0], out major) ||
199-
!int.TryParse(parts[1], out minor) ||
200-
!int.TryParse(parts[2], out build) ||
201-
!int.TryParse(parts[3], out revision))
202-
{
203-
throw new FormatException("Version parts must be numeric.");
204-
}
205-
}
206191
}
207192
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using System.Reflection;
2+
using System.Text.RegularExpressions;
3+
4+
namespace System.IO.Abstractions.TestingHelpers
5+
{
6+
/// <summary>
7+
/// Provides functionality to parse a product version string into its major, minor, build, and private parts.
8+
/// </summary>
9+
public static class ProductVersionParser
10+
{
11+
/// <summary>
12+
/// Parses a product version string and extracts the numeric values for the major, minor, build, and private parts,
13+
/// mimicking the behavior of the <see cref="AssemblyInformationalVersionAttribute"/> attribute.
14+
/// </summary>
15+
/// <param name="productVersion">The product version string to parse.</param>
16+
/// <param name="productMajorPart">Outputs the major part of the version. Defaults to 0 if not found.</param>
17+
/// <param name="productMinorPart">Outputs the minor part of the version. Defaults to 0 if not found.</param>
18+
/// <param name="productBuildPart">Outputs the build part of the version. Defaults to 0 if not found.</param>
19+
/// <param name="productPrivatePart">Outputs the private part of the version. Defaults to 0 if not found.</param>
20+
/// <remarks>
21+
/// The method splits the input string into segments separated by dots ('.') and attempts to extract
22+
/// the leading numeric value from each segment. A maximum of 4 segments are processed; if more than
23+
/// 4 segments are present, all segments are ignored. Additionally, if a segment does not contain
24+
/// a valid numeric part at its start or it contains more than just a number, the rest of the segments are ignored.
25+
/// </remarks>
26+
public static void Parse(
27+
string productVersion,
28+
out int productMajorPart,
29+
out int productMinorPart,
30+
out int productBuildPart,
31+
out int productPrivatePart)
32+
{
33+
productMajorPart = 0;
34+
productMinorPart = 0;
35+
productBuildPart = 0;
36+
productPrivatePart = 0;
37+
38+
if (string.IsNullOrWhiteSpace(productVersion))
39+
{
40+
return;
41+
}
42+
43+
var segments = productVersion.Split('.');
44+
if (segments.Length > 4)
45+
{
46+
// if more than 4 segments are present, all segments are ignored
47+
return;
48+
}
49+
50+
var regex = new Regex(@"^\d+");
51+
52+
int[] parts = new int[4];
53+
54+
for (int i = 0; i < segments.Length; i++)
55+
{
56+
var match = regex.Match(segments[i]);
57+
if (match.Success && int.TryParse(match.Value, out int number))
58+
{
59+
parts[i] = number;
60+
61+
if (match.Value != segments[i])
62+
{
63+
// when a segment contains more than a number, the rest of the segments are ignored
64+
break;
65+
}
66+
}
67+
else
68+
{
69+
// when a segment is not valid, the rest of the segments are ignored
70+
break;
71+
}
72+
}
73+
74+
productMajorPart = parts[0];
75+
productMinorPart = parts[1];
76+
productBuildPart = parts[2];
77+
productPrivatePart = parts[3];
78+
}
79+
}
80+
}

tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileVersionInfoTests.cs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -83,29 +83,5 @@ public void MockFileVersionInfo_Constructor_ShouldNotSetFileAndProductVersionNum
8383
Assert.That(mockFileVersionInfo.ProductBuildPart, Is.EqualTo(0));
8484
Assert.That(mockFileVersionInfo.ProductPrivatePart, Is.EqualTo(0));
8585
}
86-
87-
[Test]
88-
[TestCaseSource(nameof(GetInvalidVersionStrings))]
89-
public void MockFileVersionInfo_Constructor_ShouldThrowFormatExceptionIfFileVersionFormatIsInvalid(string version)
90-
{
91-
// Assert
92-
Assert.Throws<FormatException>(() => new MockFileVersionInfo(@"c:\file.txt", fileVersion: version, productVersion: null));
93-
}
94-
95-
[Test]
96-
[TestCaseSource(nameof(GetInvalidVersionStrings))]
97-
public void MockFileVersionInfo_Constructor_ShouldThrowFormatExceptionIfProductVersionFormatIsInvalid(string version)
98-
{
99-
// Assert
100-
Assert.Throws<FormatException>(() => new MockFileVersionInfo(@"c:\file.txt", fileVersion: null, productVersion: version));
101-
}
102-
103-
private static IEnumerable GetInvalidVersionStrings()
104-
{
105-
yield return "";
106-
yield return "1,2,3,4";
107-
yield return "1.2.34";
108-
yield return "1.2.build.4";
109-
}
11086
}
11187
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
using NUnit.Framework;
2+
3+
namespace System.IO.Abstractions.TestingHelpers.Tests
4+
{
5+
[TestFixture]
6+
public class ProductVersionParserTests
7+
{
8+
[Test]
9+
public void ProductVersionParser_Parse_ShouldIgnoreTheSegmentsWhenThereAreMoreThanFiveOfThem()
10+
{
11+
// Arrange
12+
string productVersion = "1.2.3.4.5";
13+
14+
// Act
15+
ProductVersionParser.Parse(
16+
productVersion,
17+
out int productMajor,
18+
out int productMinor,
19+
out int productBuild,
20+
out int productPrivate);
21+
22+
// Assert
23+
Assert.Multiple(() =>
24+
{
25+
Assert.That(productMajor, Is.Zero);
26+
Assert.That(productMinor, Is.Zero);
27+
Assert.That(productBuild, Is.Zero);
28+
Assert.That(productPrivate, Is.Zero);
29+
});
30+
}
31+
32+
[Test]
33+
[TestCase("test.2.3.4", 0, 0, 0, 0)]
34+
[TestCase("1.test.3.4", 1, 0, 0, 0)]
35+
[TestCase("1.2.test.4", 1, 2, 0, 0)]
36+
[TestCase("1.2.3.test", 1, 2, 3, 0)]
37+
public void ProductVersionParser_Parse_ShouldSkipTheRestOfTheSegmentsWhenOneIsNotValidNumber(
38+
string productVersion,
39+
int expectedMajor,
40+
int expectedMinor,
41+
int expectedBuild,
42+
int expectedRevision)
43+
{
44+
// Act
45+
ProductVersionParser.Parse(
46+
productVersion,
47+
out int productMajor,
48+
out int productMinor,
49+
out int productBuild,
50+
out int productPrivate);
51+
52+
// Assert
53+
Assert.Multiple(() =>
54+
{
55+
Assert.That(productMajor, Is.EqualTo(expectedMajor));
56+
Assert.That(productMinor, Is.EqualTo(expectedMinor));
57+
Assert.That(productBuild, Is.EqualTo(expectedBuild));
58+
Assert.That(productPrivate, Is.EqualTo(expectedRevision));
59+
});
60+
}
61+
62+
[Test]
63+
[TestCase("1-test.2.3.4", 1, 0, 0, 0)]
64+
[TestCase("1-test5.2.3.4", 1, 0, 0, 0)]
65+
[TestCase("1.2-test.3.4", 1, 2, 0, 0)]
66+
[TestCase("1.2-test5.3.4", 1, 2, 0, 0)]
67+
[TestCase("1.2.3-test.4", 1, 2, 3, 0)]
68+
[TestCase("1.2.3-test5.4", 1, 2, 3, 0)]
69+
[TestCase("1.2.3.4-test", 1, 2, 3, 4)]
70+
[TestCase("1.2.3.4-test5", 1, 2, 3, 4)]
71+
public void ProductVersionParser_Parse_ShouldSkipTheRestOfTheSegmentsWhenOneContainsMoreThanJustOneNumber(
72+
string productVersion,
73+
int expectedMajor,
74+
int expectedMinor,
75+
int expectedBuild,
76+
int expectedRevision)
77+
{
78+
// Act
79+
ProductVersionParser.Parse(
80+
productVersion,
81+
out int productMajor,
82+
out int productMinor,
83+
out int productBuild,
84+
out int productPrivate);
85+
86+
// Assert
87+
Assert.Multiple(() =>
88+
{
89+
Assert.That(productMajor, Is.EqualTo(expectedMajor));
90+
Assert.That(productMinor, Is.EqualTo(expectedMinor));
91+
Assert.That(productBuild, Is.EqualTo(expectedBuild));
92+
Assert.That(productPrivate, Is.EqualTo(expectedRevision));
93+
});
94+
}
95+
96+
[Test]
97+
[TestCase("", 0, 0, 0, 0)]
98+
[TestCase("1", 1, 0, 0, 0)]
99+
[TestCase("1.2", 1, 2, 0, 0)]
100+
[TestCase("1.2.3", 1, 2, 3, 0)]
101+
[TestCase("1.2.3.4", 1, 2, 3, 4)]
102+
public void ProductVersionParser_Parse_ShouldParseEachProvidedSegment(
103+
string productVersion,
104+
int expectedMajor,
105+
int expectedMinor,
106+
int expectedBuild,
107+
int expectedRevision)
108+
{
109+
// Act
110+
ProductVersionParser.Parse(
111+
productVersion,
112+
out int productMajor,
113+
out int productMinor,
114+
out int productBuild,
115+
out int productPrivate);
116+
117+
// Assert
118+
Assert.Multiple(() =>
119+
{
120+
Assert.That(productMajor, Is.EqualTo(expectedMajor));
121+
Assert.That(productMinor, Is.EqualTo(expectedMinor));
122+
Assert.That(productBuild, Is.EqualTo(expectedBuild));
123+
Assert.That(productPrivate, Is.EqualTo(expectedRevision));
124+
});
125+
}
126+
}
127+
}

0 commit comments

Comments
 (0)