[mapguide-commits] r9783 - in sandbox/jng/vanilla_swig/Bindings: . src/Bindings src/Managed/DotNet/OSGeo.MapGuide.Web/custom src/Test/DotNet/src/TestMisc src/Test/DotNet/src/TestRunner src/Tools src/Tools/ClassMapGen src/Tools/MgTestAdmin src/Tools/MgTestAdmin/Model src/Tools/MgTestAdmin/Model/Operations src/Tools/MgTestAdmin/Properties src/Tools/PhpPostProcess src/Tools/StampVer src/Tools/SwigPrepare

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Sat Nov 14 05:37:23 PST 2020


Author: jng
Date: 2020-11-14 05:37:21 -0800 (Sat, 14 Nov 2020)
New Revision: 9783

Added:
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/DbControl.Designer.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/DbControl.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/DbControl.resx
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/MgTestAdmin.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/IOperationProvider.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/Categories.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/DrawingService.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/FeatureService.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/ResourceService.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/ServerAdmin.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/SiteService.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/WebLayout.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/ParamSet.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/TestCase.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/TestResult.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationBrowserControl.Designer.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationBrowserControl.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationBrowserControl.resx
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationProviderService.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetControl.Designer.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetControl.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetControl.resx
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetEditor.Designer.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetEditor.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetEditor.resx
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetManager.Designer.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetManager.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetManager.resx
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetPicker.Designer.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetPicker.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetPicker.resx
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Program.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/AssemblyInfo.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Resources.Designer.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Resources.resx
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Settings.Designer.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Settings.settings
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/SqliteDb.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/SqliteDbExtensions.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestCaseControl.Designer.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestCaseControl.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestCaseControl.resx
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManager.Designer.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManager.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManager.resx
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManagerControl.Designer.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManagerControl.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManagerControl.resx
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestResultControl.Designer.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestResultControl.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestResultControl.resx
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Workbench.Designer.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Workbench.cs
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Workbench.resx
   sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/sqlite3.exe
Removed:
   sandbox/jng/vanilla_swig/Bindings/src/Tools/Tools.sln
Modified:
   sandbox/jng/vanilla_swig/Bindings/TODO.txt
   sandbox/jng/vanilla_swig/Bindings/src/Bindings/Bindings.sln
   sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/OSGeo.MapGuide.Web/custom/EntryPoint.cs
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMisc/TestMisc.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/TestRunner.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/ClassMapGen.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/PhpPostProcess.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/StampVer.csproj
   sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/
   sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/SwigPrepare.csproj
Log:
- Consolidate Tools.sln into Bindings.sln and update all tool projects to target netcoreapp3.1
- Check in MgTestAdmin, which is an updated version of SQLiteGUI for test database administration
- Add mapguide_en.res as file content for TestMisc and TestRunner projects
- Fix MgInitializeWebTier to throw any pending exception if stashed. This fixes our remaining test failure on Windows due to missing mapguide_en.res file

Modified: sandbox/jng/vanilla_swig/Bindings/TODO.txt
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/TODO.txt	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/TODO.txt	2020-11-14 13:37:21 UTC (rev 9783)
@@ -1,8 +1,10 @@
  - [x] Check that .net error messages are being read (https://github.com/jumpinjackie/mapguide-api-bindings/issues/35)
  - [x] Explore whether our current instability is due to invalid class layout assumptions due to stubbing ACE_Recursive_Thread_Mutex to avoid leaking ACE headers
- - [ ] Generate XML documentation for our .net wrapper binding projects
+ - [x] Generate XML documentation for our .net wrapper binding projects
  - [ ] Make sure we can build debug bindings
  - [ ] Fix up inconsistent stack direction in C# exceptions (C++ call stack are printed downwards, C# call stack is printed upwards)
+ - [x] Check in test case admin tool
+ - [x] Consolidate Tools.sln into Bindings.sln
  - Split .net binding into the Foundation/Geometry/PlatformBase/MapGuideCommon/Web layout (https://github.com/jumpinjackie/mapguide-api-bindings/issues/18)
    - [ ] Add CentOS 6 Dockerfile that
       - Install SWIG and common libs tarball (from docker build system)

Modified: sandbox/jng/vanilla_swig/Bindings/src/Bindings/Bindings.sln
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Bindings/Bindings.sln	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Bindings/Bindings.sln	2020-11-14 13:37:21 UTC (rev 9783)
@@ -98,6 +98,16 @@
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "split", "split", "{025034E6-A625-427B-AA99-5158095ED5F9}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassMapGen", "..\Tools\ClassMapGen\ClassMapGen.csproj", "{C266761B-BC19-4BD7-9EE8-3AE2CE7DE7A4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhpPostProcess", "..\Tools\PhpPostProcess\PhpPostProcess.csproj", "{42199427-9825-4A45-A3D1-5C3090C57035}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StampVer", "..\Tools\StampVer\StampVer.csproj", "{7A73A95F-DCCC-4880-BA8C-AE8D9D48FA59}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwigPrepare", "..\Tools\SwigPrepare\SwigPrepare.csproj", "{27210DB6-811B-4D4E-A7E0-6488E5AFA733}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MgTestAdmin", "..\Tools\MgTestAdmin\MgTestAdmin.csproj", "{096D661F-DEA7-4EAB-95BB-F6A0E0AF59FF}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -208,6 +218,46 @@
 		{C83BF842-ABB1-4870-8003-11EF860E6741}.Release|Any CPU.ActiveCfg = Release|x64
 		{C83BF842-ABB1-4870-8003-11EF860E6741}.Release|x64.ActiveCfg = Release|x64
 		{C83BF842-ABB1-4870-8003-11EF860E6741}.Release|x64.Build.0 = Release|x64
+		{C266761B-BC19-4BD7-9EE8-3AE2CE7DE7A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{C266761B-BC19-4BD7-9EE8-3AE2CE7DE7A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{C266761B-BC19-4BD7-9EE8-3AE2CE7DE7A4}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{C266761B-BC19-4BD7-9EE8-3AE2CE7DE7A4}.Debug|x64.Build.0 = Debug|Any CPU
+		{C266761B-BC19-4BD7-9EE8-3AE2CE7DE7A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{C266761B-BC19-4BD7-9EE8-3AE2CE7DE7A4}.Release|Any CPU.Build.0 = Release|Any CPU
+		{C266761B-BC19-4BD7-9EE8-3AE2CE7DE7A4}.Release|x64.ActiveCfg = Release|Any CPU
+		{C266761B-BC19-4BD7-9EE8-3AE2CE7DE7A4}.Release|x64.Build.0 = Release|Any CPU
+		{42199427-9825-4A45-A3D1-5C3090C57035}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{42199427-9825-4A45-A3D1-5C3090C57035}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{42199427-9825-4A45-A3D1-5C3090C57035}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{42199427-9825-4A45-A3D1-5C3090C57035}.Debug|x64.Build.0 = Debug|Any CPU
+		{42199427-9825-4A45-A3D1-5C3090C57035}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{42199427-9825-4A45-A3D1-5C3090C57035}.Release|Any CPU.Build.0 = Release|Any CPU
+		{42199427-9825-4A45-A3D1-5C3090C57035}.Release|x64.ActiveCfg = Release|Any CPU
+		{42199427-9825-4A45-A3D1-5C3090C57035}.Release|x64.Build.0 = Release|Any CPU
+		{7A73A95F-DCCC-4880-BA8C-AE8D9D48FA59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7A73A95F-DCCC-4880-BA8C-AE8D9D48FA59}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7A73A95F-DCCC-4880-BA8C-AE8D9D48FA59}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{7A73A95F-DCCC-4880-BA8C-AE8D9D48FA59}.Debug|x64.Build.0 = Debug|Any CPU
+		{7A73A95F-DCCC-4880-BA8C-AE8D9D48FA59}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7A73A95F-DCCC-4880-BA8C-AE8D9D48FA59}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7A73A95F-DCCC-4880-BA8C-AE8D9D48FA59}.Release|x64.ActiveCfg = Release|Any CPU
+		{7A73A95F-DCCC-4880-BA8C-AE8D9D48FA59}.Release|x64.Build.0 = Release|Any CPU
+		{27210DB6-811B-4D4E-A7E0-6488E5AFA733}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{27210DB6-811B-4D4E-A7E0-6488E5AFA733}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{27210DB6-811B-4D4E-A7E0-6488E5AFA733}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{27210DB6-811B-4D4E-A7E0-6488E5AFA733}.Debug|x64.Build.0 = Debug|Any CPU
+		{27210DB6-811B-4D4E-A7E0-6488E5AFA733}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{27210DB6-811B-4D4E-A7E0-6488E5AFA733}.Release|Any CPU.Build.0 = Release|Any CPU
+		{27210DB6-811B-4D4E-A7E0-6488E5AFA733}.Release|x64.ActiveCfg = Release|Any CPU
+		{27210DB6-811B-4D4E-A7E0-6488E5AFA733}.Release|x64.Build.0 = Release|Any CPU
+		{096D661F-DEA7-4EAB-95BB-F6A0E0AF59FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{096D661F-DEA7-4EAB-95BB-F6A0E0AF59FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{096D661F-DEA7-4EAB-95BB-F6A0E0AF59FF}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{096D661F-DEA7-4EAB-95BB-F6A0E0AF59FF}.Debug|x64.Build.0 = Debug|Any CPU
+		{096D661F-DEA7-4EAB-95BB-F6A0E0AF59FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{096D661F-DEA7-4EAB-95BB-F6A0E0AF59FF}.Release|Any CPU.Build.0 = Release|Any CPU
+		{096D661F-DEA7-4EAB-95BB-F6A0E0AF59FF}.Release|x64.ActiveCfg = Release|Any CPU
+		{096D661F-DEA7-4EAB-95BB-F6A0E0AF59FF}.Release|x64.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -230,6 +280,11 @@
 		{0B90819D-4066-4D6C-87E6-3BE4D8F595DE} = {025034E6-A625-427B-AA99-5158095ED5F9}
 		{C83BF842-ABB1-4870-8003-11EF860E6741} = {025034E6-A625-427B-AA99-5158095ED5F9}
 		{025034E6-A625-427B-AA99-5158095ED5F9} = {A9257857-9844-4CEA-AF02-A7D8AB8F46AB}
+		{C266761B-BC19-4BD7-9EE8-3AE2CE7DE7A4} = {99132700-5CAD-4540-9939-7502C1A448EA}
+		{42199427-9825-4A45-A3D1-5C3090C57035} = {99132700-5CAD-4540-9939-7502C1A448EA}
+		{7A73A95F-DCCC-4880-BA8C-AE8D9D48FA59} = {99132700-5CAD-4540-9939-7502C1A448EA}
+		{27210DB6-811B-4D4E-A7E0-6488E5AFA733} = {99132700-5CAD-4540-9939-7502C1A448EA}
+		{096D661F-DEA7-4EAB-95BB-F6A0E0AF59FF} = {99132700-5CAD-4540-9939-7502C1A448EA}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {733BCCC2-0431-4AB7-ABB6-AAE81AB54C48}

Modified: sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/OSGeo.MapGuide.Web/custom/EntryPoint.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/OSGeo.MapGuide.Web/custom/EntryPoint.cs	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Managed/DotNet/OSGeo.MapGuide.Web/custom/EntryPoint.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -36,6 +36,10 @@
         public static void MgInitializeWebTier(string configFile)
         {
             WebUnmanagedApiPINVOKE.MgInitializeWebTier(configFile);
+            // The above call is a direct P/Invoke call and not a wrapper call, so we have to make
+            // sure to throw any pending exception if the native side stashed a pending exception
+            if (WebUnmanagedApiPINVOKE.SWIGPendingException.Pending)
+                throw WebUnmanagedApiPINVOKE.SWIGPendingException.Retrieve();
         }
     }
 }
\ No newline at end of file

Modified: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMisc/TestMisc.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMisc/TestMisc.csproj	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestMisc/TestMisc.csproj	2020-11-14 13:37:21 UTC (rev 9783)
@@ -13,4 +13,14 @@
     <PackageReference Include="OSGeo.MapGuide.Web" Version="4.0.0" />
   </ItemGroup>
 
+  <ItemGroup>
+    <Folder Include="Resources\" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="..\..\..\..\..\..\Common\MapGuideCommon\Resources\mapguide_en.res" Link="Resources\mapguide_en.res">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
 </Project>

Modified: sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/TestRunner.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/TestRunner.csproj	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Test/DotNet/src/TestRunner/TestRunner.csproj	2020-11-14 13:37:21 UTC (rev 9783)
@@ -23,4 +23,12 @@
     <PackageReference Include="OSGeo.MapGuide.Geometry" Version="4.0.0" />
     <PackageReference Include="OSGeo.MapGuide.Web" Version="4.0.0" />
   </ItemGroup>
+  <ItemGroup>
+    <Folder Include="Resources\" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="..\..\..\..\..\..\Common\MapGuideCommon\Resources\mapguide_en.res" Link="Resources\mapguide_en.res">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
 </Project>
\ No newline at end of file

Index: sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen	2020-11-14 13:37:21 UTC (rev 9783)

Property changes on: sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,2 ##
+bin
+obj
Modified: sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/ClassMapGen.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/ClassMapGen.csproj	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/ClassMapGen/ClassMapGen.csproj	2020-11-14 13:37:21 UTC (rev 9783)
@@ -2,11 +2,11 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
+    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
   </ItemGroup>
 
   <ItemGroup>

Index: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin	2020-11-14 13:37:21 UTC (rev 9783)

Property changes on: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,3 ##
+MgTestAdmin.csproj.user
+bin
+obj
Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/DbControl.Designer.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/DbControl.Designer.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/DbControl.Designer.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,196 @@
+namespace MgTestAdmin
+{
+    partial class DbControl
+    {
+        /// <summary> 
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Component Designer generated code
+
+        /// <summary> 
+        /// Required method for Designer support - do not modify 
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DbControl));
+            this.mainToolstrip = new System.Windows.Forms.ToolStrip();
+            this.btnExportDump = new System.Windows.Forms.ToolStripButton();
+            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+            this.dbTree = new System.Windows.Forms.TreeView();
+            this.tabControl1 = new System.Windows.Forms.TabControl();
+            this.tabTestManager = new System.Windows.Forms.TabPage();
+            this.tabParameterSets = new System.Windows.Forms.TabPage();
+            this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
+            this.tslName = new System.Windows.Forms.ToolStripLabel();
+            this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+            this.testManagerControl = new MgTestAdmin.TestManagerControl();
+            this.parameterSetManager = new MgTestAdmin.ParameterSetManager();
+            this.mainToolstrip.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+            this.splitContainer1.Panel1.SuspendLayout();
+            this.splitContainer1.Panel2.SuspendLayout();
+            this.splitContainer1.SuspendLayout();
+            this.tabControl1.SuspendLayout();
+            this.tabTestManager.SuspendLayout();
+            this.tabParameterSets.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // mainToolstrip
+            // 
+            this.mainToolstrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.tslName,
+            this.toolStripSeparator1,
+            this.btnExportDump});
+            this.mainToolstrip.Location = new System.Drawing.Point(0, 0);
+            this.mainToolstrip.Name = "mainToolstrip";
+            this.mainToolstrip.Size = new System.Drawing.Size(925, 25);
+            this.mainToolstrip.TabIndex = 0;
+            this.mainToolstrip.Text = "toolStrip1";
+            // 
+            // btnExportDump
+            // 
+            this.btnExportDump.Image = ((System.Drawing.Image)(resources.GetObject("btnExportDump.Image")));
+            this.btnExportDump.ImageTransparentColor = System.Drawing.Color.Magenta;
+            this.btnExportDump.Name = "btnExportDump";
+            this.btnExportDump.Size = new System.Drawing.Size(96, 22);
+            this.btnExportDump.Text = "Export Dump";
+            this.btnExportDump.Click += new System.EventHandler(this.btnExportDump_Click);
+            // 
+            // splitContainer1
+            // 
+            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.splitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
+            this.splitContainer1.Location = new System.Drawing.Point(0, 25);
+            this.splitContainer1.Name = "splitContainer1";
+            // 
+            // splitContainer1.Panel1
+            // 
+            this.splitContainer1.Panel1.Controls.Add(this.dbTree);
+            // 
+            // splitContainer1.Panel2
+            // 
+            this.splitContainer1.Panel2.Controls.Add(this.tabControl1);
+            this.splitContainer1.Size = new System.Drawing.Size(925, 436);
+            this.splitContainer1.SplitterDistance = 172;
+            this.splitContainer1.TabIndex = 1;
+            // 
+            // dbTree
+            // 
+            this.dbTree.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.dbTree.Location = new System.Drawing.Point(0, 0);
+            this.dbTree.Name = "dbTree";
+            this.dbTree.Size = new System.Drawing.Size(172, 436);
+            this.dbTree.TabIndex = 0;
+            // 
+            // tabControl1
+            // 
+            this.tabControl1.Controls.Add(this.tabTestManager);
+            this.tabControl1.Controls.Add(this.tabParameterSets);
+            this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.tabControl1.Location = new System.Drawing.Point(0, 0);
+            this.tabControl1.Name = "tabControl1";
+            this.tabControl1.SelectedIndex = 0;
+            this.tabControl1.Size = new System.Drawing.Size(749, 436);
+            this.tabControl1.TabIndex = 0;
+            // 
+            // tabTestManager
+            // 
+            this.tabTestManager.Controls.Add(this.testManagerControl);
+            this.tabTestManager.Location = new System.Drawing.Point(4, 22);
+            this.tabTestManager.Name = "tabTestManager";
+            this.tabTestManager.Padding = new System.Windows.Forms.Padding(3);
+            this.tabTestManager.Size = new System.Drawing.Size(741, 410);
+            this.tabTestManager.TabIndex = 0;
+            this.tabTestManager.Text = "Test Manager";
+            this.tabTestManager.UseVisualStyleBackColor = true;
+            // 
+            // tabParameterSets
+            // 
+            this.tabParameterSets.Controls.Add(this.parameterSetManager);
+            this.tabParameterSets.Location = new System.Drawing.Point(4, 22);
+            this.tabParameterSets.Name = "tabParameterSets";
+            this.tabParameterSets.Size = new System.Drawing.Size(670, 410);
+            this.tabParameterSets.TabIndex = 1;
+            this.tabParameterSets.Text = "Parameter Sets";
+            this.tabParameterSets.UseVisualStyleBackColor = true;
+            // 
+            // tslName
+            // 
+            this.tslName.Name = "tslName";
+            this.tslName.Size = new System.Drawing.Size(0, 22);
+            // 
+            // toolStripSeparator1
+            // 
+            this.toolStripSeparator1.Name = "toolStripSeparator1";
+            this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25);
+            // 
+            // testManagerControl
+            // 
+            this.testManagerControl.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.testManagerControl.Location = new System.Drawing.Point(3, 3);
+            this.testManagerControl.Name = "testManagerControl";
+            this.testManagerControl.Size = new System.Drawing.Size(735, 404);
+            this.testManagerControl.TabIndex = 0;
+            // 
+            // parameterSetManager
+            // 
+            this.parameterSetManager.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.parameterSetManager.Location = new System.Drawing.Point(0, 0);
+            this.parameterSetManager.Name = "parameterSetManager";
+            this.parameterSetManager.Size = new System.Drawing.Size(670, 410);
+            this.parameterSetManager.TabIndex = 0;
+            // 
+            // DbControl
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.Controls.Add(this.splitContainer1);
+            this.Controls.Add(this.mainToolstrip);
+            this.Name = "DbControl";
+            this.Size = new System.Drawing.Size(925, 461);
+            this.mainToolstrip.ResumeLayout(false);
+            this.mainToolstrip.PerformLayout();
+            this.splitContainer1.Panel1.ResumeLayout(false);
+            this.splitContainer1.Panel2.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+            this.splitContainer1.ResumeLayout(false);
+            this.tabControl1.ResumeLayout(false);
+            this.tabTestManager.ResumeLayout(false);
+            this.tabParameterSets.ResumeLayout(false);
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.ToolStrip mainToolstrip;
+        private System.Windows.Forms.SplitContainer splitContainer1;
+        private System.Windows.Forms.TabControl tabControl1;
+        private System.Windows.Forms.TabPage tabTestManager;
+        private TestManagerControl testManagerControl;
+        private System.Windows.Forms.TabPage tabParameterSets;
+        private System.Windows.Forms.TreeView dbTree;
+        private System.Windows.Forms.ToolStripButton btnExportDump;
+        private ParameterSetManager parameterSetManager;
+        private System.Windows.Forms.SaveFileDialog saveFileDialog;
+        private System.Windows.Forms.ToolStripLabel tslName;
+        private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/DbControl.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/DbControl.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/DbControl.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using SqliteDotNet;
+
+namespace MgTestAdmin
+{
+    public partial class DbControl : UserControl
+    {
+        private DbControl()
+        {
+            InitializeComponent();
+            this.Disposed += DbControl_Disposed;
+        }
+
+        public DbControl(string name)
+            : this()
+        {
+            tslName.Text = name;
+        }
+
+        private void DbControl_Disposed(object sender, EventArgs e)
+        {
+            _db?.Close();
+            _db = null;
+        }
+
+        private SqliteDb _db;
+
+        public void Setup(SqliteDb db, OperationProviderService opService)
+        {
+            _db = db;
+            testManagerControl.Setup(db);
+            parameterSetManager.Setup(db, false, opService);
+
+            dbTree.BeginUpdate();
+
+            // Clear the TreeView each time the method is called.
+            dbTree.Nodes.Clear();
+
+            TreeNode databaseNode = new TreeNode("Tables");
+
+            var tables = _db.GetTables();
+            foreach (var t in tables)
+            {
+                databaseNode.Nodes.Add(t);
+            }
+
+            dbTree.Nodes.Add(databaseNode);
+
+            dbTree.EndUpdate();
+        }
+
+        private void btnExportDump_Click(object sender, EventArgs e)
+        {
+            saveFileDialog.Filter = "SQLite dump (*.dump)|*.dump";
+            saveFileDialog.RestoreDirectory = true;
+
+            if (saveFileDialog.ShowDialog() == DialogResult.OK)
+            {
+                var dumpFile = saveFileDialog.FileName;
+                _db.GenerateDump(dumpFile);
+                MessageBox.Show("Dump file generated at: " + dumpFile);
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/DbControl.resx
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/DbControl.resx	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/DbControl.resx	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="mainToolstrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+  <data name="btnExportDump.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <metadata name="saveFileDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>143, 17</value>
+  </metadata>
+</root>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/MgTestAdmin.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/MgTestAdmin.csproj	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/MgTestAdmin.csproj	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,25 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <TargetFramework>net5.0-windows</TargetFramework>
+    <UseWindowsForms>true</UseWindowsForms>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <None Remove="sqlite3.exe" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="sqlite3.exe">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="ICSharpCode.TextEditor" Version="3.2.1.6466" />
+    <PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.0" />
+  </ItemGroup>
+
+</Project>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/IOperationProvider.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/IOperationProvider.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/IOperationProvider.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,22 @@
+namespace MgTestAdmin.Model
+{
+    public interface IOperationProvider
+    {
+        string Name { get; }
+
+        string Category { get; }
+
+        ParameterSet CreateDefault(int id);
+    }
+
+    public abstract class OperationProviderBase : IOperationProvider
+    {
+        public string Name => this.GetType().Name.ToUpper();
+
+        public abstract string Category { get; }
+
+        public abstract ParameterSet CreateDefault(int id);
+
+        protected Parameter OperationParameter() => new Parameter { ParamName = "OPERATION", ParamValue = this.Name };
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/Categories.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/Categories.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/Categories.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MgTestAdmin.Model.Operations
+{
+    public abstract class FeatureServiceOperationProvider : OperationProviderBase
+    {
+        public override string Category => "Feature Service";
+    }
+
+    public abstract class ResourceServiceOperationProvider : OperationProviderBase
+    {
+        public override string Category => "Resource Service";
+    }
+
+    public abstract class TileServiceOperationProvider : OperationProviderBase
+    {
+        public override string Category => "Tile Service";
+    }
+
+    public abstract class DrawingServiceOperationProvider : OperationProviderBase
+    {
+        public override string Category => "Drawing Service";
+    }
+
+    public abstract class ServerAdminOperationProvider : OperationProviderBase
+    {
+        public override string Category => "Server Admin";
+    }
+
+    public abstract class SiteServiceOperationProvider : OperationProviderBase
+    {
+        public override string Category => "Site Service";
+    }
+
+    public abstract class WebLayoutOperationProvider : OperationProviderBase
+    {
+        public override string Category => "Web Layout";
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/DrawingService.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/DrawingService.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/DrawingService.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,120 @@
+namespace MgTestAdmin.Model.Operations
+{
+    public class DescribeDrawing : DrawingServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID")
+                }
+            };
+        }
+    }
+
+    public class EnumerateDrawingLayers : DrawingServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("SECTION")
+                }
+            };
+        }
+    }
+
+    public class GetDrawingLayer : DrawingServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("SECTION"),
+                    Parameter.Create("LAYER")
+                }
+            };
+        }
+    }
+
+    public class GetDrawingSection : DrawingServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("SECTION")
+                }
+            };
+        }
+    }
+
+    public class GetDrawingSectionResource : DrawingServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("RESOURCENAME")
+                }
+            };
+        }
+    }
+
+    public class EnumerateDrawingSections : DrawingServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID")
+                }
+            };
+        }
+    }
+
+    public class EnumerateDrawingSectionResources : DrawingServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("SECTION")
+                }
+            };
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/FeatureService.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/FeatureService.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/FeatureService.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,187 @@
+namespace MgTestAdmin.Model.Operations
+{
+    public class GetFeatureProviders : FeatureServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class GetProviderCapabilities : FeatureServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("PROVIDER")
+                }
+            };
+        }
+    }
+
+    public class DescribeFeatureSchema : FeatureServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("SCHEMA")
+                }
+            };
+        }
+    }
+
+    public class SelectFeatures : FeatureServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("CLASSNAME")
+                }
+            };
+        }
+    }
+
+    public class SelectAggregates : FeatureServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("CLASSNAME")
+                }
+            };
+        }
+    }
+
+    public class ExecuteSqlQuery : FeatureServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("SQL")
+                }
+            };
+        }
+    }
+
+    public class GetSpatialContexts : FeatureServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("ACTIVEONLY")
+                }
+            };
+        }
+    }
+
+    public class GetLongTransactions : FeatureServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("ACTIVEONLY")
+                }
+            };
+        }
+    }
+
+    public class SetLongTransaction : FeatureServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("LONGTRANSACTIONNAME"),
+                    Parameter.Create("CREATESESSION")
+                }
+            };
+        }
+    }
+
+    public class TestConnection : FeatureServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID")
+                }
+            };
+        }
+    }
+
+    public class GetConnectionPropertyValues : FeatureServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("PROPERTY"),
+                    Parameter.Create("CONNECTIONSTRING"),
+                    Parameter.Create("PROVIDER")
+                }
+            };
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/ResourceService.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/ResourceService.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/ResourceService.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,290 @@
+namespace MgTestAdmin.Model.Operations
+{
+    public class DeleteResource : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID")
+                }
+            };
+        }
+    }
+
+    public class SetResource : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new []
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("CONTENT"),
+                    Parameter.Create("HEADER")
+                }
+            };
+        }
+    }
+
+    public class SetResourceData : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("DATANAME"),
+                    Parameter.Create("DATATYPE"),
+                    Parameter.Create("DATALENGTH"),
+                    Parameter.Create("DATA")
+                }
+            };
+        }
+    }
+
+    public class GetRepositoryContent : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID")
+                }
+            };
+        }
+    }
+
+    public class UpdateRepository : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("HEADER"),
+                    Parameter.Create("CONTENT")
+                }
+            };
+        }
+    }
+
+    public class GetResourceContent : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("PREPROCESS")
+                }
+            };
+        }
+    }
+
+    public class GetResourceHeader : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID")
+                }
+            };
+        }
+    }
+
+    public class GetRepositoryHeader : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID")
+                }
+            };
+        }
+    }
+
+    public class EnumerateResources : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("TYPE"),
+                    Parameter.Create("DEPTH")
+                }
+            };
+        }
+    }
+
+    public class EnumerateResourceData : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID")
+                }
+            };
+        }
+    }
+
+    public class EnumerateResourceReferences : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID")
+                }
+            };
+        }
+    }
+
+    public class RenameResourceData : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("OLDDATANAME"),
+                    Parameter.Create("NEWDATANAME")
+                }
+            };
+        }
+    }
+
+    public class MoveResource : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("SOURCE"),
+                    Parameter.Create("DESTINATION")
+                }
+            };
+        }
+    }
+
+    public class CopyResource : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("SOURCE"),
+                    Parameter.Create("DESTINATION")
+                }
+            };
+        }
+    }
+
+    public class ChangeResourceOwner : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID"),
+                    Parameter.Create("OWNER")
+                }
+            };
+        }
+    }
+
+    public class InheritPermissionsFrom : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("RESOURCEID")
+                }
+            };
+        }
+    }
+
+    public class ApplyResourcePackage : ResourceServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("PACKAGE")
+                }
+            };
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/ServerAdmin.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/ServerAdmin.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/ServerAdmin.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,217 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MgTestAdmin.Model.Operations
+{
+    public class GetInfo : ServerAdminOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("SECTION")
+                }
+            };
+        }
+    }
+
+    public class GetStatistics : ServerAdminOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("SECTION")
+                }
+            };
+        }
+    }
+
+    public class Offline : ServerAdminOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class Online : ServerAdminOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class ClearLog : ServerAdminOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("LOGTYPE")
+                }
+            };
+        }
+    }
+
+    public class GetLog : ServerAdminOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("LOGTYPE")
+                }
+            };
+        }
+    }
+
+    public class GetLogByDate : ServerAdminOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("LOGTYPE"),
+                    Parameter.Create("FROMDATE"),
+                    Parameter.Create("TODATE")
+                }
+            };
+        }
+    }
+
+    public class DeleteLog : ServerAdminOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("FILENAME")
+                }
+            };
+        }
+    }
+
+    public class LoadPackage : ServerAdminOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("PACKAGENAME")
+                }
+            };
+        }
+    }
+
+    public class GetPackageLog : ServerAdminOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("PACKAGENAME")
+                }
+            };
+        }
+    }
+
+    public class GetPackageStatus : ServerAdminOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("PACKAGENAME")
+                }
+            };
+        }
+    }
+
+    public class RenameLog : ServerAdminOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("OLDFILENAME"),
+                    Parameter.Create("NEWFILENAME")
+                }
+            };
+        }
+    }
+
+    public class DeletePackage : ServerAdminOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("PACKAGENAME")
+                }
+            };
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/SiteService.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/SiteService.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/SiteService.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,212 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MgTestAdmin.Model.Operations
+{
+    public class EnumerateUsers : SiteServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class EnumerateGroups : SiteServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class AddUser : SiteServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("USERID"),
+                    Parameter.Create("USERNAME"),
+                    Parameter.Create("PASSWORD"),
+                    Parameter.Create("DESCRIPTION")
+                }
+            };
+        }
+    }
+
+    public class UpdateUser : SiteServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("USERID"),
+                    Parameter.Create("NEWUSERID"),
+                    Parameter.Create("NEWUSERNAME"),
+                    Parameter.Create("NEWPASSWORD"),
+                    Parameter.Create("NEWDESCRIPTION")
+                }
+            };
+        }
+    }
+
+    public class DeleteUsers : SiteServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("USERS")
+                }
+            };
+        }
+    }
+
+    public class AddGroup : SiteServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("GROUP"),
+                    Parameter.Create("DESCRIPTION")
+                }
+            };
+        }
+    }
+
+    public class UpdateGroup : SiteServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("GROUP"),
+                    Parameter.Create("NEWGROUP"),
+                    Parameter.Create("DESCRIPTION")
+                }
+            };
+        }
+    }
+
+    public class DeleteGroups : SiteServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("GROUPS")
+                }
+            };
+        }
+    }
+
+    public class GrantGroupMembershipToUsers : SiteServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("GROUPS"),
+                    Parameter.Create("USERS")
+                }
+            };
+        }
+    }
+
+    public class GrantRoleMembershipToUsers : SiteServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("USERS"),
+                    Parameter.Create("ROLES")
+                }
+            };
+        }
+    }
+
+    public class RevokeGroupMembershipFromUsers : SiteServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("GROUPS"),
+                    Parameter.Create("USERS")
+                }
+            };
+        }
+    }
+
+    public class RevokeRoleMembershipFromUsers : SiteServiceOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("USERS"),
+                    Parameter.Create("ROLES")
+                }
+            };
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/WebLayout.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/WebLayout.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/Operations/WebLayout.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,370 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MgTestAdmin.Model.Operations
+{
+    public class WL_GetTitle : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new []
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_GetMapDefinition : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_GetScale : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_GetCenter : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_ShowToolbar : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_ShowStatusBar : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_ShowTaskPane : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_ShowTaskBar : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_ShowLegend : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_ShowProperties : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_GetTaskPaneWidth : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_GetInformationPaneWidth : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_GetInitialTaskUrl : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_ShowContextMenu : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_TestUiItem : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter(),
+                    Parameter.Create("CONTAINER"),
+                    Parameter.Create("INDEX")
+                }
+            };
+        }
+    }
+
+    public class WL_HomeTooltip : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_HomeDescription : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_BackTooltip : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_BackDescription : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_ForwardTooltip : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_ForwardDescription : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_TasksName : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_TasksTooltip : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+
+    public class WL_TasksDescription : WebLayoutOperationProvider
+    {
+        public override ParameterSet CreateDefault(int id)
+        {
+            return new ParameterSet
+            {
+                Id = id,
+                Params = new[]
+                {
+                    OperationParameter()
+                }
+            };
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/ParamSet.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/ParamSet.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/ParamSet.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,30 @@
+namespace MgTestAdmin.Model
+{
+    public class ParameterSetInfo
+    {
+        public int Id { get; set; }
+
+        public string Operation { get; set; }
+
+        public override string ToString() => $"{Id}: {Operation}";
+    }
+
+    /// <summary>
+    /// Describes an operation to be executed
+    /// </summary>
+    public class ParameterSet
+    {
+        public int Id { get; set; }
+
+        public Parameter[] Params { get; set; }
+    }
+
+    public class Parameter
+    {
+        public string ParamName { get; set; }
+
+        public string ParamValue { get; set; }
+
+        public static Parameter Create(string name) => new Parameter { ParamName = name, ParamValue = string.Empty };
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/TestCase.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/TestCase.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/TestCase.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MgTestAdmin.Model
+{
+    public class TestCaseInfo
+    {
+        /// <summary>
+        /// The name of the test
+        /// </summary>
+        public string TestName { get; set; }
+
+        /// <summary>
+        /// The order when this test case will be executed
+        /// </summary>
+        public int ExecuteSequence { get; set; }
+
+        public override string ToString() => $"{ExecuteSequence}: {TestName}";
+    }
+
+    public class TestCase
+    {
+        /// <summary>
+        /// The name of the test
+        /// </summary>
+        public string TestName { get; set; }
+
+        /// <summary>
+        /// The order when this test case will be executed
+        /// </summary>
+        public int ExecuteSequence { get; set; }
+
+        /// <summary>
+        /// The description of the test case
+        /// </summary>
+        public string Description { get; set; }
+
+        /// <summary>
+        /// Pre-requisites
+        /// </summary>
+        public string Prerequsite { get; set; }
+
+        /// <summary>
+        /// The type of test. "Http" or "Api"
+        /// </summary>
+        public string TestType { get; set; }
+
+        /// <summary>
+        /// The parameter sets to execute as part of this test case
+        /// </summary>
+        public int[] ParamSets { get; set; }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/TestResult.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/TestResult.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Model/TestResult.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MgTestAdmin.Model
+{
+    /// <summary>
+    /// Describes the result of an executed parameter set
+    /// </summary>
+    public class TestResult
+    {
+        /// <summary>
+        /// The parameter set that was executed
+        /// </summary>
+        public int ParamSet { get; set; }
+
+        /// <summary>
+        /// The description of the test result
+        /// </summary>
+        public string Description { get; set; }
+
+        /// <summary>
+        /// The content type of the test result
+        /// </summary>
+        public string ContentType { get; set; }
+
+        /// <summary>
+        /// The result content
+        /// </summary>
+        public string Result { get; set; }
+    }
+
+    public class TestResultSet
+    {
+        public TestResult ApiResult { get; set; }
+
+        public TestResult HttpResult { get; set; }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationBrowserControl.Designer.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationBrowserControl.Designer.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationBrowserControl.Designer.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,79 @@
+namespace MgTestAdmin
+{
+    partial class OperationBrowserControl
+    {
+        /// <summary> 
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Component Designer generated code
+
+        /// <summary> 
+        /// Required method for Designer support - do not modify 
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+            this.tree = new System.Windows.Forms.TreeView();
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+            this.splitContainer1.Panel1.SuspendLayout();
+            this.splitContainer1.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // splitContainer1
+            // 
+            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.splitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
+            this.splitContainer1.Location = new System.Drawing.Point(0, 0);
+            this.splitContainer1.Name = "splitContainer1";
+            // 
+            // splitContainer1.Panel1
+            // 
+            this.splitContainer1.Panel1.Controls.Add(this.tree);
+            this.splitContainer1.Size = new System.Drawing.Size(791, 433);
+            this.splitContainer1.SplitterDistance = 239;
+            this.splitContainer1.TabIndex = 0;
+            // 
+            // treeView1
+            // 
+            this.tree.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.tree.Location = new System.Drawing.Point(0, 0);
+            this.tree.Name = "treeView1";
+            this.tree.Size = new System.Drawing.Size(239, 433);
+            this.tree.TabIndex = 0;
+            // 
+            // OperationBrowserControl
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.Controls.Add(this.splitContainer1);
+            this.Name = "OperationBrowserControl";
+            this.Size = new System.Drawing.Size(791, 433);
+            this.splitContainer1.Panel1.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+            this.splitContainer1.ResumeLayout(false);
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.SplitContainer splitContainer1;
+        private System.Windows.Forms.TreeView tree;
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationBrowserControl.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationBrowserControl.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationBrowserControl.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace MgTestAdmin
+{
+    public partial class OperationBrowserControl : UserControl
+    {
+        public OperationBrowserControl()
+        {
+            InitializeComponent();
+        }
+
+        private OperationProviderService _opService;
+
+        internal void Setup(OperationProviderService opService)
+        {
+            tree.BeginUpdate();
+
+            foreach (var cat in opService.GetCategories())
+            {
+                var catNode = new TreeNode(cat);
+
+                foreach (var op in opService.GetProviders(cat))
+                {
+                    var node = new TreeNode(op.Name);
+                    node.Tag = op;
+                    catNode.Nodes.Add(node);
+                }
+
+                tree.Nodes.Add(catNode);
+            }
+
+            tree.EndUpdate();
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationBrowserControl.resx
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationBrowserControl.resx	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationBrowserControl.resx	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationProviderService.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationProviderService.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/OperationProviderService.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,32 @@
+using MgTestAdmin.Model;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MgTestAdmin
+{
+    public class OperationProviderService
+    {
+        private IOperationProvider[] _providers;
+
+        public OperationProviderService()
+        {
+            _providers = typeof(OperationProviderService)
+                .Assembly
+                .GetTypes()
+                .Where(t => t.IsClass && !t.IsAbstract && typeof(IOperationProvider).IsAssignableFrom(t))
+                .Select(t => (IOperationProvider)Activator.CreateInstance(t, true))
+                .ToArray();
+        }
+
+        public IEnumerable<string> GetCategories()
+        {
+            return _providers.GroupBy(p => p.Category).Select(g => g.Key).OrderBy(c => c);
+        }
+
+        public IEnumerable<IOperationProvider> GetProviders(string category)
+        {
+            return _providers.Where(p => p.Category == category).OrderBy(p => p.Name);
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetControl.Designer.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetControl.Designer.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetControl.Designer.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,234 @@
+namespace MgTestAdmin
+{
+    partial class ParameterSetControl
+    {
+        /// <summary> 
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Component Designer generated code
+
+        /// <summary> 
+        /// Required method for Designer support - do not modify 
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.label1 = new System.Windows.Forms.Label();
+            this.txtOperation = new System.Windows.Forms.TextBox();
+            this.label2 = new System.Windows.Forms.Label();
+            this.label3 = new System.Windows.Forms.Label();
+            this.txtParamSetId = new System.Windows.Forms.TextBox();
+            this.dgParameters = new System.Windows.Forms.DataGridView();
+            this.COL_NAME = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.COL_VALUE = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.btnSave = new System.Windows.Forms.Button();
+            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+            this.apiTestResultControl = new MgTestAdmin.TestResultControl();
+            this.httpTestResultControl = new MgTestAdmin.TestResultControl();
+            this.label4 = new System.Windows.Forms.Label();
+            this.label5 = new System.Windows.Forms.Label();
+            ((System.ComponentModel.ISupportInitialize)(this.dgParameters)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+            this.splitContainer1.Panel1.SuspendLayout();
+            this.splitContainer1.Panel2.SuspendLayout();
+            this.splitContainer1.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // label1
+            // 
+            this.label1.AutoSize = true;
+            this.label1.Location = new System.Drawing.Point(21, 45);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(53, 13);
+            this.label1.TabIndex = 0;
+            this.label1.Text = "Operation";
+            // 
+            // txtOperation
+            // 
+            this.txtOperation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.txtOperation.Location = new System.Drawing.Point(133, 42);
+            this.txtOperation.Name = "txtOperation";
+            this.txtOperation.ReadOnly = true;
+            this.txtOperation.Size = new System.Drawing.Size(662, 20);
+            this.txtOperation.TabIndex = 1;
+            // 
+            // label2
+            // 
+            this.label2.AutoSize = true;
+            this.label2.Location = new System.Drawing.Point(21, 68);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(60, 13);
+            this.label2.TabIndex = 2;
+            this.label2.Text = "Parameters";
+            // 
+            // label3
+            // 
+            this.label3.AutoSize = true;
+            this.label3.Location = new System.Drawing.Point(21, 19);
+            this.label3.Name = "label3";
+            this.label3.Size = new System.Drawing.Size(88, 13);
+            this.label3.TabIndex = 3;
+            this.label3.Text = "Parameter Set ID";
+            // 
+            // txtParamSetId
+            // 
+            this.txtParamSetId.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.txtParamSetId.Location = new System.Drawing.Point(133, 16);
+            this.txtParamSetId.Name = "txtParamSetId";
+            this.txtParamSetId.ReadOnly = true;
+            this.txtParamSetId.Size = new System.Drawing.Size(662, 20);
+            this.txtParamSetId.TabIndex = 4;
+            // 
+            // dgParameters
+            // 
+            this.dgParameters.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.dgParameters.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+            this.dgParameters.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+            this.COL_NAME,
+            this.COL_VALUE});
+            this.dgParameters.Location = new System.Drawing.Point(133, 68);
+            this.dgParameters.Name = "dgParameters";
+            this.dgParameters.Size = new System.Drawing.Size(662, 121);
+            this.dgParameters.TabIndex = 5;
+            // 
+            // COL_NAME
+            // 
+            this.COL_NAME.DataPropertyName = "ParamName";
+            this.COL_NAME.HeaderText = "Name";
+            this.COL_NAME.Name = "COL_NAME";
+            // 
+            // COL_VALUE
+            // 
+            this.COL_VALUE.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+            this.COL_VALUE.DataPropertyName = "ParamValue";
+            this.COL_VALUE.HeaderText = "Value";
+            this.COL_VALUE.Name = "COL_VALUE";
+            // 
+            // btnSave
+            // 
+            this.btnSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnSave.Location = new System.Drawing.Point(677, 465);
+            this.btnSave.Name = "btnSave";
+            this.btnSave.Size = new System.Drawing.Size(118, 23);
+            this.btnSave.TabIndex = 6;
+            this.btnSave.Text = "Save Parameter Set";
+            this.btnSave.UseVisualStyleBackColor = true;
+            this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
+            // 
+            // splitContainer1
+            // 
+            this.splitContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.splitContainer1.Location = new System.Drawing.Point(24, 199);
+            this.splitContainer1.Name = "splitContainer1";
+            // 
+            // splitContainer1.Panel1
+            // 
+            this.splitContainer1.Panel1.Controls.Add(this.label4);
+            this.splitContainer1.Panel1.Controls.Add(this.apiTestResultControl);
+            // 
+            // splitContainer1.Panel2
+            // 
+            this.splitContainer1.Panel2.Controls.Add(this.label5);
+            this.splitContainer1.Panel2.Controls.Add(this.httpTestResultControl);
+            this.splitContainer1.Size = new System.Drawing.Size(771, 260);
+            this.splitContainer1.SplitterDistance = 379;
+            this.splitContainer1.TabIndex = 9;
+            // 
+            // apiTestResultControl
+            // 
+            this.apiTestResultControl.Location = new System.Drawing.Point(0, 17);
+            this.apiTestResultControl.Name = "apiTestResultControl";
+            this.apiTestResultControl.Size = new System.Drawing.Size(379, 243);
+            this.apiTestResultControl.TabIndex = 8;
+            // 
+            // httpTestResultControl
+            // 
+            this.httpTestResultControl.Location = new System.Drawing.Point(0, 17);
+            this.httpTestResultControl.Name = "httpTestResultControl";
+            this.httpTestResultControl.Size = new System.Drawing.Size(388, 243);
+            this.httpTestResultControl.TabIndex = 9;
+            // 
+            // label4
+            // 
+            this.label4.AutoSize = true;
+            this.label4.Dock = System.Windows.Forms.DockStyle.Top;
+            this.label4.Location = new System.Drawing.Point(0, 0);
+            this.label4.Name = "label4";
+            this.label4.Size = new System.Drawing.Size(81, 13);
+            this.label4.TabIndex = 9;
+            this.label4.Text = "API Test Result";
+            // 
+            // label5
+            // 
+            this.label5.AutoSize = true;
+            this.label5.Dock = System.Windows.Forms.DockStyle.Top;
+            this.label5.Location = new System.Drawing.Point(0, 0);
+            this.label5.Name = "label5";
+            this.label5.Size = new System.Drawing.Size(93, 13);
+            this.label5.TabIndex = 10;
+            this.label5.Text = "HTTP Test Result";
+            // 
+            // ParameterSetControl
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.Controls.Add(this.splitContainer1);
+            this.Controls.Add(this.btnSave);
+            this.Controls.Add(this.dgParameters);
+            this.Controls.Add(this.txtParamSetId);
+            this.Controls.Add(this.label3);
+            this.Controls.Add(this.label2);
+            this.Controls.Add(this.txtOperation);
+            this.Controls.Add(this.label1);
+            this.Name = "ParameterSetControl";
+            this.Size = new System.Drawing.Size(818, 504);
+            ((System.ComponentModel.ISupportInitialize)(this.dgParameters)).EndInit();
+            this.splitContainer1.Panel1.ResumeLayout(false);
+            this.splitContainer1.Panel1.PerformLayout();
+            this.splitContainer1.Panel2.ResumeLayout(false);
+            this.splitContainer1.Panel2.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+            this.splitContainer1.ResumeLayout(false);
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.TextBox txtOperation;
+        private System.Windows.Forms.Label label2;
+        private System.Windows.Forms.Label label3;
+        private System.Windows.Forms.TextBox txtParamSetId;
+        private System.Windows.Forms.DataGridView dgParameters;
+        private System.Windows.Forms.Button btnSave;
+        private System.Windows.Forms.DataGridViewTextBoxColumn COL_NAME;
+        private System.Windows.Forms.DataGridViewTextBoxColumn COL_VALUE;
+        private System.Windows.Forms.SplitContainer splitContainer1;
+        private TestResultControl apiTestResultControl;
+        private TestResultControl httpTestResultControl;
+        private System.Windows.Forms.Label label4;
+        private System.Windows.Forms.Label label5;
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetControl.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetControl.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetControl.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,64 @@
+using MgTestAdmin.Model;
+using SqliteDotNet;
+using System.ComponentModel;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace MgTestAdmin
+{
+    public partial class ParameterSetControl : UserControl
+    {
+        public ParameterSetControl()
+        {
+            InitializeComponent();
+            _params = new BindingList<Parameter>();
+            dgParameters.DataSource = _params;
+        }
+
+        private BindingList<Parameter> _params;
+
+        private SqliteDb _db;
+        private ParameterSet _ps;
+
+        public ParameterSetControl Attach(ParameterSet ps, SqliteDb db, bool freeze)
+        {
+            _params.Clear();
+
+            _ps = ps;
+            _db = db;
+
+            if (freeze)
+            {
+                COL_NAME.ReadOnly = true;
+                COL_VALUE.ReadOnly = true;
+                btnSave.Visible = false;
+            }
+
+            var op = ps.Params.FirstOrDefault(p => p.ParamName == "OPERATION");
+            var parms = ps.Params.Where(p => p.ParamName != "OPERATION");
+
+            txtParamSetId.Text = ps.Id.ToString();
+
+            if (op != null)
+            {
+                txtOperation.Text = op.ParamValue;
+            }
+
+            foreach (var p in parms)
+            {
+                _params.Add(p);
+            }
+
+            var tr = _db.GetTestResultSet(ps.Id);
+            apiTestResultControl.Setup(_db, tr?.ApiResult, freeze);
+            httpTestResultControl.Setup(_db, tr?.HttpResult, freeze);
+
+            return this;
+        }
+
+        private void btnSave_Click(object sender, System.EventArgs e)
+        {
+            MessageBox.Show("Not implemented yet");
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetControl.resx
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetControl.resx	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetControl.resx	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="COL_NAME.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="COL_VALUE.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+</root>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetEditor.Designer.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetEditor.Designer.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetEditor.Designer.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,251 @@
+namespace MgTestAdmin
+{
+    partial class ParameterSetEditor
+    {
+        /// <summary> 
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Component Designer generated code
+
+        /// <summary> 
+        /// Required method for Designer support - do not modify 
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ParameterSetEditor));
+            this.groupBox1 = new System.Windows.Forms.GroupBox();
+            this.lstParamSets = new System.Windows.Forms.ListBox();
+            this.toolStrip1 = new System.Windows.Forms.ToolStrip();
+            this.tsAdd = new System.Windows.Forms.ToolStripButton();
+            this.tsDelete = new System.Windows.Forms.ToolStripButton();
+            this.groupBox2 = new System.Windows.Forms.GroupBox();
+            this.dgParams = new System.Windows.Forms.DataGridView();
+            this.COL_NAME = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.COL_VALUE = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+            this.groupBox3 = new System.Windows.Forms.GroupBox();
+            this.groupBox4 = new System.Windows.Forms.GroupBox();
+            this.apiTestResultControl = new MgTestAdmin.TestResultControl();
+            this.httpTestResultControl = new MgTestAdmin.TestResultControl();
+            this.groupBox1.SuspendLayout();
+            this.toolStrip1.SuspendLayout();
+            this.groupBox2.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.dgParams)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+            this.splitContainer1.Panel1.SuspendLayout();
+            this.splitContainer1.Panel2.SuspendLayout();
+            this.splitContainer1.SuspendLayout();
+            this.groupBox3.SuspendLayout();
+            this.groupBox4.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // groupBox1
+            // 
+            this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
+            this.groupBox1.Controls.Add(this.lstParamSets);
+            this.groupBox1.Controls.Add(this.toolStrip1);
+            this.groupBox1.Location = new System.Drawing.Point(3, 3);
+            this.groupBox1.Name = "groupBox1";
+            this.groupBox1.Size = new System.Drawing.Size(154, 421);
+            this.groupBox1.TabIndex = 0;
+            this.groupBox1.TabStop = false;
+            this.groupBox1.Text = "Parameter Sets";
+            // 
+            // lstParamSets
+            // 
+            this.lstParamSets.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.lstParamSets.FormattingEnabled = true;
+            this.lstParamSets.Location = new System.Drawing.Point(3, 41);
+            this.lstParamSets.Name = "lstParamSets";
+            this.lstParamSets.Size = new System.Drawing.Size(148, 377);
+            this.lstParamSets.TabIndex = 1;
+            this.lstParamSets.SelectedIndexChanged += new System.EventHandler(this.lstParamSets_SelectedIndexChanged);
+            // 
+            // toolStrip1
+            // 
+            this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.tsAdd,
+            this.tsDelete});
+            this.toolStrip1.Location = new System.Drawing.Point(3, 16);
+            this.toolStrip1.Name = "toolStrip1";
+            this.toolStrip1.Size = new System.Drawing.Size(148, 25);
+            this.toolStrip1.TabIndex = 0;
+            this.toolStrip1.Text = "toolStrip1";
+            // 
+            // tsAdd
+            // 
+            this.tsAdd.Image = ((System.Drawing.Image)(resources.GetObject("tsAdd.Image")));
+            this.tsAdd.ImageTransparentColor = System.Drawing.Color.Magenta;
+            this.tsAdd.Name = "tsAdd";
+            this.tsAdd.Size = new System.Drawing.Size(49, 22);
+            this.tsAdd.Text = "Add";
+            this.tsAdd.Click += new System.EventHandler(this.tsAdd_Click);
+            // 
+            // tsDelete
+            // 
+            this.tsDelete.Image = ((System.Drawing.Image)(resources.GetObject("tsDelete.Image")));
+            this.tsDelete.ImageTransparentColor = System.Drawing.Color.Magenta;
+            this.tsDelete.Name = "tsDelete";
+            this.tsDelete.Size = new System.Drawing.Size(60, 22);
+            this.tsDelete.Text = "Delete";
+            this.tsDelete.Click += new System.EventHandler(this.tsDelete_Click);
+            // 
+            // groupBox2
+            // 
+            this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.groupBox2.Controls.Add(this.dgParams);
+            this.groupBox2.Location = new System.Drawing.Point(163, 3);
+            this.groupBox2.Name = "groupBox2";
+            this.groupBox2.Size = new System.Drawing.Size(445, 156);
+            this.groupBox2.TabIndex = 1;
+            this.groupBox2.TabStop = false;
+            this.groupBox2.Text = "Parameter Set Details";
+            // 
+            // dgParams
+            // 
+            this.dgParams.AllowUserToAddRows = false;
+            this.dgParams.AllowUserToDeleteRows = false;
+            this.dgParams.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+            this.dgParams.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+            this.COL_NAME,
+            this.COL_VALUE});
+            this.dgParams.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.dgParams.Location = new System.Drawing.Point(3, 16);
+            this.dgParams.Name = "dgParams";
+            this.dgParams.ReadOnly = true;
+            this.dgParams.Size = new System.Drawing.Size(439, 137);
+            this.dgParams.TabIndex = 0;
+            // 
+            // COL_NAME
+            // 
+            this.COL_NAME.DataPropertyName = "ParamName";
+            this.COL_NAME.HeaderText = "Name";
+            this.COL_NAME.Name = "COL_NAME";
+            this.COL_NAME.ReadOnly = true;
+            // 
+            // COL_VALUE
+            // 
+            this.COL_VALUE.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+            this.COL_VALUE.DataPropertyName = "ParamValue";
+            this.COL_VALUE.HeaderText = "Value";
+            this.COL_VALUE.Name = "COL_VALUE";
+            this.COL_VALUE.ReadOnly = true;
+            // 
+            // splitContainer1
+            // 
+            this.splitContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.splitContainer1.Location = new System.Drawing.Point(163, 162);
+            this.splitContainer1.Name = "splitContainer1";
+            // 
+            // splitContainer1.Panel1
+            // 
+            this.splitContainer1.Panel1.Controls.Add(this.groupBox3);
+            // 
+            // splitContainer1.Panel2
+            // 
+            this.splitContainer1.Panel2.Controls.Add(this.groupBox4);
+            this.splitContainer1.Size = new System.Drawing.Size(442, 259);
+            this.splitContainer1.SplitterDistance = 219;
+            this.splitContainer1.TabIndex = 2;
+            // 
+            // groupBox3
+            // 
+            this.groupBox3.Controls.Add(this.apiTestResultControl);
+            this.groupBox3.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.groupBox3.Location = new System.Drawing.Point(0, 0);
+            this.groupBox3.Name = "groupBox3";
+            this.groupBox3.Size = new System.Drawing.Size(219, 259);
+            this.groupBox3.TabIndex = 0;
+            this.groupBox3.TabStop = false;
+            this.groupBox3.Text = "API Test Result";
+            // 
+            // groupBox4
+            // 
+            this.groupBox4.Controls.Add(this.httpTestResultControl);
+            this.groupBox4.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.groupBox4.Location = new System.Drawing.Point(0, 0);
+            this.groupBox4.Name = "groupBox4";
+            this.groupBox4.Size = new System.Drawing.Size(219, 259);
+            this.groupBox4.TabIndex = 0;
+            this.groupBox4.TabStop = false;
+            this.groupBox4.Text = "HTTP Test Result";
+            // 
+            // apiTestResultControl
+            // 
+            this.apiTestResultControl.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.apiTestResultControl.Location = new System.Drawing.Point(3, 16);
+            this.apiTestResultControl.Name = "apiTestResultControl";
+            this.apiTestResultControl.Size = new System.Drawing.Size(213, 240);
+            this.apiTestResultControl.TabIndex = 0;
+            // 
+            // httpTestResultControl
+            // 
+            this.httpTestResultControl.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.httpTestResultControl.Location = new System.Drawing.Point(3, 16);
+            this.httpTestResultControl.Name = "httpTestResultControl";
+            this.httpTestResultControl.Size = new System.Drawing.Size(213, 240);
+            this.httpTestResultControl.TabIndex = 0;
+            // 
+            // ParameterSetEditor
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.Controls.Add(this.splitContainer1);
+            this.Controls.Add(this.groupBox2);
+            this.Controls.Add(this.groupBox1);
+            this.Name = "ParameterSetEditor";
+            this.Size = new System.Drawing.Size(611, 427);
+            this.groupBox1.ResumeLayout(false);
+            this.groupBox1.PerformLayout();
+            this.toolStrip1.ResumeLayout(false);
+            this.toolStrip1.PerformLayout();
+            this.groupBox2.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.dgParams)).EndInit();
+            this.splitContainer1.Panel1.ResumeLayout(false);
+            this.splitContainer1.Panel2.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+            this.splitContainer1.ResumeLayout(false);
+            this.groupBox3.ResumeLayout(false);
+            this.groupBox4.ResumeLayout(false);
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.GroupBox groupBox1;
+        private System.Windows.Forms.ListBox lstParamSets;
+        private System.Windows.Forms.ToolStrip toolStrip1;
+        private System.Windows.Forms.ToolStripButton tsAdd;
+        private System.Windows.Forms.ToolStripButton tsDelete;
+        private System.Windows.Forms.GroupBox groupBox2;
+        private System.Windows.Forms.DataGridView dgParams;
+        private System.Windows.Forms.DataGridViewTextBoxColumn COL_NAME;
+        private System.Windows.Forms.DataGridViewTextBoxColumn COL_VALUE;
+        private System.Windows.Forms.SplitContainer splitContainer1;
+        private System.Windows.Forms.GroupBox groupBox3;
+        private TestResultControl apiTestResultControl;
+        private System.Windows.Forms.GroupBox groupBox4;
+        private TestResultControl httpTestResultControl;
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetEditor.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetEditor.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetEditor.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,91 @@
+using SqliteDotNet;
+using System;
+using System.ComponentModel;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace MgTestAdmin
+{
+    public partial class ParameterSetEditor : UserControl
+    {
+        private BindingList<int> _paramSets;
+
+        public ParameterSetEditor()
+        {
+            InitializeComponent();
+            _paramSets = new BindingList<int>();
+            lstParamSets.DataSource = _paramSets;
+        }
+
+        private SqliteDb _db;
+
+        public ParameterSetEditor Attach(int[] paramSets, SqliteDb db)
+        {
+            _db = db;
+            this.ParamSets = paramSets;
+            return this;
+        }
+
+        public int[] ParamSets
+        {
+            get { return _paramSets.ToArray(); }
+            set
+            {
+                _paramSets.Clear();
+                lstParamSets.ClearSelected();
+                if (value != null)
+                {
+                    foreach (var i in value)
+                    {
+                        _paramSets.Add(i);
+                    }
+                    if (_paramSets.Count > 0)
+                        lstParamSets.SelectedIndex = 0;
+                }
+            }
+        }
+
+        private void lstParamSets_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            if (lstParamSets.SelectedItem != null)
+            {
+                int psid = (int)lstParamSets.SelectedItem;
+                var ps = _db.GetParamSet(psid);
+                if (ps != null)
+                {
+                    dgParams.DataSource = ps.Params;
+                    var tr = _db.GetTestResultSet(psid);
+                    if (tr != null)
+                    {
+                        apiTestResultControl.Setup(_db, tr.ApiResult, true);
+                        httpTestResultControl.Setup(_db, tr.HttpResult, true);
+                    }
+                }
+            }
+        }
+
+        private void tsDelete_Click(object sender, EventArgs e)
+        {
+            if (lstParamSets.SelectedItem != null)
+            {
+                int psid = (int)lstParamSets.SelectedItem;
+                _paramSets.Remove(psid);
+            }
+        }
+
+        private void tsAdd_Click(object sender, EventArgs e)
+        {
+            using (var picker = new ParameterSetPicker(_db))
+            {
+                if (picker.ShowDialog() == DialogResult.OK)
+                {
+                    var ps = picker.SelectedParameterSet;
+                    if (ps != null)
+                    {
+                        _paramSets.Add(ps.Id);
+                    }
+                }
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetEditor.resx
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetEditor.resx	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetEditor.resx	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+  <data name="tsAdd.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="tsDelete.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <metadata name="COL_NAME.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="COL_VALUE.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+</root>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetManager.Designer.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetManager.Designer.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetManager.Designer.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,136 @@
+namespace MgTestAdmin
+{
+    partial class ParameterSetManager
+    {
+        /// <summary> 
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Component Designer generated code
+
+        /// <summary> 
+        /// Required method for Designer support - do not modify 
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ParameterSetManager));
+            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+            this.groupBox1 = new System.Windows.Forms.GroupBox();
+            this.lstParamSets = new System.Windows.Forms.ListBox();
+            this.mainToolstrip = new System.Windows.Forms.ToolStrip();
+            this.tbDelete = new System.Windows.Forms.ToolStripButton();
+            this.tbAdd = new System.Windows.Forms.ToolStripDropDownButton();
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+            this.splitContainer1.Panel1.SuspendLayout();
+            this.splitContainer1.SuspendLayout();
+            this.groupBox1.SuspendLayout();
+            this.mainToolstrip.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // splitContainer1
+            // 
+            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.splitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
+            this.splitContainer1.Location = new System.Drawing.Point(0, 0);
+            this.splitContainer1.Name = "splitContainer1";
+            // 
+            // splitContainer1.Panel1
+            // 
+            this.splitContainer1.Panel1.Controls.Add(this.groupBox1);
+            this.splitContainer1.Size = new System.Drawing.Size(965, 466);
+            this.splitContainer1.SplitterDistance = 246;
+            this.splitContainer1.TabIndex = 0;
+            // 
+            // groupBox1
+            // 
+            this.groupBox1.Controls.Add(this.lstParamSets);
+            this.groupBox1.Controls.Add(this.mainToolstrip);
+            this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.groupBox1.Location = new System.Drawing.Point(0, 0);
+            this.groupBox1.Name = "groupBox1";
+            this.groupBox1.Size = new System.Drawing.Size(246, 466);
+            this.groupBox1.TabIndex = 0;
+            this.groupBox1.TabStop = false;
+            this.groupBox1.Text = "Current Parameter Sets";
+            // 
+            // lstParamSets
+            // 
+            this.lstParamSets.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.lstParamSets.FormattingEnabled = true;
+            this.lstParamSets.Location = new System.Drawing.Point(3, 41);
+            this.lstParamSets.Name = "lstParamSets";
+            this.lstParamSets.Size = new System.Drawing.Size(240, 422);
+            this.lstParamSets.TabIndex = 1;
+            this.lstParamSets.SelectedIndexChanged += new System.EventHandler(this.lstParamSets_SelectedIndexChanged);
+            // 
+            // mainToolstrip
+            // 
+            this.mainToolstrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.tbAdd,
+            this.tbDelete});
+            this.mainToolstrip.Location = new System.Drawing.Point(3, 16);
+            this.mainToolstrip.Name = "mainToolstrip";
+            this.mainToolstrip.Size = new System.Drawing.Size(240, 25);
+            this.mainToolstrip.TabIndex = 0;
+            this.mainToolstrip.Text = "toolStrip1";
+            // 
+            // tbDelete
+            // 
+            this.tbDelete.Image = ((System.Drawing.Image)(resources.GetObject("tbDelete.Image")));
+            this.tbDelete.ImageTransparentColor = System.Drawing.Color.Magenta;
+            this.tbDelete.Name = "tbDelete";
+            this.tbDelete.Size = new System.Drawing.Size(60, 22);
+            this.tbDelete.Text = "Delete";
+            this.tbDelete.Click += new System.EventHandler(this.tbDelete_Click);
+            // 
+            // tbAdd
+            // 
+            this.tbAdd.Image = ((System.Drawing.Image)(resources.GetObject("tbAdd.Image")));
+            this.tbAdd.ImageTransparentColor = System.Drawing.Color.Magenta;
+            this.tbAdd.Name = "tbAdd";
+            this.tbAdd.Size = new System.Drawing.Size(58, 22);
+            this.tbAdd.Text = "Add";
+            // 
+            // ParameterSetManager
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.Controls.Add(this.splitContainer1);
+            this.Name = "ParameterSetManager";
+            this.Size = new System.Drawing.Size(965, 466);
+            this.splitContainer1.Panel1.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+            this.splitContainer1.ResumeLayout(false);
+            this.groupBox1.ResumeLayout(false);
+            this.groupBox1.PerformLayout();
+            this.mainToolstrip.ResumeLayout(false);
+            this.mainToolstrip.PerformLayout();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.SplitContainer splitContainer1;
+        private System.Windows.Forms.GroupBox groupBox1;
+        private System.Windows.Forms.ToolStrip mainToolstrip;
+        private System.Windows.Forms.ListBox lstParamSets;
+        private System.Windows.Forms.ToolStripButton tbDelete;
+        private System.Windows.Forms.ToolStripDropDownButton tbAdd;
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetManager.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetManager.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetManager.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,119 @@
+using MgTestAdmin.Model;
+using SqliteDotNet;
+using System;
+using System.Windows.Forms;
+
+namespace MgTestAdmin
+{
+    public partial class ParameterSetManager : UserControl
+    {
+        public ParameterSetManager()
+        {
+            InitializeComponent();
+        }
+
+        public event EventHandler SelectedParamSetChanged;
+
+        public ParameterSetInfo SelectedParamSet
+        {
+            get
+            {
+                if (lstParamSets.SelectedItem != null)
+                    return (ParameterSetInfo)lstParamSets.SelectedItem;
+                return null;
+            }
+        }
+
+        private SqliteDb _db;
+        private bool _freeze;
+        private OperationProviderService _opService;
+
+        public void Setup(SqliteDb db, bool freeze, OperationProviderService opService)
+        {
+            _db = db;
+            _freeze = freeze;
+            _opService = opService;
+
+            tbAdd.DropDownItems.Clear();
+            if (_opService != null)
+            {
+                foreach (var cat in _opService.GetCategories())
+                {
+                    var btn = new ToolStripMenuItem(cat);
+                    tbAdd.DropDownItems.Add(btn);
+                    foreach (var op in _opService.GetProviders(cat))
+                    {
+                        var opBtn = new ToolStripMenuItem(op.Name);
+                        opBtn.Tag = op;
+                        opBtn.Click += OpBtn_Click;
+                        btn.DropDownItems.Add(opBtn);
+                    }
+                }
+            }
+            mainToolstrip.Visible = !_freeze;
+
+            lstParamSets.DataSource = _db.GetParamSets();
+            //If we have any tests in the list select the first one by default
+            if (lstParamSets.Items.Count > 0)
+            {
+                lstParamSets.SelectedIndex = 0;
+            }
+            tbDelete.Enabled = lstParamSets.SelectedIndex >= 0;
+        }
+
+        private void OpBtn_Click(object sender, EventArgs e)
+        {
+            var op = (sender as ToolStripMenuItem)?.Tag as IOperationProvider;
+            if (op != null)
+            {
+                lstParamSets.ClearSelected();
+                var ps = op.CreateDefault(_db.GetNewParamSetId());
+                this.LoadMainPanel(new ParameterSetControl().Attach(ps, _db, _freeze));
+            }
+        }
+
+        private void tbDelete_Click(object sender, EventArgs e)
+        {
+            MessageBox.Show("Not implemented yet");
+        }
+
+        private void lstParamSets_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            tbDelete.Enabled = lstParamSets.SelectedIndex >= 0;
+            if (lstParamSets.SelectedItem != null)
+            {
+                var psi = (ParameterSetInfo)lstParamSets.SelectedItem;
+                var ps = _db.GetParamSet(psi.Id);
+                if (ps != null)
+                {
+                    this.LoadMainPanel(new ParameterSetControl().Attach(ps, _db, _freeze));
+                    this.SelectedParamSetChanged?.Invoke(this, EventArgs.Empty);
+                }
+            }
+        }
+
+        private void ClearMainPanel()
+        {
+            foreach (Control c in splitContainer1.Panel2.Controls)
+            {
+                c.Dispose();
+            }
+            splitContainer1.Panel2.Controls.Clear();
+        }
+
+        private void LoadMainPanel(Control c)
+        {
+            this.SuspendLayout();
+            try
+            {
+                this.ClearMainPanel();
+                c.Dock = DockStyle.Fill;
+                splitContainer1.Panel2.Controls.Add(c);
+            }
+            finally
+            {
+                this.ResumeLayout();
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetManager.resx
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetManager.resx	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetManager.resx	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="mainToolstrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+  <data name="tbAdd.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="tbDelete.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+</root>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetPicker.Designer.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetPicker.Designer.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetPicker.Designer.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,107 @@
+namespace MgTestAdmin
+{
+    partial class ParameterSetPicker
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.btnOK = new System.Windows.Forms.Button();
+            this.btnCancel = new System.Windows.Forms.Button();
+            this.parameterSetManager = new MgTestAdmin.ParameterSetManager();
+            this.label1 = new System.Windows.Forms.Label();
+            this.SuspendLayout();
+            // 
+            // btnOK
+            // 
+            this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnOK.Location = new System.Drawing.Point(840, 695);
+            this.btnOK.Name = "btnOK";
+            this.btnOK.Size = new System.Drawing.Size(75, 23);
+            this.btnOK.TabIndex = 1;
+            this.btnOK.Text = "OK";
+            this.btnOK.UseVisualStyleBackColor = true;
+            this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
+            // 
+            // btnCancel
+            // 
+            this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+            this.btnCancel.Location = new System.Drawing.Point(921, 695);
+            this.btnCancel.Name = "btnCancel";
+            this.btnCancel.Size = new System.Drawing.Size(75, 23);
+            this.btnCancel.TabIndex = 2;
+            this.btnCancel.Text = "Cancel";
+            this.btnCancel.UseVisualStyleBackColor = true;
+            this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
+            // 
+            // parameterSetManager
+            // 
+            this.parameterSetManager.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.parameterSetManager.Location = new System.Drawing.Point(12, 13);
+            this.parameterSetManager.Name = "parameterSetManager";
+            this.parameterSetManager.Size = new System.Drawing.Size(984, 676);
+            this.parameterSetManager.TabIndex = 0;
+            this.parameterSetManager.SelectedParamSetChanged += new System.EventHandler(this.parameterSetManager_SelectedParamSetChanged);
+            // 
+            // label1
+            // 
+            this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+            this.label1.AutoSize = true;
+            this.label1.Location = new System.Drawing.Point(12, 700);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(351, 13);
+            this.label1.TabIndex = 3;
+            this.label1.Text = "If your parameter set is not found here, add it from the Parameter Sets tab";
+            // 
+            // ParameterSetPicker
+            // 
+            this.AcceptButton = this.btnOK;
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.CancelButton = this.btnCancel;
+            this.ClientSize = new System.Drawing.Size(1008, 730);
+            this.ControlBox = false;
+            this.Controls.Add(this.label1);
+            this.Controls.Add(this.btnCancel);
+            this.Controls.Add(this.btnOK);
+            this.Controls.Add(this.parameterSetManager);
+            this.Name = "ParameterSetPicker";
+            this.Text = "Select Parameter Set";
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private ParameterSetManager parameterSetManager;
+        private System.Windows.Forms.Button btnOK;
+        private System.Windows.Forms.Button btnCancel;
+        private System.Windows.Forms.Label label1;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetPicker.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetPicker.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetPicker.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,45 @@
+using MgTestAdmin.Model;
+using SqliteDotNet;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace MgTestAdmin
+{
+    public partial class ParameterSetPicker : Form
+    {
+        private ParameterSetPicker()
+        {
+            InitializeComponent();
+        }
+
+        public ParameterSetInfo SelectedParameterSet => parameterSetManager.SelectedParamSet;
+
+        public ParameterSetPicker(SqliteDb db) 
+            : this()
+        {
+            parameterSetManager.Setup(db, true, null);
+        }
+
+        private void btnCancel_Click(object sender, EventArgs e)
+        {
+            this.DialogResult = DialogResult.Cancel;
+        }
+
+        private void btnOK_Click(object sender, EventArgs e)
+        {
+            this.DialogResult = DialogResult.OK;
+        }
+
+        private void parameterSetManager_SelectedParamSetChanged(object sender, EventArgs e)
+        {
+            btnOK.Enabled = SelectedParameterSet != null;
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetPicker.resx
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetPicker.resx	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/ParameterSetPicker.resx	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Program.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Program.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Program.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace MgTestAdmin
+{
+    public class Program
+    {
+        /// <summary>
+        /// The main entry point for the application.
+        /// </summary>
+        [STAThread]
+        static void Main()
+        {
+            Application.EnableVisualStyles();
+            Application.Run(new Workbench());
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/AssemblyInfo.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/AssemblyInfo.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/AssemblyInfo.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -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("MgTestAdmin")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("MgTestAdmin")]
+[assembly: AssemblyCopyright("Copyright ©  2017")]
+[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("8c5cb4c3-a91d-42f0-9ffa-df9d32f1b177")]
+
+// 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")]

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Resources.Designer.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Resources.Designer.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Resources.Designer.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace MgTestAdmin.Properties
+{
+
+
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources
+    {
+
+        private static global::System.Resources.ResourceManager resourceMan;
+
+        private static global::System.Globalization.CultureInfo resourceCulture;
+
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources()
+        {
+        }
+
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager
+        {
+            get
+            {
+                if ((resourceMan == null))
+                {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MgTestAdmin.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture
+        {
+            get
+            {
+                return resourceCulture;
+            }
+            set
+            {
+                resourceCulture = value;
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Resources.resx
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Resources.resx	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Resources.resx	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Settings.Designer.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Settings.Designer.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Settings.Designer.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace MgTestAdmin.Properties
+{
+
+
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+    {
+
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+        public static Settings Default
+        {
+            get
+            {
+                return defaultInstance;
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Settings.settings
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Settings.settings	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Properties/Settings.settings	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+  <Settings />
+</SettingsFile>

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/SqliteDb.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/SqliteDb.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/SqliteDb.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,242 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Data.Sqlite;
+
+namespace SqliteDotNet
+{
+
+    [Serializable]
+    public class ColumNotFoundException : Exception
+    {
+        public ColumNotFoundException() { }
+        public ColumNotFoundException(string message) : base(message) { }
+        public ColumNotFoundException(string message, Exception inner) : base(message, inner) { }
+        protected ColumNotFoundException(
+          System.Runtime.Serialization.SerializationInfo info,
+          System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
+    }
+
+    public class Sqlite
+    {
+        public const int Error = 1;
+        public const int Done = 101;
+        public const int Ok = 0;
+
+        public const int Row = 100;
+    }
+
+    public class SqliteGcBlob
+    {
+        private SqliteDataReader _currentReader;
+        private int _ordinal;
+
+        public SqliteGcBlob(SqliteDataReader currentReader, int v)
+        {
+            _currentReader = currentReader;
+            _ordinal = v;
+        }
+
+        public byte[] Read()
+        {
+            if (_currentReader.IsDBNull(_ordinal))
+            {
+                return null;
+            }
+            return _currentReader.GetFieldValue<byte[]>(_ordinal);
+        }
+    }
+
+    public class SqliteDb
+    {
+        public SqliteDb()
+        {
+            InternalConnection = new Microsoft.Data.Sqlite.SqliteConnection();
+        }
+
+        internal Microsoft.Data.Sqlite.SqliteConnection InternalConnection { get; }
+
+        public void Close()
+        {
+            InternalConnection.Close();
+            this.FileName = null;
+        }
+
+        public string FileName { get; private set; }
+
+        public string GetName() => FileName;
+
+        public int Open(string unitTestVmPath)
+        {
+            InternalConnection.ConnectionString = "Data Source=" + unitTestVmPath;
+            InternalConnection.Open();
+            if (InternalConnection.State == System.Data.ConnectionState.Open)
+            {
+                this.FileName = unitTestVmPath;
+                return 0;
+            }
+            return 1;
+        }
+
+        public void GenerateDatabase(string dumpFileName, string dbPath)
+        {
+            string content = File.ReadAllText(dumpFileName);
+            var db = new SqliteDb();
+            try
+            {
+                db.Open(dbPath);
+                using (var cmd = db.InternalConnection.CreateCommand())
+                {
+                    cmd.CommandText = content;
+                    cmd.ExecuteNonQuery();
+                }
+            }
+            finally
+            {
+                db.Close();
+            }
+        }
+    }
+
+    public class SqliteVm
+    {
+        private SqliteDb _db;
+        private bool _noThrow;
+
+        private Microsoft.Data.Sqlite.SqliteCommand _currentStmt;
+        private Microsoft.Data.Sqlite.SqliteDataReader _currentReader;
+
+        private Dictionary<string, int> _nameIndexMap;
+
+        public SqliteVm(SqliteDb db, bool noThrow)
+        {
+            _db = db;
+            _noThrow = noThrow;
+            _nameIndexMap = new Dictionary<string, int>();
+        }
+
+        public int NumCols() => _nameIndexMap.Count;
+
+        public string ColumnName(int ordinal) => _currentReader.GetName(ordinal);
+
+        public string ColumnType(string name) => _currentReader.GetDataTypeName(_nameIndexMap[name]);
+
+        private string _lastCommand;
+        private Exception _lastException;
+
+        public int Execute(string sql)
+        {
+            try
+            {
+                _currentStmt = _db.InternalConnection.CreateCommand();
+                _currentStmt.CommandText = sql;
+                _currentStmt.Prepare();
+
+                _lastCommand = _currentStmt.CommandText;
+
+                _currentReader = _currentStmt.ExecuteReader();
+
+                //Compile name index map for the purposes of column binding below
+                for (int i = 0; i < _currentReader.FieldCount; i++)
+                {
+                    var name = _currentReader.GetName(i);
+                    _nameIndexMap[name] = i;
+                }
+
+                return NextRow();
+            }
+            catch (Exception ex)
+            {
+                _lastException = ex;
+                return Sqlite.Error;
+            }
+        }
+
+        public string GetErrMsg() => _lastException.Message;
+
+        public string GetQueryTail() => _lastCommand;
+
+        public void SqlFinalize()
+        {
+            if (_currentReader != null)
+            {
+                _currentReader.Close();
+                _currentReader = null;
+            }
+            if (_currentStmt != null)
+            {
+                _currentStmt.Dispose();
+                _currentStmt = null;
+            }
+        }
+
+        public int GetInt(string columnName)
+        {
+            if (_nameIndexMap.ContainsKey(columnName))
+            {
+                return _currentReader.GetFieldValue<int>(_nameIndexMap[columnName]);
+            }
+            else if (!_noThrow)
+            {
+                throw new ColumNotFoundException(columnName);
+            }
+            return default(int);
+        }
+
+        public double GetDouble(string columnName)
+        {
+            if (_nameIndexMap.ContainsKey(columnName))
+            {
+                return _currentReader.GetFieldValue<double>(_nameIndexMap[columnName]);
+            }
+            else if (!_noThrow)
+            {
+                throw new ColumNotFoundException(columnName);
+            }
+            return double.NaN;
+        }
+
+        public string GetString(string columnName)
+        {
+            if (_nameIndexMap.ContainsKey(columnName))
+            {
+                var value = _currentReader[_nameIndexMap[columnName]];
+                switch(value)
+                {
+                    case byte[] b:
+                        return Encoding.UTF8.GetString(b);
+                    default:
+                        return value.ToString();
+                }
+            }
+            else if (!_noThrow)
+            {
+                throw new ColumNotFoundException(columnName);
+            }
+            return string.Empty;
+        }
+
+        public int NextRow()
+        {
+            bool b = _currentReader.Read();
+            return b ? Sqlite.Row : -1;
+        }
+
+        public SqliteGcBlob GetBlob(string columnName)
+        {
+            SqliteGcBlob blob = null;
+            if (_nameIndexMap.ContainsKey(columnName))
+            {
+                blob = new SqliteGcBlob(_currentReader, _nameIndexMap[columnName]);
+            }
+            else if (!_noThrow)
+            {
+                throw new ColumNotFoundException(columnName);
+            }
+            return blob;
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/SqliteDbExtensions.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/SqliteDbExtensions.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/SqliteDbExtensions.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,371 @@
+using MgTestAdmin.Model;
+using SqliteDotNet;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MgTestAdmin
+{
+    public static class SqliteDbExtensions
+    {
+        //Checks if a file is read only
+        public static bool IsReadOnly(string fileName)
+        {
+            bool readOnly = false;
+            int i = 0;
+            FileInfo file = new FileInfo(fileName);
+            //Get all attributes and put them into array
+            string[] attributes = file.Attributes.ToString().Split(',');
+
+            //Parse the array
+            while ((i < attributes.Length) && ("ReadOnly" != attributes[i]))
+            {
+                i++;
+            }
+
+            if (i < attributes.Length)
+            {
+                readOnly = true;
+            }
+            return readOnly;
+        }
+
+        public static string[] GetTables(this SqliteDb db)
+        {
+            var tables = new List<string>();
+            var vm = new SqliteVm(db, true);
+            try
+            {
+                string tableList = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;";
+
+                int fini = vm.Execute(tableList);
+                while (fini == Sqlite.Row)
+                {
+                    string tableName = vm.GetString("name");
+
+                    tables.Add(tableName);
+
+                    fini = vm.NextRow();
+                }
+            }
+            finally
+            {
+                vm.SqlFinalize();
+            }
+            return tables.ToArray();
+        }
+
+        public static bool HasTestCaseTable(this SqliteDb db)
+        {
+            string sqlStmt = "SELECT name FROM sqlite_master WHERE type='table' AND name='TestCase';";
+            var vm = new SqliteVm(db, true);
+            bool exists = false;
+            try
+            {
+                exists = (vm.Execute(sqlStmt) == Sqlite.Row);
+            }
+            finally
+            {
+                vm.SqlFinalize();
+            }
+            return exists;
+        }
+
+        public static TestCaseInfo[] GetTests(this SqliteDb db)
+        {
+            var tests = new List<TestCaseInfo>();
+            var vm = new SqliteVm(db, true);
+            try
+            {
+                var status = vm.Execute("Select TestName, ExecuteSequence from TestCase");
+                while (Sqlite.Row == status)
+                {
+                    tests.Add(new TestCaseInfo
+                    {
+                        ExecuteSequence = vm.GetInt(nameof(TestCaseInfo.ExecuteSequence)),
+                        TestName = vm.GetString(nameof(TestCaseInfo.TestName))
+                    });
+                    status = vm.NextRow();
+                }
+            }
+            finally
+            {
+                vm.SqlFinalize();
+            }
+            return tests.ToArray();
+        }
+
+        public static TestCase GetTest(this SqliteDb db, string name)
+        {
+            var vm = new SqliteVm(db, true);
+            try
+            {
+                var status = vm.Execute($"Select * from TestCase where TestName=\"{name}\"");
+                if ((Sqlite.Done == status) || (Sqlite.Row == status))
+                {
+                    string psets = vm.GetString(nameof(TestCase.ParamSets));
+                    var test = new TestCase
+                    {
+                        TestName = name,
+                        ExecuteSequence = vm.GetInt(nameof(TestCase.ExecuteSequence)),
+                        Description = vm.GetString(nameof(TestCase.Description)),
+                        Prerequsite = vm.GetString(nameof(TestCase.Prerequsite)),
+                        TestType = vm.GetString(nameof(TestCase.TestType)),
+                        ParamSets = psets.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(s =>
+                        {
+                            if (int.TryParse(s, out var i))
+                                return i;
+                            return (int?)i;
+                        }).Where(i => i.HasValue).Select(i => i.Value).ToArray()
+                    };
+                    return test;
+                }
+            }
+            finally
+            {
+                vm.SqlFinalize();
+            }
+            return null;
+        }
+
+        public static ParameterSetInfo[] GetParamSets(this SqliteDb db)
+        {
+            var paramSets = new List<ParameterSetInfo>();
+            var vm = new SqliteVm(db, true);
+            try
+            {
+                var status = vm.Execute($"Select ParamSet, ParamValue from Params where ParamName = 'OPERATION' group by ParamSet, ParamValue");
+                while (Sqlite.Row == status)
+                {
+                    paramSets.Add(new ParameterSetInfo
+                    {
+                        Id = vm.GetInt("ParamSet"),
+                        Operation = vm.GetString("ParamValue")
+                    });
+
+                    status = vm.NextRow();
+                }
+            }
+            finally
+            {
+                vm.SqlFinalize();
+            }
+            return paramSets.ToArray();
+        }
+
+        public static ParameterSet GetParamSet(this SqliteDb db, int paramSet)
+        {
+            var ps = new ParameterSet { Id = paramSet };
+            var param = new List<Parameter>();
+            var vm = new SqliteVm(db, true);
+            try
+            {
+                var status = vm.Execute($"Select ParamName, ParamValue from Params where ParamSet={paramSet}");
+                while (Sqlite.Row == status)
+                {
+                    param.Add(new Parameter
+                    {
+                        ParamName = vm.GetString(nameof(Parameter.ParamName)),
+                        ParamValue = vm.GetString(nameof(Parameter.ParamValue))
+                    });
+
+                    status = vm.NextRow();
+                }
+                ps.Params = param.ToArray();
+            }
+            finally
+            {
+                vm.SqlFinalize();
+            }
+            return ps;
+        }
+
+        public static void CreateOrUpdateTest(this SqliteDb db, TestCase test)
+        {
+            var vm = new SqliteVm(db, true);
+            try
+            {
+                string sql;
+                if (Sqlite.Row == vm.Execute($"Select * From TestCase where TestName=\'{test.TestName}\'"))
+                {
+                    sql = $"UPDATE TestCase SET ExecuteSequence = {test.ExecuteSequence}, ParamSets=\"{string.Join(",", test.ParamSets)}\", Description = \"{test.Description}\", TestType = \"{test.TestType}\", Prerequisite = \"{test.Prerequsite}\" WHERE TestName = \'{test.TestName}\'";
+                }
+                else
+                {
+                    sql = $"INSERT INTO TestCase(ExecuteSequence, TestName, ParamSets, Description, TestType, Prerequisite) VALUES ({test.ExecuteSequence}, \'{test.TestName}\', \'{string.Join(",", test.ParamSets)}\', \'{test.Description}\', \'{test.TestType}\', \'{test.Prerequsite}\')";
+                }
+                vm.Execute(sql);
+            }
+            finally
+            {
+                vm.SqlFinalize();
+            }
+        }
+
+        public static bool DeleteTest(this SqliteDb db, TestCase test)
+        {
+            bool deleted = true;
+            var vm = new SqliteVm(db, true);
+            try
+            {
+                foreach (var p in test.ParamSets)
+                {
+                    db.DeleteParamSet(p, test.TestType);
+                }
+                vm.Execute($"Delete from TestCase where TestName = {test.TestName}");
+            }
+            finally
+            {
+                vm.SqlFinalize();
+            }
+            return deleted;
+        }
+
+        public static bool DeleteParamSet(this SqliteDb db, int paramSet, string testType)
+        {
+            bool deleted = true;
+            var vm = new SqliteVm(db, true);
+            try
+            {
+                vm.Execute($"DELETE FROM Params WHERE ParamSet = {paramSet}");
+                //Check which result table to modify.
+                //If the test type is invalid nothing will be deleted. Shall we display error message?
+                if ("Api" == testType)
+                {
+                    vm.Execute($"DELETE FROM ApiTestResults WHERE ParamSet = {paramSet}");
+                }
+                else if ("Http" == testType)
+                {
+                    vm.Execute($"DELETE FROM HttpTestResults WHERE ParamSet = {paramSet}");
+                }
+            }
+            finally
+            {
+                vm.SqlFinalize();
+            }
+            return deleted;
+        }
+
+        public static TestResultSet GetTestResultSet(this SqliteDb db, int paramSet)
+        {
+            return new TestResultSet
+            {
+                ApiResult = db.GetTestResult(paramSet, "Api"),
+                HttpResult = db.GetTestResult(paramSet, "Http")
+            };
+        }
+
+        public static TestResult GetTestResult(this SqliteDb db, int paramSet, string testType)
+        {
+            TestResult res = null;
+            var vm = new SqliteVm(db, true);
+            try
+            {
+                int status = Sqlite.Done;
+                if ("Api" == testType)
+                {
+                    status = vm.Execute($"Select * from ApiTestResults where ParamSet = {paramSet}");
+                }
+                else if ("Http" == testType)
+                {
+                    status = vm.Execute($"Select * from HttpTestResults where ParamSet = {paramSet}");
+                }
+
+                if (Sqlite.Row == status)
+                {
+                    res = new TestResult
+                    {
+                        ParamSet = paramSet,
+                        Description = vm.GetString(nameof(TestResult.Description)),
+                        ContentType = vm.GetString(nameof(TestResult.ContentType)),
+                        Result = vm.GetString(nameof(TestResult.Result))
+                    };
+                }
+            }
+            finally
+            {
+                vm.SqlFinalize();
+            }
+            return res;
+        }
+
+        public static void LoadFromDump(this SqliteDb db, string dumpPath)
+        {
+            var vm = new SqliteVm(db, true);
+            try
+            {
+                var content = File.ReadAllText(dumpPath);
+                vm.Execute(content);
+            }
+            finally
+            {
+                vm.SqlFinalize();
+            }
+        }
+
+        public static void CreateTemplate(this SqliteDb db)
+        {
+            var templateVm = new SqliteVm(db, true);
+            try
+            {
+                templateVm.Execute("CREATE TABLE TestCase (ExecuteSequence INTEGER, TestName TEXT, ParamSets TEXT, Description TEXT, TestType TEXT, Prerequisite TEXT)");
+                templateVm.Execute("CREATE TABLE Params (ParamSet INTEGER, ParamName TEXT, ParamValue TEXT)");
+                templateVm.Execute("CREATE TABLE CommonParams (ParamName text, ParamValue text)");
+                templateVm.Execute("CREATE TABLE ApiTestResults (Description TEXT, ParamSet INTEGER, ContentType TEXT, Result TEXT)");
+                templateVm.Execute("CREATE TABLE HttpTestResults (Description TEXT, ParamSet INTEGER, ContentType TEXT, Result BLOB)");
+            }
+            finally
+            {
+                templateVm.SqlFinalize();
+            }
+        }
+
+        public static int GetNewParamSetId(this SqliteDb db)
+        {
+            var vm = new SqliteVm(db, true);
+            int id = -1;
+            try
+            {
+                if (vm.Execute("Select COUNT(*) AS Total From Params") == Sqlite.Row)
+                {
+                    int count = vm.GetInt("Total");
+                    if (count > 0 && vm.Execute("Select MAX(ParamSet) + 1 AS NewID From Params") == Sqlite.Row)
+                    {
+                        id = vm.GetInt("NewID");
+                    }
+                    else
+                    {
+                        id = 1;
+                    }
+                }
+            }
+            finally
+            {
+                vm.SqlFinalize();
+            }
+            return id;
+        }
+
+        public static void GenerateDump(this SqliteDb db, string dumpPath)
+        {
+            using (var sw = new StreamWriter(dumpPath))
+            {
+                var psi = new ProcessStartInfo("sqlite3.exe", $"\"{db.GetName()}\" .dump");
+                psi.UseShellExecute = false;
+                psi.RedirectStandardOutput = true;
+                var proc = new Process()
+                {
+                    StartInfo = psi
+                };
+                proc.OutputDataReceived += (s, e) => sw.WriteLine(e.Data);
+                proc.Start();
+                proc.BeginOutputReadLine();
+                proc.WaitForExit();
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestCaseControl.Designer.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestCaseControl.Designer.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestCaseControl.Designer.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,263 @@
+namespace MgTestAdmin
+{
+    partial class TestCaseControl
+    {
+        /// <summary> 
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Component Designer generated code
+
+        /// <summary> 
+        /// Required method for Designer support - do not modify 
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.components = new System.ComponentModel.Container();
+            this.groupBox1 = new System.Windows.Forms.GroupBox();
+            this.numSequence = new System.Windows.Forms.NumericUpDown();
+            this.label6 = new System.Windows.Forms.Label();
+            this.rdApi = new System.Windows.Forms.RadioButton();
+            this.rdHttp = new System.Windows.Forms.RadioButton();
+            this.label5 = new System.Windows.Forms.Label();
+            this.txtDescription = new System.Windows.Forms.TextBox();
+            this.label4 = new System.Windows.Forms.Label();
+            this.txtPrerequisite = new System.Windows.Forms.TextBox();
+            this.label3 = new System.Windows.Forms.Label();
+            this.txtName = new System.Windows.Forms.TextBox();
+            this.label2 = new System.Windows.Forms.Label();
+            this.groupBox2 = new System.Windows.Forms.GroupBox();
+            this.parameterSetEditor = new MgTestAdmin.ParameterSetEditor();
+            this.btnSave = new System.Windows.Forms.Button();
+            this.errorProvider = new System.Windows.Forms.ErrorProvider(this.components);
+            this.groupBox1.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.numSequence)).BeginInit();
+            this.groupBox2.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // groupBox1
+            // 
+            this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.groupBox1.Controls.Add(this.numSequence);
+            this.groupBox1.Controls.Add(this.label6);
+            this.groupBox1.Controls.Add(this.rdApi);
+            this.groupBox1.Controls.Add(this.rdHttp);
+            this.groupBox1.Controls.Add(this.label5);
+            this.groupBox1.Controls.Add(this.txtDescription);
+            this.groupBox1.Controls.Add(this.label4);
+            this.groupBox1.Controls.Add(this.txtPrerequisite);
+            this.groupBox1.Controls.Add(this.label3);
+            this.groupBox1.Controls.Add(this.txtName);
+            this.groupBox1.Controls.Add(this.label2);
+            this.groupBox1.Location = new System.Drawing.Point(16, 3);
+            this.groupBox1.Name = "groupBox1";
+            this.groupBox1.Size = new System.Drawing.Size(608, 164);
+            this.groupBox1.TabIndex = 1;
+            this.groupBox1.TabStop = false;
+            this.groupBox1.Text = "1. Test Information";
+            // 
+            // numSequence
+            // 
+            this.numSequence.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.numSequence.Location = new System.Drawing.Point(324, 124);
+            this.numSequence.Maximum = new decimal(new int[] {
+            1215752191,
+            23,
+            0,
+            0});
+            this.numSequence.Name = "numSequence";
+            this.numSequence.Size = new System.Drawing.Size(262, 20);
+            this.numSequence.TabIndex = 10;
+            this.numSequence.ValueChanged += new System.EventHandler(this.numSequence_ValueChanged);
+            // 
+            // label6
+            // 
+            this.label6.AutoSize = true;
+            this.label6.Location = new System.Drawing.Point(220, 126);
+            this.label6.Name = "label6";
+            this.label6.Size = new System.Drawing.Size(98, 13);
+            this.label6.TabIndex = 9;
+            this.label6.Text = "Execute Sequence";
+            // 
+            // rdApi
+            // 
+            this.rdApi.AutoSize = true;
+            this.rdApi.Location = new System.Drawing.Point(160, 124);
+            this.rdApi.Name = "rdApi";
+            this.rdApi.Size = new System.Drawing.Size(40, 17);
+            this.rdApi.TabIndex = 8;
+            this.rdApi.TabStop = true;
+            this.rdApi.Text = "Api";
+            this.rdApi.UseVisualStyleBackColor = true;
+            this.rdApi.CheckedChanged += new System.EventHandler(this.rdApi_CheckedChanged);
+            // 
+            // rdHttp
+            // 
+            this.rdHttp.AutoSize = true;
+            this.rdHttp.Location = new System.Drawing.Point(109, 124);
+            this.rdHttp.Name = "rdHttp";
+            this.rdHttp.Size = new System.Drawing.Size(45, 17);
+            this.rdHttp.TabIndex = 7;
+            this.rdHttp.TabStop = true;
+            this.rdHttp.Text = "Http";
+            this.rdHttp.UseVisualStyleBackColor = true;
+            this.rdHttp.CheckedChanged += new System.EventHandler(this.rdHttp_CheckedChanged);
+            // 
+            // label5
+            // 
+            this.label5.AutoSize = true;
+            this.label5.Location = new System.Drawing.Point(22, 126);
+            this.label5.Name = "label5";
+            this.label5.Size = new System.Drawing.Size(55, 13);
+            this.label5.TabIndex = 6;
+            this.label5.Text = "Test Type";
+            // 
+            // txtDescription
+            // 
+            this.txtDescription.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.txtDescription.Location = new System.Drawing.Point(109, 83);
+            this.txtDescription.Name = "txtDescription";
+            this.txtDescription.Size = new System.Drawing.Size(477, 20);
+            this.txtDescription.TabIndex = 5;
+            this.txtDescription.TextChanged += new System.EventHandler(this.txtDescription_TextChanged);
+            // 
+            // label4
+            // 
+            this.label4.AutoSize = true;
+            this.label4.Location = new System.Drawing.Point(22, 86);
+            this.label4.Name = "label4";
+            this.label4.Size = new System.Drawing.Size(60, 13);
+            this.label4.TabIndex = 4;
+            this.label4.Text = "Description";
+            // 
+            // txtPrerequisite
+            // 
+            this.txtPrerequisite.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.txtPrerequisite.Location = new System.Drawing.Point(109, 57);
+            this.txtPrerequisite.Name = "txtPrerequisite";
+            this.txtPrerequisite.Size = new System.Drawing.Size(477, 20);
+            this.txtPrerequisite.TabIndex = 3;
+            this.txtPrerequisite.TextChanged += new System.EventHandler(this.txtPrerequisite_TextChanged);
+            // 
+            // label3
+            // 
+            this.label3.AutoSize = true;
+            this.label3.Location = new System.Drawing.Point(22, 60);
+            this.label3.Name = "label3";
+            this.label3.Size = new System.Drawing.Size(62, 13);
+            this.label3.TabIndex = 2;
+            this.label3.Text = "Prerequisite";
+            // 
+            // txtName
+            // 
+            this.txtName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.txtName.Location = new System.Drawing.Point(109, 31);
+            this.txtName.Name = "txtName";
+            this.txtName.Size = new System.Drawing.Size(477, 20);
+            this.txtName.TabIndex = 1;
+            this.txtName.TextChanged += new System.EventHandler(this.txtName_TextChanged);
+            // 
+            // label2
+            // 
+            this.label2.AutoSize = true;
+            this.label2.Location = new System.Drawing.Point(22, 34);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(35, 13);
+            this.label2.TabIndex = 0;
+            this.label2.Text = "Name";
+            // 
+            // groupBox2
+            // 
+            this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.groupBox2.Controls.Add(this.parameterSetEditor);
+            this.groupBox2.Location = new System.Drawing.Point(16, 173);
+            this.groupBox2.Name = "groupBox2";
+            this.groupBox2.Size = new System.Drawing.Size(608, 384);
+            this.groupBox2.TabIndex = 2;
+            this.groupBox2.TabStop = false;
+            this.groupBox2.Text = "2. Parameter Sets";
+            // 
+            // parameterSetEditor
+            // 
+            this.parameterSetEditor.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.parameterSetEditor.Location = new System.Drawing.Point(3, 16);
+            this.parameterSetEditor.Name = "parameterSetEditor";
+            this.parameterSetEditor.ParamSets = new int[0];
+            this.parameterSetEditor.Size = new System.Drawing.Size(602, 365);
+            this.parameterSetEditor.TabIndex = 0;
+            // 
+            // btnSave
+            // 
+            this.btnSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+            this.btnSave.Location = new System.Drawing.Point(16, 563);
+            this.btnSave.Name = "btnSave";
+            this.btnSave.Size = new System.Drawing.Size(75, 23);
+            this.btnSave.TabIndex = 3;
+            this.btnSave.Text = "Save Test";
+            this.btnSave.UseVisualStyleBackColor = true;
+            this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
+            // 
+            // errorProvider
+            // 
+            this.errorProvider.ContainerControl = this;
+            // 
+            // TestCaseControl
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.Controls.Add(this.btnSave);
+            this.Controls.Add(this.groupBox2);
+            this.Controls.Add(this.groupBox1);
+            this.Name = "TestCaseControl";
+            this.Size = new System.Drawing.Size(645, 597);
+            this.groupBox1.ResumeLayout(false);
+            this.groupBox1.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.numSequence)).EndInit();
+            this.groupBox2.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).EndInit();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+        private System.Windows.Forms.GroupBox groupBox1;
+        private System.Windows.Forms.TextBox txtName;
+        private System.Windows.Forms.Label label2;
+        private System.Windows.Forms.RadioButton rdApi;
+        private System.Windows.Forms.RadioButton rdHttp;
+        private System.Windows.Forms.Label label5;
+        private System.Windows.Forms.TextBox txtDescription;
+        private System.Windows.Forms.Label label4;
+        private System.Windows.Forms.TextBox txtPrerequisite;
+        private System.Windows.Forms.Label label3;
+        private System.Windows.Forms.NumericUpDown numSequence;
+        private System.Windows.Forms.Label label6;
+        private System.Windows.Forms.GroupBox groupBox2;
+        private ParameterSetEditor parameterSetEditor;
+        private System.Windows.Forms.Button btnSave;
+        private System.Windows.Forms.ErrorProvider errorProvider;
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestCaseControl.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestCaseControl.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestCaseControl.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,77 @@
+using MgTestAdmin.Model;
+using SqliteDotNet;
+using System;
+using System.Windows.Forms;
+
+namespace MgTestAdmin
+{
+    public partial class TestCaseControl : UserControl
+    {
+        public TestCaseControl()
+        {
+            InitializeComponent();
+        }
+
+        private TestCase _test;
+        private SqliteDb _db;
+
+        public TestCaseControl Attach(TestCase test, SqliteDb db)
+        {
+            _test = test;
+            _db = db;
+
+            txtName.Text = _test.TestName;
+            txtDescription.Text = _test.Description;
+            txtPrerequisite.Text = _test.Prerequsite;
+            numSequence.Value = _test.ExecuteSequence;
+            switch (_test.TestType)
+            {
+                case "Api":
+                    rdApi.Checked = true;
+                    break;
+                case "Http":
+                    rdHttp.Checked = true;
+                    break;
+            }
+
+            parameterSetEditor.Attach(_test.ParamSets, _db);
+
+            return this;
+        }
+
+        private void btnSave_Click(object sender, EventArgs e)
+        {
+            
+        }
+
+        private void txtName_TextChanged(object sender, EventArgs e)
+        {
+            _test.TestName = txtPrerequisite.Name;
+        }
+
+        private void txtPrerequisite_TextChanged(object sender, EventArgs e)
+        {
+            _test.Prerequsite = txtPrerequisite.Text;
+        }
+
+        private void txtDescription_TextChanged(object sender, EventArgs e)
+        {
+            _test.Description = txtDescription.Text;
+        }
+
+        private void numSequence_ValueChanged(object sender, EventArgs e)
+        {
+            _test.ExecuteSequence = Convert.ToInt32(numSequence.Value);
+        }
+
+        private void rdHttp_CheckedChanged(object sender, EventArgs e)
+        {
+            _test.TestType = "Http";
+        }
+
+        private void rdApi_CheckedChanged(object sender, EventArgs e)
+        {
+            _test.TestType = "Api";
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestCaseControl.resx
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestCaseControl.resx	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestCaseControl.resx	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="errorProvider.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManager.Designer.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManager.Designer.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManager.Designer.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,58 @@
+namespace MgTestAdmin
+{
+    partial class TestManager
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.testManagerControl = new MgTestAdmin.TestManagerControl();
+            this.SuspendLayout();
+            // 
+            // testManagerControl
+            // 
+            this.testManagerControl.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.testManagerControl.Location = new System.Drawing.Point(0, 0);
+            this.testManagerControl.Name = "testManagerControl";
+            this.testManagerControl.Size = new System.Drawing.Size(818, 459);
+            this.testManagerControl.TabIndex = 0;
+            // 
+            // TestManager
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(818, 459);
+            this.Controls.Add(this.testManagerControl);
+            this.Name = "TestManager";
+            this.Text = "Test Manager";
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private TestManagerControl testManagerControl;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManager.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManager.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManager.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,27 @@
+using MgTestAdmin.Model;
+using SqliteDotNet;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace MgTestAdmin
+{
+    public partial class TestManager : Form
+    {
+        public TestManager()
+        {
+            InitializeComponent();
+        }
+
+        public void LoadForm(SqliteDb db)
+        {
+            testManagerControl.Setup(db);
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManager.resx
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManager.resx	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManager.resx	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManagerControl.Designer.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManagerControl.Designer.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManagerControl.Designer.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,137 @@
+namespace MgTestAdmin
+{
+    partial class TestManagerControl
+    {
+        /// <summary> 
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Component Designer generated code
+
+        /// <summary> 
+        /// Required method for Designer support - do not modify 
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TestManagerControl));
+            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+            this.groupBox1 = new System.Windows.Forms.GroupBox();
+            this.lstTests = new System.Windows.Forms.ListBox();
+            this.toolStrip1 = new System.Windows.Forms.ToolStrip();
+            this.tbAddTest = new System.Windows.Forms.ToolStripButton();
+            this.tbDeleteTest = new System.Windows.Forms.ToolStripButton();
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
+            this.splitContainer1.Panel1.SuspendLayout();
+            this.splitContainer1.SuspendLayout();
+            this.groupBox1.SuspendLayout();
+            this.toolStrip1.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // splitContainer1
+            // 
+            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.splitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
+            this.splitContainer1.Location = new System.Drawing.Point(0, 0);
+            this.splitContainer1.Name = "splitContainer1";
+            // 
+            // splitContainer1.Panel1
+            // 
+            this.splitContainer1.Panel1.Controls.Add(this.groupBox1);
+            this.splitContainer1.Size = new System.Drawing.Size(930, 501);
+            this.splitContainer1.SplitterDistance = 211;
+            this.splitContainer1.TabIndex = 1;
+            // 
+            // groupBox1
+            // 
+            this.groupBox1.Controls.Add(this.lstTests);
+            this.groupBox1.Controls.Add(this.toolStrip1);
+            this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.groupBox1.Location = new System.Drawing.Point(0, 0);
+            this.groupBox1.Name = "groupBox1";
+            this.groupBox1.Size = new System.Drawing.Size(211, 501);
+            this.groupBox1.TabIndex = 0;
+            this.groupBox1.TabStop = false;
+            this.groupBox1.Text = "Current Tests";
+            // 
+            // lstTests
+            // 
+            this.lstTests.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.lstTests.FormattingEnabled = true;
+            this.lstTests.Location = new System.Drawing.Point(3, 41);
+            this.lstTests.Name = "lstTests";
+            this.lstTests.Size = new System.Drawing.Size(205, 457);
+            this.lstTests.TabIndex = 1;
+            this.lstTests.SelectedIndexChanged += new System.EventHandler(this.lstTests_SelectedIndexChanged);
+            // 
+            // toolStrip1
+            // 
+            this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.tbAddTest,
+            this.tbDeleteTest});
+            this.toolStrip1.Location = new System.Drawing.Point(3, 16);
+            this.toolStrip1.Name = "toolStrip1";
+            this.toolStrip1.Size = new System.Drawing.Size(205, 25);
+            this.toolStrip1.TabIndex = 0;
+            this.toolStrip1.Text = "toolStrip1";
+            // 
+            // tbAddTest
+            // 
+            this.tbAddTest.Image = ((System.Drawing.Image)(resources.GetObject("tbAddTest.Image")));
+            this.tbAddTest.ImageTransparentColor = System.Drawing.Color.Magenta;
+            this.tbAddTest.Name = "tbAddTest";
+            this.tbAddTest.Size = new System.Drawing.Size(74, 22);
+            this.tbAddTest.Text = "Add Test";
+            this.tbAddTest.Click += new System.EventHandler(this.tbAddTest_Click);
+            // 
+            // tbDeleteTest
+            // 
+            this.tbDeleteTest.Image = ((System.Drawing.Image)(resources.GetObject("tbDeleteTest.Image")));
+            this.tbDeleteTest.ImageTransparentColor = System.Drawing.Color.Magenta;
+            this.tbDeleteTest.Name = "tbDeleteTest";
+            this.tbDeleteTest.Size = new System.Drawing.Size(60, 22);
+            this.tbDeleteTest.Text = "Delete";
+            this.tbDeleteTest.Click += new System.EventHandler(this.tbDeleteTest_Click);
+            // 
+            // TestManagerControl
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.Controls.Add(this.splitContainer1);
+            this.Name = "TestManagerControl";
+            this.Size = new System.Drawing.Size(930, 501);
+            this.splitContainer1.Panel1.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
+            this.splitContainer1.ResumeLayout(false);
+            this.groupBox1.ResumeLayout(false);
+            this.groupBox1.PerformLayout();
+            this.toolStrip1.ResumeLayout(false);
+            this.toolStrip1.PerformLayout();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.SplitContainer splitContainer1;
+        private System.Windows.Forms.GroupBox groupBox1;
+        private System.Windows.Forms.ListBox lstTests;
+        private System.Windows.Forms.ToolStrip toolStrip1;
+        private System.Windows.Forms.ToolStripButton tbAddTest;
+        private System.Windows.Forms.ToolStripButton tbDeleteTest;
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManagerControl.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManagerControl.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManagerControl.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,75 @@
+using MgTestAdmin.Model;
+using SqliteDotNet;
+using System;
+using System.Windows.Forms;
+
+namespace MgTestAdmin
+{
+    public partial class TestManagerControl : UserControl
+    {
+        public TestManagerControl()
+        {
+            InitializeComponent();
+        }
+
+        private SqliteDb _db;
+
+        public void Setup(SqliteDb db)
+        {
+            _db = db;
+            lstTests.DataSource = _db.GetTests();
+            //If we have any tests in the list select the first one by default
+            if (lstTests.Items.Count > 0)
+            {
+                lstTests.SelectedIndex = 0;
+            }
+            tbDeleteTest.Enabled = lstTests.SelectedIndex >= 0;
+        }
+
+        private void ClearMainPanel()
+        {
+            foreach (Control c in splitContainer1.Panel2.Controls)
+            {
+                c.Dispose();
+            }
+            splitContainer1.Panel2.Controls.Clear();
+        }
+
+        private void LoadMainPanel(Control c)
+        {
+            this.ClearMainPanel();
+            c.Dock = DockStyle.Fill;
+            splitContainer1.Panel2.Controls.Add(c);
+        }
+
+        private void lstTests_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            tbDeleteTest.Enabled = lstTests.SelectedIndex >= 0;
+            if (lstTests.SelectedItem != null)
+            {
+                var tc = (TestCaseInfo)lstTests.SelectedItem;
+                var test = _db.GetTest(tc.TestName);
+                if (test != null)
+                {
+                    this.LoadMainPanel(new TestCaseControl().Attach(test, _db));
+                }
+            }
+        }
+
+        private void tbDeleteTest_Click(object sender, EventArgs e)
+        {
+            if (lstTests.SelectedItem != null)
+            {
+                var tc = (TestCaseInfo)lstTests.SelectedItem;
+                MessageBox.Show("TODO: Delete " + tc.TestName);
+            }
+        }
+
+        private void tbAddTest_Click(object sender, EventArgs e)
+        {
+            lstTests.ClearSelected();
+            var newTest = new TestCase { };
+            this.LoadMainPanel(new TestCaseControl().Attach(newTest, _db));
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManagerControl.resx
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManagerControl.resx	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestManagerControl.resx	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+  <data name="tbAddTest.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="tbDeleteTest.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+</root>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestResultControl.Designer.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestResultControl.Designer.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestResultControl.Designer.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,135 @@
+namespace MgTestAdmin
+{
+    partial class TestResultControl
+    {
+        /// <summary> 
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary> 
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Component Designer generated code
+
+        /// <summary> 
+        /// Required method for Designer support - do not modify 
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.mainToolstrip = new System.Windows.Forms.ToolStrip();
+            this.label1 = new System.Windows.Forms.Label();
+            this.label2 = new System.Windows.Forms.Label();
+            this.label3 = new System.Windows.Forms.Label();
+            this.txtContentType = new System.Windows.Forms.TextBox();
+            this.txtDescription = new System.Windows.Forms.TextBox();
+            this.txtResult = new ICSharpCode.TextEditor.TextEditorControl();
+            this.SuspendLayout();
+            // 
+            // mainToolstrip
+            // 
+            this.mainToolstrip.Location = new System.Drawing.Point(0, 0);
+            this.mainToolstrip.Name = "mainToolstrip";
+            this.mainToolstrip.Size = new System.Drawing.Size(445, 25);
+            this.mainToolstrip.TabIndex = 0;
+            this.mainToolstrip.Text = "toolStrip1";
+            // 
+            // label1
+            // 
+            this.label1.AutoSize = true;
+            this.label1.Location = new System.Drawing.Point(14, 34);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(60, 13);
+            this.label1.TabIndex = 1;
+            this.label1.Text = "Description";
+            // 
+            // label2
+            // 
+            this.label2.AutoSize = true;
+            this.label2.Location = new System.Drawing.Point(14, 81);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(71, 13);
+            this.label2.TabIndex = 2;
+            this.label2.Text = "Content Type";
+            // 
+            // label3
+            // 
+            this.label3.AutoSize = true;
+            this.label3.Location = new System.Drawing.Point(14, 107);
+            this.label3.Name = "label3";
+            this.label3.Size = new System.Drawing.Size(61, 13);
+            this.label3.TabIndex = 3;
+            this.label3.Text = "Test Result";
+            // 
+            // txtContentType
+            // 
+            this.txtContentType.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.txtContentType.Location = new System.Drawing.Point(91, 78);
+            this.txtContentType.Name = "txtContentType";
+            this.txtContentType.Size = new System.Drawing.Size(339, 20);
+            this.txtContentType.TabIndex = 5;
+            // 
+            // txtDescription
+            // 
+            this.txtDescription.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.txtDescription.Location = new System.Drawing.Point(91, 31);
+            this.txtDescription.Multiline = true;
+            this.txtDescription.Name = "txtDescription";
+            this.txtDescription.Size = new System.Drawing.Size(339, 41);
+            this.txtDescription.TabIndex = 6;
+            // 
+            // txtResult
+            // 
+            this.txtResult.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.txtResult.IsReadOnly = false;
+            this.txtResult.Location = new System.Drawing.Point(91, 107);
+            this.txtResult.Name = "txtResult";
+            this.txtResult.ShowSpaces = true;
+            this.txtResult.ShowTabs = true;
+            this.txtResult.Size = new System.Drawing.Size(339, 265);
+            this.txtResult.TabIndex = 7;
+            // 
+            // TestResultControl
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.Controls.Add(this.txtResult);
+            this.Controls.Add(this.txtDescription);
+            this.Controls.Add(this.txtContentType);
+            this.Controls.Add(this.label3);
+            this.Controls.Add(this.label2);
+            this.Controls.Add(this.label1);
+            this.Controls.Add(this.mainToolstrip);
+            this.Name = "TestResultControl";
+            this.Size = new System.Drawing.Size(445, 390);
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.ToolStrip mainToolstrip;
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.Label label2;
+        private System.Windows.Forms.Label label3;
+        private System.Windows.Forms.TextBox txtContentType;
+        private System.Windows.Forms.TextBox txtDescription;
+        private ICSharpCode.TextEditor.TextEditorControl txtResult;
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestResultControl.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestResultControl.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestResultControl.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using MgTestAdmin.Model;
+using SqliteDotNet;
+
+namespace MgTestAdmin
+{
+    public partial class TestResultControl : UserControl
+    {
+        public TestResultControl()
+        {
+            InitializeComponent();
+        }
+
+        private SqliteDb _db;
+        private TestResult _tr;
+        private bool _frozen;
+
+        public TestResultControl Setup(SqliteDb db, TestResult result, bool freeze)
+        {
+            _db = db;
+            _tr = result;
+            _frozen = freeze;
+
+            if (result != null)
+            {
+                txtContentType.Text = result.ContentType;
+                txtDescription.Text = result.Description;
+                txtResult.Text = result.Result;
+            }
+            else
+            {
+                txtContentType.Text = txtDescription.Text = txtResult.Text = string.Empty;
+            }
+
+            if (_frozen)
+            {
+                mainToolstrip.Visible = false;
+                txtContentType.ReadOnly = txtDescription.ReadOnly = txtResult.IsReadOnly = true;
+            }
+            else
+            {
+                mainToolstrip.Visible = true;
+                txtContentType.ReadOnly = txtDescription.ReadOnly = txtResult.IsReadOnly = false;
+            }
+
+            return this;
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestResultControl.resx
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestResultControl.resx	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/TestResultControl.resx	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="mainToolstrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Workbench.Designer.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Workbench.Designer.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Workbench.Designer.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,219 @@
+namespace MgTestAdmin
+{
+    partial class Workbench
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Workbench));
+            this.mainMenu = new System.Windows.Forms.MenuStrip();
+            this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.openDatabaseToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
+            this.createUnitTestTemplateDatabaseToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.createDatabaseFromDumpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+            this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.operationBrowserToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.openFileDialog = new System.Windows.Forms.OpenFileDialog();
+            this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
+            this.mainContainer = new System.Windows.Forms.SplitContainer();
+            this.toolStrip1 = new System.Windows.Forms.ToolStrip();
+            this.tsbOpenDb = new System.Windows.Forms.ToolStripButton();
+            this.lstOpenDbs = new System.Windows.Forms.ListBox();
+            this.mainMenu.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.mainContainer)).BeginInit();
+            this.mainContainer.Panel1.SuspendLayout();
+            this.mainContainer.SuspendLayout();
+            this.toolStrip1.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // mainMenu
+            // 
+            this.mainMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.fileToolStripMenuItem,
+            this.toolsToolStripMenuItem});
+            this.mainMenu.Location = new System.Drawing.Point(0, 0);
+            this.mainMenu.Name = "mainMenu";
+            this.mainMenu.Size = new System.Drawing.Size(1264, 24);
+            this.mainMenu.TabIndex = 0;
+            this.mainMenu.Text = "menuStrip1";
+            // 
+            // fileToolStripMenuItem
+            // 
+            this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.openDatabaseToolStripMenuItem,
+            this.toolStripSeparator2,
+            this.createUnitTestTemplateDatabaseToolStripMenuItem,
+            this.createDatabaseFromDumpToolStripMenuItem,
+            this.toolStripSeparator1,
+            this.exitToolStripMenuItem});
+            this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
+            this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
+            this.fileToolStripMenuItem.Text = "File";
+            // 
+            // openDatabaseToolStripMenuItem
+            // 
+            this.openDatabaseToolStripMenuItem.Name = "openDatabaseToolStripMenuItem";
+            this.openDatabaseToolStripMenuItem.Size = new System.Drawing.Size(262, 22);
+            this.openDatabaseToolStripMenuItem.Text = "Open Database";
+            this.openDatabaseToolStripMenuItem.Click += new System.EventHandler(this.openDatabaseToolStripMenuItem_Click);
+            // 
+            // toolStripSeparator2
+            // 
+            this.toolStripSeparator2.Name = "toolStripSeparator2";
+            this.toolStripSeparator2.Size = new System.Drawing.Size(259, 6);
+            // 
+            // createUnitTestTemplateDatabaseToolStripMenuItem
+            // 
+            this.createUnitTestTemplateDatabaseToolStripMenuItem.Name = "createUnitTestTemplateDatabaseToolStripMenuItem";
+            this.createUnitTestTemplateDatabaseToolStripMenuItem.Size = new System.Drawing.Size(262, 22);
+            this.createUnitTestTemplateDatabaseToolStripMenuItem.Text = "Create Unit Test Template Database";
+            this.createUnitTestTemplateDatabaseToolStripMenuItem.Click += new System.EventHandler(this.createUnitTestTemplateDatabaseToolStripMenuItem_Click);
+            // 
+            // createDatabaseFromDumpToolStripMenuItem
+            // 
+            this.createDatabaseFromDumpToolStripMenuItem.Name = "createDatabaseFromDumpToolStripMenuItem";
+            this.createDatabaseFromDumpToolStripMenuItem.Size = new System.Drawing.Size(262, 22);
+            this.createDatabaseFromDumpToolStripMenuItem.Text = "Create Database from dump";
+            this.createDatabaseFromDumpToolStripMenuItem.Click += new System.EventHandler(this.createDatabaseFromDumpToolStripMenuItem_Click);
+            // 
+            // toolStripSeparator1
+            // 
+            this.toolStripSeparator1.Name = "toolStripSeparator1";
+            this.toolStripSeparator1.Size = new System.Drawing.Size(259, 6);
+            // 
+            // exitToolStripMenuItem
+            // 
+            this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
+            this.exitToolStripMenuItem.Size = new System.Drawing.Size(262, 22);
+            this.exitToolStripMenuItem.Text = "Exit";
+            this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
+            // 
+            // toolsToolStripMenuItem
+            // 
+            this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.operationBrowserToolStripMenuItem});
+            this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem";
+            this.toolsToolStripMenuItem.Size = new System.Drawing.Size(48, 20);
+            this.toolsToolStripMenuItem.Text = "Tools";
+            // 
+            // operationBrowserToolStripMenuItem
+            // 
+            this.operationBrowserToolStripMenuItem.Name = "operationBrowserToolStripMenuItem";
+            this.operationBrowserToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
+            this.operationBrowserToolStripMenuItem.Text = "Operation Browser";
+            this.operationBrowserToolStripMenuItem.Click += new System.EventHandler(this.operationBrowserToolStripMenuItem_Click);
+            // 
+            // mainContainer
+            // 
+            this.mainContainer.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.mainContainer.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
+            this.mainContainer.Location = new System.Drawing.Point(0, 24);
+            this.mainContainer.Name = "mainContainer";
+            // 
+            // mainContainer.Panel1
+            // 
+            this.mainContainer.Panel1.Controls.Add(this.lstOpenDbs);
+            this.mainContainer.Panel1.Controls.Add(this.toolStrip1);
+            this.mainContainer.Size = new System.Drawing.Size(1264, 706);
+            this.mainContainer.SplitterDistance = 207;
+            this.mainContainer.TabIndex = 1;
+            // 
+            // toolStrip1
+            // 
+            this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.tsbOpenDb});
+            this.toolStrip1.Location = new System.Drawing.Point(0, 0);
+            this.toolStrip1.Name = "toolStrip1";
+            this.toolStrip1.Size = new System.Drawing.Size(207, 25);
+            this.toolStrip1.TabIndex = 0;
+            this.toolStrip1.Text = "toolStrip1";
+            // 
+            // tsbOpenDb
+            // 
+            this.tsbOpenDb.Image = ((System.Drawing.Image)(resources.GetObject("tsbOpenDb.Image")));
+            this.tsbOpenDb.ImageTransparentColor = System.Drawing.Color.Magenta;
+            this.tsbOpenDb.Name = "tsbOpenDb";
+            this.tsbOpenDb.Size = new System.Drawing.Size(74, 22);
+            this.tsbOpenDb.Text = "Open DB";
+            this.tsbOpenDb.Click += new System.EventHandler(this.openDatabaseToolStripMenuItem_Click);
+            // 
+            // lstOpenDbs
+            // 
+            this.lstOpenDbs.DisplayMember = "Name";
+            this.lstOpenDbs.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.lstOpenDbs.FormattingEnabled = true;
+            this.lstOpenDbs.Location = new System.Drawing.Point(0, 25);
+            this.lstOpenDbs.Name = "lstOpenDbs";
+            this.lstOpenDbs.Size = new System.Drawing.Size(207, 681);
+            this.lstOpenDbs.TabIndex = 1;
+            this.lstOpenDbs.SelectedIndexChanged += new System.EventHandler(this.lstOpenDbs_SelectedIndexChanged);
+            // 
+            // Workbench
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(1264, 730);
+            this.Controls.Add(this.mainContainer);
+            this.Controls.Add(this.mainMenu);
+            this.MainMenuStrip = this.mainMenu;
+            this.Name = "Workbench";
+            this.Text = "MapGuide Test Administrator";
+            this.mainMenu.ResumeLayout(false);
+            this.mainMenu.PerformLayout();
+            this.mainContainer.Panel1.ResumeLayout(false);
+            this.mainContainer.Panel1.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.mainContainer)).EndInit();
+            this.mainContainer.ResumeLayout(false);
+            this.toolStrip1.ResumeLayout(false);
+            this.toolStrip1.PerformLayout();
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.MenuStrip mainMenu;
+        private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
+        private System.Windows.Forms.ToolStripMenuItem createUnitTestTemplateDatabaseToolStripMenuItem;
+        private System.Windows.Forms.ToolStripMenuItem openDatabaseToolStripMenuItem;
+        private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
+        private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
+        private System.Windows.Forms.OpenFileDialog openFileDialog;
+        private System.Windows.Forms.SaveFileDialog saveFileDialog;
+        private System.Windows.Forms.ToolStripMenuItem toolsToolStripMenuItem;
+        private System.Windows.Forms.ToolStripMenuItem operationBrowserToolStripMenuItem;
+        private System.Windows.Forms.ToolStripMenuItem createDatabaseFromDumpToolStripMenuItem;
+        private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
+        private System.Windows.Forms.SplitContainer mainContainer;
+        private System.Windows.Forms.ToolStrip toolStrip1;
+        private System.Windows.Forms.ToolStripButton tsbOpenDb;
+        private System.Windows.Forms.ListBox lstOpenDbs;
+    }
+}
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Workbench.cs
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Workbench.cs	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Workbench.cs	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,148 @@
+using SqliteDotNet;
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.IO;
+using System.Windows.Forms;
+
+namespace MgTestAdmin
+{
+    public partial class Workbench : Form
+    {
+        class OpenDatabase
+        {
+            public string Name { get; set; }
+
+            public DbControl Control { get; set; }
+        }
+
+        private BindingList<OpenDatabase> _openDbs = new BindingList<OpenDatabase>();
+        private OperationProviderService _opService;
+
+        public Workbench()
+        {
+            InitializeComponent();
+            _opService = new OperationProviderService();
+            lstOpenDbs.DataSource = _openDbs;
+        }
+
+        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
+        {
+            Application.Exit();
+        }
+
+        private void openDatabaseToolStripMenuItem_Click(object sender, EventArgs e)
+        {
+            openFileDialog.Filter = "db files (*.db)|*.db|All files (*.*)|*.*";
+            openFileDialog.RestoreDirectory = true;
+
+            string fName = "";
+
+            if (openFileDialog.ShowDialog() == DialogResult.OK)
+            {
+                fName = openFileDialog.FileName;
+                var db = new SqliteDb();
+                db.Open(fName);
+                OpenTestManager(fName, db);
+            }
+        }
+
+        private void OpenTestManager(string fName, SqliteDb db)
+        {
+            var name = Path.GetFileName(fName);
+            var manager = new DbControl(name);
+            manager.Dock = DockStyle.Fill;
+            manager.Setup(db, _opService);
+
+            var odb = new OpenDatabase
+            {
+                Name = name,
+                Control = manager
+            };
+
+            _openDbs.Add(odb);
+            UpdateSelectedDb();
+        }
+
+        private void operationBrowserToolStripMenuItem_Click(object sender, EventArgs e)
+        {
+            var manager = new OperationBrowserControl();
+            manager.Dock = DockStyle.Fill;
+            manager.Setup(_opService);
+
+            using (var window = new Form())
+            {
+                window.Size = new Size(1024, 768);
+                window.StartPosition = FormStartPosition.CenterParent;
+                window.Text = "Operation Browser";
+                window.Controls.Add(manager);
+                window.ShowDialog();
+            }
+        }
+
+        private void createUnitTestTemplateDatabaseToolStripMenuItem_Click(object sender, EventArgs e)
+        {
+            saveFileDialog.Filter = "db files (*.db)|*.db|All files (*.*)|*.*";
+            saveFileDialog.RestoreDirectory = true;
+
+            if (saveFileDialog.ShowDialog() == DialogResult.OK)
+            {
+                var dbName = saveFileDialog.FileName;
+                if (!SqliteDbExtensions.IsReadOnly(dbName))
+                {
+                    //Open the database. SQLite will create the file for us
+                    var db = new SqliteDb();
+                    db.Open(dbName);
+
+                    //Create a template database for the unit test infrastructure
+                    db.CreateTemplate();
+
+                    OpenTestManager(dbName, db);
+                }
+            }
+        }
+
+        private void createDatabaseFromDumpToolStripMenuItem_Click(object sender, EventArgs e)
+        {
+            saveFileDialog.Filter = "db files (*.db)|*.db|All files (*.*)|*.*";
+            saveFileDialog.RestoreDirectory = true;
+
+            if (saveFileDialog.ShowDialog() == DialogResult.OK)
+            {
+                openFileDialog.Filter = "SQLite dump files (*.dump)|*.dump";
+                openFileDialog.RestoreDirectory = true;
+                if (openFileDialog.ShowDialog() == DialogResult.OK)
+                {
+                    var dbName = saveFileDialog.FileName;
+                    var dumpPath = openFileDialog.FileName;
+
+                    if (!SqliteDbExtensions.IsReadOnly(dbName))
+                    {
+                        //Open the database. SQLite will create the file for us
+                        var db = new SqliteDb();
+                        db.Open(dbName);
+
+                        db.LoadFromDump(dumpPath);
+
+                        OpenTestManager(dbName, db);
+                    }
+                }
+            }
+        }
+
+        private void lstOpenDbs_SelectedIndexChanged(object sender, EventArgs e)
+        {
+            UpdateSelectedDb();
+        }
+
+        private void UpdateSelectedDb()
+        {
+            var item = lstOpenDbs.SelectedItem as OpenDatabase;
+            if (item != null)
+            {
+                mainContainer.Panel2.Controls.Clear();
+                mainContainer.Panel2.Controls.Add(item.Control);
+            }
+        }
+    }
+}

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Workbench.resx
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Workbench.resx	                        (rev 0)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/Workbench.resx	2020-11-14 13:37:21 UTC (rev 9783)
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="mainMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <metadata name="openFileDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>127, 17</value>
+  </metadata>
+  <metadata name="saveFileDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>260, 17</value>
+  </metadata>
+  <metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>389, 17</value>
+  </metadata>
+  <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+  <data name="tsbOpenDb.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+</root>
\ No newline at end of file

Added: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/sqlite3.exe
===================================================================
(Binary files differ)

Index: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/sqlite3.exe
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/sqlite3.exe	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/sqlite3.exe	2020-11-14 13:37:21 UTC (rev 9783)

Property changes on: sandbox/jng/vanilla_swig/Bindings/src/Tools/MgTestAdmin/sqlite3.exe
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess	2020-11-14 13:37:21 UTC (rev 9783)

Property changes on: sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,2 ##
+bin
+obj
Modified: sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/PhpPostProcess.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/PhpPostProcess.csproj	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/PhpPostProcess/PhpPostProcess.csproj	2020-11-14 13:37:21 UTC (rev 9783)
@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
     <LangVersion>latest</LangVersion>
   </PropertyGroup>
 

Index: sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer	2020-11-14 13:37:21 UTC (rev 9783)

Property changes on: sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,2 ##
+bin
+obj
Modified: sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/StampVer.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/StampVer.csproj	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/StampVer/StampVer.csproj	2020-11-14 13:37:21 UTC (rev 9783)
@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
   </PropertyGroup>
 
 </Project>

Index: sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare	2020-11-14 13:37:21 UTC (rev 9783)

Property changes on: sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,2 ##
+bin
+obj
Modified: sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/SwigPrepare.csproj
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/SwigPrepare.csproj	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/SwigPrepare/SwigPrepare.csproj	2020-11-14 13:37:21 UTC (rev 9783)
@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
   </PropertyGroup>
 
 </Project>

Deleted: sandbox/jng/vanilla_swig/Bindings/src/Tools/Tools.sln
===================================================================
--- sandbox/jng/vanilla_swig/Bindings/src/Tools/Tools.sln	2020-11-14 10:58:50 UTC (rev 9782)
+++ sandbox/jng/vanilla_swig/Bindings/src/Tools/Tools.sln	2020-11-14 13:37:21 UTC (rev 9783)
@@ -1,79 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.26124.0
-MinimumVisualStudioVersion = 15.0.26124.0
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StampVer", "StampVer\StampVer.csproj", "{00311DA9-58F4-4460-B44E-2B60900A9E10}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwigPrepare", "SwigPrepare\SwigPrepare.csproj", "{63E7E7D7-1256-440B-8700-00D33E6DDDAE}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassMapGen", "ClassMapGen\ClassMapGen.csproj", "{505B50EF-9041-49AD-8B78-7EC6FE98D990}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhpPostProcess", "PhpPostProcess\PhpPostProcess.csproj", "{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}"
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|Any CPU = Debug|Any CPU
-		Debug|x64 = Debug|x64
-		Debug|x86 = Debug|x86
-		Release|Any CPU = Release|Any CPU
-		Release|x64 = Release|x64
-		Release|x86 = Release|x86
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Debug|x64.Build.0 = Debug|Any CPU
-		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Debug|x86.Build.0 = Debug|Any CPU
-		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Release|Any CPU.Build.0 = Release|Any CPU
-		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Release|x64.ActiveCfg = Release|Any CPU
-		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Release|x64.Build.0 = Release|Any CPU
-		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Release|x86.ActiveCfg = Release|Any CPU
-		{00311DA9-58F4-4460-B44E-2B60900A9E10}.Release|x86.Build.0 = Release|Any CPU
-		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Debug|x64.Build.0 = Debug|Any CPU
-		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Debug|x86.Build.0 = Debug|Any CPU
-		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Release|Any CPU.Build.0 = Release|Any CPU
-		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Release|x64.ActiveCfg = Release|Any CPU
-		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Release|x64.Build.0 = Release|Any CPU
-		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Release|x86.ActiveCfg = Release|Any CPU
-		{63E7E7D7-1256-440B-8700-00D33E6DDDAE}.Release|x86.Build.0 = Release|Any CPU
-		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Debug|x64.Build.0 = Debug|Any CPU
-		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Debug|x86.Build.0 = Debug|Any CPU
-		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Release|Any CPU.Build.0 = Release|Any CPU
-		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Release|x64.ActiveCfg = Release|Any CPU
-		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Release|x64.Build.0 = Release|Any CPU
-		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Release|x86.ActiveCfg = Release|Any CPU
-		{505B50EF-9041-49AD-8B78-7EC6FE98D990}.Release|x86.Build.0 = Release|Any CPU
-		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Debug|x64.Build.0 = Debug|Any CPU
-		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Debug|x86.Build.0 = Debug|Any CPU
-		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Release|Any CPU.Build.0 = Release|Any CPU
-		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Release|x64.ActiveCfg = Release|Any CPU
-		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Release|x64.Build.0 = Release|Any CPU
-		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Release|x86.ActiveCfg = Release|Any CPU
-		{DE54CB22-B574-4BD4-AC6B-0AEF19B56A93}.Release|x86.Build.0 = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-	GlobalSection(ExtensibilityGlobals) = postSolution
-		SolutionGuid = {5BB4C6B2-72AF-4F0A-8FB1-0575EBC08176}
-	EndGlobalSection
-EndGlobal



More information about the mapguide-commits mailing list