Add a couple of integration tests as rust/tests/test_async_*.rs. They
are very similar to the tests for the synchronous API.
---
rust/tests/test_async_100_handle.rs | 25 +++
rust/tests/test_async_200_connect_command.rs | 34 ++++
rust/tests/test_async_210_opt_abort.rs | 40 +++++
rust/tests/test_async_220_opt_list.rs | 86 ++++++++++
rust/tests/test_async_230_opt_info.rs | 126 +++++++++++++++
rust/tests/test_async_235memleak.rs | 57 +++++++
rust/tests/test_async_240_opt_list_meta.rs | 151 ++++++++++++++++++
.../test_async_245_opt_list_meta_queries.rs | 96 +++++++++++
rust/tests/test_async_250_opt_set_meta.rs | 123 ++++++++++++++
.../test_async_255_opt_set_meta_queries.rs | 111 +++++++++++++
rust/tests/test_async_400_pread.rs | 41 +++++
rust/tests/test_async_405_pread_structured.rs | 85 ++++++++++
rust/tests/test_async_410_pwrite.rs | 63 ++++++++
rust/tests/test_async_460_block_status.rs | 96 +++++++++++
rust/tests/test_async_620_stats.rs | 77 +++++++++
15 files changed, 1211 insertions(+)
create mode 100644 rust/tests/test_async_100_handle.rs
create mode 100644 rust/tests/test_async_200_connect_command.rs
create mode 100644 rust/tests/test_async_210_opt_abort.rs
create mode 100644 rust/tests/test_async_220_opt_list.rs
create mode 100644 rust/tests/test_async_230_opt_info.rs
create mode 100644 rust/tests/test_async_235memleak.rs
create mode 100644 rust/tests/test_async_240_opt_list_meta.rs
create mode 100644 rust/tests/test_async_245_opt_list_meta_queries.rs
create mode 100644 rust/tests/test_async_250_opt_set_meta.rs
create mode 100644 rust/tests/test_async_255_opt_set_meta_queries.rs
create mode 100644 rust/tests/test_async_400_pread.rs
create mode 100644 rust/tests/test_async_405_pread_structured.rs
create mode 100644 rust/tests/test_async_410_pwrite.rs
create mode 100644 rust/tests/test_async_460_block_status.rs
create mode 100644 rust/tests/test_async_620_stats.rs
diff --git a/rust/tests/test_async_100_handle.rs b/rust/tests/test_async_100_handle.rs
new file mode 100644
index 0000000..e50bad9
--- /dev/null
+++ b/rust/tests/test_async_100_handle.rs
@@ -0,0 +1,25 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+//! Just check that we can link with libnbd and create a handle.
+
+#![deny(warnings)]
+
+#[tokio::test]
+async fn test_async_nbd_handle_new() {
+ let _ = libnbd::AsyncHandle::new().unwrap();
+}
diff --git a/rust/tests/test_async_200_connect_command.rs
b/rust/tests/test_async_200_connect_command.rs
new file mode 100644
index 0000000..dae08a6
--- /dev/null
+++ b/rust/tests/test_async_200_connect_command.rs
@@ -0,0 +1,34 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#![deny(warnings)]
+
+use byte_strings::c_str;
+
+#[tokio::test]
+async fn test_async_connect_command() {
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("null"),
+ ])
+ .await
+ .unwrap();
+}
diff --git a/rust/tests/test_async_210_opt_abort.rs
b/rust/tests/test_async_210_opt_abort.rs
new file mode 100644
index 0000000..a84783e
--- /dev/null
+++ b/rust/tests/test_async_210_opt_abort.rs
@@ -0,0 +1,40 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#![deny(warnings)]
+
+use byte_strings::c_str;
+
+#[tokio::test]
+async fn test_opt_abort() {
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+ nbd.set_opt_mode(true).unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("null"),
+ ])
+ .await
+ .unwrap();
+ assert_eq!(nbd.get_protocol().unwrap(), c_str!("newstyle-fixed"));
+ assert!(nbd.get_structured_replies_negotiated().unwrap());
+
+ nbd.opt_abort().await.unwrap();
+ assert!(nbd.aio_is_closed());
+}
diff --git a/rust/tests/test_async_220_opt_list.rs
b/rust/tests/test_async_220_opt_list.rs
new file mode 100644
index 0000000..bfe29f2
--- /dev/null
+++ b/rust/tests/test_async_220_opt_list.rs
@@ -0,0 +1,86 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#![deny(warnings)]
+
+use byte_strings::c_str;
+use std::env;
+use std::ffi::{CStr, CString};
+use std::os::unix::ffi::OsStringExt as _;
+use std::path::Path;
+use std::sync::Arc;
+
+/// Test different types of connections.
+struct ConnTester {
+ script_path: CString,
+}
+
+impl ConnTester {
+ fn new() -> Self {
+ let srcdir = env::var("srcdir").unwrap();
+ let srcdir = Path::new(&srcdir);
+ let script_path = srcdir.join("../tests/opt-list.sh");
+ let script_path =
+ CString::new(script_path.into_os_string().into_vec()).unwrap();
+ Self { script_path }
+ }
+
+ async fn connect(
+ &self,
+ mode: u8,
+ expected_exports: &[&CStr],
+ ) -> Result<(), Arc<libnbd::Error>> {
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+ nbd.set_opt_mode(true).unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("sh"),
+ &self.script_path,
+ &CString::new(format!("mode={mode}")).unwrap(),
+ ])
+ .await
+ .unwrap();
+
+ // Collect all exports in this list.
+ let mut exports = Vec::new();
+ nbd.opt_list(|name, _| {
+ exports.push(name.to_owned());
+ 0
+ })
+ .await?;
+ assert_eq!(exports.len(), expected_exports.len());
+ for (export, &expected) in exports.iter().zip(expected_exports) {
+ assert_eq!(export.as_c_str(), expected);
+ }
+ Ok(())
+ }
+}
+
+#[tokio::test]
+async fn test_opt_list() {
+ let conn_tester = ConnTester::new();
+ assert!(conn_tester.connect(0, &[]).await.is_err());
+ assert!(conn_tester
+ .connect(1, &[c_str!("a"), c_str!("b")])
+ .await
+ .is_ok());
+ assert!(conn_tester.connect(2, &[]).await.is_ok());
+ assert!(conn_tester.connect(3, &[c_str!("a")]).await.is_ok());
+}
diff --git a/rust/tests/test_async_230_opt_info.rs
b/rust/tests/test_async_230_opt_info.rs
new file mode 100644
index 0000000..b72ed5e
--- /dev/null
+++ b/rust/tests/test_async_230_opt_info.rs
@@ -0,0 +1,126 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#![deny(warnings)]
+
+use byte_strings::c_str;
+use libnbd::CONTEXT_BASE_ALLOCATION;
+use std::env;
+use std::ffi::CString;
+use std::os::unix::ffi::OsStringExt as _;
+use std::path::Path;
+
+#[tokio::test]
+async fn test_opt_info() {
+ let srcdir = env::var("srcdir").unwrap();
+ let srcdir = Path::new(&srcdir);
+ let script_path = srcdir.join("../tests/opt-info.sh");
+ let script_path =
+ CString::new(script_path.into_os_string().into_vec()).unwrap();
+
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+ nbd.set_opt_mode(true).unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("sh"),
+ &script_path,
+ ])
+ .await
+ .unwrap();
+ nbd.add_meta_context(CONTEXT_BASE_ALLOCATION).unwrap();
+
+ // No size, flags, or meta-contexts yet
+ assert!(nbd.get_size().is_err());
+ assert!(nbd.is_read_only().is_err());
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).is_err());
+
+ // info with no prior name gets info on ""
+ assert!(nbd.opt_info().await.is_ok());
+ assert_eq!(nbd.get_size().unwrap(), 0);
+ assert!(nbd.is_read_only().unwrap());
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap());
+
+ // changing export wipes out prior info
+ nbd.set_export_name(c_str!("b")).unwrap();
+ assert!(nbd.get_size().is_err());
+ assert!(nbd.is_read_only().is_err());
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).is_err());
+
+ // info on something not present fails
+ nbd.set_export_name(c_str!("a")).unwrap();
+ assert!(nbd.opt_info().await.is_err());
+
+ // info for a different export, with automatic meta_context disabled
+ nbd.set_export_name(c_str!("b")).unwrap();
+ nbd.set_request_meta_context(false).unwrap();
+ nbd.opt_info().await.unwrap();
+ // idempotent name change is no-op
+ nbd.set_export_name(c_str!("b")).unwrap();
+ assert_eq!(nbd.get_size().unwrap(), 1);
+ assert!(!nbd.is_read_only().unwrap());
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).is_err());
+ nbd.set_request_meta_context(true).unwrap();
+
+ // go on something not present
+ nbd.set_export_name(c_str!("a")).unwrap();
+ assert!(nbd.opt_go().await.is_err());
+ assert!(nbd.get_size().is_err());
+ assert!(nbd.is_read_only().is_err());
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).is_err());
+
+ // go on a valid export
+ nbd.set_export_name(c_str!("good")).unwrap();
+ nbd.opt_go().await.unwrap();
+ assert_eq!(nbd.get_size().unwrap(), 4);
+ assert!(nbd.is_read_only().unwrap());
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap());
+
+ // now info is no longer valid, but does not wipe data
+ assert!(nbd.set_export_name(c_str!("a")).is_err());
+ assert_eq!(nbd.get_export_name().unwrap().as_c_str(), c_str!("good"));
+ assert!(nbd.opt_info().await.is_err());
+ assert_eq!(nbd.get_size().unwrap(), 4);
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap());
+ nbd.shutdown(None).unwrap();
+
+ // Another connection. This time, check that SET_META triggered by opt_info
+ // persists through nbd_opt_go with set_request_meta_context disabled.
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+ nbd.set_opt_mode(true).unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("sh"),
+ &script_path,
+ ])
+ .await
+ .unwrap();
+ nbd.add_meta_context(c_str!("x-unexpected:bogus")).unwrap();
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).is_err());
+ nbd.opt_info().await.unwrap();
+ assert!(!nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap());
+ nbd.set_request_meta_context(false).unwrap();
+ // Adding to the request list now won't matter
+ nbd.add_meta_context(CONTEXT_BASE_ALLOCATION).unwrap();
+ nbd.opt_go().await.unwrap();
+ assert!(!nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap());
+}
diff --git a/rust/tests/test_async_235memleak.rs b/rust/tests/test_async_235memleak.rs
new file mode 100644
index 0000000..68ab909
--- /dev/null
+++ b/rust/tests/test_async_235memleak.rs
@@ -0,0 +1,57 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+//#![deny(warnings)]
+
+use byte_strings::c_str;
+use std::env;
+use std::ffi::CString;
+use std::os::unix::ffi::OsStringExt as _;
+use std::path::Path;
+
+#[tokio::test]
+async fn test_opt_info() {
+ let srcdir = env::var("srcdir").unwrap();
+ let srcdir = Path::new(&srcdir);
+ let script_path = srcdir.join("../tests/opt-info.sh");
+ let script_path =
+ CString::new(script_path.into_os_string().into_vec()).unwrap();
+
+ let nbd = libnbd::Handle::new().unwrap();
+ nbd.set_opt_mode(true).unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("sh"),
+ &script_path,
+ ])
+ .unwrap();
+
+ // go on a valid export
+ nbd.set_export_name(c_str!("good")).unwrap();
+ nbd.opt_go().unwrap();
+ let v = vec![1, 2, 3];
+ assert!(nbd
+ .aio_opt_info(Some(move |i: &mut i32| {
+ println!("I is: {i}");
+ dbg!(&v);
+ std::process::abort()
+ }))
+ .is_err());
+}
diff --git a/rust/tests/test_async_240_opt_list_meta.rs
b/rust/tests/test_async_240_opt_list_meta.rs
new file mode 100644
index 0000000..ec353c5
--- /dev/null
+++ b/rust/tests/test_async_240_opt_list_meta.rs
@@ -0,0 +1,151 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#![deny(warnings)]
+
+use byte_strings::c_str;
+use std::sync::Arc;
+
+/// A struct with information about listed meta contexts.
+#[derive(Debug, Clone, PartialEq, Eq)]
+struct CtxInfo {
+ /// Whether the meta context "base:alloc" is listed.
+ has_alloc: bool,
+ /// The number of listed meta contexts.
+ count: u32,
+}
+
+async fn list_meta_ctxs(
+ nbd: &libnbd::AsyncHandle,
+) -> Result<CtxInfo, Arc<libnbd::Error>> {
+ let mut info = CtxInfo {
+ has_alloc: false,
+ count: 0,
+ };
+ nbd.opt_list_meta_context(|ctx| {
+ info.count += 1;
+ if ctx == libnbd::CONTEXT_BASE_ALLOCATION {
+ info.has_alloc = true;
+ }
+ 0
+ })
+ .await?;
+ Ok(info)
+}
+
+#[tokio::test]
+async fn test_async_opt_list_meta() {
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+ nbd.set_opt_mode(true).unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("memory"),
+ c_str!("size=1M"),
+ ])
+ .await
+ .unwrap();
+
+ // First pass: empty query should give at least "base:allocation".
+ let info = list_meta_ctxs(&nbd).await.unwrap();
+ assert!(info.count >= 1);
+ assert!(info.has_alloc);
+ let max = info.count;
+
+ // Second pass: bogus query has no response.
+ nbd.add_meta_context(c_str!("x-nosuch:")).unwrap();
+ assert_eq!(
+ list_meta_ctxs(&nbd).await.unwrap(),
+ CtxInfo {
+ count: 0,
+ has_alloc: false
+ }
+ );
+
+ // Third pass: specific query should have one match.
+ nbd.add_meta_context(c_str!("base:allocation")).unwrap();
+ assert_eq!(nbd.get_nr_meta_contexts().unwrap(), 2);
+ assert_eq!(
+ nbd.get_meta_context(1).unwrap().as_c_str(),
+ c_str!("base:allocation")
+ );
+ assert_eq!(
+ list_meta_ctxs(&nbd).await.unwrap(),
+ CtxInfo {
+ count: 1,
+ has_alloc: true
+ }
+ );
+
+ // Fourth pass: opt_list_meta_context is stateless, so it should
+ // not wipe status learned during opt_info
+ assert!(nbd.can_meta_context(c_str!("base:allocation")).is_err());
+ assert!(nbd.get_size().is_err());
+ nbd.opt_info().await.unwrap();
+ assert_eq!(nbd.get_size().unwrap(), 1048576);
+ assert!(nbd.can_meta_context(c_str!("base:allocation")).unwrap());
+ nbd.clear_meta_contexts().unwrap();
+ nbd.add_meta_context(c_str!("x-nosuch:")).unwrap();
+ assert_eq!(
+ list_meta_ctxs(&nbd).await.unwrap(),
+ CtxInfo {
+ count: 0,
+ has_alloc: false
+ }
+ );
+ assert_eq!(nbd.get_size().unwrap(), 1048576);
+ assert!(nbd.can_meta_context(c_str!("base:allocation")).unwrap());
+
+ // Final pass: "base:" query should get at least
"base:allocation"
+ nbd.add_meta_context(c_str!("base:")).unwrap();
+ let info = list_meta_ctxs(&nbd).await.unwrap();
+ assert!(info.count >= 1);
+ assert!(info.count <= max);
+ assert!(info.has_alloc);
+
+ // Repeat but this time without structured replies. Deal gracefully
+ // with older servers that don't allow the attempt.
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+ nbd.set_opt_mode(true).unwrap();
+ nbd.set_request_structured_replies(false).unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("memory"),
+ c_str!("size=1M"),
+ ])
+ .await
+ .unwrap();
+ let bytes = nbd.stats_bytes_sent();
+ if let Ok(info) = list_meta_ctxs(&nbd).await {
+ assert!(info.count >= 1);
+ assert!(info.has_alloc)
+ } else {
+ assert!(nbd.stats_bytes_sent() > bytes);
+ // ignoring failure from old server
+ }
+
+ // Now enable structured replies, and a retry should pass.
+ nbd.opt_structured_reply().await.unwrap();
+ let info = list_meta_ctxs(&nbd).await.unwrap();
+ assert!(info.count >= 1);
+ assert!(info.has_alloc);
+}
diff --git a/rust/tests/test_async_245_opt_list_meta_queries.rs
b/rust/tests/test_async_245_opt_list_meta_queries.rs
new file mode 100644
index 0000000..47046e7
--- /dev/null
+++ b/rust/tests/test_async_245_opt_list_meta_queries.rs
@@ -0,0 +1,96 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#![deny(warnings)]
+
+use byte_strings::c_str;
+use std::ffi::CStr;
+use std::sync::Arc;
+
+/// A struct with information about listed meta contexts.
+#[derive(Debug, Clone, PartialEq, Eq)]
+struct CtxInfo {
+ /// Whether the meta context "base:allocation" is listed.
+ has_alloc: bool,
+ /// The number of listed meta contexts.
+ count: u32,
+}
+
+async fn list_meta_ctxs(
+ nbd: &libnbd::AsyncHandle,
+ queries: &[&CStr],
+) -> Result<CtxInfo, Arc<libnbd::Error>> {
+ let mut info = CtxInfo {
+ has_alloc: false,
+ count: 0,
+ };
+ nbd.opt_list_meta_context_queries(queries, |ctx| {
+ info.count += 1;
+ if ctx == libnbd::CONTEXT_BASE_ALLOCATION {
+ info.has_alloc = true;
+ }
+ 0
+ })
+ .await?;
+ Ok(info)
+}
+
+#[tokio::test]
+async fn test_async_opt_list_meta_queries() {
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+ nbd.set_opt_mode(true).unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("memory"),
+ c_str!("size=1M"),
+ ])
+ .await
+ .unwrap();
+
+ // First pass: empty query should give at least "base:allocation".
+ nbd.add_meta_context(c_str!("x-nosuch:")).unwrap();
+ let info = list_meta_ctxs(&nbd, &[]).await.unwrap();
+ assert!(info.count >= 1);
+ assert!(info.has_alloc);
+
+ // Second pass: bogus query has no response.
+ nbd.clear_meta_contexts().unwrap();
+ assert_eq!(
+ list_meta_ctxs(&nbd, &[c_str!("x-nosuch:")]).await.unwrap(),
+ CtxInfo {
+ count: 0,
+ has_alloc: false
+ }
+ );
+
+ // Third pass: specific query should have one match.
+ assert_eq!(
+ list_meta_ctxs(
+ &nbd,
+ &[c_str!("x-nosuch:"), libnbd::CONTEXT_BASE_ALLOCATION]
+ )
+ .await
+ .unwrap(),
+ CtxInfo {
+ count: 1,
+ has_alloc: true
+ }
+ );
+}
diff --git a/rust/tests/test_async_250_opt_set_meta.rs
b/rust/tests/test_async_250_opt_set_meta.rs
new file mode 100644
index 0000000..7fc6953
--- /dev/null
+++ b/rust/tests/test_async_250_opt_set_meta.rs
@@ -0,0 +1,123 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#![deny(warnings)]
+
+use byte_strings::c_str;
+use libnbd::CONTEXT_BASE_ALLOCATION;
+use std::sync::Arc;
+
+/// A struct with information about set meta contexts.
+#[derive(Debug, Clone, PartialEq, Eq)]
+struct CtxInfo {
+ /// Whether the meta context "base:allocation" is set.
+ has_alloc: bool,
+ /// The number of set meta contexts.
+ count: u32,
+}
+
+async fn set_meta_ctxs(
+ nbd: &libnbd::AsyncHandle,
+) -> Result<CtxInfo, Arc<libnbd::Error>> {
+ let mut info = CtxInfo {
+ has_alloc: false,
+ count: 0,
+ };
+ nbd.opt_set_meta_context(|ctx| {
+ info.count += 1;
+ if ctx == CONTEXT_BASE_ALLOCATION {
+ info.has_alloc = true;
+ }
+ 0
+ })
+ .await?;
+ Ok(info)
+}
+
+#[tokio::test]
+async fn test_async_opt_set_meta() {
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+ nbd.set_opt_mode(true).unwrap();
+ nbd.set_request_structured_replies(false).unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("memory"),
+ c_str!("size=1M"),
+ ])
+ .await
+ .unwrap();
+
+ // No contexts negotiated yet; can_meta should be error if any requested
+ assert!(!nbd.get_structured_replies_negotiated().unwrap());
+ assert!(!nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap());
+ nbd.add_meta_context(CONTEXT_BASE_ALLOCATION).unwrap();
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).is_err());
+
+ // SET cannot succeed until SR is negotiated.
+ nbd.opt_structured_reply().await.unwrap();
+ assert!(nbd.get_structured_replies_negotiated().unwrap());
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).is_err());
+
+ // nbdkit does not match wildcard for SET, even though it does for LIST
+ nbd.clear_meta_contexts().unwrap();
+ nbd.add_meta_context(c_str!("base:")).unwrap();
+ assert_eq!(
+ set_meta_ctxs(&nbd).await.unwrap(),
+ CtxInfo {
+ count: 0,
+ has_alloc: false
+ }
+ );
+ assert!(!nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap());
+
+ // Negotiating with no contexts is not an error, but selects nothing
+ nbd.clear_meta_contexts().unwrap();
+ assert_eq!(
+ set_meta_ctxs(&nbd).await.unwrap(),
+ CtxInfo {
+ count: 0,
+ has_alloc: false
+ }
+ );
+
+ // Request 2 with expectation of 1; with set_request_meta_context off
+ nbd.add_meta_context(c_str!("x-nosuch:context")).unwrap();
+ nbd.add_meta_context(CONTEXT_BASE_ALLOCATION).unwrap();
+ nbd.set_request_meta_context(false).unwrap();
+ assert_eq!(
+ set_meta_ctxs(&nbd).await.unwrap(),
+ CtxInfo {
+ count: 1,
+ has_alloc: true
+ }
+ );
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap());
+
+ // Transition to transmission phase; our last set should remain active
+ nbd.clear_meta_contexts().unwrap();
+ nbd.add_meta_context(c_str!("x-nosuch:context")).unwrap();
+ nbd.opt_go().await.unwrap();
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap());
+
+ // Now too late to set; but should not lose earlier state
+ assert!(set_meta_ctxs(&nbd).await.is_err());
+ assert_eq!(nbd.get_size().unwrap(), 1048576);
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap());
+}
diff --git a/rust/tests/test_async_255_opt_set_meta_queries.rs
b/rust/tests/test_async_255_opt_set_meta_queries.rs
new file mode 100644
index 0000000..6cec20b
--- /dev/null
+++ b/rust/tests/test_async_255_opt_set_meta_queries.rs
@@ -0,0 +1,111 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#![deny(warnings)]
+
+use byte_strings::c_str;
+use libnbd::CONTEXT_BASE_ALLOCATION;
+use std::ffi::CStr;
+use std::sync::Arc;
+
+/// A struct with information about set meta contexts.
+#[derive(Debug, Clone, PartialEq, Eq)]
+struct CtxInfo {
+ /// Whether the meta context "base:allocation" is set.
+ has_alloc: bool,
+ /// The number of set meta contexts.
+ count: u32,
+}
+
+async fn set_meta_ctxs_queries(
+ nbd: &libnbd::AsyncHandle,
+ queries: &[&CStr],
+) -> Result<CtxInfo, Arc<libnbd::Error>> {
+ let mut info = CtxInfo {
+ has_alloc: false,
+ count: 0,
+ };
+ nbd.opt_set_meta_context_queries(queries, |ctx| {
+ info.count += 1;
+ if ctx == CONTEXT_BASE_ALLOCATION {
+ info.has_alloc = true;
+ }
+ 0
+ })
+ .await?;
+ Ok(info)
+}
+
+#[tokio::test]
+async fn test_async_opt_set_meta_queries() {
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+ nbd.set_opt_mode(true).unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("memory"),
+ c_str!("size=1M"),
+ ])
+ .await
+ .unwrap();
+
+ // nbdkit does not match wildcard for SET, even though it does for LIST
+ assert_eq!(
+ set_meta_ctxs_queries(&nbd, &[c_str!("base:")])
+ .await
+ .unwrap(),
+ CtxInfo {
+ count: 0,
+ has_alloc: false
+ }
+ );
+ assert!(!nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap());
+
+ // Negotiating with no contexts is not an error, but selects nothing
+ // An explicit empty list overrides a non-empty implicit list.
+ nbd.add_meta_context(CONTEXT_BASE_ALLOCATION).unwrap();
+ assert_eq!(
+ set_meta_ctxs_queries(&nbd, &[]).await.unwrap(),
+ CtxInfo {
+ count: 0,
+ has_alloc: false
+ }
+ );
+ assert!(!nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap());
+
+ // Request 2 with expectation of 1.
+ assert_eq!(
+ set_meta_ctxs_queries(
+ &nbd,
+ &[c_str!("x-nosuch:context"), CONTEXT_BASE_ALLOCATION]
+ )
+ .await
+ .unwrap(),
+ CtxInfo {
+ count: 1,
+ has_alloc: true
+ }
+ );
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap());
+
+ // Transition to transmission phase; our last set should remain active
+ nbd.set_request_meta_context(false).unwrap();
+ nbd.opt_go().await.unwrap();
+ assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap());
+}
diff --git a/rust/tests/test_async_400_pread.rs b/rust/tests/test_async_400_pread.rs
new file mode 100644
index 0000000..96c4ce1
--- /dev/null
+++ b/rust/tests/test_async_400_pread.rs
@@ -0,0 +1,41 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#![deny(warnings)]
+
+mod nbdkit_pattern;
+use byte_strings::c_str;
+use nbdkit_pattern::PATTERN;
+
+#[tokio::test]
+async fn test_async_pread() {
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("pattern"),
+ c_str!("size=1M"),
+ ])
+ .await
+ .unwrap();
+
+ let mut buf = [0; 512];
+ nbd.pread(&mut buf, 0, None).await.unwrap();
+ assert_eq!(buf.as_slice(), PATTERN.as_slice());
+}
diff --git a/rust/tests/test_async_405_pread_structured.rs
b/rust/tests/test_async_405_pread_structured.rs
new file mode 100644
index 0000000..40b342f
--- /dev/null
+++ b/rust/tests/test_async_405_pread_structured.rs
@@ -0,0 +1,85 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#![deny(warnings)]
+
+mod nbdkit_pattern;
+use byte_strings::c_str;
+use nbdkit_pattern::PATTERN;
+
+#[tokio::test]
+async fn test_async_pread_structured() {
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ //c_str!("-v"), XXX: uncomment this
+ c_str!("pattern"),
+ c_str!("size=1M"),
+ ])
+ .await
+ .unwrap();
+
+ fn f(buf: &[u8], offset: u64, s: u32, err: &mut i32) {
+ assert_eq!(*err, 0);
+ *err = 42;
+ assert_eq!(buf, PATTERN.as_slice());
+ assert_eq!(offset, 0);
+ assert_eq!(s, libnbd::READ_DATA);
+ }
+
+ let mut buf = [0; 512];
+ nbd.pread_structured(
+ &mut buf,
+ 0,
+ |b, o, s, e| {
+ f(b, o, s, e);
+ 0
+ },
+ None,
+ )
+ .await
+ .unwrap();
+ assert_eq!(buf.as_slice(), PATTERN.as_slice());
+
+ nbd.pread_structured(
+ &mut buf,
+ 0,
+ |b, o, s, e| {
+ f(b, o, s, e);
+ 0
+ },
+ Some(libnbd::CmdFlag::DF),
+ )
+ .await
+ .unwrap();
+ assert_eq!(buf.as_slice(), PATTERN.as_slice());
+
+ let res = nbd
+ .pread_structured(
+ &mut buf,
+ 0,
+ |b, o, s, e| {
+ f(b, o, s, e);
+ -1
+ },
+ Some(libnbd::CmdFlag::DF),
+ )
+ .await;
+ assert_eq!(res.unwrap_err().errno(), Some(42));
+}
diff --git a/rust/tests/test_async_410_pwrite.rs b/rust/tests/test_async_410_pwrite.rs
new file mode 100644
index 0000000..fdaae42
--- /dev/null
+++ b/rust/tests/test_async_410_pwrite.rs
@@ -0,0 +1,63 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#![deny(warnings)]
+
+use byte_strings::c_str;
+use std::ffi::CString;
+use std::fs::{self, File};
+use std::os::unix::prelude::*;
+
+#[tokio::test]
+async fn test_async_pwrite() {
+ let tmp_dir = tempfile::tempdir().unwrap();
+ let data_file_path = tmp_dir.path().join("pwrite_test.data");
+ let data_file = File::create(&data_file_path).unwrap();
+ data_file.set_len(512).unwrap();
+ drop(data_file);
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("file"),
+ &CString::new(data_file_path.clone().into_os_string().into_vec())
+ .unwrap(),
+ ])
+ .await
+ .unwrap();
+
+ let mut buf_1 = [0; 512];
+ buf_1[10] = 0x01;
+ buf_1[510] = 0x55;
+ buf_1[511] = 0xAA;
+
+ let flags = Some(libnbd::CmdFlag::FUA);
+ nbd.pwrite(&buf_1, 0, flags).await.unwrap();
+
+ let mut buf_2 = [0; 512];
+ nbd.pread(&mut buf_2, 0, None).await.unwrap();
+
+ assert_eq!(buf_1, buf_2);
+
+ // Drop nbd before tmp_dir is dropped.
+ drop(nbd);
+
+ let data_file_content = fs::read(&data_file_path).unwrap();
+ assert_eq!(buf_1.as_slice(), data_file_content.as_slice());
+}
diff --git a/rust/tests/test_async_460_block_status.rs
b/rust/tests/test_async_460_block_status.rs
new file mode 100644
index 0000000..7f1b041
--- /dev/null
+++ b/rust/tests/test_async_460_block_status.rs
@@ -0,0 +1,96 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#![deny(warnings)]
+
+use byte_strings::c_str;
+use std::env;
+use std::ffi::CString;
+use std::os::unix::prelude::*;
+use std::path::Path;
+
+async fn block_status_get_entries(
+ nbd: &libnbd::AsyncHandle,
+ count: u64,
+ offset: u64,
+ flags: Option<libnbd::CmdFlag>,
+) -> Vec<u32> {
+ let mut found_entries = None;
+ nbd.block_status(
+ count,
+ offset,
+ |metacontext, _, entries, err| {
+ assert_eq!(*err, 0);
+ if metacontext == libnbd::CONTEXT_BASE_ALLOCATION {
+ found_entries = Some(entries.to_vec());
+ }
+ 0
+ },
+ flags,
+ )
+ .await
+ .unwrap();
+ found_entries.unwrap()
+}
+
+#[tokio::test]
+async fn test_async_block_status() {
+ let srcdir = env::var("srcdir").unwrap();
+ let srcdir = Path::new(&srcdir);
+ let script_path = srcdir.join("../tests/meta-base-allocation.sh");
+ let script_path =
+ CString::new(script_path.into_os_string().into_vec()).unwrap();
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+ nbd.add_meta_context(libnbd::CONTEXT_BASE_ALLOCATION)
+ .unwrap();
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("sh"),
+ &script_path,
+ ])
+ .await
+ .unwrap();
+
+ assert_eq!(
+ block_status_get_entries(&nbd, 65536, 0, None)
+ .await
+ .as_slice(),
+ &[8192, 0, 8192, 1, 16384, 3, 16384, 2, 16384, 0,]
+ );
+
+ assert_eq!(
+ block_status_get_entries(&nbd, 1024, 32256, None)
+ .await
+ .as_slice(),
+ &[512, 3, 16384, 2]
+ );
+
+ assert_eq!(
+ block_status_get_entries(
+ &nbd,
+ 1024,
+ 32256,
+ Some(libnbd::CmdFlag::REQ_ONE)
+ )
+ .await
+ .as_slice(),
+ &[512, 3]
+ );
+}
diff --git a/rust/tests/test_async_620_stats.rs b/rust/tests/test_async_620_stats.rs
new file mode 100644
index 0000000..870adb8
--- /dev/null
+++ b/rust/tests/test_async_620_stats.rs
@@ -0,0 +1,77 @@
+// libnbd Rust test case
+// Copyright Tage Johansson
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#![deny(warnings)]
+
+use byte_strings::c_str;
+
+#[tokio::test]
+async fn test_async_stats() {
+ let nbd = libnbd::AsyncHandle::new().unwrap();
+
+ // Pre-connection, stats start out at 0
+ assert_eq!(nbd.stats_bytes_sent(), 0);
+ assert_eq!(nbd.stats_chunks_sent(), 0);
+ assert_eq!(nbd.stats_bytes_received(), 0);
+ assert_eq!(nbd.stats_chunks_received(), 0);
+
+ // Connection performs handshaking, which increments stats.
+ // The number of bytes/chunks here may grow over time as more features get
+ // automatically negotiated, so merely check that they are non-zero.
+ nbd.connect_command(&[
+ c_str!("nbdkit"),
+ c_str!("-s"),
+ c_str!("--exit-with-parent"),
+ c_str!("-v"),
+ c_str!("null"),
+ ])
+ .await
+ .unwrap();
+
+ let bs1 = nbd.stats_bytes_sent();
+ let cs1 = nbd.stats_chunks_sent();
+ let br1 = nbd.stats_bytes_received();
+ let cr1 = nbd.stats_chunks_received();
+ assert!(cs1 > 0);
+ assert!(bs1 > cs1);
+ assert!(cr1 > 0);
+ assert!(br1 > cr1);
+
+ // A flush command should be one chunk out, one chunk back (even if
+ // structured replies are in use)
+ nbd.flush(None).await.unwrap();
+ let bs2 = nbd.stats_bytes_sent();
+ let cs2 = nbd.stats_chunks_sent();
+ let br2 = nbd.stats_bytes_received();
+ let cr2 = nbd.stats_chunks_received();
+ assert_eq!(bs2, bs1 + 28);
+ assert_eq!(cs2, cs1 + 1);
+ assert_eq!(br2, br1 + 16); // assumes nbdkit uses simple reply
+ assert_eq!(cr2, cr1 + 1);
+
+ // Stats are still readable after the connection closes; we don't know if
+ // the server sent reply bytes to our NBD_CMD_DISC, so don't insist on it.
+ nbd.shutdown(None).unwrap();
+ let bs3 = nbd.stats_bytes_sent();
+ let cs3 = nbd.stats_chunks_sent();
+ let br3 = nbd.stats_bytes_received();
+ let cr3 = nbd.stats_chunks_received();
+ assert!(bs3 > bs2);
+ assert_eq!(cs3, cs2 + 1);
+ assert!(br3 >= br2);
+ assert!(cr3 == cr2 || cr3 == cr2 + 1);
+}
--
2.41.0