Operator SDK
Build MIDI processing and UI modes for the Operator device
Loading...
Searching...
No Matches
operator_sdk_sim.h File Reference

The mock device a mode is tested against. More...

#include <operator_sdk/abi/mode_api.h>
#include <cstddef>
#include <cstdint>

Namespaces

namespace  op
 Root namespace for the Operator SDK.
namespace  op::sim
 The mock device a mode is tested against on a host machine.

Macros

#define OP_MODE_UNDER_TEST()
 Declare everything a mode exports, so a test can call it.

Functions

const OperatorApi * op::sim::get_api ()
 The mock OperatorApi, ready to hand to a mode's init.
void op::sim::reset_state ()
 Put the device back to how it starts: the clock at zero, the queues and the framebuffer empty, every param zero, no seeded files.
void op::sim::apply_param_defaults (const ::op::modes::ParamSpec *specs, std::uint8_t count)
 Seed the params with the defaults a mode declares, the way the device does before it starts one.
void op::sim::advance_tick (std::uint32_t microseconds)
 Move the virtual clock forward, in microseconds.
void op::sim::set_pulse_count (std::uint32_t pulse)
 Set what api->get_pulse_count() reads.
void op::sim::set_beat_position (float beat)
 Set the pulse count in beats.
void op::sim::set_clock_state (std::uint8_t state)
 Set what api->get_clock_state() reads.
std::uint32_t op::sim::get_last_clock_period_us ()
 Read the period a mode last asked for with api->set_clock_period_us().
void op::sim::set_output_active (std::uint8_t output_index, bool active)
 Take one output away, so a mode meets an output that is not there.
std::uint16_t op::sim::drain_outgoing_midi (std::uint8_t output_index, OpMidiMessage *out, std::uint16_t max_messages)
 Read back the MIDI a mode sent to one output, up to max_messages of it, and empty the queue.
void op::sim::capture_framebuffer (std::uint8_t out[kFramebufferBytes])
 Copy the framebuffer out, as it stands after every draw a mode has made since the last reset_state().
void op::sim::seed_extras_file (const char *mode_name, const char *filename, const std::uint8_t *data, std::uint32_t size)
 Put a file where a mode will find it, at /extras/<mode name>/<filename>.
void op::sim::pick_filename (std::uint8_t param_id, const char *filename)
 Pick a file for a FilePicker param, as a player would on the device.
void op::sim::set_current_mode_name (const char *mode_name)
 Name the mode, which is what scopes its storage to /extras/<mode name>/.

Variables

constexpr std::size_t op::sim::kFramebufferBytes = 1024
 The size of the framebuffer, so a capture buffer can be stack-allocated.

Detailed Description

The mock device a mode is tested against.

Stands in for the hardware so a mode can be exercised in unit tests on a host machine. It carries a mock OperatorApi, a virtual clock, capture queues for the MIDI a mode sends, a framebuffer, and a small read-only store, so a test drives a mode exactly as the device does and reads back exactly what a player would see and hear.

Every test begins with reset_state(), which is what keeps one case from leaking into the next.

#include <doctest.h>
TEST_CASE("the mode plays the note it is given") {
mode_init(op::sim::get_api());
OpMidiMessage message {0x90, 60, 100, 0, 3};
mode_process(&message, 1, 0);
OpMidiMessage sent[4];
CHECK(op::sim::drain_outgoing_midi(0, sent, 4) == 1);
}
const OperatorApi * get_api()
The mock OperatorApi, ready to hand to a mode's init.
void reset_state()
Put the device back to how it starts: the clock at zero, the queues and the framebuffer empty,...
std::uint16_t drain_outgoing_midi(std::uint8_t output_index, OpMidiMessage *out, std::uint16_t max_messages)
Read back the MIDI a mode sent to one output, up to max_messages of it, and empty the queue.
The mock device a mode is tested against.
#define OP_MODE_UNDER_TEST()
Declare everything a mode exports, so a test can call it.
Definition operator_sdk_sim.h:185
A single MIDI message passed to a mode's process function.
Definition mode_api.h:22

Macro Definition Documentation

◆ OP_MODE_UNDER_TEST

#define OP_MODE_UNDER_TEST ( )

Declare everything a mode exports, so a test can call it.

Put it once at the top of a test file, under the includes. It brings in the lifecycle (mode_init, mode_process, mode_destroy), the screen (mode_ui_render, mode_ui_gesture) and the param tables (kParams, kParamCount, kParamStrings), and the linker takes only what the test calls.

It also declares op::sim::ui_gesture, which takes the scoped op::Gesture and op::GestureType enums, so a test says the gesture it means and the narrowing to the ABI's bytes happens in one place.

TEST_CASE("the mode boots") { mode_init(op::sim::get_api()); }