Skip to content
Draft
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
35 changes: 35 additions & 0 deletions src/GitVersion.Core.Tests/Formatting/BackwardCompatibilityTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
namespace GitVersion.Core.Tests.Formatting;

[TestFixture]
public class LegacyRegexPatternTests
{
[Test]
public void ExpandTokensRegex_ShouldParseLegacySemicolonSyntax()
{
const string input = "{CommitsSinceVersionSource:0000;;''}";

var matches = RegexPatterns.Common.ExpandTokensRegex().Matches(input);

matches.Count.ShouldBe(1);
var match = matches[0];
match.Groups["member"].Value.ShouldBe("CommitsSinceVersionSource");
match.Groups["format"].Success.ShouldBeTrue();
}

[Test]
public void ExpandTokensRegex_ShouldHandleMixedSyntax()
{
const string input = "{NewStyle:0000 ?? 'fallback'} {OldStyle:pos;neg;zero}";

var matches = RegexPatterns.Common.ExpandTokensRegex().Matches(input);

matches.Count.ShouldBe(2);

var newMatch = matches[0];
newMatch.Groups["member"].Value.ShouldBe("NewStyle");
newMatch.Groups["fallback"].Value.ShouldBe("fallback");

var oldMatch = matches[1];
oldMatch.Groups["member"].Value.ShouldBe("OldStyle");
}
}
2 changes: 1 addition & 1 deletion src/GitVersion.Core.Tests/Formatting/DateFormatterTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Globalization;
using GitVersion.Formatting;

namespace GitVersion.Tests.Formatting;
namespace GitVersion.Core.Tests.Formatting;

[TestFixture]
public class DateFormatterTests
Expand Down
163 changes: 163 additions & 0 deletions src/GitVersion.Core.Tests/Formatting/LegacyFormatterProblemTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
using GitVersion.Core.Tests.Helpers;
using GitVersion.Formatting;

namespace GitVersion.Core.Tests.Formatting;

[TestFixture]
public class LegacyFormatterProblemTests
{
private TestEnvironment environment;

[SetUp]
public void Setup() => environment = new TestEnvironment();

// ==========================================
// PROBLEM 1: Non-existent properties
// ==========================================

[Test]
[Category("Problem2")]
public void Problem2_NullValue_ShouldUseZeroSection()
{
var testObject = new { Value = (int?)null };
const string template = "{Value:positive;negative;zero}";
const string expected = "zero";

var actual = template.FormatWith(testObject, environment);
actual.ShouldBe(expected, "Null values should use zero section without transformation");
}

[Test]
[Category("Problem1")]
public void Problem1_MissingProperty_ShouldFailGracefully()
{
// Test tries to use {MajorMinorPatch} on SemanticVersion but that property doesn't exist
var semanticVersion = new SemanticVersion
{
Major = 1,
Minor = 2,
Patch = 3
};

const string template = "{MajorMinorPatch}"; // This property doesn't exist on SemanticVersion

// Currently this will throw or behave unexpectedly
// Should either throw meaningful error or handle gracefully
Assert.Throws<ArgumentException>(() => template.FormatWith(semanticVersion, environment));
}

// ==========================================
// PROBLEM 2: Double negative handling
// ==========================================

[Test]
[Category("Problem2")]
public void Problem2_NegativeValue_ShouldNotDoubleNegative()
{
var testObject = new { Value = -5 };
const string template = "{Value:positive;negative;zero}";

// EXPECTED: "negative" (just the literal text from section 2)
// ACTUAL: "-negative" (the negative sign from -5 plus the literal "negative")
const string expected = "negative";

var actual = template.FormatWith(testObject, environment);

// This will currently fail - we get "-negative" instead of "negative"
actual.ShouldBe(expected, "Negative values should use section text without the negative sign");
}

[Test]
[Category("Problem2")]
public void Problem2_PositiveValue_ShouldFormatCorrectly()
{
var testObject = new { Value = 5 };
const string template = "{Value:positive;negative;zero}";
const string expected = "positive";

var actual = template.FormatWith(testObject, environment);
actual.ShouldBe(expected);
}

[Test]
[Category("Problem2")]
public void Problem2_ZeroValue_ShouldUseZeroSection()
{
var testObject = new { Value = 0 };
const string template = "{Value:positive;negative;zero}";
const string expected = "zero";

var actual = template.FormatWith(testObject, environment);
actual.ShouldBe(expected);
}

// ==========================================
// PROBLEM 3: Insufficient formatting logic
// ==========================================

[Test]
[Category("Problem3")]
public void Problem3_NumericFormatting_AllSectionsShouldFormat()
{
// Test that numeric formatting works in ALL sections, not just first
var testObject = new { Value = -42 };
const string template = "{Value:0000;0000;0000}"; // All sections should pad with zeros

// EXPECTED: "0042" (absolute value 42, formatted with 0000 in negative section)
// ACTUAL: "0000" (literal text instead of formatted value)
const string expected = "0042";

var actual = template.FormatWith(testObject, environment);
actual.ShouldBe(expected, "Negative section should format the absolute value, not return literal");
}

[Test]
[Category("Problem3")]
public void Problem3_FirstSectionWorks_OthersDont()
{
// Demonstrate that first section works but others don't
var positiveObject = new { Value = 42 };
var negativeObject = new { Value = -42 };

const string template = "{Value:0000;WRONG;WRONG}";

// First section (positive) should work correctly
var positiveResult = template.FormatWith(positiveObject, environment);
positiveResult.ShouldBe("0042", "First section should format correctly");

// Second section (negative) should return literal when invalid format provided
var negativeResult = template.FormatWith(negativeObject, environment);
// Invalid format "WRONG" should return literal to give user feedback about their error
negativeResult.ShouldBe("WRONG", "Invalid format should return literal to indicate user error");
}

// ==========================================
// VERIFY #4654 FIX STILL WORKS
// ==========================================

[Test]
[Category("Issue4654")]
public void Issue4654_LegacySyntax_ShouldStillWork()
{
// Verify the original #4654 fix still works
var testObject = new { CommitsSinceVersionSource = 2 };
const string template = "{CommitsSinceVersionSource:0000;;''}";
const string expected = "0002";

var actual = template.FormatWith(testObject, environment);
actual.ShouldBe(expected, "Issue #4654 fix must be preserved");
}

[Test]
[Category("Issue4654")]
public void Issue4654_ZeroValue_ShouldUseEmptyString()
{
// Zero values should use the third section (empty string)
var testObject = new { CommitsSinceVersionSource = 0 };
const string template = "{CommitsSinceVersionSource:0000;;''}";
const string expected = "";

var actual = template.FormatWith(testObject, environment);
actual.ShouldBe(expected, "Zero values should use third section (empty)");
}
}
138 changes: 138 additions & 0 deletions src/GitVersion.Core.Tests/Formatting/LegacyFormattingSyntaxTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
using System.Globalization;
using GitVersion.Core.Tests.Helpers;
using GitVersion.Formatting;

namespace GitVersion.Core.Tests.Formatting;

[TestFixture]
public class LegacyFormattingSyntaxTests
{
[Test]
public void FormatWith_LegacyZeroFallbackSyntax_ShouldWork()
{
var semanticVersion = new SemanticVersion
{
Major = 6,
Minor = 13,
Patch = 54,
PreReleaseTag = new SemanticVersionPreReleaseTag("gv6", 1, true),
BuildMetaData = new SemanticVersionBuildMetaData()
{
Branch = "feature/gv6",
VersionSourceSha = "versionSourceSha",
Sha = "489a0c0ab425214def918e36399f3cc3c9a9c42d",
ShortSha = "489a0c0",
CommitsSinceVersionSource = 2,
CommitDate = DateTimeOffset.Parse("2025-08-12", CultureInfo.InvariantCulture)
}
};

const string template = "{MajorMinorPatch}{PreReleaseLabelWithDash}{CommitsSinceVersionSource:0000;;''}";
const string expected = "6.13.54-gv60002";

var actual = template.FormatWith(semanticVersion, new TestEnvironment());

actual.ShouldBe(expected);
}

[Test]
public void FormatWith_LegacyThreeSectionSyntax_ShouldWork()
{
var testObject = new { Value = -5 };
const string template = "{Value:positive;negative;zero}";
const string expected = "negative";

var actual = template.FormatWith(testObject, new TestEnvironment());

actual.ShouldBe(expected);
}

[Test]
public void FormatWith_LegacyTwoSectionSyntax_ShouldWork()
{
var testObject = new { Value = -10 };
const string template = "{Value:positive;negative}";
const string expected = "negative";

var actual = template.FormatWith(testObject, new TestEnvironment());

actual.ShouldBe(expected);
}

[Test]
public void FormatWith_LegacyZeroValue_ShouldUseThirdSection()
{
var testObject = new { Value = 0 };
const string template = "{Value:pos;neg;ZERO}";
const string expected = "ZERO";

var actual = template.FormatWith(testObject, new TestEnvironment());

actual.ShouldBe(expected);
}

[Test]
public void FormatWith_MixedLegacyAndNewSyntax_ShouldWork()
{
var testObject = new
{
OldStyle = 0,
NewStyle = 42,
RegularProp = "test"
};
const string template = "{OldStyle:pos;neg;''}{NewStyle:0000 ?? 'fallback'}{RegularProp}";
const string expected = "0042test";

var actual = template.FormatWith(testObject, new TestEnvironment());

actual.ShouldBe(expected);
}

[Test]
public void FormatWith_LegacyWithStandardFormatSpecifiers_ShouldWork()
{
var testObject = new { Amount = 1234.56 };
const string template = "{Amount:C2;(C2);'No Amount'}";
const string expected = "¤1,234.56";

var actual = template.FormatWith(testObject, new TestEnvironment());

actual.ShouldBe(expected);
}

[Test]
public void FormatWith_Issue4654ExactCase_ShouldWork()
{
var semanticVersion = new SemanticVersion
{
Major = 6,
Minor = 13,
Patch = 54,
PreReleaseTag = new SemanticVersionPreReleaseTag("gv6", 1, true),
BuildMetaData = new SemanticVersionBuildMetaData("Branch.feature-gv6")
{
CommitsSinceVersionSource = 2
}
};

var mainBranchVersion = new SemanticVersion
{
Major = 6,
Minor = 13,
Patch = 54,
PreReleaseTag = new SemanticVersionPreReleaseTag(string.Empty, 0, true),
BuildMetaData = new SemanticVersionBuildMetaData()
{
CommitsSinceVersionSource = 0
}
};

const string template = "{MajorMinorPatch}{PreReleaseLabelWithDash}{CommitsSinceVersionSource:0000;;''}";

var featureResult = template.FormatWith(semanticVersion, new TestEnvironment());
featureResult.ShouldBe("6.13.54-gv60002");

var mainResult = template.FormatWith(mainBranchVersion, new TestEnvironment());
mainResult.ShouldBe("6.13.54");
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using GitVersion.Formatting;

namespace GitVersion.Tests.Formatting;
namespace GitVersion.Core.Tests.Formatting;

[TestFixture]
public class StringFormatterTests
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Globalization;
using GitVersion.Formatting;

namespace GitVersion.Tests.Formatting;
namespace GitVersion.Core.Tests.Formatting;

[TestFixture]
public class ValueFormatterTests
Expand Down
Loading
Loading