1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! # 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.
pub use GCMockingError;
pub use GCGatewayMock;
pub use GCGatewayMockBuilder;