From 7b6d74b026c556afd91796eb959906d472f9cd3a Mon Sep 17 00:00:00 2001 From: Wade Vollink Date: Fri, 24 Oct 2025 09:57:38 -0600 Subject: [PATCH 01/17] add config for windowedrhino and load appropriately in rhinocoreloader --- .../Rhino.Testing.Configs.xml | 1 + .../SetupFixture.cs | 2 +- src/Rhino.Testing/Configs.cs | 4 ++++ src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs | 16 +++++++++++++++- 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Configs.xml b/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Configs.xml index 53cd297..7abbd24 100644 --- a/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Configs.xml +++ b/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Configs.xml @@ -7,4 +7,5 @@ true false false + true diff --git a/src/Rhino.Testing.Tests.SetupFixture/SetupFixture.cs b/src/Rhino.Testing.Tests.SetupFixture/SetupFixture.cs index 36e90b0..08522ff 100644 --- a/src/Rhino.Testing.Tests.SetupFixture/SetupFixture.cs +++ b/src/Rhino.Testing.Tests.SetupFixture/SetupFixture.cs @@ -1,6 +1,6 @@ namespace Rhino.Testing.Tests { - [SetUpFixture] + [SetUpFixture, Apartment(ApartmentState.STA)] public sealed class SetupFixture : Rhino.Testing.Fixtures.RhinoSetupFixture { public override void OneTimeSetup() => base.OneTimeSetup(); diff --git a/src/Rhino.Testing/Configs.cs b/src/Rhino.Testing/Configs.cs index 205d399..806ab9f 100644 --- a/src/Rhino.Testing/Configs.cs +++ b/src/Rhino.Testing/Configs.cs @@ -50,6 +50,10 @@ public static T Deserialize(XmlSerializer serializer, string settingsFile) [XmlElement] public bool LoadGrasshopper { get; set; } = false; + [XmlElement] + public bool WindowedRhino { get; set; } = false; + + [XmlArray("LoadPlugins")] [XmlArrayItem("Plugin")] #pragma warning disable CA1002 // Do not expose generic lists diff --git a/src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs b/src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs index b98f4bd..2ddea81 100644 --- a/src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs +++ b/src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Threading; using NUnit.Framework; @@ -23,7 +25,19 @@ public static void LoadCore(bool createDoc, bool createView) lock (s_coreLock) { - s_core = new Rhino.Runtime.InProcess.RhinoCore(args); + if (!Configs.Current.WindowedRhino) + { + s_core = new Rhino.Runtime.InProcess.RhinoCore(args); + } + else + { + //Verify apartment state before initialization + var apartmentState = Thread.CurrentThread.GetApartmentState(); + TestContext.WriteLine($"Current thread apartment state: {apartmentState}"); + Assert.That(apartmentState, Is.EqualTo(ApartmentState.STA), "For windowed mode, thread must be static (STA). Add Apartment(ApartmentState.STA) to test SetupFixture"); + List windowedArgs = new List(args); + s_core = new Rhino.Runtime.InProcess.RhinoCore(args, Runtime.InProcess.WindowStyle.Hidden); + } if (createDoc) { From ca70b44495ea326280b243c7e4d5fca386ae208c Mon Sep 17 00:00:00 2001 From: Wade Vollink Date: Fri, 24 Oct 2025 12:25:18 -0600 Subject: [PATCH 02/17] Add WindowedSetup test to suite. vert setupfixture test to initial state. add event tests --- Rhino.Testing.sln | 10 +++++-- .../Rhino.Testing.Configs.xml | 1 - .../SetupFixture.cs | 2 +- .../EventTest.cs | 27 +++++++++++++++++++ .../Rhino.Testing.Configs.xml | 11 ++++++++ .../Rhino.Testing.Tests.WindowedSetup.csproj | 23 ++++++++++++++++ .../SetupFixture.cs | 10 +++++++ .../SimpleTest.cs | 17 ++++++++++++ .../Usings.cs | 1 + src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs | 3 ++- 10 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 src/Rhino.Testing.Tests.WindowedSetup/EventTest.cs create mode 100644 src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Configs.xml create mode 100644 src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Tests.WindowedSetup.csproj create mode 100644 src/Rhino.Testing.Tests.WindowedSetup/SetupFixture.cs create mode 100644 src/Rhino.Testing.Tests.WindowedSetup/SimpleTest.cs create mode 100644 src/Rhino.Testing.Tests.WindowedSetup/Usings.cs diff --git a/Rhino.Testing.sln b/Rhino.Testing.sln index 50f5bc4..1b222cd 100644 --- a/Rhino.Testing.sln +++ b/Rhino.Testing.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.8.34511.84 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11111.16 d18.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rhino.Testing", "src\Rhino.Testing\Rhino.Testing.csproj", "{EE10AB9B-51C3-4025-AA26-C4DD3517E04C}" EndProject @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rhino.Testing.Tests.SetupFi EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rhino.Testing.Tests.Utils", "src\Rhino.Testing.Tests.Utils\Rhino.Testing.Tests.Utils.csproj", "{A49EB5F1-7D83-4668-AF75-290BDCF350FA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rhino.Testing.Tests.WindowedSetup", "src\Rhino.Testing.Tests.WindowedSetup\Rhino.Testing.Tests.WindowedSetup.csproj", "{31DA1866-70AC-437B-8B97-169EDDFDE4B1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {A49EB5F1-7D83-4668-AF75-290BDCF350FA}.Debug|Any CPU.Build.0 = Debug|Any CPU {A49EB5F1-7D83-4668-AF75-290BDCF350FA}.Release|Any CPU.ActiveCfg = Release|Any CPU {A49EB5F1-7D83-4668-AF75-290BDCF350FA}.Release|Any CPU.Build.0 = Release|Any CPU + {31DA1866-70AC-437B-8B97-169EDDFDE4B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {31DA1866-70AC-437B-8B97-169EDDFDE4B1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {31DA1866-70AC-437B-8B97-169EDDFDE4B1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {31DA1866-70AC-437B-8B97-169EDDFDE4B1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Configs.xml b/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Configs.xml index 7abbd24..53cd297 100644 --- a/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Configs.xml +++ b/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Configs.xml @@ -7,5 +7,4 @@ true false false - true diff --git a/src/Rhino.Testing.Tests.SetupFixture/SetupFixture.cs b/src/Rhino.Testing.Tests.SetupFixture/SetupFixture.cs index 08522ff..36e90b0 100644 --- a/src/Rhino.Testing.Tests.SetupFixture/SetupFixture.cs +++ b/src/Rhino.Testing.Tests.SetupFixture/SetupFixture.cs @@ -1,6 +1,6 @@ namespace Rhino.Testing.Tests { - [SetUpFixture, Apartment(ApartmentState.STA)] + [SetUpFixture] public sealed class SetupFixture : Rhino.Testing.Fixtures.RhinoSetupFixture { public override void OneTimeSetup() => base.OneTimeSetup(); diff --git a/src/Rhino.Testing.Tests.WindowedSetup/EventTest.cs b/src/Rhino.Testing.Tests.WindowedSetup/EventTest.cs new file mode 100644 index 0000000..47bd2b1 --- /dev/null +++ b/src/Rhino.Testing.Tests.WindowedSetup/EventTest.cs @@ -0,0 +1,27 @@ +using System; +using Rhino.Testing.Fixtures; + +namespace Rhino.Testing.Tests.WindowedSetup +{ + internal class EventTest : RhinoTestFixture + { + [Test] + public void NewDocumentTest() + { + bool documentEventFired = false; + + void LocalScopeNewDocument(object? sender, DocumentEventArgs e) + { + documentEventFired = true; + } + + RhinoDoc.NewDocument += LocalScopeNewDocument; + using (var doc = RhinoDoc.Create(null)) + { + Assert.That(documentEventFired, Is.True); + Assert.That(doc, Is.Not.Null); + } + RhinoDoc.NewDocument -= LocalScopeNewDocument; + } + } +} diff --git a/src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Configs.xml b/src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Configs.xml new file mode 100644 index 0000000..8b9af75 --- /dev/null +++ b/src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Configs.xml @@ -0,0 +1,11 @@ + + + C:\Program Files\Rhino 8\System + false + false + false + false + false + false + true + diff --git a/src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Tests.WindowedSetup.csproj b/src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Tests.WindowedSetup.csproj new file mode 100644 index 0000000..cfb89df --- /dev/null +++ b/src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Tests.WindowedSetup.csproj @@ -0,0 +1,23 @@ + + + net48;net7.0-windows;net8.0-windows + false + enable + enable + + + + + + + + + + + + + + Always + + + \ No newline at end of file diff --git a/src/Rhino.Testing.Tests.WindowedSetup/SetupFixture.cs b/src/Rhino.Testing.Tests.WindowedSetup/SetupFixture.cs new file mode 100644 index 0000000..08522ff --- /dev/null +++ b/src/Rhino.Testing.Tests.WindowedSetup/SetupFixture.cs @@ -0,0 +1,10 @@ +namespace Rhino.Testing.Tests +{ + [SetUpFixture, Apartment(ApartmentState.STA)] + public sealed class SetupFixture : Rhino.Testing.Fixtures.RhinoSetupFixture + { + public override void OneTimeSetup() => base.OneTimeSetup(); + + public override void OneTimeTearDown() => base.OneTimeTearDown(); + } +} diff --git a/src/Rhino.Testing.Tests.WindowedSetup/SimpleTest.cs b/src/Rhino.Testing.Tests.WindowedSetup/SimpleTest.cs new file mode 100644 index 0000000..8420faf --- /dev/null +++ b/src/Rhino.Testing.Tests.WindowedSetup/SimpleTest.cs @@ -0,0 +1,17 @@ +using Rhino.Geometry; +using Rhino.Testing.Fixtures; + +namespace Rhino.Testing.Tests +{ + public class SimpleTest : RhinoTestFixture + { + [Test] + public void Test() + { + Point3d point = new(1, 2, 3); + Assert.That(point.X, Is.GreaterThan(0)); + Assert.That(point.Y, Is.GreaterThan(0)); + Assert.That(point.Z, Is.GreaterThan(0)); + } + } +} diff --git a/src/Rhino.Testing.Tests.WindowedSetup/Usings.cs b/src/Rhino.Testing.Tests.WindowedSetup/Usings.cs new file mode 100644 index 0000000..cefced4 --- /dev/null +++ b/src/Rhino.Testing.Tests.WindowedSetup/Usings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs b/src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs index 2ddea81..25b3cc5 100644 --- a/src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs +++ b/src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs @@ -36,7 +36,8 @@ public static void LoadCore(bool createDoc, bool createView) TestContext.WriteLine($"Current thread apartment state: {apartmentState}"); Assert.That(apartmentState, Is.EqualTo(ApartmentState.STA), "For windowed mode, thread must be static (STA). Add Apartment(ApartmentState.STA) to test SetupFixture"); List windowedArgs = new List(args); - s_core = new Rhino.Runtime.InProcess.RhinoCore(args, Runtime.InProcess.WindowStyle.Hidden); + windowedArgs.AddRange(new string[] { "/notemplate", "/nosplash"}); + s_core = new Rhino.Runtime.InProcess.RhinoCore(windowedArgs.ToArray(), Runtime.InProcess.WindowStyle.Hidden); } if (createDoc) From 805b81cd46b5c7b396085c6fe68762428b990b29 Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Fri, 24 Oct 2025 13:49:04 -0500 Subject: [PATCH 03/17] connect to ado feed --- nuget.config | 1 + src/Directory.Build.props | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nuget.config b/nuget.config index 6ce9759..ffc43e7 100644 --- a/nuget.config +++ b/nuget.config @@ -4,5 +4,6 @@ + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 7217518..080c157 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -15,7 +15,7 @@ - 8.0.28 + 8.0.29-ryan.1 8.0.6-beta 8.0.23304.9001 $(MSBuildThisFileDirectory)src\ @@ -56,4 +56,4 @@ - \ No newline at end of file + From 22e70be038b517438b8c08fdc900102ac214d0d3 Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Fri, 24 Oct 2025 14:01:22 -0500 Subject: [PATCH 04/17] don't use assert statements in the library --- src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs b/src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs index 25b3cc5..b1e60c7 100644 --- a/src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs +++ b/src/Rhino.Testing/_Loaders/RhinoCoreLoader.cs @@ -34,7 +34,10 @@ public static void LoadCore(bool createDoc, bool createView) //Verify apartment state before initialization var apartmentState = Thread.CurrentThread.GetApartmentState(); TestContext.WriteLine($"Current thread apartment state: {apartmentState}"); - Assert.That(apartmentState, Is.EqualTo(ApartmentState.STA), "For windowed mode, thread must be static (STA). Add Apartment(ApartmentState.STA) to test SetupFixture"); + if(apartmentState != ApartmentState.STA) + { + throw new InvalidOperationException("For windowed mode, thread must be static (STA). Add Apartment(ApartmentState.STA) to test SetupFixture"); + } List windowedArgs = new List(args); windowedArgs.AddRange(new string[] { "/notemplate", "/nosplash"}); s_core = new Rhino.Runtime.InProcess.RhinoCore(windowedArgs.ToArray(), Runtime.InProcess.WindowStyle.Hidden); From 67fe269bb9bc8054150128f2f1ac542010c34562 Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Fri, 24 Oct 2025 14:18:11 -0500 Subject: [PATCH 05/17] version update --- src/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 080c157..00890ae 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -15,7 +15,7 @@ - 8.0.29-ryan.1 + 8.0.29-ryan.2 8.0.6-beta 8.0.23304.9001 $(MSBuildThisFileDirectory)src\ From 9e9cb46088b33827eb187c78b131bc23c85f4537 Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Tue, 10 Mar 2026 10:07:17 -0500 Subject: [PATCH 06/17] update rhino library version, remove net7.0 --- Rhino.Testing.sln | 2 +- src/Directory.Build.props | 4 ++-- .../Rhino.Testing.Tests.SetupAttribute.csproj | 2 +- .../Rhino.Testing.Tests.SetupFixture.csproj | 2 +- .../Rhino.Testing.Tests.WindowedSetup.csproj | 2 +- src/Rhino.Testing.Tests/Rhino.Testing.Tests.csproj | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Rhino.Testing.sln b/Rhino.Testing.sln index 1b222cd..7cb3235 100644 --- a/Rhino.Testing.sln +++ b/Rhino.Testing.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 18 -VisualStudioVersion = 18.0.11111.16 d18.0 +VisualStudioVersion = 18.0.11111.16 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rhino.Testing", "src\Rhino.Testing\Rhino.Testing.csproj", "{EE10AB9B-51C3-4025-AA26-C4DD3517E04C}" EndProject diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 00890ae..11c5e4d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,6 +1,6 @@ - net48;net7.0;net7.0-windows;net8.0;net8.0-windows + net48;net8.0;net8.0-windows @@ -17,7 +17,7 @@ 8.0.29-ryan.2 8.0.6-beta - 8.0.23304.9001 + 8.27.26019.16021 $(MSBuildThisFileDirectory)src\ true 2025 diff --git a/src/Rhino.Testing.Tests.SetupAttribute/Rhino.Testing.Tests.SetupAttribute.csproj b/src/Rhino.Testing.Tests.SetupAttribute/Rhino.Testing.Tests.SetupAttribute.csproj index 1bd3eca..afcc957 100644 --- a/src/Rhino.Testing.Tests.SetupAttribute/Rhino.Testing.Tests.SetupAttribute.csproj +++ b/src/Rhino.Testing.Tests.SetupAttribute/Rhino.Testing.Tests.SetupAttribute.csproj @@ -1,6 +1,6 @@  - net48;net7.0-windows;net8.0-windows + net48;net8.0-windows false enable enable diff --git a/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Tests.SetupFixture.csproj b/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Tests.SetupFixture.csproj index 121bb3c..2d59f17 100644 --- a/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Tests.SetupFixture.csproj +++ b/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Tests.SetupFixture.csproj @@ -1,6 +1,6 @@  - net48;net7.0-windows;net8.0-windows + net48;net8.0-windows false enable enable diff --git a/src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Tests.WindowedSetup.csproj b/src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Tests.WindowedSetup.csproj index cfb89df..ed34567 100644 --- a/src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Tests.WindowedSetup.csproj +++ b/src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Tests.WindowedSetup.csproj @@ -1,6 +1,6 @@ - net48;net7.0-windows;net8.0-windows + net48;net8.0-windows false enable enable diff --git a/src/Rhino.Testing.Tests/Rhino.Testing.Tests.csproj b/src/Rhino.Testing.Tests/Rhino.Testing.Tests.csproj index 1bd3eca..afcc957 100644 --- a/src/Rhino.Testing.Tests/Rhino.Testing.Tests.csproj +++ b/src/Rhino.Testing.Tests/Rhino.Testing.Tests.csproj @@ -1,6 +1,6 @@  - net48;net7.0-windows;net8.0-windows + net48;net8.0-windows false enable enable From 50a68b99209951d6d5723e93daeed6073dae38ff Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Tue, 10 Mar 2026 10:16:28 -0500 Subject: [PATCH 07/17] update version for new pkg --- src/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 11c5e4d..6bf6436 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -15,7 +15,7 @@ - 8.0.29-ryan.2 + 8.0.29-ryan.3 8.0.6-beta 8.27.26019.16021 $(MSBuildThisFileDirectory)src\ From 9e8bfc400201828dc51c724ca34ec413ce433669 Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Thu, 19 Mar 2026 11:32:47 -0500 Subject: [PATCH 08/17] Setup Package Directories and environment paths --- src/Directory.Build.props | 3 +- src/Rhino.Testing/Configs.cs | 65 ++++++++++++++++++++++ src/Rhino.Testing/RhinoCore.cs | 2 +- src/Rhino.Testing/_Loaders/PluginLoader.cs | 25 +++++++-- 4 files changed, 89 insertions(+), 6 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 6bf6436..5e4e710 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -15,7 +15,7 @@ - 8.0.29-ryan.3 + 8.0.29-ryan.4 8.0.6-beta 8.27.26019.16021 $(MSBuildThisFileDirectory)src\ @@ -55,5 +55,6 @@ compile; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Rhino.Testing/Configs.cs b/src/Rhino.Testing/Configs.cs index 806ab9f..e853ec3 100644 --- a/src/Rhino.Testing/Configs.cs +++ b/src/Rhino.Testing/Configs.cs @@ -60,6 +60,14 @@ public static T Deserialize(XmlSerializer serializer, string settingsFile) #pragma warning disable CA2227 // Collection properties should be read only public List LoadPlugins { get; set; } = new List(); #pragma warning restore CA2227 // Collection properties should be read only +#pragma warning restore CA1002 // Do not expose generic lists + + [XmlArray("PackageDirectories")] + [XmlArrayItem("Directory")] +#pragma warning disable CA1002 // Do not expose generic lists +#pragma warning disable CA2227 // Collection properties should be read only + public List PackageDirectories { get; set; } = new List(); +#pragma warning restore CA2227 // Collection properties should be read only #pragma warning restore CA1002 // Do not expose generic lists [XmlIgnore] @@ -74,6 +82,36 @@ public Configs() SettingsDir = Path.GetDirectoryName(SettingsFile); } + /// + /// Support for using environment variables in the rhino system directory, package directories, and plugin paths, in the form of ${env:VAR_NAME} + /// + /// + /// + public static string ReplaceEnvVars(string path) + { + if (string.IsNullOrEmpty(path)) + return path; + int startIndex = 0; + while (true) + { + int envStart = path.IndexOf("${env:", startIndex, StringComparison.OrdinalIgnoreCase); + if (envStart == -1) + break; + int envEnd = path.IndexOf('}', envStart); + if (envEnd == -1) + break; + string envVar = path.Substring(envStart + 6, envEnd - envStart - 6); + string envVal = Environment.GetEnvironmentVariable(envVar) ?? string.Empty; +#if NET8_0_OR_GREATER + path = path[..envStart] + envVal + path[(envEnd + 1)..]; +#else + path = path.Substring(0, envStart) + envVal + path.Substring(envEnd + 1); +#endif + startIndex = envStart + envVal.Length; + } + return path; + } + static Configs() { string cfgFile = GetConfigsFile(); @@ -81,6 +119,25 @@ static Configs() if (File.Exists(cfgFile)) { Current = Deserialize(new XmlSerializer(typeof(Configs)), cfgFile); + Current.RhinoSystemDir = ReplaceEnvVars(Current.RhinoSystemDir); + + // separate our the Package Directories by the path separator and trim whitespace + List dirs = new List(); + foreach (var dir in Current.PackageDirectories) + { + var path = ReplaceEnvVars(dir.Path.Trim()); + string[] splitDirs = path.Split(new char[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries); + foreach (var splitDir in splitDirs) + { + dirs.Add(new DirectoryConfigs() { Path = splitDir.Trim() }); + } + } + Current.PackageDirectories = dirs; + + foreach (var plugin in Current.LoadPlugins) + { + plugin.Location = ReplaceEnvVars(plugin.Location); + } if (Path.IsPathRooted(Current.RhinoSystemDir)) { @@ -109,4 +166,12 @@ public sealed class PluginConfigs [XmlAttribute] public string Location { get; set; } = string.Empty; } + + [Serializable] + [XmlRoot("Directory")] + public sealed class DirectoryConfigs + { + [XmlAttribute] + public string Path { get; set; } = string.Empty; + } } diff --git a/src/Rhino.Testing/RhinoCore.cs b/src/Rhino.Testing/RhinoCore.cs index e64ac6c..41cc05c 100644 --- a/src/Rhino.Testing/RhinoCore.cs +++ b/src/Rhino.Testing/RhinoCore.cs @@ -63,7 +63,7 @@ public static void Initialize() if (Configs.Current.LoadPlugins.Count != 0) { - PluginLoader.LoadPlugins(Configs.Current.LoadPlugins.Select(p => p.Location)); + PluginLoader.LoadPlugins(Configs.Current.LoadPlugins.Select(p => p.Location), Configs.Current.PackageDirectories.Select(d => d.Path)); } if (Configs.Current.LoadGrasshopper) diff --git a/src/Rhino.Testing/_Loaders/PluginLoader.cs b/src/Rhino.Testing/_Loaders/PluginLoader.cs index 9095630..592d0d4 100644 --- a/src/Rhino.Testing/_Loaders/PluginLoader.cs +++ b/src/Rhino.Testing/_Loaders/PluginLoader.cs @@ -11,8 +11,10 @@ namespace Rhino.Testing { static class PluginLoader { - public static string GetRHPPath(string rhpPath) + public static string GetRHPPath(string rhpPath, IEnumerable packageDirs = null) { + if(File.Exists(rhpPath)) return rhpPath; + // first look in the rhino system directory string rhp = Path.Combine(Configs.Current.RhinoSystemDir, rhpPath); if (File.Exists(rhp)) { @@ -38,6 +40,22 @@ public static string GetRHPPath(string rhpPath) } } + if (packageDirs != null) + { + foreach (var packageDir in packageDirs) + { + rhp = Path.Combine(packageDir, rhpPath); + if (File.Exists(rhp)) + { + return rhp; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && Directory.Exists(rhp)) + { + return rhp; + } + } + } + throw new FileNotFoundException(rhpPath); } @@ -145,12 +163,11 @@ public static void LoadGrasshopper() } } - public static void LoadPlugins(IEnumerable rhpPaths) + public static void LoadPlugins(IEnumerable rhpPaths, IEnumerable packageDirs) { foreach (var rhpPath in rhpPaths) { - string fullPath = GetRHPPath(rhpPath); - + string fullPath = GetRHPPath(rhpPath, packageDirs); TestContext.WriteLine($"Loading plugin from {fullPath}"); if (PlugIns.PlugIn.LoadPlugIn(fullPath, out Guid _) From 38d80184ed389236d095265a597b235d12e3c11e Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Thu, 19 Mar 2026 15:03:55 -0500 Subject: [PATCH 09/17] add PackageDirectories to the Rhino_Package_Directories env variable for windowed rhino load --- src/Directory.Build.props | 2 +- src/Rhino.Testing.Tests/Rhino.Testing.Configs.xml | 3 +++ src/Rhino.Testing/Configs.cs | 2 ++ src/Rhino.Testing/RhinoCore.cs | 8 ++++++++ src/Rhino.Testing/_Loaders/PluginLoader.cs | 2 ++ 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 5e4e710..b612f81 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -15,7 +15,7 @@ - 8.0.29-ryan.4 + 8.0.29-ryan.6 8.0.6-beta 8.27.26019.16021 $(MSBuildThisFileDirectory)src\ diff --git a/src/Rhino.Testing.Tests/Rhino.Testing.Configs.xml b/src/Rhino.Testing.Tests/Rhino.Testing.Configs.xml index 53cd297..3f232d6 100644 --- a/src/Rhino.Testing.Tests/Rhino.Testing.Configs.xml +++ b/src/Rhino.Testing.Tests/Rhino.Testing.Configs.xml @@ -7,4 +7,7 @@ true false false + + + diff --git a/src/Rhino.Testing/Configs.cs b/src/Rhino.Testing/Configs.cs index e853ec3..8714acb 100644 --- a/src/Rhino.Testing/Configs.cs +++ b/src/Rhino.Testing/Configs.cs @@ -132,6 +132,8 @@ static Configs() dirs.Add(new DirectoryConfigs() { Path = splitDir.Trim() }); } } + //add the config file directory as a package directory so that plugins can be specified relative to the config file location + dirs.Add(new DirectoryConfigs() { Path = Path.GetDirectoryName(cfgFile) }); Current.PackageDirectories = dirs; foreach (var plugin in Current.LoadPlugins) diff --git a/src/Rhino.Testing/RhinoCore.cs b/src/Rhino.Testing/RhinoCore.cs index 41cc05c..53f4bcf 100644 --- a/src/Rhino.Testing/RhinoCore.cs +++ b/src/Rhino.Testing/RhinoCore.cs @@ -37,6 +37,14 @@ public static void Initialize() throw new DirectoryNotFoundException(Configs.Current.RhinoSystemDir); } + //add the Package directories to the RHINO_PACKAGE_DIRS environment variable so that RhinoInside can find the plugins in those directories + foreach (var packageDir in Configs.Current.PackageDirectories) + { + var env = Environment.GetEnvironmentVariable("RHINO_PACKAGE_DIRS", EnvironmentVariableTarget.Process); + env = string.IsNullOrEmpty(env) ? packageDir.Path : $"{env};{packageDir.Path}"; + Environment.SetEnvironmentVariable("RHINO_PACKAGE_DIRS", env, EnvironmentVariableTarget.Process); + } + RhinoInside.Resolver.Initialize(Configs.Current.RhinoSystemDir); Configs.Current.RhinoSystemDir = RhinoInside.Resolver.RhinoSystemDirectory; diff --git a/src/Rhino.Testing/_Loaders/PluginLoader.cs b/src/Rhino.Testing/_Loaders/PluginLoader.cs index 592d0d4..44d2a4b 100644 --- a/src/Rhino.Testing/_Loaders/PluginLoader.cs +++ b/src/Rhino.Testing/_Loaders/PluginLoader.cs @@ -6,6 +6,7 @@ using RhinoInside; using NUnit.Framework; +using System.Diagnostics; namespace Rhino.Testing { @@ -169,6 +170,7 @@ public static void LoadPlugins(IEnumerable rhpPaths, IEnumerable { string fullPath = GetRHPPath(rhpPath, packageDirs); TestContext.WriteLine($"Loading plugin from {fullPath}"); + Debug.WriteLine($"Loading plugin from {fullPath}"); if (PlugIns.PlugIn.LoadPlugIn(fullPath, out Guid _) != PlugIns.LoadPlugInResult.Success) From 4e58851614d3ef21b1b5dd50ce049d6a4cf0fdbd Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Fri, 20 Mar 2026 06:54:29 -0500 Subject: [PATCH 10/17] Make sure that the plugin path is rooted --- src/Directory.Build.props | 2 +- src/Rhino.Testing/_Loaders/PluginLoader.cs | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index b612f81..e745dce 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -15,7 +15,7 @@ - 8.0.29-ryan.6 + 8.0.29-ryan.8 8.0.6-beta 8.27.26019.16021 $(MSBuildThisFileDirectory)src\ diff --git a/src/Rhino.Testing/_Loaders/PluginLoader.cs b/src/Rhino.Testing/_Loaders/PluginLoader.cs index 44d2a4b..7b3dde7 100644 --- a/src/Rhino.Testing/_Loaders/PluginLoader.cs +++ b/src/Rhino.Testing/_Loaders/PluginLoader.cs @@ -14,7 +14,16 @@ static class PluginLoader { public static string GetRHPPath(string rhpPath, IEnumerable packageDirs = null) { - if(File.Exists(rhpPath)) return rhpPath; + if(File.Exists(rhpPath)) + { + if (Path.IsPathRooted(rhpPath)) + { + return rhpPath; + } + var dir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + rhpPath = Path.Combine(dir, rhpPath); + return rhpPath; + } // first look in the rhino system directory string rhp = Path.Combine(Configs.Current.RhinoSystemDir, rhpPath); if (File.Exists(rhp)) From 22a67c09deb9c0e2f6a48075b69a53996317c17b Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Fri, 20 Mar 2026 06:56:27 -0500 Subject: [PATCH 11/17] remove nuget note --- src/Directory.Build.props | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index e745dce..cead297 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -55,6 +55,4 @@ compile; build; native; contentfiles; analyzers; buildtransitive - - From c1915b14767b2e1950b03a4be57a562e1b8cfd36 Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Fri, 20 Mar 2026 07:07:21 -0500 Subject: [PATCH 12/17] Update README.md Added documentation of the new config file features --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 822233c..54cb0ca 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,21 @@ Specify list of plugins to be loaded (These plugins are always loaded before Gra ``` +Specify Directories to Load Packages from +```xml + + + +``` + +Use Environment Variables for Paths in the config file +```xml + + + ${env:ProgramFiles}\Rhino 8\System + +``` + Specify Grasshopper to be loaded: ```xml From c7345cba2251cab685add4b92b767ccb01882eb7 Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Fri, 20 Mar 2026 07:08:36 -0500 Subject: [PATCH 13/17] clean up example configs --- src/Rhino.Testing.Tests/Rhino.Testing.Configs.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rhino.Testing.Tests/Rhino.Testing.Configs.xml b/src/Rhino.Testing.Tests/Rhino.Testing.Configs.xml index 3f232d6..69a3a53 100644 --- a/src/Rhino.Testing.Tests/Rhino.Testing.Configs.xml +++ b/src/Rhino.Testing.Tests/Rhino.Testing.Configs.xml @@ -8,6 +8,6 @@ false false - + From c9495f46774e08c68a2d8b32050a6b2ffa6c0331 Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Fri, 20 Mar 2026 07:38:14 -0500 Subject: [PATCH 14/17] Adjust to reduce rhino api version back to 8.0 --- README.md | 14 ++++++++++++++ src/Directory.Build.props | 6 +++--- src/Rhino.Testing/Configs.cs | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 54cb0ca..0a36670 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,20 @@ Specify Legacy IronPython to be loaded: true ``` +Specify Rhino To Start in Windowed Mode (default is headless): +```xml +true +``` +Important note: if you set `WindowedMode` to true, you must ensure that your tests are running in a static appartment: +To do that, add the following attribute to your setup fixture class `Apartment(ApartmentState.STA)`: +```csharp +[SetupFixture, Apartment(ApartmentState.STA)] +public sealed class SetupFixture : Rhino.Testing.Fixtures.RhinoSetupFixture +{ + // your setup fixture implementation +} +``` + Specify list of plugins to be loaded (These plugins are always loaded before Grasshopper) ```xml diff --git a/src/Directory.Build.props b/src/Directory.Build.props index cead297..69472d4 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,6 +1,6 @@ - net48;net8.0;net8.0-windows + net48;net7.0;net7.0-windows;net8.0;net8.0-windows @@ -15,9 +15,9 @@ - 8.0.29-ryan.8 + 8.1.0 8.0.6-beta - 8.27.26019.16021 + 8.0.23304.9001 $(MSBuildThisFileDirectory)src\ true 2025 diff --git a/src/Rhino.Testing/Configs.cs b/src/Rhino.Testing/Configs.cs index 8714acb..db74e6e 100644 --- a/src/Rhino.Testing/Configs.cs +++ b/src/Rhino.Testing/Configs.cs @@ -102,7 +102,7 @@ public static string ReplaceEnvVars(string path) break; string envVar = path.Substring(envStart + 6, envEnd - envStart - 6); string envVal = Environment.GetEnvironmentVariable(envVar) ?? string.Empty; -#if NET8_0_OR_GREATER +#if NET7_0_OR_GREATER path = path[..envStart] + envVal + path[(envEnd + 1)..]; #else path = path.Substring(0, envStart) + envVal + path.Substring(envEnd + 1); From 19790e6ad55938d038e195df236119a4726e025b Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Fri, 20 Mar 2026 07:44:33 -0500 Subject: [PATCH 15/17] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd55581..2f38ca4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## [8.1.0-beta] - 2026-03-20 +- Add Windowed Rhino Option +- Add Support for Environment Variables in Paths +- Add Support for specifying Package Directories + ## [8.0.28-beta] - 2025-05-12 - Resolver bug fixes From 1adb91c11e2e760ca25d44c128007fe5e0a5bba2 Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Fri, 20 Mar 2026 08:10:38 -0500 Subject: [PATCH 16/17] Update nuget.config --- nuget.config | 1 - 1 file changed, 1 deletion(-) diff --git a/nuget.config b/nuget.config index ffc43e7..6ce9759 100644 --- a/nuget.config +++ b/nuget.config @@ -4,6 +4,5 @@ - From 528b124fee79f3b93cafbae02e31c47c6801ea38 Mon Sep 17 00:00:00 2001 From: Cullen Sarles Date: Thu, 2 Apr 2026 11:51:42 -0500 Subject: [PATCH 17/17] remove test adapter dependency from shared library --- src/Directory.Build.props | 5 ----- .../Rhino.Testing.Tests.SetupAttribute.csproj | 6 ++++++ .../Rhino.Testing.Tests.SetupFixture.csproj | 6 ++++++ .../Rhino.Testing.Tests.WindowedSetup.csproj | 6 ++++++ src/Rhino.Testing.Tests/Rhino.Testing.Tests.csproj | 5 +++++ 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 69472d4..1e98e29 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -33,11 +33,6 @@ - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - diff --git a/src/Rhino.Testing.Tests.SetupAttribute/Rhino.Testing.Tests.SetupAttribute.csproj b/src/Rhino.Testing.Tests.SetupAttribute/Rhino.Testing.Tests.SetupAttribute.csproj index afcc957..8b9b024 100644 --- a/src/Rhino.Testing.Tests.SetupAttribute/Rhino.Testing.Tests.SetupAttribute.csproj +++ b/src/Rhino.Testing.Tests.SetupAttribute/Rhino.Testing.Tests.SetupAttribute.csproj @@ -9,12 +9,18 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + Always diff --git a/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Tests.SetupFixture.csproj b/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Tests.SetupFixture.csproj index 2d59f17..f79ca7f 100644 --- a/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Tests.SetupFixture.csproj +++ b/src/Rhino.Testing.Tests.SetupFixture/Rhino.Testing.Tests.SetupFixture.csproj @@ -9,6 +9,11 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + @@ -16,6 +21,7 @@ + Always diff --git a/src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Tests.WindowedSetup.csproj b/src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Tests.WindowedSetup.csproj index ed34567..d76d4c8 100644 --- a/src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Tests.WindowedSetup.csproj +++ b/src/Rhino.Testing.Tests.WindowedSetup/Rhino.Testing.Tests.WindowedSetup.csproj @@ -9,12 +9,18 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + Always diff --git a/src/Rhino.Testing.Tests/Rhino.Testing.Tests.csproj b/src/Rhino.Testing.Tests/Rhino.Testing.Tests.csproj index afcc957..e0602cf 100644 --- a/src/Rhino.Testing.Tests/Rhino.Testing.Tests.csproj +++ b/src/Rhino.Testing.Tests/Rhino.Testing.Tests.csproj @@ -9,6 +9,11 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all +