gc_plugin_abi 0.5.0

Gridcore Plugin API
Documentation
//! # Testing utilities for plugins
//! This submodule contains the necessary utilities to test plugins without having to load the core gateway.
//!
//! ## How does it work
//! The [GCGatewayMockBuilder] is used to create a mock of the core gateway without by providing multiple options such as injecting datapoints, configurations and setting functions to
//! intercept calls to the core gateway.
//! Once [GCGatewayMockBuilder::start] is called, it will start the plugin and yield and instance of [GCGatewayMock] which can be used to interact with the plugin.
//!
//! ## How to do integration tests for a plugin
//! Ensure that the plugin imports the "testing" feature from the gc_plugin_abi crate.
//! ```toml
//! [dev-dependencies]
//! gc_plugin_abi = { version = "*", features = ["testing"] }
//! ```
//!
//! Using the same sample plugin found in [crate], it can be tested by creating a mock gateway and sending datapoints to it.
//! ```rust
//! # use gc_plugin_abi::{GCBorrowedDatapointValue, GCPluginInfo, GCPluginInterface, GCPluginInstance, gc_plugin, GCDatapointValue};
//! # use gc_plugin_abi::testing::{GCGatewayMockBuilder, GCGatewayMock};
//! # use std::sync::Arc;
//! # use std::sync::atomic::{AtomicBool, Ordering};
//! # #[gc_plugin]
//! # pub struct ExamplePlugin<'a> {
//! #     plugin_interface: &'a GCPluginInterface,
//! # }
//! # impl<'a> ExamplePlugin<'a> {
//! #    pub fn new(plugin_interface: &'a GCPluginInterface) -> Self {
//! #        Self {
//! #            plugin_interface: plugin_interface,
//! #        }
//! #    }
//! # }
//! #
//! # impl<'a> GCPluginInstance<'a> for ExamplePlugin<'a> {
//! #
//! #     // Entrypoint which contains the interface to communicate with the core gateway
//! #     fn init(plugin_interface: &'a GCPluginInterface) -> Box<Self> {
//! #         let state = Box::new(ExamplePlugin::new(plugin_interface));
//! #         state
//! #     }
//! #
//! #     fn get_plugin_info() -> GCPluginInfo {
//! #         GCPluginInfo::new("ExamplePlugin", "0.1.0", "0", 0)
//! #     }
//! #
//! #     // Receive datapoint by subscription
//! #     fn receive_datapoint(&self, _data_value: GCBorrowedDatapointValue) -> bool {
//! #         // Handle incoming datapoint
//! #         true
//! #     }
//! # }
//! #[test]
//! fn test_plugin_info(){
//!     let info = GCGatewayMock::<ExamplePlugin>::get_plugin_info();
//!     assert_eq!(info.name(), "ExamplePlugin");
//!     assert_eq!(info.version(), "0.1.0");
//!     assert_eq!(info.plugin_ifc_version(), "0");
//!     assert_eq!(info.features(), 0);
//! }
//!
//! #[test]
//! fn test_plugin() {
//!     // GIVEN
//!     let name = CString::new("example_name").unwrap();
//!     let description = CString::new("example_description").unwrap();
//!     let unit = CString::new("example_unit").unwrap();
//!
//!     let assert_publisher_called = Arc::new(AtomicBool::new(false));
//!
//!     let mut mock_builder = GCGatewayMockBuilder::new();
//!     mock_builder.add_subscribed_datapoint(GCDatapoint::new(0, &name, &description, &unit));
//!     mock_builder.set_publish_datapoint_value_callback(Box::new(|value: &GCDatapointValue| {
//!         assert_publisher_called.store(true, Ordering::SeqCst);
//!         return true;
//!     }));
//!
//!     // WHEN
//!     let gateway_mock: GCGatewayMock<RerunPluginState> = mock_builder.start().unwrap();
//!     let dpv = GCDatapointValue::new_u32(0, 20, 20, GCDatapointValueQuality::new_good());
//!     gateway_mock.send_subscribed_datapoint(&dpv);
//!
//!     // THEN
//!     assert_eq!(assert_publisher_called.load(Ordering::SeqCst), true);
//! }
//!
//! ```
//!
//! ## Retrieving [crate::GCPluginInterface] for unit testing
//! It can also be useful to retrieve the [crate::GCPluginInterface] for unit testing without starting the plugin, this can be achieved by using the builder
//! and calling [GCGatewayMockBuilder::build], this will yield a [GCGatewayMock] but will not start the plugin. It can then be used to retrieve the [crate::GCPluginInterface]
//! upon calling [GCGatewayMock::get_gateway_interface].
//! ```rust
//! # use gc_plugin_abi::{GCBorrowedDatapointValue, GCPluginInfo, GCPluginInterface, GCPluginInstance, gc_plugin, GCDatapointValue};
//! # use gc_plugin_abi::testing::{GCGatewayMockBuilder, GCGatewayMock};
//! # use std::sync::Arc;
//! # use std::sync::atomic::{AtomicBool, Ordering};
//! # #[gc_plugin]
//! # pub struct ExamplePlugin<'a> {
//! #     plugin_interface: &'a GCPluginInterface,
//! # }
//! # impl<'a> ExamplePlugin<'a> {
//! #    pub fn new(plugin_interface: &'a GCPluginInterface) -> Self {
//! #        Self {
//! #            plugin_interface: plugin_interface,
//! #        }
//! #    }
//! # }
//! #
//! # impl<'a> GCPluginInstance<'a> for ExamplePlugin<'a> {
//! #
//! #     // Entrypoint which contains the interface to communicate with the core gateway
//! #     fn init(plugin_interface: &'a GCPluginInterface) -> Box<Self> {
//! #         let state = Box::new(ExamplePlugin::new(plugin_interface));
//! #         state
//! #     }
//! #
//! #     fn get_plugin_info() -> GCPluginInfo {
//! #         GCPluginInfo::new("ExamplePlugin", "0.1.0", "0", 0)
//! #     }
//! #
//! #     // Receive datapoint by subscription
//! #     fn receive_datapoint(&self, _data_value: GCBorrowedDatapointValue) -> bool {
//! #         // Handle incoming datapoint
//! #         true
//! #     }
//! # }
//!
//! let mut mock_builder = GCGatewayMockBuilder::new();
//! let mock: GCGatewayMock<ExamplePlugin> = mock_builder.build().unwrap(); // Does not start the plugin
//! let gateway_interface: &GCPluginInterface = mock.get_gateway_interface();
//!
//! ```
//!
//! ## Testing for memory leaks
//! This can be achieved by using crates such as [allocation_counter](https://crates.io/crates/allocation_counter) or [dhat](https://crates.io/crates/dhat).
//!
//! It is recommended to run each integration test in it's own file, this is because cargo run creates one binary per each integration test file and run
//! each one in it's own process, thus ensuring that different tests do not interfere with each other.

#![allow(clippy::test_attr_in_doctest)]

mod errors;
mod gateway_instance_ctx;
mod gateway_mock;
mod gateway_mock_builder;
mod gateway_mock_extern;
pub use errors::GCMockingError;
pub use gateway_mock::GCGatewayMock;
pub use gateway_mock_builder::GCGatewayMockBuilder;