From 60983492c393cee771600d0585e8d87d9c34b362 Mon Sep 17 00:00:00 2001 From: cld99 <115433496+cld99@users.noreply.github.com> Date: Mon, 14 Oct 2024 11:54:08 -0400 Subject: [PATCH 01/34] Create testing class for StringHierarchyRegion --- .../StringHierarchy/StringHierarchyRegionTest.cs | 8 ++++++++ .../StringHierarchy/StringHierarchyRegionTests.cs | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTest.cs create mode 100644 tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs diff --git a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTest.cs b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTest.cs new file mode 100644 index 0000000..d555ecb --- /dev/null +++ b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTest.cs @@ -0,0 +1,8 @@ +using System; + +public class Class1 +{ + public Class1() + { + } +} diff --git a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs new file mode 100644 index 0000000..9ee1487 --- /dev/null +++ b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs @@ -0,0 +1,8 @@ +using System; + +public class StringHierarchyRegionTests +{ + public StringHierarchyRegionTests() + { + } +} From 10e4336c02aac9db1d16779a77aba1f4dab402ec Mon Sep 17 00:00:00 2001 From: cld99 <115433496+cld99@users.noreply.github.com> Date: Mon, 28 Oct 2024 10:56:09 -0400 Subject: [PATCH 02/34] Add tests for constructor null parameters --- EStimLibrary.sln | 11 +-- docs/images/association_arrows-techvidvan.png | Bin 48207 -> 130 bytes ...ion_multiplicity_annotations-vertabelo.png | Bin 30432 -> 130 bytes ...iation_multiplicity_concept-techvidvan.png | Bin 109670 -> 131 bytes .../images/class_notation-tutorialspoint.jpeg | Bin 13935 -> 130 bytes docs/images/uml_arrows-stack_overflow.png | Bin 4388 -> 129 bytes .../StringHierarchyRegionTest.cs | 8 --- .../StringHierarchyRegionTests.cs | 67 ++++++++++++++++-- 8 files changed, 70 insertions(+), 16 deletions(-) delete mode 100644 tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTest.cs diff --git a/EStimLibrary.sln b/EStimLibrary.sln index 4222e38..7f12a6c 100644 --- a/EStimLibrary.sln +++ b/EStimLibrary.sln @@ -5,23 +5,26 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7E35BD38-1080-4A2E-B8B4-7CB0C5C08607}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EStimLibrary", "src\EStimLibrary\EStimLibrary.csproj", "{23710686-EDC8-41FC-AAC8-52B002F488D6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EStimLibrary", "src\EStimLibrary\EStimLibrary.csproj", "{23710686-EDC8-41FC-AAC8-52B002F488D6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {23710686-EDC8-41FC-AAC8-52B002F488D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {23710686-EDC8-41FC-AAC8-52B002F488D6}.Debug|Any CPU.Build.0 = Debug|Any CPU {23710686-EDC8-41FC-AAC8-52B002F488D6}.Release|Any CPU.ActiveCfg = Release|Any CPU {23710686-EDC8-41FC-AAC8-52B002F488D6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection GlobalSection(NestedProjects) = preSolution {23710686-EDC8-41FC-AAC8-52B002F488D6} = {7E35BD38-1080-4A2E-B8B4-7CB0C5C08607} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FAF6FE9F-5CE1-429C-A3E8-3C31D6AC479B} + EndGlobalSection EndGlobal diff --git a/docs/images/association_arrows-techvidvan.png b/docs/images/association_arrows-techvidvan.png index 7642f51a57d09fd68a1b222a4b1ee1de2968f450..54ff5ffa9326e76c2c66e700c9e96f19e8c5fad4 100644 GIT binary patch literal 130 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 N3`)k2b+8b;`UC0DC%pgw literal 30432 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 G?7sljSbZ-5 diff --git a/docs/images/class_notation-tutorialspoint.jpeg b/docs/images/class_notation-tutorialspoint.jpeg index 198e734ce2e75d02d096da11cf7a1dd5ea66d648..ef737b2b794d80b8d4e6732da71630197c54f8b5 100644 GIT binary patch literal 130 zcmWN?K@!3s3;@78uiyg~Lkg7s29qGnsB{eW;OliSd&*n;c-c1Rp}TYUJ}-}|%m4Pd zEi|4^4_8Vl!P*@spec5X=AP- M8SP)OfEk1N0qk}s{Qv*} literal 13935 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 diff --git a/docs/images/uml_arrows-stack_overflow.png b/docs/images/uml_arrows-stack_overflow.png index 3fd853350ceede03e2e906ece89129b87e982cd3..56809c2ffc8c8b4f72c6e438282dc4a078432738 100644 GIT binary patch literal 129 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 J`{n$v{{xBHSmyu$ diff --git a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTest.cs b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTest.cs deleted file mode 100644 index d555ecb..0000000 --- a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTest.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -public class Class1 -{ - public Class1() - { - } -} diff --git a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs index 9ee1487..e557033 100644 --- a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs +++ b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs @@ -1,8 +1,67 @@ -using System; +using EStimLibrary.Extensions.SpatialModel.StringHierarchy; + +namespace EStimLibrary.UnitTests.Extensions.SpatialModel.StringHierarchy; + + +// Test class naming convention: LibClassTests public class StringHierarchyRegionTests { - public StringHierarchyRegionTests() - { - } + private readonly ITestOutputHelper _output; + + // Test class constructor creates an output helper so can write console output. + public StringHierarchyRegionTests(ITestOutputHelper testOutputHelper) + { + this._output = testOutputHelper; + } + + // Test method naming convention: LibClassMethodName_ScenarioShouldExpect + + ///

+ /// Test the constructor with null parameter values. + /// + [Fact] + public void Constructor_ShouldAcceptNull() + { + var stringHierarchyRegion = new StringHierarchyRegion("hand", null, null, null, null, null); + + Assert.Equal("hand", stringHierarchyRegion.BaseName); + Assert.Null(stringHierarchyRegion.ParentRegion); + Assert.Empty(stringHierarchyRegion.ParentOptions); + Assert.Empty(stringHierarchyRegion.Options); + Assert.False(stringHierarchyRegion.HasOptions); + Assert.Single(stringHierarchyRegion.OptionedRegionNames); + Assert.Equal("hand", stringHierarchyRegion.OptionedRegionNames[0]); + Assert.Empty(stringHierarchyRegion.Modifiers); + Assert.False(stringHierarchyRegion.HasModifiers); + Assert.Empty(stringHierarchyRegion.Subregions); + Assert.False(stringHierarchyRegion.HasSubregions); + Assert.True(stringHierarchyRegion.IsLeaf); + Assert.Empty(stringHierarchyRegion.SavedLocations); + Assert.Empty(stringHierarchyRegion.SavedAreas); + } + + /// + /// Test the constructor with unspecified parameter values. + /// + [Fact] + public void Constructor_ShouldDefaultNull() + { + var stringHierarchyRegion = new StringHierarchyRegion("hand", null); + + Assert.Equal("hand", stringHierarchyRegion.BaseName); + Assert.Null(stringHierarchyRegion.ParentRegion); + Assert.Empty(stringHierarchyRegion.ParentOptions); + Assert.Empty(stringHierarchyRegion.Options); + Assert.False(stringHierarchyRegion.HasOptions); + Assert.Single(stringHierarchyRegion.OptionedRegionNames); + Assert.Equal("hand", stringHierarchyRegion.OptionedRegionNames[0]); + Assert.Empty(stringHierarchyRegion.Modifiers); + Assert.False(stringHierarchyRegion.HasModifiers); + Assert.Empty(stringHierarchyRegion.Subregions); + Assert.False(stringHierarchyRegion.HasSubregions); + Assert.True(stringHierarchyRegion.IsLeaf); + Assert.Empty(stringHierarchyRegion.SavedLocations); + Assert.Empty(stringHierarchyRegion.SavedAreas); + } } From 9d6fcedcea61f190ec5d709701d061eb90e7860b Mon Sep 17 00:00:00 2001 From: cld99 <115433496+cld99@users.noreply.github.com> Date: Thu, 7 Nov 2024 01:35:47 -0500 Subject: [PATCH 03/34] Add remaining test cases --- .../StringHierarchyRegionTests.cs | 460 +++++++++++++++++- 1 file changed, 453 insertions(+), 7 deletions(-) diff --git a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs index e557033..e475455 100644 --- a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs +++ b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs @@ -1,5 +1,5 @@ using EStimLibrary.Extensions.SpatialModel.StringHierarchy; - +using Xunit.Sdk; namespace EStimLibrary.UnitTests.Extensions.SpatialModel.StringHierarchy; @@ -23,15 +23,15 @@ public StringHierarchyRegionTests(ITestOutputHelper testOutputHelper) [Fact] public void Constructor_ShouldAcceptNull() { - var stringHierarchyRegion = new StringHierarchyRegion("hand", null, null, null, null, null); + var stringHierarchyRegion = new StringHierarchyRegion("base", null, null, null, null, null); - Assert.Equal("hand", stringHierarchyRegion.BaseName); + Assert.Equal("base", stringHierarchyRegion.BaseName); Assert.Null(stringHierarchyRegion.ParentRegion); Assert.Empty(stringHierarchyRegion.ParentOptions); Assert.Empty(stringHierarchyRegion.Options); Assert.False(stringHierarchyRegion.HasOptions); Assert.Single(stringHierarchyRegion.OptionedRegionNames); - Assert.Equal("hand", stringHierarchyRegion.OptionedRegionNames[0]); + Assert.Equal("base", stringHierarchyRegion.OptionedRegionNames[0]); Assert.Empty(stringHierarchyRegion.Modifiers); Assert.False(stringHierarchyRegion.HasModifiers); Assert.Empty(stringHierarchyRegion.Subregions); @@ -47,15 +47,41 @@ public void Constructor_ShouldAcceptNull() [Fact] public void Constructor_ShouldDefaultNull() { - var stringHierarchyRegion = new StringHierarchyRegion("hand", null); + var stringHierarchyRegion = new StringHierarchyRegion("base", null); - Assert.Equal("hand", stringHierarchyRegion.BaseName); + Assert.Equal("base", stringHierarchyRegion.BaseName); Assert.Null(stringHierarchyRegion.ParentRegion); Assert.Empty(stringHierarchyRegion.ParentOptions); Assert.Empty(stringHierarchyRegion.Options); Assert.False(stringHierarchyRegion.HasOptions); Assert.Single(stringHierarchyRegion.OptionedRegionNames); - Assert.Equal("hand", stringHierarchyRegion.OptionedRegionNames[0]); + Assert.Equal("base", stringHierarchyRegion.OptionedRegionNames[0]); + Assert.Empty(stringHierarchyRegion.Modifiers); + Assert.False(stringHierarchyRegion.HasModifiers); + Assert.Empty(stringHierarchyRegion.Subregions); + Assert.False(stringHierarchyRegion.HasSubregions); + Assert.True(stringHierarchyRegion.IsLeaf); + Assert.Empty(stringHierarchyRegion.SavedLocations); + Assert.Empty(stringHierarchyRegion.SavedAreas); + } + + /// + /// Test the constructor with non-null, empty parameter values. + /// + [Fact] + public void Constructor_ShouldAcceptEmpty() + { + var parent = new StringHierarchyRegion("root", null); + + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, new HashSet(), new Dictionary>(), new Dictionary()); + + Assert.Equal("base", stringHierarchyRegion.BaseName); + Assert.StrictEqual(parent, stringHierarchyRegion.ParentRegion); + Assert.Empty(stringHierarchyRegion.ParentOptions); + Assert.Empty(stringHierarchyRegion.Options); + Assert.False(stringHierarchyRegion.HasOptions); + Assert.Single(stringHierarchyRegion.OptionedRegionNames); + Assert.Equal("base", stringHierarchyRegion.OptionedRegionNames[0]); Assert.Empty(stringHierarchyRegion.Modifiers); Assert.False(stringHierarchyRegion.HasModifiers); Assert.Empty(stringHierarchyRegion.Subregions); @@ -64,4 +90,424 @@ public void Constructor_ShouldDefaultNull() Assert.Empty(stringHierarchyRegion.SavedLocations); Assert.Empty(stringHierarchyRegion.SavedAreas); } + + /// + /// Test the constructor with non-null, non-empty parameter values. + /// + [Fact] + public void Constructor_ShouldDeepCopy() + { + var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + var regionOptions = new HashSet() { "right", "left" }; + var regionModifiers = new Dictionary> + { + { "right", new HashSet() { "mod1", "mod2" } }, + { "left", new HashSet() { "mod3", "mod4" } } + }; + var regionSubregions = new Dictionary + { + { "child1", new StringHierarchyRegion("child1", null) }, + { "child2", new StringHierarchyRegion("child2", null) } + }; + + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); + + Assert.Equal("base", stringHierarchyRegion.BaseName); + Assert.Same(parent, stringHierarchyRegion.ParentRegion); + Assert.Equal(parent.Options, stringHierarchyRegion.ParentOptions); + Assert.NotSame(parent.Options, stringHierarchyRegion.ParentOptions); + Assert.Equal(regionOptions, stringHierarchyRegion.Options); + Assert.NotSame(regionOptions, stringHierarchyRegion.Options); + Assert.True(stringHierarchyRegion.HasOptions); + Assert.Equal(2, stringHierarchyRegion.OptionedRegionNames.Count); + Assert.Equal(new List() {$"right{StringHierarchySpec.OPTION_REGION_DELIMITER}base", $"left{StringHierarchySpec.OPTION_REGION_DELIMITER}base"}, stringHierarchyRegion.OptionedRegionNames); + Assert.Equal(regionModifiers, stringHierarchyRegion.Modifiers); + Assert.NotSame(regionModifiers, stringHierarchyRegion.Modifiers); + Assert.True(stringHierarchyRegion.HasModifiers); + Assert.Equal(regionSubregions, stringHierarchyRegion.Subregions); + Assert.NotSame(regionSubregions, stringHierarchyRegion.Subregions); + Assert.Same(regionSubregions["child1"], stringHierarchyRegion.Subregions["child1"]); + Assert.Same(regionSubregions["child2"], stringHierarchyRegion.Subregions["child2"]); + Assert.True(stringHierarchyRegion.HasSubregions); + Assert.False(stringHierarchyRegion.IsLeaf); + Assert.Empty(stringHierarchyRegion.SavedLocations); + Assert.Empty(stringHierarchyRegion.SavedAreas); + + var child = new StringHierarchyRegion("child", stringHierarchyRegion, stringHierarchyRegion.Options, new HashSet(), new Dictionary>(), new Dictionary()); + + Assert.Same(parent, stringHierarchyRegion.ParentRegion); + Assert.Equal(parent.Options, stringHierarchyRegion.ParentOptions); + } + + /// + /// Test the TryGetSubregion method with various malformed or otherwise incorrect regions. + /// + [Fact] + public void TryGetSubregion_ShouldOutputNull() + { + var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + var regionOptions = new HashSet() { "right", "left" }; + var regionModifiers = new Dictionary> + { + { "modSet1", new HashSet() { "mod1", "mod2" } }, + { "modSet2", new HashSet() { "mod3", "mod4" } } + }; + var regionSubregions = new Dictionary + { + { "child1", new StringHierarchyRegion("child1", null) }, + { "child2", new StringHierarchyRegion("child2", null) } + }; + + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); + + StringHierarchyRegion foundSubregion; + var output = stringHierarchyRegion.TryGetSubregion("", out foundSubregion); + + Assert.False(output); + Assert.Equal(null, foundSubregion); + + output = stringHierarchyRegion.TryGetSubregion("reg1, , reg2", out foundSubregion); + + Assert.False(output); + Assert.Equal(null, foundSubregion); + + output = stringHierarchyRegion.TryGetSubregion("reg1, reg2", out foundSubregion); + + Assert.False(output); + Assert.Equal(null, foundSubregion); + + output = stringHierarchyRegion.TryGetSubregion("opt1 opt2 reg1", out foundSubregion); + + Assert.False(output); + Assert.Equal(null, foundSubregion); + + output = stringHierarchyRegion.TryGetSubregion("opt1 reg1", out foundSubregion); + + Assert.False(output); + Assert.Equal(null, foundSubregion); + + output = stringHierarchyRegion.TryGetSubregion("base2", out foundSubregion); + + Assert.False(output); + Assert.Equal(null, foundSubregion); + + output = stringHierarchyRegion.TryGetSubregion("middle base", out foundSubregion); + + Assert.False(output); + Assert.Equal(null, foundSubregion); + + output = stringHierarchyRegion.TryGetSubregion("base", out foundSubregion); + + Assert.False(output); + Assert.Equal(null, foundSubregion); + + output = stringHierarchyRegion.TryGetSubregion("child1", out foundSubregion); + + Assert.False(output); + Assert.Equal(null, foundSubregion); + + + output = stringHierarchyRegion.TryGetSubregion("left base | modSet1", out foundSubregion); + + Assert.False(output); + Assert.Equal(null, foundSubregion); + } + + /// + /// Test the TryGetSubregion method with region present in current level and region present in subregion. + /// + [Fact] + public void TryGetSubregion_ShouldOutputRegion() + { + var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + var regionOptions = new HashSet() { "right", "left" }; + var regionModifiers = new Dictionary> + { + { "modSet1", new HashSet() { "mod1", "mod2" } }, + { "modSet2", new HashSet() { "mod3", "mod4" } } + }; + var subregionSubregions = new Dictionary + { + { "grandchild", new StringHierarchyRegion("grandchild", null) } + }; + var regionSubregions = new Dictionary + { + { "child1", new StringHierarchyRegion("child1", null, null, new HashSet { "opt1", "opt2" }, null, subregionSubregions) }, + { "child2", new StringHierarchyRegion("child2", null) } + }; + + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); + + StringHierarchyRegion foundSubregion; + var output = stringHierarchyRegion.TryGetSubregion("right base", out foundSubregion); + + Assert.True(output); + Assert.Equal(stringHierarchyRegion, foundSubregion); + + output = stringHierarchyRegion.TryGetSubregion("left base, child2", out foundSubregion); + + Assert.True(output); + Assert.Equal(regionSubregions["child2"], foundSubregion); + + output = stringHierarchyRegion.TryGetSubregion("left base, opt2 child1", out foundSubregion); + + Assert.True(output); + Assert.Equal(regionSubregions["child1"], foundSubregion); + + output = stringHierarchyRegion.TryGetSubregion("left base, opt2 child1, grandchild", out foundSubregion); + + Assert.True(output); + Assert.Equal(subregionSubregions["grandchild"], foundSubregion); + } + + /// + /// Test the AddSubregion method with new subregions. + /// + [Fact] + public void AddSubregion_ShouldAddSubregion() + { + var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + var regionOptions = new HashSet() { "right", "left" }; + var regionModifiers = new Dictionary> + { + { "modSet1", new HashSet() { "mod1", "mod2" } }, + { "modSet2", new HashSet() { "mod3", "mod4" } } + }; + var regionSubregions = new Dictionary + { + { "child1", new StringHierarchyRegion("child1", null) }, + { "child2", new StringHierarchyRegion("child2", null) } + }; + + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); + + StringHierarchyRegion oldRegion; + + //stringHierarchyRegion.AddSubregion(null, out oldRegion); + + var newChildRegion = new StringHierarchyRegion("child3", null); + + stringHierarchyRegion.AddSubregion(newChildRegion, out oldRegion); + + Assert.Equal(null, oldRegion); + Assert.Same(newChildRegion, stringHierarchyRegion.Subregions["child3"]); + } + + /// + /// Test the AddSubregion method with duplicate subregions. + /// + [Fact] + public void AddSubregion_ShouldReplaceSubregion() + { + var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + var regionOptions = new HashSet() { "right", "left" }; + var regionModifiers = new Dictionary> + { + { "modSet1", new HashSet() { "mod1", "mod2" } }, + { "modSet2", new HashSet() { "mod3", "mod4" } } + }; + var regionSubregions = new Dictionary + { + { "child1", new StringHierarchyRegion("child1", null) }, + { "child2", new StringHierarchyRegion("child2", null) } + }; + + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); + + StringHierarchyRegion oldRegion; + + stringHierarchyRegion.AddSubregion(regionSubregions["child2"], out oldRegion); + + Assert.Equal(regionSubregions["child2"], oldRegion); + Assert.Same(regionSubregions["child2"], stringHierarchyRegion.Subregions["child2"]); + + var newChildRegion1 = new StringHierarchyRegion("child1", null); + + stringHierarchyRegion.AddSubregion(newChildRegion1, out oldRegion); + + Assert.Equal(regionSubregions["child1"], oldRegion); + Assert.Same(newChildRegion1, stringHierarchyRegion.Subregions["child1"]); + + var newChildRegion2 = new StringHierarchyRegion("child2", null, null, regionOptions); + + stringHierarchyRegion.AddSubregion(newChildRegion2, out oldRegion); + + Assert.Equal(regionSubregions["child2"], oldRegion); + Assert.Same(newChildRegion2, stringHierarchyRegion.Subregions["child2"]); + } + + /// + /// Test the DeepCopy method with retaining the parent reference. + /// + [Fact] + // Test retain and not retain reference; test for null, empty, and normal (check for deep copy) + public void DeepCopy_ShouldDeepCopy() + { + var nullRegion = new StringHierarchyRegion("base", null); + var emptyRegion = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + var regionOptions = new HashSet() { "right", "left" }; + var regionModifiers = new Dictionary> + { + { "modSet1", new HashSet() { "mod1", "mod2" } }, + { "modSet2", new HashSet() { "mod3", "mod4" } } + }; + var regionSubregions = new Dictionary + { + { "child1", new StringHierarchyRegion("child1", null) }, + { "child2", new StringHierarchyRegion("child2", null) } + }; + + var stringHierarchyRegion = new StringHierarchyRegion("base", emptyRegion, emptyRegion.Options, regionOptions, regionModifiers, regionSubregions); + + var nullRegionCopy = nullRegion.DeepCopy(true); + + Assert.Equivalent(nullRegionCopy, nullRegion, true); + Assert.NotEqual(nullRegionCopy, nullRegion); + Assert.NotSame(nullRegionCopy, nullRegion); + + var emptyRegionCopy = emptyRegion.DeepCopy(true); + + Assert.Equivalent(emptyRegionCopy, emptyRegion, true); + Assert.NotEqual(emptyRegionCopy, emptyRegion); + Assert.NotSame(emptyRegionCopy, emptyRegion); + + var stringHierarchyRegionCopy = stringHierarchyRegion.DeepCopy(true); + + Assert.Equivalent(stringHierarchyRegionCopy, stringHierarchyRegion, true); + Assert.NotEqual(stringHierarchyRegionCopy, stringHierarchyRegion); + Assert.NotSame(stringHierarchyRegionCopy, stringHierarchyRegion); + } + + /// + /// Test the DeepCopy method with retaining the parent reference. + /// + [Fact] + // Test retain and not retain reference; test for null, empty, and normal (check for deep copy) + public void DeepCopy_ShouldDeepCopyResetParent() + { + var nullRegion = new StringHierarchyRegion("base", null); + var parent = new StringHierarchyRegion("root", nullRegion, null, new HashSet(), new Dictionary>(), new Dictionary()); + var regionOptions = new HashSet() { "right", "left" }; + var regionModifiers = new Dictionary> + { + { "modSet1", new HashSet() { "mod1", "mod2" } }, + { "modSet2", new HashSet() { "mod3", "mod4" } } + }; + var regionSubregions = new Dictionary + { + { "child1", new StringHierarchyRegion("child1", null) }, + { "child2", new StringHierarchyRegion("child2", null) } + }; + + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); + + var parentCopy = parent.DeepCopy(false); + + Assert.NotEqual(parentCopy.ParentRegion, parent.ParentRegion); + parent.ParentRegion = null; + Assert.Equal(parentCopy.ParentRegion, parent.ParentRegion); + Assert.Equivalent(parentCopy, parent, true); + Assert.NotEqual(parentCopy, parent); + Assert.NotSame(parentCopy, parent); + + var stringHierarchyRegionCopy = stringHierarchyRegion.DeepCopy(false); + + Assert.NotEqual(stringHierarchyRegionCopy.ParentRegion, stringHierarchyRegion.ParentRegion); + stringHierarchyRegion.ParentRegion = null; + Assert.Equal(stringHierarchyRegionCopy.ParentRegion, stringHierarchyRegion.ParentRegion); + Assert.Equivalent(stringHierarchyRegionCopy, stringHierarchyRegion, true); + Assert.NotEqual(stringHierarchyRegionCopy, stringHierarchyRegion); + Assert.NotSame(stringHierarchyRegionCopy, stringHierarchyRegion); + } + + /// + /// Test the IsValidModifierSpec method with invalid modifier specs. + /// + [Fact] + public void IsValidModifierSpec_ShouldOutputFalse() + { + var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + var regionOptions = new HashSet() { "right", "left" }; + var regionModifiers = new Dictionary> + { + { "modSet1", new HashSet() { "mod1", "mod2" } }, + { "modSet2", new HashSet() { "mod3", "mod4" } } + }; + var regionSubregions = new Dictionary + { + { "child1", new StringHierarchyRegion("child1", null) }, + { "child2", new StringHierarchyRegion("child2", null) } + }; + + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); + + var modSpec1 = ""; + var modSpec2 = "mod5"; + var modSpec3 = "mod1, mod2"; + var modSpec4 = "mod1, mod2, mod3"; + + Assert.False(stringHierarchyRegion.IsValidModifierSpec(modSpec1)); + Assert.False(stringHierarchyRegion.IsValidModifierSpec(modSpec2)); + Assert.False(stringHierarchyRegion.IsValidModifierSpec(modSpec3)); + Assert.False(stringHierarchyRegion.IsValidModifierSpec(modSpec4)); + } + + /// + /// Test the IsValidModifierSpec method with valid modifier specs. + /// + [Fact] + public void IsValidModifierSpec_ShouldOutputTrue() + { + var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + var regionOptions = new HashSet() { "right", "left" }; + var regionModifiers = new Dictionary> + { + { "modSet1", new HashSet() { "mod1", "mod2" } }, + { "modSet2", new HashSet() { "mod3", "mod4" } } + }; + var regionSubregions = new Dictionary + { + { "child1", new StringHierarchyRegion("child1", null) }, + { "child2", new StringHierarchyRegion("child2", null) } + }; + + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); + + var modSpec1 = "mod3"; + var modSpec2 = "mod1, mod4"; + + Assert.True(stringHierarchyRegion.IsValidModifierSpec(modSpec1)); + Assert.True(stringHierarchyRegion.IsValidModifierSpec(modSpec2)); + } + + /// + /// Test the ToString method. + /// + [Fact] + public void ToString_ShouldOutputStringRep() + { + var nullRegion = new StringHierarchyRegion("base", null); + var emptyRegion = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + var regionOptions = new HashSet() { "right", "left" }; + var regionModifiers = new Dictionary> + { + { "modSet1", new HashSet() { "mod1", "mod2" } }, + { "modSet2", new HashSet() { "mod3", "mod4" } } + }; + var subregionSubregions = new Dictionary + { + { "grandchild", new StringHierarchyRegion("grandchild", null) } + }; + var regionSubregions = new Dictionary + { + { "child1", new StringHierarchyRegion("child1", null, null, new HashSet { "opt1", "opt2" }, null, subregionSubregions) }, + { "child2", new StringHierarchyRegion("child2", null) } + }; + + var stringHierarchyRegion = new StringHierarchyRegion("base", emptyRegion, emptyRegion.Options, regionOptions, regionModifiers, regionSubregions); + + Assert.Equal("base", nullRegion.ToString()); + Assert.Equal("root", emptyRegion.ToString()); + Assert.Equal("[right,left] base | [mod1,mod2], [mod3,mod4]\n [right,left] base, [opt1,opt2] child1\n [right,left] base, [opt1,opt2] child1, grandchild\n [right,left] base, child2", stringHierarchyRegion.ToString()); + } } From d0ee51d9842a7ed83eef2dc8c31e2160440c496e Mon Sep 17 00:00:00 2001 From: cld99 <115433496+cld99@users.noreply.github.com> Date: Fri, 8 Nov 2024 23:51:20 -0500 Subject: [PATCH 04/34] Finalize test cases and add null argument checking to AddSubregion --- .../StringHierarchy/StringHierarchyRegion.cs | 8 +- .../StringHierarchyRegionTests.cs | 145 ++++++++++-------- 2 files changed, 88 insertions(+), 65 deletions(-) diff --git a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs index fe56217..5c477d2 100644 --- a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs +++ b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs @@ -1,4 +1,6 @@ -namespace EStimLibrary.Extensions.SpatialModel.StringHierarchy; +using System; + +namespace EStimLibrary.Extensions.SpatialModel.StringHierarchy; ///
@@ -120,6 +122,10 @@ public StringHierarchyRegion(string baseName, StringHierarchyRegion parent, public void AddSubregion(StringHierarchyRegion subregion, out StringHierarchyRegion? existingSubregion) { + /// The provided + /// subregion argument is null. + if (subregion == null) throw new ArgumentNullException(); + // Fill the out parameter with the existing subregion if exists. if (this.Subregions.TryGetValue(subregion.BaseName, out existingSubregion)) diff --git a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs index e475455..134e569 100644 --- a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs +++ b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs @@ -1,4 +1,5 @@ using EStimLibrary.Extensions.SpatialModel.StringHierarchy; +using System.Runtime.InteropServices; using Xunit.Sdk; namespace EStimLibrary.UnitTests.Extensions.SpatialModel.StringHierarchy; @@ -139,6 +140,37 @@ public void Constructor_ShouldDeepCopy() Assert.Equal(parent.Options, stringHierarchyRegion.ParentOptions); } + /// + /// Test the ToString method. + /// + [Fact] + public void ToString_ShouldOutputStringRep() + { + var nullRegion = new StringHierarchyRegion("base", null); + var emptyRegion = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + var regionOptions = new HashSet() { "right", "left" }; + var regionModifiers = new Dictionary> + { + { "modSet1", new HashSet() { "mod1", "mod2" } }, + { "modSet2", new HashSet() { "mod3", "mod4" } } + }; + var subregionSubregions = new Dictionary + { + { "grandchild", new StringHierarchyRegion("grandchild", null) } + }; + var regionSubregions = new Dictionary + { + { "child1", new StringHierarchyRegion("child1", null, null, new HashSet { "opt1", "opt2" }, null, subregionSubregions) }, + { "child2", new StringHierarchyRegion("child2", null) } + }; + + var stringHierarchyRegion = new StringHierarchyRegion("base", emptyRegion, emptyRegion.Options, regionOptions, regionModifiers, regionSubregions); + + Assert.Equal("base", nullRegion.ToString()); + Assert.Equal("root", emptyRegion.ToString()); + Assert.Equal("[right,left] base | [mod1,mod2], [mod3,mod4]\n [right,left] base, [opt1,opt2] child1\n [right,left] base, [opt1,opt2] child1, grandchild\n [right,left] base, child2", stringHierarchyRegion.ToString()); + } + /// /// Test the TryGetSubregion method with various malformed or otherwise incorrect regions. /// @@ -164,53 +196,53 @@ public void TryGetSubregion_ShouldOutputNull() var output = stringHierarchyRegion.TryGetSubregion("", out foundSubregion); Assert.False(output); - Assert.Equal(null, foundSubregion); + Assert.Null(foundSubregion); output = stringHierarchyRegion.TryGetSubregion("reg1, , reg2", out foundSubregion); Assert.False(output); - Assert.Equal(null, foundSubregion); + Assert.Null(foundSubregion); output = stringHierarchyRegion.TryGetSubregion("reg1, reg2", out foundSubregion); Assert.False(output); - Assert.Equal(null, foundSubregion); + Assert.Null(foundSubregion); output = stringHierarchyRegion.TryGetSubregion("opt1 opt2 reg1", out foundSubregion); Assert.False(output); - Assert.Equal(null, foundSubregion); + Assert.Null(foundSubregion); output = stringHierarchyRegion.TryGetSubregion("opt1 reg1", out foundSubregion); Assert.False(output); - Assert.Equal(null, foundSubregion); + Assert.Null(foundSubregion); output = stringHierarchyRegion.TryGetSubregion("base2", out foundSubregion); Assert.False(output); - Assert.Equal(null, foundSubregion); + Assert.Null(foundSubregion); output = stringHierarchyRegion.TryGetSubregion("middle base", out foundSubregion); Assert.False(output); - Assert.Equal(null, foundSubregion); + Assert.Null(foundSubregion); output = stringHierarchyRegion.TryGetSubregion("base", out foundSubregion); Assert.False(output); - Assert.Equal(null, foundSubregion); + Assert.Null(foundSubregion); output = stringHierarchyRegion.TryGetSubregion("child1", out foundSubregion); Assert.False(output); - Assert.Equal(null, foundSubregion); + Assert.Null(foundSubregion); output = stringHierarchyRegion.TryGetSubregion("left base | modSet1", out foundSubregion); Assert.False(output); - Assert.Equal(null, foundSubregion); + Assert.Null(foundSubregion); } /// @@ -283,13 +315,19 @@ public void AddSubregion_ShouldAddSubregion() StringHierarchyRegion oldRegion; - //stringHierarchyRegion.AddSubregion(null, out oldRegion); + var errorSuccess = false; + try + { + stringHierarchyRegion.AddSubregion(null, out oldRegion); + } + catch (ArgumentNullException ex) { errorSuccess = true; } + Assert.True(errorSuccess); var newChildRegion = new StringHierarchyRegion("child3", null); stringHierarchyRegion.AddSubregion(newChildRegion, out oldRegion); - Assert.Equal(null, oldRegion); + Assert.Null(oldRegion); Assert.Same(newChildRegion, stringHierarchyRegion.Subregions["child3"]); } @@ -340,7 +378,6 @@ public void AddSubregion_ShouldReplaceSubregion() /// Test the DeepCopy method with retaining the parent reference. /// [Fact] - // Test retain and not retain reference; test for null, empty, and normal (check for deep copy) public void DeepCopy_ShouldDeepCopy() { var nullRegion = new StringHierarchyRegion("base", null); @@ -361,28 +398,33 @@ public void DeepCopy_ShouldDeepCopy() var nullRegionCopy = nullRegion.DeepCopy(true); - Assert.Equivalent(nullRegionCopy, nullRegion, true); - Assert.NotEqual(nullRegionCopy, nullRegion); - Assert.NotSame(nullRegionCopy, nullRegion); + Assert.Equivalent(nullRegion, nullRegionCopy, strict: true); + Assert.NotEqual(nullRegion, nullRegionCopy); + Assert.NotSame(nullRegion, nullRegionCopy); var emptyRegionCopy = emptyRegion.DeepCopy(true); - Assert.Equivalent(emptyRegionCopy, emptyRegion, true); - Assert.NotEqual(emptyRegionCopy, emptyRegion); - Assert.NotSame(emptyRegionCopy, emptyRegion); + Assert.Equivalent(emptyRegion, emptyRegionCopy, strict: true); + Assert.NotEqual(emptyRegion, emptyRegionCopy); + Assert.NotSame(emptyRegion, emptyRegionCopy); var stringHierarchyRegionCopy = stringHierarchyRegion.DeepCopy(true); - Assert.Equivalent(stringHierarchyRegionCopy, stringHierarchyRegion, true); - Assert.NotEqual(stringHierarchyRegionCopy, stringHierarchyRegion); - Assert.NotSame(stringHierarchyRegionCopy, stringHierarchyRegion); + Assert.Equal(stringHierarchyRegion.ToString(), stringHierarchyRegionCopy.ToString()); + Assert.Same(stringHierarchyRegion.ParentRegion, stringHierarchyRegionCopy.ParentRegion); + Assert.Equal(stringHierarchyRegion.Options, stringHierarchyRegionCopy.Options); + Assert.NotSame(stringHierarchyRegion.Options, stringHierarchyRegionCopy.Options); + Assert.Equal(stringHierarchyRegion.Modifiers, stringHierarchyRegionCopy.Modifiers); + Assert.NotSame(stringHierarchyRegion.Modifiers, stringHierarchyRegionCopy.Modifiers); + // Testing Note: Due to the presence of parentRegion in each of the subregions and due to the deep copying of the subregions themselves, it is not feasible to check equality or equivalence of the subregions + Assert.NotEqual(stringHierarchyRegion.Subregions, stringHierarchyRegionCopy.Subregions); + Assert.NotSame(stringHierarchyRegion.Subregions, stringHierarchyRegionCopy.Subregions); } /// /// Test the DeepCopy method with retaining the parent reference. /// [Fact] - // Test retain and not retain reference; test for null, empty, and normal (check for deep copy) public void DeepCopy_ShouldDeepCopyResetParent() { var nullRegion = new StringHierarchyRegion("base", null); @@ -403,21 +445,27 @@ public void DeepCopy_ShouldDeepCopyResetParent() var parentCopy = parent.DeepCopy(false); - Assert.NotEqual(parentCopy.ParentRegion, parent.ParentRegion); + Assert.NotEqual(parent.ParentRegion, parentCopy.ParentRegion); parent.ParentRegion = null; - Assert.Equal(parentCopy.ParentRegion, parent.ParentRegion); - Assert.Equivalent(parentCopy, parent, true); - Assert.NotEqual(parentCopy, parent); - Assert.NotSame(parentCopy, parent); + Assert.Equal(parent.ParentRegion, parentCopy.ParentRegion); + Assert.Equivalent(parent, parentCopy, strict: true); + Assert.NotEqual(parent, parentCopy); + Assert.NotSame(parent, parentCopy); var stringHierarchyRegionCopy = stringHierarchyRegion.DeepCopy(false); - Assert.NotEqual(stringHierarchyRegionCopy.ParentRegion, stringHierarchyRegion.ParentRegion); + Assert.NotEqual(stringHierarchyRegion.ParentRegion, stringHierarchyRegionCopy.ParentRegion); stringHierarchyRegion.ParentRegion = null; - Assert.Equal(stringHierarchyRegionCopy.ParentRegion, stringHierarchyRegion.ParentRegion); - Assert.Equivalent(stringHierarchyRegionCopy, stringHierarchyRegion, true); - Assert.NotEqual(stringHierarchyRegionCopy, stringHierarchyRegion); - Assert.NotSame(stringHierarchyRegionCopy, stringHierarchyRegion); + Assert.Equal(stringHierarchyRegion.ParentRegion, stringHierarchyRegionCopy.ParentRegion); + Assert.Equal(stringHierarchyRegion.ToString(), stringHierarchyRegionCopy.ToString()); + Assert.Same(stringHierarchyRegion.ParentRegion, stringHierarchyRegionCopy.ParentRegion); + Assert.Equal(stringHierarchyRegion.Options, stringHierarchyRegionCopy.Options); + Assert.NotSame(stringHierarchyRegion.Options, stringHierarchyRegionCopy.Options); + Assert.Equal(stringHierarchyRegion.Modifiers, stringHierarchyRegionCopy.Modifiers); + Assert.NotSame(stringHierarchyRegion.Modifiers, stringHierarchyRegionCopy.Modifiers); + // Testing Note: Due to the presence of parentRegion in each of the subregions and due to the deep copying of the subregions themselves, it is not feasible to check equality or equivalence of the subregions + Assert.NotEqual(stringHierarchyRegion.Subregions, stringHierarchyRegionCopy.Subregions); + Assert.NotSame(stringHierarchyRegion.Subregions, stringHierarchyRegionCopy.Subregions); } /// @@ -479,35 +527,4 @@ public void IsValidModifierSpec_ShouldOutputTrue() Assert.True(stringHierarchyRegion.IsValidModifierSpec(modSpec1)); Assert.True(stringHierarchyRegion.IsValidModifierSpec(modSpec2)); } - - /// - /// Test the ToString method. - /// - [Fact] - public void ToString_ShouldOutputStringRep() - { - var nullRegion = new StringHierarchyRegion("base", null); - var emptyRegion = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); - var regionOptions = new HashSet() { "right", "left" }; - var regionModifiers = new Dictionary> - { - { "modSet1", new HashSet() { "mod1", "mod2" } }, - { "modSet2", new HashSet() { "mod3", "mod4" } } - }; - var subregionSubregions = new Dictionary - { - { "grandchild", new StringHierarchyRegion("grandchild", null) } - }; - var regionSubregions = new Dictionary - { - { "child1", new StringHierarchyRegion("child1", null, null, new HashSet { "opt1", "opt2" }, null, subregionSubregions) }, - { "child2", new StringHierarchyRegion("child2", null) } - }; - - var stringHierarchyRegion = new StringHierarchyRegion("base", emptyRegion, emptyRegion.Options, regionOptions, regionModifiers, regionSubregions); - - Assert.Equal("base", nullRegion.ToString()); - Assert.Equal("root", emptyRegion.ToString()); - Assert.Equal("[right,left] base | [mod1,mod2], [mod3,mod4]\n [right,left] base, [opt1,opt2] child1\n [right,left] base, [opt1,opt2] child1, grandchild\n [right,left] base, child2", stringHierarchyRegion.ToString()); - } } From b7e1e56459813eb790dc5576dd0fccf9bfa2b595 Mon Sep 17 00:00:00 2001 From: Dorian Hawkins <123764790+Blubby24@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:58:26 -0500 Subject: [PATCH 05/34] Tests/lead (#33) * Creating a branch for lead testing * Testing SSH signing * Improved the tests for get connected outputs * Improved the tests for get connected contacts * Improved the tests for IsFullyIndependent * Finished adding complete tests for all methods * Fixed bug in GetConnectedContacts and GetConnectedOutputs * Changed formating of test lead file * completed requested changes * Lead test formatting edits and a few new test cases * Lead test comment edits * Lead small implementation simplification --------- Co-authored-by: Laura McGann --- .../Core/HardwareInterfaces/Lead.cs | 32 +- .../Core/HardwareInterfaces/LeadTests.cs | 1103 +++++++++++++++++ .../EStimLibrary.UnitTests.csproj | 3 - 3 files changed, 1123 insertions(+), 15 deletions(-) create mode 100644 tests/EStimLibrary.UnitTests/Core/HardwareInterfaces/LeadTests.cs diff --git a/src/EStimLibrary/Core/HardwareInterfaces/Lead.cs b/src/EStimLibrary/Core/HardwareInterfaces/Lead.cs index 9a155e3..2e0d4db 100644 --- a/src/EStimLibrary/Core/HardwareInterfaces/Lead.cs +++ b/src/EStimLibrary/Core/HardwareInterfaces/Lead.cs @@ -1,7 +1,4 @@ -using EStimLibrary.Core; - - -namespace EStimLibrary.Core.HardwareInterfaces; +namespace EStimLibrary.Core.HardwareInterfaces; /// @@ -21,9 +18,18 @@ public record Lead(SortedSet ContactSet, SortedSet OutputSet, Constants.CurrentDirection CurrentDirection) : IIdentifiable { - // Manager-given ID of the lead, -1 if unset. - public int Id => this._Id; // IIdentifiable - internal int _Id = -1; // to be set by the manager. + + /// + /// The manager-given ID of the lead. + /// + /// The ID of the lead, or -1 if unset. + public int Id => this._Id; // IIdentifiable + + /// + /// Internal storage for the lead ID, to be set by the manager. + /// + /// The ID of the lead, default is -1. + internal int _Id = -1; // to be set by the manager. /// /// Get which outputs are connected to a given output or contact by this @@ -44,11 +50,12 @@ public bool GetConnectedOutputs(int id, bool searchIsAnOutput, // Output the set of outputs even if the requested ID is invalid. connectedOutputs = new(this.OutputSet); - var validId = false; + bool validId; // Search by output or contact ID, respectively. - if (searchIsAnOutput && (validId = this.OutputSet.Contains(id))) + if (searchIsAnOutput) { // Exclude the search output ID from the returned set. + validId = this.OutputSet.Contains(id); connectedOutputs.ExceptWith(new int[] { id }); } else @@ -71,7 +78,7 @@ public bool GetConnectedOutputs(int id, bool searchIsAnOutput, /// contact, False if the given search ID is of an output. /// An output parameter: the set of /// contacts connected to the searched contact or output. If a contact was - /// searched, the set will exclude that contact. If the method returns False, + /// searched, the set will exclude that contact. If the method returns False /// this will just be the set of all contacts in this Lead. /// True if the given search ID was found in this Lead and the /// returned contact ID set is valid, False if not. @@ -81,10 +88,11 @@ public bool GetConnectedContacts(int id, bool searchIsAContact, // Output the set of contacts even if the requested ID is invalid. connectedContacts = new(this.ContactSet); - var validId = false; + bool validId; // Search by contact or output ID, respectively. - if (searchIsAContact && (validId = this.ContactSet.Contains(id))) + if (searchIsAContact) { + validId = this.ContactSet.Contains(id); // Exclude the search contact ID from the returned set. connectedContacts.ExceptWith(new int[] { id }); } diff --git a/tests/EStimLibrary.UnitTests/Core/HardwareInterfaces/LeadTests.cs b/tests/EStimLibrary.UnitTests/Core/HardwareInterfaces/LeadTests.cs new file mode 100644 index 0000000..647ddc4 --- /dev/null +++ b/tests/EStimLibrary.UnitTests/Core/HardwareInterfaces/LeadTests.cs @@ -0,0 +1,1103 @@ +using EStimLibrary.Core; +using EStimLibrary.Core.HardwareInterfaces; + + +namespace EStimLibrary.UnitTests.Core.HardwareInterfaces; + + +/// +/// A class for unit testing the Lead class +/// +public class LeadTests +{ + /// + /// Test the parameterized Lead constructor with different data values. + /// + /// The set of global contact IDs the lead is + /// connected to. Assumed to be valid. + /// The set of global output IDs the lead is + /// connected to. Assumed to be valid. + /// The default direction of current on the + /// lead. + [Theory] + [MemberData(nameof(Constructor_TestData))] + public void Constructor_ShouldInitSortedSetsAndDirection( + SortedSet contactSet, SortedSet outputSet, + Constants.CurrentDirection currentDirection) + { + // Act + var lead = new Lead(contactSet, outputSet, currentDirection); + + // Assert + Assert.Equal(contactSet, lead.ContactSet); + Assert.Equal(outputSet, lead.OutputSet); + Assert.Equal(currentDirection, lead.CurrentDirection); + } + + /// + /// Test parameter data for the Lead constructor, following the form: + /// globalContactIdSet, + /// globalOutputIdSet, + /// defaultCurrentDirection + /// Assuming all global IDs are valid (existing non-negative integers) in + /// the session. + /// + public static IEnumerable Constructor_TestData() + { + return new List + { + // 1:1 mapping + new object[] + { + new SortedSet { 1 }, + new SortedSet { 0 }, + Constants.CurrentDirection.SOURCE + }, + // 1:many mapping; non-consecutive + new object[] + { + new SortedSet { 1 }, + new SortedSet { 0, 2, 4 }, + Constants.CurrentDirection.SOURCE + }, + // many:1 mapping; non-consecutive; double-digit IDs + new object[] + { + new SortedSet { 1, 3, 6, 10, 21 }, + new SortedSet { 0 }, + Constants.CurrentDirection.SOURCE + }, + // many:many mapping, same number + new object[] + { + new SortedSet { 1, 2, 3 }, + new SortedSet { 0, 2, 3 }, + Constants.CurrentDirection.SOURCE + }, + // Different output and contact set sizes + new object[] + { + new SortedSet { 4, 5, 6 }, + new SortedSet { 1, 2 }, + Constants.CurrentDirection.SOURCE + }, + // Different current direction + new object[] + { + new SortedSet { 7, 8, 9 }, + new SortedSet { 3, 5, 7 }, + Constants.CurrentDirection.SINK + } + }; + } + + /// + /// Test for GetConnectedOutputs. + /// + /// The output or contact ID to check the connections of. + /// + /// Indicates if the search is for an output + /// (true) or a contact (false). + /// The lead instance to test. + /// The expected output IDs found. + /// The expected boolean result of the search. + /// + [Theory] + [MemberData(nameof(GetConnectedOutputs_TestData))] + public void GetConnectedOutputs_ShouldReturnExpectedResults(int id, + bool searchIsAnOutput, Lead lead, SortedSet expectedOutputs, + bool expectedResult) + { + var originalOutputs = lead.OutputSet; + var originalContacts = lead.ContactSet; + + var expectedOriginalOutputs = new SortedSet(originalOutputs); + var expectedOriginalContacts = new SortedSet(originalContacts); + + var result = lead.GetConnectedOutputs(id, searchIsAnOutput, + out var connectedOutputs); + + // Make sure method outputs are correct. + Assert.Equal(expectedResult, result); + Assert.Equal(expectedOutputs, connectedOutputs); + + // Make sure the values of the lead's output and contact sets have not + // changed. + Assert.Equal(expectedOriginalOutputs, lead.OutputSet); + Assert.Equal(expectedOriginalContacts, lead.ContactSet); + // Make sure the objects (instances) of the lead's output and contact + // sets have not been replaced. + Assert.Same(originalOutputs, lead.OutputSet); + Assert.Same(originalContacts, lead.ContactSet); + } + + + /// + /// Test parameter data for GetConnectedOutputs, following the form: + /// id (to search for), + /// boolean (searchIsAContact), + /// leadInstance, + /// globalOutputIdSet, + /// boolean (expectedResult) + /// Assuming all global IDs and leads are valid in the session. + /// + public static IEnumerable GetConnectedOutputs_TestData() + { + return new List + { + #region Search by Output ID + // Testing with an output as input when that output is connected to + // the lead. We expect true and an output set without the given + // output. + new object[] + { + 3, + true, + new Lead(new SortedSet { 1, 2, 3 }, + new SortedSet { 3, 4, 5 }, + Constants.CurrentDirection.SINK), + new SortedSet { 4, 5 }, + true + }, + // Test when contactSet is much smaller than outputSet + new object[] + { + 8, + true, + new Lead(new SortedSet { 1 }, + new SortedSet { 3, 4, 5, 6, 8 }, + Constants.CurrentDirection.SINK), + new SortedSet { 3, 4, 5, 6 }, + true + }, + // Test when outputSet is much smaller than contactSet + new object[] + { + 3, + true, + new Lead(new SortedSet { 2, 3, 4, 8 }, + new SortedSet { 3 }, + Constants.CurrentDirection.SINK), + new SortedSet { }, + true + }, + // Testing with an output ID that is not connected to the lead + // (although there is a connected contact of that ID). We expect to + // get false and a full output set. + new object[] + { + 6, + true, + new Lead(new SortedSet { 1, 2, 6 }, + new SortedSet { 3, 4, 5 }, + Constants.CurrentDirection.SOURCE), + new SortedSet { 3, 4, 5 }, + false + }, + // Testing with an empty outputSet, so output ID DNE. + new object[] + { + 6, + true, + new Lead(new SortedSet { 1, 2 }, + new SortedSet { }, + Constants.CurrentDirection.SOURCE), + new SortedSet { }, + false + }, + // Testing with a very large outputSet. Output ID DNE. + new object[] + { + 1, + true, + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 2, 3, 4, 5, 6, 7, 8 }, + Constants.CurrentDirection.SOURCE), + new SortedSet { 2, 3, 4, 5, 6, 7, 8 }, + false + }, + #endregion Search by Output ID + #region Search by Contact ID + // Testing with a connected contact ID. ID also exists as a + // connected output ID. We expect to get true but a full output + // set. + new object[] + { + 1, + false, + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 1, 3, 4, 5 }, + Constants.CurrentDirection.SINK), + new SortedSet { 1, 3, 4, 5 }, + true + }, + // Testing with many things in the outputSet + new object[] + { + 2, + false, + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 3, 4, 5, 6, 7 }, + Constants.CurrentDirection.SINK), + new SortedSet { 3, 4, 5, 6, 7 }, + true + }, + // Testing with an empty outputSet but contact ID found. + new object[] + { + 2, + false, + new Lead(new SortedSet { 2 }, + new SortedSet { }, + Constants.CurrentDirection.SINK), + new SortedSet { }, + true + }, + // Testing with only one ID in the outputSet + new object[] + { + 2, + false, + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 1 }, + Constants.CurrentDirection.SINK), + new SortedSet { 1 }, + true + }, + // Testing with a contact as the input which is not connected to + // the lead. We expect to get false and a full output set + new object[] + { + 1, + false, + new Lead(new SortedSet { 2 }, + new SortedSet { 3, 4, 5 }, + Constants.CurrentDirection.SINK), + new SortedSet { 3, 4, 5 }, + false + }, + // Testing with an empty contact set + new object[] + { + 2, + false, + new Lead(new SortedSet { }, + new SortedSet { 3, 4, 5, 6, 7 }, + Constants.CurrentDirection.SINK), + new SortedSet { 3, 4, 5, 6, 7 }, + false + }, + // Testing with an empty outputSet and contact ID not found + new object[] + { + 2, + false, + new Lead(new SortedSet { 9 }, + new SortedSet { }, + Constants.CurrentDirection.SINK), + new SortedSet { }, + false + }, + // Testing with a large contactSet and an outputSet with only one + // element in it + new object[] + { + 2, + false, + new Lead(new SortedSet { 5, 6, 7, 8 }, + new SortedSet { 1 }, + Constants.CurrentDirection.SINK), + new SortedSet { 1 }, + false + } + #endregion Search by Contact ID + }; + } + + /// + /// Test for GetConnectedContacts. + /// + /// The contact or output ID to check the connections of. + /// + /// Indicates if the search is for a contact + /// (true) or an output (false). + /// The lead instance to test. + /// The expected contact IDs found. + /// The expected boolean result of the search. + /// + [Theory] + [MemberData(nameof(GetConnectedContacts_TestData))] + public void GetConnectedContacts_ShouldReturnExpectedResults(int id, + bool searchIsAContact, Lead lead, SortedSet expectedContacts, + bool expectedResult) + { + var originalOutputs = lead.OutputSet; + var originalContacts = lead.ContactSet; + + var expectedOriginalOutputs = new SortedSet(originalOutputs); + var expectedOriginalContacts = new SortedSet(originalContacts); + + var result = lead.GetConnectedContacts(id, searchIsAContact, + out var connectedContacts); + + // Make sure method outputs are correct. + Assert.Equal(expectedResult, result); + Assert.Equal(expectedContacts, connectedContacts); + + // Make sure the values of the lead's output and contact sets have not + // changed. + Assert.Equal(expectedOriginalOutputs, lead.OutputSet); + Assert.Equal(expectedOriginalContacts, lead.ContactSet); + // Make sure the objects (instances) of the lead's output and contact + // sets have not been replaced. + Assert.Same(originalOutputs, lead.OutputSet); + Assert.Same(originalContacts, lead.ContactSet); + } + + /// + /// Test parameter data for GetConnectedContacts, following the form: + /// id, + /// boolean (searchIsAContact), + /// leadInstance, + /// globalContactIdSet, + /// boolean (expectedResult) + /// Assuming all global IDs and leads are valid in the session. + /// + public static IEnumerable GetConnectedContacts_TestData() + { + return new List + { + #region Search by Contact ID + // Testing when a connected contact ID is given. We expect true and + // a non-inclusive set of contacts. + new object[] + { + 3, + true, + new Lead(new SortedSet { 1, 2, 3 }, + new SortedSet { 3, 4, 5 }, + Constants.CurrentDirection.SINK), + new SortedSet { 1, 2 }, + true + }, + // Testing with only one ID in the contactSet + new object[] + { + 2, + true, + new Lead(new SortedSet { 2 }, + new SortedSet { 3, 4, 5 }, + Constants.CurrentDirection.SOURCE), + new SortedSet { }, + true + }, + // Testing with an empty outputSet + new object[] + { + 3, + true, + new Lead(new SortedSet { 2, 3 }, + new SortedSet { }, + Constants.CurrentDirection.SINK), + new SortedSet { 2 }, + true + }, + // Testing with only one contact in the contactSet + new object[] + { + 3, + true, + new Lead(new SortedSet { 3 }, + new SortedSet { 2, 3, 4 }, + Constants.CurrentDirection.SOURCE), + new SortedSet { }, + true + }, + // Testing when an unconnected contact ID is given. We expect false + // and a full set of contacts. + new object[] + { + 4, + true, + new Lead(new SortedSet { 1, 2, 3 }, + new SortedSet { 3, 5 }, + Constants.CurrentDirection.SINK), + new SortedSet { 1, 2, 3 }, + false + }, + // Testing with a different current direction + new object[] + { + 5, + true, + new Lead(new SortedSet { 1, 2, 3 }, + new SortedSet { 3, 4, 5 }, + Constants.CurrentDirection.SOURCE), + new SortedSet { 1, 2, 3 }, + false + }, + // Testing with an empty contactSet + new object[] + { + 4, + true, + new Lead(new SortedSet { }, + new SortedSet { 3, 5 }, + Constants.CurrentDirection.SINK), + new SortedSet { }, + false + }, + // Testing with an empty contactSet but ID matches an output. + // Searching by contact, so should still return false + new object[] + { + 4, + true, + new Lead(new SortedSet { }, + new SortedSet { 4, 3, 5 }, + Constants.CurrentDirection.SINK), + new SortedSet { }, + false + }, + // Testing with a contact set of size 1 + new object[] + { + 2, + true, + new Lead(new SortedSet { 1 }, + new SortedSet { 3, 4, 5 }, + Constants.CurrentDirection.SOURCE), + new SortedSet { 1 }, + false + }, + #endregion Search by Contact ID + #region Search by Output ID + // Testing when a connected output is given. We expect true and a + // full set of contacts. + new object[] + { + 4, + false, + new Lead(new SortedSet { 1, 2, 3 }, + new SortedSet { 3, 4, 5 }, + Constants.CurrentDirection.SINK), + new SortedSet { 1, 2, 3 }, + true + }, + // Testing with an outputSet of size 1 + new object[] + { + 3, + false, + new Lead(new SortedSet { 1, 2, 3 }, + new SortedSet { 3 }, + Constants.CurrentDirection.SOURCE), + new SortedSet { 1, 2, 3 }, + true + }, + // Testing with a contactSet of size 1 + new object[] + { + 3, + false, + new Lead(new SortedSet { 1 }, + new SortedSet { 3 }, + Constants.CurrentDirection.SOURCE), + new SortedSet { 1 }, + true + }, + // Testing with an empty contact set + new object[] + { + 4, + false, + new Lead(new SortedSet { }, + new SortedSet { 3, 4 }, + Constants.CurrentDirection.SOURCE), + new SortedSet { }, + true + }, + // Testing when an unconnected output is given. We expect false and + // a full set of contacts. + new object[] + { + 4, + false, + new Lead(new SortedSet { 1, 2, 3, 4 }, + new SortedSet { 3, 5 }, + Constants.CurrentDirection.SINK), + new SortedSet { 1, 2, 3, 4 }, + false + }, + // Testing with an empty outputSet + new object[] + { + 5, + false, + new Lead(new SortedSet { 1 }, + new SortedSet { }, + Constants.CurrentDirection.SOURCE), + new SortedSet { 1 }, + false + }, + // Testing with an empty contactSet + new object[] + { + 4, + false, + new Lead(new SortedSet { }, + new SortedSet { 3, 5 }, + Constants.CurrentDirection.SINK), + new SortedSet { }, + false + }, + // Testing with no overlap between the output and contact set ids. + new object[] + { + 2, + false, + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 3 }, + Constants.CurrentDirection.SOURCE), + new SortedSet { 1, 2 }, + false + } + #endregion Search by Output ID + }; + } + + + /// + /// Test for IsFullyIndependent. + /// + /// The first Lead instance to check for independence. + /// + /// The second Lead instance to compare against. + /// + /// The expected result indicating whether or + /// or not the two Leads are fully independent. + [Theory] + [MemberData(nameof(IsFullyIndependent_TestData))] + public void IsFullyIndependent_ShouldReturnExpectedResults(Lead lead1, + Lead lead2, bool expectedResult) + { + // Act + var result = lead1.IsFullyIndependent(lead2); + + // Assert + Assert.Equal(expectedResult, result); + } + + /// + /// Test parameter data for isFullyIndependent, following the form: + /// leadInstance, + /// leadInstance, + /// boolean (expectedResult) + /// Assuming all leads are valid in the session. + /// + public static IEnumerable IsFullyIndependent_TestData() + { + return new List + { + #region Test Independent Leads + // Test when the leads are fully independent + new object[] + { + new Lead(new SortedSet { 1 }, + new SortedSet { 1 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { 2 }, + new SortedSet { 2 }, + Constants.CurrentDirection.SOURCE), + true + }, + // Testing on two empty leads + new object[] + { + new Lead(new SortedSet { }, + new SortedSet { }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { }, + new SortedSet { }, + Constants.CurrentDirection.SINK), + true + }, + // Testing on leads with many contacts and outputs. Some contact + // IDs match the other Lead's output IDs and vice versa, but still + // independent. + new object[] + { + new Lead(new SortedSet { 1, 2, 3 }, + new SortedSet { 1, 4, 5 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 4, 5, 6 }, + new SortedSet { 2, 3, 6 }, + Constants.CurrentDirection.SOURCE), + true + }, + // Testing where contact and output sets are the same IDs within + // each Lead. + new object[] + { + new Lead(new SortedSet { 1, 4, 5 }, + new SortedSet { 1, 4, 5 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 2, 3, 6 }, + new SortedSet { 2, 3, 6 }, + Constants.CurrentDirection.SOURCE), + true + }, + // Testing where one lead has many contacts and outputs and the + // other has few. + new object[] + { + new Lead(new SortedSet { 1 }, + new SortedSet { 4 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { 2, 3, 6 }, + new SortedSet { 2, 3, 6 }, + Constants.CurrentDirection.SINK), + true + }, + // Testing where one lead has empty sets + new object[] + { + new Lead(new SortedSet { 1 }, + new SortedSet { 4 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { }, + new SortedSet { }, + Constants.CurrentDirection.SINK), + true + }, + // Test for when the contact set of one lead is many but the other + // is one, and the opposite for the output sets. + new object[] + { + new Lead(new SortedSet { 1, 2, 4 }, + new SortedSet { 4 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { 3 }, + new SortedSet { 1, 2, 3, 5, 6, 7, 8 }, + Constants.CurrentDirection.SINK), + true + }, + #endregion Test Independent Leads + #region Test Non-Independent Leads + // Tests for when the leads are not fully independent + new object[] + { + new Lead(new SortedSet { 1 }, + new SortedSet { 1 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { 1 }, + new SortedSet { 1 }, + Constants.CurrentDirection.SOURCE), + false + }, + // Test for when the leads are the exact same but with larger + // contact sets than output sets. + new object[] + { + new Lead(new SortedSet { 1, 2, 3 }, + new SortedSet { 1 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { 1, 2, 3 }, + new SortedSet { 1 }, + Constants.CurrentDirection.SOURCE), + false + }, + // Test for when the leads are the exact same but with larger + // output sets than contact sets + new object[] + { + new Lead(new SortedSet { 1 }, + new SortedSet { 1, 2, 3 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { 1 }, + new SortedSet { 1, 2, 3 }, + Constants.CurrentDirection.SOURCE), + false + }, + // Test for when the outputs are the exact same but different and + // larger contact sets than output sets. + new object[] + { + new Lead(new SortedSet { 1, 2, 3 }, + new SortedSet { 1 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { 4, 5, 6 }, + new SortedSet { 1 }, + Constants.CurrentDirection.SOURCE), + false + }, + // Test for when one Lead has only one output and the other has + // many. Contact IDs conflict. + new object[] + { + new Lead(new SortedSet { 1 }, + new SortedSet { 1, 2, 4 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { 1 }, + new SortedSet { 3 }, + Constants.CurrentDirection.SOURCE), + false + }, + // Test for when both Leads have very different sized output and + // contact sets, but one output conflicts. + new object[] + { + new Lead(new SortedSet { 1 }, + new SortedSet { 1, 2, 3, 4 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { 2, 3, 4, 5 }, + new SortedSet { 4 }, + Constants.CurrentDirection.SOURCE), + false + }, + // Test for when both leads have the same current direction. + // One output conflicts. + new object[] + { + new Lead(new SortedSet { 8, 9, 10 }, + new SortedSet { 3, 4 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 1, 2, 3, 9 }, + new SortedSet { 1 }, + Constants.CurrentDirection.SOURCE), + false + }, + // Test for when one contact and one output conflict. + // Same current direction. + new object[] + { + new Lead(new SortedSet { 8, 9, 10 }, + new SortedSet { 1, 3, 4 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 1, 2, 3, 9 }, + new SortedSet { 1, 5 }, + Constants.CurrentDirection.SOURCE), + false + }, + // Test for when multiple contacts and one output conflict. + // Same current direction. + new object[] + { + new Lead(new SortedSet { 7, 9, 10 }, + new SortedSet { 1, 3, 4 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 1, 2, 3, 7, 9, 12 }, + new SortedSet { 1, 5 }, + Constants.CurrentDirection.SOURCE), + false + }, + // Test for when one contact and multiple outputs conflict. + // Same current direction. + new object[] + { + new Lead(new SortedSet { 8, 9, 10 }, + new SortedSet { 1, 3, 4 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 1, 2, 3, 9 }, + new SortedSet { 1, 4, 5, 6 }, + Constants.CurrentDirection.SOURCE), + false + }, + // Test for when multiple contacts and multiple outputs conflict. + // Same current direction. + new object[] + { + new Lead(new SortedSet { 7, 8, 9, 10 }, + new SortedSet { 1, 3, 4 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 1, 2, 3, 7, 9, 12 }, + new SortedSet { 1, 4, 5, 6 }, + Constants.CurrentDirection.SOURCE), + false + } + #endregion Test Non-Independent Leads + }; + } + + /// + /// Test for IndependentLeadsExist. + /// + /// A collection of Lead instances to check for an + /// independent pair. + /// The expected result indicating whether or + /// not at least one pair of independent Leads exist. + [Theory] + [MemberData(nameof(IndependentLeadsExist_TestData))] + public void IndependentLeadsExist_ShouldReturnExpectedResults( + IEnumerable leads, bool expectedResult) + { + // Act + var result = Lead.IndependentLeadsExist(leads); + + // Assert + Assert.Equal(expectedResult, result); + } + + /// + /// Test parameter data for IndependentLeadsExist, following the form: + /// IEnumerableLeadSet, + /// boolean (expectedResult) + /// Assuming all leads are valid in the session. + /// + public static IEnumerable IndependentLeadsExist_TestData() + { + return new List + { + // Testing where 2 leads are fully independent + new object[] + { + new List + { + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 3, 4 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 5, 6 }, + new SortedSet { 7, 8 }, + Constants.CurrentDirection.SINK) + }, + true + }, + // Testing where the outputSets have only one ID and leads are + // dependent (single conflicting contact) + new object[] + { + new List + { + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 3 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { 1, 3 }, + new SortedSet { 4 }, + Constants.CurrentDirection.SINK) + }, + false + }, + // Testing where currentDirection is opposite, but all conflicting + // contacts + new object[] + { + new List + { + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 3, 4 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 5 }, + Constants.CurrentDirection.SINK) + }, + false + }, + // Testing where the outputSets have only one ID and leads are + // independent + new object[] + { + new List + { + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 3 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 4, 5 }, + new SortedSet { 6 }, + Constants.CurrentDirection.SINK) + }, + true + }, + // Testing where sets are the same size and dependent: conflicting + // single contact and output + new object[] + { + new List + { + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 3, 4 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 2, 3 }, + new SortedSet { 4, 5 }, + Constants.CurrentDirection.SOURCE) + }, + false + }, + // Testing with more than 2 leads in list. One conflicting pair by + // single output ID, but independent pairs exist + new object[] + { + new List + { + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 3 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 4, 5 }, + new SortedSet { 6, 7 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { 8, 9 }, + new SortedSet { 3 }, + Constants.CurrentDirection.SOURCE) + }, + true + }, + // Testing with more than 2 leads where all current directions are + // the same + new object[] + { + new List + { + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 3 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 3 }, + new SortedSet { 4 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 4 }, + new SortedSet { 5 }, + Constants.CurrentDirection.SOURCE) + }, + true + }, + // Testing with leads with conflicting outputs but different + // contacts + new object[] + { + new List + { + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 3 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 4 }, + new SortedSet { 3, 5 }, + Constants.CurrentDirection.SINK) + }, + false + }, + // Testing with an empty lead set + new object[] + { + new List(), + false + }, + // Testing with a single lead + new object[] + { + new List + { + new Lead(new SortedSet { 1 }, + new SortedSet { 2 }, + Constants.CurrentDirection.SOURCE) + }, + false + }, + // Testing with large fully independent lead set + new object[] + { + new List + { + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 3, 4 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 5, 6 }, + new SortedSet { 7, 8 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { 9, 10 }, + new SortedSet { 11 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet {11, 12, 13 }, + new SortedSet { 12 }, + Constants.CurrentDirection.SINK) + }, + true + }, + // Testing with large mostly independent lead set + new object[] + { + new List + { + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 3, 4 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 5, 6 }, // contact 5 + new SortedSet { 7, 8 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { 9, 10 }, + new SortedSet { 11 }, // output 11 + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet {5, 12, 13 }, // contact 5 + new SortedSet { 11 }, // output 11 + Constants.CurrentDirection.SINK) + }, + true + }, + // Testing with large lead set where some conflicting but a few + // independent pairs exist. + new object[] + { + new List + { + new Lead(new SortedSet { 1, 2 }, // indp A B + new SortedSet { 3 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 2, 3 }, // indp C + new SortedSet { 4 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 4, 5 }, // indp B + new SortedSet { 5 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 5 }, // indp A C + new SortedSet { 6 }, + Constants.CurrentDirection.SOURCE) + }, + true + }, + // Testing with large lead set with mixed independence + new object[] + { + new List + { + new Lead(new SortedSet { 1, 2 }, // contact 2 + new SortedSet { 3 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 3, 4 }, + new SortedSet { 5 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 6, 7 }, + new SortedSet { 8 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet { 9, 10 }, // contact 9 + new SortedSet { 11 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 2, 9 }, // contact 2, 9 + new SortedSet { 10 }, + Constants.CurrentDirection.SINK) + }, + true + }, + // Testing with longer list, and all conflict with each other in + // some way. + new object[] + { + new List + { + new Lead(new SortedSet { 1, 2 }, + new SortedSet { 3 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 3, 4 }, + new SortedSet { 5, 3}, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet { 3, 7 }, + new SortedSet { 8, 3 }, + Constants.CurrentDirection.SINK), + new Lead(new SortedSet {1, 3, 9, 10 }, + new SortedSet { 11 }, + Constants.CurrentDirection.SOURCE), + new Lead(new SortedSet {2, 3, 9 }, + new SortedSet { 10 }, + Constants.CurrentDirection.SINK) + }, + false + } + }; + } +} diff --git a/tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.csproj b/tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.csproj index 84cf4a8..64ce108 100644 --- a/tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.csproj +++ b/tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.csproj @@ -33,9 +33,6 @@ - - - PreserveNewest From 9714a618dc5308bdc2ab53fbe7a2e86bd8616cf5 Mon Sep 17 00:00:00 2001 From: Thao Nguyen <71063988+fernthao@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:01:31 -0500 Subject: [PATCH 06/34] Tests for GelPad and ContactGroup (#35) * Tests for GelPad and ContactGroup * Add config to be able to run command dotnet test * Throw exception for non-positive NumContact * Add docstring and fix styling for ContactGroup.cs * Fix styling and directory structure for HardwareInterfacesTests * Revert "Add config to be able to run command dotnet test" This reverts commit 1c57537b616063f34e9510bf173edda3e1766222. * Added comments * Added Id check * Added comments on test method params --------- Co-authored-by: Laura McGann --- .../HardwareInterfaces/ContactGroup.cs | 24 +++++++++- .../Extensions/HardwareInterfaces/GelPad.cs | 12 +++++ .../HardwareInterfaces/ContactGroupTests.cs | 46 +++++++++++++++++++ .../HardwareInterfaces/GelPadTests.cs | 28 +++++++++++ 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 tests/EStimLibrary.UnitTests/Extensions/HardwareInterfaces/ContactGroupTests.cs create mode 100644 tests/EStimLibrary.UnitTests/Extensions/HardwareInterfaces/GelPadTests.cs diff --git a/src/EStimLibrary/Extensions/HardwareInterfaces/ContactGroup.cs b/src/EStimLibrary/Extensions/HardwareInterfaces/ContactGroup.cs index 876b8d8..afe8df1 100644 --- a/src/EStimLibrary/Extensions/HardwareInterfaces/ContactGroup.cs +++ b/src/EStimLibrary/Extensions/HardwareInterfaces/ContactGroup.cs @@ -4,16 +4,38 @@ namespace EStimLibrary.Extensions.HardwareInterfaces; +/// +/// A generic neural interface containing 1+ contacts. +/// public class ContactGroup : NeuralInterfaceHardware { + /// + /// The name of this neural interface type. + /// public override string Name => "Contact Group"; + /// + /// The integer number of contacts in this neural interface. + /// public override int NumContacts => this._NumContacts; protected int _NumContacts; + /// + /// Create a contact group with a positive integer number of contacts. + /// + /// Number of contacts. + /// Invalid argument, numContact must be + /// positive. public ContactGroup(int numContacts) : base() { - this._NumContacts = numContacts; + if (numContacts > 0) + { + this._NumContacts = numContacts; + } + else + { + throw new ArgumentException("Number of contacts must be positive"); + } } } diff --git a/src/EStimLibrary/Extensions/HardwareInterfaces/GelPad.cs b/src/EStimLibrary/Extensions/HardwareInterfaces/GelPad.cs index 28ad9fe..cafbf7f 100644 --- a/src/EStimLibrary/Extensions/HardwareInterfaces/GelPad.cs +++ b/src/EStimLibrary/Extensions/HardwareInterfaces/GelPad.cs @@ -4,12 +4,24 @@ namespace EStimLibrary.Extensions.HardwareInterfaces; +/// +/// A single gel pad electrode. +/// public class GelPad : NeuralInterfaceHardware { + /// + /// The name of this neural interface type. + /// public override string Name => "Gel Pad"; + /// + /// A gel pad represents a single contact. + /// public override int NumContacts => 1; + /// + /// Create a gel pad. + /// public GelPad() : base() { } diff --git a/tests/EStimLibrary.UnitTests/Extensions/HardwareInterfaces/ContactGroupTests.cs b/tests/EStimLibrary.UnitTests/Extensions/HardwareInterfaces/ContactGroupTests.cs new file mode 100644 index 0000000..4c23901 --- /dev/null +++ b/tests/EStimLibrary.UnitTests/Extensions/HardwareInterfaces/ContactGroupTests.cs @@ -0,0 +1,46 @@ +using EStimLibrary.Extensions.HardwareInterfaces; + + +namespace EStimLibrary.UnitTests.Extensions.HardwareInterfaces; + + +/// +/// A test class for the ContactGroup INeuralInterfaceHardware class. +/// +public class ContactGroupTests +{ + /// + /// Test the parameterized constructor with different valid parameters, + /// i.e., positive integer values for number of contacts. The neural + /// interface object should initialize with correct NumContact and Id + /// values. + /// + /// The number of contacts to test ContactGroup + /// construction with. + [Theory] + [InlineData(1)] // Valid single contact. + [InlineData(10)] // Valid multiple contacts. + public void Constructor_ShouldInit(int numContacts) + { + var contactGroup = new ContactGroup(numContacts); + + Assert.Equal(-1, contactGroup.Id); + Assert.Equal(numContacts, contactGroup.NumContacts); + } + + /// + /// Test the parameterized constructor with different invalid parameters, + /// i.e., 0 or negative integer values for number of contacts. + /// Constructioin should fail and throw an argument exception. + /// + /// The number of contacts to test ContactGroup + /// construction with. + [Theory] + [InlineData(0)] // Invalid 0 contacts. + [InlineData(-1)] // Invalid single negative contact number. + [InlineData(-10)] // Invalid multiple negative contact number. + public void Constructor_ShouldThrowException(int numContacts) + { + Assert.Throws(() => new ContactGroup(numContacts)); + } +} diff --git a/tests/EStimLibrary.UnitTests/Extensions/HardwareInterfaces/GelPadTests.cs b/tests/EStimLibrary.UnitTests/Extensions/HardwareInterfaces/GelPadTests.cs new file mode 100644 index 0000000..707f09b --- /dev/null +++ b/tests/EStimLibrary.UnitTests/Extensions/HardwareInterfaces/GelPadTests.cs @@ -0,0 +1,28 @@ +using EStimLibrary.Extensions.HardwareInterfaces; + + +namespace EStimLibrary.UnitTests.Extensions.HardwareInterfaces; + + +/// +/// A test class for the GelPad INeuralInterfaceHardware class. +/// +public class GelPadTests +{ + /// + /// Make sure ID is -1 after constructor (ID is only set once added to a + /// session). + /// Number of contacts for gel pad should be 1. + /// + [Fact] + public void Constructor_ShouldInitWithCorrectParams() + { + // Arrange (Init new object) + var gelPad = new GelPad(); + + // Assert (Test if initialized params are correct) + Assert.Equal(-1, gelPad.Id); + Assert.Equal(1, gelPad.NumContacts); + + } +} From cccac61bd8229aacaf3dd15bb2b8ab623bf8606b Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Mon, 18 Nov 2024 16:25:18 -0500 Subject: [PATCH 07/34] Comments and formatting --- .../StringHierarchy/StringHierarchyRegion.cs | 18 ++++---- .../StringHierarchyRegionTests.cs | 43 +++++++++++++------ 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs index 5c477d2..a7facd5 100644 --- a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs +++ b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs @@ -1,6 +1,4 @@ -using System; - -namespace EStimLibrary.Extensions.SpatialModel.StringHierarchy; +namespace EStimLibrary.Extensions.SpatialModel.StringHierarchy; /// @@ -36,8 +34,9 @@ public List OptionedRegionNames if (this.HasOptions) { return this.Options.Select( - option => $"{option}{StringHierarchySpec.OPTION_REGION_DELIMITER}" + - $"{this.BaseName}").ToList(); + option => $"{option}" + + $"{StringHierarchySpec.OPTION_REGION_DELIMITER}" + + $"{this.BaseName}").ToList(); } // Else, add include the base name. else @@ -119,12 +118,15 @@ public StringHierarchyRegion(string baseName, StringHierarchyRegion parent, /// Parent reference set. /// An output parameter: the replaced but /// unaltered existing subregion if any, else null. + /// The provided + /// subregion argument is null. public void AddSubregion(StringHierarchyRegion subregion, out StringHierarchyRegion? existingSubregion) { - /// The provided - /// subregion argument is null. - if (subregion == null) throw new ArgumentNullException(); + if (subregion == null) + { + throw new ArgumentNullException(); + } // Fill the out parameter with the existing subregion if exists. if (this.Subregions.TryGetValue(subregion.BaseName, diff --git a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs index 134e569..9254838 100644 --- a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs +++ b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs @@ -1,16 +1,18 @@ using EStimLibrary.Extensions.SpatialModel.StringHierarchy; -using System.Runtime.InteropServices; -using Xunit.Sdk; + namespace EStimLibrary.UnitTests.Extensions.SpatialModel.StringHierarchy; -// Test class naming convention: LibClassTests +/// +/// Test class for StringHierarchyRegion. +/// public class StringHierarchyRegionTests { private readonly ITestOutputHelper _output; - // Test class constructor creates an output helper so can write console output. + // Test class constructor creates an output helper so can write console + // output. public StringHierarchyRegionTests(ITestOutputHelper testOutputHelper) { this._output = testOutputHelper; @@ -24,7 +26,8 @@ public StringHierarchyRegionTests(ITestOutputHelper testOutputHelper) [Fact] public void Constructor_ShouldAcceptNull() { - var stringHierarchyRegion = new StringHierarchyRegion("base", null, null, null, null, null); + var stringHierarchyRegion = new StringHierarchyRegion("base", null, + null, null, null, null); Assert.Equal("base", stringHierarchyRegion.BaseName); Assert.Null(stringHierarchyRegion.ParentRegion); @@ -74,7 +77,10 @@ public void Constructor_ShouldAcceptEmpty() { var parent = new StringHierarchyRegion("root", null); - var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, new HashSet(), new Dictionary>(), new Dictionary()); + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, + parent.Options, new HashSet(), + new Dictionary>(), + new Dictionary()); Assert.Equal("base", stringHierarchyRegion.BaseName); Assert.StrictEqual(parent, stringHierarchyRegion.ParentRegion); @@ -98,7 +104,9 @@ public void Constructor_ShouldAcceptEmpty() [Fact] public void Constructor_ShouldDeepCopy() { - var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + var parent = new StringHierarchyRegion("root", null, null, + new HashSet(), new Dictionary>(), + new Dictionary()); var regionOptions = new HashSet() { "right", "left" }; var regionModifiers = new Dictionary> { @@ -111,8 +119,9 @@ public void Constructor_ShouldDeepCopy() { "child2", new StringHierarchyRegion("child2", null) } }; - var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); - + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, + parent.Options, regionOptions, regionModifiers, regionSubregions); + Assert.Equal("base", stringHierarchyRegion.BaseName); Assert.Same(parent, stringHierarchyRegion.ParentRegion); Assert.Equal(parent.Options, stringHierarchyRegion.ParentOptions); @@ -121,20 +130,28 @@ public void Constructor_ShouldDeepCopy() Assert.NotSame(regionOptions, stringHierarchyRegion.Options); Assert.True(stringHierarchyRegion.HasOptions); Assert.Equal(2, stringHierarchyRegion.OptionedRegionNames.Count); - Assert.Equal(new List() {$"right{StringHierarchySpec.OPTION_REGION_DELIMITER}base", $"left{StringHierarchySpec.OPTION_REGION_DELIMITER}base"}, stringHierarchyRegion.OptionedRegionNames); + Assert.Equal(new List() { + $"right{StringHierarchySpec.OPTION_REGION_DELIMITER}base", + $"left{StringHierarchySpec.OPTION_REGION_DELIMITER}base" }, + stringHierarchyRegion.OptionedRegionNames); Assert.Equal(regionModifiers, stringHierarchyRegion.Modifiers); Assert.NotSame(regionModifiers, stringHierarchyRegion.Modifiers); Assert.True(stringHierarchyRegion.HasModifiers); Assert.Equal(regionSubregions, stringHierarchyRegion.Subregions); Assert.NotSame(regionSubregions, stringHierarchyRegion.Subregions); - Assert.Same(regionSubregions["child1"], stringHierarchyRegion.Subregions["child1"]); - Assert.Same(regionSubregions["child2"], stringHierarchyRegion.Subregions["child2"]); + Assert.Same(regionSubregions["child1"], + stringHierarchyRegion.Subregions["child1"]); + Assert.Same(regionSubregions["child2"], + stringHierarchyRegion.Subregions["child2"]); Assert.True(stringHierarchyRegion.HasSubregions); Assert.False(stringHierarchyRegion.IsLeaf); Assert.Empty(stringHierarchyRegion.SavedLocations); Assert.Empty(stringHierarchyRegion.SavedAreas); - var child = new StringHierarchyRegion("child", stringHierarchyRegion, stringHierarchyRegion.Options, new HashSet(), new Dictionary>(), new Dictionary()); + var child = new StringHierarchyRegion("child", stringHierarchyRegion, + stringHierarchyRegion.Options, new HashSet(), + new Dictionary>(), new Dictionary()); Assert.Same(parent, stringHierarchyRegion.ParentRegion); Assert.Equal(parent.Options, stringHierarchyRegion.ParentOptions); From bc26aa79d5f08a1d4dce4078f7148fc6fd1bdd30 Mon Sep 17 00:00:00 2001 From: cld99 <115433496+cld99@users.noreply.github.com> Date: Sun, 1 Dec 2024 11:27:33 -0500 Subject: [PATCH 08/34] Revert changes to EStimLibrary.sln --- EStimLibrary.sln | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/EStimLibrary.sln b/EStimLibrary.sln index 7f12a6c..4222e38 100644 --- a/EStimLibrary.sln +++ b/EStimLibrary.sln @@ -5,26 +5,23 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7E35BD38-1080-4A2E-B8B4-7CB0C5C08607}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EStimLibrary", "src\EStimLibrary\EStimLibrary.csproj", "{23710686-EDC8-41FC-AAC8-52B002F488D6}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EStimLibrary", "src\EStimLibrary\EStimLibrary.csproj", "{23710686-EDC8-41FC-AAC8-52B002F488D6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {23710686-EDC8-41FC-AAC8-52B002F488D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {23710686-EDC8-41FC-AAC8-52B002F488D6}.Debug|Any CPU.Build.0 = Debug|Any CPU {23710686-EDC8-41FC-AAC8-52B002F488D6}.Release|Any CPU.ActiveCfg = Release|Any CPU {23710686-EDC8-41FC-AAC8-52B002F488D6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection GlobalSection(NestedProjects) = preSolution {23710686-EDC8-41FC-AAC8-52B002F488D6} = {7E35BD38-1080-4A2E-B8B4-7CB0C5C08607} EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {FAF6FE9F-5CE1-429C-A3E8-3C31D6AC479B} - EndGlobalSection EndGlobal From f0e0ed2abbeeeeef865840cd3e1f62563997a161 Mon Sep 17 00:00:00 2001 From: cld99 <115433496+cld99@users.noreply.github.com> Date: Sun, 1 Dec 2024 13:51:28 -0500 Subject: [PATCH 09/34] Format testing class and add comments --- .../StringHierarchy/StringHierarchyRegion.cs | 4 +- .../StringHierarchyRegionTests.cs | 420 ++++++++++++------ 2 files changed, 288 insertions(+), 136 deletions(-) diff --git a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs index 5c477d2..cee1d80 100644 --- a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs +++ b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs @@ -1,6 +1,4 @@ -using System; - -namespace EStimLibrary.Extensions.SpatialModel.StringHierarchy; +namespace EStimLibrary.Extensions.SpatialModel.StringHierarchy; /// diff --git a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs index 134e569..61641b2 100644 --- a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs +++ b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs @@ -10,7 +10,8 @@ public class StringHierarchyRegionTests { private readonly ITestOutputHelper _output; - // Test class constructor creates an output helper so can write console output. + // Test class constructor creates an output helper so it can write console + // output. public StringHierarchyRegionTests(ITestOutputHelper testOutputHelper) { this._output = testOutputHelper; @@ -24,8 +25,10 @@ public StringHierarchyRegionTests(ITestOutputHelper testOutputHelper) [Fact] public void Constructor_ShouldAcceptNull() { - var stringHierarchyRegion = new StringHierarchyRegion("base", null, null, null, null, null); + var stringHierarchyRegion = new StringHierarchyRegion("base", null, + null, null, null, null); + // Check that all fields correctly instantiate Assert.Equal("base", stringHierarchyRegion.BaseName); Assert.Null(stringHierarchyRegion.ParentRegion); Assert.Empty(stringHierarchyRegion.ParentOptions); @@ -48,8 +51,10 @@ public void Constructor_ShouldAcceptNull() [Fact] public void Constructor_ShouldDefaultNull() { + // Create region with all default parameters unspecified var stringHierarchyRegion = new StringHierarchyRegion("base", null); + // Check that all fields correctly instantiate Assert.Equal("base", stringHierarchyRegion.BaseName); Assert.Null(stringHierarchyRegion.ParentRegion); Assert.Empty(stringHierarchyRegion.ParentOptions); @@ -72,10 +77,16 @@ public void Constructor_ShouldDefaultNull() [Fact] public void Constructor_ShouldAcceptEmpty() { + // Create simple parent region var parent = new StringHierarchyRegion("root", null); - var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, new HashSet(), new Dictionary>(), new Dictionary()); + // Create region with all parameters non-null + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, + parent.Options, new HashSet(), + new Dictionary>(), + new Dictionary()); + // Check that all fields correctly instantiate Assert.Equal("base", stringHierarchyRegion.BaseName); Assert.StrictEqual(parent, stringHierarchyRegion.ParentRegion); Assert.Empty(stringHierarchyRegion.ParentOptions); @@ -98,7 +109,12 @@ public void Constructor_ShouldAcceptEmpty() [Fact] public void Constructor_ShouldDeepCopy() { - var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + // Create parent region with non-null object parameters for deep copy + // testing + var parent = new StringHierarchyRegion("root", null, null, + new HashSet(), new Dictionary>(), + new Dictionary()); + // Create non-empty options, modifiers, and subregions parameters var regionOptions = new HashSet() { "right", "left" }; var regionModifiers = new Dictionary> { @@ -110,34 +126,58 @@ public void Constructor_ShouldDeepCopy() { "child1", new StringHierarchyRegion("child1", null) }, { "child2", new StringHierarchyRegion("child2", null) } }; + // Create region with the above parameters for deep copy testing + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, + parent.Options, regionOptions, regionModifiers, regionSubregions); + + // Check that all non-region object fields are deep copies of + // parameters and all region fields are shallow copies (i.e., + // check for value equality and reference equality) - var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); - Assert.Equal("base", stringHierarchyRegion.BaseName); + // Check parent shallow copy Assert.Same(parent, stringHierarchyRegion.ParentRegion); + // Check parent options deep copy Assert.Equal(parent.Options, stringHierarchyRegion.ParentOptions); Assert.NotSame(parent.Options, stringHierarchyRegion.ParentOptions); + // Check options deep copy Assert.Equal(regionOptions, stringHierarchyRegion.Options); Assert.NotSame(regionOptions, stringHierarchyRegion.Options); Assert.True(stringHierarchyRegion.HasOptions); + // Check optioned region names (since not tested previously) Assert.Equal(2, stringHierarchyRegion.OptionedRegionNames.Count); - Assert.Equal(new List() {$"right{StringHierarchySpec.OPTION_REGION_DELIMITER}base", $"left{StringHierarchySpec.OPTION_REGION_DELIMITER}base"}, stringHierarchyRegion.OptionedRegionNames); + Assert.Equal(new List() { + $"right{StringHierarchySpec.OPTION_REGION_DELIMITER}base", + $"left{StringHierarchySpec.OPTION_REGION_DELIMITER}base"}, + stringHierarchyRegion.OptionedRegionNames); + // Check modifiers deep copy Assert.Equal(regionModifiers, stringHierarchyRegion.Modifiers); Assert.NotSame(regionModifiers, stringHierarchyRegion.Modifiers); Assert.True(stringHierarchyRegion.HasModifiers); + // Check subregions deep copy dictionary but shallow copy subregions Assert.Equal(regionSubregions, stringHierarchyRegion.Subregions); Assert.NotSame(regionSubregions, stringHierarchyRegion.Subregions); - Assert.Same(regionSubregions["child1"], stringHierarchyRegion.Subregions["child1"]); - Assert.Same(regionSubregions["child2"], stringHierarchyRegion.Subregions["child2"]); + Assert.Same(regionSubregions["child1"], + stringHierarchyRegion.Subregions["child1"]); + Assert.Same(regionSubregions["child2"], + stringHierarchyRegion.Subregions["child2"]); + // Check miscellaneous subregion-related fields (since not tested + // previously) Assert.True(stringHierarchyRegion.HasSubregions); Assert.False(stringHierarchyRegion.IsLeaf); Assert.Empty(stringHierarchyRegion.SavedLocations); Assert.Empty(stringHierarchyRegion.SavedAreas); - var child = new StringHierarchyRegion("child", stringHierarchyRegion, stringHierarchyRegion.Options, new HashSet(), new Dictionary>(), new Dictionary()); + // Create region with above region as parent for further parent testing + var child = new StringHierarchyRegion("child", stringHierarchyRegion, + stringHierarchyRegion.Options, new HashSet(), + new Dictionary>(), + new Dictionary()); - Assert.Same(parent, stringHierarchyRegion.ParentRegion); - Assert.Equal(parent.Options, stringHierarchyRegion.ParentOptions); + // Check for parent and parent option equality with non-null parent options + Assert.Same(stringHierarchyRegion, child.ParentRegion); + Assert.Equal(stringHierarchyRegion.Options, child.ParentOptions); + Assert.NotSame(stringHierarchyRegion.Options, child.ParentOptions); } /// @@ -146,8 +186,15 @@ public void Constructor_ShouldDeepCopy() [Fact] public void ToString_ShouldOutputStringRep() { + // Create region with null parameter values var nullRegion = new StringHierarchyRegion("base", null); - var emptyRegion = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + // Create region with non-null but empty options, modifiers, and + // subregions + var emptyRegion = new StringHierarchyRegion("root", null, null, + new HashSet(), new Dictionary>(), + new Dictionary()); + // Create non-empty options, modifiers, and subregions parameters + // to cover all components of string representation var regionOptions = new HashSet() { "right", "left" }; var regionModifiers = new Dictionary> { @@ -160,24 +207,37 @@ public void ToString_ShouldOutputStringRep() }; var regionSubregions = new Dictionary { - { "child1", new StringHierarchyRegion("child1", null, null, new HashSet { "opt1", "opt2" }, null, subregionSubregions) }, + { "child1", new StringHierarchyRegion("child1", null, null, + new HashSet { "opt1", "opt2" }, null, + subregionSubregions) }, { "child2", new StringHierarchyRegion("child2", null) } }; + // Create region with the above parameters + var stringHierarchyRegion = new StringHierarchyRegion("base", + emptyRegion, emptyRegion.Options, regionOptions, regionModifiers, + regionSubregions); - var stringHierarchyRegion = new StringHierarchyRegion("base", emptyRegion, emptyRegion.Options, regionOptions, regionModifiers, regionSubregions); - + // Check string outputs for correctness Assert.Equal("base", nullRegion.ToString()); Assert.Equal("root", emptyRegion.ToString()); - Assert.Equal("[right,left] base | [mod1,mod2], [mod3,mod4]\n [right,left] base, [opt1,opt2] child1\n [right,left] base, [opt1,opt2] child1, grandchild\n [right,left] base, child2", stringHierarchyRegion.ToString()); + Assert.Equal("[right,left] base | [mod1,mod2], [mod3,mod4]\n" + + " [right,left] base, [opt1,opt2] child1\n" + + " [right,left] base, [opt1,opt2] child1, grandchild\n" + + " [right,left] base, child2", stringHierarchyRegion.ToString()); } /// - /// Test the TryGetSubregion method with various malformed or otherwise incorrect regions. + /// Test the TryGetSubregion method with various malformed or otherwise + /// incorrect regions. /// [Fact] public void TryGetSubregion_ShouldOutputNull() { - var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + // Create simple parent region + var parent = new StringHierarchyRegion("root", null, null, + new HashSet(), new Dictionary>(), + new Dictionary()); + // Create non-empty options, modifiers, and subregions parameters var regionOptions = new HashSet() { "right", "left" }; var regionModifiers = new Dictionary> { @@ -189,69 +249,70 @@ public void TryGetSubregion_ShouldOutputNull() { "child1", new StringHierarchyRegion("child1", null) }, { "child2", new StringHierarchyRegion("child2", null) } }; + // Create region with the above parameters + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, + parent.Options, regionOptions, regionModifiers, regionSubregions); - var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); - + // Test with empty string representation StringHierarchyRegion foundSubregion; - var output = stringHierarchyRegion.TryGetSubregion("", out foundSubregion); - + var output = stringHierarchyRegion.TryGetSubregion("", + out foundSubregion); Assert.False(output); Assert.Null(foundSubregion); - - output = stringHierarchyRegion.TryGetSubregion("reg1, , reg2", out foundSubregion); - + // Test with invalid base region + output = stringHierarchyRegion.TryGetSubregion("base2", + out foundSubregion); Assert.False(output); Assert.Null(foundSubregion); - - output = stringHierarchyRegion.TryGetSubregion("reg1, reg2", out foundSubregion); - + // Test with valid base region but invalid option + output = stringHierarchyRegion.TryGetSubregion("middle base", + out foundSubregion); Assert.False(output); Assert.Null(foundSubregion); - - output = stringHierarchyRegion.TryGetSubregion("opt1 opt2 reg1", out foundSubregion); - + // Test with valid base region but no options specified + output = stringHierarchyRegion.TryGetSubregion("base", + out foundSubregion); Assert.False(output); Assert.Null(foundSubregion); - - output = stringHierarchyRegion.TryGetSubregion("opt1 reg1", out foundSubregion); - + // Test valid base region and option but specified modifier + output = stringHierarchyRegion.TryGetSubregion("left base | modSet1", + out foundSubregion); Assert.False(output); Assert.Null(foundSubregion); - - output = stringHierarchyRegion.TryGetSubregion("base2", out foundSubregion); - + // Test with valid subregion but no base region + output = stringHierarchyRegion.TryGetSubregion("child1", + out foundSubregion); Assert.False(output); Assert.Null(foundSubregion); - - output = stringHierarchyRegion.TryGetSubregion("middle base", out foundSubregion); - + // Test with additional comma delimiters + output = stringHierarchyRegion.TryGetSubregion("left base, , child1", + out foundSubregion); Assert.False(output); Assert.Null(foundSubregion); - - output = stringHierarchyRegion.TryGetSubregion("base", out foundSubregion); - + // Test with multiple options + output = stringHierarchyRegion.TryGetSubregion("right left base", + out foundSubregion); Assert.False(output); Assert.Null(foundSubregion); - - output = stringHierarchyRegion.TryGetSubregion("child1", out foundSubregion); - - Assert.False(output); - Assert.Null(foundSubregion); - - - output = stringHierarchyRegion.TryGetSubregion("left base | modSet1", out foundSubregion); - + // Test with additional whitespace between option and region + output = stringHierarchyRegion.TryGetSubregion("left base", + out foundSubregion); Assert.False(output); Assert.Null(foundSubregion); } /// - /// Test the TryGetSubregion method with region present in current level and region present in subregion. + /// Test the TryGetSubregion method with region present in current level + /// and region present in subregion. /// [Fact] public void TryGetSubregion_ShouldOutputRegion() { - var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + // Create simple parent region + var parent = new StringHierarchyRegion("root", null, null, + new HashSet(), new Dictionary>(), + new Dictionary()); + // Create non-empty options, modifiers, and subregions parameters var regionOptions = new HashSet() { "right", "left" }; var regionModifiers = new Dictionary> { @@ -264,32 +325,41 @@ public void TryGetSubregion_ShouldOutputRegion() }; var regionSubregions = new Dictionary { - { "child1", new StringHierarchyRegion("child1", null, null, new HashSet { "opt1", "opt2" }, null, subregionSubregions) }, + { "child1", new StringHierarchyRegion("child1", null, null, + new HashSet { "opt1", "opt2" }, null, + subregionSubregions) }, { "child2", new StringHierarchyRegion("child2", null) } }; + // Create region with the above parameters + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, + parent.Options, regionOptions, regionModifiers, regionSubregions); - var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); - + // Test with valid option and base region StringHierarchyRegion foundSubregion; - var output = stringHierarchyRegion.TryGetSubregion("right base", out foundSubregion); - + var output = stringHierarchyRegion.TryGetSubregion("right base", + out foundSubregion); Assert.True(output); Assert.Equal(stringHierarchyRegion, foundSubregion); - - output = stringHierarchyRegion.TryGetSubregion("left base, child2", out foundSubregion); - + // Test with valid subregion + output = stringHierarchyRegion.TryGetSubregion("left base, child2", + out foundSubregion); Assert.True(output); Assert.Equal(regionSubregions["child2"], foundSubregion); - - output = stringHierarchyRegion.TryGetSubregion("left base, opt2 child1", out foundSubregion); - + // Test with valid subregion option + output = stringHierarchyRegion.TryGetSubregion("left base, opt2 child1", + out foundSubregion); Assert.True(output); Assert.Equal(regionSubregions["child1"], foundSubregion); - - output = stringHierarchyRegion.TryGetSubregion("left base, opt2 child1, grandchild", out foundSubregion); - + // Test with valid subregion of subregion + output = stringHierarchyRegion.TryGetSubregion("left base, opt2 child1, " + + "grandchild", out foundSubregion); Assert.True(output); Assert.Equal(subregionSubregions["grandchild"], foundSubregion); + // Test with additional whitespace between regions + output = stringHierarchyRegion.TryGetSubregion("left base, child1", + out foundSubregion); + Assert.True(output); + Assert.Equal(regionSubregions["child1"], foundSubregion); } /// @@ -298,7 +368,11 @@ public void TryGetSubregion_ShouldOutputRegion() [Fact] public void AddSubregion_ShouldAddSubregion() { - var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + // Create simple parent region + var parent = new StringHierarchyRegion("root", null, null, + new HashSet(), new Dictionary>(), + new Dictionary()); + // Create non-empty options, modifiers, and subregions parameters var regionOptions = new HashSet() { "right", "left" }; var regionModifiers = new Dictionary> { @@ -310,11 +384,14 @@ public void AddSubregion_ShouldAddSubregion() { "child1", new StringHierarchyRegion("child1", null) }, { "child2", new StringHierarchyRegion("child2", null) } }; + // Create region with the above parameters + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, + parent.Options, regionOptions, regionModifiers, regionSubregions); - var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); - + // Create variable to store old region output StringHierarchyRegion oldRegion; + // Check error output for null subregion parameter var errorSuccess = false; try { @@ -323,12 +400,15 @@ public void AddSubregion_ShouldAddSubregion() catch (ArgumentNullException ex) { errorSuccess = true; } Assert.True(errorSuccess); + // Create new region with different name to existing subregions var newChildRegion = new StringHierarchyRegion("child3", null); + // Check shallow copy of subregion into base region and old subregion + // output stringHierarchyRegion.AddSubregion(newChildRegion, out oldRegion); - Assert.Null(oldRegion); - Assert.Same(newChildRegion, stringHierarchyRegion.Subregions["child3"]); + Assert.Same(newChildRegion, + stringHierarchyRegion.Subregions["child3"]); } /// @@ -337,7 +417,11 @@ public void AddSubregion_ShouldAddSubregion() [Fact] public void AddSubregion_ShouldReplaceSubregion() { - var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + // Create simple parent region + var parent = new StringHierarchyRegion("root", null, null, + new HashSet(), new Dictionary>(), + new Dictionary()); + // Create non-empty options, modifiers, and subregions parameters var regionOptions = new HashSet() { "right", "left" }; var regionModifiers = new Dictionary> { @@ -349,29 +433,29 @@ public void AddSubregion_ShouldReplaceSubregion() { "child1", new StringHierarchyRegion("child1", null) }, { "child2", new StringHierarchyRegion("child2", null) } }; + // Create region with the above parameters + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, + parent.Options, regionOptions, regionModifiers, regionSubregions); - var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); - + // Create variable to store old region output StringHierarchyRegion oldRegion; - stringHierarchyRegion.AddSubregion(regionSubregions["child2"], out oldRegion); - + // Test re-adding existing subregion + stringHierarchyRegion.AddSubregion(regionSubregions["child2"], + out oldRegion); Assert.Equal(regionSubregions["child2"], oldRegion); - Assert.Same(regionSubregions["child2"], stringHierarchyRegion.Subregions["child2"]); + Assert.Same(regionSubregions["child2"], + stringHierarchyRegion.Subregions["child2"]); - var newChildRegion1 = new StringHierarchyRegion("child1", null); - - stringHierarchyRegion.AddSubregion(newChildRegion1, out oldRegion); + // Create new region with same name as existing subregion + var newChildRegion = new StringHierarchyRegion("child1", null); + // Test adding new subregion with same name as existing subregion + stringHierarchyRegion.AddSubregion(newChildRegion, out oldRegion); Assert.Equal(regionSubregions["child1"], oldRegion); - Assert.Same(newChildRegion1, stringHierarchyRegion.Subregions["child1"]); - - var newChildRegion2 = new StringHierarchyRegion("child2", null, null, regionOptions); - - stringHierarchyRegion.AddSubregion(newChildRegion2, out oldRegion); - - Assert.Equal(regionSubregions["child2"], oldRegion); - Assert.Same(newChildRegion2, stringHierarchyRegion.Subregions["child2"]); + Assert.Same(newChildRegion, + stringHierarchyRegion.Subregions["child1"]); + Assert.Same(stringHierarchyRegion, newChildRegion.ParentRegion); } /// @@ -380,8 +464,13 @@ public void AddSubregion_ShouldReplaceSubregion() [Fact] public void DeepCopy_ShouldDeepCopy() { + // Create region with null parameter values var nullRegion = new StringHierarchyRegion("base", null); - var emptyRegion = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + // Create region with non-null but empty options, modifiers, and subregions + var emptyRegion = new StringHierarchyRegion("root", null, null, + new HashSet(), new Dictionary>(), + new Dictionary()); + // Create non-empty options and modifiers parameters var regionOptions = new HashSet() { "right", "left" }; var regionModifiers = new Dictionary> { @@ -393,58 +482,90 @@ public void DeepCopy_ShouldDeepCopy() { "child1", new StringHierarchyRegion("child1", null) }, { "child2", new StringHierarchyRegion("child2", null) } }; + // Create region with the above parameters + var stringHierarchyRegion = new StringHierarchyRegion("base", + emptyRegion, emptyRegion.Options, regionOptions, regionModifiers, + new Dictionary()); - var stringHierarchyRegion = new StringHierarchyRegion("base", emptyRegion, emptyRegion.Options, regionOptions, regionModifiers, regionSubregions); + // Create variable to store old region output + StringHierarchyRegion oldRegion; + // Add subregions after creating region to ensure correct parent region fields + stringHierarchyRegion.AddSubregion(new StringHierarchyRegion("child1", null), out oldRegion); + stringHierarchyRegion.AddSubregion(new StringHierarchyRegion("child2", null), out oldRegion); + // Check deep copy for region with null parameter values (i.e., value + // equality but not reference equality) var nullRegionCopy = nullRegion.DeepCopy(true); - Assert.Equivalent(nullRegion, nullRegionCopy, strict: true); Assert.NotEqual(nullRegion, nullRegionCopy); Assert.NotSame(nullRegion, nullRegionCopy); + // Check deep copy for region with empty parameter values (i.e., value + // equality but not reference equality) var emptyRegionCopy = emptyRegion.DeepCopy(true); - Assert.Equivalent(emptyRegion, emptyRegionCopy, strict: true); Assert.NotEqual(emptyRegion, emptyRegionCopy); Assert.NotSame(emptyRegion, emptyRegionCopy); + // Check deep copy for region with non-empty parameter values (i.e., value + // equality but not reference equality for all non-region fields) var stringHierarchyRegionCopy = stringHierarchyRegion.DeepCopy(true); - - Assert.Equal(stringHierarchyRegion.ToString(), stringHierarchyRegionCopy.ToString()); - Assert.Same(stringHierarchyRegion.ParentRegion, stringHierarchyRegionCopy.ParentRegion); - Assert.Equal(stringHierarchyRegion.Options, stringHierarchyRegionCopy.Options); - Assert.NotSame(stringHierarchyRegion.Options, stringHierarchyRegionCopy.Options); - Assert.Equal(stringHierarchyRegion.Modifiers, stringHierarchyRegionCopy.Modifiers); - Assert.NotSame(stringHierarchyRegion.Modifiers, stringHierarchyRegionCopy.Modifiers); - // Testing Note: Due to the presence of parentRegion in each of the subregions and due to the deep copying of the subregions themselves, it is not feasible to check equality or equivalence of the subregions - Assert.NotEqual(stringHierarchyRegion.Subregions, stringHierarchyRegionCopy.Subregions); - Assert.NotSame(stringHierarchyRegion.Subregions, stringHierarchyRegionCopy.Subregions); + Assert.Equal(stringHierarchyRegion.ToString(), + stringHierarchyRegionCopy.ToString()); + Assert.Same(stringHierarchyRegion.ParentRegion, + stringHierarchyRegionCopy.ParentRegion); + Assert.Equal(stringHierarchyRegion.Options, + stringHierarchyRegionCopy.Options); + Assert.NotSame(stringHierarchyRegion.Options, + stringHierarchyRegionCopy.Options); + Assert.Equal(stringHierarchyRegion.Modifiers, + stringHierarchyRegionCopy.Modifiers); + Assert.NotSame(stringHierarchyRegion.Modifiers, + stringHierarchyRegionCopy.Modifiers); + // Testing Note: Due to the presence of parentRegion in each of the + // subregions and due to the deep copying of the subregions themselves, + // it is not feasible to check equality or equivalence of the + // subregions + Assert.NotEqual(stringHierarchyRegion.Subregions, + stringHierarchyRegionCopy.Subregions); + Assert.NotSame(stringHierarchyRegion.Subregions, + stringHierarchyRegionCopy.Subregions); } /// - /// Test the DeepCopy method with retaining the parent reference. + /// Test the DeepCopy method without retaining the parent reference. /// [Fact] public void DeepCopy_ShouldDeepCopyResetParent() { + // Create region with null parameter values var nullRegion = new StringHierarchyRegion("base", null); - var parent = new StringHierarchyRegion("root", nullRegion, null, new HashSet(), new Dictionary>(), new Dictionary()); + // Create region with non-null but empty options, modifiers, and subregions + var parent = new StringHierarchyRegion("root", nullRegion, null, + new HashSet(), new Dictionary>(), + new Dictionary()); + // Create non-empty options, modifiers, and subregions parameters var regionOptions = new HashSet() { "right", "left" }; var regionModifiers = new Dictionary> { { "modSet1", new HashSet() { "mod1", "mod2" } }, { "modSet2", new HashSet() { "mod3", "mod4" } } }; - var regionSubregions = new Dictionary - { - { "child1", new StringHierarchyRegion("child1", null) }, - { "child2", new StringHierarchyRegion("child2", null) } - }; + // Create region with the above parameters + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, + parent.Options, regionOptions, regionModifiers, + new Dictionary()); - var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); + // Create variable to store old region output + StringHierarchyRegion oldRegion; + // Add subregions after creating region to ensure correct parent region fields + stringHierarchyRegion.AddSubregion(new StringHierarchyRegion("child1", null), out oldRegion); + stringHierarchyRegion.AddSubregion(new StringHierarchyRegion("child2", null), out oldRegion); + // Check deep copy for region with empty parameter values (i.e., value + // equality but not reference equality) before and after setting parent + // region of original to null (should be not equal before, equal after) var parentCopy = parent.DeepCopy(false); - Assert.NotEqual(parent.ParentRegion, parentCopy.ParentRegion); parent.ParentRegion = null; Assert.Equal(parent.ParentRegion, parentCopy.ParentRegion); @@ -452,20 +573,35 @@ public void DeepCopy_ShouldDeepCopyResetParent() Assert.NotEqual(parent, parentCopy); Assert.NotSame(parent, parentCopy); + // Check deep copy for region with non-empty parameter values (i.e., + // value equality but not reference equality) before and after setting + // parent region of original to null var stringHierarchyRegionCopy = stringHierarchyRegion.DeepCopy(false); - - Assert.NotEqual(stringHierarchyRegion.ParentRegion, stringHierarchyRegionCopy.ParentRegion); + Assert.NotEqual(stringHierarchyRegion.ParentRegion, + stringHierarchyRegionCopy.ParentRegion); stringHierarchyRegion.ParentRegion = null; - Assert.Equal(stringHierarchyRegion.ParentRegion, stringHierarchyRegionCopy.ParentRegion); - Assert.Equal(stringHierarchyRegion.ToString(), stringHierarchyRegionCopy.ToString()); - Assert.Same(stringHierarchyRegion.ParentRegion, stringHierarchyRegionCopy.ParentRegion); - Assert.Equal(stringHierarchyRegion.Options, stringHierarchyRegionCopy.Options); - Assert.NotSame(stringHierarchyRegion.Options, stringHierarchyRegionCopy.Options); - Assert.Equal(stringHierarchyRegion.Modifiers, stringHierarchyRegionCopy.Modifiers); - Assert.NotSame(stringHierarchyRegion.Modifiers, stringHierarchyRegionCopy.Modifiers); - // Testing Note: Due to the presence of parentRegion in each of the subregions and due to the deep copying of the subregions themselves, it is not feasible to check equality or equivalence of the subregions - Assert.NotEqual(stringHierarchyRegion.Subregions, stringHierarchyRegionCopy.Subregions); - Assert.NotSame(stringHierarchyRegion.Subregions, stringHierarchyRegionCopy.Subregions); + Assert.Equal(stringHierarchyRegion.ParentRegion, + stringHierarchyRegionCopy.ParentRegion); + Assert.Equal(stringHierarchyRegion.ToString(), + stringHierarchyRegionCopy.ToString()); + Assert.Same(stringHierarchyRegion.ParentRegion, + stringHierarchyRegionCopy.ParentRegion); + Assert.Equal(stringHierarchyRegion.Options, + stringHierarchyRegionCopy.Options); + Assert.NotSame(stringHierarchyRegion.Options, + stringHierarchyRegionCopy.Options); + Assert.Equal(stringHierarchyRegion.Modifiers, + stringHierarchyRegionCopy.Modifiers); + Assert.NotSame(stringHierarchyRegion.Modifiers, + stringHierarchyRegionCopy.Modifiers); + // Testing Note: Due to the presence of parentRegion in each of the + // subregions and due to the deep copying of the subregions themselves, + // it is not feasible to check equality or equivalence of the + // subregions + Assert.NotEqual(stringHierarchyRegion.Subregions, + stringHierarchyRegionCopy.Subregions); + Assert.NotSame(stringHierarchyRegion.Subregions, + stringHierarchyRegionCopy.Subregions); } /// @@ -474,7 +610,11 @@ public void DeepCopy_ShouldDeepCopyResetParent() [Fact] public void IsValidModifierSpec_ShouldOutputFalse() { - var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + // Create simple parent region + var parent = new StringHierarchyRegion("root", null, null, + new HashSet(), new Dictionary>(), + new Dictionary()); + // Create non-empty options, modifiers, and subregions parameters var regionOptions = new HashSet() { "right", "left" }; var regionModifiers = new Dictionary> { @@ -486,17 +626,23 @@ public void IsValidModifierSpec_ShouldOutputFalse() { "child1", new StringHierarchyRegion("child1", null) }, { "child2", new StringHierarchyRegion("child2", null) } }; + // Create region using the above parameters + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, + parent.Options, regionOptions, regionModifiers, regionSubregions); - var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); - + // Create invalid test modifier specs var modSpec1 = ""; var modSpec2 = "mod5"; var modSpec3 = "mod1, mod2"; - var modSpec4 = "mod1, mod2, mod3"; + var modSpec4 = "mod1, mod3, mod4"; + // Test empty modifier spec Assert.False(stringHierarchyRegion.IsValidModifierSpec(modSpec1)); + // Test invalid modifier Assert.False(stringHierarchyRegion.IsValidModifierSpec(modSpec2)); + // Test invalid modifier set (all in same set) Assert.False(stringHierarchyRegion.IsValidModifierSpec(modSpec3)); + // Test invalid modifier set (pair in same set) Assert.False(stringHierarchyRegion.IsValidModifierSpec(modSpec4)); } @@ -506,7 +652,11 @@ public void IsValidModifierSpec_ShouldOutputFalse() [Fact] public void IsValidModifierSpec_ShouldOutputTrue() { - var parent = new StringHierarchyRegion("root", null, null, new HashSet(), new Dictionary>(), new Dictionary()); + // Create simple parent region + var parent = new StringHierarchyRegion("root", null, null, + new HashSet(), new Dictionary>(), + new Dictionary()); + // Create non-empty options, modifiers, and subregions parameters var regionOptions = new HashSet() { "right", "left" }; var regionModifiers = new Dictionary> { @@ -518,13 +668,17 @@ public void IsValidModifierSpec_ShouldOutputTrue() { "child1", new StringHierarchyRegion("child1", null) }, { "child2", new StringHierarchyRegion("child2", null) } }; + // Create region using the above parameters + var stringHierarchyRegion = new StringHierarchyRegion("base", parent, + parent.Options, regionOptions, regionModifiers, regionSubregions); - var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); - + // Create invalid test modifier specs var modSpec1 = "mod3"; var modSpec2 = "mod1, mod4"; + // Test valid modifier Assert.True(stringHierarchyRegion.IsValidModifierSpec(modSpec1)); + // Test valid modifier set Assert.True(stringHierarchyRegion.IsValidModifierSpec(modSpec2)); } } From 974b2ab2eabae8d763fdfd2f39aceb27188d5a2d Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Wed, 15 Jan 2025 14:00:16 -0800 Subject: [PATCH 10/34] Bugfix/tooling (#61) * Workflow uses dotnet-tools * Trigger CI workflow on all branches --- dotnet-tools.json => .config/dotnet-tools.json | 0 .github/workflows/ci.yaml | 9 ++++----- 2 files changed, 4 insertions(+), 5 deletions(-) rename dotnet-tools.json => .config/dotnet-tools.json (100%) diff --git a/dotnet-tools.json b/.config/dotnet-tools.json similarity index 100% rename from dotnet-tools.json rename to .config/dotnet-tools.json diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index eced69d..f1c4d9d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,8 +9,7 @@ on: - '**/ci.yaml' - '**/xunit.runner.json' branches: - - main - - '**release**' # Trigger on branches with 'release' in the name + - '**' # Trigger on all branches pull_request: paths: # Trigger when source code, sln or prj files, this file, or the xunit config file is changed. - '**/*.cs' @@ -55,15 +54,15 @@ jobs: - name: Build solution without restore run: dotnet build tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.sln --no-restore --configuration Release - - name: Install ReportGenerator - run: dotnet tool install -g dotnet-reportgenerator-globaltool + - name: Restore .NET Tools + run: dotnet tool restore - name: Run unit tests and collect coverage run: dotnet test tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.csproj --no-build --configuration Release --collect:"XPlat Code Coverage" - name: Generate coverage report run: | - reportgenerator -reports:tests/**/coverage.cobertura.xml -targetdir:coverage -reporttypes:Html + dotnet tool run reportgenerator -reports:tests/**/coverage.cobertura.xml -targetdir:coverage -reporttypes:Html - name: Upload coverage report uses: actions/upload-artifact@v4 From 4ee04b4625fc354562781914ed4c519ababc9f85 Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Thu, 23 Jan 2025 17:18:26 -0500 Subject: [PATCH 11/34] TODO comments --- src/EStimLibrary/Core/DataLimits.cs | 5 +++++ src/EStimLibrary/Core/Haptics/HapticTransducer.cs | 2 ++ .../Core/Stimulation/Data/BaseStimParams.cs | 3 +++ .../Core/Stimulation/StimulatorManager.cs | 1 + .../Core/Stimulation/Stimulators/Stimulator.cs | 15 ++++++++++++--- 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/EStimLibrary/Core/DataLimits.cs b/src/EStimLibrary/Core/DataLimits.cs index 2d04ef3..a9eebf6 100644 --- a/src/EStimLibrary/Core/DataLimits.cs +++ b/src/EStimLibrary/Core/DataLimits.cs @@ -27,6 +27,10 @@ public interface IDataLimits : ISelectable bool IsValidDataValue(object value); } + +// TODO: move these "example" implementations to Extensions + + public record StringDataLimits : IDataLimits { public string Name => "String Data Limits"; @@ -69,6 +73,7 @@ public bool IsValidDataValue(object value) /// /// The lower bound, inclusive. /// The upper bound, inclusive. +/// TODO: add resolution and rounding scheme param public record ContinuousDataLimits(double MinBound, double MaxBound) : IDataLimits { diff --git a/src/EStimLibrary/Core/Haptics/HapticTransducer.cs b/src/EStimLibrary/Core/Haptics/HapticTransducer.cs index 2087606..2b14249 100644 --- a/src/EStimLibrary/Core/Haptics/HapticTransducer.cs +++ b/src/EStimLibrary/Core/Haptics/HapticTransducer.cs @@ -9,6 +9,8 @@ public abstract class HapticTransducer : ISelectable { public abstract string Name { get; } // ISelectable + // TODO: property that says which parameters are modulated + /// /// Transduce a haptic event into stimulation data and push the data changes /// to stimulator hardware. A new programmatic thread is created for each diff --git a/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs b/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs index 0ce7f65..8e8a777 100644 --- a/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs +++ b/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs @@ -56,10 +56,13 @@ public static class BaseStimParams {AnodeRatio, 4 }, {AnodeFirst, 5 }, {Period, 6 }, + // TODO; delete this param {FixedRepeats, 7 } }; // TODO: put in the actual limits for each param; rn just semi-dummy values + // TODO: write doc comments for this and other things in this class + // TODO: put this in EchoStimulator directly bc it's just an example! public static Dictionary> ExampleParamData => new() { diff --git a/src/EStimLibrary/Core/Stimulation/StimulatorManager.cs b/src/EStimLibrary/Core/Stimulation/StimulatorManager.cs index 5a19169..ecc5c1b 100644 --- a/src/EStimLibrary/Core/Stimulation/StimulatorManager.cs +++ b/src/EStimLibrary/Core/Stimulation/StimulatorManager.cs @@ -368,6 +368,7 @@ public void UpdateStim(StimThread stimThread) // the value of Tuple(trains, globalToLocalOutputIds). //ThreadPool.QueueUserWorkItem(state => stim.UpdateStim(state), // (trains, globalToLocalOutputIds)); + // TODO: try-catch exception here; make error user-accessible but continue on safely stim.UpdateStim((trainsParams, localOutputAssignments)); } // Do nothing if invalid stim ID. TODO: how best to indicate this diff --git a/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs b/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs index e37287f..2e438cb 100644 --- a/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs +++ b/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs @@ -51,6 +51,7 @@ public abstract SortedSet ModulatableStimParams BaseStimParams.ParamOrderIndices.Keys.All( this.StimParamData.Keys.Contains) && // b) all available parameters are from the same enum + // TODO: delete? not an enum anymore... this.StimParamData.Keys.Select(param => param.GetType()) .Distinct().Count() == 1; @@ -116,14 +117,21 @@ protected Stimulator()//, int baseOutputConfigId) /// public abstract bool IsValidOutputWiring(IEnumerable localOutputIds); + // TODO: use this somewhere!!! e.g., UpdateStim() public bool IsValidParamValue(string stimParam, object paramValue) { var (paramLims, defaultVal) = this.StimParamData[stimParam]; return paramLims.IsValidDataValue(paramValue); } + + // TODO: calibration/comfort safety check function, then also call that + // somewhere like IsValidParamValue, e.g., in UpdateStim + + // MAIN UPDATE STIM METHOD PASSED TO EACH THREAD DURING CONFIG AND CALLED // BY TRANSDUCER ON UPDATE + // TODO; propogate exception public bool UpdateStim(object state) { // TODO: there is probably a better way to do this global-local output @@ -135,10 +143,13 @@ public bool UpdateStim(object state) Dictionary))state; var trainsParams = data.Item1; var localOutputAssignments = data.Item2; + // TODO: apply check functions return this.HW_UpdateStim(trainsParams, localOutputAssignments); } //protected abstract bool HW_UpdateStim(IEnumerable stimTrains, // Dictionary globalToLocalOutputIds); + // TODO @Rachel: implement for WSS! NOT UpdateStim + // TODO: throw exception upon failure protected abstract bool HW_UpdateStim( IEnumerable> trainsParams, Dictionary localOutputAssignments); @@ -234,6 +245,4 @@ protected void SendMessage(byte[] data) /// /// The full byte array of data to send. protected abstract void HW_SendMessage(byte[] data); - -} - +} \ No newline at end of file From 3d13a3e64fb1c17651399892029b678fbc7cebee Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Thu, 23 Jan 2025 17:36:35 -0500 Subject: [PATCH 12/34] Reorganized DataLimits files --- src/EStimLibrary/Core/Data/IDataLimits.cs | 28 ++ src/EStimLibrary/Core/DataLimits.cs | 254 ------------------ src/EStimLibrary/Core/IFactory.cs | 5 +- src/EStimLibrary/Core/ILimitable.cs | 2 +- .../Core/SpatialModel/BodyModelBuilderBase.cs | 6 +- .../Core/SpatialModel/IBodyModel.cs | 2 +- .../Core/Stimulation/Data/BaseStimParams.cs | 3 +- .../Stimulation/Stimulators/Stimulator.cs | 2 +- .../ThreadConfigDataPerStimulator.cs | 1 + src/EStimLibrary/Core/Utils.cs | 2 +- src/EStimLibrary/EStimLibrary.csproj | 4 + .../Extensions/Data/ContinuousDataLimits.cs | 30 +++ .../Data/ContinuousIntDataLimits.cs | 28 ++ .../Extensions/Data/DynamicDataLimits.cs | 31 +++ .../Extensions/Data/FixedOptionDataLimits.cs | 29 ++ .../Extensions/Data/SequenceDataLimits.cs | 118 ++++++++ .../Extensions/Data/StringDataLimits.cs | 17 ++ .../Haptics/ClassicDirectTransducer.cs | 1 + .../StringHierarchyAreaFactory.cs | 2 + .../StringHierarchyBodyModel.cs | 1 + .../StringHierarchyLocationFactory.cs | 2 + .../Stimulation/Stimulators/EchoStimulator.cs | 4 +- 22 files changed, 308 insertions(+), 264 deletions(-) create mode 100644 src/EStimLibrary/Core/Data/IDataLimits.cs delete mode 100644 src/EStimLibrary/Core/DataLimits.cs create mode 100644 src/EStimLibrary/Extensions/Data/ContinuousDataLimits.cs create mode 100644 src/EStimLibrary/Extensions/Data/ContinuousIntDataLimits.cs create mode 100644 src/EStimLibrary/Extensions/Data/DynamicDataLimits.cs create mode 100644 src/EStimLibrary/Extensions/Data/FixedOptionDataLimits.cs create mode 100644 src/EStimLibrary/Extensions/Data/SequenceDataLimits.cs create mode 100644 src/EStimLibrary/Extensions/Data/StringDataLimits.cs diff --git a/src/EStimLibrary/Core/Data/IDataLimits.cs b/src/EStimLibrary/Core/Data/IDataLimits.cs new file mode 100644 index 0000000..3a2462c --- /dev/null +++ b/src/EStimLibrary/Core/Data/IDataLimits.cs @@ -0,0 +1,28 @@ +namespace EStimLibrary.Core.Data; + + +/// +/// A structure to represent the limits on a specific associated parameter or +/// other data value, supplying data type information and bounding/limiting +/// validation. +/// +public interface IDataLimits : ISelectable +{ + /// + /// The data type that is valid for the associated data. + /// + public Type ValidDataType { get; } + /// + /// A strign description of the limits this structure imposes on the + /// associated data. Meant to be open-ended to accommodate listing discrete + /// options, explaining bounds, etc. Basically a 'help' message. + /// + public string Description { get; } + /// + /// Validate a specific data value. Must be non-null and of the + /// ValidDataType, in addition to any other validation checks. + /// + /// + /// True if the value is valid, false if not. + bool IsValidDataValue(object value); +} \ No newline at end of file diff --git a/src/EStimLibrary/Core/DataLimits.cs b/src/EStimLibrary/Core/DataLimits.cs deleted file mode 100644 index a9eebf6..0000000 --- a/src/EStimLibrary/Core/DataLimits.cs +++ /dev/null @@ -1,254 +0,0 @@ -namespace EStimLibrary.Core; - - -/// -/// A structure to represent the limits on a specific associated parameter or -/// other data value, supplying data type information and bounding/limiting -/// validation. -/// -public interface IDataLimits : ISelectable -{ - /// - /// The data type that is valid for the associated data. - /// - public Type ValidDataType { get; } - /// - /// A strign description of the limits this structure imposes on the - /// associated data. Meant to be open-ended to accommodate listing discrete - /// options, explaining bounds, etc. Basically a 'help' message. - /// - public string Description { get; } - /// - /// Validate a specific data value. Must be non-null and of the - /// ValidDataType, in addition to any other validation checks. - /// - /// - /// True if the value is valid, false if not. - bool IsValidDataValue(object value); -} - - -// TODO: move these "example" implementations to Extensions - - -public record StringDataLimits : IDataLimits -{ - public string Name => "String Data Limits"; - - public Type ValidDataType => typeof(string); - public string Description => "Any string-type data."; - public bool IsValidDataValue(object value) - { - return value.GetType() == this.ValidDataType; - } -} - -/// -/// A limit structure for data that can only have values from a specific list. -/// -/// The data type of the specific options for the -/// associated data. -/// The specific values the associated data can be. -/// -public record FixedOptionDataLimits( - SortedSet DataOptions) : IDataLimits -{ - public string Name => "Fixed Option Data Limits"; - public Type ValidDataType => typeof(DataType); - public string Description => $"Data of type '{typeof(DataType)}' with " + - $"discrete options:\n\t" + - string.Join($"\n\t", this.DataOptions); - - public bool IsValidDataValue(object value) - { - return value is not null && - value.GetType() == this.ValidDataType && - this.DataOptions.Contains((DataType)value); - } -} - -/// -/// A limit structure for data that can have values within a continuous, -/// real-valued range. -/// -/// The lower bound, inclusive. -/// The upper bound, inclusive. -/// TODO: add resolution and rounding scheme param -public record ContinuousDataLimits(double MinBound, double MaxBound) : - IDataLimits -{ - public string Name => "Continuous Data Limits"; - public Type ValidDataType => typeof(double); - public string Description => $"{this.Name}: [{this.MinBound}, " + - $"{this.MaxBound}]"; - - public bool IsValidDataValue(object value) - { - return value is not null && - value.GetType() == this.ValidDataType && - this.MinBound <= (double)value && - (double)value <= this.MaxBound; - } -} - -/// -/// A limit structure for data that can have values within a continuous, -/// integer-valued range. -/// -/// The lower bound, inclusive. -/// The upper bound, inclusive. -public record ContinuousIntDataLimits(int MinBound, int MaxBound) : - IDataLimits -{ - public string Name => "Continuous Integer Data Limits"; - public Type ValidDataType => typeof(int); - public string Description => $"{this.Name}: [{this.MinBound}, " + - $"{this.MaxBound}]"; - - public bool IsValidDataValue(object value) - { - return value is not null && - value.GetType() == this.ValidDataType && - this.MinBound <= (int)value && - (int)value <= this.MaxBound; - } -} - -/// -/// A limit structure for data with run-time-dependent limits or some other form -/// of dynamic validation. -/// -/// The data type of the associated data. -/// The dynamic validation function that is called -/// within the IsValidDataValue() method of this limits record. The function -/// must take in the data value (of the correct data type) and return a boolean -/// indicating if the data value is valid at the time when the function is -/// called. -/// The string description of the dynamic validation. -/// Will be used as the overall description for the limits imposed. -public record DynamicDataLimits( - Func CheckFunction, string Description) : IDataLimits -{ - public string Name => "Dynamic Data Limits"; - public Type ValidDataType => typeof(DataType); - - public bool IsValidDataValue(object value) - { - return value is not null && - value.GetType() == this.ValidDataType && - this.CheckFunction((DataType)value); - } -} - -/// -/// A limit structure for data that is itself a sequence of data values, each -/// element with its own subsequent data limits. -/// -/// A list of string names for each element in -/// the sequence. The names must be unique and in the desired order. -/// The corresponding data limits for each element, -/// keyed by the element's string name which must be present in -/// OrderedElementNames. -/// An optional parameter: the string names of -/// any elements in the sequence that are themselves optional and don't have to -/// be included in an actual sequence data record. This set must only contain -/// strings found in OrderedElementNames. Value is null by default, i.e., if all -/// elements are required. -public record SequenceDataLimits(List OrderedElementNames, - Dictionary ElementLimits, - IEnumerable OptionalElements = null) : IDataLimits -{ - public string Name => "Sequence Data Limits"; - // At bare minimum, data instances valid for further validation of this - // against this sequence limitations are ordered lists of sequence element - // name-value pairs. - public Type ValidDataType => typeof(List>); - public string Description - { - get - { - string description = "A sequence of data values, each with their " + - $"own data limits, as follows:\n"; - // Add the description of each element's data limits. - for (int i = 0; i < this.OrderedElementNames.Count; i++) - { - string elementName = this.OrderedElementNames[i]; - - // Add a '*' to the beginning of the element description if it - // is an optional element in the sequence. - bool isOptional = this.OptionalElements is not null && this.OptionalElements.Contains(elementName); - string optionalPrefix = isOptional ? "*" : ""; - - description = description + $"\t{optionalPrefix}" + - $"Element {i + 1} = '{elementName}': " + - $"{this.ElementLimits[elementName].Description}\n"; - } - return description; - } - } - - public bool IsValidDataValue(object value) - { - // Fail immediately if null or not a list of name-value pairs is given. - if (value is null || value.GetType() != this.ValidDataType) - { - return false; - } - - // Extract the sequence, a list of element name-value pairs. - var sequence = (List>)value; - - // Iterate through each element name-value pair. - int headerIndex = 0; - for (int dataIndex = 0; dataIndex < sequence.Count; dataIndex++) - { - // Return failure if data values provided but no more expected - // in the sequence. - if (headerIndex >= this.OrderedElementNames.Count) - { - return false; - } - - // Get the element name-value pair. - (string givenName, object givenValue) = sequence[dataIndex]; - - // Get the name of the element expected to be at this index. - var expectedName = this.OrderedElementNames[headerIndex]; - - // Check if the expected and given names match. - if (!givenName.Equals(expectedName)) - { - // If names don't match, check if the expected element is - // optional. Simply go to next header index if so. - if (this.OptionalElements is not null && - this.OptionalElements.Contains(expectedName)) - { - // Decrement the data index so this value is checked again - // and not skipped. - dataIndex--; - } - // Otherwise, return failure: the sequence is missing an - // element. - else - { - return false; - } - } - - // Otherwise, the names match, so get the data limits. - var limits = this.ElementLimits[givenName]; - // Return failure if an invalid element value is given. - if (!limits.IsValidDataValue(givenValue)) - { - return false; - } - - // Otherwise, it's valid data so far. Look at the next expected - // value. - headerIndex++; - } - - // If made it to the end of the loop, all data valid. Return success. - return true; - } -} \ No newline at end of file diff --git a/src/EStimLibrary/Core/IFactory.cs b/src/EStimLibrary/Core/IFactory.cs index ef0f6ae..033c374 100644 --- a/src/EStimLibrary/Core/IFactory.cs +++ b/src/EStimLibrary/Core/IFactory.cs @@ -1,4 +1,7 @@ -namespace EStimLibrary.Core; +using EStimLibrary.Core.Data; + + +namespace EStimLibrary.Core; public interface IFactory diff --git a/src/EStimLibrary/Core/ILimitable.cs b/src/EStimLibrary/Core/ILimitable.cs index 1aa7430..f9fc984 100644 --- a/src/EStimLibrary/Core/ILimitable.cs +++ b/src/EStimLibrary/Core/ILimitable.cs @@ -1,4 +1,4 @@ -using EStimLibrary.Core; +using EStimLibrary.Core.Data; namespace EStimLibrary; diff --git a/src/EStimLibrary/Core/SpatialModel/BodyModelBuilderBase.cs b/src/EStimLibrary/Core/SpatialModel/BodyModelBuilderBase.cs index b56a09a..833e98b 100644 --- a/src/EStimLibrary/Core/SpatialModel/BodyModelBuilderBase.cs +++ b/src/EStimLibrary/Core/SpatialModel/BodyModelBuilderBase.cs @@ -1,4 +1,8 @@ -namespace EStimLibrary.Core.SpatialModel; +using EStimLibrary.Core.Data; +using EStimLibrary.Extensions.Data; + + +namespace EStimLibrary.Core.SpatialModel; public abstract class BodyModelBuilderBase : ISelectable, IFactory diff --git a/src/EStimLibrary/Core/SpatialModel/IBodyModel.cs b/src/EStimLibrary/Core/SpatialModel/IBodyModel.cs index 98cb7b9..cabbf8e 100644 --- a/src/EStimLibrary/Core/SpatialModel/IBodyModel.cs +++ b/src/EStimLibrary/Core/SpatialModel/IBodyModel.cs @@ -1,4 +1,4 @@ -using EStimLibrary.Core; +using EStimLibrary.Core.Data; namespace EStimLibrary.Core.SpatialModel; diff --git a/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs b/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs index 8e8a777..d03f506 100644 --- a/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs +++ b/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs @@ -1,5 +1,6 @@ using EStimLibrary.Extensions.Stimulation.Phases; -using EStimLibrary.Core; +using EStimLibrary.Core.Data; +using EStimLibrary.Extensions.Data; namespace EStimLibrary.Core.Stimulation.Data; diff --git a/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs b/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs index 2e438cb..d27141f 100644 --- a/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs +++ b/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs @@ -1,6 +1,6 @@ using EStimLibrary.Core.Stimulation.Data; using EStimLibrary.Core.Stimulation.Functions; -using EStimLibrary.Core.Stimulation.Trains; +using EStimLibrary.Core.Data; namespace EStimLibrary.Core.Stimulation.Stimulators; diff --git a/src/EStimLibrary/Core/Stimulation/ThreadConfigDataPerStimulator.cs b/src/EStimLibrary/Core/Stimulation/ThreadConfigDataPerStimulator.cs index b1308cd..d622ec2 100644 --- a/src/EStimLibrary/Core/Stimulation/ThreadConfigDataPerStimulator.cs +++ b/src/EStimLibrary/Core/Stimulation/ThreadConfigDataPerStimulator.cs @@ -1,4 +1,5 @@ using EStimLibrary.Core.HardwareInterfaces; +using EStimLibrary.Core.Data; namespace EStimLibrary.Core.Stimulation; diff --git a/src/EStimLibrary/Core/Utils.cs b/src/EStimLibrary/Core/Utils.cs index c01ebf5..4371f8c 100644 --- a/src/EStimLibrary/Core/Utils.cs +++ b/src/EStimLibrary/Core/Utils.cs @@ -2,7 +2,7 @@ using System.IO.Ports; using System.Reflection; -using EStimLibrary.Core.Haptics; +using EStimLibrary.Core.Data; using EStimLibrary.Core.Stimulation.Stimulators; diff --git a/src/EStimLibrary/EStimLibrary.csproj b/src/EStimLibrary/EStimLibrary.csproj index d7cd0ff..8212393 100644 --- a/src/EStimLibrary/EStimLibrary.csproj +++ b/src/EStimLibrary/EStimLibrary.csproj @@ -25,6 +25,8 @@ + + @@ -44,6 +46,8 @@ + + diff --git a/src/EStimLibrary/Extensions/Data/ContinuousDataLimits.cs b/src/EStimLibrary/Extensions/Data/ContinuousDataLimits.cs new file mode 100644 index 0000000..908e030 --- /dev/null +++ b/src/EStimLibrary/Extensions/Data/ContinuousDataLimits.cs @@ -0,0 +1,30 @@ +using EStimLibrary.Core.Data; + + +namespace EStimLibrary.Extensions.Data; + + +/// +/// A limit structure for data that can have values within a continuous, +/// real-valued range. +/// +/// The lower bound, inclusive. +/// The upper bound, inclusive. +/// TODO: add resolution? and rounding scheme param? or leave rounding scheme +/// for elsewhere to do since this is just validation, not subsequent action... +public record ContinuousDataLimits(double MinBound, double MaxBound) : + IDataLimits +{ + public string Name => "Continuous Data Limits"; + public Type ValidDataType => typeof(double); + public string Description => $"{this.Name}: [{this.MinBound}, " + + $"{this.MaxBound}]"; + + public bool IsValidDataValue(object value) + { + return value is not null && + value.GetType() == this.ValidDataType && + this.MinBound <= (double)value && + (double)value <= this.MaxBound; + } +} \ No newline at end of file diff --git a/src/EStimLibrary/Extensions/Data/ContinuousIntDataLimits.cs b/src/EStimLibrary/Extensions/Data/ContinuousIntDataLimits.cs new file mode 100644 index 0000000..d4565a0 --- /dev/null +++ b/src/EStimLibrary/Extensions/Data/ContinuousIntDataLimits.cs @@ -0,0 +1,28 @@ +using EStimLibrary.Core.Data; + + +namespace EStimLibrary.Extensions.Data; + + +/// +/// A limit structure for data that can have values within a continuous, +/// integer-valued range. +/// +/// The lower bound, inclusive. +/// The upper bound, inclusive. +public record ContinuousIntDataLimits(int MinBound, int MaxBound) : + IDataLimits +{ + public string Name => "Continuous Integer Data Limits"; + public Type ValidDataType => typeof(int); + public string Description => $"{this.Name}: [{this.MinBound}, " + + $"{this.MaxBound}]"; + + public bool IsValidDataValue(object value) + { + return value is not null && + value.GetType() == this.ValidDataType && + this.MinBound <= (int)value && + (int)value <= this.MaxBound; + } +} \ No newline at end of file diff --git a/src/EStimLibrary/Extensions/Data/DynamicDataLimits.cs b/src/EStimLibrary/Extensions/Data/DynamicDataLimits.cs new file mode 100644 index 0000000..05e84b1 --- /dev/null +++ b/src/EStimLibrary/Extensions/Data/DynamicDataLimits.cs @@ -0,0 +1,31 @@ +using EStimLibrary.Core.Data; + + +namespace EStimLibrary.Extensions.Data; + + +/// +/// A limit structure for data with run-time-dependent limits or some other form +/// of dynamic validation. +/// +/// The data type of the associated data. +/// The dynamic validation function that is called +/// within the IsValidDataValue() method of this limits record. The function +/// must take in the data value (of the correct data type) and return a boolean +/// indicating if the data value is valid at the time when the function is +/// called. +/// The string description of the dynamic validation. +/// Will be used as the overall description for the limits imposed. +public record DynamicDataLimits( + Func CheckFunction, string Description) : IDataLimits +{ + public string Name => "Dynamic Data Limits"; + public Type ValidDataType => typeof(DataType); + + public bool IsValidDataValue(object value) + { + return value is not null && + value.GetType() == this.ValidDataType && + this.CheckFunction((DataType)value); + } +} \ No newline at end of file diff --git a/src/EStimLibrary/Extensions/Data/FixedOptionDataLimits.cs b/src/EStimLibrary/Extensions/Data/FixedOptionDataLimits.cs new file mode 100644 index 0000000..c17c9ce --- /dev/null +++ b/src/EStimLibrary/Extensions/Data/FixedOptionDataLimits.cs @@ -0,0 +1,29 @@ +using EStimLibrary.Core.Data; + + +namespace EStimLibrary.Extensions.Data; + + +/// +/// A limit structure for data that can only have values from a specific list. +/// +/// The data type of the specific options for the +/// associated data. +/// The specific values the associated data can be. +/// +public record FixedOptionDataLimits( + SortedSet DataOptions) : IDataLimits +{ + public string Name => "Fixed Option Data Limits"; + public Type ValidDataType => typeof(DataType); + public string Description => $"Data of type '{typeof(DataType)}' with " + + $"discrete options:\n\t" + + string.Join($"\n\t", this.DataOptions); + + public bool IsValidDataValue(object value) + { + return value is not null && + value.GetType() == this.ValidDataType && + this.DataOptions.Contains((DataType)value); + } +} \ No newline at end of file diff --git a/src/EStimLibrary/Extensions/Data/SequenceDataLimits.cs b/src/EStimLibrary/Extensions/Data/SequenceDataLimits.cs new file mode 100644 index 0000000..a6f6f6e --- /dev/null +++ b/src/EStimLibrary/Extensions/Data/SequenceDataLimits.cs @@ -0,0 +1,118 @@ +using EStimLibrary.Core.Data; + + +namespace EStimLibrary.Extensions.Data; + + +/// +/// A limit structure for data that is itself a sequence of data values, each +/// element with its own subsequent data limits. +/// +/// A list of string names for each element in +/// the sequence. The names must be unique and in the desired order. +/// The corresponding data limits for each element, +/// keyed by the element's string name which must be present in +/// OrderedElementNames. +/// An optional parameter: the string names of +/// any elements in the sequence that are themselves optional and don't have to +/// be included in an actual sequence data record. This set must only contain +/// strings found in OrderedElementNames. Value is null by default, i.e., if all +/// elements are required. +public record SequenceDataLimits(List OrderedElementNames, + Dictionary ElementLimits, + IEnumerable OptionalElements = null) : IDataLimits +{ + public string Name => "Sequence Data Limits"; + // At bare minimum, data instances valid for further validation of this + // against this sequence limitations are ordered lists of sequence element + // name-value pairs. + public Type ValidDataType => typeof(List>); + public string Description + { + get + { + string description = "A sequence of data values, each with their " + + $"own data limits, as follows:\n"; + // Add the description of each element's data limits. + for (int i = 0; i < this.OrderedElementNames.Count; i++) + { + string elementName = this.OrderedElementNames[i]; + + // Add a '*' to the beginning of the element description if it + // is an optional element in the sequence. + bool isOptional = this.OptionalElements is not null && this.OptionalElements.Contains(elementName); + string optionalPrefix = isOptional ? "*" : ""; + + description = description + $"\t{optionalPrefix}" + + $"Element {i + 1} = '{elementName}': " + + $"{this.ElementLimits[elementName].Description}\n"; + } + return description; + } + } + + public bool IsValidDataValue(object value) + { + // Fail immediately if null or not a list of name-value pairs is given. + if (value is null || value.GetType() != this.ValidDataType) + { + return false; + } + + // Extract the sequence, a list of element name-value pairs. + var sequence = (List>)value; + + // Iterate through each element name-value pair. + int headerIndex = 0; + for (int dataIndex = 0; dataIndex < sequence.Count; dataIndex++) + { + // Return failure if data values provided but no more expected + // in the sequence. + if (headerIndex >= this.OrderedElementNames.Count) + { + return false; + } + + // Get the element name-value pair. + (string givenName, object givenValue) = sequence[dataIndex]; + + // Get the name of the element expected to be at this index. + var expectedName = this.OrderedElementNames[headerIndex]; + + // Check if the expected and given names match. + if (!givenName.Equals(expectedName)) + { + // If names don't match, check if the expected element is + // optional. Simply go to next header index if so. + if (this.OptionalElements is not null && + this.OptionalElements.Contains(expectedName)) + { + // Decrement the data index so this value is checked again + // and not skipped. + dataIndex--; + } + // Otherwise, return failure: the sequence is missing an + // element. + else + { + return false; + } + } + + // Otherwise, the names match, so get the data limits. + var limits = this.ElementLimits[givenName]; + // Return failure if an invalid element value is given. + if (!limits.IsValidDataValue(givenValue)) + { + return false; + } + + // Otherwise, it's valid data so far. Look at the next expected + // value. + headerIndex++; + } + + // If made it to the end of the loop, all data valid. Return success. + return true; + } +} \ No newline at end of file diff --git a/src/EStimLibrary/Extensions/Data/StringDataLimits.cs b/src/EStimLibrary/Extensions/Data/StringDataLimits.cs new file mode 100644 index 0000000..71396dd --- /dev/null +++ b/src/EStimLibrary/Extensions/Data/StringDataLimits.cs @@ -0,0 +1,17 @@ +using EStimLibrary.Core.Data; + + +namespace EStimLibrary.Extensions.Data; + + +public record StringDataLimits : IDataLimits +{ + public string Name => "String Data Limits"; + + public Type ValidDataType => typeof(string); + public string Description => "Any string-type data."; + public bool IsValidDataValue(object value) + { + return value.GetType() == this.ValidDataType; + } +} \ No newline at end of file diff --git a/src/EStimLibrary/Extensions/Haptics/ClassicDirectTransducer.cs b/src/EStimLibrary/Extensions/Haptics/ClassicDirectTransducer.cs index 8709cbb..4c832b8 100644 --- a/src/EStimLibrary/Extensions/Haptics/ClassicDirectTransducer.cs +++ b/src/EStimLibrary/Extensions/Haptics/ClassicDirectTransducer.cs @@ -3,6 +3,7 @@ using EStimLibrary.Core.Haptics; using EStimLibrary.Core.SpatialModel; using EStimLibrary.Core; +using EStimLibrary.Extensions.Data; namespace EStimLibrary.Extensions.Haptics; diff --git a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyAreaFactory.cs b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyAreaFactory.cs index f2bedd2..3a4582c 100644 --- a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyAreaFactory.cs +++ b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyAreaFactory.cs @@ -1,4 +1,6 @@ using EStimLibrary.Core; +using EStimLibrary.Core.Data; +using EStimLibrary.Extensions.Data; using EStimLibrary.Core.SpatialModel; diff --git a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyBodyModel.cs b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyBodyModel.cs index 45b872d..3436dab 100644 --- a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyBodyModel.cs +++ b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyBodyModel.cs @@ -1,4 +1,5 @@ using EStimLibrary.Core; +using EStimLibrary.Core.Data; using EStimLibrary.Core.SpatialModel; diff --git a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyLocationFactory.cs b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyLocationFactory.cs index b07b0b1..3d04e62 100644 --- a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyLocationFactory.cs +++ b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyLocationFactory.cs @@ -1,4 +1,6 @@ using EStimLibrary.Core; +using EStimLibrary.Core.Data; +using EStimLibrary.Extensions.Data; using EStimLibrary.Core.SpatialModel; diff --git a/src/EStimLibrary/Extensions/Stimulation/Stimulators/EchoStimulator.cs b/src/EStimLibrary/Extensions/Stimulation/Stimulators/EchoStimulator.cs index 2c762ca..3c6dd5d 100644 --- a/src/EStimLibrary/Extensions/Stimulation/Stimulators/EchoStimulator.cs +++ b/src/EStimLibrary/Extensions/Stimulation/Stimulators/EchoStimulator.cs @@ -2,9 +2,7 @@ using EStimLibrary.Core.Stimulation.Data; using EStimLibrary.Core; using EStimLibrary.Core.Stimulation.Functions; -using EStimLibrary.Core.Stimulation.Trains; -using EStimLibrary.Extensions.Stimulation.Phases; -using Newtonsoft.Json.Linq; +using EStimLibrary.Core.Data; namespace EStimLibrary.Extensions.Stimulation.Stimulators; From 4e884214c22c7cdce4c5658f18beb46435cbf821 Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Thu, 23 Jan 2025 17:39:53 -0500 Subject: [PATCH 13/34] Deleted FixedRepeats BaseParam --- .../Core/Stimulation/Data/BaseStimParams.cs | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs b/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs index d03f506..8903b95 100644 --- a/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs +++ b/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs @@ -46,7 +46,6 @@ public static class BaseStimParams public const string AnodeRatio = "AnodeRatio"; public const string AnodeFirst = "AnodeFirst"; public const string Period = "Period"; - public const string FixedRepeats = "FixedRepeats"; public static Dictionary ParamOrderIndices = new() { @@ -56,9 +55,7 @@ public static class BaseStimParams {IPD, 3 }, {AnodeRatio, 4 }, {AnodeFirst, 5 }, - {Period, 6 }, - // TODO; delete this param - {FixedRepeats, 7 } + {Period, 6 } }; // TODO: put in the actual limits for each param; rn just semi-dummy values @@ -99,10 +96,7 @@ public static Dictionary> // Pulse period in s (1/Hz) { Period, new( new ContinuousDataLimits(0.0, 1/250.0), - 1/60.0)}, - { FixedRepeats, new( - new ContinuousIntDataLimits(0, Constants.POS_INFINITY), - 10)} + 1/60.0)} }; //public const int FirstPhaseParamIdx = (int)BaseStimParam.PA; @@ -150,17 +144,7 @@ public static bool IsPatternParam(string param) } } - public static bool IsTrainParam(string param) - { - switch (param) - { - case BaseStimParams.FixedRepeats: - return true; - - default: - return false; - } - } + // Could have similar IsTrainParam() function if add any in future. /// /// Sort parameter string keys by paired integer value. From 082a0f130469709914b4e443a71ca22298ebb168 Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Wed, 29 Jan 2025 16:08:59 -0500 Subject: [PATCH 14/34] TODO comments abt param mod and checking --- .../Core/Stimulation/Stimulators/Stimulator.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs b/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs index d27141f..688ded1 100644 --- a/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs +++ b/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs @@ -35,6 +35,12 @@ protected abstract ValidateStimParamDataDelegate StimParamDataCheckFunction // Derived classes must implement the get() of the abstract properties. // e.g., SpecificStimulator.NumOutputs get { return constNumOutputs; }. + // TODO: rename to StimParamBounds or StimParamSpecification or something + // like that + // TODO: define (in Extensions) a type that is a NestedParam; maybe it's a + // nested param DataLimits, idk, but a tool ppl who are running into the + // "trainMods: List" case can use; maybe it's just an example + // implementation, but something public abstract Dictionary> StimParamData { get; } public abstract SortedSet ModulatableStimParams @@ -46,7 +52,9 @@ public abstract SortedSet ModulatableStimParams //{ get; } // Essentially a validation check on stim param specification after // Stimulator construction. + // TODO: check that all the default/fixed values are within the IDataLimits public bool ValidStimParamSpecification => + // TODO: remove this check; e.g., bc voltage sitmulator doesn't have these! just leave as an example // a) at least the base stim params are included in the enum. BaseStimParams.ParamOrderIndices.Keys.All( this.StimParamData.Keys.Contains) && From 55f87508b5608c2b1d7f4727a988872fcb588bd2 Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Wed, 29 Jan 2025 16:58:14 -0500 Subject: [PATCH 15/34] Stim param spec cleanup; removed BaseStimParams --- .../Core/Haptics/HapticSession.cs | 2 +- .../Core/Stimulation/Data/BaseStimParams.cs | 159 ------------------ .../Core/Stimulation/StimulatorManager.cs | 13 +- .../Stimulation/Stimulators/Stimulator.cs | 58 ++++--- .../Haptics/ClassicDirectTransducer.cs | 8 +- .../Stimulation/Stimulators/EchoStimulator.cs | 75 ++++++++- 6 files changed, 116 insertions(+), 199 deletions(-) delete mode 100644 src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs diff --git a/src/EStimLibrary/Core/Haptics/HapticSession.cs b/src/EStimLibrary/Core/Haptics/HapticSession.cs index 8bf0163..5ed2181 100644 --- a/src/EStimLibrary/Core/Haptics/HapticSession.cs +++ b/src/EStimLibrary/Core/Haptics/HapticSession.cs @@ -466,7 +466,7 @@ public bool TryMapLeadPool(SortedSet leadIds, string bodyModelKey, this._stimManager.TryGetStimulator(stimId, out var stimulator); // Create config data: [stimId, leads, availableParams] ThreadConfigDataPerStimulator data = new(stimId, stimLeads, - stimulator.StimParamData, stimulator.ModulatableStimParams); + stimulator.StimParamSpecs, stimulator.ModulatableStimParams); // Add to total set of config data. allConfigData.Add(stimId, data); } diff --git a/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs b/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs deleted file mode 100644 index 8903b95..0000000 --- a/src/EStimLibrary/Core/Stimulation/Data/BaseStimParams.cs +++ /dev/null @@ -1,159 +0,0 @@ -using EStimLibrary.Extensions.Stimulation.Phases; -using EStimLibrary.Core.Data; -using EStimLibrary.Extensions.Data; - - -namespace EStimLibrary.Core.Stimulation.Data; - -// TODO: DELETE once implemented w/ individual enums -// TODO: OR make these Flags so can use bit arithmetic and flagging principles, -// along with the SortedSet list of params available or used, to mark -// simultaneously when and which parameters are changed - -// TODO: describe each param more as a flag for something the stimulator can do, -// rather than as a data value, even though the two may correspond - -//public enum BaseStimParam -//{ -// // Phase Params -// PA, // phase amplitude-- the amplitude in mA of the cathodic pulse phase -// PW, // phase width-- the width in us of the cathodic pulse phase -// PhaseShape, // phase shape-- refers to the shape of each phase of the pulse (default is rectangular) - -// // Pulse Params -// IPD, // interphase delay-- the time in us between two phases of a pulse -// AnodeRatio, // anode ratio-- the multiplier of the anodic phase's PW/divisor of the PA for charge balance. ex: AR = 2 -> anodic phase is twice the PW and half the PA of the cathodic phase -// AnodeFirst, // anode first-- binary that indicates that the anodic phase of the biphasic pulse should go first (default 0 is cathode-first) - -// // Pattern Params -// Period, // pattern period-- the period in ms of a pattern; all pulses (and the delays between them) in the pattern must fit within this time window; classically 1/PF given a pattern with only 1 pulse - -// // Train Params -// FixedRepeats // number of repeats-- the number of pattern/period repeats this train should run for -//} - -/// -/// A static class of *Extension Methods* to check which sub-group a stim param -/// falls under. -/// Ref.: https://stackoverflow.com/questions/9299279/how-to-group-enum-values -/// -public static class BaseStimParams -{ - public const string PA = "PA"; - public const string PW = "PW"; - public const string PhaseShape = "PhaseShape"; - public const string IPD = "IPD"; - public const string AnodeRatio = "AnodeRatio"; - public const string AnodeFirst = "AnodeFirst"; - public const string Period = "Period"; - - public static Dictionary ParamOrderIndices = new() - { - {PA, 0 }, - {PW, 1 }, - {PhaseShape, 2 }, - {IPD, 3 }, - {AnodeRatio, 4 }, - {AnodeFirst, 5 }, - {Period, 6 } - }; - - // TODO: put in the actual limits for each param; rn just semi-dummy values - // TODO: write doc comments for this and other things in this class - // TODO: put this in EchoStimulator directly bc it's just an example! - public static Dictionary> - ExampleParamData => new() - { - // Phase amplitude in mA - { PA, new( - new ContinuousDataLimits(0.0, 100), - 12.0)}, - // Phase width in us - { PW, new( - new ContinuousDataLimits(1.0, 250.0), - 4.0)}, - // Phase shape - { PhaseShape, new( - new FixedOptionDataLimits(new() - { - typeof(SquarePhaseData) - }), - typeof(SquarePhaseData))}, - // Inter-phase delay in us - { IPD, new( - new ContinuousDataLimits(1.0, 10.0), - 1.0)}, - { AnodeRatio, new( - new ContinuousDataLimits(0.0, 10.0), - 8.0)}, - { AnodeFirst, new( - new FixedOptionDataLimits(new() - { - Constants.ANODE_FIRST, - Constants.ANODE_SECOND - }), - Constants.ANODE_SECOND)}, - // Pulse period in s (1/Hz) - { Period, new( - new ContinuousDataLimits(0.0, 1/250.0), - 1/60.0)} - }; - - //public const int FirstPhaseParamIdx = (int)BaseStimParam.PA; - //public const int FirstPulseParamIdx = (int)BaseStimParam.IPD; - //public const int FirstPatternParamIdx = (int)BaseStimParam.Period; - //public const int FirstTrainParamIdx = (int)BaseStimParam.FixedRepeats; - - public static bool IsPhaseParam(string param) - { - switch (param) - { - case BaseStimParams.PA: - case BaseStimParams.PW: - case BaseStimParams.PhaseShape: - return true; - - default: - return false; - } - } - - public static bool IsPulseParam(string param) - { - switch (param) - { - case BaseStimParams.IPD: - case BaseStimParams.AnodeRatio: - case BaseStimParams.AnodeFirst: - return true; - - default: - return false; - } - } - - public static bool IsPatternParam(string param) - { - switch (param) - { - case BaseStimParams.Period: - return true; - - default: - return false; - } - } - - // Could have similar IsTrainParam() function if add any in future. - - /// - /// Sort parameter string keys by paired integer value. - /// - /// The parameter key: order index pairs. - /// An ordered list of parameter keys. - public static List SortParams(Dictionary paramIndices) - { - return paramIndices.OrderBy(pair => pair.Value) - .Select(pair => pair.Key).ToList(); - } -} diff --git a/src/EStimLibrary/Core/Stimulation/StimulatorManager.cs b/src/EStimLibrary/Core/Stimulation/StimulatorManager.cs index ecc5c1b..8f08ac9 100644 --- a/src/EStimLibrary/Core/Stimulation/StimulatorManager.cs +++ b/src/EStimLibrary/Core/Stimulation/StimulatorManager.cs @@ -55,12 +55,6 @@ public StimulatorManager() : base() this._OutputIdPool = new(); this._OutputStimulatorIdMap = new(); this._OutputsPerStimulatorUsage = new(); - - // Create stimulator ability dict with empty sets for all stim params. - foreach (string p in BaseStimParams.ParamOrderIndices.Keys) - { - this._stimulatorsWithAbilities.Add(p, new()); - } } /// @@ -113,6 +107,13 @@ public bool TryCreateAndRegisterStimulator(Type stimulatorType, // 4) Add the new stim ID to the set of each pulse param it supports. foreach (string p in stim.ModulatableStimParams) { + // If new param, add an empty set entry for it in the dictionary + if (!this._stimulatorsWithAbilities.ContainsKey(p)) + { + this._stimulatorsWithAbilities[p] = new(); + } + + // Add the global stimulator ID this._stimulatorsWithAbilities[p].Add(globalStimId); } diff --git a/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs b/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs index 688ded1..4b46771 100644 --- a/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs +++ b/src/EStimLibrary/Core/Stimulation/Stimulators/Stimulator.cs @@ -35,35 +35,51 @@ protected abstract ValidateStimParamDataDelegate StimParamDataCheckFunction // Derived classes must implement the get() of the abstract properties. // e.g., SpecificStimulator.NumOutputs get { return constNumOutputs; }. - // TODO: rename to StimParamBounds or StimParamSpecification or something - // like that // TODO: define (in Extensions) a type that is a NestedParam; maybe it's a // nested param DataLimits, idk, but a tool ppl who are running into the // "trainMods: List" case can use; maybe it's just an example // implementation, but something - public abstract Dictionary> StimParamData + + /// + /// The definition of available stimulation parameters on the stimulator, + /// their data limits, and their default/fixed value. + /// {"paramNameOrKey": (dataLimitsObject, defaultOrFixedValue), ...} + /// typeof(defaultOrFixedValue) must match dataLimitsObject.ValidDataType. + /// dataLimitsObject.IsValidDataValue(defaultOrFixedValue) must return True. + /// + public abstract Dictionary> + StimParamSpecs { get; } + + /// + /// The set of string keys of stimluation parameters that can be modulated + /// on the fly at run-time. All keys specified must be in StimParamSpecs. + /// public abstract SortedSet ModulatableStimParams { get; } - // Fixed-value params must be all params available that aren't modulatable. + + /// + /// All stimulation parameters available that are not modulatable on the fly + /// at run-time. All keys listed will be in StimParamSpecs. + /// public SortedSet FixedStimParams => new(this._StimParamsAvailable.Except(this.ModulatableStimParams)); - //public abstract Dictionary FixedStimParamValues - //{ get; } - // Essentially a validation check on stim param specification after - // Stimulator construction. - // TODO: check that all the default/fixed values are within the IDataLimits + + /// + /// A validation check on stimulation parameter specification after + /// Stimulator construction. True if: + /// 1) all specified default/fixed values are within the specified data + /// limits per specified parameter, and + /// 2) all param keys marked as modulatable are real specified parameters + /// public bool ValidStimParamSpecification => - // TODO: remove this check; e.g., bc voltage sitmulator doesn't have these! just leave as an example - // a) at least the base stim params are included in the enum. - BaseStimParams.ParamOrderIndices.Keys.All( - this.StimParamData.Keys.Contains) && - // b) all available parameters are from the same enum - // TODO: delete? not an enum anymore... - this.StimParamData.Keys.Select(param => param.GetType()) - .Distinct().Count() == 1; - - // Instantiated in constructor based on the keys in StimParamData. + // 1) Spec provides value default/fixed values + StimParamSpecs.All( + kvp => kvp.Value.Item1.IsValidDataValue(kvp.Value.Item2)) && + // 2) All param keys in modulatable list are specified + ModulatableStimParams.All(k => StimParamSpecs.Keys.Contains(k)); + + // Instantiated in constructor based on the keys in StimParamSpecs. protected readonly SortedSet _StimParamsAvailable; public SortedSet StimParamsAvailable => this._StimParamsAvailable; //new SortedSet((Enum[])Enum.GetValues(typeof(StimParamType))); @@ -105,7 +121,7 @@ protected Stimulator()//, int baseOutputConfigId) this.Id = -1; // Store the param options. - this._StimParamsAvailable = new(this.StimParamData.Keys); + this._StimParamsAvailable = new(this.StimParamSpecs.Keys); // Init empty output config dict and array of used markings per output. this._outputConfigs = new(); @@ -128,7 +144,7 @@ protected Stimulator()//, int baseOutputConfigId) // TODO: use this somewhere!!! e.g., UpdateStim() public bool IsValidParamValue(string stimParam, object paramValue) { - var (paramLims, defaultVal) = this.StimParamData[stimParam]; + var (paramLims, defaultVal) = this.StimParamSpecs[stimParam]; return paramLims.IsValidDataValue(paramValue); } diff --git a/src/EStimLibrary/Extensions/Haptics/ClassicDirectTransducer.cs b/src/EStimLibrary/Extensions/Haptics/ClassicDirectTransducer.cs index 4c832b8..b9601af 100644 --- a/src/EStimLibrary/Extensions/Haptics/ClassicDirectTransducer.cs +++ b/src/EStimLibrary/Extensions/Haptics/ClassicDirectTransducer.cs @@ -20,15 +20,11 @@ public class ClassicDirectTransducer : HapticTransducer public ClassicDirectTransducer(string modParam) { // TODO: adjust this validation to be flexible to user-input param - // lists. + // lists? or just relegate validation to session config?? // TODO: how to also factor in stimulator-specific modulation abilities? // e.g., even if valid param name, the stimulator used for a given event // may not be able to mod it... - if (!BaseStimParams.ParamOrderIndices.Keys.Contains(modParam)) - { - throw new ArgumentException($"{this.Name} Constructor Error: " + - $"{modParam} is not a valid stim param."); - } + this.ModulatedParam = modParam; } diff --git a/src/EStimLibrary/Extensions/Stimulation/Stimulators/EchoStimulator.cs b/src/EStimLibrary/Extensions/Stimulation/Stimulators/EchoStimulator.cs index 3c6dd5d..7347861 100644 --- a/src/EStimLibrary/Extensions/Stimulation/Stimulators/EchoStimulator.cs +++ b/src/EStimLibrary/Extensions/Stimulation/Stimulators/EchoStimulator.cs @@ -1,8 +1,8 @@ using EStimLibrary.Core.Stimulation.Stimulators; -using EStimLibrary.Core.Stimulation.Data; using EStimLibrary.Core; using EStimLibrary.Core.Stimulation.Functions; using EStimLibrary.Core.Data; +using EStimLibrary.Extensions.Data; namespace EStimLibrary.Extensions.Stimulation.Stimulators; @@ -26,14 +26,77 @@ public EchoStimulator(string port, int numOutputs) // Only allow half as many configs as there are outputs. public override int MaxNumOutputConfigs => this._NumOutputs / 2; - // Use example limits and defaults for all base stim params. + #region Convenience StimParam String Name Defines + protected const string PA = "PA"; + protected const string PW = "PW"; + protected const string StimPhaseShape = "StimPhaseShape"; + protected const string RechargePhaseShape = "RechargePhaseShape"; + protected const string IPD = "IPD"; + protected const string AnodeRatio = "AnodeRatio"; + protected const string AnodeFirst = "AnodeFirst"; + protected const string Period = "Period"; + #endregion + + /// + /// Example stimulation parameter specification. + /// {"paramNameOrKey": (dataLimitsObject, defaultOrFixedValue), ...} + /// public override Dictionary> - StimParamData => BaseStimParams.ExampleParamData; - // Params that can be dynamically modulated + StimParamSpecs => new() + { + // Phase amplitude in mA + { PA, new( + new ContinuousDataLimits(0.0, 10.0), + 0.0)}, + // Phase width in us + { PW, new( + new ContinuousDataLimits(0.0, 250.0), + 0.0)}, + // Activation/stimulation phase shape + { StimPhaseShape, new( + new FixedOptionDataLimits(new() + { + "square", + "sine", + "triangle" + }), + "sine")}, + // Recharge phase shape + { RechargePhaseShape, new( + new FixedOptionDataLimits(new() + { + "square", + "sine", + "triangle" + }), + "sine")}, + // Inter-phase delay in us + { IPD, new( + new ContinuousDataLimits(0.0, 150.0), + 100.0)}, + { AnodeRatio, new( + new ContinuousDataLimits(0.0, 12.0), + 12.0)}, + { AnodeFirst, new( + new FixedOptionDataLimits(new() + { + Constants.ANODE_FIRST, + Constants.ANODE_SECOND + }), + Constants.ANODE_SECOND)}, + // Pulse period in s (1/Hz) + { Period, new( + new ContinuousDataLimits(0.0, 1/250.0), + 1/100.0)} + }; + + /// + /// Example set of specified parameters that can be dynamically modulated. + /// public override SortedSet ModulatableStimParams => new() { - BaseStimParams.PA, - BaseStimParams.PW + PA, + PW }; #region TODO From cb8a085b50fea82c283da2494ca9391774f41d9d Mon Sep 17 00:00:00 2001 From: cld99 <115433496+cld99@users.noreply.github.com> Date: Thu, 30 Jan 2025 11:27:09 -0500 Subject: [PATCH 16/34] Fix code coverage for StringHierarchyRegionTests --- .../StringHierarchy/StringHierarchyRegionTests.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs index 935b110..a4fd0a1 100644 --- a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs +++ b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs @@ -119,8 +119,8 @@ public void Constructor_ShouldDeepCopy() var regionOptions = new HashSet() { "right", "left" }; var regionModifiers = new Dictionary> { - { "right", new HashSet() { "mod1", "mod2" } }, - { "left", new HashSet() { "mod3", "mod4" } } + { "right", new HashSet() { "mod1", "mod2", "mod2" } }, + { "left", new HashSet() { "mod2", "mod3", "mod4" } } }; var regionSubregions = new Dictionary { @@ -260,6 +260,11 @@ public void TryGetSubregion_ShouldOutputNull() out foundSubregion); Assert.False(output); Assert.Null(foundSubregion); + // Test with empty base region + output = stringHierarchyRegion.TryGetSubregion(", left base", + out foundSubregion); + Assert.False(output); + Assert.Null(foundSubregion); // Test with invalid base region output = stringHierarchyRegion.TryGetSubregion("base2", out foundSubregion); @@ -285,8 +290,8 @@ public void TryGetSubregion_ShouldOutputNull() out foundSubregion); Assert.False(output); Assert.Null(foundSubregion); - // Test with additional comma delimiters - output = stringHierarchyRegion.TryGetSubregion("left base, , child1", + // Test with invalid subregion + output = stringHierarchyRegion.TryGetSubregion("left base, ", out foundSubregion); Assert.False(output); Assert.Null(foundSubregion); From 1f842318ba0cf8573f70530bf8dd8985bd4bfa87 Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Wed, 5 Feb 2025 19:15:40 -0500 Subject: [PATCH 17/34] Upgraded to .NET 9.0 --- .github/workflows/ci.yaml | 2 +- .github/workflows/release.yaml | 2 +- .../EStimLibrary.ConsoleAppDemo.csproj | 2 +- src/EStimLibrary/EStimLibrary.csproj | 4 ++-- .../EStimLibrary.UnitTests.csproj | 12 ++++++------ 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f1c4d9d..286d2ce 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -41,7 +41,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v4 with: - dotnet-version: '7.x' # Adjust to your target .NET version + dotnet-version: '9.x' # Adjust to your target .NET version # Uncomment this process if you want the windows build to take 3mins extra but get rid of the 'Workload updates are available. Run `dotnet workload list` for more information' msg after building #- name: Update dotnet workloads (Windows) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e6628f8..700305d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -24,7 +24,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v4 with: - dotnet-version: '7.x' # Adjust to your target .NET version + dotnet-version: '9.x' # Adjust to your target .NET version - name: Restore dependencies run: dotnet restore $SLN_PATH diff --git a/examples/EStimLibrary.ConsoleAppDemo/EStimLibrary.ConsoleAppDemo.csproj b/examples/EStimLibrary.ConsoleAppDemo/EStimLibrary.ConsoleAppDemo.csproj index c47af07..eb43873 100644 --- a/examples/EStimLibrary.ConsoleAppDemo/EStimLibrary.ConsoleAppDemo.csproj +++ b/examples/EStimLibrary.ConsoleAppDemo/EStimLibrary.ConsoleAppDemo.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net9.0 enable enable diff --git a/src/EStimLibrary/EStimLibrary.csproj b/src/EStimLibrary/EStimLibrary.csproj index 8212393..1831c35 100644 --- a/src/EStimLibrary/EStimLibrary.csproj +++ b/src/EStimLibrary/EStimLibrary.csproj @@ -1,7 +1,7 @@ - net7.0 + net9.0 enable enable @@ -50,7 +50,7 @@ - + diff --git a/tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.csproj b/tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.csproj index 64ce108..f4931e2 100644 --- a/tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.csproj +++ b/tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.csproj @@ -1,7 +1,7 @@ - net7.0 + net9.0 enable enable @@ -10,17 +10,17 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all From a69f354c2092f565502f67e2d21f8ab473916977 Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Wed, 5 Feb 2025 19:21:10 -0500 Subject: [PATCH 18/34] Upgraded dotnet-tools.json --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 73f9597..bbab4bd 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-reportgenerator-globaltool": { - "version": "5.3.6", + "version": "5.4.3", "commands": [ "reportgenerator" ] From 7d712e604743f3b86071751c0da79eb8c7634e3b Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Wed, 5 Feb 2025 19:21:47 -0500 Subject: [PATCH 19/34] VSCode settings: dotrush sln specified --- .vscode/settings.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..afd473a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "_comment1": "specify which solution to load for C# Dev Kit tooling. Switch if working on a different sln", + "_comment2": "/Users/lauramcgann/Documents/CWRU/Lab/Code/EStimLibrary/examples/EStimLibrary.ConsoleAppDemo/EStimLibrary.ConsoleAppDemo.sln", + "dotrush.roslyn.projectOrSolutionFiles": [ + "/Users/lauramcgann/Documents/CWRU/Lab/Code/EStimLibrary/tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.sln" + ] +} \ No newline at end of file From 713be2a05ad93d6c2ac578fecb1b907ccd7c2c08 Mon Sep 17 00:00:00 2001 From: cld99 <115433496+cld99@users.noreply.github.com> Date: Wed, 5 Feb 2025 21:48:31 -0500 Subject: [PATCH 20/34] Remove extraneous error handling --- .../StringHierarchy/StringHierarchyRegion.cs | 5 ----- .../StringHierarchy/StringHierarchyRegionTests.cs | 9 --------- 2 files changed, 14 deletions(-) diff --git a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs index a7facd5..f63fbf7 100644 --- a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs +++ b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs @@ -123,11 +123,6 @@ public StringHierarchyRegion(string baseName, StringHierarchyRegion parent, public void AddSubregion(StringHierarchyRegion subregion, out StringHierarchyRegion? existingSubregion) { - if (subregion == null) - { - throw new ArgumentNullException(); - } - // Fill the out parameter with the existing subregion if exists. if (this.Subregions.TryGetValue(subregion.BaseName, out existingSubregion)) diff --git a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs index a4fd0a1..e262443 100644 --- a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs +++ b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs @@ -397,15 +397,6 @@ public void AddSubregion_ShouldAddSubregion() // Create variable to store old region output StringHierarchyRegion oldRegion; - // Check error output for null subregion parameter - var errorSuccess = false; - try - { - stringHierarchyRegion.AddSubregion(null, out oldRegion); - } - catch (ArgumentNullException ex) { errorSuccess = true; } - Assert.True(errorSuccess); - // Create new region with different name to existing subregions var newChildRegion = new StringHierarchyRegion("child3", null); From d255469e389546ddfc44c6c268077a42bae5cea5 Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Thu, 6 Feb 2025 13:45:21 -0500 Subject: [PATCH 21/34] Image pointers --- docs/images/association_arrows-techvidvan.png | Bin 48207 -> 130 bytes ...ion_multiplicity_annotations-vertabelo.png | Bin 30432 -> 130 bytes ...iation_multiplicity_concept-techvidvan.png | Bin 109670 -> 131 bytes .../images/class_notation-tutorialspoint.jpeg | Bin 13935 -> 130 bytes docs/images/uml_arrows-stack_overflow.png | Bin 4388 -> 129 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/association_arrows-techvidvan.png b/docs/images/association_arrows-techvidvan.png index 7642f51a57d09fd68a1b222a4b1ee1de2968f450..54ff5ffa9326e76c2c66e700c9e96f19e8c5fad4 100644 GIT binary patch literal 130 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 N3`)k2b+8b;`UC0DC%pgw literal 30432 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 G?7sljSbZ-5 diff --git a/docs/images/class_notation-tutorialspoint.jpeg b/docs/images/class_notation-tutorialspoint.jpeg index 198e734ce2e75d02d096da11cf7a1dd5ea66d648..ef737b2b794d80b8d4e6732da71630197c54f8b5 100644 GIT binary patch literal 130 zcmWN?K@!3s3;@78uiyg~Lkg7s29qGnsB{eW;OliSd&*n;c-c1Rp}TYUJ}-}|%m4Pd zEi|4^4_8Vl!P*@spec5X=AP- M8SP)OfEk1N0qk}s{Qv*} literal 13935 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 diff --git a/docs/images/uml_arrows-stack_overflow.png b/docs/images/uml_arrows-stack_overflow.png index 3fd853350ceede03e2e906ece89129b87e982cd3..56809c2ffc8c8b4f72c6e438282dc4a078432738 100644 GIT binary patch literal 129 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 J`{n$v{{xBHSmyu$ From 68079d2bc589da7825a5a9f0627a2158552b1863 Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Fri, 7 Feb 2025 14:20:26 -0500 Subject: [PATCH 22/34] Relative paths --- .vscode/settings.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index afd473a..f1884ae 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "_comment1": "specify which solution to load for C# Dev Kit tooling. Switch if working on a different sln", - "_comment2": "/Users/lauramcgann/Documents/CWRU/Lab/Code/EStimLibrary/examples/EStimLibrary.ConsoleAppDemo/EStimLibrary.ConsoleAppDemo.sln", + "_comment2": "examples/EStimLibrary.ConsoleAppDemo/EStimLibrary.ConsoleAppDemo.sln", "dotrush.roslyn.projectOrSolutionFiles": [ - "/Users/lauramcgann/Documents/CWRU/Lab/Code/EStimLibrary/tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.sln" + "tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.sln" ] -} \ No newline at end of file +} From 1d6bf67778d0869f6b5b8e94762e3ea7046163ba Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Sun, 9 Feb 2025 21:24:40 -0500 Subject: [PATCH 23/34] Two bugs found 1. IsValidModifierSpec: shared modifiers don't work. See TODO comments. 2. DeepCopy: parent options weren't copied (easy fix). New test method scheme. Comments in source class. --- .../StringHierarchy/StringHierarchyRegion.cs | 162 +++++-- .../StringHierarchyRegionTests.cs | 428 +++++++++++++++--- 2 files changed, 492 insertions(+), 98 deletions(-) diff --git a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs index f63fbf7..e928778 100644 --- a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs +++ b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs @@ -11,21 +11,31 @@ public class StringHierarchyRegion /// The base name of this region, e.g., hand. ///

public string BaseName { get; init; } + /// - /// The parent region of this region. + /// The parent region of this region. Null if this region is the root + /// region. /// - public StringHierarchyRegion ParentRegion { get; set; } + public StringHierarchyRegion? ParentRegion { get; set; } + /// - /// The all possible option strings through the parent tree to get to this - /// region. + /// All region options specified through the path through the parent tree + /// to this region. /// public HashSet ParentOptions { get; set; } /// - /// Options of the base region that can be selected, e.g., [left, right]. + /// Options of this base region that can be selected, e.g., [left, right]. /// Ensured to be non-null. Empty if none. /// public HashSet Options { get; set; } + /// + /// Whether or not there are options that must be specified at this base + /// region. + /// public bool HasOptions => this.Options.Count > 0; + /// + /// All optioned region names that can be specified at this base region. + /// public List OptionedRegionNames { get @@ -38,13 +48,14 @@ public List OptionedRegionNames $"{StringHierarchySpec.OPTION_REGION_DELIMITER}" + $"{this.BaseName}").ToList(); } - // Else, add include the base name. + // Else, just include the base name. else { return new() { this.BaseName }; } } } + /// /// Directional modifiers that could be applied to this region. Dictionary /// keyed by directional axis name. Value set contains possible modifiers @@ -59,9 +70,27 @@ public Dictionary> Modifiers _UpdateFrequencyDict(); } } + /// + /// Whether or not there are directional modifiers specified at this base + /// region. + /// public bool HasModifiers => this.Modifiers.Count > 0; - private Dictionary> _modifiers; + /// + /// Directional modifiers that could be applied to this region. Dictionary + /// keyed by directional axis name. Value set contains possible modifier + /// values along that axis. Ensured to be non-null. Empty if none. Accessed + /// by the Modifiers property. + /// + private Dictionary> _modifiers = new(); + /// + /// Dictionary of how frequently a modifier value occurs across all + /// specified modifier axes. Keyed by modifier value. Integer value is how + /// many axes contain that modifier value. Used when parsing string + /// hierarchy modifier specs to handle axes with the same modifier value, + /// e.g., x: center, y: center. Updated by the Mofidiers property setter. + /// private Dictionary _modifierFrequencyDict = new(); + /// /// The subregions of this region, keyed by string name. Ensured to be /// non-null. Empty if none, meaning this region is a "leaf" in the nodal @@ -72,7 +101,14 @@ public Dictionary Subregions get; protected set; } + /// + /// Whether or not this region has subregions. + /// public bool HasSubregions => this.Subregions.Count > 0; + /// + /// Whether or not this region is a "leaf" in the nodal graph, i.e., has no + /// subregions. + /// public bool IsLeaf => this.Subregions.Count == 0; /// @@ -84,17 +120,41 @@ public Dictionary Subregions /// public SortedSet SavedAreas { get; set; } - // Deep copies made of data structs but NOT of any referenced - // StringHierarchyRegions. References retained to those objects. - public StringHierarchyRegion(string baseName, StringHierarchyRegion parent, - HashSet parentOptions = null, - HashSet options = null, - Dictionary> modifiers = null, - Dictionary subregions = null) + /// + /// Create a new StringHierarchyRegion with the given base name and parent + /// region. Optionally provide parent options, region options, modifiers, + /// and subregions. Ensures all properties non-null after construction. + /// Deep copies made of collections passed in but NOT of any referenced + /// StringHierarchyRegions. References retained to those objects. + /// + /// The base name of this region, e.g., "hand". + /// + /// The parent region of this region (default: null, + /// meaning root region). + /// The specification of option values passed + /// to this region by the parent region (default: empty). + /// The possible option values for this region + /// (default: empty) + /// The directional modifiers that can be specified + /// at this region (default: empty). Key by modifier axis name. Valued by + /// set of possible modifier values along that directional axis. + /// The subregions of this region, if any + /// (default: empty). If none, this region is a "leaf" in the nodal graph. + /// + public StringHierarchyRegion(string baseName, + StringHierarchyRegion? parent = null, + // Note: the ! alleviates null warning, promising the value will be set + // to not-null in the constructor. + HashSet parentOptions = null!, + HashSet options = null!, + Dictionary> modifiers = null!, + Dictionary subregions = null!) { + // Store base name and reference to parent region. this.BaseName = baseName; this.ParentRegion = parent; - // Deep copy options and modifier structs. + + // Deep copy options and modifier collection structs. this.ParentOptions = (parentOptions is not null) ? new(parentOptions) : new(); this.Options = (options is not null) ? new(options) : new(); @@ -104,6 +164,8 @@ public StringHierarchyRegion(string baseName, StringHierarchyRegion parent, // Copy the dictionary but keep references to same subregion objects. this.Subregions = (subregions is not null) ? new(subregions) : new(); + // Initialize sets noting the IDs of saved locations and areas that + // point to this region. this.SavedLocations = new(); this.SavedAreas = new(); } @@ -114,20 +176,22 @@ public StringHierarchyRegion(string baseName, StringHierarchyRegion parent, /// any existing subregion. Sets the parent reference of the added subregion /// to be this region. /// - /// The subregion to add. Shallow copied. - /// Parent reference set. + /// The subregion to add. Shallow copied. Parent + /// reference set. /// An output parameter: the replaced but - /// unaltered existing subregion if any, else null. - /// The provided - /// subregion argument is null. + /// unaltered existing subregion of the same base name if any, else null. + /// + /// The provided subregion + /// argument is null. public void AddSubregion(StringHierarchyRegion subregion, out StringHierarchyRegion? existingSubregion) { - // Fill the out parameter with the existing subregion if exists. + // Fill the output parameter with the existing subregion if exists. if (this.Subregions.TryGetValue(subregion.BaseName, out existingSubregion)) { - // Overwrite the stored subregion with this basename. + // Replace the existing subregion at this basename with the new + // one. this.Subregions[subregion.BaseName] = subregion; } // Else add the new subregion keyed by its basename. @@ -140,19 +204,18 @@ public void AddSubregion(StringHierarchyRegion subregion, subregion.ParentRegion = this; } - // TODO: TEST THE HECK OUT OF THIS /// /// Try to get a given subregion of this region. /// - /// The specified region to search for, given as a + /// The specified region to search for, given as a /// string sequence of appropriately delimited option-region names. /// An output parameter: the searched /// subregion if found, null if not. /// True if the subregion could be found, False if not. public bool TryGetSubregion(string regionSpec, - out StringHierarchyRegion foundSubregion) + out StringHierarchyRegion? foundSubregion) { - // Split full linked name into sequence of option+region names. + // Split full region spec into sequence of option+region names. var regionSet = StringHierarchySpec.ParseRegionSpec(regionSpec); // Navigate the nodal graph to find the region. @@ -185,7 +248,7 @@ public bool TryGetSubregion(string regionSpec, if (searchCurrentRegion) { // If search name found, look for next item in subregions. - if (foundSubregion.OptionedRegionNames.Contains( + if (foundSubregion!.OptionedRegionNames.Contains( optionedRegionName)) { searchCurrentRegion = false; @@ -197,33 +260,54 @@ public bool TryGetSubregion(string regionSpec, return false; } } - // Else try to find matching subregion. + // Else search subregion for the option+region name. else { - // Fail if search region found in subregion, else search for next - // item in that subregion's subregions. - if (!(foundSubregion.Subregions.TryGetValue(searchBaseName, - out foundSubregion) && (searchOption.Equals("") || - foundSubregion.Options.Contains(searchOption)))) + // Check if search basename is in subregions. + // Sets foundSubregion to the subregion if found. + bool viableSubregion = foundSubregion!.Subregions.TryGetValue( + searchBaseName, out foundSubregion); + + // Fail if search basename not found in subregions or search + // option exists and not found in subregion matching search + // basename. + if (!(viableSubregion && + (searchOption.Equals("") || + foundSubregion!.Options.Contains(searchOption)))) { foundSubregion = null; return false; } + + // Else, search continues in located matching subregion. } } - // If made it here, subregion is found. Fill output param and return. + // If made it here, subregion is found. Output param already filled. + // Return success. return true; } + /// + /// Check if a given modifier specification is valid within this region. + /// Valid if: + /// - formatted (delimited) correctly + /// - all modifiers values are found in the possible value sets of the + /// modifier axes of this region + /// - each modifier axis is used at most once + /// + /// The modifier specification to check. + /// T/F if the spec is valid in this region. public bool IsValidModifierSpec(string modifierSpec) { - // Split into modifier set. + // Parse modifier spec into a set of modifier values. var modifierSet = StringHierarchySpec.ParseModifierSpec(modifierSpec); // Sort modifiers by their frequency across axis option sets, ascending // order so duplicate modifier values (e.g., "center" as a valid value // on two axes) doesn't use the only axis another modifier value may be - // valid for. + // valid for. I.e., duplicate modifier values "used" for the most + // restricted axis first. + // TODO: FIX THIS!!! var sortedModifierSet = modifierSet.OrderBy(modifier => this._modifierFrequencyDict.ContainsKey(modifier) ? this._modifierFrequencyDict[modifier] : 0) @@ -281,7 +365,6 @@ private void _UpdateFrequencyDict() } } - // TODO: TEST!!! /// /// Create a deep copy of the whole subtree starting at this region. /// @@ -294,8 +377,9 @@ public StringHierarchyRegion DeepCopy(bool retainParentReference = true) { var parentRegion = (retainParentReference) ? this.ParentRegion : null; // Create new region. Inherently deep copies options and modifiers. - var newRegion = new StringHierarchyRegion(this.BaseName, parentRegion, - options: this.Options, modifiers: this.Modifiers); + var newRegion = new StringHierarchyRegion(this.BaseName, parent: parentRegion!, + parentOptions: this.ParentOptions, options: this.Options, + modifiers: this.Modifiers); // Add deep copies of all subregions. foreach (var (_, subregion) in this.Subregions) diff --git a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs index e262443..101d1e9 100644 --- a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs +++ b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs @@ -11,23 +11,94 @@ public class StringHierarchyRegionTests { private readonly ITestOutputHelper _output; - // Test class constructor creates an output helper so it can write console - // output. + private static StringHierarchyRegion ConstructEmptyRoot() + { + return new StringHierarchyRegion("rootEmpty"); + } + + private static StringHierarchyRegion ConstructTwoLevelRoot() + { + StringHierarchyRegion rootTwoLevel; + StringHierarchyRegion middleTwoLevelA; + StringHierarchyRegion middleTwoLevelB; + StringHierarchyRegion leafTwoLevelAa; + StringHierarchyRegion leafTwoLevelAb; + + // root: arm + // / \ + // midA: hand midB: random + // / \ + // leafAa: finger leafAb: handbody + // Leaf regions + leafTwoLevelAa = new StringHierarchyRegion("finger", + parentOptions: new HashSet { "left", "right" }, + options: new HashSet { "thumb", "index", "middle", "ring", "pinky" }, + modifiers: new Dictionary> { + { "x", new HashSet { "lateral", "medial" } }, + { "y", new HashSet { "proximal", "middle", "distal" } }, + { "z", new HashSet { "palmar", "dorsal" } } } + ); + leafTwoLevelAb = new StringHierarchyRegion("handbody", + parentOptions: new(), + options: new HashSet { "left", "right" }, + modifiers: new Dictionary> { + { "x", new HashSet { "lateral", "medial" } }, + { "y", new HashSet { "proximal", "middle", "distal" } }, + { "z", new HashSet { "palmar", "dorsal" } } } + ); + // Middle regions + middleTwoLevelA = new StringHierarchyRegion("hand", + parentOptions: new HashSet { "upper", "lower" }, + options: new HashSet { "left", "right" }, + modifiers: new Dictionary> { + { "x", new HashSet { "lateral", "medial" } }, + { "y", new HashSet { "proximal", "middle", "distal" } }, + { "z", new HashSet { "palmar", "dorsal" } } }, + subregions: new Dictionary { + { "finger", leafTwoLevelAa }, + { "handbody", leafTwoLevelAb } } + ); + middleTwoLevelB = new StringHierarchyRegion("middleTwoLevelB", + parentOptions: new HashSet { "upper", "lower" }, + options: new HashSet { "ulnar", "radial" }); + // Root region + rootTwoLevel = new StringHierarchyRegion("rootTwoLevel", + options: new(), + subregions: new Dictionary { + { "hand", middleTwoLevelA }, + { "middleTwoLevelB", middleTwoLevelB } } + ); + + // Set parent references for all regions. + leafTwoLevelAa.ParentRegion = middleTwoLevelA; + leafTwoLevelAb.ParentRegion = middleTwoLevelA; + middleTwoLevelA.ParentRegion = rootTwoLevel; + middleTwoLevelB.ParentRegion = rootTwoLevel; + + return rootTwoLevel; + } + + /// + /// Test class constructor creates an output helper so it can write console + /// output. + /// + /// public StringHierarchyRegionTests(ITestOutputHelper testOutputHelper) { this._output = testOutputHelper; } - // Test method naming convention: LibClassMethodName_ScenarioShouldExpect - + #region Constructor /// - /// Test the constructor with null parameter values. + /// Test the constructor with null parameter values: + /// no parent region, + /// optional params passed null. /// [Fact] public void Constructor_ShouldAcceptNull() { - var stringHierarchyRegion = new StringHierarchyRegion("base", null, - null, null, null, null); + var stringHierarchyRegion = new StringHierarchyRegion("base", null!, + null!, null!, null!, null!); // Check that all fields correctly instantiate Assert.Equal("base", stringHierarchyRegion.BaseName); @@ -53,7 +124,7 @@ public void Constructor_ShouldAcceptNull() public void Constructor_ShouldDefaultNull() { // Create region with all default parameters unspecified - var stringHierarchyRegion = new StringHierarchyRegion("base", null); + var stringHierarchyRegion = new StringHierarchyRegion("base", null!); // Check that all fields correctly instantiate Assert.Equal("base", stringHierarchyRegion.BaseName); @@ -79,7 +150,7 @@ public void Constructor_ShouldDefaultNull() public void Constructor_ShouldAcceptEmpty() { // Create simple parent region - var parent = new StringHierarchyRegion("root", null); + var parent = new StringHierarchyRegion("root", null!); // Create region with all parameters non-null var stringHierarchyRegion = new StringHierarchyRegion("base", parent, @@ -112,7 +183,7 @@ public void Constructor_ShouldDeepCopy() { // Create parent region with non-null object parameters for deep copy // testing - var parent = new StringHierarchyRegion("root", null, null, + var parent = new StringHierarchyRegion("root", null!, null!, new HashSet(), new Dictionary>(), new Dictionary()); // Create non-empty options, modifiers, and subregions parameters @@ -124,8 +195,8 @@ public void Constructor_ShouldDeepCopy() }; var regionSubregions = new Dictionary { - { "child1", new StringHierarchyRegion("child1", null) }, - { "child2", new StringHierarchyRegion("child2", null) } + { "child1", new StringHierarchyRegion("child1", null!) }, + { "child2", new StringHierarchyRegion("child2", null!) } }; // Create region with the above parameters for deep copy testing var stringHierarchyRegion = new StringHierarchyRegion("base", parent, @@ -180,6 +251,7 @@ public void Constructor_ShouldDeepCopy() Assert.Equal(stringHierarchyRegion.Options, child.ParentOptions); Assert.NotSame(stringHierarchyRegion.Options, child.ParentOptions); } + #endregion Constructor /// /// Test the ToString method. @@ -188,10 +260,10 @@ public void Constructor_ShouldDeepCopy() public void ToString_ShouldOutputStringRep() { // Create region with null parameter values - var nullRegion = new StringHierarchyRegion("base", null); + var nullRegion = new StringHierarchyRegion("base", null!); // Create region with non-null but empty options, modifiers, and // subregions - var emptyRegion = new StringHierarchyRegion("root", null, null, + var emptyRegion = new StringHierarchyRegion("root", null!, null!, new HashSet(), new Dictionary>(), new Dictionary()); // Create non-empty options, modifiers, and subregions parameters @@ -204,14 +276,14 @@ public void ToString_ShouldOutputStringRep() }; var subregionSubregions = new Dictionary { - { "grandchild", new StringHierarchyRegion("grandchild", null) } + { "grandchild", new StringHierarchyRegion("grandchild", null!) } }; var regionSubregions = new Dictionary { - { "child1", new StringHierarchyRegion("child1", null, null, - new HashSet { "opt1", "opt2" }, null, + { "child1", new StringHierarchyRegion("child1", null!, null!, + new HashSet { "opt1", "opt2" }, null!, subregionSubregions) }, - { "child2", new StringHierarchyRegion("child2", null) } + { "child2", new StringHierarchyRegion("child2", null!) } }; // Create region with the above parameters var stringHierarchyRegion = new StringHierarchyRegion("base", @@ -227,6 +299,7 @@ public void ToString_ShouldOutputStringRep() " [right,left] base, child2", stringHierarchyRegion.ToString()); } + #region TryGetSubregion /// /// Test the TryGetSubregion method with various malformed or otherwise /// incorrect regions. @@ -235,7 +308,7 @@ public void ToString_ShouldOutputStringRep() public void TryGetSubregion_ShouldOutputNull() { // Create simple parent region - var parent = new StringHierarchyRegion("root", null, null, + var parent = new StringHierarchyRegion("root", null!, null!, new HashSet(), new Dictionary>(), new Dictionary()); // Create non-empty options, modifiers, and subregions parameters @@ -247,15 +320,16 @@ public void TryGetSubregion_ShouldOutputNull() }; var regionSubregions = new Dictionary { - { "child1", new StringHierarchyRegion("child1", null) }, - { "child2", new StringHierarchyRegion("child2", null) } + { "child1", new StringHierarchyRegion("child1", null!) }, + { "child2", new StringHierarchyRegion("child2", null!) } }; // Create region with the above parameters var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); + // TODO: make comment on PR abt using InlineData[] tags in future for cleaner code // Test with empty string representation - StringHierarchyRegion foundSubregion; + StringHierarchyRegion? foundSubregion; var output = stringHierarchyRegion.TryGetSubregion("", out foundSubregion); Assert.False(output); @@ -315,7 +389,7 @@ public void TryGetSubregion_ShouldOutputNull() public void TryGetSubregion_ShouldOutputRegion() { // Create simple parent region - var parent = new StringHierarchyRegion("root", null, null, + var parent = new StringHierarchyRegion("root", null!, null!, new HashSet(), new Dictionary>(), new Dictionary()); // Create non-empty options, modifiers, and subregions parameters @@ -327,21 +401,21 @@ public void TryGetSubregion_ShouldOutputRegion() }; var subregionSubregions = new Dictionary { - { "grandchild", new StringHierarchyRegion("grandchild", null) } + { "grandchild", new StringHierarchyRegion("grandchild", null!) } }; var regionSubregions = new Dictionary { - { "child1", new StringHierarchyRegion("child1", null, null, - new HashSet { "opt1", "opt2" }, null, + { "child1", new StringHierarchyRegion("child1", null!, null!, + new HashSet { "opt1", "opt2" }, null!, subregionSubregions) }, - { "child2", new StringHierarchyRegion("child2", null) } + { "child2", new StringHierarchyRegion("child2", null!) } }; // Create region with the above parameters var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); // Test with valid option and base region - StringHierarchyRegion foundSubregion; + StringHierarchyRegion? foundSubregion; var output = stringHierarchyRegion.TryGetSubregion("right base", out foundSubregion); Assert.True(output); @@ -367,7 +441,9 @@ public void TryGetSubregion_ShouldOutputRegion() Assert.True(output); Assert.Equal(regionSubregions["child1"], foundSubregion); } + #endregion TryGetSubregion + #region AddSubregion /// /// Test the AddSubregion method with new subregions. /// @@ -375,7 +451,7 @@ public void TryGetSubregion_ShouldOutputRegion() public void AddSubregion_ShouldAddSubregion() { // Create simple parent region - var parent = new StringHierarchyRegion("root", null, null, + var parent = new StringHierarchyRegion("root", null!, null!, new HashSet(), new Dictionary>(), new Dictionary()); // Create non-empty options, modifiers, and subregions parameters @@ -387,18 +463,18 @@ public void AddSubregion_ShouldAddSubregion() }; var regionSubregions = new Dictionary { - { "child1", new StringHierarchyRegion("child1", null) }, - { "child2", new StringHierarchyRegion("child2", null) } + { "child1", new StringHierarchyRegion("child1", null!) }, + { "child2", new StringHierarchyRegion("child2", null!) } }; // Create region with the above parameters var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); // Create variable to store old region output - StringHierarchyRegion oldRegion; + StringHierarchyRegion? oldRegion; // Create new region with different name to existing subregions - var newChildRegion = new StringHierarchyRegion("child3", null); + var newChildRegion = new StringHierarchyRegion("child3", null!); // Check shallow copy of subregion into base region and old subregion // output @@ -415,7 +491,7 @@ public void AddSubregion_ShouldAddSubregion() public void AddSubregion_ShouldReplaceSubregion() { // Create simple parent region - var parent = new StringHierarchyRegion("root", null, null, + var parent = new StringHierarchyRegion("root", null!, null!, new HashSet(), new Dictionary>(), new Dictionary()); // Create non-empty options, modifiers, and subregions parameters @@ -427,15 +503,15 @@ public void AddSubregion_ShouldReplaceSubregion() }; var regionSubregions = new Dictionary { - { "child1", new StringHierarchyRegion("child1", null) }, - { "child2", new StringHierarchyRegion("child2", null) } + { "child1", new StringHierarchyRegion("child1", null!) }, + { "child2", new StringHierarchyRegion("child2", null!) } }; // Create region with the above parameters var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); // Create variable to store old region output - StringHierarchyRegion oldRegion; + StringHierarchyRegion? oldRegion; // Test re-adding existing subregion stringHierarchyRegion.AddSubregion(regionSubregions["child2"], @@ -445,7 +521,7 @@ public void AddSubregion_ShouldReplaceSubregion() stringHierarchyRegion.Subregions["child2"]); // Create new region with same name as existing subregion - var newChildRegion = new StringHierarchyRegion("child1", null); + var newChildRegion = new StringHierarchyRegion("child1", null!); // Test adding new subregion with same name as existing subregion stringHierarchyRegion.AddSubregion(newChildRegion, out oldRegion); @@ -454,17 +530,117 @@ public void AddSubregion_ShouldReplaceSubregion() stringHierarchyRegion.Subregions["child1"]); Assert.Same(stringHierarchyRegion, newChildRegion.ParentRegion); } + #endregion AddSubregion + + #region DeepCopy + /// + /// Test the DeepCopy method with various root region structures. + /// + /// + [Theory] + [MemberData(nameof(DeepCopy_TestData))] + public void DeepCopy_ShouldSucceed(StringHierarchyRegion root) + { + // Create the deep copy + var deepCopy = root.DeepCopy(); + + // Recursive check. + DeepCopy_ShouldSucceed_Helper(root, deepCopy); + } + + /// + /// Recursive helper method for DeepCopy_ShouldSucceed. + /// + /// + /// + private void DeepCopy_ShouldSucceed_Helper(StringHierarchyRegion original, + StringHierarchyRegion deepCopy) + { + // Check root object is different address. + Assert.NotSame(original, deepCopy); + + // Check basename is same value. + Assert.Equal(original.BaseName, deepCopy.BaseName); + + // Check parent region is different address. + if (original.ParentRegion is null) + { + Assert.Null(deepCopy.ParentRegion); + } + else + { + Assert.NotSame(original.ParentRegion, deepCopy.ParentRegion); + } + + // Check parent options is different address. + Assert.NotSame(original.ParentOptions, deepCopy.ParentOptions); + + // Check parent options is same value. + Assert.Equal(original.ParentOptions, deepCopy.ParentOptions); + + // Check options is different address. + Assert.NotSame(original.Options, deepCopy.Options); + + // Check options is same value. + Assert.Equal(original.Options, deepCopy.Options); + + // Check modifiers is different address. + Assert.NotSame(original.Modifiers, deepCopy.Modifiers); + + // Check modifiers is same value. + Assert.Equal(original.Modifiers, deepCopy.Modifiers); + + // Check saved locations and areas are different address, same values. + Assert.NotSame(original.SavedLocations, deepCopy.SavedLocations); + Assert.Equal(original.SavedLocations, deepCopy.SavedLocations); + Assert.NotSame(original.SavedAreas, deepCopy.SavedAreas); + Assert.Equal(original.SavedAreas, deepCopy.SavedAreas); + + // Check subregions collection is different address. + Assert.NotSame(original.Subregions, deepCopy.Subregions); + + // Check subregion collections have same length. + Assert.Equal(original.Subregions.Count, deepCopy.Subregions.Count); + + // Check subregions. + foreach (var (subregionName, originalSubregion) in original.Subregions) + { + // Check copy has a subregion of the same name. + Assert.True(deepCopy.Subregions.TryGetValue(subregionName, + // Get copied subregion inherently. + out var copiedSubregion)); + + // Check value (address) of subregion is different. + Assert.NotSame(originalSubregion, copiedSubregion); + + // Recurse into subregion. + DeepCopy_ShouldSucceed_Helper(originalSubregion, copiedSubregion); + } + } + + /// + /// Test data for DeepCopy method. + /// + /// + public static IEnumerable DeepCopy_TestData() + { + return new List + { + new object[] { ConstructEmptyRoot() }, + new object[] { ConstructTwoLevelRoot() } + }; + } /// /// Test the DeepCopy method with retaining the parent reference. /// [Fact] - public void DeepCopy_ShouldDeepCopy() + public void DeepCopy_ShouldDeepCopy_Fact() { // Create region with null parameter values - var nullRegion = new StringHierarchyRegion("base", null); + var nullRegion = new StringHierarchyRegion("base", null!); // Create region with non-null but empty options, modifiers, and subregions - var emptyRegion = new StringHierarchyRegion("root", null, null, + var emptyRegion = new StringHierarchyRegion("root", null!, null!, new HashSet(), new Dictionary>(), new Dictionary()); // Create non-empty options and modifiers parameters @@ -476,8 +652,8 @@ public void DeepCopy_ShouldDeepCopy() }; var regionSubregions = new Dictionary { - { "child1", new StringHierarchyRegion("child1", null) }, - { "child2", new StringHierarchyRegion("child2", null) } + { "child1", new StringHierarchyRegion("child1", null!) }, + { "child2", new StringHierarchyRegion("child2", null!) } }; // Create region with the above parameters var stringHierarchyRegion = new StringHierarchyRegion("base", @@ -485,10 +661,10 @@ public void DeepCopy_ShouldDeepCopy() new Dictionary()); // Create variable to store old region output - StringHierarchyRegion oldRegion; + StringHierarchyRegion? oldRegion; // Add subregions after creating region to ensure correct parent region fields - stringHierarchyRegion.AddSubregion(new StringHierarchyRegion("child1", null), out oldRegion); - stringHierarchyRegion.AddSubregion(new StringHierarchyRegion("child2", null), out oldRegion); + stringHierarchyRegion.AddSubregion(new StringHierarchyRegion("child1", null!), out oldRegion); + stringHierarchyRegion.AddSubregion(new StringHierarchyRegion("child2", null!), out oldRegion); // Check deep copy for region with null parameter values (i.e., value // equality but not reference equality) @@ -536,9 +712,9 @@ public void DeepCopy_ShouldDeepCopy() public void DeepCopy_ShouldDeepCopyResetParent() { // Create region with null parameter values - var nullRegion = new StringHierarchyRegion("base", null); + var nullRegion = new StringHierarchyRegion("base", null!); // Create region with non-null but empty options, modifiers, and subregions - var parent = new StringHierarchyRegion("root", nullRegion, null, + var parent = new StringHierarchyRegion("root", nullRegion, null!, new HashSet(), new Dictionary>(), new Dictionary()); // Create non-empty options, modifiers, and subregions parameters @@ -554,17 +730,17 @@ public void DeepCopy_ShouldDeepCopyResetParent() new Dictionary()); // Create variable to store old region output - StringHierarchyRegion oldRegion; + StringHierarchyRegion? oldRegion; // Add subregions after creating region to ensure correct parent region fields - stringHierarchyRegion.AddSubregion(new StringHierarchyRegion("child1", null), out oldRegion); - stringHierarchyRegion.AddSubregion(new StringHierarchyRegion("child2", null), out oldRegion); + stringHierarchyRegion.AddSubregion(new StringHierarchyRegion("child1", null!), out oldRegion); + stringHierarchyRegion.AddSubregion(new StringHierarchyRegion("child2", null!), out oldRegion); // Check deep copy for region with empty parameter values (i.e., value // equality but not reference equality) before and after setting parent // region of original to null (should be not equal before, equal after) var parentCopy = parent.DeepCopy(false); Assert.NotEqual(parent.ParentRegion, parentCopy.ParentRegion); - parent.ParentRegion = null; + parent.ParentRegion = null!; Assert.Equal(parent.ParentRegion, parentCopy.ParentRegion); Assert.Equivalent(parent, parentCopy, strict: true); Assert.NotEqual(parent, parentCopy); @@ -576,7 +752,7 @@ public void DeepCopy_ShouldDeepCopyResetParent() var stringHierarchyRegionCopy = stringHierarchyRegion.DeepCopy(false); Assert.NotEqual(stringHierarchyRegion.ParentRegion, stringHierarchyRegionCopy.ParentRegion); - stringHierarchyRegion.ParentRegion = null; + stringHierarchyRegion.ParentRegion = null!; Assert.Equal(stringHierarchyRegion.ParentRegion, stringHierarchyRegionCopy.ParentRegion); Assert.Equal(stringHierarchyRegion.ToString(), @@ -600,7 +776,9 @@ public void DeepCopy_ShouldDeepCopyResetParent() Assert.NotSame(stringHierarchyRegion.Subregions, stringHierarchyRegionCopy.Subregions); } + #endregion DeepCopy + #region IsValidModifierSpec /// /// Test the IsValidModifierSpec method with invalid modifier specs. /// @@ -608,7 +786,7 @@ public void DeepCopy_ShouldDeepCopyResetParent() public void IsValidModifierSpec_ShouldOutputFalse() { // Create simple parent region - var parent = new StringHierarchyRegion("root", null, null, + var parent = new StringHierarchyRegion("root", null!, null!, new HashSet(), new Dictionary>(), new Dictionary()); // Create non-empty options, modifiers, and subregions parameters @@ -620,8 +798,8 @@ public void IsValidModifierSpec_ShouldOutputFalse() }; var regionSubregions = new Dictionary { - { "child1", new StringHierarchyRegion("child1", null) }, - { "child2", new StringHierarchyRegion("child2", null) } + { "child1", new StringHierarchyRegion("child1", null!) }, + { "child2", new StringHierarchyRegion("child2", null!) } }; // Create region using the above parameters var stringHierarchyRegion = new StringHierarchyRegion("base", parent, @@ -650,7 +828,7 @@ public void IsValidModifierSpec_ShouldOutputFalse() public void IsValidModifierSpec_ShouldOutputTrue() { // Create simple parent region - var parent = new StringHierarchyRegion("root", null, null, + var parent = new StringHierarchyRegion("root", null!, null!, new HashSet(), new Dictionary>(), new Dictionary()); // Create non-empty options, modifiers, and subregions parameters @@ -662,8 +840,8 @@ public void IsValidModifierSpec_ShouldOutputTrue() }; var regionSubregions = new Dictionary { - { "child1", new StringHierarchyRegion("child1", null) }, - { "child2", new StringHierarchyRegion("child2", null) } + { "child1", new StringHierarchyRegion("child1", null!) }, + { "child2", new StringHierarchyRegion("child2", null!) } }; // Create region using the above parameters var stringHierarchyRegion = new StringHierarchyRegion("base", parent, @@ -678,4 +856,136 @@ public void IsValidModifierSpec_ShouldOutputTrue() // Test valid modifier set Assert.True(stringHierarchyRegion.IsValidModifierSpec(modSpec2)); } + + /// + /// Test the IsValidModifierSpec method handles cases with same-valued + /// modifier axes. + /// + [Theory] + #region Single shared modifier. Same-length axis modifier value sets. + // Shared modifier value used in first axis. + [InlineData( + new string[] { "left", "middle", "right" }, + new string[] { "proximal", "middle", "distal" }, + "middle, proximal")] + // Shared modifier value used in second axis. + [InlineData( + new string[] { "left", "middle", "right" }, + new string[] { "proximal", "middle", "distal" }, + "left, middle")] + // Shared modifier value used in both axes. + [InlineData( + new string[] { "left", "middle", "right" }, + new string[] { "proximal", "middle", "distal" }, + "middle, middle")] + // Shared modifier value used in one axis, no modifier value used in other. + // TODO: Should this non-determinism be allowed!!! + [InlineData( + new string[] { "left", "middle", "right" }, + new string[] { "proximal", "middle", "distal" }, + "middle")] + // Shared modifier not used. + [InlineData( + new string[] { "left", "middle", "right" }, + new string[] { "proximal", "middle", "distal" }, + "distal")] + #endregion + #region Single shared modifier. Different-length axis modifier value sets. + // Shared modifier value used in first (shorter) axis. Listed first. + [InlineData( + new string[] { "left", "middle" }, + new string[] { "proximal", "middle", "distal" }, + "middle, proximal")] + // Shared modifier value used in first (shorter) axis. Listed second. + [InlineData( + new string[] { "left", "middle" }, + new string[] { "proximal", "middle", "distal" }, + "proximal, middle")] + // Shared modifier value used in second (longer) axis. Listed first. + // TODO: this test case fails!!! --> REQUIRES FIX in Region!!! larger refactor/reimplement + // [InlineData( + // new string[] { "left", "middle" }, + // new string[] { "proximal", "middle", "distal" }, + // "middle, left")] + // Shared modifier value used in second (longer) axis. Listed second. + [InlineData( + new string[] { "left", "middle" }, + new string[] { "proximal", "middle", "distal" }, + "left, middle")] + // Shared modifier value used in both axes. + [InlineData( + new string[] { "left", "middle" }, + new string[] { "proximal", "middle", "distal" }, + "middle, middle")] + // Shared modifier value used in one axis, no modifier value used in other. + // TODO: Should this non-determinism be allowed!!! + [InlineData( + new string[] { "left", "middle" }, + new string[] { "proximal", "middle", "distal" }, + "middle")] + // Shared modifier not used. + [InlineData( + new string[] { "left", "middle" }, + new string[] { "proximal", "middle", "distal" }, + "distal")] + #endregion + #region Two shared modifiers. Different-length axis modifier value sets. + // TODO: Which axis are shared modifiers bucketed into? Should this + // pseudo-non-determinism (rly just unknown-to-the-user behavior) be + // allowed???!!! + // Both values used: order 1. + [InlineData( + new string[] { "left", "middle" }, + new string[] { "left", "middle", "right" }, + "middle, left")] + // Both values used: order 2. + [InlineData( + new string[] { "left", "middle" }, + new string[] { "left", "middle", "right" }, + "left, middle")] + // One value use: axis 1, listed first. + [InlineData( + new string[] { "left", "middle" }, + new string[] { "left", "middle", "right" }, + "middle, right")] + // One value used: axis 1, listed second. + [InlineData( + new string[] { "left", "middle" }, + new string[] { "left", "middle", "right" }, + "right, middle")] + #endregion + #region Single shared modifier. One axis only has the shared modifier. + // Shared modifier value used in first (shorter) axis. Listed first. + [InlineData( + new string[] { "middle" }, + new string[] { "proximal", "middle", "distal" }, + "middle, proximal")] + // Shared modifier value used in first (shorter) axis. Listed second. + [InlineData( + new string[] { "middle" }, + new string[] { "proximal", "middle", "distal" }, + "proximal, middle")] + // Shared modifier value used in one axis, no modifier value used in other. + // TODO: Should this non-determinism be allowed!!! + [InlineData( + new string[] { "middle" }, + new string[] { "proximal", "middle", "distal" }, + "middle")] + #endregion + public void IsValidModifierSpec_DuplicateModifierValues_ShouldSucceed( + string[] axis1Values, string[] axis2Values, + string modSpec) + { + // Create a region with the same value on two modifier axes. + var region = new StringHierarchyRegion("hand", null!, + modifiers: new Dictionary> + { + { "x", new HashSet(axis1Values) }, + { "y", new HashSet(axis2Values) } + }); + + // Test valid modifier set + Assert.True(region.IsValidModifierSpec(modSpec)); + } + #endregion IsValidModifierSpec } From d99d722e7e9f64c33d9e18eef4204f1e2bb8ca82 Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Sun, 9 Feb 2025 21:42:20 -0500 Subject: [PATCH 24/34] Added comments --- .../StringHierarchy/StringHierarchyRegion.cs | 39 +++++++++++++++---- .../StringHierarchyRegionTests.cs | 9 +++++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs index e928778..d8523f1 100644 --- a/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs +++ b/src/EStimLibrary/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegion.cs @@ -207,8 +207,9 @@ public void AddSubregion(StringHierarchyRegion subregion, /// /// Try to get a given subregion of this region. /// - /// The specified region to search for, given as a - /// string sequence of appropriately delimited option-region names. + /// The string specification of the region to + /// search for, given as a string sequence of appropriately delimited + /// option-region names. /// An output parameter: the searched /// subregion if found, null if not. /// True if the subregion could be found, False if not. @@ -343,6 +344,10 @@ public bool IsValidModifierSpec(string modifierSpec) return true; } + /// + /// Update the frequency dictionary of modifier values across all axes + /// based on current Modifiers. + /// private void _UpdateFrequencyDict() { // Clear the frequency dictionary @@ -393,13 +398,32 @@ public StringHierarchyRegion DeepCopy(bool retainParentReference = true) return newRegion; } + /// + /// Create a string representation of this region and all subregions. + /// Override default behavior to provide a more detailed string output. + /// + /// A string representation of this region. public override string ToString() { - return s_BuildSpecOptionsString("", 0, this); + return s_BuildSpecOptionsString(this); } - private static string s_BuildSpecOptionsString(string parentRegionSpec, - int indentLevel, StringHierarchyRegion region) + /// + /// Create a string representation of the given region, recursing to contain + /// all subregions. + /// + /// The region to build a string representation of. + /// + /// The string specification of the parent + /// region (default: empty string, assuming given region is a root). + /// + /// Depth in the tree and thus number of indents + /// to included in the string output (default: 0, assuming given region is + /// a root). + /// A printable string representation of the given region. + /// + private static string s_BuildSpecOptionsString(StringHierarchyRegion region, + string parentRegionSpec = "", int indentLevel = 0) { // Output: [prev regionSpec], [options] baseName | [mod1Options], ... @@ -434,8 +458,9 @@ private static string s_BuildSpecOptionsString(string parentRegionSpec, List subregionStrings = new() { fullSpec }; foreach (var (_, subregion) in region.Subregions) { - subregionStrings.Add(s_BuildSpecOptionsString(regionSpec, - indentLevel + 1, subregion)); + subregionStrings.Add(s_BuildSpecOptionsString(subregion, + parentRegionSpec: regionSpec, + indentLevel: indentLevel + 1)); } // Return the single string. diff --git a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs index 101d1e9..0501915 100644 --- a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs +++ b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs @@ -11,11 +11,20 @@ public class StringHierarchyRegionTests { private readonly ITestOutputHelper _output; + /// + /// Construct an empty root region. Test helper. + /// + /// An empty root region. private static StringHierarchyRegion ConstructEmptyRoot() { return new StringHierarchyRegion("rootEmpty"); } + /// + /// Construct a two-level root region. Test helper. + /// + /// The root region of a two-level tree constructed manually + /// without method use. private static StringHierarchyRegion ConstructTwoLevelRoot() { StringHierarchyRegion rootTwoLevel; From d898992c2ba7eb999183bd5f18a3251fb6745063 Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Sun, 9 Feb 2025 23:46:23 -0500 Subject: [PATCH 25/34] Added tests; commenting --- .../StringHierarchyRegionTests.cs | 122 ++++++++++++++---- 1 file changed, 98 insertions(+), 24 deletions(-) diff --git a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs index 0501915..a461f33 100644 --- a/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs +++ b/tests/EStimLibrary.UnitTests/Extensions/SpatialModel/StringHierarchy/StringHierarchyRegionTests.cs @@ -35,7 +35,7 @@ private static StringHierarchyRegion ConstructTwoLevelRoot() // root: arm // / \ - // midA: hand midB: random + // midA: hand midB: middleTwoLevelB // / \ // leafAa: finger leafAb: handbody // Leaf regions @@ -48,16 +48,14 @@ private static StringHierarchyRegion ConstructTwoLevelRoot() { "z", new HashSet { "palmar", "dorsal" } } } ); leafTwoLevelAb = new StringHierarchyRegion("handbody", - parentOptions: new(), - options: new HashSet { "left", "right" }, + parentOptions: new HashSet { "left", "right" }, modifiers: new Dictionary> { { "x", new HashSet { "lateral", "medial" } }, { "y", new HashSet { "proximal", "middle", "distal" } }, { "z", new HashSet { "palmar", "dorsal" } } } ); - // Middle regions + // Middle regions: one data-full, one minimal middleTwoLevelA = new StringHierarchyRegion("hand", - parentOptions: new HashSet { "upper", "lower" }, options: new HashSet { "left", "right" }, modifiers: new Dictionary> { { "x", new HashSet { "lateral", "medial" } }, @@ -68,11 +66,9 @@ private static StringHierarchyRegion ConstructTwoLevelRoot() { "handbody", leafTwoLevelAb } } ); middleTwoLevelB = new StringHierarchyRegion("middleTwoLevelB", - parentOptions: new HashSet { "upper", "lower" }, options: new HashSet { "ulnar", "radial" }); - // Root region + // Root region: minimal data; just subregions rootTwoLevel = new StringHierarchyRegion("rootTwoLevel", - options: new(), subregions: new Dictionary { { "hand", middleTwoLevelA }, { "middleTwoLevelB", middleTwoLevelB } } @@ -277,7 +273,7 @@ public void ToString_ShouldOutputStringRep() new Dictionary()); // Create non-empty options, modifiers, and subregions parameters // to cover all components of string representation - var regionOptions = new HashSet() { "right", "left" }; + var regionOptions = new HashSet() { "right", "left", "center" }; var regionModifiers = new Dictionary> { { "modSet1", new HashSet() { "mod1", "mod2" } }, @@ -302,13 +298,72 @@ public void ToString_ShouldOutputStringRep() // Check string outputs for correctness Assert.Equal("base", nullRegion.ToString()); Assert.Equal("root", emptyRegion.ToString()); - Assert.Equal("[right,left] base | [mod1,mod2], [mod3,mod4]\n" + - " [right,left] base, [opt1,opt2] child1\n" + - " [right,left] base, [opt1,opt2] child1, grandchild\n" + - " [right,left] base, child2", stringHierarchyRegion.ToString()); + Assert.Equal("[right,left,center] base | [mod1,mod2], [mod3,mod4]\n" + + " [right,left,center] base, [opt1,opt2] child1\n" + + " [right,left,center] base, [opt1,opt2] child1, grandchild\n" + + " [right,left,center] base, child2", stringHierarchyRegion.ToString()); } #region TryGetSubregion + /// + /// Test the TryGetSubregion method with invalid region specifications. + /// + /// The root region to search within. + /// The invalid region spec. + [Theory] + [MemberData(nameof(TryGetSubregion_InvalidOR_Fail_TestData))] + public void TryGetSubregion_InvalidOptionRegionName_ShouldFail( + StringHierarchyRegion rootRegion, string regionSpec) + { + // Call the method. + bool res = rootRegion.TryGetSubregion(regionSpec, out var foundRegion); + + // Check failed: return is false, output is null. + Assert.False(res); + Assert.Null(foundRegion); + } + + /// + /// Test data for TryGetSubregion method that tries to get a subregion via + /// and invalid option-region combo in the region specification and should + /// return failure. + /// + /// + public static IEnumerable TryGetSubregion_InvalidOR_Fail_TestData() + { + return new List + { + // { rootRegion, searchRegionSpec } + new object[] { ConstructEmptyRoot(), ""}, + new object[] { ConstructEmptyRoot(), "left foot"}, + new object[] { ConstructEmptyRoot(), "anythingThatsNotRootEmpty"}, + // Correct would be from: + // rootTwoLevel + // [left right] hand + // [thumb index middle ring pinky] finger + // handbody + // [ulnar radial] middleTwoLevelB + new object[] { ConstructTwoLevelRoot(), + // Fail on empty region spec + "" }, + new object[] { ConstructTwoLevelRoot(), + // Fail on root level: would need "rootTwoLevel" first! + "left hand" }, + new object[] { ConstructTwoLevelRoot(), + // Fail on second level: incorrect region name + "rootTwoLevel, left hands" }, + new object[] { ConstructTwoLevelRoot(), + // Fail on second level: incorrect option name + "rootTwoLevel, my hand" }, + new object[] { ConstructTwoLevelRoot(), + // Fail on second level: missing option + "rootTwoLevel, hand" }, + new object[] { ConstructTwoLevelRoot(), + // Fail on thirs level: extra option + "rootTwoLevel, left hand, right handbody" } + }; + } + /// /// Test the TryGetSubregion method with various malformed or otherwise /// incorrect regions. @@ -336,7 +391,6 @@ public void TryGetSubregion_ShouldOutputNull() var stringHierarchyRegion = new StringHierarchyRegion("base", parent, parent.Options, regionOptions, regionModifiers, regionSubregions); - // TODO: make comment on PR abt using InlineData[] tags in future for cleaner code // Test with empty string representation StringHierarchyRegion? foundSubregion; var output = stringHierarchyRegion.TryGetSubregion("", @@ -545,25 +599,34 @@ public void AddSubregion_ShouldReplaceSubregion() /// /// Test the DeepCopy method with various root region structures. /// - /// + /// The root region of the tree to deep copy. + /// Whether to retain the parent + /// reference(s) in the deep copy or not. [Theory] [MemberData(nameof(DeepCopy_TestData))] - public void DeepCopy_ShouldSucceed(StringHierarchyRegion root) + public void DeepCopy_ShouldSucceed(StringHierarchyRegion root, + bool retainParentReference) { // Create the deep copy var deepCopy = root.DeepCopy(); // Recursive check. - DeepCopy_ShouldSucceed_Helper(root, deepCopy); + DeepCopy_ShouldSucceed_Helper(root, deepCopy, retainParentReference, + isRoot: true); } /// /// Recursive helper method for DeepCopy_ShouldSucceed. /// - /// - /// + /// An original region of the tree that was deep + /// copied. + /// The supposed deep copy of the original region. + /// + /// Whether to the parent reference(s) + /// were supposed to be retained or not in the deep copy. private void DeepCopy_ShouldSucceed_Helper(StringHierarchyRegion original, - StringHierarchyRegion deepCopy) + StringHierarchyRegion deepCopy, bool retainParentReference, + bool isRoot = false) { // Check root object is different address. Assert.NotSame(original, deepCopy); @@ -571,11 +634,18 @@ private void DeepCopy_ShouldSucceed_Helper(StringHierarchyRegion original, // Check basename is same value. Assert.Equal(original.BaseName, deepCopy.BaseName); - // Check parent region is different address. + // Check parent region is correct. if (original.ParentRegion is null) { Assert.Null(deepCopy.ParentRegion); } + // Check parent region is same address if rot and retention specified. + else if (isRoot && retainParentReference) + { + Assert.Same(original.ParentRegion, deepCopy.ParentRegion); + } + // Check parent region is different address if non-root or no retention + // specified. else { Assert.NotSame(original.ParentRegion, deepCopy.ParentRegion); @@ -623,7 +693,8 @@ private void DeepCopy_ShouldSucceed_Helper(StringHierarchyRegion original, Assert.NotSame(originalSubregion, copiedSubregion); // Recurse into subregion. - DeepCopy_ShouldSucceed_Helper(originalSubregion, copiedSubregion); + DeepCopy_ShouldSucceed_Helper(originalSubregion, copiedSubregion, + retainParentReference); } } @@ -635,8 +706,11 @@ public static IEnumerable DeepCopy_TestData() { return new List { - new object[] { ConstructEmptyRoot() }, - new object[] { ConstructTwoLevelRoot() } + // { rootRegion, retainParentReference } + new object[] { ConstructEmptyRoot(), true }, + new object[] { ConstructEmptyRoot(), false }, + new object[] { ConstructTwoLevelRoot(), true }, + new object[] { ConstructTwoLevelRoot(), false } }; } From 8dd788480a1a91f83ef37bd1bb22a1bece8da966 Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Wed, 12 Feb 2025 15:54:53 -0500 Subject: [PATCH 26/34] LeadMgr: call base constructor --- src/EStimLibrary/Core/HardwareInterfaces/LeadManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EStimLibrary/Core/HardwareInterfaces/LeadManager.cs b/src/EStimLibrary/Core/HardwareInterfaces/LeadManager.cs index e9c8158..04e5912 100644 --- a/src/EStimLibrary/Core/HardwareInterfaces/LeadManager.cs +++ b/src/EStimLibrary/Core/HardwareInterfaces/LeadManager.cs @@ -30,7 +30,7 @@ public class LeadManager : ResourceManager //TODO? public Dictionary ContactOutputMap { get; protected set; } - public LeadManager() + public LeadManager() : base() { // Initialize wired ID sets. this._WiredContacts = new(); From 3c00491283f2bc5877beb547a899ebeb084402fd Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Wed, 12 Feb 2025 15:55:21 -0500 Subject: [PATCH 27/34] ThreadConfig: rename param to match renaming in Stimulator --- .../Core/Stimulation/ThreadConfigDataPerStimulator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EStimLibrary/Core/Stimulation/ThreadConfigDataPerStimulator.cs b/src/EStimLibrary/Core/Stimulation/ThreadConfigDataPerStimulator.cs index d622ec2..d981b77 100644 --- a/src/EStimLibrary/Core/Stimulation/ThreadConfigDataPerStimulator.cs +++ b/src/EStimLibrary/Core/Stimulation/ThreadConfigDataPerStimulator.cs @@ -7,7 +7,7 @@ namespace EStimLibrary.Core.Stimulation; public record ThreadConfigDataPerStimulator(int GlobalStimId, IEnumerable IndependentLeads, - Dictionary> StimParamData, + Dictionary> StimParamSpecs, // TODO: edit once have sorted how will resolve StimParams and enum stuff. SortedSet ModulatableStimParams); From 7e336e1830a6f03c894d83f2343a121e0f14b50c Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Wed, 12 Feb 2025 15:55:34 -0500 Subject: [PATCH 28/34] Update README --- README.md | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7811b97..496d950 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,74 @@ # EStimLibrary A C# library to facilitate electrical stimulation research and development. -More details coming soon. +## Dependencies +* .NET 9.0 + * 10-minute [Tim Corey video](https://www.youtube.com/watch?v=sXEsvqCCTTc): how to upgrade or install fresh + * Windows: Use VisualStudio + * MacOS, Linux: download and install manually to access `dotnet` command-line interface (CLI) +* `nuget` packages: + * For base library: + * System.IO.Ports + * MathNet.Numerics + * Newtonsoft.Json + * For xUnit testing project: + * Microsoft.NET.Test.Sdk + * xunit + * xunit.runner.visualstudio + * xunit.runner.console + * coverlet.collector + * Versions are listed in the `.csproj` files and should be pulled automatically when building projects and solutions in this repo. -## Repo Structure -* `src/` - the library source code -* `tests/` - the test projects +## Extensions for VSCode Users +Optional but highly recommended: +* C# Dev Kit: provides much of the same C# support and within-solution navigation features as VisualStudio + +## Repo Contents +### Top-Level Structure +* `src/` - the library source code project +* `tests/` - the test project(s) * `models/` - examples of spatial model definition files -* `examples/` - example applications using the library's high-level API +* `examples/` - example applications (projects) using the library's high-level API * `docs/` - additional documentation files, including a dictionary and coding conventions +### Projects and Solutions +There are currently 3 projects in this repo: +1. `src/EStimLibrary/EStimLibrary.csproj`: the base library project itself + * Builds into a `.dll` +2. `tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.csproj`: the xUnit test project + * The unit tests for the library +3. `examples/EStimLibrary.ConsoleAppDemo/EStimLibrary.ConsoleAppDemo.csproj`: an example console app project that either runs through a hard-coded session config and execution, or walks through the config step-by-step, asking for user input at each step + * Highlights the `Utils` reflection capabilities, + * and exemplifies the major `HapticSession` config components. + +There are 3 corresponding solutions in this repo: +1. `EStimLibrary.sln`: builds only the base library project +2. `tests/EStimLibrary.UnitTests/EStimLibrary.UnitTests.sln`: builds the base library and the xUnit test projects +3. `examples/EStimLibrary.ConsoleAppDemo/EStimLibrary.ConsoleAppDemo.sln`: builds the base library and the example console app projects + +## Library Structure +The main library, in `src/EStimLibrary/`, is broken down into two top-level folders: +* `Core/`: the base declarations and implementatsions of interfaces, abstract classes, and core classes +* `Extensions/`: provided implementations of some core interfaces and abstract classes that are commonly used + +The `Core/` folder contains the following structure: +* `Data/`: primarily `IDataLimits` +* `Haptics/`: classes on the haptic event side of the pipeline, e.g., `HapticSession`, `HapticEvent`, and `HapticTransducer` +* `HardwareInterfaces/`: leads, cables, neural interfaces +* `SpatialModel/`: location, area, body model, and related content +* `Stimulation/`: stimulators and stimulation representations +* Miscellaneous infrastructure interfaces and classes are in the top level of the `Core/` folder, e.g., `Utils`, `ResourceManager`, `ReusableIdPool` + +The `Extensions/` folder mimics this structure to the extent that example implementations are provided. + +The `EStimLibrary.UnitTests` project also mimics the `Core/`, `Extensions/`, and sub-directory structures. + ## UML Diagrams -Last updated: March 2024 -* [Class Diagram](https://lucid.app/lucidchart/298700bf-4e2e-4c7d-bb06-76365f1efb98/edit?viewport_loc=-7329%2C-1594%2C17982%2C8602%2C0_0&invitationId=inv_11b76774-377e-4967-87a5-c173614a4ef3) -* [Sequence and Use Case Diagrams](https://lucid.app/lucidchart/e774e654-27e9-4168-aee4-39f6f8133738/edit?viewport_loc=-403%2C-342%2C1628%2C779%2C0_0&invitationId=inv_cb3b6cf3-cb84-4091-bd72-88547d50cb3e) +Last updated: February 2025 +* [Class Diagram - High-Level](https://lucid.app/lucidchart/1366a885-086c-45d0-8763-63448fe11a86/edit?viewport_loc=-3539%2C-4330%2C16143%2C7727%2C0_0&invitationId=inv_10ca78f7-8fcc-4d34-8466-aa8da86d8716) +* [`StringHierarchy` Class Diagram](https://lucid.app/lucidchart/8f0c6f70-c343-444e-9e6f-5c51557384ec/edit?viewport_loc=-2888%2C-1102%2C7382%2C3534%2C0_0&invitationId=inv_7e104f4d-b695-4ff7-84b2-87b3bff9a9fb) +* [Sequence Diagrams](https://lucid.app/lucidchart/312bfb4e-807b-4f90-9e56-7cdc64a0172d/edit?viewport_loc=-858%2C313%2C2104%2C917%2C0_0&invitationId=inv_94e9dfe8-1656-45c9-abc7-d433c77d7a95) +* [Use Case Diagrams](https://lucid.app/lucidchart/85097f76-39d7-49a4-b1a6-5fa749e0ad9f/edit?viewport_loc=-3588%2C-303%2C10396%2C4532%2C0_0&invitationId=inv_c1be5bbe-3b3e-4b08-b021-a66debdfbed8) Beneficial in the future: * package diagram showing the interface exposed to the public @@ -23,3 +78,26 @@ Beneficial in the future: See [UML.md](./docs/UML.md) for a UML intro. + +## Usage +To use the library, clone a local the repo and checkout the `release-v2.0` branch for the most recent development: +``` +git@github.com:lhmcgann/EStimLibrary.git +cd EStimLibrary +git checkout release-v2.0 +``` + +### VisualStudio +1. Open the `.sln` file corresponding to the projects you want to open. +2. Press the "play" button in the top left to build (and run, if applicable) the loaded solution. +3. To run the test project, click the green "play" button in Test Explorer. + +### VSCode or your other preferred IDE +1. Open the `EStimLibrary/` folder in your workspace. +2. In Terminal, navigate to the folder with the desired project or solution. +3. Use `dotnet` CLI commands. See a cheatsheet [here](https://www.lostindetails.com/articles/dotnet-cheatsheet). + 1. `dotnet build` to build any project + 2. `dotnet run` to run the example console app + 3. `dotnet test` to run the xUnit test suite + + From 846c9332d871c30c1586916f7a9788d039626e3a Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Wed, 12 Feb 2025 15:59:06 -0500 Subject: [PATCH 29/34] Update README: clone with submodules --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 496d950..83473a4 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Optional but highly recommended: * `src/` - the library source code project * `tests/` - the test project(s) * `models/` - examples of spatial model definition files + * contains [`EStimLibrary.SpatialModels`](https://github.com/lhmcgann/EStimLibrary.SpatialModels/) as a submodule * `examples/` - example applications (projects) using the library's high-level API * `docs/` - additional documentation files, including a dictionary and coding conventions @@ -82,7 +83,8 @@ See [UML.md](./docs/UML.md) for a UML intro. ## Usage To use the library, clone a local the repo and checkout the `release-v2.0` branch for the most recent development: ``` -git@github.com:lhmcgann/EStimLibrary.git +# To clone submodules as well: call git clone with the --recurse-submodules option if you have Git 2.13 or later, else --recursive +git clone git@github.com:lhmcgann/EStimLibrary.git cd EStimLibrary git checkout release-v2.0 ``` From e2c1084c7cff01fd13f7a4efae474ca9ac0e41ef Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Wed, 12 Feb 2025 16:03:00 -0500 Subject: [PATCH 30/34] Update REEADME: ToC --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 83473a4..52562de 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,17 @@ # EStimLibrary A C# library to facilitate electrical stimulation research and development. +[Dependencies](#dependencies) + [Extensions for VSCode Users](#extensions-for-vscode-users) +[Repo Contents](#repo-contents) + [Top-Level Structures](#top-level-structure) + [Projects and Solutions](#projects-and-solutions) +[Library Structure](#library-structure) +[UML Diagrams](#uml-diagrams) +[Usage](#usage) + [VisualStudio](#visualstudio) + [VSCode or your other preferred IDE](#vscode-or-your-other-preferred-ide) + ## Dependencies * .NET 9.0 * 10-minute [Tim Corey video](https://www.youtube.com/watch?v=sXEsvqCCTTc): how to upgrade or install fresh @@ -19,7 +30,7 @@ A C# library to facilitate electrical stimulation research and development. * coverlet.collector * Versions are listed in the `.csproj` files and should be pulled automatically when building projects and solutions in this repo. -## Extensions for VSCode Users +### Extensions for VSCode Users Optional but highly recommended: * C# Dev Kit: provides much of the same C# support and within-solution navigation features as VisualStudio @@ -81,7 +92,7 @@ See [UML.md](./docs/UML.md) for a UML intro. ## Usage -To use the library, clone a local the repo and checkout the `release-v2.0` branch for the most recent development: +To use the library, clone the repo and checkout the `release-v2.0` branch for the most recent development: ``` # To clone submodules as well: call git clone with the --recurse-submodules option if you have Git 2.13 or later, else --recursive git clone git@github.com:lhmcgann/EStimLibrary.git From 3dcf68b32adf85e8a5511e34b30c7a08746b2be3 Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Wed, 12 Feb 2025 16:04:06 -0500 Subject: [PATCH 31/34] UpdateREADME: ToC formatting --- README.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 52562de..a07dca8 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,17 @@ # EStimLibrary A C# library to facilitate electrical stimulation research and development. -[Dependencies](#dependencies) - [Extensions for VSCode Users](#extensions-for-vscode-users) -[Repo Contents](#repo-contents) - [Top-Level Structures](#top-level-structure) - [Projects and Solutions](#projects-and-solutions) -[Library Structure](#library-structure) -[UML Diagrams](#uml-diagrams) -[Usage](#usage) - [VisualStudio](#visualstudio) - [VSCode or your other preferred IDE](#vscode-or-your-other-preferred-ide) +## Table of Contents +* [Dependencies](#dependencies) + * [Extensions for VSCode Users](#extensions-for-vscode-users) +* [Repo Contents](#repo-contents) + * [Top-Level Structures](#top-level-structure) + * [Projects and Solutions](#projects-and-solutions) +* [Library Structure](#library-structure) +* [UML Diagrams](#uml-diagrams) +* [Usage](#usage) + * [VisualStudio](#visualstudio) + * [VSCode or your other preferred IDE](#vscode-or-your-other-preferred-ide) ## Dependencies * .NET 9.0 From 91a8c5341cadcb73075c2efbf8ba9003c349740e Mon Sep 17 00:00:00 2001 From: Laura McGann Date: Wed, 12 Feb 2025 17:24:39 -0500 Subject: [PATCH 32/34] Update README: Key Components diagram --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a07dca8..49bb23a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A C# library to facilitate electrical stimulation research and development. * [Top-Level Structures](#top-level-structure) * [Projects and Solutions](#projects-and-solutions) * [Library Structure](#library-structure) -* [UML Diagrams](#uml-diagrams) +* [Diagrams](#diagrams) * [Usage](#usage) * [VisualStudio](#visualstudio) * [VSCode or your other preferred IDE](#vscode-or-your-other-preferred-ide) @@ -76,8 +76,11 @@ The `Extensions/` folder mimics this structure to the extent that example implem The `EStimLibrary.UnitTests` project also mimics the `Core/`, `Extensions/`, and sub-directory structures. -## UML Diagrams +## Diagrams Last updated: February 2025 + +These diagrams are based on UML formatting but adhere to that to varying degrees. +* [Key Components](https://lucid.app/lucidchart/7b9b75ff-8dee-41d8-b650-082adc9bcb8f/edit?viewport_loc=-1212%2C-695%2C5508%2C2636%2C0_0&invitationId=inv_0a3eaaf0-7a4f-4fcd-a813-b786f47e548f) * [Class Diagram - High-Level](https://lucid.app/lucidchart/1366a885-086c-45d0-8763-63448fe11a86/edit?viewport_loc=-3539%2C-4330%2C16143%2C7727%2C0_0&invitationId=inv_10ca78f7-8fcc-4d34-8466-aa8da86d8716) * [`StringHierarchy` Class Diagram](https://lucid.app/lucidchart/8f0c6f70-c343-444e-9e6f-5c51557384ec/edit?viewport_loc=-2888%2C-1102%2C7382%2C3534%2C0_0&invitationId=inv_7e104f4d-b695-4ff7-84b2-87b3bff9a9fb) * [Sequence Diagrams](https://lucid.app/lucidchart/312bfb4e-807b-4f90-9e56-7cdc64a0172d/edit?viewport_loc=-858%2C313%2C2104%2C917%2C0_0&invitationId=inv_94e9dfe8-1656-45c9-abc7-d433c77d7a95) From 243ba2cd7eb2ee5d47a4dc408946eeaa744d89de Mon Sep 17 00:00:00 2001 From: cld99 Date: Tue, 29 Apr 2025 15:25:59 -0400 Subject: [PATCH 33/34] Change HapticEvent to use dictionary for params --- examples/EStimLibrary.ConsoleAppDemo/Program.cs | 6 ++++-- src/EStimLibrary/Core/Haptics/HapticEvent.cs | 14 ++++---------- .../Extensions/Haptics/ClassicDirectTransducer.cs | 3 +-- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/examples/EStimLibrary.ConsoleAppDemo/Program.cs b/examples/EStimLibrary.ConsoleAppDemo/Program.cs index 5a44cf7..296aa60 100644 --- a/examples/EStimLibrary.ConsoleAppDemo/Program.cs +++ b/examples/EStimLibrary.ConsoleAppDemo/Program.cs @@ -660,12 +660,14 @@ "left hand, index finger, distal phalanx | palmar"); foreach (var pVal in pressureValues) { - var pNormVec = Vector.Build.Dense(new double[] { pVal }); + //var pNormVec = Vector.Build.Dense(new double[] { pVal }); + var hapticParams = new Dictionary(); + hapticParams.Add(HapticParam.P, pVal); var event1 = new HapticEvent(DateTime.Now, null, new() { { "left hand", new List() { eventArea1 } }//, eventArea2 } } }, - pNormVec); + hapticParams); session.AddEvent(event1); } session.Stop(); diff --git a/src/EStimLibrary/Core/Haptics/HapticEvent.cs b/src/EStimLibrary/Core/Haptics/HapticEvent.cs index 5159a2a..dbe500f 100644 --- a/src/EStimLibrary/Core/Haptics/HapticEvent.cs +++ b/src/EStimLibrary/Core/Haptics/HapticEvent.cs @@ -24,19 +24,13 @@ namespace EStimLibrary.Core.Haptics; /// conforming to the body model. Parameter may be null or contain otherwise /// invalid data if LocalizeByArea is false. /// -/// +/// The dictionary of parameters for the haptic +/// event. Key is the haptic param label (see HapticParamEnum). /// True if this event should be localized on the /// body model using this event's Area, False if this event should be localized /// on the body model using this event's Location. public record HapticEvent(DateTime Timestamp, Dictionary> Locations, Dictionary> Areas, - Vector HapticParamData, - bool LocalizeByArea = true); - -// TODO: add labels/headers to stim params. Could either change this record to -// by default include the below (but need to figure out how to do with enums and -// allowing extension), or add a record inheriting from this one that is a -// ParameterizedHapticEvent. If did add this, would need to add a config step to -// select haptic params. -// public SortedSet HapticParams { get; init; } \ No newline at end of file + Dictionary HapticParamData, + bool LocalizeByArea = true); \ No newline at end of file diff --git a/src/EStimLibrary/Extensions/Haptics/ClassicDirectTransducer.cs b/src/EStimLibrary/Extensions/Haptics/ClassicDirectTransducer.cs index b9601af..da1dde6 100644 --- a/src/EStimLibrary/Extensions/Haptics/ClassicDirectTransducer.cs +++ b/src/EStimLibrary/Extensions/Haptics/ClassicDirectTransducer.cs @@ -56,8 +56,7 @@ protected override IEnumerable _TransduceHapticEvent( // i.e., P not included at all (otherwise would be first), use the first // value. // Should be between 0 and 1 - // TODO: make sure array len >0 before indexing into - double modValue = hapticEvent.HapticParamData[0]; + double modValue = hapticEvent.HapticParamData[HapticParam.P]; // StimThread properties: // PerStimulatorConfigs (MAIN constructor input; next 2 derive from it) From d5716b3241df8c1d99b947e4523ffadba98e5e1c Mon Sep 17 00:00:00 2001 From: cld99 Date: Tue, 29 Apr 2025 17:11:34 -0400 Subject: [PATCH 34/34] Add test class for HapticEvent --- .../Core/Haptics/HapticEventTests.cs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 tests/EStimLibrary.UnitTests/Core/Haptics/HapticEventTests.cs diff --git a/tests/EStimLibrary.UnitTests/Core/Haptics/HapticEventTests.cs b/tests/EStimLibrary.UnitTests/Core/Haptics/HapticEventTests.cs new file mode 100644 index 0000000..973857a --- /dev/null +++ b/tests/EStimLibrary.UnitTests/Core/Haptics/HapticEventTests.cs @@ -0,0 +1,63 @@ +using EStimLibrary.Core.Haptics; +using EStimLibrary.Core.SpatialModel; + + +namespace EStimLibrary.UnitTests.Core.Haptics; + + +public class HapticEventTests +{ + private readonly ITestOutputHelper _output; + + // Test class constructor creates an output helper so can write console + // output. + public HapticEventTests(ITestOutputHelper testOutputHelper) + { + this._output = testOutputHelper; + } + + // Test method naming convention: LibClassMethodName_ScenarioShouldExpectn + + /// + /// Test record initialization. + /// + [Fact] + public void Constuctor_ShouldInit() + { + var timestamp = DateTime.Now; + var locations = new Dictionary>(); + var areas = new Dictionary>(); + var hapticParams = new Dictionary(); + hapticParams.Add(HapticParam.P, 2.5); + hapticParams.Add(HapticParam.dP, 1.0); + var hapticEvent = new HapticEvent(timestamp, locations, areas, hapticParams); + Assert.NotNull(hapticEvent); + Assert.Equal(timestamp, hapticEvent.Timestamp); + Assert.Equal(locations, hapticEvent.Locations); + Assert.Equal(areas, hapticEvent.Areas); + Assert.Equal(hapticParams, hapticEvent.HapticParamData); + Assert.True(hapticEvent.LocalizeByArea); + } + + /// + /// Test record copying. + /// + [Fact] + public void With_ShouldCopy() + { + var timestamp = DateTime.Now; + var locations = new Dictionary>(); + var areas = new Dictionary>(); + var hapticParams = new Dictionary(); + hapticParams.Add(HapticParam.P, 2.5); + hapticParams.Add(HapticParam.dP, 1.0); + var hapticEvent1 = new HapticEvent(timestamp, locations, areas, hapticParams); + var hapticEvent2 = hapticEvent1 with { }; + var hapticEvent3 = hapticEvent2 with { Timestamp = DateTime.Now }; + var hapticEvent4 = hapticEvent3 with { Areas = new Dictionary>() }; + + Assert.Equal(hapticEvent1, hapticEvent2); + Assert.NotEqual(hapticEvent2, hapticEvent3); + Assert.NotEqual(hapticEvent3, hapticEvent4); + } +} \ No newline at end of file