Skip to content

Commit 76d224b

Browse files
tests: zenoh: Add bidirectional integration tests
1 parent 97f68ee commit 76d224b

4 files changed

Lines changed: 214 additions & 0 deletions

File tree

tests/zenoh.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
mod common;
2+
3+
#[path = "zenoh/mod.rs"]
4+
mod zenoh;
5+
6+
#[path = "zenoh/json.rs"]
7+
mod json;
8+
9+
#[path = "zenoh/raw.rs"]
10+
mod raw;

tests/zenoh/json.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use std::{sync::Arc, time::Duration};
2+
3+
use anyhow::{Context, Result};
4+
use clap::Parser;
5+
use mavlink_server::{cli, drivers::zenoh::json::Zenoh, hub, stats};
6+
use tracing::*;
7+
8+
use crate::{
9+
common::{pick_free_port, wait_for_stats_collection},
10+
zenoh::{spawn_zenoh_router, wait_for_router, write_zenoh_client_config},
11+
};
12+
13+
#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
14+
async fn test_zenoh_bidirectional() -> Result<()> {
15+
let driver_name = "Zenoh";
16+
let port = pick_free_port();
17+
let _router = spawn_zenoh_router(port, "mavlink").await;
18+
wait_for_router().await;
19+
20+
let zenoh_config = write_zenoh_client_config(port)?;
21+
let zenoh_config = zenoh_config
22+
.to_str()
23+
.context("Zenoh config path is not valid UTF-8")?;
24+
25+
cli::init_with(cli::Args::parse_from(vec![
26+
&std::env::args().next().unwrap_or_default(),
27+
"--allow-no-endpoints",
28+
"--zenoh-config-file",
29+
zenoh_config,
30+
"--mavlink-heartbeat-frequency",
31+
"10",
32+
]));
33+
34+
hub::add_driver(Arc::new(Zenoh::builder(driver_name).build())).await?;
35+
36+
stats::set_period(Duration::from_millis(100)).await?;
37+
wait_for_stats_collection().await;
38+
39+
let mut matching_drivers = 0;
40+
for (_uuid, driver) in stats::drivers_stats().await? {
41+
if *driver.name != driver_name {
42+
continue;
43+
}
44+
45+
matching_drivers += 1;
46+
assert!(
47+
driver.stats.input.is_some(),
48+
"{driver_name} should receive mavlink over zenoh"
49+
);
50+
assert!(
51+
driver.stats.output.is_some(),
52+
"{driver_name} should publish mavlink over zenoh"
53+
);
54+
}
55+
56+
assert_eq!(
57+
matching_drivers, 1,
58+
"expected one {driver_name} driver in stats"
59+
);
60+
61+
for (id, driver_info) in hub::drivers().await? {
62+
debug!("Removing driver id {id:?} ({driver_info:?})");
63+
hub::remove_driver(id).await?;
64+
}
65+
66+
Ok(())
67+
}

tests/zenoh/mod.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use std::{path::PathBuf, time::Duration};
2+
3+
use anyhow::{Context, Result};
4+
use zenoh;
5+
6+
pub async fn spawn_zenoh_router(port: u16, topic_prefix: &str) -> tokio::task::JoinHandle<()> {
7+
let topic_prefix = topic_prefix.to_string();
8+
9+
tokio::spawn(async move {
10+
let mut config = zenoh::Config::default();
11+
config
12+
.insert_json5("mode", r#""router""#)
13+
.expect("Failed to insert router mode");
14+
config
15+
.insert_json5("listen/endpoints", &format!(r#"["tcp/127.0.0.1:{port}"]"#))
16+
.expect("Failed to insert listen endpoints");
17+
18+
let session = zenoh::open(config)
19+
.await
20+
.expect("Failed to start zenoh router for tests");
21+
22+
spawn_zenoh_loopback(session, &topic_prefix).await;
23+
24+
std::future::pending::<()>().await
25+
})
26+
}
27+
28+
async fn spawn_zenoh_loopback(session: zenoh::Session, topic_prefix: &str) {
29+
let out_topic = format!("{topic_prefix}/out");
30+
let in_topic = format!("{topic_prefix}/in");
31+
32+
tokio::spawn(async move {
33+
let subscriber = session
34+
.declare_subscriber(&out_topic)
35+
.await
36+
.unwrap_or_else(|error| panic!("Failed to subscribe to {out_topic}: {error:?}"));
37+
let publisher = session
38+
.declare_publisher(&in_topic)
39+
.await
40+
.unwrap_or_else(|error| panic!("Failed to publish on {in_topic}: {error:?}"));
41+
42+
while let Ok(sample) = subscriber.recv_async().await {
43+
if let Err(error) = publisher.put(sample.payload().to_bytes()).await {
44+
panic!("Failed to loop back {out_topic} -> {in_topic}: {error:?}");
45+
}
46+
}
47+
});
48+
}
49+
50+
pub fn write_zenoh_client_config(port: u16) -> Result<PathBuf> {
51+
let path = std::env::temp_dir().join(format!(
52+
"mavlink-server-zenoh-test-{}-{port}.json5",
53+
std::process::id()
54+
));
55+
56+
std::fs::write(
57+
&path,
58+
format!(
59+
r#"{{
60+
mode: "client",
61+
connect: {{ endpoints: ["tcp/127.0.0.1:{port}"] }}
62+
}}"#
63+
),
64+
)
65+
.with_context(|| format!("Failed to write zenoh config to {}", path.display()))?;
66+
67+
Ok(path)
68+
}
69+
70+
pub async fn wait_for_router() {
71+
tokio::time::sleep(Duration::from_millis(500)).await;
72+
}

tests/zenoh/raw.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use std::{sync::Arc, time::Duration};
2+
3+
use anyhow::{Context, Result};
4+
use clap::Parser;
5+
use mavlink_server::{cli, drivers::zenoh::raw::ZenohRaw, hub, stats};
6+
use tracing::*;
7+
8+
use crate::common::{pick_free_port, wait_for_stats_collection};
9+
use crate::zenoh::{spawn_zenoh_router, wait_for_router, write_zenoh_client_config};
10+
11+
#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
12+
async fn test_zenohraw_bidirectional() -> Result<()> {
13+
let driver_name = "ZenohRaw";
14+
let port = pick_free_port();
15+
let _router = spawn_zenoh_router(port, "mavlink_raw").await;
16+
wait_for_router().await;
17+
18+
let zenoh_config = write_zenoh_client_config(port)?;
19+
let zenoh_config = zenoh_config
20+
.to_str()
21+
.context("Zenoh config path is not valid UTF-8")?;
22+
23+
cli::init_with(cli::Args::parse_from(vec![
24+
&std::env::args().next().unwrap_or_default(),
25+
"--allow-no-endpoints",
26+
"--zenoh-config-file",
27+
zenoh_config,
28+
"--mavlink-heartbeat-frequency",
29+
"10",
30+
]));
31+
32+
hub::add_driver(Arc::new(ZenohRaw::builder(driver_name).build())).await?;
33+
34+
stats::set_period(Duration::from_millis(100)).await?;
35+
wait_for_stats_collection().await;
36+
37+
let mut matching_drivers = 0;
38+
for (_uuid, driver) in stats::drivers_stats().await? {
39+
if *driver.name != driver_name {
40+
continue;
41+
}
42+
43+
matching_drivers += 1;
44+
assert!(
45+
driver.stats.input.is_some(),
46+
"{driver_name} should receive mavlink over zenoh"
47+
);
48+
assert!(
49+
driver.stats.output.is_some(),
50+
"{driver_name} should publish mavlink over zenoh"
51+
);
52+
}
53+
54+
assert_eq!(
55+
matching_drivers, 1,
56+
"expected one {driver_name} driver in stats"
57+
);
58+
59+
for (id, driver_info) in hub::drivers().await? {
60+
debug!("Removing driver id {id:?} ({driver_info:?})");
61+
hub::remove_driver(id).await?;
62+
}
63+
64+
Ok(())
65+
}

0 commit comments

Comments
 (0)