SDK Rhino.Inside C++: ‘ExecuteCommand()’ and headless mode

Hello there!

I am running an executable that fires commands locally with Rhino.Inside for C++ in headless mode (with the hope of deploying it in CI server):

CRhinoCreateDocumentOptions options;
options.SetCreateHeadless(true);
int doc_runtime_serial_number = CRhinoDoc::CreateDocument(nullptr, &options);
CRhinoDoc* pDoc = CRhinoDoc::FromRuntimeSerialNumber(doc_runtime_serial_number);

Now, I noticed that if I run my commands with:

pDoc->ExecuteCommand(cmdName);

It works, but if I run with:

pDoc->RunScript(cmdName, 1);

It throws an error. How come?

In the SDK interface it’s also written that the ExecutedCommand()will be dropped in future..

Hi @andrea.settimi,

Rhino.Inside C++ is experimental. I’ll have to setup a test case, based on the sample, in order to give you an answer.

Why are you trying to run commands on a headless document? Is your plug-in code not available to your command line utility?

Hopefully the commands have no UI or command line interaction.

– Dale

Hello @dale !

Thanks for taking the time, and just want to say that the Rhino.Inside C++ is quite something and enjoying very much running inside in C++. it’s amazing so thanks!

What I am trying to do is add a testing framework to my Rhino plugin.

It’s compiled in cmake and all the linking works both for the plugin compilation of .rhp and the test suite compilation. Here’s a quick diagram for context:

So, I am using Rhino.Inside C++ (amazing again) together with gtest to run integration and unit testing on the commands. This is because I want to run my suite nicely from my console but also potentially on a CI server.

So unit testing is easy, and for integration/smoke test (aka running the command in Rhino), I found the following way:

Given my command like this:

CRhinoCommand::result CCommandcmake4rhino::RunCommand(const CRhinoCommandContext& context)
{
  ...

  // Testing text input
  CRhinoGetString gs;
  gs.SetCommandPrompt(L"Testing input");
  gs.GetString();
  if (gs.CommandResult() != CRhinoCommand::success)
    return gs.CommandResult();
  ON_wString block_name = gs.String();
  block_name.TrimLeftAndRight();
  if (block_name.IsEmpty())
    return CRhinoCommand::cancel;


  // add a simple curve circle to test geometries
  ON_3dPoint center(0, 0, 0);
  double radius = 10.0;
  ON_Circle circle( center, radius );
  ON_ArcCurve arc_curve( circle );
  CRhinoCurveObject* curve_object = context.m_doc.AddCurveObject( arc_curve );
  context.m_doc.Redraw();

  ...
}

And my test is the following: it seemed to me using macros was the easiest way to test the integration of the commands..

#include <gtest/gtest.h>

#include "pch.h"
#include "RhinoCore.h"

TEST(TEST_INTEGRATION_CommandsSuite, MyIntegrationTest) {
    CRhinoCreateDocumentOptions options;
    options.SetCreateHeadless(true);
    int doc_runtime_serial_number = CRhinoDoc::CreateDocument(nullptr, &options);
    CRhinoDoc* pDoc = CRhinoDoc::FromRuntimeSerialNumber(doc_runtime_serial_number);

    const wchar_t* cmdName = L"!_cmake4rhino \"test\" _Enter";
    bool resCmd = pDoc->ExecuteCommand(cmdName);

    ASSERT_TRUE(resCmd);
}

And the test is working locally:

So I haven’t tested UI but in this scenario I would always test it with the - “no dialog flag”.

Nevertheless, command line interaction seems to work as shown in the the example above.

So here are my doubts @dale

  • is there a better way to build a test suite for a .rhp plugin in Rhino?
  • what is running behind the curtains of SetCreateHeadless() and what are their limits?

This is such an interesting topic and having a feedback on this would be great! Thanks in advance :raising_hands:

Just a quick update: the command RunCommand() in headless mode does not add or take in consideration any geometry related, if I run RunScript() NOT in headless mode, it will add geometries too. Now my question is: so I suppose that in headless mode geometries cannot be tested, but will RunScript() in CI server? I am now becoming even more curious to know more about the SetCreateHeadless() function..

Hi @andrea.settimi,

I’m assuming you’re running Rhino inside of some C++ console application. Please correct me if my assumption is wrong.

In this case, there is no Rhino UI for command to interact with. That is, your command prompts the for a string - CRhinoGetString - and there is no Rhino command line.

I would think that anyone writing test suites would remove the user interface element and focus strictly on geometry opeations; comparing the results and looking for regressions.

I believe that is what these tests does:

@fraguada is a far better resource on this topic than I.

– Dale

Hello @dale thanks for your time as usual. Yes correct!

Indeed, then my question to @dale is more a clarification on the role of options.SetCreateHeadless(true) , if this is not allowing the generation of geometries when I run RunScript() what’s its role in the API?

And related to the test unit, the two resources posted by @dale are amazing, but I am looking for something in C++ and that could run on CI server (either headless or simulating graphics for testing geometries?). Can you point to some existing resources to this matter @fraguada ? :raising_hands:

Many thanks again for your insights!