Grasshopper and Unit tests with XUnit

Hi,

I have been looking for a way how to perform Unit test on .gh files with my on scripts. These scripts contains my custom components which were coded with C# in visual studio. I have created my own plugin. If I run that script in Rhino it works perfectly.

I tried to dig into unit testing of that .gh files with my components. I started with this repo which I was able to replicate and successfully run. However, If I modify .gh file to use my components and unit test to insert data to a parameter and recompute some of my components it fails. After some investigation I found out few problems which may or may not occurre:

  • If I run more then one class with unit tests at once it fails during disposing grasshopper assembly (happens sometimes, few cases)
  • If I run more then one class with unit tests at once it fails and throw error during redrawing rhino active doc. (I understand that rhino is open in headless mode and active doc == null, my code in the plugin is not ready for this option) (happens sometimes, few cases)
  • If I run only one class with one test it works. However when, compute my custom component which should produce my own class as a output it do so but some of the output class properties are not correctly set or calculated. That cause some problems in the downstream objects and test result is fail. (happens every time). However, on default components (circle, line, etc.) it works smoothly.

Last issue is most challenging and the worst as it prevents me from using further using of unit tests.

I have also read many of the topics regarding unit testing at this forum. Unfortunately, non of it solve my problem. There are some topics, regarding similar problems.

I have also found there is a few more test repos
tmakin/RhinoCommonUnitTesting: Example of unit testing RhinoCommon from within the Visual Studio test runner on windows (github.com)

and

mcneel/Rhino.Testing: NUnit dotnet unit testing for Rhino3D (github.com)

which I am not sure if they are compatible with Rhino 7 and can run .gh files and test them.

So my questions are:

  1. Can I use unit tests with .gh file and custom components?
  2. Why only part of output data are computed correctly?
  3. Is it possible to run many unit test on multiple .gh files?
  4. What is the best option to preform such unit test with .gh files?

Many thanks for you time and reply. Any help is much appreciated.

Best Ondřej

Code of my unit tests:

  public class CenterLineFixture : Rhino.Test.GrasshopperFixture
  {
      public CenterLineFixture() : base("CenterLine.gh") { }
  }

public class CenterLine : IClassFixture<CenterLineFixture>
{
  CenterLineFixture fixture { get; set; }

  public CenterLine(CenterLineFixture fixture)
  {
      this.fixture = fixture;
  }
  /// This test works.
  [Fact]
  public void CircleTest()
  {
      List<double> doubles = new List<double>() { 1, 2, 3, 4, 5 };
      foreach (var obj in (fixture.Doc.Objects))
      {
          if (obj is Grasshopper.Kernel.IGH_Param param)
          {
              if (param.NickName == "Radii")
              {
                  for (var i = 0; i < doubles.Count; i++)
                  {
                      (param as Grasshopper.Kernel.Parameters.Param_Number).AddVolatileData(new Grasshopper.Kernel.Data.GH_Path(0), i, doubles[i]);
                  }
                  param.CollectData();
                  param.ComputeData();

                  Assert.Equal(doubles.Count, param.VolatileData.DataCount);
              }
          }
      }
      foreach (var obj in (fixture.Doc.Objects))
      {
          if (obj is Grasshopper.Kernel.IGH_Param param)
          {
              if (param.NickName == "Circle")
              {
                  param.CollectData();
                  param.ComputeData();

                  Assert.Equal(doubles.Count, param.VolatileData.DataCount);
                  var data = param.VolatileData.AllData(true).GetEnumerator();
                  data.Reset();
                  int i = 0;
                  while (data.MoveNext())
                  {
                      var curve = data.Current;
                      Assert.True(curve.CastTo(out Curve curveToTest), "Cannot cast curve");
                      Assert.True(curveToTest.IsClosed, "Curve is not closed");
                      i++;
                  }
                  return;
              }
          }

      }
  }
  /// This test does not work.
  [Fact]
  public void TunnelSectionTest()
  {
      foreach (var obj in (fixture.Doc.Objects))
      {
          if (obj is Grasshopper.Kernel.IGH_Component param)
          {
              /// Just get the custom component
              if (param.NickName == "BoredSection")
              {
                  /// Recompute custom component
                  param.ExpireSolution(true);
                  param.CollectData();
                  param.ComputeData();

                  /// Get output data of custom component. Output is custom class with properties. Some of them are not correctly computed.
                  object sectionClass = param.Params.Output.Last();
                  Assert.Equal(22, param.Params.Output.Count);
              }
          }
      }
  }

}

I’ve also set up unit tests with xUnit for a GH plugin, but I chose to not start a specific file in that setup. However, my setup is based on McNeels sample project, which is written so you can use specific gh files. It can be found here:

When I turned of xUnits parallelization it worked fine for me to run multiple test classes. I still had some issues when using multiple test projects in one solution if I ran the tests from the Visual Studio test explorer as I couldn’t turn of the parallelisation between projects, but it works fine if I start the tests from the command line with dotnet vstest.

Otherwise, Rhino.Testing is now in beta and seems very promising. It’s for NUnit though.

Thanks for the reply.

I develop my solution from same repo as you.

rhino-developer-samples/rhino.inside/dotnet/SampleUnitTests at 8 · mcneel/rhino-developer-samples (github.com)

I turn off parallelization it works as well. But my main problem is that, my custom class have only some of the properties computed Others are in their default state. I don’t know if there is some kinds of usage limit (which I have reached) or I am doing something wrong. If I use it on components which simply calculates geometry (line, arc, etc) it works.

Regarding the Rhino.Testing, I am not sure, if it can run .gh file and iterate over the single component output paramters. I will need to dig into it, but from the description it seems that you can only evaluate if “Context Bake” component is true. So it is usable when .gh scripts are little but modified.

Thanks

Ondřej