Code generation & integration

Popili provides automated code generation from your verified Coco, with end-to-end traceability between the two. Our code generators are built in an intelligent way to ensure they’re deterministic and efficient – minimising changes that come from updates to your Coco source code.

Coco

state Idle {
  client.initialise(setting) = {
    val lightOk : Bool = lights.initialise(setting);
    val barrierOk : Bool = barriers.initialise(setting);
    if (lights.initialise(setting) && barriers.initialise(setting)) {
      setNextState(Initialising);
      return true;
    } else {
      setNextState(Recovery(lightOk, barrierOk));
      return false;
    }
  }
}

External components

Coco systems need to interact with non-Coco code – external components make this easy. When Coco has to generate code for an external component, it instead generates a skeleton – along with comments explaining how to create a suitable implementation. Logging and locking code is automatically generated if needed, making it easy to build safe and reliable integrations.

  • TrafficControlImpl BarrierController Coco Non-Coco client : BarrierControl
  • @runtime(.MultiThreaded)
    external component BarrierController {
      val client : Provided<BarrierControl>
    }
    
    port BarrierControl {
      function initialise() : Bool
      function open() : Nil
      function close() : Nil
      outgoing signal status(isOpen : Bool)
    
      machine {
        // …
      }
    }
  • /**
    \code
    Header:
    
    #include "ExternalComponent.h"
    
    class BarrierControllerImpl : public BarrierController {
     protected:
      bool client_initialise() override;
      void client_open() override;
      void client_close() override;
      void execute_start() override;
      void execute_unused() override;
    };
    Implementation:
    
    bool BarrierControllerImpl::client_initialise() {}
    
    void BarrierControllerImpl::client_open() {}
    
    …
    
    \endcode
    */

Integration testing

Testing Coco-based systems is simple thanks to our built-in mock generators. For supported languages, Popili can automatically generate mocks for components, so you can quickly test whether the platform is interacting with the hand-written code as expected. For example, our C++ generator automatically generates gMocks for external components.

  • Coco software Auto-generated mocks for external components BridgeControlImpl Gui LiftController BarrierController TrafficControlImpl BridgeControl LiftControl BarrierControl
  • TEST_F(BridgeTest, BridgeOpen) {
      BridgeSystem bridge(bridge_arguments);
      bridge.set_name("mt_bridge");
      bridge.set_logger_recursively(coco::StreamMachineLogger::cerr());
    
      // When we receive the ready signal we need to call terminate.
      EXPECT_CALL(gui_mock, bridge_initialised())
          .WillOnce(helper.InvokeAsync(Invoke(&gui_mock.bridge().connected(), 
                                              &BridgeControlProvided::prepareToOpen)));
      EXPECT_CALL(gui_mock, bridge_ready()).WillOnce(helper.InvokeAsync(gui_mock.coco_shutdown_action()));
      EXPECT_CALL(gui_mock, bridge_trafficStopped())
          .WillOnce(helper.InvokeAsync(Invoke(&gui_mock.bridge().connected(),
                                              &BridgeControlProvided::confirmClearToOpen)));
    
      // Make sure the road lights are turned on and off
      EXPECT_CALL(light_mock, client_onOff(false)).Times(AtLeast(2));
      EXPECT_CALL(light_mock, client_onOff(true)).Times(AtLeast(2));
    
      // Check we close the traffic
      EXPECT_CALL(barrier_mock, client_close(BarrierControl::BarrierNames::Entry))
          .WillOnce(helper.InvokeAsync(barrier_mock.send_client_ready_action(), 
                                       std::chrono::seconds(1)));
      …
    }

Logging

Coco’s generated code automatically includes logging of all events in a standardised format designed to be both human and machine-readable. This allows exact executions to be recorded from your system during testing or production, and then Popili provides various tools to analyse these logs to validate whether your requirements are met.

> traffic (Operational.TrafficFlowing): client.stopTraffic{}
> light (Operational): client.flashingOn{}
> halLight: client.onOff{onOff=true}
< halLight: client.onOff{}
< light (Operational.Flashing): client.flashingOn{}
< traffic (Stopping.SettingLights): client.stopTraffic{}

Tool integration

Popili is designed to integrate into modern CI/CD pipelines. Every feature – from automated formatting, to verification, to code generation – has a fully featured command-line interface, providing machine-readable output and enabling easy integration into custom scripts. If the command-line tools aren’t enough, our extensive Java API can implement custom tooling.

Further, our code generators can output a structured description of every part of the auto-generated code. This means you can construct custom tooling to auto-generate further code that fully integrates with the platform. So you can automatically generate bindings from Coco-generated code to other languages, generate customised mocks for bespoke testing frameworks, or even auto-generate RPC implementations.

Next step

Popili

Explore more

Make your software development more effective and efficient

Book a demo