Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
exports_files([
|
||||
"logging.ini",
|
||||
])
|
||||
@@ -0,0 +1,5 @@
|
||||
filegroup(
|
||||
name = "errors",
|
||||
srcs = glob(["*.yaml"]),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
description: Example errors
|
||||
errors:
|
||||
- name: ExampleErrorA
|
||||
description: Example error A
|
||||
- name: ExampleErrorB
|
||||
description: Example error B
|
||||
- name: ExampleErrorC
|
||||
description: Example error C
|
||||
- name: ExampleErrorD
|
||||
description: Example error D
|
||||
@@ -0,0 +1,10 @@
|
||||
description: More errors.
|
||||
errors:
|
||||
- name: ExampleErrorA
|
||||
description: >-
|
||||
The same error as in example_errors.yaml. The error names are placed in a
|
||||
namespace
|
||||
- name: MoreError
|
||||
description: additinal error
|
||||
- name: snake_case_error
|
||||
description: We will correct snake cases
|
||||
@@ -0,0 +1,2 @@
|
||||
description: No errors.
|
||||
errors: []
|
||||
@@ -0,0 +1,5 @@
|
||||
filegroup(
|
||||
name = "interfaces",
|
||||
srcs = glob(["*.yaml"]),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
description: An empty interface.
|
||||
@@ -0,0 +1,5 @@
|
||||
description: >-
|
||||
We make sure that we allow duplicate errors
|
||||
errors:
|
||||
- reference: /errors/example_errors#/ExampleErrorA
|
||||
- reference: /errors/example_errors
|
||||
@@ -0,0 +1,5 @@
|
||||
description: >-
|
||||
We make sure that we respect multiple error definitions
|
||||
errors:
|
||||
- reference: /errors/example_errors
|
||||
- reference: /errors/more_errors
|
||||
@@ -0,0 +1,4 @@
|
||||
description: >-
|
||||
We make sure that we can parse the error files which don't define any errors
|
||||
errors:
|
||||
- reference: /errors/no_errors
|
||||
@@ -0,0 +1,5 @@
|
||||
description: >-
|
||||
We make sure that we respect a subset of errors
|
||||
errors:
|
||||
- reference: /errors/example_errors#/ExampleErrorA
|
||||
- reference: /errors/example_errors#/ExampleErrorB
|
||||
@@ -0,0 +1,20 @@
|
||||
description: >-
|
||||
This interface defines an example interface that uses multiple framework
|
||||
features
|
||||
cmds:
|
||||
uses_something:
|
||||
description: This command checks if something is stored under a given key
|
||||
arguments:
|
||||
key:
|
||||
description: Key to check the existence for
|
||||
type: string
|
||||
pattern: ^[A-Za-z0-9_.]+$
|
||||
result:
|
||||
description: Returns 'True' if something was stored for this key
|
||||
type: boolean
|
||||
vars:
|
||||
max_current:
|
||||
description: Provides maximum current of this supply in ampere
|
||||
type: number
|
||||
errors:
|
||||
- reference: /errors/example_errors
|
||||
@@ -0,0 +1,24 @@
|
||||
# for documentation on this file format see:
|
||||
# https://www.boost.org/doc/libs/1_54_0/libs/log/doc/html/log/detailed/utilities.html#log.detailed.utilities.setup.filter_formatter
|
||||
|
||||
[Core]
|
||||
DisableLogging=false
|
||||
|
||||
# To get debug logs of only one module, add the "%Process% contains" filter, e.g.:
|
||||
#
|
||||
# "(%Process% contains OCPP201 and %Severity% >= DEBG)"
|
||||
#
|
||||
# whereas "OCPP201" is the value of the field `active_modules.NAME.module` in the respective /config/config-*.yaml.
|
||||
Filter="%Severity% >= INFO"
|
||||
|
||||
[Sinks.Console]
|
||||
Destination=Console
|
||||
# Filter="%Target% contains \"MySink1\""
|
||||
Format="%TimeStamp% [%Severity%] \033[1;32m%Process%\033[0m \033[1;36m%function%\033[0m \033[1;30m%file%:\033[0m\033[1;32m%line%\033[0m: %Message%"
|
||||
Asynchronous=false
|
||||
AutoFlush=true
|
||||
SeverityStringColorDebug="\033[1;30m"
|
||||
SeverityStringColorInfo="\033[1;37m"
|
||||
SeverityStringColorWarning="\033[1;33m"
|
||||
SeverityStringColorError="\033[1;31m"
|
||||
SeverityStringColorCritical="\033[1;35m"
|
||||
@@ -0,0 +1 @@
|
||||
exports_files(["smoke_test.sh"])
|
||||
@@ -0,0 +1,96 @@
|
||||
load("@rules_python//python:defs.bzl", "py_test")
|
||||
load("@rules_rust//cargo:defs.bzl", "cargo_build_script")
|
||||
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test")
|
||||
load("@rules_shell//shell:sh_test.bzl", "sh_test")
|
||||
load("//applications/utils:requirements.bzl", "requirement")
|
||||
load("//lib/everest/framework/bazel:everest_env.bzl", "everest_env", "everest_test")
|
||||
load("//lib/everest/framework/bazel:modules_def.bzl", "rs_everest_module")
|
||||
load("//third-party/bazel/toolchains:defs.bzl", "CROSS_PYTHON_INCOMPATIBLE")
|
||||
|
||||
cargo_build_script(
|
||||
name = "build_script",
|
||||
srcs = ["build.rs"],
|
||||
build_script_env = {
|
||||
"EVEREST_CORE_ROOT": "../..",
|
||||
},
|
||||
data = [
|
||||
"manifest.yaml",
|
||||
"//lib/everest/framework/everestrs/tests/errors",
|
||||
"//lib/everest/framework/everestrs/tests/interfaces",
|
||||
"//lib/everest/framework/everestrs/tests/types",
|
||||
],
|
||||
edition = "2021",
|
||||
deps = [
|
||||
"//lib/everest/framework/everestrs/everestrs-build",
|
||||
],
|
||||
)
|
||||
|
||||
rust_binary(
|
||||
name = "RsAsyncBinary",
|
||||
srcs = glob(["src/**/*.rs"]),
|
||||
edition = "2021",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":build_script",
|
||||
"//lib/everest/framework/everestrs/everestrs",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_bridge",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_sys",
|
||||
"@everest_framework_crate_index//:log",
|
||||
"@everest_framework_crate_index//:tokio",
|
||||
],
|
||||
)
|
||||
|
||||
rs_everest_module(
|
||||
name = "RsAsync",
|
||||
binary = ":RsAsyncBinary",
|
||||
manifest = "manifest.yaml",
|
||||
)
|
||||
|
||||
everest_env(
|
||||
name = "config_env",
|
||||
config_file = "config.yaml",
|
||||
modules = [
|
||||
":RsAsync",
|
||||
],
|
||||
)
|
||||
|
||||
sh_test(
|
||||
name = "integration_test",
|
||||
srcs = ["//lib/everest/framework/everestrs/tests/modules:smoke_test.sh"],
|
||||
data = [":config_env"],
|
||||
tags = ["exclusive"],
|
||||
)
|
||||
|
||||
# Integration tests for RsAsync. Will launch EVerest with a ProbeModule and
|
||||
# validate calls to our trait mocks.
|
||||
rust_test(
|
||||
name = "RsAsyncMockedTestBinary",
|
||||
srcs = ["rs_tests/tests.rs"],
|
||||
compile_data = [
|
||||
"config.yaml",
|
||||
"manifest.yaml",
|
||||
"//lib/everest/framework/everestrs/tests/errors",
|
||||
"//lib/everest/framework/everestrs/tests/interfaces",
|
||||
"//lib/everest/framework/everestrs/tests/types",
|
||||
],
|
||||
crate_features = [
|
||||
"mockall",
|
||||
"trait",
|
||||
],
|
||||
data = ["config_env"],
|
||||
edition = "2021",
|
||||
proc_macro_deps = [
|
||||
"//lib/everest/framework/everestrs/everestrs-derive",
|
||||
"@everest_framework_crate_index//:mockall_double",
|
||||
],
|
||||
rustc_env = {
|
||||
"CARGO_MANIFEST_DIR": "lib/everest/framework/everestrs/tests/modules/RsAsync",
|
||||
"EVEREST_CORE_ROOT": "lib/everest/framework/everestrs/tests",
|
||||
},
|
||||
tags = ["exclusive"],
|
||||
deps = [
|
||||
"//lib/everest/framework/everestrs/everestrs",
|
||||
"@everest_framework_crate_index//:mockall",
|
||||
"@everest_framework_crate_index//:tokio",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
use everestrs_build::Builder;
|
||||
|
||||
pub fn main() {
|
||||
Builder::new(
|
||||
"manifest.yaml",
|
||||
vec![std::env::var("EVEREST_CORE_ROOT").unwrap_or("../../..".to_string())],
|
||||
)
|
||||
.generate()
|
||||
.unwrap();
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=manifest.yaml");
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Test config for regression tests for the framework.
|
||||
active_modules:
|
||||
example_0:
|
||||
module: RsAsync
|
||||
connections:
|
||||
receiver:
|
||||
- module_id: example_1
|
||||
implementation_id: sender
|
||||
example_1:
|
||||
module: RsAsync
|
||||
connections:
|
||||
receiver:
|
||||
- module_id: example_0
|
||||
implementation_id: sender
|
||||
@@ -0,0 +1,14 @@
|
||||
description: Simple Example
|
||||
provides:
|
||||
sender:
|
||||
interface: example
|
||||
description: An example interface.
|
||||
requires:
|
||||
receiver:
|
||||
interface: example
|
||||
ignore:
|
||||
errors: true
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- Everest authors
|
||||
@@ -0,0 +1,60 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
#[tokio::test]
|
||||
#[everestrs::test(config = "config.yaml", module = "example_1", harness = true)]
|
||||
async fn test_tokio_everest(module: &Module) {
|
||||
use super::*;
|
||||
use generated::*;
|
||||
|
||||
let mock_service = Arc::new(MockExampleServiceSubscriber::new());
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let mut mock_client = MockExampleClientSubscriber::new();
|
||||
mock_client
|
||||
.expect_on_max_current()
|
||||
.withf(|_, value| *value == 123.0)
|
||||
.times(1)
|
||||
.return_once(move |_, _| {
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
let mock_client = Arc::new(mock_client);
|
||||
|
||||
let mut mock_on_ready = MockOnReadySubscriber::new();
|
||||
mock_on_ready.expect_on_ready().times(1).return_once(|_| ());
|
||||
|
||||
let _pub = module.start(Arc::new(mock_on_ready), mock_service, mock_client);
|
||||
|
||||
// Wait for RsExample's on_ready to publish max_current(123.0).
|
||||
rx.await.expect("Timed out waiting for on_max_current");
|
||||
}
|
||||
|
||||
#[everestrs::test(config = "config.yaml", module = "example_1", harness = true)]
|
||||
#[tokio::test]
|
||||
async fn test_everest_tokio(module: &Module) {
|
||||
use super::*;
|
||||
use generated::*;
|
||||
|
||||
let mock_service = Arc::new(MockExampleServiceSubscriber::new());
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let mut mock_client = MockExampleClientSubscriber::new();
|
||||
mock_client
|
||||
.expect_on_max_current()
|
||||
.withf(|_, value| *value == 123.0)
|
||||
.times(1)
|
||||
.return_once(move |_, _| {
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
let mock_client = Arc::new(mock_client);
|
||||
|
||||
let mut mock_on_ready = MockOnReadySubscriber::new();
|
||||
mock_on_ready.expect_on_ready().times(1).return_once(|_| ());
|
||||
|
||||
let _pub = module.start(Arc::new(mock_on_ready), mock_service, mock_client);
|
||||
|
||||
// Wait for RsExample's on_ready to publish max_current(123.0).
|
||||
rx.await.expect("Timed out waiting for on_max_current");
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#![allow(non_snake_case)]
|
||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||
|
||||
use generated::{
|
||||
Context, ExampleClientSubscriber, ExampleServiceSubscriber, Module, ModulePublisher,
|
||||
OnReadySubscriber,
|
||||
};
|
||||
use std::sync::{Arc, Mutex};
|
||||
// Just to use async.
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::time::sleep;
|
||||
|
||||
pub struct OneClass {
|
||||
tx: Mutex<Option<oneshot::Sender<String>>>,
|
||||
}
|
||||
|
||||
impl ExampleServiceSubscriber for OneClass {
|
||||
fn uses_something(&self, _context: &Context, key: String) -> ::everestrs::Result<bool> {
|
||||
log::info!("Received {key}");
|
||||
let tx = self.tx.lock().unwrap().take();
|
||||
if let Some(tx) = tx {
|
||||
tx.send(key).unwrap();
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExampleClientSubscriber for OneClass {
|
||||
fn on_max_current(&self, context: &Context, value: f64) {
|
||||
log::info!("Received {value}");
|
||||
context
|
||||
.publisher
|
||||
.receiver
|
||||
.uses_something(format!("{value}"))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl OnReadySubscriber for OneClass {
|
||||
fn on_ready(&self, _publishers: &ModulePublisher) {
|
||||
log::info!("Ready");
|
||||
}
|
||||
}
|
||||
|
||||
/// Example how to use async with Everest. Everything in EVerest (all traits)
|
||||
/// remain strictly sync because of the underlying c++ runtime. However, you can
|
||||
/// combine your async code with sync EVerest.
|
||||
///
|
||||
/// You can combine the `everestrs::main` macro with `tokio::main` macro. The
|
||||
/// ordering does not really matter, so for non-main function (functions which
|
||||
/// can receive input args), you can also write
|
||||
/// ```ignore
|
||||
/// #[tokio::main]
|
||||
/// #[everestrs::main]
|
||||
/// async fn my_fun(module: &Module) {}
|
||||
/// ```
|
||||
#[everestrs::main]
|
||||
#[tokio::main]
|
||||
async fn main(module: &Module) {
|
||||
let config = module.get_config();
|
||||
log::info!("Received the config {config:?}");
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let one_class = Arc::new(OneClass {
|
||||
tx: Mutex::new(Some(tx)),
|
||||
});
|
||||
let publishers = module.start(one_class.clone(), one_class.clone(), one_class.clone());
|
||||
|
||||
publishers.sender.max_current(123.).unwrap();
|
||||
|
||||
// Simulate some async steps...
|
||||
let result = rx.await.unwrap();
|
||||
log::info!("Done {result}");
|
||||
|
||||
loop {
|
||||
sleep(std::time::Duration::from_secs(1)).await;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
load("@rules_rust//cargo:defs.bzl", "cargo_build_script")
|
||||
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test")
|
||||
load("@rules_shell//shell:sh_test.bzl", "sh_test")
|
||||
load("//lib/everest/framework/bazel:everest_env.bzl", "everest_env", "everest_test")
|
||||
load("//lib/everest/framework/bazel:modules_def.bzl", "rs_everest_module")
|
||||
|
||||
cargo_build_script(
|
||||
name = "build_script",
|
||||
srcs = ["build.rs"],
|
||||
build_script_env = {
|
||||
"EVEREST_CORE_ROOT": "../..",
|
||||
},
|
||||
data = [
|
||||
"manifest.yaml",
|
||||
"//lib/everest/framework/everestrs/tests/errors",
|
||||
"//lib/everest/framework/everestrs/tests/interfaces",
|
||||
"//lib/everest/framework/everestrs/tests/types",
|
||||
],
|
||||
edition = "2021",
|
||||
deps = [
|
||||
"//lib/everest/framework/everestrs/everestrs-build",
|
||||
],
|
||||
)
|
||||
|
||||
rust_binary(
|
||||
name = "RsCmdErrorsBinary",
|
||||
srcs = glob(["src/**/*.rs"]),
|
||||
edition = "2021",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":build_script",
|
||||
"//lib/everest/framework/everestrs/everestrs",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_bridge",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_sys",
|
||||
"@everest_framework_crate_index//:log",
|
||||
],
|
||||
)
|
||||
|
||||
rust_test(
|
||||
name = "RsCmdErrorsTest",
|
||||
srcs = [],
|
||||
crate = ":RsCmdErrorsBinary",
|
||||
crate_features = [
|
||||
"mockall",
|
||||
"mockall_double",
|
||||
],
|
||||
edition = "2021",
|
||||
proc_macro_deps = ["@everest_framework_crate_index//:mockall_double"],
|
||||
deps = ["@everest_framework_crate_index//:mockall"],
|
||||
)
|
||||
|
||||
rs_everest_module(
|
||||
name = "RsCmdErrors",
|
||||
binary = ":RsCmdErrorsBinary",
|
||||
manifest = "manifest.yaml",
|
||||
)
|
||||
|
||||
everest_env(
|
||||
name = "integration_env",
|
||||
config_file = "config.yaml",
|
||||
modules = [
|
||||
":RsCmdErrors",
|
||||
],
|
||||
)
|
||||
|
||||
sh_test(
|
||||
name = "integration_test",
|
||||
srcs = ["//lib/everest/framework/everestrs/tests/modules:smoke_test.sh"],
|
||||
data = [":integration_env"],
|
||||
tags = ["exclusive"],
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
use everestrs_build::Builder;
|
||||
|
||||
pub fn main() {
|
||||
Builder::new(
|
||||
"manifest.yaml",
|
||||
vec![std::env::var("EVEREST_CORE_ROOT").unwrap_or("../../..".to_string())],
|
||||
)
|
||||
.generate()
|
||||
.unwrap();
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=manifest.yaml");
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
settings:
|
||||
# This is the setting we want to test.
|
||||
forward_exceptions: true
|
||||
active_modules:
|
||||
example_0:
|
||||
module: RsCmdErrors
|
||||
connections:
|
||||
other:
|
||||
- module_id: example_1
|
||||
implementation_id: example
|
||||
example_1:
|
||||
module: RsCmdErrors
|
||||
connections:
|
||||
other:
|
||||
- module_id: example_0
|
||||
implementation_id: example
|
||||
@@ -0,0 +1,18 @@
|
||||
description: Tests if we can raise errors from commands
|
||||
provides:
|
||||
example:
|
||||
interface: example
|
||||
description: An example interface.
|
||||
requires:
|
||||
other:
|
||||
interface: example
|
||||
# We don't care about everything which is non-cmd.
|
||||
ignore:
|
||||
vars:
|
||||
- max_current
|
||||
errors: true
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- Everest authors
|
||||
enable_external_mqtt: false
|
||||
@@ -0,0 +1,90 @@
|
||||
//! Integration test for the "cmd-errors" handling.
|
||||
//!
|
||||
//! The Rust binding can receive/return errors from command calls. The
|
||||
//! exceptions need the `forward_exceptions` setting. Then errors from the
|
||||
//! server should propagate to the client.
|
||||
//!
|
||||
//! Below we test all possible errors supported by EVerest. In user code it
|
||||
//! only `HandlerException` typically makes sense.
|
||||
#![allow(non_snake_case)]
|
||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||
|
||||
use generated::{
|
||||
Context, ExampleClientSubscriber, ExampleServiceSubscriber, Module, ModulePublisher,
|
||||
OnReadySubscriber,
|
||||
};
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use std::{thread, time};
|
||||
|
||||
pub struct OneClass {
|
||||
publisher: OnceLock<ModulePublisher>,
|
||||
}
|
||||
|
||||
impl ExampleServiceSubscriber for OneClass {
|
||||
fn uses_something(&self, _context: &Context, key: String) -> ::everestrs::Result<bool> {
|
||||
match key.as_str() {
|
||||
"MessageParsingError" => Err(::everestrs::Error::MessageParsingError(
|
||||
"this message?".to_string(),
|
||||
)),
|
||||
"SchemaValidationError" => Err(::everestrs::Error::SchemaValidationError(
|
||||
"not my schema".to_string(),
|
||||
)),
|
||||
"HandlerException" => Err(::everestrs::Error::HandlerException(
|
||||
"my handler".to_string(),
|
||||
)),
|
||||
"CmdTimeout" => Err(::everestrs::Error::CmdTimeout("no time".to_string())),
|
||||
"Shutdown" => Err(::everestrs::Error::Shutdown("dead".to_string())),
|
||||
"NotReady" => Err(::everestrs::Error::NotReady("too soon".to_string())),
|
||||
_ => Ok(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The compilation test is that we don't generate the method interfaces for
|
||||
// the ignored methods.
|
||||
impl ExampleClientSubscriber for OneClass {}
|
||||
|
||||
impl OnReadySubscriber for OneClass {
|
||||
fn on_ready(&self, publishers: &ModulePublisher) {
|
||||
let _ = self.publisher.set(publishers.clone());
|
||||
}
|
||||
}
|
||||
|
||||
#[everestrs::main]
|
||||
fn main(module: &Module) {
|
||||
let one_class = Arc::new(OneClass {
|
||||
publisher: OnceLock::new(),
|
||||
});
|
||||
let _publishers = module.start(one_class.clone(), one_class.clone(), one_class.clone());
|
||||
log::info!("Module initialized");
|
||||
|
||||
let publisher = one_class.publisher.wait();
|
||||
for (key, _expected) in [
|
||||
(
|
||||
"HandlerException",
|
||||
Err(::everestrs::Error::HandlerException(String::new())),
|
||||
),
|
||||
("foo", Ok(true)),
|
||||
(
|
||||
"SchemaValidationError",
|
||||
Err(::everestrs::Error::SchemaValidationError(String::new())),
|
||||
),
|
||||
(
|
||||
"MessageParsingError",
|
||||
Err(::everestrs::Error::MessageParsingError(String::new())),
|
||||
),
|
||||
(
|
||||
"CmdTimeout",
|
||||
Err(::everestrs::Error::CmdTimeout(String::new())),
|
||||
),
|
||||
("Shutdown", Err(::everestrs::Error::Shutdown(String::new()))),
|
||||
("NotReady", Err(::everestrs::Error::NotReady(String::new()))),
|
||||
] {
|
||||
let res = publisher.other.uses_something(key.to_string());
|
||||
assert!(matches!(res, _expected));
|
||||
}
|
||||
loop {
|
||||
let dt = time::Duration::from_millis(250);
|
||||
thread::sleep(dt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
load("@rules_rust//cargo:defs.bzl", "cargo_build_script")
|
||||
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test")
|
||||
load("@rules_shell//shell:sh_test.bzl", "sh_test")
|
||||
load("//lib/everest/framework/bazel:everest_env.bzl", "everest_env")
|
||||
load("//lib/everest/framework/bazel:modules_def.bzl", "rs_everest_module")
|
||||
|
||||
cargo_build_script(
|
||||
name = "build_script",
|
||||
srcs = ["build.rs"],
|
||||
build_script_env = {
|
||||
"EVEREST_CORE_ROOT": "../..",
|
||||
},
|
||||
data = [
|
||||
"manifest.yaml",
|
||||
"//lib/everest/framework/everestrs/tests/errors",
|
||||
"//lib/everest/framework/everestrs/tests/interfaces",
|
||||
"//lib/everest/framework/everestrs/tests/types",
|
||||
],
|
||||
edition = "2021",
|
||||
deps = [
|
||||
"//lib/everest/framework/everestrs/everestrs-build",
|
||||
],
|
||||
)
|
||||
|
||||
rust_binary(
|
||||
name = "RsErrors",
|
||||
srcs = glob(["src/**/*.rs"]),
|
||||
edition = "2021",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":build_script",
|
||||
"//lib/everest/framework/everestrs/everestrs",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_bridge",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_sys",
|
||||
"@everest_framework_crate_index//:log",
|
||||
],
|
||||
)
|
||||
|
||||
rust_test(
|
||||
name = "RsErrorsTest",
|
||||
srcs = [],
|
||||
crate = ":RsErrors",
|
||||
edition = "2021",
|
||||
deps = ["@everest_framework_crate_index//:serde_yaml"],
|
||||
)
|
||||
|
||||
rs_everest_module(
|
||||
name = "RsErrorsModule",
|
||||
binary = ":RsErrors",
|
||||
manifest = "manifest.yaml",
|
||||
)
|
||||
|
||||
everest_env(
|
||||
name = "integration_env",
|
||||
config_file = "config.yaml",
|
||||
modules = [
|
||||
":RsErrorsModule",
|
||||
],
|
||||
)
|
||||
|
||||
sh_test(
|
||||
name = "integration_test",
|
||||
srcs = ["//lib/everest/framework/everestrs/tests/modules:smoke_test.sh"],
|
||||
data = [":integration_env"],
|
||||
tags = ["exclusive"],
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
use everestrs_build::Builder;
|
||||
|
||||
pub fn main() {
|
||||
Builder::new(
|
||||
"manifest.yaml",
|
||||
vec![std::env::var("EVEREST_CORE_ROOT").unwrap_or("../../..".to_string())],
|
||||
)
|
||||
.generate()
|
||||
.unwrap();
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=manifest.yaml");
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
# Test config for regression tests for the framework.
|
||||
active_modules:
|
||||
errors_0:
|
||||
module: RsErrorsModule
|
||||
connections:
|
||||
errors_multiple:
|
||||
- module_id: errors_1
|
||||
implementation_id: multiple
|
||||
mapping:
|
||||
module:
|
||||
evse: 1
|
||||
errors_1:
|
||||
module: RsErrorsModule
|
||||
connections:
|
||||
errors_multiple:
|
||||
- module_id: errors_0
|
||||
implementation_id: multiple
|
||||
mapping:
|
||||
module:
|
||||
evse: 2
|
||||
@@ -0,0 +1,13 @@
|
||||
description: Tests for the Errors
|
||||
requires:
|
||||
errors_multiple:
|
||||
interface: errors_multiple
|
||||
provides:
|
||||
multiple:
|
||||
interface: errors_multiple
|
||||
description: Multiple errors
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- Everest authors
|
||||
enable_external_mqtt: false
|
||||
@@ -0,0 +1,119 @@
|
||||
#![allow(non_snake_case)]
|
||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||
|
||||
use crate::generated::Context;
|
||||
use crate::generated::ErrorsMultipleClientSubscriber;
|
||||
use crate::generated::Module;
|
||||
use crate::generated::{ModulePublisher, OnReadySubscriber};
|
||||
use everestrs::{ErrorSeverity, ErrorType};
|
||||
use generated::errors::errors_multiple::{Error as ExampleError, ExampleErrorsError};
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
const MESSAGE: &str = "a message";
|
||||
const DESCRIPTION: &str = "a description";
|
||||
const SEVERITY: ErrorSeverity = ErrorSeverity::Low;
|
||||
|
||||
struct ErrorCommunacator {
|
||||
errors_raised: Mutex<HashSet<ExampleErrorsError>>,
|
||||
errors_cleared: Mutex<HashSet<ExampleErrorsError>>,
|
||||
errors_cleared_cv: Condvar,
|
||||
}
|
||||
|
||||
impl Eq for ExampleErrorsError {}
|
||||
|
||||
impl std::hash::Hash for ExampleErrorsError {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
std::mem::discriminant(self).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl OnReadySubscriber for ErrorCommunacator {
|
||||
fn on_ready(&self, publishers: &ModulePublisher) {
|
||||
let error_a = ExampleError::ExampleErrors(ExampleErrorsError::ExampleErrorA);
|
||||
let error_b = ExampleError::ExampleErrors(ExampleErrorsError::ExampleErrorB);
|
||||
let error_c = ExampleError::ExampleErrors(ExampleErrorsError::ExampleErrorC);
|
||||
publishers.multiple.raise_error(error_a.clone().into());
|
||||
publishers.multiple.raise_error(error_b.into());
|
||||
// Raise an error also with description and severity.
|
||||
let error_c = ErrorType {
|
||||
error_type: error_c,
|
||||
description: DESCRIPTION.to_owned(),
|
||||
message: MESSAGE.to_owned(),
|
||||
severity: SEVERITY,
|
||||
};
|
||||
publishers.multiple.raise_error(error_c);
|
||||
|
||||
publishers.multiple.clear_error(error_a);
|
||||
publishers.multiple.clear_all_errors();
|
||||
}
|
||||
}
|
||||
|
||||
impl ErrorsMultipleClientSubscriber for ErrorCommunacator {
|
||||
fn on_error_raised(&self, _context: &Context, error: ErrorType<ExampleError>) {
|
||||
let mut raised_set = self.errors_raised.lock().unwrap();
|
||||
log::info!("Error raised {:?}", error.error_type);
|
||||
if let ExampleError::ExampleErrors(inner) = &error.error_type {
|
||||
raised_set.insert(inner.clone());
|
||||
}
|
||||
|
||||
// Check the handling for custom message, description and severity.
|
||||
if let ExampleError::ExampleErrors(ExampleErrorsError::ExampleErrorC) = error.error_type {
|
||||
assert_eq!(&error.description, DESCRIPTION);
|
||||
assert_eq!(&error.message, MESSAGE);
|
||||
assert_eq!(error.severity, SEVERITY);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_error_cleared(&self, _context: &Context, error: ErrorType<ExampleError>) {
|
||||
let mut cleared_set = self.errors_cleared.lock().unwrap();
|
||||
log::info!("Error cleared {:?}", error.error_type);
|
||||
if let ExampleError::ExampleErrors(inner) = error.error_type {
|
||||
cleared_set.insert(inner.clone());
|
||||
}
|
||||
|
||||
// The integration test links this module to another version of itself
|
||||
// so the magic 3 here must match the number of calls to raise_error
|
||||
// (and thus also clear_error through the clear_all_errors call)
|
||||
// in on_ready
|
||||
if cleared_set.len() == 3 {
|
||||
self.errors_cleared_cv.notify_one();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::generated::ErrorsMultipleServiceSubscriber for ErrorCommunacator {}
|
||||
|
||||
#[everestrs::main]
|
||||
fn main(module: &Module) {
|
||||
let one_class = Arc::new(ErrorCommunacator {
|
||||
errors_raised: Mutex::new(HashSet::new()),
|
||||
errors_cleared: Mutex::new(HashSet::new()),
|
||||
errors_cleared_cv: Condvar::new(),
|
||||
});
|
||||
let _publishers = module.start(one_class.clone(), one_class.clone(), one_class.clone());
|
||||
|
||||
let mut tests_passed = false;
|
||||
// This mutex goes into the condvar, but it is dummy data as we know that the
|
||||
// notify_once will fire after this
|
||||
let mutex = Mutex::new(true);
|
||||
loop {
|
||||
let mutex_inner = mutex.lock().unwrap();
|
||||
let res = one_class
|
||||
.errors_cleared_cv
|
||||
.wait_timeout(mutex_inner, std::time::Duration::from_secs(2))
|
||||
.unwrap();
|
||||
|
||||
if res.1.timed_out() & !tests_passed {
|
||||
panic!("Timeout hit");
|
||||
} else {
|
||||
let raised_set = one_class.errors_raised.lock().unwrap();
|
||||
let cleared_set = one_class.errors_cleared.lock().unwrap();
|
||||
log::info!("Raised Errors: {:?}", raised_set);
|
||||
log::info!("Cleared Errors: {:?}", cleared_set);
|
||||
assert_eq!(*raised_set, *cleared_set);
|
||||
tests_passed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
load("@rules_rust//cargo:defs.bzl", "cargo_build_script")
|
||||
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test")
|
||||
|
||||
cargo_build_script(
|
||||
name = "build_script",
|
||||
srcs = ["build.rs"],
|
||||
build_script_env = {
|
||||
"EVEREST_CORE_ROOT": "../..",
|
||||
},
|
||||
data = [
|
||||
"manifest.yaml",
|
||||
"//lib/everest/framework/everestrs/tests/errors",
|
||||
"//lib/everest/framework/everestrs/tests/interfaces",
|
||||
"//lib/everest/framework/everestrs/tests/types",
|
||||
],
|
||||
edition = "2021",
|
||||
deps = [
|
||||
"//lib/everest/framework/everestrs/everestrs-build",
|
||||
],
|
||||
)
|
||||
|
||||
rust_binary(
|
||||
name = "RsErrorsCompilation",
|
||||
srcs = glob(["src/**/*.rs"]),
|
||||
edition = "2021",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":build_script",
|
||||
"//lib/everest/framework/everestrs/everestrs",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_bridge",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_sys",
|
||||
"@everest_framework_crate_index//:log",
|
||||
],
|
||||
)
|
||||
|
||||
rust_test(
|
||||
name = "RsErrorsCompilationTest",
|
||||
srcs = [],
|
||||
crate = ":RsErrorsCompilation",
|
||||
edition = "2021",
|
||||
deps = ["@everest_framework_crate_index//:serde_yaml"],
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
use everestrs_build::Builder;
|
||||
|
||||
pub fn main() {
|
||||
Builder::new(
|
||||
"manifest.yaml",
|
||||
vec![std::env::var("EVEREST_CORE_ROOT").unwrap_or("../../..".to_string())],
|
||||
)
|
||||
.generate()
|
||||
.unwrap();
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=manifest.yaml");
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
description: Tests for the Errors
|
||||
requires:
|
||||
errors_multiple:
|
||||
interface: errors_multiple
|
||||
provides:
|
||||
duplicate:
|
||||
interface: errors_duplicate
|
||||
description: Duplicated errors
|
||||
multiple:
|
||||
interface: errors_multiple
|
||||
description: Multiple errors
|
||||
selected:
|
||||
interface: errors_selected
|
||||
description: Selected errors
|
||||
none:
|
||||
interface: errors_none
|
||||
description: No errors defined
|
||||
empty:
|
||||
interface: empty
|
||||
description: An empty interface
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- Everest authors
|
||||
enable_external_mqtt: false
|
||||
@@ -0,0 +1,86 @@
|
||||
#![allow(non_snake_case)]
|
||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||
|
||||
fn main() {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
/// In this test we check that we can deserialize all four errors.
|
||||
#[test]
|
||||
fn test_duplicate() {
|
||||
use crate::generated::errors::errors_duplicate::Error;
|
||||
use crate::generated::errors::errors_duplicate::ExampleErrorsError;
|
||||
for (error_str, error_enum) in [
|
||||
(
|
||||
"example_errors/ExampleErrorA",
|
||||
ExampleErrorsError::ExampleErrorA,
|
||||
),
|
||||
(
|
||||
"example_errors/ExampleErrorB",
|
||||
ExampleErrorsError::ExampleErrorB,
|
||||
),
|
||||
(
|
||||
"example_errors/ExampleErrorC",
|
||||
ExampleErrorsError::ExampleErrorC,
|
||||
),
|
||||
(
|
||||
"example_errors/ExampleErrorD",
|
||||
ExampleErrorsError::ExampleErrorD,
|
||||
),
|
||||
] {
|
||||
assert_eq!(
|
||||
serde_yaml::from_str::<Error>(error_str).unwrap(),
|
||||
Error::ExampleErrors(error_enum)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// In this test we check that we can only deserialize the selected two
|
||||
/// errors.
|
||||
#[test]
|
||||
fn test_selected() {
|
||||
use crate::generated::errors::errors_selected::Error;
|
||||
use crate::generated::errors::errors_selected::ExampleErrorsError;
|
||||
for (error_str, error_enum) in [
|
||||
(
|
||||
"example_errors/ExampleErrorA",
|
||||
ExampleErrorsError::ExampleErrorA,
|
||||
),
|
||||
(
|
||||
"example_errors/ExampleErrorB",
|
||||
ExampleErrorsError::ExampleErrorB,
|
||||
),
|
||||
] {
|
||||
assert_eq!(
|
||||
serde_yaml::from_str::<Error>(error_str).unwrap(),
|
||||
Error::ExampleErrors(error_enum)
|
||||
);
|
||||
}
|
||||
|
||||
for error_str in [
|
||||
"example_errors/ExampleErrorC",
|
||||
"example_errors/ExampleErrorD",
|
||||
] {
|
||||
assert!(serde_yaml::from_str::<Error>(error_str).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
/// This test should just compile. The deserialization is tested above.
|
||||
#[test]
|
||||
fn test_multiple() {
|
||||
{
|
||||
use crate::generated::errors::errors_multiple::ExampleErrorsError;
|
||||
let _ = ExampleErrorsError::ExampleErrorA;
|
||||
let _ = ExampleErrorsError::ExampleErrorB;
|
||||
let _ = ExampleErrorsError::ExampleErrorC;
|
||||
let _ = ExampleErrorsError::ExampleErrorD;
|
||||
}
|
||||
|
||||
{
|
||||
use crate::generated::errors::errors_multiple::MoreErrorsError;
|
||||
let _ = MoreErrorsError::ExampleErrorA;
|
||||
let _ = MoreErrorsError::MoreError;
|
||||
let _ = MoreErrorsError::SnakeCaseError;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
load("@rules_python//python:defs.bzl", "py_test")
|
||||
load("@rules_rust//cargo:defs.bzl", "cargo_build_script")
|
||||
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test")
|
||||
load("@rules_shell//shell:sh_test.bzl", "sh_test")
|
||||
load("//applications/utils:requirements.bzl", "requirement")
|
||||
load("//lib/everest/framework/bazel:everest_env.bzl", "everest_env", "everest_test")
|
||||
load("//lib/everest/framework/bazel:modules_def.bzl", "rs_everest_module")
|
||||
load("//third-party/bazel/toolchains:defs.bzl", "CROSS_PYTHON_INCOMPATIBLE")
|
||||
|
||||
cargo_build_script(
|
||||
name = "build_script",
|
||||
srcs = ["build.rs"],
|
||||
build_script_env = {
|
||||
"EVEREST_CORE_ROOT": "../..",
|
||||
},
|
||||
data = [
|
||||
"manifest.yaml",
|
||||
"//lib/everest/framework/everestrs/tests/errors",
|
||||
"//lib/everest/framework/everestrs/tests/interfaces",
|
||||
"//lib/everest/framework/everestrs/tests/types",
|
||||
],
|
||||
edition = "2021",
|
||||
deps = [
|
||||
"//lib/everest/framework/everestrs/everestrs-build",
|
||||
],
|
||||
)
|
||||
|
||||
rust_binary(
|
||||
name = "RsExampleBinary",
|
||||
srcs = glob(["src/**/*.rs"]),
|
||||
edition = "2021",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":build_script",
|
||||
"//lib/everest/framework/everestrs/everestrs",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_bridge",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_sys",
|
||||
"@everest_framework_crate_index//:log",
|
||||
],
|
||||
)
|
||||
|
||||
rust_test(
|
||||
name = "RsExampleTest",
|
||||
srcs = [],
|
||||
crate = ":RsExampleBinary",
|
||||
crate_features = [
|
||||
"mockall",
|
||||
"mockall_double",
|
||||
],
|
||||
edition = "2021",
|
||||
proc_macro_deps = ["@everest_framework_crate_index//:mockall_double"],
|
||||
deps = ["@everest_framework_crate_index//:mockall"],
|
||||
)
|
||||
|
||||
rs_everest_module(
|
||||
name = "RsExample",
|
||||
binary = ":RsExampleBinary",
|
||||
manifest = "manifest.yaml",
|
||||
)
|
||||
|
||||
everest_env(
|
||||
name = "config_env",
|
||||
config_file = "config.yaml",
|
||||
modules = [
|
||||
":RsExample",
|
||||
],
|
||||
)
|
||||
|
||||
sh_test(
|
||||
name = "integration_test",
|
||||
srcs = ["//lib/everest/framework/everestrs/tests/modules:smoke_test.sh"],
|
||||
data = [":config_env"],
|
||||
tags = ["exclusive"],
|
||||
)
|
||||
|
||||
everest_env(
|
||||
name = "config_probe_env",
|
||||
config_file = "config_probe.yaml",
|
||||
modules = [
|
||||
":RsExample",
|
||||
],
|
||||
)
|
||||
|
||||
# Integration tests for RsExample using Python's everest.testing framework.
|
||||
# Uses ProbeModule to verify RsExample publishes variables and handles commands.
|
||||
py_test(
|
||||
name = "py_mocked_test",
|
||||
srcs = glob(["py_tests/**/*.py"]),
|
||||
data = ["config_probe_env"],
|
||||
legacy_create_init = False,
|
||||
main = "py_tests/mocked_test.py",
|
||||
tags = ["exclusive"],
|
||||
target_compatible_with = CROSS_PYTHON_INCOMPATIBLE,
|
||||
deps = [
|
||||
"//applications/utils/everest-testing",
|
||||
"//lib/everest/framework/everestpy/src:framework",
|
||||
requirement("pytest"),
|
||||
requirement("pytest-asyncio"),
|
||||
],
|
||||
)
|
||||
|
||||
# Integration tests for RsExample. Will launch EVerest with a ProbeModule and
|
||||
# verify that we can launch our module together with the ProbeModule.
|
||||
rust_test(
|
||||
name = "RsExampleHarnessTestBinary",
|
||||
srcs = ["rs_tests/harness_test.rs"],
|
||||
compile_data = [
|
||||
"config_probe.yaml",
|
||||
"config_multiple_connections.yaml",
|
||||
"manifest.yaml",
|
||||
"//lib/everest/framework/everestrs/tests/errors",
|
||||
"//lib/everest/framework/everestrs/tests/interfaces",
|
||||
"//lib/everest/framework/everestrs/tests/types",
|
||||
],
|
||||
data = ["config_probe_env"],
|
||||
edition = "2021",
|
||||
proc_macro_deps = [
|
||||
"//lib/everest/framework/everestrs/everestrs-derive",
|
||||
],
|
||||
rustc_env = {
|
||||
"CARGO_MANIFEST_DIR": "lib/everest/framework/everestrs/tests/modules/RsExample",
|
||||
"EVEREST_CORE_ROOT": "lib/everest/framework/everestrs/tests",
|
||||
},
|
||||
tags = ["exclusive"],
|
||||
deps = [
|
||||
"//lib/everest/framework/everestrs/everestrs",
|
||||
],
|
||||
)
|
||||
|
||||
# Integration tests for RsExample. Will launch EVerest with a ProbeModule and
|
||||
# validate calls to our trait mocks.
|
||||
rust_test(
|
||||
name = "RsExampleMockedTestBinary",
|
||||
srcs = ["rs_tests/mocked_test.rs"],
|
||||
compile_data = [
|
||||
"config_probe.yaml",
|
||||
"manifest.yaml",
|
||||
"//lib/everest/framework/everestrs/tests/errors",
|
||||
"//lib/everest/framework/everestrs/tests/interfaces",
|
||||
"//lib/everest/framework/everestrs/tests/types",
|
||||
],
|
||||
crate_features = [
|
||||
"mockall",
|
||||
"trait",
|
||||
],
|
||||
data = ["config_probe_env"],
|
||||
edition = "2021",
|
||||
proc_macro_deps = [
|
||||
"//lib/everest/framework/everestrs/everestrs-derive",
|
||||
"@everest_framework_crate_index//:mockall_double",
|
||||
],
|
||||
rustc_env = {
|
||||
"CARGO_MANIFEST_DIR": "lib/everest/framework/everestrs/tests/modules/RsExample",
|
||||
"EVEREST_CORE_ROOT": "lib/everest/framework/everestrs/tests",
|
||||
},
|
||||
tags = ["exclusive"],
|
||||
deps = [
|
||||
"//lib/everest/framework/everestrs/everestrs",
|
||||
"@everest_framework_crate_index//:mockall",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
use everestrs_build::Builder;
|
||||
|
||||
pub fn main() {
|
||||
Builder::new(
|
||||
"manifest.yaml",
|
||||
vec![std::env::var("EVEREST_CORE_ROOT").unwrap_or("../../..".to_string())],
|
||||
)
|
||||
.generate()
|
||||
.unwrap();
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=manifest.yaml");
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Test config for regression tests for the framework.
|
||||
active_modules:
|
||||
example_0:
|
||||
module: RsExample
|
||||
connections:
|
||||
a_friend:
|
||||
- module_id: example_1
|
||||
implementation_id: foobar
|
||||
example_1:
|
||||
module: RsExample
|
||||
connections:
|
||||
a_friend:
|
||||
- module_id: example_0
|
||||
implementation_id: foobar
|
||||
@@ -0,0 +1,21 @@
|
||||
active_modules:
|
||||
example_0:
|
||||
module: RsExample
|
||||
connections:
|
||||
a_friend:
|
||||
- module_id: probe
|
||||
implementation_id: foobar
|
||||
example_1:
|
||||
module: RsExample
|
||||
connections:
|
||||
a_friend:
|
||||
- module_id: probe
|
||||
implementation_id: foobar
|
||||
probe:
|
||||
module: ProbeModule
|
||||
connections:
|
||||
a_friend:
|
||||
- module_id: example_0
|
||||
implementation_id: foobar
|
||||
- module_id: example_1
|
||||
implementation_id: foobar
|
||||
@@ -0,0 +1,14 @@
|
||||
# Test config for regression tests for the framework.
|
||||
active_modules:
|
||||
example_0:
|
||||
module: RsExample
|
||||
connections:
|
||||
a_friend:
|
||||
- module_id: example_1
|
||||
implementation_id: foobar
|
||||
example_1:
|
||||
module: ProbeModule
|
||||
connections:
|
||||
a_friend:
|
||||
- module_id: example_0
|
||||
implementation_id: foobar
|
||||
@@ -0,0 +1,30 @@
|
||||
description: Simple Example
|
||||
config:
|
||||
some_string_config:
|
||||
description: A module level string config.
|
||||
type: string
|
||||
default: Hello world
|
||||
some_number_config:
|
||||
description: A module level number config.
|
||||
type: number
|
||||
default: 42
|
||||
provides:
|
||||
foobar:
|
||||
interface: example
|
||||
description: An example interface.
|
||||
config:
|
||||
some_bool_config:
|
||||
description: An interface level bool config
|
||||
type: boolean
|
||||
default: true
|
||||
some_integer_config:
|
||||
description: An interface level integer config.
|
||||
type: integer
|
||||
default: 1234
|
||||
requires:
|
||||
a_friend:
|
||||
interface: example
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- Everest authors
|
||||
@@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption(
|
||||
"--everest-prefix",
|
||||
action="store",
|
||||
default=".",
|
||||
help="everest prefix path; default = '.' (for bazel runfiles)",
|
||||
)
|
||||
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright Pionix GmbH and Contributors to EVerest
|
||||
"""
|
||||
Smoke test for RsExample using the Python testing framework.
|
||||
|
||||
Launches EVerest with RsExample (example_0) and a ProbeModule (example_1),
|
||||
then verifies that RsExample publishes max_current(123.0) on ready and
|
||||
responds to the uses_something command.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
from everest.testing.core_utils.common import Requirement
|
||||
from everest.testing.core_utils.fixtures import *
|
||||
from everest.testing.core_utils.everest_core import EverestCore
|
||||
from everest.testing.core_utils.probe_module import ProbeModule
|
||||
|
||||
|
||||
async def wait_for_mock(mock, timeout=10):
|
||||
"""Poll until mock has been called or timeout."""
|
||||
for _ in range(int(timeout * 10)):
|
||||
if mock.called:
|
||||
return
|
||||
await asyncio.sleep(0.1)
|
||||
raise TimeoutError(f"Mock not called within {timeout}s")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.probe_module(
|
||||
connections={"a_friend": [Requirement("example_0", "foobar")]},
|
||||
module_id="example_1",
|
||||
)
|
||||
@pytest.mark.everest_core_config("config_probe.yaml")
|
||||
async def test_rs_example_publishes_max_current(everest_core: EverestCore):
|
||||
"""Verify that RsExample publishes max_current(123.0) on ready."""
|
||||
everest_core.start()
|
||||
|
||||
probe = ProbeModule(everest_core.get_runtime_session(), module_id="example_1")
|
||||
|
||||
max_current_mock = Mock()
|
||||
probe.subscribe_variable("a_friend", "max_current", max_current_mock)
|
||||
|
||||
probe.implement_command("foobar", "uses_something", lambda args: True)
|
||||
|
||||
probe.start()
|
||||
await probe.wait_to_be_ready(timeout=10)
|
||||
|
||||
await wait_for_mock(max_current_mock, timeout=10)
|
||||
value = max_current_mock.call_args[0][0]
|
||||
assert value == 123.0, f"Expected max_current=123.0, got {value}"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.probe_module(
|
||||
connections={"a_friend": [Requirement("example_0", "foobar")]},
|
||||
module_id="example_1",
|
||||
)
|
||||
@pytest.mark.everest_core_config("config_probe.yaml")
|
||||
async def test_rs_example_uses_something(everest_core: EverestCore):
|
||||
"""Verify that we can call uses_something on RsExample."""
|
||||
everest_core.start()
|
||||
|
||||
probe = ProbeModule(everest_core.get_runtime_session(), module_id="example_1")
|
||||
|
||||
probe.implement_command("foobar", "uses_something", lambda args: True)
|
||||
|
||||
probe.start()
|
||||
await probe.wait_to_be_ready(timeout=10)
|
||||
|
||||
result = await probe.call_command("a_friend", "uses_something", {"key": "hello"})
|
||||
# The C++ binding may return None for boolean results; verify at least no exception.
|
||||
assert result is None or result == True, f"Unexpected result: {result!r}"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(pytest.main([__file__, "-v"] + sys.argv[1:]))
|
||||
@@ -0,0 +1,148 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
#[everestrs::test(config = "config_probe.yaml", module = "example_1", harness = true)]
|
||||
fn test_harness_generates_counterpart(module: &Module) {
|
||||
use generated::*;
|
||||
use std::sync::mpsc::{channel, Sender};
|
||||
use std::sync::Arc;
|
||||
let (tx, rx) = channel();
|
||||
|
||||
struct Dummy(Sender<()>);
|
||||
|
||||
impl OnReadySubscriber for Dummy {
|
||||
fn on_ready(&self, _pub_impl: &ModulePublisher) {}
|
||||
}
|
||||
|
||||
impl ExampleServiceSubscriber for Dummy {
|
||||
fn uses_something(&self, _context: &Context, _key: String) -> everestrs::Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExampleClientSubscriber for Dummy {
|
||||
fn on_max_current(&self, _context: &Context, value: f64) {
|
||||
assert_eq!(value, 123.);
|
||||
self.0.send(()).unwrap();
|
||||
}
|
||||
fn on_error_raised(
|
||||
&self,
|
||||
_context: &Context,
|
||||
_error: everestrs::ErrorType<errors::example::Error>,
|
||||
) {
|
||||
}
|
||||
fn on_error_cleared(
|
||||
&self,
|
||||
_context: &Context,
|
||||
_error: everestrs::ErrorType<errors::example::Error>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
let dummy = Arc::new(Dummy(tx));
|
||||
let _pub = module.start(dummy.clone(), dummy.clone(), dummy.clone());
|
||||
|
||||
// Wait for RsExample's on_ready to publish max_current(123.0).
|
||||
rx.recv_timeout(std::time::Duration::from_secs(5))
|
||||
.expect("Timed out waiting for on_max_current");
|
||||
}
|
||||
|
||||
#[everestrs::harness(config = "config_probe.yaml", module = "example_1")]
|
||||
mod some_module {
|
||||
use generated::*;
|
||||
use std::sync::mpsc::{channel, Sender};
|
||||
use std::sync::Arc;
|
||||
|
||||
struct Dummy(Sender<()>, f64);
|
||||
|
||||
impl OnReadySubscriber for Dummy {
|
||||
fn on_ready(&self, _pub_impl: &ModulePublisher) {}
|
||||
}
|
||||
|
||||
impl ExampleServiceSubscriber for Dummy {
|
||||
fn uses_something(&self, _context: &Context, _key: String) -> everestrs::Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExampleClientSubscriber for Dummy {
|
||||
fn on_max_current(&self, _context: &Context, value: f64) {
|
||||
assert_eq!(value, self.1);
|
||||
self.0.send(()).unwrap();
|
||||
}
|
||||
fn on_error_raised(
|
||||
&self,
|
||||
_context: &Context,
|
||||
_error: everestrs::ErrorType<errors::example::Error>,
|
||||
) {
|
||||
}
|
||||
fn on_error_cleared(
|
||||
&self,
|
||||
_context: &Context,
|
||||
_error: everestrs::ErrorType<errors::example::Error>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
#[everestrs::test(config = "config_probe.yaml", module = "example_1")]
|
||||
fn test_harness_in_module(module: &Module) {
|
||||
let (tx, rx) = channel();
|
||||
let dummy = Arc::new(Dummy(tx, 123.));
|
||||
let _pub = module.start(dummy.clone(), dummy.clone(), dummy.clone());
|
||||
|
||||
// Wait for RsExample's on_ready to publish max_current(123.0).
|
||||
rx.recv_timeout(std::time::Duration::from_secs(5))
|
||||
.expect("Timed out waiting for on_max_current");
|
||||
}
|
||||
|
||||
#[everestrs::test(config = "config_probe.yaml", module = "example_1")]
|
||||
#[should_panic]
|
||||
fn test_harness_with_panic(module: &Module) {
|
||||
let (tx, rx) = channel();
|
||||
let dummy = Arc::new(Dummy(tx, 124.));
|
||||
let _pub = module.start(dummy.clone(), dummy.clone(), dummy.clone());
|
||||
|
||||
// Wait for RsExample's on_ready to publish max_current(123.0).
|
||||
rx.recv_timeout(std::time::Duration::from_secs(5))
|
||||
.expect("Timed out waiting for on_max_current");
|
||||
}
|
||||
}
|
||||
|
||||
#[everestrs::harness(config = "config_multiple_connections.yaml", module = "probe")]
|
||||
mod multiple_connections_compilation {
|
||||
use generated::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
struct Dummy;
|
||||
|
||||
impl OnReadySubscriber for Dummy {
|
||||
fn on_ready(&self, _pub_impl: &ModulePublisher) {}
|
||||
}
|
||||
|
||||
impl ExampleServiceSubscriber for Dummy {
|
||||
fn uses_something(&self, _context: &Context, _key: String) -> everestrs::Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExampleClientSubscriber for Dummy {
|
||||
fn on_max_current(&self, _context: &Context, _value: f64) {}
|
||||
fn on_error_raised(
|
||||
&self,
|
||||
_context: &Context,
|
||||
_error: everestrs::ErrorType<errors::example::Error>,
|
||||
) {
|
||||
}
|
||||
fn on_error_cleared(
|
||||
&self,
|
||||
_context: &Context,
|
||||
_error: everestrs::ErrorType<errors::example::Error>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
#[everestrs::test(config = "config_probe.yaml", module = "example_1")]
|
||||
fn test_harness_in_module(module: &Module) {
|
||||
let dummy = Arc::new(Dummy);
|
||||
let _pub = module.start(dummy.clone(), dummy.clone(), |_a: usize| dummy.clone());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
#[everestrs::test(config = "config_probe.yaml", module = "example_1", harness = true)]
|
||||
fn test_mocked_generates_counterpart(module: &Module) {
|
||||
use generated::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
let mock_service = Arc::new(MockExampleServiceSubscriber::new());
|
||||
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
let mut mock_client = MockExampleClientSubscriber::new();
|
||||
mock_client
|
||||
.expect_on_max_current()
|
||||
.withf(|_, value| *value == 123.0)
|
||||
.times(1)
|
||||
.return_once(move |_, _| {
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
let mock_client = Arc::new(mock_client);
|
||||
|
||||
let mut mock_on_ready = MockOnReadySubscriber::new();
|
||||
mock_on_ready.expect_on_ready().times(1).return_once(|_| ());
|
||||
|
||||
let _pub = module.start(Arc::new(mock_on_ready), mock_service, mock_client);
|
||||
|
||||
// Wait for RsExample's on_ready to publish max_current(123.0).
|
||||
rx.recv_timeout(std::time::Duration::from_secs(5))
|
||||
.expect("Timed out waiting for on_max_current");
|
||||
}
|
||||
|
||||
#[everestrs::harness(config = "config_probe.yaml", module = "example_1")]
|
||||
mod some_module {
|
||||
use generated::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[everestrs::test(config = "config_probe.yaml", module = "example_1")]
|
||||
fn test_mocked_in_module(module: &Module) {
|
||||
let mock_service = Arc::new(MockExampleServiceSubscriber::new());
|
||||
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
let mut mock_client = MockExampleClientSubscriber::new();
|
||||
mock_client
|
||||
.expect_on_max_current()
|
||||
.withf(|_, value| *value == 123.0)
|
||||
.times(1)
|
||||
.return_once(move |_, _| {
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
let mock_client = Arc::new(mock_client);
|
||||
|
||||
let mut mock_on_ready = MockOnReadySubscriber::new();
|
||||
mock_on_ready.expect_on_ready().times(1).return_once(|_| ());
|
||||
|
||||
let _pub = module.start(Arc::new(mock_on_ready), mock_service, mock_client);
|
||||
|
||||
// Wait for RsExample's on_ready to publish max_current(123.0).
|
||||
rx.recv_timeout(std::time::Duration::from_secs(5))
|
||||
.expect("Timed out waiting for on_max_current");
|
||||
}
|
||||
|
||||
#[everestrs::test(config = "config_probe.yaml", module = "example_1")]
|
||||
#[should_panic]
|
||||
fn test_mocked_with_panic_handler(module: &Module) {
|
||||
let mock_service = Arc::new(MockExampleServiceSubscriber::new());
|
||||
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
let mut mock_client = MockExampleClientSubscriber::new();
|
||||
mock_client
|
||||
.expect_on_max_current()
|
||||
.withf(|_, value| *value != 123.0)
|
||||
.times(1)
|
||||
.return_once(move |_, _| {
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
let mock_client = Arc::new(mock_client);
|
||||
|
||||
let mut mock_on_ready = MockOnReadySubscriber::new();
|
||||
mock_on_ready.expect_on_ready().times(1).return_once(|_| ());
|
||||
|
||||
let _pub = module.start(Arc::new(mock_on_ready), mock_service, mock_client);
|
||||
|
||||
// Wait for RsExample's on_ready to publish max_current(123.0).
|
||||
rx.recv_timeout(std::time::Duration::from_secs(5))
|
||||
.expect("Timed out waiting for on_max_current");
|
||||
}
|
||||
|
||||
#[everestrs::test(config = "config_probe.yaml", module = "example_1")]
|
||||
#[should_panic]
|
||||
fn test_mocked_with_panic_mocks(module: &Module) {
|
||||
let mock_service = Arc::new(MockExampleServiceSubscriber::new());
|
||||
|
||||
let mut mock_client = MockExampleClientSubscriber::new();
|
||||
mock_client
|
||||
.expect_on_max_current()
|
||||
.times(2..) // No one will call us twice.
|
||||
.return_const(());
|
||||
let mock_client = Arc::new(mock_client);
|
||||
|
||||
let mut mock_on_ready = MockOnReadySubscriber::new();
|
||||
mock_on_ready.expect_on_ready().times(1).return_once(|_| ());
|
||||
|
||||
let _pub = module.start(Arc::new(mock_on_ready), mock_service, mock_client);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
#![allow(non_snake_case)]
|
||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||
|
||||
use everestrs::ErrorType;
|
||||
use generated::errors::example::Error as ExampleError;
|
||||
use generated::{
|
||||
Context, ExampleClientSubscriber, ExampleServiceSubscriber, Module,
|
||||
ModulePublisher, OnReadySubscriber,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use std::{thread, time};
|
||||
|
||||
pub struct OneClass {}
|
||||
|
||||
impl ExampleServiceSubscriber for OneClass {
|
||||
fn uses_something(&self, context: &Context, key: String) -> ::everestrs::Result<bool> {
|
||||
use crate::generated::errors::example::ExampleErrorsError;
|
||||
let error = ExampleError::ExampleErrors(ExampleErrorsError::ExampleErrorA);
|
||||
if key.is_empty() {
|
||||
// Explicit cast
|
||||
let error: ErrorType<_> = error.into();
|
||||
context.publisher.foobar.raise_error(error);
|
||||
} else if &key == "clear_all" {
|
||||
context.publisher.foobar.clear_all_errors();
|
||||
} else {
|
||||
context.publisher.foobar.clear_error(error);
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExampleClientSubscriber for OneClass {
|
||||
fn on_max_current(&self, _context: &Context, value: f64) {
|
||||
log::info!("Received {value}");
|
||||
}
|
||||
|
||||
fn on_error_raised(&self, _context: &Context, error: ErrorType<ExampleError>) {
|
||||
log::warn!("Recieved an error {:?}", error.error_type);
|
||||
}
|
||||
|
||||
fn on_error_cleared(&self, _context: &Context, error: ErrorType<ExampleError>) {
|
||||
log::info!("Cleared an error {:?} - what a relief", error.error_type);
|
||||
}
|
||||
}
|
||||
|
||||
impl OnReadySubscriber for OneClass {
|
||||
fn on_ready(&self, publishers: &ModulePublisher) {
|
||||
log::info!("Ready");
|
||||
match publishers.foobar.max_current(123.0) {
|
||||
Ok(_) => log::info!("Adjusted the max current"),
|
||||
Err(err) => log::error!("Failed to set the max current: {err:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[everestrs::main]
|
||||
fn main(module: &Module) {
|
||||
let config = module.get_config();
|
||||
log::info!("Received the config {config:?}");
|
||||
let one_class = Arc::new(OneClass {});
|
||||
let _publishers = module.start(one_class.clone(), one_class.clone(), one_class.clone());
|
||||
log::info!("Module initialized");
|
||||
|
||||
loop {
|
||||
let dt = time::Duration::from_millis(250);
|
||||
thread::sleep(dt);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_on_ready() {
|
||||
let mut everest_mock = ModulePublisher::default();
|
||||
everest_mock
|
||||
.foobar
|
||||
.expect_max_current()
|
||||
.times(1)
|
||||
.return_once(|_| Ok(()));
|
||||
|
||||
let module = OneClass {};
|
||||
module.on_ready(&everest_mock);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uses_something() {
|
||||
use mockall::Sequence;
|
||||
|
||||
let mut seq = Sequence::new();
|
||||
let mut everest_mock = ModulePublisher::default();
|
||||
|
||||
everest_mock
|
||||
.foobar
|
||||
.expect_raise_error()
|
||||
.times(1)
|
||||
.in_sequence(&mut seq)
|
||||
.return_once(|_| ());
|
||||
|
||||
everest_mock
|
||||
.foobar
|
||||
.expect_clear_error()
|
||||
.times(1)
|
||||
.in_sequence(&mut seq)
|
||||
.return_once(|_| ());
|
||||
|
||||
everest_mock
|
||||
.foobar
|
||||
.expect_clear_all_errors()
|
||||
.times(1)
|
||||
.in_sequence(&mut seq)
|
||||
.return_once(|| ());
|
||||
|
||||
let context = Context {
|
||||
name: "foo",
|
||||
publisher: &everest_mock,
|
||||
index: 0,
|
||||
};
|
||||
|
||||
let module = OneClass {};
|
||||
for message in [String::new(), "clear".to_owned(), "clear_all".to_owned()] {
|
||||
let _ = module.uses_something(&context, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
load("@rules_rust//cargo:defs.bzl", "cargo_build_script")
|
||||
load("@rules_rust//rust:defs.bzl", "rust_binary")
|
||||
load("@rules_shell//shell:sh_test.bzl", "sh_test")
|
||||
load("//lib/everest/framework/bazel:everest_env.bzl", "everest_env")
|
||||
load("//lib/everest/framework/bazel:modules_def.bzl", "rs_everest_module")
|
||||
|
||||
cargo_build_script(
|
||||
name = "build_script",
|
||||
srcs = ["build.rs"],
|
||||
build_script_env = {
|
||||
"EVEREST_CORE_ROOT": "../..",
|
||||
},
|
||||
data = [
|
||||
"manifest.yaml",
|
||||
"//lib/everest/framework/everestrs/tests/errors",
|
||||
"//lib/everest/framework/everestrs/tests/interfaces",
|
||||
"//lib/everest/framework/everestrs/tests/types",
|
||||
],
|
||||
edition = "2021",
|
||||
deps = [
|
||||
"//lib/everest/framework/everestrs/everestrs-build",
|
||||
],
|
||||
)
|
||||
|
||||
rust_binary(
|
||||
name = "RsIgnoreBinary",
|
||||
srcs = glob(["src/**/*.rs"]),
|
||||
edition = "2021",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":build_script",
|
||||
"//lib/everest/framework/everestrs/everestrs",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_bridge",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_sys",
|
||||
"@everest_framework_crate_index//:log",
|
||||
],
|
||||
)
|
||||
|
||||
rs_everest_module(
|
||||
name = "RsIgnore",
|
||||
binary = ":RsIgnoreBinary",
|
||||
manifest = "manifest.yaml",
|
||||
)
|
||||
|
||||
everest_env(
|
||||
name = "integration_env",
|
||||
config_file = "config.yaml",
|
||||
modules = [
|
||||
":RsIgnore",
|
||||
],
|
||||
)
|
||||
|
||||
sh_test(
|
||||
name = "integration_test",
|
||||
srcs = ["//lib/everest/framework/everestrs/tests/modules:smoke_test.sh"],
|
||||
data = [":integration_env"],
|
||||
tags = ["exclusive"],
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
use everestrs_build::Builder;
|
||||
|
||||
pub fn main() {
|
||||
Builder::new(
|
||||
"manifest.yaml",
|
||||
vec![std::env::var("EVEREST_CORE_ROOT").unwrap_or("../../..".to_string())],
|
||||
)
|
||||
.generate()
|
||||
.unwrap();
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=manifest.yaml");
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Test config for regression tests for the framework.
|
||||
active_modules:
|
||||
example_0:
|
||||
module: RsIgnore
|
||||
connections:
|
||||
other:
|
||||
- module_id: example_1
|
||||
implementation_id: example
|
||||
example_1:
|
||||
module: RsIgnore
|
||||
connections:
|
||||
other:
|
||||
- module_id: example_0
|
||||
implementation_id: example
|
||||
@@ -0,0 +1,17 @@
|
||||
description: Simple Example
|
||||
provides:
|
||||
example:
|
||||
interface: example
|
||||
description: An example interface.
|
||||
requires:
|
||||
other:
|
||||
interface: example
|
||||
ignore:
|
||||
vars:
|
||||
- max_current
|
||||
errors: true
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- Everest authors
|
||||
enable_external_mqtt: false
|
||||
@@ -0,0 +1,50 @@
|
||||
//! Integration test for the "ignore" handling.
|
||||
//!
|
||||
//! The Rust binding allow you to ignore variables and errors. The variables
|
||||
//! are ignored by adding them to the "ignore.vars" list. Errors can only be ignored
|
||||
//! at bulk, by setting "ignore.errors" to true. Ignored elements are removed
|
||||
//! from the trait and thus don't have to be implemented.
|
||||
#![allow(non_snake_case)]
|
||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||
|
||||
use generated::errors::example::{Error as ExampleError, ExampleErrorsError};
|
||||
use generated::{
|
||||
Context, ExampleClientSubscriber, ExampleServiceSubscriber, Module, ModulePublisher,
|
||||
OnReadySubscriber,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use std::{thread, time};
|
||||
|
||||
pub struct OneClass {}
|
||||
|
||||
impl ExampleServiceSubscriber for OneClass {
|
||||
fn uses_something(&self, _context: &Context, _key: String) -> ::everestrs::Result<bool> {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
// The compilation test is that we don't generate the method interfaces for
|
||||
// the ignored methods.
|
||||
impl ExampleClientSubscriber for OneClass {}
|
||||
|
||||
impl OnReadySubscriber for OneClass {
|
||||
fn on_ready(&self, publishers: &ModulePublisher) {
|
||||
// Call the other module. This calls should be ignored.
|
||||
publishers.example.max_current(12.3).unwrap();
|
||||
let error = ExampleError::ExampleErrors(ExampleErrorsError::ExampleErrorA);
|
||||
publishers.example.raise_error(error.clone().into());
|
||||
publishers.example.clear_error(error);
|
||||
}
|
||||
}
|
||||
|
||||
#[everestrs::main]
|
||||
fn main(module: &Module) {
|
||||
let one_class = Arc::new(OneClass {});
|
||||
let _publishers = module.start(one_class.clone(), one_class.clone(), one_class.clone());
|
||||
log::info!("Module initialized");
|
||||
|
||||
loop {
|
||||
let dt = time::Duration::from_millis(250);
|
||||
thread::sleep(dt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
load("@rules_rust//cargo:defs.bzl", "cargo_build_script")
|
||||
load("@rules_rust//rust:defs.bzl", "rust_binary")
|
||||
load("@rules_shell//shell:sh_test.bzl", "sh_test")
|
||||
load("//lib/everest/framework/bazel:everest_env.bzl", "everest_env")
|
||||
load("//lib/everest/framework/bazel:modules_def.bzl", "rs_everest_module")
|
||||
|
||||
cargo_build_script(
|
||||
name = "build_script",
|
||||
srcs = ["build.rs"],
|
||||
build_script_env = {
|
||||
"EVEREST_CORE_ROOT": "../..",
|
||||
},
|
||||
data = [
|
||||
"manifest.yaml",
|
||||
"//lib/everest/framework/everestrs/tests/errors",
|
||||
"//lib/everest/framework/everestrs/tests/interfaces",
|
||||
"//lib/everest/framework/everestrs/tests/types",
|
||||
],
|
||||
edition = "2021",
|
||||
deps = [
|
||||
"//lib/everest/framework/everestrs/everestrs-build",
|
||||
],
|
||||
)
|
||||
|
||||
rust_binary(
|
||||
name = "RsOnReadyRaceConditionBinary",
|
||||
srcs = glob(["src/**/*.rs"]),
|
||||
edition = "2021",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":build_script",
|
||||
"//lib/everest/framework/everestrs/everestrs",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_bridge",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_sys",
|
||||
"@everest_framework_crate_index//:log",
|
||||
],
|
||||
)
|
||||
|
||||
rs_everest_module(
|
||||
name = "RsOnReadyRaceCondition",
|
||||
binary = ":RsOnReadyRaceConditionBinary",
|
||||
manifest = "manifest.yaml",
|
||||
)
|
||||
|
||||
everest_env(
|
||||
name = "integration_env",
|
||||
config_file = "config.yaml",
|
||||
modules = [
|
||||
":RsOnReadyRaceCondition",
|
||||
],
|
||||
)
|
||||
|
||||
sh_test(
|
||||
name = "integration_test",
|
||||
srcs = ["//lib/everest/framework/everestrs/tests/modules:smoke_test.sh"],
|
||||
data = [":integration_env"],
|
||||
tags = ["exclusive"],
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
use everestrs_build::Builder;
|
||||
|
||||
pub fn main() {
|
||||
Builder::new(
|
||||
"manifest.yaml",
|
||||
vec![std::env::var("EVEREST_CORE_ROOT").unwrap_or("../../..".to_string())],
|
||||
)
|
||||
.generate()
|
||||
.unwrap();
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=manifest.yaml");
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Test config for regression tests for the framework.
|
||||
active_modules:
|
||||
example_0:
|
||||
module: RsOnReadyRaceCondition
|
||||
connections:
|
||||
other:
|
||||
- module_id: example_1
|
||||
implementation_id: example
|
||||
example_1:
|
||||
module: RsOnReadyRaceCondition
|
||||
connections:
|
||||
other:
|
||||
- module_id: example_0
|
||||
implementation_id: example
|
||||
@@ -0,0 +1,13 @@
|
||||
description: Simple Example
|
||||
provides:
|
||||
example:
|
||||
interface: example
|
||||
description: An example interface.
|
||||
requires:
|
||||
other:
|
||||
interface: example
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- Everest authors
|
||||
enable_external_mqtt: false
|
||||
@@ -0,0 +1,82 @@
|
||||
//! Integration test for the "ready_received" handling.
|
||||
//!
|
||||
//! We assume that every module shall recieve first `on_ready` before forwarding
|
||||
//! any other call to the user code. The code below recreates the race condition
|
||||
//! by making calls to the `other` module from within `on_ready` (and adding a
|
||||
//! delay in `on_ready`).
|
||||
#![allow(non_snake_case)]
|
||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||
|
||||
use everestrs::ErrorType;
|
||||
use generated::errors::example::{Error as ExampleError, ExampleErrorsError};
|
||||
use generated::{
|
||||
Context, ExampleClientSubscriber, ExampleServiceSubscriber, Module, ModulePublisher,
|
||||
OnReadySubscriber,
|
||||
};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::{thread, time};
|
||||
|
||||
pub struct OneClass {
|
||||
/// Flag that the on-ready has been called.
|
||||
on_ready_called: AtomicBool,
|
||||
}
|
||||
|
||||
impl ExampleServiceSubscriber for OneClass {
|
||||
fn uses_something(&self, _context: &Context, _key: String) -> ::everestrs::Result<bool> {
|
||||
assert!(self.on_ready_called.load(Ordering::Relaxed));
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExampleClientSubscriber for OneClass {
|
||||
fn on_max_current(&self, _context: &Context, _value: f64) {
|
||||
assert!(self.on_ready_called.load(Ordering::Relaxed));
|
||||
log::info!("max current");
|
||||
}
|
||||
|
||||
fn on_error_raised(&self, _context: &Context, _error: ErrorType<ExampleError>) {
|
||||
assert!(self.on_ready_called.load(Ordering::Relaxed));
|
||||
log::info!("Error raised");
|
||||
}
|
||||
|
||||
fn on_error_cleared(&self, _context: &Context, _error: ErrorType<ExampleError>) {
|
||||
assert!(self.on_ready_called.load(Ordering::Relaxed));
|
||||
log::info!("Error cleared");
|
||||
}
|
||||
}
|
||||
|
||||
impl OnReadySubscriber for OneClass {
|
||||
fn on_ready(&self, publishers: &ModulePublisher) {
|
||||
log::info!("Enter Ready");
|
||||
// Call the other module.
|
||||
publishers.example.max_current(12.3).unwrap();
|
||||
let error = ExampleError::ExampleErrors(ExampleErrorsError::ExampleErrorA);
|
||||
publishers.example.raise_error(error.clone().into());
|
||||
publishers.example.clear_error(error);
|
||||
|
||||
// TODO(ddo) Add here the `uses_something` call once the framework can
|
||||
// reject too early calls.
|
||||
|
||||
// Sleep here to trigger the race condition.
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
// Update the flag.
|
||||
self.on_ready_called.store(true, Ordering::Relaxed);
|
||||
|
||||
log::info!("Exit Ready!");
|
||||
}
|
||||
}
|
||||
|
||||
#[everestrs::main]
|
||||
fn main(module: &Module) {
|
||||
let one_class = Arc::new(OneClass {
|
||||
on_ready_called: AtomicBool::new(false),
|
||||
});
|
||||
let _publishers = module.start(one_class.clone(), one_class.clone(), one_class.clone());
|
||||
log::info!("Module initialized");
|
||||
|
||||
loop {
|
||||
let dt = time::Duration::from_millis(250);
|
||||
thread::sleep(dt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
load("@rules_rust//cargo:defs.bzl", "cargo_build_script")
|
||||
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test")
|
||||
|
||||
cargo_build_script(
|
||||
name = "build_script",
|
||||
srcs = ["build.rs"],
|
||||
build_script_env = {
|
||||
"EVEREST_CORE_ROOT": "../..",
|
||||
},
|
||||
data = [
|
||||
"manifest.yaml",
|
||||
"//lib/everest/framework/everestrs/tests/errors",
|
||||
"//lib/everest/framework/everestrs/tests/interfaces",
|
||||
"//lib/everest/framework/everestrs/tests/types",
|
||||
],
|
||||
edition = "2021",
|
||||
deps = [
|
||||
"//lib/everest/framework/everestrs/everestrs-build",
|
||||
],
|
||||
)
|
||||
|
||||
# For now just a compilation test
|
||||
rust_binary(
|
||||
name = "RsOptionalConnection",
|
||||
srcs = glob(["src/**/*.rs"]),
|
||||
edition = "2021",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":build_script",
|
||||
"//lib/everest/framework/everestrs/everestrs",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_bridge",
|
||||
"//lib/everest/framework/everestrs/everestrs:everestrs_sys",
|
||||
"@everest_framework_crate_index//:log",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
use everestrs_build::Builder;
|
||||
|
||||
pub fn main() {
|
||||
Builder::new(
|
||||
"manifest.yaml",
|
||||
vec![std::env::var("EVEREST_CORE_ROOT").unwrap_or("../../..".to_string())],
|
||||
)
|
||||
.generate()
|
||||
.unwrap();
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=manifest.yaml");
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
description: The tests how to use optinal connections in Rust
|
||||
provides:
|
||||
foobar:
|
||||
interface: example
|
||||
description: An example interface.
|
||||
requires:
|
||||
optional_connection:
|
||||
interface: example
|
||||
min_connections: 0
|
||||
max_connections: 1
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- Everest authors
|
||||
enable_external_mqtt: false
|
||||
@@ -0,0 +1,58 @@
|
||||
#![allow(non_snake_case)]
|
||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||
|
||||
use everestrs::ErrorType;
|
||||
use generated::errors::example::Error as ExampleError;
|
||||
use generated::{
|
||||
Context, ExampleClientSubscriber, ExampleServiceSubscriber, Module, ModulePublisher,
|
||||
OnReadySubscriber,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use std::{thread, time};
|
||||
|
||||
pub struct OptionalConnection {}
|
||||
|
||||
impl ExampleServiceSubscriber for OptionalConnection {
|
||||
fn uses_something(&self, _context: &Context, key: String) -> ::everestrs::Result<bool> {
|
||||
log::info!("Received {key}");
|
||||
Ok(&key == "hello")
|
||||
}
|
||||
}
|
||||
|
||||
impl ExampleClientSubscriber for OptionalConnection {
|
||||
fn on_max_current(&self, _context: &Context, value: f64) {
|
||||
log::info!("Received {value}");
|
||||
}
|
||||
|
||||
fn on_error_raised(&self, _context: &Context, error: ErrorType<ExampleError>) {
|
||||
log::info!("Recieved an error {:?}", error.error_type);
|
||||
}
|
||||
|
||||
fn on_error_cleared(&self, _context: &Context, error: ErrorType<ExampleError>) {
|
||||
log::info!("Cleared an error {:?} - what a relief", error.error_type);
|
||||
}
|
||||
}
|
||||
|
||||
impl OnReadySubscriber for OptionalConnection {
|
||||
fn on_ready(&self, publishers: &ModulePublisher) {
|
||||
log::info!("Ready");
|
||||
if let Some(publisher) = publishers.optional_connection_slots.get(0) {
|
||||
let res = publisher.uses_something("hello".to_string()).unwrap();
|
||||
assert!(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[everestrs::main]
|
||||
fn main(module: &Module) {
|
||||
let one_class = Arc::new(OptionalConnection {});
|
||||
let _publishers = module.start(one_class.clone(), one_class.clone(), |_index| {
|
||||
one_class.clone()
|
||||
});
|
||||
log::info!("Module initialized");
|
||||
|
||||
loop {
|
||||
let dt = time::Duration::from_millis(250);
|
||||
thread::sleep(dt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
set -ex
|
||||
|
||||
echo "Starting the manager"
|
||||
|
||||
bin/manager --prefix . --config etc/everest/config.yaml &
|
||||
PID_MANAGER=$!
|
||||
|
||||
sleep 5
|
||||
echo "Exit"
|
||||
|
||||
if ps -p $PID_MANAGER > /dev/null
|
||||
then
|
||||
kill $PID_MANAGER
|
||||
else
|
||||
echo "manager died"
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,5 @@
|
||||
filegroup(
|
||||
name = "types",
|
||||
srcs = glob(["*.yaml"]),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
description: Example type
|
||||
types:
|
||||
Something:
|
||||
description: Some simple type.
|
||||
type: string
|
||||
Reference in New Issue
Block a user