diff --git a/Tests/TryAtSoftware.CleanTests.UnitTests/ConstructionManagerTests.cs b/Tests/TryAtSoftware.CleanTests.UnitTests/ConstructionManagerTests.cs index e73fa87..ca5c91a 100644 --- a/Tests/TryAtSoftware.CleanTests.UnitTests/ConstructionManagerTests.cs +++ b/Tests/TryAtSoftware.CleanTests.UnitTests/ConstructionManagerTests.cs @@ -2,6 +2,8 @@ using System.Text.Json; using TryAtSoftware.CleanTests.Core.Construction; +using TryAtSoftware.CleanTests.Core.Interfaces; +using TryAtSoftware.CleanTests.Core.XUnit; using TryAtSoftware.CleanTests.UnitTests.Constants; using TryAtSoftware.CleanTests.UnitTests.Extensions; using TryAtSoftware.CleanTests.UnitTests.Parametrization; @@ -9,8 +11,7 @@ public class ConstructionManagerTests { - [Theory(Timeout = UnitTestConstants.Timeout)] - [MemberData(nameof(GetDependenciesManagerSetups))] + [Theory(Timeout = UnitTestConstants.Timeout), MemberData(nameof(GetDependenciesManagerSetups))] public Task DependencyGraphsShouldBeConstructedSuccessfully(EnvironmentSetup setup, string pathToExpectedResult) { var assemblyTestData = setup.MaterializeAsAssemblyData(); @@ -33,167 +34,208 @@ public Task DependencyGraphsShouldBeConstructedSuccessfully(EnvironmentSetup set [Fact] public void OuterDemandsShouldBeAppliedCorrectlyAtRootLevel() { - var utilitiesCount = RandomizationHelper.RandomInteger(3, 10); var setup = new EnvironmentSetup("outer_demands_env"); - setup.WithCategory("FL1", 1).WithCategory("FL2", utilitiesCount).WithCategory("SL1", utilitiesCount); - setup.WithRequirements("FL1", 1, "SL1"); - - for (var i = 1; i <= utilitiesCount; i++) + var count = RandomizationHelper.RandomInteger(3, 10); + PrepareForOuterDemandsTesting(setup, depth: 1, count); + + for (var i = 1; i <= count; i++) { - var characteristic = $"C-{i}"; - setup.WithCharacteristics("FL2", i, characteristic); - setup.WithOuterDemands("SL1", i, "FL2", characteristic); + setup.WithCharacteristics("Q", i, $"O{i}"); + setup.WithOuterDemands("N1", i, "Q", $"O{i}"); } var assemblyTestData = setup.MaterializeAsAssemblyData(); var manager = new ConstructionManager(assemblyTestData); - var fl1Utilities = assemblyTestData.CleanUtilities.Get("FL1").ToArray(); - Assert.Single(fl1Utilities); - - var fl2Utilities = assemblyTestData.CleanUtilities.Get("FL2").ToArray(); - Assert.Equal(utilitiesCount, fl2Utilities.Length); - - var sl1Utilities = assemblyTestData.CleanUtilities.Get("SL1").ToArray(); - Assert.Equal(utilitiesCount, sl1Utilities.Length); + var matrix = ExtractMatrixForOuterDemandsTesting(assemblyTestData, depth: 1, count); - for (var i = 0; i < utilitiesCount; i++) + for (var i = 0; i < count; i++) { - var utilityIds = new[] { fl1Utilities[0].Id, fl2Utilities[i].Id }; + var utilityIds = new[] { matrix[0][0].Id, matrix[^1][i].Id }; var constructionGraphs = manager.BuildIndividualConstructionGraphs(utilityIds); - Assert.Single(constructionGraphs); - var fl1ConstructionGraph = constructionGraphs[0][0]; - - Assert.NotNull(fl1Utilities); - Assert.Equal(fl1Utilities[0].Id, fl1ConstructionGraph.Id); - - var sl1ConstructionGraph = Assert.Single(fl1ConstructionGraph.Dependencies); - Assert.NotNull(sl1ConstructionGraph); - Assert.Equal(sl1Utilities[i].Id, sl1ConstructionGraph.Id); - - var fl2ConstructionGraph = constructionGraphs[0][1]; - Assert.NotNull(fl2ConstructionGraph); - Assert.Equal(fl2Utilities[i].Id, fl2ConstructionGraph.Id); - - Assert.Empty(fl2ConstructionGraph.Dependencies); + Assert.Equal(2, constructionGraphs[0].Length); + + var penultConstructionGraph = constructionGraphs[0][0]; + Assert.Equal(matrix[0][0].Id, penultConstructionGraph.Id); + Assert.Single(penultConstructionGraph.Dependencies); + + var qConstructionGraph = constructionGraphs[0][1]; + Assert.Equal(matrix[^1][i].Id, qConstructionGraph.Id); + Assert.Empty(qConstructionGraph.Dependencies); + + var lastConstructionGraph = penultConstructionGraph.Dependencies[0]; + Assert.Equal(matrix[1][i].Id, lastConstructionGraph.Id); + Assert.Empty(lastConstructionGraph.Dependencies); } } [Fact] public void UnsatisfiableOuterDemandsShouldBeHandledCorrectlyAtRootLevel() { - var utilitiesCount = RandomizationHelper.RandomInteger(3, 10); var setup = new EnvironmentSetup("outer_demands_env"); - setup.WithCategory("FL1", 1).WithCategory("FL2", utilitiesCount).WithCategory("SL1", utilitiesCount); - setup.WithRequirements("FL1", 1, "SL1"); + var count = RandomizationHelper.RandomInteger(3, 10); + PrepareForOuterDemandsTesting(setup, depth: 1, count); - for (var i = 1; i <= utilitiesCount; i++) + for (var i = 1; i <= count; i++) { - setup.WithCharacteristics("FL2", i, $"C{i}"); - setup.WithOuterDemands("SL1", i, "FL2", $"D{i}"); + setup.WithCharacteristics("Q", i, $"O{i}"); + setup.WithOuterDemands("N1", i, "Q", $"X{i}"); } var assemblyTestData = setup.MaterializeAsAssemblyData(); var manager = new ConstructionManager(assemblyTestData); - var fl1Utilities = assemblyTestData.CleanUtilities.Get("FL1").ToArray(); - Assert.Single(fl1Utilities); + var matrix = ExtractMatrixForOuterDemandsTesting(assemblyTestData, depth: 1, count); - var fl2Utilities = assemblyTestData.CleanUtilities.Get("FL2").ToArray(); - Assert.Equal(utilitiesCount, fl2Utilities.Length); - - for (var i = 0; i < utilitiesCount; i++) + for (var i = 0; i < count; i++) { - var utilityIds = new[] { fl1Utilities[0].Id, fl2Utilities[i].Id }; + var utilityIds = new[] { matrix[0][0].Id, matrix[^1][i].Id }; var constructionGraphs = manager.BuildIndividualConstructionGraphs(utilityIds); - Assert.Empty(constructionGraphs); } } - [Fact] - public void OuterDemandsShouldBeAppliedCorrectlyAtDeeperLevels() + [Theory, MemberData(nameof(GetDepthMembers))] + public void OuterDemandsShouldBeAppliedCorrectlyAtDeeperLevels(int depth) { - var utilitiesCount = RandomizationHelper.RandomInteger(3, 10); var setup = new EnvironmentSetup("outer_demands_env"); - setup.WithCategory("FL1", 1).WithCategory("SL1", 1).WithCategory("SL2", utilitiesCount).WithCategory("TL1", utilitiesCount); - setup.WithRequirements("FL1", 1, "SL1", "SL2").WithRequirements("SL1", 1, "TL1"); + var count = RandomizationHelper.RandomInteger(3, 10); + PrepareForOuterDemandsTesting(setup, depth, count); - for (var i = 1; i <= utilitiesCount; i++) + for (var i = 1; i <= count; i++) { - var characteristic = $"C-{i}"; - setup.WithCharacteristics("SL2", i, characteristic); - setup.WithOuterDemands("TL1", i, "SL2", characteristic); + setup.WithCharacteristics("Q", i, $"O{i}"); + setup.WithOuterDemands($"N{depth}", i, "Q", $"O{i}"); } var assemblyTestData = setup.MaterializeAsAssemblyData(); var manager = new ConstructionManager(assemblyTestData); - var fl1Utilities = assemblyTestData.CleanUtilities.Get("FL1").ToArray(); - Assert.Single(fl1Utilities); - - var sl1Utilities = assemblyTestData.CleanUtilities.Get("SL1").ToArray(); - Assert.Single(sl1Utilities); - - var sl2Utilities = assemblyTestData.CleanUtilities.Get("SL2").ToArray(); - Assert.Equal(utilitiesCount, sl2Utilities.Length); - - var tl1Utilities = assemblyTestData.CleanUtilities.Get("TL1").ToArray(); - Assert.Equal(utilitiesCount, tl1Utilities.Length); + var matrix = ExtractMatrixForOuterDemandsTesting(assemblyTestData, depth, count); + var constructionGraphs = manager.BuildIndividualConstructionGraphs(matrix[0].Select(x => x.Id)); + AssertCorrectConstructionWithOuterDemandsAtDeeperLevels(constructionGraphs, matrix, depth, count); + } - var utilityIds = new[] { fl1Utilities[0].Id }; - var constructionGraphs = manager.BuildIndividualConstructionGraphs(utilityIds); + [Theory, MemberData(nameof(GetDepthMembers))] + public void UnsatisfiableOuterDemandsShouldBeHandledCorrectlyAtDeeperLevels(int depth) + { + var setup = new EnvironmentSetup("outer_demands_env"); + var count = RandomizationHelper.RandomInteger(3, 10); + PrepareForOuterDemandsTesting(setup, depth, count); - Assert.Equal(utilitiesCount, constructionGraphs.Length); - for (var i = 0; i < utilitiesCount; i++) + for (var i = 1; i <= count; i++) { - var fl1ConstructionGraph = constructionGraphs[i][0]; - - Assert.NotNull(fl1Utilities); - Assert.Equal(fl1Utilities[0].Id, fl1ConstructionGraph.Id); - Assert.Equal(2, fl1ConstructionGraph.Dependencies.Count); - - var sl1ConstructionGraph = fl1ConstructionGraph.Dependencies[0]; - Assert.NotNull(sl1ConstructionGraph); - Assert.Equal(sl1Utilities[0].Id, sl1ConstructionGraph.Id); + setup.WithCharacteristics("Q", i, $"O{i}"); + setup.WithOuterDemands($"N{depth}", i, "Q", $"X{i}"); + } - var sl2ConstructionGraph = fl1ConstructionGraph.Dependencies[1]; - Assert.NotNull(sl2ConstructionGraph); - Assert.Equal(sl2Utilities[i].Id, sl2ConstructionGraph.Id); - Assert.Empty(sl2ConstructionGraph.Dependencies); + var assemblyTestData = setup.MaterializeAsAssemblyData(); + var manager = new ConstructionManager(assemblyTestData); - var tl1ConstructionGraph = Assert.Single(sl1ConstructionGraph.Dependencies); - Assert.NotNull(tl1ConstructionGraph); - Assert.Equal(tl1Utilities[i].Id, tl1ConstructionGraph.Id); - Assert.Empty(tl1ConstructionGraph.Dependencies); - } + var matrix = ExtractMatrixForOuterDemandsTesting(assemblyTestData, depth, count); + var constructionGraphs = manager.BuildIndividualConstructionGraphs(matrix[0].Select(x => x.Id)); + Assert.Empty(constructionGraphs); } - - [Fact] - public void UnsatisfiableOuterDemandsShouldBeHandledCorrectlyAtDeeperLevels() + + /// + /// N{depth} defines unsatisfiable outer demands towards N0, N1, ..., N{depth - 2}. + /// These unsatisfiable demands do not impact the construction of individual graphs, + /// as the referenced utilities are not located within the immediate higher hierarchical level. + /// + [Theory, MemberData(nameof(GetDepthMembers))] + public void OuterDemandsShouldOnlyTargetTheImmediateHigherLevel(int depth) { - var utilitiesCount = RandomizationHelper.RandomInteger(3, 10); var setup = new EnvironmentSetup("outer_demands_env"); - setup.WithCategory("FL1", 1).WithCategory("SL1", 1).WithCategory("SL2", utilitiesCount).WithCategory("TL1", utilitiesCount); - setup.WithRequirements("FL1", 1, "SL1", "SL2").WithRequirements("SL1", 1, "TL1"); + var count = RandomizationHelper.RandomInteger(3, 10); + PrepareForOuterDemandsTesting(setup, depth, count); - for (var i = 1; i <= utilitiesCount; i++) + for (var i = 1; i <= count; i++) { - setup.WithCharacteristics("SL2", i, $"C{i}"); - setup.WithOuterDemands("TL1", i, "SL2", $"D{i}"); + setup.WithCharacteristics("Q", i, $"O{i}"); + setup.WithOuterDemands($"N{depth}", i, "Q", $"O{i}"); + + for (var j = 0; j < depth - 1; j++) + { + setup.WithCharacteristics($"N{j}", 1, $"O{i}"); + setup.WithOuterDemands($"N{depth}", i, $"N{j}", $"X{i}"); + } } var assemblyTestData = setup.MaterializeAsAssemblyData(); var manager = new ConstructionManager(assemblyTestData); - var fl1Utilities = assemblyTestData.CleanUtilities.Get("FL1").ToArray(); - Assert.Single(fl1Utilities); + var matrix = ExtractMatrixForOuterDemandsTesting(assemblyTestData, depth, count); + var constructionGraphs = manager.BuildIndividualConstructionGraphs(matrix[0].Select(x => x.Id)); + AssertCorrectConstructionWithOuterDemandsAtDeeperLevels(constructionGraphs, matrix, depth, count); + } - var utilityIds = new[] { fl1Utilities[0].Id }; - var constructionGraphs = manager.BuildIndividualConstructionGraphs(utilityIds); + /// + /// |N0| -> |N1| -> ... -> |N{depth - 2}| -> |N{depth - 1}| -> |N{depth}| + /// -> |Q| -----------------^ + /// + /// N{depth} defines outer demands towards Q + /// + private static void PrepareForOuterDemandsTesting(EnvironmentSetup setup, int depth, int count) + { + for (var i = 0; i < depth; i++) + { + setup.WithCategory($"N{i}", 1); + setup.WithRequirements($"N{i}", 1, $"N{i + 1}"); + } - Assert.Empty(constructionGraphs); + setup.WithCategory($"N{depth}", count).WithCategory("Q", count); + if (depth > 1) setup.WithRequirements($"N{depth - 2}", 1, "Q"); + } + + /// + /// Returns a matrix of length `depth + 2`: + /// - The first `depth + 1` entries are reserved for the `N#` categories. + /// - The last entry is reserved for the `Q` category. + /// + private static ICleanUtilityDescriptor[][] ExtractMatrixForOuterDemandsTesting(CleanTestAssemblyData assemblyTestData, int depth, int count) + { + // NOTE: There are depth + 1 `N#` categories and `Q`. + var matrix = new ICleanUtilityDescriptor[depth + 2][]; + for (var i = 0; i <= depth; i++) matrix[i] = assemblyTestData.CleanUtilities.Get($"N{i}").ToArray(); + matrix[^1] = assemblyTestData.CleanUtilities.Get("Q").ToArray(); + + for (var i = 0; i < depth; i++) Assert.Single(matrix[i]); + for (var i = depth; i < matrix.Length; i++) Assert.Equal(count, matrix[i].Length); + + return matrix; + } + + private static void AssertCorrectConstructionWithOuterDemandsAtDeeperLevels(IndividualCleanUtilityConstructionGraph[][] constructionGraphs, ICleanUtilityDescriptor[][] matrix, int depth, int count) + { + Assert.Equal(count, constructionGraphs.Length); + for (var i = 0; i < count; i++) + { + var analyzedGraph = Assert.Single(constructionGraphs[i]); + + for (var j = 0; j < depth - 2; j++) + { + Assert.Equal(matrix[j][0].Id, analyzedGraph.Id); + Assert.Single(analyzedGraph.Dependencies); + + analyzedGraph = analyzedGraph.Dependencies[0]; + } + + Assert.Equal(matrix[depth - 2][0].Id, analyzedGraph.Id); + Assert.Equal(2, analyzedGraph.Dependencies.Count); + + var penultConstructionGraph = analyzedGraph.Dependencies[0]; + Assert.Equal(matrix[depth - 1][0].Id, penultConstructionGraph.Id); + Assert.Single(penultConstructionGraph.Dependencies); + + var qConstructionGraph = analyzedGraph.Dependencies[1]; + Assert.Equal(matrix[^1][i].Id, qConstructionGraph.Id); + Assert.Empty(qConstructionGraph.Dependencies); + + var lastConstructionGraph = penultConstructionGraph.Dependencies[0]; + Assert.Equal(matrix[depth][i].Id, lastConstructionGraph.Id); + Assert.Empty(lastConstructionGraph.Dependencies); + } } public static TheoryData GetDependenciesManagerSetups() @@ -201,7 +243,15 @@ public static TheoryData GetDependenciesManagerSetups( var data = new TheoryData(); foreach (var dependenciesManagerSetup in TestParameters.ConstructObservableConstructionManagerSetups()) data.Add(dependenciesManagerSetup.EnvironmentSetup, dependenciesManagerSetup.PathToExpectedResult); - + + return data; + } + + public static TheoryData GetDepthMembers() + { + var data = new TheoryData(); + for (var i = 2; i <= 10; i++) data.Add(i); + return data; } } \ No newline at end of file diff --git a/TryAtSoftware.CleanTests.Core/Attributes/ExternalDemandsAttribute.cs b/TryAtSoftware.CleanTests.Core/Attributes/ExternalDemandsAttribute.cs index ec2705c..7618c95 100644 --- a/TryAtSoftware.CleanTests.Core/Attributes/ExternalDemandsAttribute.cs +++ b/TryAtSoftware.CleanTests.Core/Attributes/ExternalDemandsAttribute.cs @@ -3,10 +3,4 @@ using System; [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] -public class ExternalDemandsAttribute : BaseDemandsAttribute -{ - public ExternalDemandsAttribute(string category, params string[] demands) - : base(category, demands) - { - } -} \ No newline at end of file +public class ExternalDemandsAttribute(string category, params string[] demands) : BaseDemandsAttribute(category, demands); \ No newline at end of file diff --git a/TryAtSoftware.CleanTests.Core/Attributes/InternalDemandsAttribute.cs b/TryAtSoftware.CleanTests.Core/Attributes/InternalDemandsAttribute.cs index df3b96a..004d345 100644 --- a/TryAtSoftware.CleanTests.Core/Attributes/InternalDemandsAttribute.cs +++ b/TryAtSoftware.CleanTests.Core/Attributes/InternalDemandsAttribute.cs @@ -3,10 +3,4 @@ using System; [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] -public class InternalDemandsAttribute : BaseDemandsAttribute -{ - public InternalDemandsAttribute(string category, params string[] demands) - : base(category, demands) - { - } -} \ No newline at end of file +public class InternalDemandsAttribute(string category, params string[] demands) : BaseDemandsAttribute(category, demands); \ No newline at end of file diff --git a/TryAtSoftware.CleanTests.Core/Attributes/OuterDemandsAttribute.cs b/TryAtSoftware.CleanTests.Core/Attributes/OuterDemandsAttribute.cs index a87e416..85e128b 100644 --- a/TryAtSoftware.CleanTests.Core/Attributes/OuterDemandsAttribute.cs +++ b/TryAtSoftware.CleanTests.Core/Attributes/OuterDemandsAttribute.cs @@ -3,10 +3,4 @@ using System; [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] -public class OuterDemandsAttribute : BaseDemandsAttribute -{ - public OuterDemandsAttribute(string category, params string[] demands) - : base(category, demands) - { - } -} \ No newline at end of file +public class OuterDemandsAttribute(string category, params string[] demands) : BaseDemandsAttribute(category, demands); \ No newline at end of file