diff --git a/Axuno.BackgroundTask.Test/Axuno.BackgroundTask.Tests.csproj b/Axuno.BackgroundTask.Test/Axuno.BackgroundTask.Tests.csproj
index 18a2f357..f65b0342 100644
--- a/Axuno.BackgroundTask.Test/Axuno.BackgroundTask.Tests.csproj
+++ b/Axuno.BackgroundTask.Test/Axuno.BackgroundTask.Tests.csproj
@@ -6,6 +6,7 @@
+
all
diff --git a/Axuno.Tools.Tests/Axuno.Tools.Tests.csproj b/Axuno.Tools.Tests/Axuno.Tools.Tests.csproj
index c484cedf..4bc70ff6 100644
--- a/Axuno.Tools.Tests/Axuno.Tools.Tests.csproj
+++ b/Axuno.Tools.Tests/Axuno.Tools.Tests.csproj
@@ -14,6 +14,7 @@
+
all
diff --git a/League.Demo/League.Demo.csproj b/League.Demo/League.Demo.csproj
index ef8f7d61..b10c6770 100644
--- a/League.Demo/League.Demo.csproj
+++ b/League.Demo/League.Demo.csproj
@@ -4,6 +4,8 @@
League.Demo
net6.0
en
+
+ en;de
diff --git a/League.Tests/League.Tests.csproj b/League.Tests/League.Tests.csproj
index 3f804fd3..a35522e4 100644
--- a/League.Tests/League.Tests.csproj
+++ b/League.Tests/League.Tests.csproj
@@ -7,6 +7,7 @@
+
diff --git a/League/Models/MatchViewModels/EnterResultViewModel.cs b/League/Models/MatchViewModels/EnterResultViewModel.cs
index 7c4c3f46..2bb61fa3 100644
--- a/League/Models/MatchViewModels/EnterResultViewModel.cs
+++ b/League/Models/MatchViewModels/EnterResultViewModel.cs
@@ -12,7 +12,6 @@
using TournamentManager.DAL;
using TournamentManager.DAL.EntityClasses;
using TournamentManager.ExtensionMethods;
-using TournamentManager.Match;
using TournamentManager.ModelValidators;
namespace League.Models.MatchViewModels;
@@ -71,12 +70,12 @@ public void MapEntityToFormFields()
Match.Sets.Sort((int)SetFieldIndex.SequenceNo, ListSortDirection.Ascending);
foreach (var set in Match.Sets)
{
- Sets.Add(new PointResultNullable(set.HomeBallPoints, set.GuestBallPoints));
+ Sets.Add(new PointResult(set.HomeBallPoints, set.GuestBallPoints));
}
while (Sets.Count < _maxNumberOfSets)
{
- Sets.Add(new PointResultNullable(null, null));
+ Sets.Add(new PointResult(default, default(int?)));
}
Id = Match.Id;
@@ -99,18 +98,11 @@ public void MapFormFieldsToEntity()
// Add sets to entity
Match.Sets.Clear(true);
- for (var i = 0; i < Sets.Count; i++)
- {
- // sets where home and guest ball points are NULL, will be ignored
- if (Sets[i].Home.HasValue || Sets[i].Guest.HasValue)
- {
- // home or guest NULL values are invalidated with -1
- Match.Sets.Add(new SetEntity { MatchId = Match.Id, SequenceNo = i + 1, HomeBallPoints = Sets[i].Home ?? -1, GuestBallPoints = Sets[i].Guest ?? -1 });
- }
- }
+ // sets where home or guest ball points are NULL will be ignored
+ Match.Sets.Add(Match.Id, Sets);
// point calculation must run before validation because of tie-break handling
- Match.Sets.CalculateSetPoints(Round!.SetRule, Round.MatchRule);
+ _ = Match.Sets.CalculateSetPoints(Round!.SetRule, Round.MatchRule);
Match.Remarks = Remarks;
Match.ChangeSerial++;
Match.IsComplete = true;
@@ -147,7 +139,7 @@ public void MapFormFieldsToEntity()
[MaxLength(2000)]
public string? Remarks { get; set; }
- public List Sets { get; set; } = new();
+ public List Sets { get; set; } = new();
#endregion
diff --git a/TournamentManager/DAL/DatabaseGeneric/CustomExtensions/MatchEntity.cs b/TournamentManager/DAL/DatabaseGeneric/CustomExtensions/MatchEntity.cs
index f6aab996..28f0ffb4 100644
--- a/TournamentManager/DAL/DatabaseGeneric/CustomExtensions/MatchEntity.cs
+++ b/TournamentManager/DAL/DatabaseGeneric/CustomExtensions/MatchEntity.cs
@@ -91,8 +91,7 @@ public void SetVenueId(long? venueId)
{
if (venueId == VenueId) return;
- if (!OrigVenueId.HasValue)
- OrigVenueId = VenueId;
+ OrigVenueId ??= VenueId;
VenueId = venueId;
diff --git a/TournamentManager/DAL/DatabaseGeneric/CustomExtensions/SetEntity.cs b/TournamentManager/DAL/DatabaseGeneric/CustomExtensions/SetEntity.cs
index 0aec9239..46fdc0c8 100644
--- a/TournamentManager/DAL/DatabaseGeneric/CustomExtensions/SetEntity.cs
+++ b/TournamentManager/DAL/DatabaseGeneric/CustomExtensions/SetEntity.cs
@@ -9,49 +9,6 @@ namespace TournamentManager.DAL.EntityClasses
{
public partial class SetEntity
{
- ///
- /// Assigns "set points" to the .
- /// The property will be set to
- ///
- /// The with the rules to apply.
- public void CalculateSetPoints(SetRuleEntity setRule)
- {
- IsOverruled = false;
- if (HomeBallPoints > GuestBallPoints)
- {
- HomeSetPoints = setRule.PointsSetWon;
- GuestSetPoints = setRule.PointsSetLost;
- }
- else if (HomeBallPoints < GuestBallPoints)
- {
- HomeSetPoints = setRule.PointsSetLost;
- GuestSetPoints = setRule.PointsSetWon;
- }
- else
- {
- HomeSetPoints = GuestSetPoints = setRule.PointsSetTie;
- }
- }
-
- ///
- /// Sets home/guest ball points and home/guest set points.
- /// The property will be set to ,
- /// while will be set to .
- ///
- ///
- ///
- ///
- ///
- public void Overrule(int homeBallPoints, int guestBallPoints, int homeSetPoints, int guestSetPoints)
- {
- HomeBallPoints = homeBallPoints;
- GuestBallPoints = guestBallPoints;
- HomeSetPoints = homeSetPoints;
- GuestSetPoints = guestSetPoints;
- IsTieBreak = false;
- IsOverruled = true;
- }
-
protected override void OnBeforeEntitySave()
{
var now = DateTime.UtcNow;
diff --git a/TournamentManager/TournamentManager.Tests/ExtensionMethods/MatchEntityExtensionTests.cs b/TournamentManager/TournamentManager.Tests/ExtensionMethods/MatchEntityExtensionTests.cs
new file mode 100644
index 00000000..b2223874
--- /dev/null
+++ b/TournamentManager/TournamentManager.Tests/ExtensionMethods/MatchEntityExtensionTests.cs
@@ -0,0 +1,124 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using FluentAssertions;
+using TournamentManager.DAL.EntityClasses;
+using TournamentManager.ExtensionMethods;
+using TournamentManager.ModelValidators;
+using TournamentManager.MultiTenancy;
+
+namespace TournamentManager.Tests.ExtensionMethods;
+
+[TestFixture]
+public class MatchEntityExtensionTests
+{
+ [Test]
+ public void Calc_With_No_Sets_Should_Not_Throw()
+ {
+ var matchRule = GetMatchRule_NoTieBreakRule();
+ var setRule = GetSetRule();
+ var match = new MatchEntity(1234);
+
+ Assert.That(del: () => _ = match.Sets.CalculateSetPoints(setRule, matchRule), Throws.Nothing);
+ }
+
+ [TestCase("25:1 25:2 25:3", 3, 0)]
+ [TestCase("25:1 25:2 1:25 1:25 15:1", 3, 0)]
+ [TestCase("1:25 2:25 2:25", 0, 3)]
+ [TestCase("1:25 2:25 25:13 25:1 1:15", 0, 3)]
+ [TestCase("1:25 25:1", 1, 1)]
+ public void Calc_MatchPoints_No_TieBreakRule(string setResults, int expectedHome, int expectedGuest)
+ {
+ // Note: Test cases are not validated against the rules here, but they are valid.
+ // Validation is tested in ModelValidator tests
+
+ var matchRule = GetMatchRule_NoTieBreakRule();
+ var setRule = GetSetRule();
+ var match = new MatchEntity(1234);
+ match.Sets.Add(match.Id, setResults);
+ _ = match.Sets.CalculateSetPoints(setRule, matchRule);
+ _ = match.CalculateMatchPoints(matchRule);
+
+ var pointResult = new PointResult(match.HomePoints, match.GuestPoints);
+ var expectedResult = new PointResult(expectedHome, expectedGuest);
+
+ pointResult.Should().BeEquivalentTo(expectedResult);
+ }
+
+ [TestCase("25:1 25:2 25:3", 3, 0)]
+ [TestCase("25:1 25:2 1:25 1:25 15:1", 2, 1)]
+ [TestCase("1:25 2:25 2:25", 0, 3)]
+ [TestCase("1:25 2:25 25:13 25:1 1:15", 1, 2)]
+ [TestCase("1:25 25:1", 1, 1)]
+ public void Calc_MatchPoints_With_TieBreakRule(string setResults, int expectedHome, int expectedGuest)
+ {
+ // Note: Test cases are not validated against the rules here, but they are valid.
+ // Validation is tested in ModelValidator tests
+
+ var matchRule = GetMatchRule_TieBreakRule();
+ var setRule = GetSetRule();
+ var match = new MatchEntity(1234);
+ match.Sets.Add(match.Id, setResults);
+ _ = match.Sets.CalculateSetPoints(setRule, matchRule);
+ _ = match.CalculateMatchPoints(matchRule);
+
+ var pointResult = new PointResult(match.HomePoints, match.GuestPoints);
+ var expectedResult = new PointResult(expectedHome, expectedGuest);
+
+ pointResult.Should().BeEquivalentTo(expectedResult);
+ }
+
+ [Test]
+ public void Calc_MatchPoints_With_TieBreakRule_Throws()
+ {
+ var matchRule = GetMatchRule_TieBreakRule();
+ var setRule = GetSetRule();
+ var match = new MatchEntity(1234);
+ match.Sets.Add(match.Id, "25:1 25:2 1:25 1:25 15:1");
+ _ = match.Sets.CalculateSetPoints(setRule, matchRule);
+
+ // This will trigger an exception, because the set points are tie
+ var lastSet = match.Sets.Last();
+ lastSet.HomeSetPoints = lastSet.GuestSetPoints = 0;
+
+ Assert.That(del: () => _ = match.CalculateMatchPoints(matchRule), Throws.InvalidOperationException);
+ }
+
+ private static MatchRuleEntity GetMatchRule_NoTieBreakRule()
+ {
+ return new MatchRuleEntity {
+ BestOf = true,
+ NumOfSets = 3,
+ PointsMatchWon = 3,
+ PointsMatchLost = 0,
+ PointsMatchTie = 1
+ };
+ }
+
+ private static MatchRuleEntity GetMatchRule_TieBreakRule()
+ {
+ return new MatchRuleEntity {
+ BestOf = true,
+ NumOfSets = 3,
+ PointsMatchWon = 3,
+ PointsMatchLost = 0,
+ PointsMatchWonAfterTieBreak = 2,
+ PointsMatchLostAfterTieBreak = 1,
+ PointsMatchTie = 1
+ };
+ }
+
+ private static SetRuleEntity GetSetRule()
+ {
+ return new SetRuleEntity {
+ NumOfPointsToWinRegular = 25,
+ NumOfPointsToWinTiebreak = 15,
+ PointsDiffToWinRegular = 2,
+ PointsDiffToWinTiebreak = 2,
+ PointsSetWon = 1,
+ PointsSetLost = 0,
+ PointsSetTie = 0
+ };
+ }
+}
diff --git a/TournamentManager/TournamentManager.Tests/DAL_CustomExtensions/MatchRuleEntityExtensionTests.cs b/TournamentManager/TournamentManager.Tests/ExtensionMethods/MatchRuleEntityExtensionTests.cs
similarity index 58%
rename from TournamentManager/TournamentManager.Tests/DAL_CustomExtensions/MatchRuleEntityExtensionTests.cs
rename to TournamentManager/TournamentManager.Tests/ExtensionMethods/MatchRuleEntityExtensionTests.cs
index 961c8256..db79500e 100644
--- a/TournamentManager/TournamentManager.Tests/DAL_CustomExtensions/MatchRuleEntityExtensionTests.cs
+++ b/TournamentManager/TournamentManager.Tests/ExtensionMethods/MatchRuleEntityExtensionTests.cs
@@ -1,7 +1,10 @@
-using NUnit.Framework;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
using TournamentManager.DAL.EntityClasses;
-namespace TournamentManager.Tests.DAL_CustomExtensions;
+namespace TournamentManager.Tests.ExtensionMethods;
[TestFixture]
public class MatchRuleEntityExtensionTests
@@ -11,7 +14,7 @@ public class MatchRuleEntityExtensionTests
[TestCase(true, 2, 3)]
public void Calculate_Max_Number_Of_Sets(bool isBestOf, int numOfSets, int expected)
{
- var rule = new MatchRuleEntity {BestOf = isBestOf, NumOfSets = numOfSets};
+ var rule = new MatchRuleEntity { BestOf = isBestOf, NumOfSets = numOfSets };
Assert.AreEqual(expected, rule.MaxNumOfSets());
}
}
diff --git a/TournamentManager/TournamentManager.Tests/DAL_CustomExtensions/SetEntityExtensionTests.cs b/TournamentManager/TournamentManager.Tests/ExtensionMethods/SetEntityExtensionTests.cs
similarity index 86%
rename from TournamentManager/TournamentManager.Tests/DAL_CustomExtensions/SetEntityExtensionTests.cs
rename to TournamentManager/TournamentManager.Tests/ExtensionMethods/SetEntityExtensionTests.cs
index 7638ef9e..dd3aecd6 100644
--- a/TournamentManager/TournamentManager.Tests/DAL_CustomExtensions/SetEntityExtensionTests.cs
+++ b/TournamentManager/TournamentManager.Tests/ExtensionMethods/SetEntityExtensionTests.cs
@@ -5,18 +5,18 @@
using TournamentManager.DAL.EntityClasses;
using TournamentManager.ExtensionMethods;
-namespace TournamentManager.Tests.DAL_CustomExtensions;
+namespace TournamentManager.Tests.ExtensionMethods;
[TestFixture]
public class SetEntityExtensionTests
{
- [TestCase(25, 1, 3,1)]
+ [TestCase(25, 1, 3, 1)]
[TestCase(1, 25, 1, 3)]
[TestCase(25, 25, 2, 2)]
public void Calculate_Set_Points(int homeBallPts, int guestBallPts, int expectedHomeSetPts, int expectedGuestSetPts)
{
- var set = new SetEntity {HomeBallPoints = homeBallPts, GuestBallPoints = guestBallPts};
- var setRule = new SetRuleEntity { PointsSetWon = 3, PointsSetLost = 1, PointsSetTie = 2};
+ var set = new SetEntity { HomeBallPoints = homeBallPts, GuestBallPoints = guestBallPts };
+ var setRule = new SetRuleEntity { PointsSetWon = 3, PointsSetLost = 1, PointsSetTie = 2 };
set.CalculateSetPoints(setRule);
Assert.Multiple(() =>
{
@@ -30,7 +30,7 @@ public void Calculate_Set_Points(int homeBallPts, int guestBallPts, int expected
[TestCase(1, 25, 1, 2)]
public void Overrule_Set_Points(int homeBallPts, int guestBallPts, int homeSetPts, int guestSetPts)
{
- var set = new SetEntity { HomeBallPoints = homeBallPts, GuestBallPoints = guestBallPts, IsTieBreak = true, IsOverruled = false};
+ var set = new SetEntity { HomeBallPoints = homeBallPts, GuestBallPoints = guestBallPts, IsTieBreak = true, IsOverruled = false };
set.Overrule(homeBallPts, guestBallPts, homeSetPts, guestSetPts);
Assert.Multiple(() =>
{
@@ -40,4 +40,4 @@ public void Overrule_Set_Points(int homeBallPts, int guestBallPts, int homeSetPt
Assert.IsTrue(set.IsOverruled);
});
}
-}
\ No newline at end of file
+}
diff --git a/TournamentManager/TournamentManager.Tests/ExtensionMethods/SetEntityListExtensionTests.cs b/TournamentManager/TournamentManager.Tests/ExtensionMethods/SetEntityListExtensionTests.cs
index a9d8909c..0840e399 100644
--- a/TournamentManager/TournamentManager.Tests/ExtensionMethods/SetEntityListExtensionTests.cs
+++ b/TournamentManager/TournamentManager.Tests/ExtensionMethods/SetEntityListExtensionTests.cs
@@ -1,8 +1,5 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
-using System.Text;
-using NuGet.Frameworks;
using NUnit.Framework;
using TournamentManager.DAL.EntityClasses;
using TournamentManager.ExtensionMethods;
@@ -12,96 +9,95 @@ namespace TournamentManager.Tests.ExtensionMethods;
[TestFixture]
public class SetEntityListExtensionTests
{
- [TestCase(25, 1, 25, 1, 25, 1, 9, 3)]
- [TestCase(1, 25, 1, 25, 1, 25, 3, 9)]
- [TestCase(25, 25, 25, 25, 25, 25, 6, 6)]
- public void Calculate_Set_Points_NoTieBreak(int s1HomeBall, int s1GuestBall, int s2HomeBall, int s2GuestBall, int s3HomeBall, int s3GuestBall, int expectedHomeSetPts, int expectedGuestSetPts)
+ [TestCase("25:1 25:1 25:1", 9, 3)]
+ [TestCase("1:25 1:25 1:25", 3, 9)]
+ [TestCase("25:25 25:25 25:25",6, 6)]
+ public void Calculate_Set_Points_NoTieBreak(string setResults, int expectedHomeSetPts, int expectedGuestSetPts)
{
- var sets = new List
- {
- new() {HomeBallPoints = s1HomeBall, GuestBallPoints = s1GuestBall},
- new() {HomeBallPoints = s2HomeBall, GuestBallPoints = s2GuestBall},
- new() {HomeBallPoints = s3HomeBall, GuestBallPoints = s3GuestBall}
- };
+ var sets = new List { { -1, setResults } };
+
var setRule = new SetRuleEntity { PointsSetWon = 3, PointsSetLost = 1, PointsSetTie = 2 };
var matchRule = new MatchRuleEntity { BestOf = false, NumOfSets = 3 };
sets.CalculateSetPoints(setRule, matchRule);
Assert.Multiple(() =>
{
- Assert.AreEqual(expectedHomeSetPts, sets.GetSetPoints().Home);
- Assert.AreEqual(expectedGuestSetPts, sets.GetSetPoints().Guest);
+ Assert.That(expectedHomeSetPts, Is.EqualTo(sets.GetSetPoints().Home));
+ Assert.That(expectedGuestSetPts, Is.EqualTo(sets.GetSetPoints().Guest));
});
}
- [TestCase(25, 1, 1, 25, 25, 1, 7, 5, true)]
- [TestCase(25, 1, 25, 1, 25, 1, 9, 3, false)]
- [TestCase(25, 25, 25, 25, 25, 25, 0, 0, false)]
- public void Calculate_Set_Points_TieBreak(int s1HomeBall, int s1GuestBall, int s2HomeBall, int s2GuestBall, int s3HomeBall, int s3GuestBall, int expectedHomeSetPts, int expectedGuestSetPts, bool expectedTieBreak)
+ [TestCase("25:1 1:25 25:1", 7, 5, true)]
+ [TestCase("25:1 25:1 25:1", 9, 3, false)]
+ [TestCase("25:25 25:25 25:25", 0, 0, false)]
+ public void Calculate_Set_Points_TieBreak(string setResults, int expectedHomeSetPts, int expectedGuestSetPts, bool expectedTieBreak)
{
- var sets = new List
- {
- new() {HomeBallPoints = s1HomeBall, GuestBallPoints = s1GuestBall},
- new() {HomeBallPoints = s2HomeBall, GuestBallPoints = s2GuestBall},
- new() {HomeBallPoints = s3HomeBall, GuestBallPoints = s3GuestBall}
- };
+ var sets = new List { { -1, setResults } };
+
var setRule = new SetRuleEntity { PointsSetWon = 3, PointsSetLost = 1, PointsSetTie = 0 };
var matchRule = new MatchRuleEntity { BestOf = true, NumOfSets = 2 };
sets.CalculateSetPoints(setRule, matchRule);
Assert.Multiple(() =>
{
- Assert.AreEqual(expectedHomeSetPts, sets.GetSetPoints().Home);
- Assert.AreEqual(expectedGuestSetPts, sets.GetSetPoints().Guest);
- Assert.AreEqual(expectedTieBreak, sets.Last().IsTieBreak);
+ Assert.That(expectedHomeSetPts, Is.EqualTo(sets.GetSetPoints().Home));
+ Assert.That(expectedGuestSetPts, Is.EqualTo(sets.GetSetPoints().Guest));
+ Assert.That(expectedTieBreak, Is.EqualTo(sets.Last().IsTieBreak));
});
}
+ [Test]
+ public void Sequence_Of_Sets_Is_As_Played()
+ {
+ var sets = new List { { -1, "25:1 25:2 25:3" } };
+
+ Assert.That(sets.Count, Is.EqualTo(3));
+ Assert.That(sets.All(s => s.SequenceNo == s.GuestBallPoints));
+ }
+
+ [Test]
+ public void All_Sets_Contain_MatchId()
+ {
+ var sets = new List { { 12345, "25:1 25:2 25:3" } };
+
+ Assert.That(sets.Count, Is.EqualTo(3));
+ Assert.That(sets.All(s => s.MatchId == 12345));
+ }
+
[TestCase(25, 1, 5, 4)]
[TestCase(25, 1, 2, 0)]
[TestCase(1, 25, 1, 2)]
public void Overrule_Set_Points(int homeBallPts, int guestBallPts, int homeSetPts, int guestSetPts)
{
- var set = new SetEntity { HomeBallPoints = homeBallPts, GuestBallPoints = guestBallPts, IsTieBreak = true, IsOverruled = false};
+ var set = new SetEntity {IsTieBreak = true} ;
set.Overrule(homeBallPts, guestBallPts, homeSetPts, guestSetPts);
Assert.Multiple(() =>
{
- Assert.AreEqual(homeSetPts, set.HomeSetPoints);
- Assert.AreEqual(guestSetPts, set.GuestSetPoints);
- Assert.IsFalse(set.IsTieBreak);
- Assert.IsTrue(set.IsOverruled);
+ Assert.That(homeSetPts, Is.EqualTo(set.HomeSetPoints));
+ Assert.That(guestSetPts, Is.EqualTo(set.GuestSetPoints));
+ Assert.That(set.IsTieBreak, Is.False); // reset by overrule
+ Assert.That(set.IsOverruled, Is.True); // set by overrule
});
}
- [TestCase(25, 1, 25, 1, 25, 1, 3, 0)]
- [TestCase(1, 25, 1, 25, 1, 25, 0, 3)]
- [TestCase(25, 25, 25, 25, 25, 25, 0, 0)]
- public void Get_Sets_Won(int s1HomeBall, int s1GuestBall, int s2HomeBall, int s2GuestBall, int s3HomeBall, int s3GuestBall, int expectedWonHome, int expectedWonGuest)
+ [TestCase("25:1 25:1 25:1", 3, 0)]
+ [TestCase("1:25 1:25 1:25", 0, 3)]
+ [TestCase("25:25 25:25 25:25", 0, 0)]
+ public void Get_Sets_Won(string setResults, int expectedWonHome, int expectedWonGuest)
{
- var sets = new List
- {
- new() {HomeBallPoints = s1HomeBall, GuestBallPoints = s1GuestBall},
- new() {HomeBallPoints = s2HomeBall, GuestBallPoints = s2GuestBall},
- new() {HomeBallPoints = s3HomeBall, GuestBallPoints = s3GuestBall}
- };
+ var sets = new List { { -1, setResults } };
Assert.Multiple(() =>
{
- Assert.AreEqual(expectedWonHome, sets.GetSetsWon().Home);
- Assert.AreEqual(expectedWonGuest, sets.GetSetsWon().Guest);
- Assert.AreEqual(expectedWonHome > expectedWonGuest ? expectedWonHome : expectedWonGuest, sets.MaxBestOf());
+ Assert.That(expectedWonHome, Is.EqualTo(sets.GetSetsWon().Home));
+ Assert.That(expectedWonGuest, Is.EqualTo(sets.GetSetsWon().Guest));
+ Assert.That(expectedWonHome > expectedWonGuest ? expectedWonHome : expectedWonGuest, Is.EqualTo(sets.MaxBestOf()));
});
}
- [TestCase(0, 0, 0, 0, 0, 0, 0)]
- [TestCase(25, 1, 25, 1, 25, 1, 78)]
- public void Get_Sets_Total_Ball_Points(int s1HomeBall, int s1GuestBall, int s2HomeBall, int s2GuestBall,
- int s3HomeBall, int s3GuestBall, int expected)
+ [TestCase("0:0 0:0 0:0", 0)]
+ [TestCase("25:1 25:1 25:1", 78)]
+ public void Get_Sets_Total_Ball_Points(string setResults, int expected)
{
- var sets = new List
- {
- new() {HomeBallPoints = s1HomeBall, GuestBallPoints = s1GuestBall},
- new() {HomeBallPoints = s2HomeBall, GuestBallPoints = s2GuestBall},
- new() {HomeBallPoints = s3HomeBall, GuestBallPoints = s3GuestBall}
- };
+ var sets = new List { { -1, setResults } };
- Assert.AreEqual(expected, sets.GetTotalBallPoints());
+ Assert.That(expected, Is.EqualTo(sets.GetTotalBallPoints()));
}
-}
\ No newline at end of file
+}
diff --git a/TournamentManager/TournamentManager.Tests/PointResultTests.cs b/TournamentManager/TournamentManager.Tests/PointResultTests.cs
new file mode 100644
index 00000000..4acca233
--- /dev/null
+++ b/TournamentManager/TournamentManager.Tests/PointResultTests.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using FluentAssertions;
+using NUnit.Framework;
+
+namespace TournamentManager.Tests;
+
+[TestFixture]
+public class PointResultTests
+{
+ [Test]
+ public void Create_Result_From_String_Containing_Only_Separator()
+ {
+ var pointResult = new PointResult("-", '-');
+ new PointResult(null, null) { PointsSeparator = '-' }.Should().BeEquivalentTo(pointResult);
+ }
+
+ [TestCase(12, 25)]
+ [TestCase(null, null)]
+ public void Create_Result_From_Nullable_Int(int? home, int? guest)
+ {
+ var pointResult = new PointResult(home, guest);
+
+ Assert.That(pointResult.Home == home);
+ Assert.That(pointResult.Guest == guest);
+ Assert.That(pointResult.ToString(), Is.EqualTo($"{(home != null ? home.Value : "-")}:{(guest != null ? guest.Value : "-")}"));
+ }
+
+ [Test]
+ public void Add_Integer_Results()
+ {
+ var pointResult1 = new PointResult("1:25");
+ var pointResult2 = new PointResult("25:1");
+
+ var result = pointResult1 + pointResult2;
+
+ Assert.That(result is { Home: 26, Guest: 26 });
+ }
+
+ [Test]
+ public void Add_Null_Results()
+ {
+ var pointResult1 = new PointResult("1:25");
+ var pointResult2 = new PointResult(null, null);
+
+ var result = pointResult1 + pointResult2;
+
+ Assert.That(result is { Home: 1, Guest: 25 });
+ }
+
+ [Test]
+ public void Subtract_Integer_Results()
+ {
+ var pointResult1 = new PointResult("25:23");
+ var pointResult2 = new PointResult("23:21");
+
+ var result = pointResult1 - pointResult2;
+
+ Assert.That(result is { Home: 2, Guest: 2 });
+ }
+
+ [Test]
+ public void Compare_Results_For_Equality()
+ {
+ var pointResult1 = new PointResult("25:18");
+ var pointResult2 = new PointResult("25:18");
+
+ Assert.That(pointResult1 == pointResult2, Is.True);
+ Assert.That(pointResult1 != pointResult2, Is.False);
+ }
+
+ [TestCase("25:0", "25:1", true)]
+ [TestCase("0:25", "25:0", false)]
+ [TestCase("0:25", "1:25", false)]
+ [TestCase("4:25", "1:25", true)]
+ [TestCase("25:4", "25:1", false)]
+ public void Compare_Results_A_more_than_B(string a, string b, bool expected)
+ {
+ var pointResult1 = new PointResult(a);
+ var pointResult2 = new PointResult(b);
+
+ Assert.That(pointResult1 > pointResult2, Is.EqualTo(expected));
+ Assert.That(pointResult1 < pointResult2, Is.EqualTo(!expected));
+ }
+
+ [Test]
+ public void Compare_Results_Null_Results()
+ {
+ var pointResult = new PointResult(25, 1);
+
+ Assert.That(pointResult.CompareTo(null), Is.EqualTo(1));
+ Assert.That(pointResult.Compare(null, pointResult), Is.EqualTo(-1));
+ Assert.That(pointResult.Compare(null, null), Is.EqualTo(0));
+ }
+}
diff --git a/TournamentManager/TournamentManager.Tests/TournamentManager.Tests.csproj b/TournamentManager/TournamentManager.Tests/TournamentManager.Tests.csproj
index d6f9dd4e..18fcde02 100644
--- a/TournamentManager/TournamentManager.Tests/TournamentManager.Tests.csproj
+++ b/TournamentManager/TournamentManager.Tests/TournamentManager.Tests.csproj
@@ -6,6 +6,7 @@
+
diff --git a/TournamentManager/TournamentManager/ExtensionMethods/CloneHelperExtensions.cs b/TournamentManager/TournamentManager/ExtensionMethods/EntityCoreExtensions.cs
similarity index 69%
rename from TournamentManager/TournamentManager/ExtensionMethods/CloneHelperExtensions.cs
rename to TournamentManager/TournamentManager/ExtensionMethods/EntityCoreExtensions.cs
index 3c38e096..4a5c0a06 100644
--- a/TournamentManager/TournamentManager/ExtensionMethods/CloneHelperExtensions.cs
+++ b/TournamentManager/TournamentManager/ExtensionMethods/EntityCoreExtensions.cs
@@ -1,20 +1,15 @@
using System.IO;
using System.Runtime.Serialization;
-using System.Runtime.Serialization.Formatters.Binary;
using SD.LLBLGen.Pro.ORMSupportClasses;
namespace TournamentManager.ExtensionMethods;
///
-/// For cloning an Entity and all related entities, i.e. the whole graph
+/// Extension methods for types implementing .
///
-public static class CloneHelperExtensions
+public static class EntityCoreExtensions
{
- static CloneHelperExtensions()
- {
- }
-
- public static void CloneEntity(T sourceObject, out T targetObject) where T : class, IEntityCore
+ public static void CloneEntity(this IEntityCore sourceObject, out T targetObject) where T : class, IEntityCore
{
var ms = new MemoryStream();
var bf = new DataContractSerializer(typeof(T));
@@ -35,4 +30,4 @@ public static void ResetEntityAsNew(this IEntityCore entity)
entity.Fields[f].IsChanged = true;
}
}
-}
\ No newline at end of file
+}
diff --git a/TournamentManager/TournamentManager/ExtensionMethods/MatchEntityExtensions.cs b/TournamentManager/TournamentManager/ExtensionMethods/MatchEntityExtensions.cs
index 40cc62c7..cb0eb6f6 100644
--- a/TournamentManager/TournamentManager/ExtensionMethods/MatchEntityExtensions.cs
+++ b/TournamentManager/TournamentManager/ExtensionMethods/MatchEntityExtensions.cs
@@ -1,24 +1,54 @@
using System;
-using System.Collections.Generic;
using System.Linq;
using TournamentManager.DAL.EntityClasses;
namespace TournamentManager.ExtensionMethods;
///
-/// extensions, which can't be located in generic CustomExtensions
-/// because of dependencies to classes of .
+/// extension methods.
///
public static class MatchEntityExtensions
{
+ ///
+ /// Calculates the match points following the defined rules
+ /// and stores them in the .
+ ///
+ ///
+ ///
+ ///
public static MatchEntity CalculateMatchPoints(this MatchEntity match, MatchRuleEntity matchRule)
{
if (!match.Sets.Any()) return match;
+ var hasTieBreak = match.Sets.Any(s => s.IsTieBreak);
match.IsOverruled = match.Sets.Any(s => s.IsOverruled);
-
var setPoints = match.Sets.GetSetPoints();
+ // Only check PointsMatchWonAfterTieBreak for not zero,
+ // because for PointsMatchLostAfterTieBreak it might be the desired value
+ if (hasTieBreak && matchRule.PointsMatchWonAfterTieBreak != 0)
+ {
+ // Special match point distribution for tie-breaks
+ TieBreakRule(match, matchRule, setPoints);
+ }
+ else
+ {
+ // Regular match point distribution
+ NoTieBreakRule(match, matchRule, setPoints);
+ }
+
+ match.IsComplete = true;
+ return match;
+ }
+
+ ///
+ /// This method gets called, when the
+ /// and the are zero,
+ /// or the match was won without a tie-break.
+ ///
+ /// Throws if set result is tie.
+ private static void NoTieBreakRule(MatchEntity match, MatchRuleEntity matchRule, IOpponent setPoints)
+ {
if (setPoints.Home < setPoints.Guest)
{
match.HomePoints = matchRule.PointsMatchLost;
@@ -33,9 +63,29 @@ public static MatchEntity CalculateMatchPoints(this MatchEntity match, MatchRule
{
match.HomePoints = match.GuestPoints = matchRule.PointsMatchTie;
}
+ }
- match.IsComplete = true;
-
- return match;
+ ///
+ /// Usually, there are 3 match points to distribute, if the tie-break rule applies:
+ /// a) If a team wins without a tie-break, the match points are 3:0
+ /// b) If a team wins after a tie-break, the match points are 2:1
+ ///
+ private static void TieBreakRule(MatchEntity match, MatchRuleEntity matchRule, IOpponent setPoints)
+ {
+ if (setPoints.Home < setPoints.Guest)
+ {
+ match.HomePoints = matchRule.PointsMatchLostAfterTieBreak;
+ match.GuestPoints = matchRule.PointsMatchWonAfterTieBreak;
+ }
+ else if (setPoints.Home > setPoints.Guest)
+ {
+ match.HomePoints = matchRule.PointsMatchWonAfterTieBreak;
+ match.GuestPoints = matchRule.PointsMatchLostAfterTieBreak;
+ }
+ else
+ {
+ throw new InvalidOperationException(
+ $"Set points '{setPoints.Home}:{setPoints.Guest}' is a tie result, which is not allowed using {nameof(TieBreakRule)}.");
+ }
}
-}
\ No newline at end of file
+}
diff --git a/TournamentManager/TournamentManager/ExtensionMethods/SetEntityExtensions.cs b/TournamentManager/TournamentManager/ExtensionMethods/SetEntityExtensions.cs
new file mode 100644
index 00000000..89df3339
--- /dev/null
+++ b/TournamentManager/TournamentManager/ExtensionMethods/SetEntityExtensions.cs
@@ -0,0 +1,55 @@
+using System.Linq;
+using TournamentManager.DAL.EntityClasses;
+
+namespace TournamentManager.ExtensionMethods;
+
+///
+/// extension methods.
+///
+public static class SetEntityExtensions
+{
+ ///
+ /// Assigns "set points" to the .
+ /// The property will be set to
+ ///
+ ///
+ /// The with the rules to apply.
+ public static void CalculateSetPoints(this SetEntity setEntity, SetRuleEntity setRule)
+ {
+ setEntity.IsOverruled = false;
+ if (setEntity.HomeBallPoints > setEntity.GuestBallPoints)
+ {
+ setEntity.HomeSetPoints = setRule.PointsSetWon;
+ setEntity.GuestSetPoints = setRule.PointsSetLost;
+ }
+ else if (setEntity.HomeBallPoints < setEntity.GuestBallPoints)
+ {
+ setEntity.HomeSetPoints = setRule.PointsSetLost;
+ setEntity.GuestSetPoints = setRule.PointsSetWon;
+ }
+ else
+ {
+ setEntity.HomeSetPoints = setEntity.GuestSetPoints = setRule.PointsSetTie;
+ }
+ }
+
+ ///
+ /// Sets home/guest ball points and home/guest set points.
+ /// The property will be set to ,
+ /// while will be set to .
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void Overrule(this SetEntity setEntity, int homeBallPoints, int guestBallPoints, int homeSetPoints, int guestSetPoints)
+ {
+ setEntity.HomeBallPoints = homeBallPoints;
+ setEntity.GuestBallPoints = guestBallPoints;
+ setEntity.HomeSetPoints = homeSetPoints;
+ setEntity.GuestSetPoints = guestSetPoints;
+ setEntity.IsTieBreak = false;
+ setEntity.IsOverruled = true;
+ }
+}
diff --git a/TournamentManager/TournamentManager/ExtensionMethods/SetEntityListExtensions.cs b/TournamentManager/TournamentManager/ExtensionMethods/SetEntityListExtensions.cs
index a851ef9f..7bf79e62 100644
--- a/TournamentManager/TournamentManager/ExtensionMethods/SetEntityListExtensions.cs
+++ b/TournamentManager/TournamentManager/ExtensionMethods/SetEntityListExtensions.cs
@@ -17,7 +17,7 @@ public static class SetEntityListExtensions
///
/// The for which the calculation takes place.
/// Returns a with the number of won sets for home and guest team.
- public static IOpponent GetSetsWon(this IList setList)
+ public static IOpponent GetSetsWon(this IList setList)
{
var setsWon = new PointResult(setList.Count(set => set.HomeBallPoints > set.GuestBallPoints), setList.Count(set => set.HomeBallPoints < set.GuestBallPoints));
return setsWon;
@@ -28,7 +28,7 @@ public static IOpponent GetSetsWon(this IList setList)
///
/// The for which the calculation takes place.
/// Returns the maximum of won sets by home or guest team
- public static int MaxBestOf(this IList setList)
+ public static int? MaxBestOf(this IList setList)
{
var setsWon = setList.GetSetsWon();
return setsWon.Home < setsWon.Guest ? setsWon.Guest : setsWon.Home;
@@ -48,21 +48,21 @@ public static IList CalculateSetPoints(this IList setList,
foreach (var set in setList)
{
set.CalculateSetPoints(setRule);
- set.IsTieBreak = false || matchRule.BestOf && wonSets.Home == wonSets.Guest &&
+ set.IsTieBreak = matchRule.BestOf && wonSets.Home == wonSets.Guest &&
wonSets.Home + wonSets.Guest == matchRule.MaxNumOfSets() - 1;
wonSets.Home += set.HomeBallPoints > set.GuestBallPoints ? 1 : 0;
- wonSets.Guest += set.HomeBallPoints < set.GuestBallPoints ? 0 : 1;
+ wonSets.Guest += set.HomeBallPoints > set.GuestBallPoints ? 0 : 1;
}
return setList;
}
///
- /// Gets the sum of set points for home and guest team.
+ /// Gets the sum of set points for home and guest team stored in the list s.
///
///
/// Returns an with the sum of set points for home and guest team.
- public static IOpponent GetSetPoints(this IList setList)
+ public static IOpponent GetSetPoints(this IList setList)
{
return new PointResult(setList.Sum(s => s.HomeSetPoints), setList.Sum(s => s.GuestSetPoints));
}
@@ -76,4 +76,42 @@ public static int GetTotalBallPoints(this IList setList)
{
return setList.Sum(s => s.HomeBallPoints) + setList.Sum(s => s.GuestBallPoints);
}
-}
\ No newline at end of file
+
+ ///
+ /// Adds a delimited string with set results to the of .
+ ///
+ ///
+ /// The
+ /// The set results, e.g. "25:23 25:18 12:25"
+ /// The character to delimit sets. Default is blank.
+ public static void Add(this IList setList, long matchId, string setResults, char setSeparator = ' ')
+ {
+ var pointResults = setResults.Split(setSeparator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
+ .Select(set => new PointResult(set)).ToList();
+
+ setList.Add(matchId, pointResults);
+ }
+
+ ///
+ /// Adds a of s to the of .
+ ///
+ ///
+ /// The
+ /// The of .
+ public static void Add(this IList setList, long matchId, IList pointResults)
+ {
+ var sequenceNo = 0;
+ foreach (var pointResult in pointResults)
+ {
+ if(pointResult.Home is null || pointResult.Guest is null)
+ continue;
+
+ setList.Add(new SetEntity {
+ MatchId = matchId,
+ SequenceNo = ++sequenceNo,
+ HomeBallPoints = pointResult.Home.Value,
+ GuestBallPoints = pointResult.Guest.Value
+ });
+ }
+ }
+}
diff --git a/TournamentManager/TournamentManager/ExtensionMethods/TimeSpanExtensions.cs b/TournamentManager/TournamentManager/ExtensionMethods/TimeSpanExtensions.cs
index a8a8ba76..c2986754 100644
--- a/TournamentManager/TournamentManager/ExtensionMethods/TimeSpanExtensions.cs
+++ b/TournamentManager/TournamentManager/ExtensionMethods/TimeSpanExtensions.cs
@@ -1,16 +1,16 @@
using System;
-using System.Collections.Generic;
+
namespace TournamentManager.ExtensionMethods;
public static class TimeSpanExtensions
{
public static string ToShortTimeString(this TimeSpan timeSpan)
{
- return DateTime.UtcNow.Date.Add(timeSpan).ToShortTimeString();
+ return TimeOnly.FromTimeSpan(timeSpan).ToShortTimeString();
}
public static string ToLongTimeString(this TimeSpan timeSpan)
{
- return DateTime.UtcNow.Date.Add(timeSpan).ToLongTimeString();
+ return TimeOnly.FromTimeSpan(timeSpan).ToLongTimeString();
}
-}
\ No newline at end of file
+}
diff --git a/TournamentManager/TournamentManager/Match/PointResult.cs b/TournamentManager/TournamentManager/Match/PointResult.cs
deleted file mode 100644
index 8993bb45..00000000
--- a/TournamentManager/TournamentManager/Match/PointResult.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace TournamentManager.Match;
-
-///
-/// Class which represents any points which can occur in sports match with 2 teams.
-///
-public class PointResult : IOpponent, IComparable>, IComparer>
-{
- ///
- /// Initializes a new instance of the class TournamentManager.Match.ResultBase
- ///
- /// The result as a string.
- /// The separator which is used to separate home points and guest points
- public PointResult(string result, string pointsSeparator = ":")
- {
- PointsSeparator = pointsSeparator;
- var r = result.Split(new[] { PointsSeparator }, StringSplitOptions.None);
- try
- {
- Home = int.Parse(r[0], System.Globalization.CultureInfo.CurrentCulture);
- Guest = int.Parse(r[1], System.Globalization.CultureInfo.CurrentCulture);
- }
- catch
- {
- throw new Exception();
- }
- }
-
- ///
- /// Initializes a new instance of the class TournamentManager.Match.ResultBase
- ///
- /// The home points of the result.
- /// The guest points of the result.
- public PointResult(int home, int guest)
- {
- Home = home;
- Guest = guest;
- }
-
- static PointResult()
- {
- }
-
- ///
- /// Gets or sets the separator string which is used to separate home points and guest points.
- ///
- public string PointsSeparator { get; set; } = ":";
-
- ///
- /// Gets or sets the home points of the result.
- ///
- public int Home { get; set; }
-
- ///
- /// Gets or sets the guest points of the result.
- ///
- public int Guest { get; set; }
-
- ///
- /// Implements
- ///
- ///
- ///
- public int CompareTo(IOpponent? other)
- {
- return Compare(this, other);
- }
-
- ///
- /// Implements
- ///
- ///
- ///
- ///
- public int Compare(IOpponent? x, IOpponent? y)
- {
- if (x == null)
- throw new ArgumentNullException(nameof(x));
- if (y == null)
- throw new ArgumentNullException(nameof(y));
-
- if (x.Home < y.Home)
- return -1;
- if (x.Home > y.Home)
- return 1;
-
- // Home points are equal
-
- if (x.Guest < y.Guest)
- return 1;
- if (x.Guest > y.Guest)
- return -1;
-
- // Home and guest points are equal
- return 0;
- }
-
- ///
- /// Gets the string representation of the result using a format string with 2 placeholders, e.g. "Home: {0} - Guest: {1} "
- ///
- /// The format string with 2 placeholders, e.g. "Home: {0} - Guest: {1} "
- /// Returns the string representation of the result using a format string.
- public string ToString(string format)
- {
- return string.Format(format, Home, Guest);
- }
-
- ///
- /// Gets the string representation of the result using the default points separator.
- ///
- /// Returns the string representation of the result using the default points separator.
- public override string ToString()
- {
- return string.Concat(Home, PointsSeparator, Guest);
- }
-
- ///
- /// Adds the 2 results.
- ///
- /// The result of team A.
- /// The result of team B.
- ///
- public static PointResult operator +(PointResult a, IOpponent b)
- {
- return new PointResult(a.Home + b.Home, a.Guest + b.Guest);
- }
-
- ///
- /// Subtracts results A from result B.
- ///
- /// The result of team A.
- /// The result of team B.
- ///
- public static PointResult operator -(PointResult a, IOpponent b)
- {
- if (a.Home < b.Home || a.Guest < b.Guest)
- throw new ArgumentException("Operation would lead to negative points value.", b.ToString());
-
- return new PointResult(a.Home - b.Home, a.Guest - b.Guest);
- }
-}
\ No newline at end of file
diff --git a/TournamentManager/TournamentManager/Match/PointResultNullable.cs b/TournamentManager/TournamentManager/Match/PointResultNullable.cs
deleted file mode 100644
index 0379c939..00000000
--- a/TournamentManager/TournamentManager/Match/PointResultNullable.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace TournamentManager.Match;
-
-public class PointResultNullable : IOpponent
-{
- public PointResultNullable()
- {}
-
- public PointResultNullable(int? home, int? guest)
- {
- Home = home;
- Guest = guest;
- }
-
- public int? Home { get; set; }
- public int? Guest { get; set; }
-}
diff --git a/TournamentManager/TournamentManager/PointResult.cs b/TournamentManager/TournamentManager/PointResult.cs
new file mode 100644
index 00000000..bac86f30
--- /dev/null
+++ b/TournamentManager/TournamentManager/PointResult.cs
@@ -0,0 +1,209 @@
+using System;
+using System.Collections.Generic;
+
+namespace TournamentManager;
+
+///
+/// Class which represents any points which can occur in sports match with 2 teams.
+///
+public class PointResult : IOpponent, IComparable>, IComparer>
+{
+ ///
+ /// Initializes a new instance
+ ///
+ /// The result as a string.
+ /// The separators which are used to separate home points and guest points
+ public PointResult(string result, char pointsSeparator = ':')
+ {
+ PointsSeparator = pointsSeparator;
+ var r = result.Split(PointsSeparator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
+
+ if (r.Length < 2)
+ {
+ Home = Guest = null;
+ }
+ else
+ {
+ Home = int.Parse(r[0], System.Globalization.CultureInfo.InvariantCulture);
+ Guest = int.Parse(r[1], System.Globalization.CultureInfo.InvariantCulture);
+ }
+ }
+
+ ///
+ /// Initializes a new instance
+ ///
+ /// The home points of the result.
+ /// The guest points of the result.
+ public PointResult(int? home, int? guest)
+ {
+ Home = home;
+ Guest = guest;
+ }
+
+ ///
+ /// Initializes a new instance
+ ///
+ public PointResult()
+ {
+ }
+
+ ///
+ /// Gets or sets the separator string which is used to separate home points and guest points.
+ ///
+ public char PointsSeparator { get; set; } = ':';
+
+ ///
+ /// Gets or sets the home points of the result.
+ ///
+ public int? Home { get; set; }
+
+ ///
+ /// Gets or sets the guest points of the result.
+ ///
+ public int? Guest { get; set; }
+
+ ///
+ /// Implements . results are treated as zero.
+ ///
+ ///
+ ///
+ public int CompareTo(IOpponent? other)
+ {
+ return Compare(this, other);
+ }
+
+ ///
+ /// Implements . results are treated as zero.
+ ///
+ ///
+ ///
+ ///
+ public int Compare(IOpponent? x, IOpponent? y)
+ {
+ // The "not null" is more than the "null" opponent
+
+ if (x is null && y is not null) return -1;
+ if (x is not null && y is null) return 1;
+ if (x is null && y is null) return 0;
+
+ if ((x!.Home ?? 0) < (y!.Home ?? 0)) return -1;
+ if ((x.Home ?? 0) > (y.Home ?? 0)) return 1;
+
+ // Home points are equal
+
+ if ((x.Guest ?? 0) < (y.Guest ?? 0)) return 1;
+ if ((x.Guest ?? 0) > (y.Guest ?? 0)) return -1;
+
+ // Home and guest points are equal
+ return 0;
+ }
+
+ ///
+ /// Gets the string representation of the result using a format string with 2 placeholders, e.g. "Home: {0} - Guest: {1} "
+ ///
+ /// The format string with 2 placeholders, e.g. "Home: {0} - Guest: {1} "
+ /// Returns the string representation of the result using a format string.
+ public string ToString(string format)
+ {
+ return string.Format(format, Home.HasValue ? Home : "-", Guest.HasValue ? Guest : "-");
+ }
+
+ ///
+ /// Gets the string representation of the result using the default points separator.
+ ///
+ /// Returns the string representation of the result using the default points separator.
+ public override string ToString()
+ {
+ return ToString($"{{0}}{PointsSeparator}{{1}}");
+ }
+
+ ///
+ /// Adds the 2 results. results are treated as zero.
+ ///
+ /// The result of team A.
+ /// The result of team B.
+ ///
+ public static PointResult operator +(PointResult a, IOpponent b)
+ {
+ return new PointResult((a.Home ?? 0) + (b.Home ?? 0), (a.Guest ?? 0) + (b.Guest ?? 0));
+ }
+
+ ///
+ /// Subtracts results A from result B. results are treated as zero.
+ ///
+ /// The result of team A.
+ /// The result of team B.
+ ///
+ public static PointResult operator -(PointResult a, IOpponent b)
+ {
+ if (a.Home < b.Home || a.Guest < b.Guest)
+ throw new ArgumentException(@"Operation would lead to negative points value.", b.ToString());
+
+ return new PointResult((a.Home ?? 0) - (b.Home ?? 0), (a.Guest ?? 0) - (b.Guest ?? 0));
+ }
+
+ ///
+ /// Compares 2 results for equality. results are treated as zero.
+ ///
+ /// The result of team A.
+ /// The result of team B.
+ ///
+ public static bool operator ==(PointResult a, IOpponent b)
+ {
+ return a.CompareTo(b) == 0;
+ }
+
+ ///
+ /// Compares 2 results for inequality. results are treated as zero.
+ ///
+ /// The result of team A.
+ /// The result of team B.
+ ///
+ public static bool operator !=(PointResult a, IOpponent b)
+ {
+ return a.CompareTo(b) != 0;
+ }
+
+ ///
+ /// Compares 2 results for less than. results are treated as zero.
+ ///
+ /// The result of team A.
+ /// The result of team B.
+ ///
+ public static bool operator <(PointResult a, IOpponent b)
+ {
+ return a.CompareTo(b) == -1;
+ }
+
+ ///
+ /// Compares 2 results for more than. results are treated as zero.
+ ///
+ /// The result of team A.
+ /// The result of team B.
+ ///
+ public static bool operator >(PointResult a, IOpponent b)
+ {
+ return a.CompareTo(b) == 1;
+ }
+
+ #region ** Equality members **
+
+ protected bool Equals(PointResult other)
+ {
+ return PointsSeparator == other.PointsSeparator && Home == other.Home && Guest == other.Guest;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (obj is null) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ return obj.GetType() == GetType() && Equals((PointResult) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(ToString());
+ }
+
+ #endregion
+}
diff --git a/TournamentManager/TournamentManager/TournamentManager.csproj b/TournamentManager/TournamentManager/TournamentManager.csproj
index 3c9e0f8a..0584993a 100644
--- a/TournamentManager/TournamentManager/TournamentManager.csproj
+++ b/TournamentManager/TournamentManager/TournamentManager.csproj
@@ -4,8 +4,10 @@
Tournament Manager
net6.0
en
- 6.1.0
- 6.1.0
+
+ en;de
+ 6.2.0
+ 6.2.0
true
1591
TournamentManager is the backend for Volleyball League.