From be478f389e4e2bba2196c8ca7772f2ed59f36670 Mon Sep 17 00:00:00 2001 From: Marios Tsiliakos Date: Thu, 17 Jun 2021 12:58:42 +0100 Subject: [PATCH 1/5] Fixes GH_IO load issue in XUnit --- .../RhinoPlugin.Tests.Xunit.csproj | 2 +- .../XunitTestFixture.cs | 20 ++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Src/RhinoPlugin.Tests.Xunit/RhinoPlugin.Tests.Xunit.csproj b/Src/RhinoPlugin.Tests.Xunit/RhinoPlugin.Tests.Xunit.csproj index d534dce..8d0fa03 100644 --- a/Src/RhinoPlugin.Tests.Xunit/RhinoPlugin.Tests.Xunit.csproj +++ b/Src/RhinoPlugin.Tests.Xunit/RhinoPlugin.Tests.Xunit.csproj @@ -5,7 +5,7 @@ - x64 + AnyCPU false diff --git a/Src/RhinoPlugin.Tests.Xunit/XunitTestFixture.cs b/Src/RhinoPlugin.Tests.Xunit/XunitTestFixture.cs index 3f7a7af..ca2928d 100644 --- a/Src/RhinoPlugin.Tests.Xunit/XunitTestFixture.cs +++ b/Src/RhinoPlugin.Tests.Xunit/XunitTestFixture.cs @@ -60,19 +60,29 @@ public void StartRhino() } /// - /// Add Grasshopper.dll to the current Appdomain + /// Add Grasshopper.dll and GH_IO to the current Appdomain /// private Assembly ResolveGrasshopper(object sender, ResolveEventArgs args) { var name = args.Name; - if (!name.StartsWith("Grasshopper")) + if (name.StartsWith("Grasshopper")) { - return null; + var path = Path.Combine(Path.GetFullPath(Path.Combine(rhinoDir, @"..\")), "Plug-ins\\Grasshopper\\Grasshopper.dll"); + return Assembly.LoadFrom(path); } - var path = Path.Combine(Path.GetFullPath(Path.Combine(rhinoDir, @"..\")), "Plug-ins\\Grasshopper\\Grasshopper.dll"); - return Assembly.LoadFrom(path); + if (name.StartsWith("GH_IO")) + { + var path = Path.Combine(Path.GetFullPath(Path.Combine(rhinoDir, @"..\")), "Plug-ins\\Grasshopper\\GH_IO.dll"); + return Assembly.LoadFrom(path); + } + + else + { + return null; + } + } /// From fbdbee091dad59e452da499f0ea6be8bbf4e7d90 Mon Sep 17 00:00:00 2001 From: Marios Tsiliakos Date: Thu, 17 Jun 2021 12:59:45 +0100 Subject: [PATCH 2/5] Switches back to x64 platform target --- Src/RhinoPlugin.Tests.Xunit/RhinoPlugin.Tests.Xunit.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/RhinoPlugin.Tests.Xunit/RhinoPlugin.Tests.Xunit.csproj b/Src/RhinoPlugin.Tests.Xunit/RhinoPlugin.Tests.Xunit.csproj index 8d0fa03..d534dce 100644 --- a/Src/RhinoPlugin.Tests.Xunit/RhinoPlugin.Tests.Xunit.csproj +++ b/Src/RhinoPlugin.Tests.Xunit/RhinoPlugin.Tests.Xunit.csproj @@ -5,7 +5,7 @@ - AnyCPU + x64 false From 9f8569ec59a7d2101d5af3b7c6b4b19d20035ad8 Mon Sep 17 00:00:00 2001 From: Marios Tsiliakos Date: Thu, 17 Jun 2021 14:21:23 +0100 Subject: [PATCH 3/5] Adds simple Rhino Load project --- RhinoCommonUnitTesting.sln | 6 + .../Properties/AssemblyInfo.cs | 36 ++++++ .../RhinoPlugin.Tests.RHInside.csproj | 111 ++++++++++++++++++ .../XunitExampleTestsRI.cs | 77 ++++++++++++ .../XunitTestFixtureRI.cs | 74 ++++++++++++ .../packages.config | 15 +++ 6 files changed, 319 insertions(+) create mode 100644 Src/RhinoPlugin.Tests.RHInside/Properties/AssemblyInfo.cs create mode 100644 Src/RhinoPlugin.Tests.RHInside/RhinoPlugin.Tests.RHInside.csproj create mode 100644 Src/RhinoPlugin.Tests.RHInside/XunitExampleTestsRI.cs create mode 100644 Src/RhinoPlugin.Tests.RHInside/XunitTestFixtureRI.cs create mode 100644 Src/RhinoPlugin.Tests.RHInside/packages.config diff --git a/RhinoCommonUnitTesting.sln b/RhinoCommonUnitTesting.sln index c11353a..73bc812 100644 --- a/RhinoCommonUnitTesting.sln +++ b/RhinoCommonUnitTesting.sln @@ -12,6 +12,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RhinoPlugin.Tests.Xunit", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RhinoPlugin.Tests", "Src\RhinoPlugin.Tests\RhinoPlugin.Tests.csproj", "{5F877072-8EA6-4CE3-9A6B-6CF201E3CB73}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RhinoPlugin.Tests.RHInside", "Src\RhinoPlugin.Tests.RHInside\RhinoPlugin.Tests.RHInside.csproj", "{ACE64814-311F-4B9B-9DFE-31AE9A9030E3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -26,6 +28,10 @@ Global {5F877072-8EA6-4CE3-9A6B-6CF201E3CB73}.Debug|Any CPU.Build.0 = Debug|Any CPU {5F877072-8EA6-4CE3-9A6B-6CF201E3CB73}.Release|Any CPU.ActiveCfg = Release|Any CPU {5F877072-8EA6-4CE3-9A6B-6CF201E3CB73}.Release|Any CPU.Build.0 = Release|Any CPU + {ACE64814-311F-4B9B-9DFE-31AE9A9030E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACE64814-311F-4B9B-9DFE-31AE9A9030E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACE64814-311F-4B9B-9DFE-31AE9A9030E3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACE64814-311F-4B9B-9DFE-31AE9A9030E3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Src/RhinoPlugin.Tests.RHInside/Properties/AssemblyInfo.cs b/Src/RhinoPlugin.Tests.RHInside/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a63e55a --- /dev/null +++ b/Src/RhinoPlugin.Tests.RHInside/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RhinoPlugin.Tests.RHInside")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RhinoPlugin.Tests.RHInside")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ace64814-311f-4b9b-9dfe-31ae9a9030e3")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Src/RhinoPlugin.Tests.RHInside/RhinoPlugin.Tests.RHInside.csproj b/Src/RhinoPlugin.Tests.RHInside/RhinoPlugin.Tests.RHInside.csproj new file mode 100644 index 0000000..2339934 --- /dev/null +++ b/Src/RhinoPlugin.Tests.RHInside/RhinoPlugin.Tests.RHInside.csproj @@ -0,0 +1,111 @@ + + + + + + + Debug + AnyCPU + {ACE64814-311F-4B9B-9DFE-31AE9A9030E3} + Library + Properties + RhinoPlugin.Tests.RHInside + RhinoPlugin.Tests.RHInside + v4.8 + 512 + true + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\packages\RhinoCommon.7.0.20314.3001\lib\net45\Eto.dll + + + ..\..\packages\RhinoWindows.7.0.20314.3001\lib\net45\Eto.Wpf.dll + + + ..\..\packages\Grasshopper.7.0.20314.3001\lib\net45\GH_IO.dll + + + ..\..\packages\Grasshopper.7.0.20314.3001\lib\net45\Grasshopper.dll + + + ..\..\packages\RhinoCommon.7.0.20314.3001\lib\net45\Rhino.UI.dll + + + ..\..\packages\RhinoCommon.7.0.20314.3001\lib\net45\RhinoCommon.dll + + + ..\..\packages\Rhino.Inside.7.0.0\lib\net48\RhinoInside.dll + + + ..\..\packages\RhinoWindows.7.0.20314.3001\lib\net45\RhinoWindows.dll + + + + + + + + + + + ..\..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll + True + + + ..\..\packages\xunit.assert.2.4.1\lib\netstandard1.1\xunit.assert.dll + + + ..\..\packages\xunit.extensibility.core.2.4.1\lib\net452\xunit.core.dll + + + ..\..\packages\xunit.extensibility.execution.2.4.1\lib\net452\xunit.execution.desktop.dll + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + + + \ No newline at end of file diff --git a/Src/RhinoPlugin.Tests.RHInside/XunitExampleTestsRI.cs b/Src/RhinoPlugin.Tests.RHInside/XunitExampleTestsRI.cs new file mode 100644 index 0000000..d598343 --- /dev/null +++ b/Src/RhinoPlugin.Tests.RHInside/XunitExampleTestsRI.cs @@ -0,0 +1,77 @@ +using System; + +using Rhino.Geometry; +using Grasshopper.Kernel.Types; + +using Xunit; + + +namespace RhinoPlugin.Tests.RHInside +{ + /// + /// XUnit tests + /// + [Collection("RhinoTestingCollection")] + public class XunitExampleTestsRI + { + XunitTestFixtureRI _fixture; + + public XunitExampleTestsRI(XunitTestFixtureRI fixture) + { + fixture = _fixture; + } + + [Fact] + public void Brep_Translation() + { + + // Arrange + var bb = new BoundingBox(new Point3d(0, 0, 0), new Point3d(100, 100, 100)); + var brep = bb.ToBrep(); + var t = Transform.Translation(new Vector3d(30, 40, 50)); + + // Act + brep.Transform(t); + + // Assert + Assert.Equal(brep.GetBoundingBox(true).Center, new Point3d(80, 90, 100)); + + } + + /// + /// Xunit Test to Intersect sphere with a plane to generate a circle + /// + [Fact] + public void Brep_Intersection() + { + // Arrange + var radius = 4.0; + var brep = Brep.CreateFromSphere(new Sphere(new Point3d(), radius)); + var cuttingPlane = Plane.WorldXY; + + // Act + Rhino.Geometry.Intersect.Intersection.BrepPlane(brep, cuttingPlane, 0.001, out var curves, out var points); + + // Assert + Assert.Single(curves); + Assert.Equal(2 * Math.PI * radius, curves[0].GetLength()); + } + + /// + /// Xunit Test to ensure Centroid of GH_Box outputs a GH_Point + /// + [Fact] + public void GHBox_Centroid_ReturnsGHPoint() + { + // Arrange + var myBox = new GH_Box(new Box()); + + // Act + var result = myBox.Boundingbox.Center; + + // Assert + Assert.IsType(result); + } + + } +} \ No newline at end of file diff --git a/Src/RhinoPlugin.Tests.RHInside/XunitTestFixtureRI.cs b/Src/RhinoPlugin.Tests.RHInside/XunitTestFixtureRI.cs new file mode 100644 index 0000000..2a8430c --- /dev/null +++ b/Src/RhinoPlugin.Tests.RHInside/XunitTestFixtureRI.cs @@ -0,0 +1,74 @@ +using Microsoft.Win32; +using System; +using System.IO; +using System.Reflection; +using Xunit; + +namespace RhinoPlugin.Tests.RHInside +{ + + /// + /// Shared test context across unit tests that loads rhinocommon.dll and grasshopper.dll + /// + public class XunitTestFixtureRI : IDisposable + { + + private Rhino.Runtime.InProcess.RhinoCore _rhinoCore; + + /// + /// Empty Constuctor + /// + public XunitTestFixtureRI() + { + // Use the latest version of RH7 installed in your system + RhinoInside.Resolver.UseLatest = true; + + // Make sure we are running the tests as 64x + Assert.True(Environment.Is64BitProcess, "Tests must be run as x64"); + + //Initialize Rhino.Inside + RhinoInside.Resolver.Initialize(); + + // Set path to rhino system directory + string envPath = Environment.GetEnvironmentVariable("path"); + Environment.SetEnvironmentVariable("path", envPath + ";" + RhinoInside.Resolver.RhinoSystemDirectory); + + // Start Rhino and load all libraries + StartRhino(); + + } + + /// + /// Starting Rhino - loading the relevant libraries + /// + [STAThread] + public void StartRhino() + { + _rhinoCore = new Rhino.Runtime.InProcess.RhinoCore(null, Rhino.Runtime.InProcess.WindowStyle.NoWindow); + } + + /// + /// Disposing the context after running all the tests + /// + public void Dispose() + { + // do nothing or... + _rhinoCore?.Dispose(); + _rhinoCore = null; + } + } + + + /// + /// Collection Fixture - shared context across test classes + /// + [CollectionDefinition("RhinoTestingCollection")] + public class RhinoCollection : ICollectionFixture + { + // This class has no code, and is never created. Its purpose is simply + // to be the place to apply [CollectionDefinition] and all the + // ICollectionFixture<> interfaces. + } + +} + diff --git a/Src/RhinoPlugin.Tests.RHInside/packages.config b/Src/RhinoPlugin.Tests.RHInside/packages.config new file mode 100644 index 0000000..46ff6d7 --- /dev/null +++ b/Src/RhinoPlugin.Tests.RHInside/packages.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file From 3e38410d10767491ff98f6a933cf1549ed1531af Mon Sep 17 00:00:00 2001 From: Marios Tsiliakos Date: Mon, 4 Oct 2021 16:27:45 +0100 Subject: [PATCH 4/5] Updates Project name --- RhinoCommonUnitTesting.sln | 2 +- .../Properties/AssemblyInfo.cs | 0 .../RhinoPlugin.Tests.GHAutoLoad.csproj} | 14 +++++++------- .../XunitExampleTestsRI.cs | 2 +- .../XunitTestFixtureRI.cs | 2 +- .../packages.config | 0 6 files changed, 10 insertions(+), 10 deletions(-) rename Src/{RhinoPlugin.Tests.RHInside => RhinoPlugin.Tests.GHAutoLoad}/Properties/AssemblyInfo.cs (100%) rename Src/{RhinoPlugin.Tests.RHInside/RhinoPlugin.Tests.RHInside.csproj => RhinoPlugin.Tests.GHAutoLoad/RhinoPlugin.Tests.GHAutoLoad.csproj} (100%) rename Src/{RhinoPlugin.Tests.RHInside => RhinoPlugin.Tests.GHAutoLoad}/XunitExampleTestsRI.cs (98%) rename Src/{RhinoPlugin.Tests.RHInside => RhinoPlugin.Tests.GHAutoLoad}/XunitTestFixtureRI.cs (98%) rename Src/{RhinoPlugin.Tests.RHInside => RhinoPlugin.Tests.GHAutoLoad}/packages.config (100%) diff --git a/RhinoCommonUnitTesting.sln b/RhinoCommonUnitTesting.sln index 73bc812..9a16064 100644 --- a/RhinoCommonUnitTesting.sln +++ b/RhinoCommonUnitTesting.sln @@ -12,7 +12,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RhinoPlugin.Tests.Xunit", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RhinoPlugin.Tests", "Src\RhinoPlugin.Tests\RhinoPlugin.Tests.csproj", "{5F877072-8EA6-4CE3-9A6B-6CF201E3CB73}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RhinoPlugin.Tests.RHInside", "Src\RhinoPlugin.Tests.RHInside\RhinoPlugin.Tests.RHInside.csproj", "{ACE64814-311F-4B9B-9DFE-31AE9A9030E3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RhinoPlugin.Tests.GHAutoLoad", "Src\RhinoPlugin.Tests.GHAutoLoad\RhinoPlugin.Tests.GHAutoLoad.csproj", "{ACE64814-311F-4B9B-9DFE-31AE9A9030E3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Src/RhinoPlugin.Tests.RHInside/Properties/AssemblyInfo.cs b/Src/RhinoPlugin.Tests.GHAutoLoad/Properties/AssemblyInfo.cs similarity index 100% rename from Src/RhinoPlugin.Tests.RHInside/Properties/AssemblyInfo.cs rename to Src/RhinoPlugin.Tests.GHAutoLoad/Properties/AssemblyInfo.cs diff --git a/Src/RhinoPlugin.Tests.RHInside/RhinoPlugin.Tests.RHInside.csproj b/Src/RhinoPlugin.Tests.GHAutoLoad/RhinoPlugin.Tests.GHAutoLoad.csproj similarity index 100% rename from Src/RhinoPlugin.Tests.RHInside/RhinoPlugin.Tests.RHInside.csproj rename to Src/RhinoPlugin.Tests.GHAutoLoad/RhinoPlugin.Tests.GHAutoLoad.csproj index 2339934..10fd04d 100644 --- a/Src/RhinoPlugin.Tests.RHInside/RhinoPlugin.Tests.RHInside.csproj +++ b/Src/RhinoPlugin.Tests.GHAutoLoad/RhinoPlugin.Tests.GHAutoLoad.csproj @@ -1,7 +1,7 @@  - + Debug @@ -87,25 +87,25 @@ - + - + - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + - - + + - \ No newline at end of file diff --git a/Src/RhinoPlugin.Tests.RHInside/XunitExampleTestsRI.cs b/Src/RhinoPlugin.Tests.GHAutoLoad/XunitExampleTestsRI.cs similarity index 98% rename from Src/RhinoPlugin.Tests.RHInside/XunitExampleTestsRI.cs rename to Src/RhinoPlugin.Tests.GHAutoLoad/XunitExampleTestsRI.cs index d598343..121c959 100644 --- a/Src/RhinoPlugin.Tests.RHInside/XunitExampleTestsRI.cs +++ b/Src/RhinoPlugin.Tests.GHAutoLoad/XunitExampleTestsRI.cs @@ -6,7 +6,7 @@ using Xunit; -namespace RhinoPlugin.Tests.RHInside +namespace RhinoPlugin.Tests.GHAutoLoad { /// /// XUnit tests diff --git a/Src/RhinoPlugin.Tests.RHInside/XunitTestFixtureRI.cs b/Src/RhinoPlugin.Tests.GHAutoLoad/XunitTestFixtureRI.cs similarity index 98% rename from Src/RhinoPlugin.Tests.RHInside/XunitTestFixtureRI.cs rename to Src/RhinoPlugin.Tests.GHAutoLoad/XunitTestFixtureRI.cs index 2a8430c..6f63caa 100644 --- a/Src/RhinoPlugin.Tests.RHInside/XunitTestFixtureRI.cs +++ b/Src/RhinoPlugin.Tests.GHAutoLoad/XunitTestFixtureRI.cs @@ -4,7 +4,7 @@ using System.Reflection; using Xunit; -namespace RhinoPlugin.Tests.RHInside +namespace RhinoPlugin.Tests.GHAutoLoad { /// diff --git a/Src/RhinoPlugin.Tests.RHInside/packages.config b/Src/RhinoPlugin.Tests.GHAutoLoad/packages.config similarity index 100% rename from Src/RhinoPlugin.Tests.RHInside/packages.config rename to Src/RhinoPlugin.Tests.GHAutoLoad/packages.config From 942dcf59cbbe921cb25bce0e1eb9ebe3629f5064 Mon Sep 17 00:00:00 2001 From: Marios Tsiliakos Date: Mon, 4 Oct 2021 16:59:10 +0100 Subject: [PATCH 5/5] Updates README --- README.md | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9b7c2ca..72d128f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # RhinoCommon Unit Testing ## Intro -Example of unit testing RhinoCommon from within the Visual Studio test runner on windows. +Examples of unit testing RhinoCommon and Grasshopper3d. ## Prerequisites Rhino 7 @@ -11,23 +11,34 @@ Visual Studio 2019 https://www.visualstudio.com/downloads/ ## Test Framework -This project provides examples for XUnit and MS Test, -but the principles would be easily transferable to other frameworks if needs. +This project provides three example projects. + - One using the MsTest Framework `RhinoPlugin.Tests` + - A second one using Xunit `RhinoPlugin.Tests.Xunit` + - And a thrid one using the old .csproj format without PackageReference Migration where Grasshopper3d libraries are loaded directly by Rhino.Inside `RhinoPlugin.Tests.GHAutoLoad` + +but the principles would be easily transferable to other frameworks if needed. ## How to Run - Build the solution - Test should appear in Visual Studio Test Explorer - Set test enviroment to x64 `Test > Test Settings > Default Processor Architecture > x64` -- Click `Run All` to run the tests +- Click `Run All` to run the tests. +- Some times you may need to run each project individualy as the testhost tends to get stuck. ## Further Reading -For more info on using Rhino in a headless environment see the Rhino Compute project: -https://github.com/mcneel/compute.rhino3d +For more info on using Rhino in a headless environment or inside other applications please see the projects below: +- https://github.com/mcneel/compute.rhino3d +- https://github.com/mcneel/rhino.inside + + +## Known issues +Using the new *.csproj* format, the headless version of Rhino fails to load the GH libraries. To tackle this we are loading the library manually to the test context by resolving the specific assembly. + +If you are using the old .csproj format and with a packages.config for the references then GH is loading automatically. ## Troubleshooting - Note that when Rhino is in headless mode there is no document defined. The static property `RhinoDoc.ActiveDoc` will thus be null which may trip up your plugin code. -- If you have any problems getting this to work or you have a more complex use case, -then please get in touch via the issue tracker. +- If you have any problems getting this to work or you have a more complex use case, please get in touch via the issue tracker. \ No newline at end of file