Skip to content

Commit e2295a8

Browse files
committed
Support C# format strings in config
1 parent f37d7df commit e2295a8

26 files changed

+1146
-29
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace GitVersion.Core.Tests.Extensions;
2+
3+
public static class ShouldlyExtensions
4+
{
5+
/// <summary>
6+
/// Asserts that the action throws an exception of type TException
7+
/// with the expected message.
8+
/// </summary>
9+
public static void ShouldThrowWithMessage<TException>(this Action action, string expectedMessage) where TException : Exception
10+
{
11+
var ex = Should.Throw<TException>(action);
12+
ex.Message.ShouldBe(expectedMessage);
13+
}
14+
15+
/// <summary>
16+
/// Asserts that the action throws an exception of type TException,
17+
/// and allows further assertion on the exception instance.
18+
/// </summary>
19+
public static void ShouldThrow<TException>(this Action action, Action<TException> additionalAssertions) where TException : Exception
20+
{
21+
var ex = Should.Throw<TException>(action);
22+
additionalAssertions(ex);
23+
}
24+
}

src/GitVersion.Core.Tests/Extensions/StringFormatWithExtensionTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,4 +244,29 @@ public void FormatProperty_NullObject_WithFallback_QuotedAndEmpty()
244244
var actual = target.FormatWith(propertyObject, this.environment);
245245
Assert.That(actual, Is.EqualTo(""));
246246
}
247+
248+
[Test]
249+
public void FormatAssemblyInformationalVersionWithSemanticVersionCustomFormattedCommitsSinceVersionSource()
250+
{
251+
var semanticVersion = new SemanticVersion
252+
{
253+
Major = 1,
254+
Minor = 2,
255+
Patch = 3,
256+
PreReleaseTag = new SemanticVersionPreReleaseTag(string.Empty, 9, true),
257+
BuildMetaData = new SemanticVersionBuildMetaData("Branch.main")
258+
{
259+
Branch = "main",
260+
VersionSourceSha = "versionSourceSha",
261+
Sha = "commitSha",
262+
ShortSha = "commitShortSha",
263+
CommitsSinceVersionSource = 42,
264+
CommitDate = DateTimeOffset.Parse("2014-03-06 23:59:59Z")
265+
}
266+
};
267+
const string target = "{Major}.{Minor}.{Patch}-{CommitsSinceVersionSource:0000}";
268+
const string expected = "1.2.3-0042";
269+
var actual = target.FormatWith(semanticVersion, this.environment);
270+
Assert.That(actual, Is.EqualTo(expected));
271+
}
247272
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using GitVersion.Helpers;
2+
3+
namespace GitVersion.Tests.Helpers;
4+
5+
[TestFixture]
6+
public class DateFormatterTests
7+
{
8+
[Test]
9+
public void Priority_ShouldBe2() => new DateFormatter().Priority.ShouldBe(2);
10+
11+
[Test]
12+
public void TryFormat_NullValue_ReturnsFalse()
13+
{
14+
var sut = new DateFormatter();
15+
var result = sut.TryFormat(null, "yyyy-MM-dd", out var formatted);
16+
result.ShouldBeFalse();
17+
formatted.ShouldBeEmpty();
18+
}
19+
20+
[TestCase("2021-01-01", "date:yyyy-MM-dd", "2021-01-01")]
21+
[TestCase("2021-01-01T12:00:00Z", "date:yyyy-MM-ddTHH:mm:ssZ", "2021-01-01T12:00:00Z")]
22+
public void TryFormat_ValidDateFormats_ReturnsExpectedResult(string input, string format, string expected)
23+
{
24+
var date = DateTime.Parse(input);
25+
var sut = new DateFormatter();
26+
var result = sut.TryFormat(date, format, out var formatted);
27+
result.ShouldBeTrue();
28+
formatted.ShouldBe(expected);
29+
}
30+
31+
[Test]
32+
public void TryFormat_UnsupportedFormat_ReturnsFalse()
33+
{
34+
var sut = new DateFormatter();
35+
var result = sut.TryFormat(DateTime.Now, "unsupported", out var formatted);
36+
result.ShouldBeFalse();
37+
formatted.ShouldBeEmpty();
38+
}
39+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
using GitVersion.Core.Tests.Extensions;
2+
using GitVersion.Helpers;
3+
4+
namespace GitVersion.Tests.Helpers;
5+
6+
public partial class InputSanitizerTests
7+
{
8+
[TestFixture]
9+
public class EdgeCaseTests : InputSanitizerTests
10+
{
11+
[Test]
12+
public void AllMethods_WithBoundaryLengths_HandleCorrectly()
13+
{
14+
var sut = new InputSanitizer();
15+
16+
var format49 = new string('x', 49);
17+
var format50 = new string('x', 50);
18+
var envVar199 = new string('A', 199);
19+
var envVar200 = new string('A', 200);
20+
var member99 = new string('A', 99);
21+
var member100 = new string('A', 100);
22+
23+
sut.SanitizeFormat(format49).ShouldBe(format49);
24+
sut.SanitizeFormat(format50).ShouldBe(format50);
25+
26+
sut.SanitizeEnvVarName(envVar199).ShouldBe(envVar199);
27+
sut.SanitizeEnvVarName(envVar200).ShouldBe(envVar200);
28+
29+
sut.SanitizeMemberName(member99).ShouldBe(member99);
30+
sut.SanitizeMemberName(member100).ShouldBe(member100);
31+
}
32+
33+
[Test]
34+
public void AllMethods_WithUnicodeCharacters_HandleAccordingToRegex()
35+
{
36+
var sut = new InputSanitizer();
37+
38+
const string unicodeFormat = "测试format";
39+
const string unicodeEnvVar = "测试_VAR";
40+
const string unicodeMember = "测试Member";
41+
42+
sut.SanitizeFormat(unicodeFormat).ShouldBe(unicodeFormat);
43+
44+
Action envVarAction = () => sut.SanitizeEnvVarName(unicodeEnvVar);
45+
Action memberAction = () => sut.SanitizeMemberName(unicodeMember);
46+
47+
envVarAction.ShouldThrowWithMessage<ArgumentException>($"Environment variable name contains disallowed characters: '{unicodeEnvVar}'");
48+
memberAction.ShouldThrowWithMessage<ArgumentException>($"Member name contains disallowed characters: '{unicodeMember}'");
49+
}
50+
51+
[Test]
52+
public void SanitizeFormat_WithLength49_ReturnsInput()
53+
{
54+
var format49 = new string('x', 49);
55+
new InputSanitizer().SanitizeFormat(format49).ShouldBe(format49);
56+
}
57+
58+
[Test]
59+
public void SanitizeFormat_WithLength50_ReturnsInput()
60+
{
61+
var format50 = new string('x', 50);
62+
new InputSanitizer().SanitizeFormat(format50).ShouldBe(format50);
63+
}
64+
65+
[Test]
66+
public void SanitizeEnvVarName_WithLength199_ReturnsInput()
67+
{
68+
var envVar199 = new string('A', 199);
69+
new InputSanitizer().SanitizeEnvVarName(envVar199).ShouldBe(envVar199);
70+
}
71+
72+
[Test]
73+
public void SanitizeEnvVarName_WithLength200_ReturnsInput()
74+
{
75+
var envVar200 = new string('A', 200);
76+
new InputSanitizer().SanitizeEnvVarName(envVar200).ShouldBe(envVar200);
77+
}
78+
79+
[Test]
80+
public void SanitizeMemberName_WithLength99_ReturnsInput()
81+
{
82+
var member99 = new string('A', 99);
83+
new InputSanitizer().SanitizeMemberName(member99).ShouldBe(member99);
84+
}
85+
86+
[Test]
87+
public void SanitizeMemberName_WithLength100_ReturnsInput()
88+
{
89+
var member100 = new string('A', 100);
90+
new InputSanitizer().SanitizeMemberName(member100).ShouldBe(member100);
91+
}
92+
93+
[Test]
94+
public void SanitizeFormat_WithUnicode_ReturnsInput()
95+
{
96+
const string unicodeFormat = "测试format";
97+
new InputSanitizer().SanitizeFormat(unicodeFormat).ShouldBe(unicodeFormat);
98+
}
99+
100+
[Test]
101+
public void SanitizeEnvVarName_WithUnicode_ThrowsArgumentException()
102+
{
103+
const string unicodeEnvVar = "测试_VAR";
104+
Action act = () => new InputSanitizer().SanitizeEnvVarName(unicodeEnvVar);
105+
act.ShouldThrowWithMessage<ArgumentException>(
106+
$"Environment variable name contains disallowed characters: '{unicodeEnvVar}'");
107+
}
108+
109+
[Test]
110+
public void SanitizeMemberName_WithUnicode_ThrowsArgumentException()
111+
{
112+
const string unicodeMember = "测试Member";
113+
Action act = () => new InputSanitizer().SanitizeMemberName(unicodeMember);
114+
act.ShouldThrowWithMessage<ArgumentException>(
115+
$"Member name contains disallowed characters: '{unicodeMember}'");
116+
}
117+
}
118+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using GitVersion.Helpers;
2+
3+
namespace GitVersion.Tests.Helpers;
4+
5+
[TestFixture]
6+
public class FormattableFormatterTests
7+
{
8+
[Test]
9+
public void Priority_ShouldBe2() => new FormattableFormatter().Priority.ShouldBe(2);
10+
11+
[Test]
12+
public void TryFormat_NullValue_ReturnsFalse()
13+
{
14+
var sut = new FormattableFormatter();
15+
var result = sut.TryFormat(null, "G", out var formatted);
16+
result.ShouldBeFalse();
17+
formatted.ShouldBeEmpty();
18+
}
19+
20+
[TestCase(123.456, "F2", "123.46")]
21+
[TestCase(1234.456, "F2", "1234.46")]
22+
public void TryFormat_ValidFormats_ReturnsExpectedResult(object input, string format, string expected)
23+
{
24+
var sut = new FormattableFormatter();
25+
var result = sut.TryFormat(input, format, out var formatted);
26+
result.ShouldBeTrue();
27+
formatted.ShouldBe(expected);
28+
}
29+
30+
[TestCase(123.456, "C", "Format 'C' is not supported in FormattableFormatter")]
31+
[TestCase(123.456, "P", "Format 'P' is not supported in FormattableFormatter")]
32+
[TestCase(1234567890, "N0", "Format 'N0' is not supported in FormattableFormatter")]
33+
[TestCase(1234567890, "Z", "Format 'Z' is not supported in FormattableFormatter")]
34+
public void TryFormat_UnsupportedFormat_ReturnsFalse(object input, string format, string expected)
35+
{
36+
var sut = new FormattableFormatter();
37+
var result = sut.TryFormat(input, format, out var formatted);
38+
result.ShouldBeFalse();
39+
formatted.ShouldBe(expected);
40+
}
41+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using GitVersion.Core.Tests.Extensions;
2+
using GitVersion.Helpers;
3+
4+
namespace GitVersion.Tests.Helpers;
5+
6+
[TestFixture]
7+
public partial class InputSanitizerTests
8+
{
9+
[TestFixture]
10+
public class SanitizeFormatTests : InputSanitizerTests
11+
{
12+
[Test]
13+
public void SanitizeFormat_WithValidFormat_ReturnsInput()
14+
{
15+
var sut = new InputSanitizer();
16+
const string validFormat = "yyyy-MM-dd";
17+
sut.SanitizeFormat(validFormat).ShouldBe(validFormat);
18+
}
19+
20+
[TestCase("")]
21+
[TestCase(" ")]
22+
[TestCase("\t")]
23+
public void SanitizeFormat_WithEmptyOrWhitespace_ThrowsFormatException(string invalidFormat)
24+
{
25+
var sut = new InputSanitizer();
26+
Action act = () => sut.SanitizeFormat(invalidFormat);
27+
act.ShouldThrowWithMessage<FormatException>("Format string cannot be empty.");
28+
}
29+
30+
[Test]
31+
public void SanitizeFormat_WithTooLongFormat_ThrowsFormatException()
32+
{
33+
var sut = new InputSanitizer();
34+
var longFormat = new string('x', 51);
35+
Action act = () => sut.SanitizeFormat(longFormat);
36+
act.ShouldThrowWithMessage<FormatException>("Format string too long: 'xxxxxxxxxxxxxxxxxxxx...'");
37+
}
38+
39+
[Test]
40+
public void SanitizeFormat_WithMaxValidLength_ReturnsInput()
41+
{
42+
var sut = new InputSanitizer();
43+
var maxLengthFormat = new string('x', 50);
44+
sut.SanitizeFormat(maxLengthFormat).ShouldBe(maxLengthFormat);
45+
}
46+
47+
[TestCase("\r", TestName = "SanitizeFormat_ControlChar_CR")]
48+
[TestCase("\n", TestName = "SanitizeFormat_ControlChar_LF")]
49+
[TestCase("\0", TestName = "SanitizeFormat_ControlChar_Null")]
50+
[TestCase("\x01", TestName = "SanitizeFormat_ControlChar_0x01")]
51+
[TestCase("\x1F", TestName = "SanitizeFormat_ControlChar_0x1F")]
52+
public void SanitizeFormat_WithControlCharacters_ThrowsFormatException(string controlChar)
53+
{
54+
var sut = new InputSanitizer();
55+
var formatWithControl = $"valid{controlChar}format";
56+
Action act = () => sut.SanitizeFormat(formatWithControl);
57+
act.ShouldThrowWithMessage<FormatException>("Format string contains invalid control characters");
58+
}
59+
60+
[Test]
61+
public void SanitizeFormat_WithTabCharacter_ReturnsInput()
62+
{
63+
var sut = new InputSanitizer();
64+
const string formatWithTab = "format\twith\ttab";
65+
sut.SanitizeFormat(formatWithTab).ShouldBe(formatWithTab);
66+
}
67+
68+
[TestCase("yyyy-MM-dd")]
69+
[TestCase("HH:mm:ss")]
70+
[TestCase("0.00")]
71+
[TestCase("C2")]
72+
[TestCase("X8")]
73+
[TestCase("format with spaces")]
74+
[TestCase("format-with-dashes")]
75+
[TestCase("format_with_underscores")]
76+
public void SanitizeFormat_WithValidFormats_ReturnsInput(string validFormat)
77+
{
78+
var sut = new InputSanitizer();
79+
sut.SanitizeFormat(validFormat).ShouldBe(validFormat);
80+
}
81+
}
82+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using GitVersion.Helpers;
2+
3+
namespace GitVersion.Tests.Helpers;
4+
5+
[TestFixture]
6+
public class NumericFormatterTests
7+
{
8+
[Test]
9+
public void Priority_ShouldBe1() => new NumericFormatter().Priority.ShouldBe(1);
10+
[Test]
11+
public void TryFormat_NullValue_ReturnsFalse()
12+
{
13+
var sut = new NumericFormatter();
14+
var result = sut.TryFormat(null, "n", out var formatted);
15+
result.ShouldBeFalse();
16+
formatted.ShouldBeEmpty();
17+
}
18+
19+
[TestCase("1234.5678", "n", "1,234.57")]
20+
[TestCase("1234.5678", "f2", "1234.57")]
21+
[TestCase("1234.5678", "f0", "1235")]
22+
[TestCase("1234.5678", "g", "1234.5678")]
23+
public void TryFormat_ValidFormats_ReturnsExpectedResult(string input, string format, string expected)
24+
{
25+
var sut = new NumericFormatter();
26+
var result = sut.TryFormat(input, format, out var formatted);
27+
result.ShouldBeTrue();
28+
formatted.ShouldBe(expected);
29+
}
30+
[Test]
31+
public void TryFormat_UnsupportedFormat_ReturnsFalse()
32+
{
33+
var sut = new NumericFormatter();
34+
var result = sut.TryFormat(1234.5678, "z", out var formatted);
35+
result.ShouldBeFalse();
36+
formatted.ShouldBeEmpty();
37+
}
38+
}

0 commit comments

Comments
 (0)