Tage Johansson
2023-Jul-19 09:09 UTC
[Libguestfs] [libnbd PATCH 07/10] rust: Add a couple of integration tests
A couple of integration tests are added in rust/tests. They are mostly ported from the OCaml tests. --- rust/Cargo.toml | 4 + rust/Makefile.am | 1 + rust/run-tests.sh | 6 +- rust/tests/nbdkit_pattern/mod.rs | 28 ++++ rust/tests/test_100_handle.rs | 25 +++ rust/tests/test_110_defaults.rs | 33 ++++ rust/tests/test_120_set_non_defaults.rs | 56 +++++++ rust/tests/test_130_private_data.rs | 28 ++++ rust/tests/test_140_explicit_close.rs | 31 ++++ rust/tests/test_200_connect_command.rs | 33 ++++ rust/tests/test_210_opt_abort.rs | 39 +++++ rust/tests/test_220_opt_list.rs | 85 +++++++++++ rust/tests/test_230_opt_info.rs | 124 +++++++++++++++ rust/tests/test_240_opt_list_meta.rs | 151 +++++++++++++++++++ rust/tests/test_245_opt_list_meta_queries.rs | 98 ++++++++++++ rust/tests/test_250_opt_set_meta.rs | 124 +++++++++++++++ rust/tests/test_255_opt_set_meta_queries.rs | 111 ++++++++++++++ rust/tests/test_300_get_size.rs | 36 +++++ rust/tests/test_400_pread.rs | 40 +++++ rust/tests/test_405_pread_structured.rs | 80 ++++++++++ rust/tests/test_410_pwrite.rs | 62 ++++++++ rust/tests/test_460_block_status.rs | 96 ++++++++++++ rust/tests/test_620_stats.rs | 76 ++++++++++ rust/tests/test_log/mod.rs | 86 +++++++++++ 24 files changed, 1451 insertions(+), 2 deletions(-) create mode 100644 rust/tests/nbdkit_pattern/mod.rs create mode 100644 rust/tests/test_100_handle.rs create mode 100644 rust/tests/test_110_defaults.rs create mode 100644 rust/tests/test_120_set_non_defaults.rs create mode 100644 rust/tests/test_130_private_data.rs create mode 100644 rust/tests/test_140_explicit_close.rs create mode 100644 rust/tests/test_200_connect_command.rs create mode 100644 rust/tests/test_210_opt_abort.rs create mode 100644 rust/tests/test_220_opt_list.rs create mode 100644 rust/tests/test_230_opt_info.rs create mode 100644 rust/tests/test_240_opt_list_meta.rs create mode 100644 rust/tests/test_245_opt_list_meta_queries.rs create mode 100644 rust/tests/test_250_opt_set_meta.rs create mode 100644 rust/tests/test_255_opt_set_meta_queries.rs create mode 100644 rust/tests/test_300_get_size.rs create mode 100644 rust/tests/test_400_pread.rs create mode 100644 rust/tests/test_405_pread_structured.rs create mode 100644 rust/tests/test_410_pwrite.rs create mode 100644 rust/tests/test_460_block_status.rs create mode 100644 rust/tests/test_620_stats.rs create mode 100644 rust/tests/test_log/mod.rs diff --git a/rust/Cargo.toml b/rust/Cargo.toml index e81360b..f74c3ac 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -48,3 +48,7 @@ byte-strings = "0.3.1" [features] default = ["log"] + +[dev-dependencies] +once_cell = "1.18.0" +tempfile = "3.6.0" diff --git a/rust/Makefile.am b/rust/Makefile.am index cb8d7c9..9ff0779 100644 --- a/rust/Makefile.am +++ b/rust/Makefile.am @@ -49,6 +49,7 @@ TESTS_ENVIRONMENT = \ LIBNBD_DEBUG=1 \ $(MALLOC_CHECKS) \ abs_top_srcdir=$(abs_top_srcdir) \ + CARGO=$(CARGO) \ $(NULL) LOG_COMPILER = $(top_builddir)/run TESTS = run-tests.sh diff --git a/rust/run-tests.sh b/rust/run-tests.sh index 7a0bc85..005000e 100755 --- a/rust/run-tests.sh +++ b/rust/run-tests.sh @@ -1,6 +1,6 @@ #!/bin/bash - # nbd client library in userspace -# Copyright Red Hat +# 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 @@ -21,4 +21,6 @@ set -e set -x -cargo test +requires nbdkit --version + +$CARGO test -- --nocapture diff --git a/rust/tests/nbdkit_pattern/mod.rs b/rust/tests/nbdkit_pattern/mod.rs new file mode 100644 index 0000000..5f4069e --- /dev/null +++ b/rust/tests/nbdkit_pattern/mod.rs @@ -0,0 +1,28 @@ +// 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 + +use once_cell::sync::Lazy; + +/// The byte pattern as described in nbdkit-PATTERN-plugin(1). +pub static PATTERN: Lazy<Vec<u8>> = Lazy::new(|| { + let mut pattern = Vec::with_capacity(512); + for i in 0u64..64 { + pattern.extend_from_slice((i * 8).to_be_bytes().as_slice()); + } + assert_eq!(pattern.len(), 512); + pattern +}); diff --git a/rust/tests/test_100_handle.rs b/rust/tests/test_100_handle.rs new file mode 100644 index 0000000..85e18aa --- /dev/null +++ b/rust/tests/test_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)] + +#[test] +fn test_nbd_handle_new() { + let _ = libnbd::Handle::new().unwrap(); +} diff --git a/rust/tests/test_110_defaults.rs b/rust/tests/test_110_defaults.rs new file mode 100644 index 0000000..88b072c --- /dev/null +++ b/rust/tests/test_110_defaults.rs @@ -0,0 +1,33 @@ +// 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)] + +#[test] +fn test_defaults() { + let nbd = libnbd::Handle::new().unwrap(); + + assert!(nbd.get_export_name().unwrap().to_str().unwrap().is_empty()); + assert!(!nbd.get_full_info().unwrap()); + assert_eq!(nbd.get_tls(), libnbd::Tls::Disable); + assert!(nbd.get_request_structured_replies()); + assert!(nbd.get_request_meta_context().unwrap()); + assert!(nbd.get_request_block_size().unwrap()); + assert!(nbd.get_pread_initialize()); + assert!(nbd.get_handshake_flags().is_all()); + assert!(!nbd.get_opt_mode()); +} diff --git a/rust/tests/test_120_set_non_defaults.rs b/rust/tests/test_120_set_non_defaults.rs new file mode 100644 index 0000000..08920d2 --- /dev/null +++ b/rust/tests/test_120_set_non_defaults.rs @@ -0,0 +1,56 @@ +// 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 std::ffi::CString; + +#[test] +fn test_set_non_defaults() { + let nbd = libnbd::Handle::new().unwrap(); + + let name = CString::new("name").unwrap(); + nbd.set_export_name(&name).unwrap(); + assert_eq!(nbd.get_export_name().unwrap(), name); + + nbd.set_full_info(true).unwrap(); + assert!(nbd.get_full_info().unwrap()); + + if nbd.supports_tls() { + nbd.set_tls(libnbd::Tls::Allow).unwrap(); + assert_eq!(nbd.get_tls(), libnbd::Tls::Allow); + } + + nbd.set_request_structured_replies(false).unwrap(); + assert!(!nbd.get_request_structured_replies()); + + nbd.set_request_meta_context(false).unwrap(); + assert!(!nbd.get_request_meta_context().unwrap()); + + nbd.set_request_block_size(false).unwrap(); + assert!(!nbd.get_request_block_size().unwrap()); + + nbd.set_pread_initialize(false).unwrap(); + assert!(!nbd.get_pread_initialize()); + + nbd.set_handshake_flags(libnbd::HandshakeFlag::empty()) + .unwrap(); + assert!(nbd.get_handshake_flags().is_empty()); + + nbd.set_opt_mode(true).unwrap(); + assert!(nbd.get_opt_mode()); +} diff --git a/rust/tests/test_130_private_data.rs b/rust/tests/test_130_private_data.rs new file mode 100644 index 0000000..bb507fb --- /dev/null +++ b/rust/tests/test_130_private_data.rs @@ -0,0 +1,28 @@ +// 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)] + +#[test] +fn test_private_data() { + let nbd = libnbd::Handle::new().unwrap(); + + assert_eq!(nbd.get_private_data(), 0); + assert_eq!(nbd.set_private_data(42), 0); + assert_eq!(nbd.set_private_data(314), 42); + assert_eq!(nbd.get_private_data(), 314); +} diff --git a/rust/tests/test_140_explicit_close.rs b/rust/tests/test_140_explicit_close.rs new file mode 100644 index 0000000..59ab382 --- /dev/null +++ b/rust/tests/test_140_explicit_close.rs @@ -0,0 +1,31 @@ +// 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 test_log; + +use test_log::DEBUG_LOGGER; + +#[test] +fn test_private_data() { + DEBUG_LOGGER.init(); + + let nbd = libnbd::Handle::new().unwrap(); + drop(nbd); + assert!(DEBUG_LOGGER.contains("closing handle")); +} diff --git a/rust/tests/test_200_connect_command.rs b/rust/tests/test_200_connect_command.rs new file mode 100644 index 0000000..dcaa1a2 --- /dev/null +++ b/rust/tests/test_200_connect_command.rs @@ -0,0 +1,33 @@ +// 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; + +#[test] +fn test_connect_command() { + let nbd = libnbd::Handle::new().unwrap(); + nbd.connect_command(&[ + c_str!("nbdkit"), + c_str!("-s"), + c_str!("--exit-with-parent"), + c_str!("-v"), + c_str!("null"), + ]) + .unwrap(); +} diff --git a/rust/tests/test_210_opt_abort.rs b/rust/tests/test_210_opt_abort.rs new file mode 100644 index 0000000..1cba9a8 --- /dev/null +++ b/rust/tests/test_210_opt_abort.rs @@ -0,0 +1,39 @@ +// 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; + +#[test] +fn test_opt_abort() { + 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!("null"), + ]) + .unwrap(); + assert_eq!(nbd.get_protocol().unwrap(), c_str!("newstyle-fixed")); + assert!(nbd.get_structured_replies_negotiated().unwrap()); + + nbd.opt_abort().unwrap(); + assert!(nbd.aio_is_closed()); +} diff --git a/rust/tests/test_220_opt_list.rs b/rust/tests/test_220_opt_list.rs new file mode 100644 index 0000000..5abec5f --- /dev/null +++ b/rust/tests/test_220_opt_list.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)] + +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; +use std::sync::Mutex; + +/// 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 } + } + + fn connect( + &self, + mode: u8, + expected_exports: &[&CStr], + ) -> libnbd::Result<()> { + 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"), + &self.script_path, + &CString::new(format!("mode={mode}")).unwrap(), + ]) + .unwrap(); + + // Collect all exports in this list. + let exports = Arc::new(Mutex::new(Vec::new())); + let exports_clone = exports.clone(); + let count = nbd.opt_list(move |name, _| { + exports_clone.lock().unwrap().push(name.to_owned()); + 0 + })?; + let exports = Arc::into_inner(exports).unwrap().into_inner().unwrap(); + assert_eq!(exports.len(), count as usize); + assert_eq!(exports.len(), expected_exports.len()); + for (export, &expected) in exports.iter().zip(expected_exports) { + assert_eq!(export.as_c_str(), expected); + } + Ok(()) + } +} + +#[test] +fn test_opt_list() { + let conn_tester = ConnTester::new(); + assert!(conn_tester.connect(0, &[]).is_err()); + assert!(conn_tester.connect(1, &[c_str!("a"), c_str!("b")]).is_ok()); + assert!(conn_tester.connect(2, &[]).is_ok()); + assert!(conn_tester.connect(3, &[c_str!("a")]).is_ok()); +} diff --git a/rust/tests/test_230_opt_info.rs b/rust/tests/test_230_opt_info.rs new file mode 100644 index 0000000..f5103c1 --- /dev/null +++ b/rust/tests/test_230_opt_info.rs @@ -0,0 +1,124 @@ +// 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; + +#[test] +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(); + 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().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().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().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().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().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().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::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(); + nbd.add_meta_context(c_str!("x-unexpected:bogus")).unwrap(); + assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).is_err()); + nbd.opt_info().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().unwrap(); + assert!(!nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap()); +} diff --git a/rust/tests/test_240_opt_list_meta.rs b/rust/tests/test_240_opt_list_meta.rs new file mode 100644 index 0000000..60c1841 --- /dev/null +++ b/rust/tests/test_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; +use std::sync::Mutex; + +/// 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, +} + +fn list_meta_ctxs(nbd: &libnbd::Handle) -> libnbd::Result<CtxInfo> { + let info = Arc::new(Mutex::new(CtxInfo { + has_alloc: false, + count: 0, + })); + let info_clone = info.clone(); + let replies = nbd.opt_list_meta_context(move |ctx| { + let mut info = info_clone.lock().unwrap(); + info.count += 1; + if ctx == libnbd::CONTEXT_BASE_ALLOCATION { + info.has_alloc = true; + } + 0 + })?; + let info = Arc::into_inner(info).unwrap().into_inner().unwrap(); + assert_eq!(info.count, replies); + Ok(info) +} + +#[test] +fn test_opt_list_meta() { + 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!("memory"), + c_str!("size=1M"), + ]) + .unwrap(); + + // First pass: empty query should give at least "base:allocation". + let info = list_meta_ctxs(&nbd).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).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).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().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).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).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::Handle::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"), + ]) + .unwrap(); + let bytes = nbd.stats_bytes_sent(); + if let Ok(info) = list_meta_ctxs(&nbd) { + 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. + assert!(nbd.opt_structured_reply().unwrap()); + let info = list_meta_ctxs(&nbd).unwrap(); + assert!(info.count >= 1); + assert!(info.has_alloc); +} diff --git a/rust/tests/test_245_opt_list_meta_queries.rs b/rust/tests/test_245_opt_list_meta_queries.rs new file mode 100644 index 0000000..0a5bb05 --- /dev/null +++ b/rust/tests/test_245_opt_list_meta_queries.rs @@ -0,0 +1,98 @@ +// 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; +use std::sync::Mutex; + +/// 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, +} + +fn list_meta_ctxs( + nbd: &libnbd::Handle, + queries: &[&CStr], +) -> libnbd::Result<CtxInfo> { + let info = Arc::new(Mutex::new(CtxInfo { + has_alloc: false, + count: 0, + })); + let info_clone = info.clone(); + let replies = nbd.opt_list_meta_context_queries(queries, move |ctx| { + let mut info = info_clone.lock().unwrap(); + info.count += 1; + if ctx == libnbd::CONTEXT_BASE_ALLOCATION { + info.has_alloc = true; + } + 0 + })?; + let info = Arc::into_inner(info).unwrap().into_inner().unwrap(); + assert_eq!(info.count, replies); + Ok(info) +} + +#[test] +fn test_opt_list_meta_queries() { + 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!("memory"), + c_str!("size=1M"), + ]) + .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, &[]).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:")]).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] + ) + .unwrap(), + CtxInfo { + count: 1, + has_alloc: true + } + ); +} diff --git a/rust/tests/test_250_opt_set_meta.rs b/rust/tests/test_250_opt_set_meta.rs new file mode 100644 index 0000000..bd78950 --- /dev/null +++ b/rust/tests/test_250_opt_set_meta.rs @@ -0,0 +1,124 @@ +// 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; +use std::sync::Mutex; + +/// 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, +} + +fn set_meta_ctxs(nbd: &libnbd::Handle) -> libnbd::Result<CtxInfo> { + let info = Arc::new(Mutex::new(CtxInfo { + has_alloc: false, + count: 0, + })); + let info_clone = info.clone(); + let replies = nbd.opt_set_meta_context(move |ctx| { + let mut info = info_clone.lock().unwrap(); + info.count += 1; + if ctx == CONTEXT_BASE_ALLOCATION { + info.has_alloc = true; + } + 0 + })?; + let info = Arc::into_inner(info).unwrap().into_inner().unwrap(); + assert_eq!(info.count, replies); + Ok(info) +} + +#[test] +fn test_opt_set_meta() { + let nbd = libnbd::Handle::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"), + ]) + .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. + assert!(nbd.opt_structured_reply().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).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).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).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().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).is_err()); + assert_eq!(nbd.get_size().unwrap(), 1048576); + assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap()); +} diff --git a/rust/tests/test_255_opt_set_meta_queries.rs b/rust/tests/test_255_opt_set_meta_queries.rs new file mode 100644 index 0000000..bbe53a9 --- /dev/null +++ b/rust/tests/test_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; +use std::sync::Mutex; + +/// 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, +} + +fn set_meta_ctxs_queries( + nbd: &libnbd::Handle, + queries: &[&CStr], +) -> libnbd::Result<CtxInfo> { + let info = Arc::new(Mutex::new(CtxInfo { + has_alloc: false, + count: 0, + })); + let info_clone = info.clone(); + let replies = nbd.opt_set_meta_context_queries(queries, move |ctx| { + let mut info = info_clone.lock().unwrap(); + info.count += 1; + if ctx == CONTEXT_BASE_ALLOCATION { + info.has_alloc = true; + } + 0 + })?; + let info = Arc::into_inner(info).unwrap().into_inner().unwrap(); + assert_eq!(info.count, replies); + Ok(info) +} + +#[test] +fn test_opt_set_meta_queries() { + 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!("memory"), + c_str!("size=1M"), + ]) + .unwrap(); + + // nbdkit does not match wildcard for SET, even though it does for LIST + assert_eq!( + set_meta_ctxs_queries(&nbd, &[c_str!("base:")]).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, &[]).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] + ) + .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().unwrap(); + assert!(nbd.can_meta_context(CONTEXT_BASE_ALLOCATION).unwrap()); +} diff --git a/rust/tests/test_300_get_size.rs b/rust/tests/test_300_get_size.rs new file mode 100644 index 0000000..fbf1c79 --- /dev/null +++ b/rust/tests/test_300_get_size.rs @@ -0,0 +1,36 @@ +// 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; + +#[test] +fn test_get_size() { + let nbd = libnbd::Handle::new().unwrap(); + nbd.connect_command(&[ + c_str!("nbdkit"), + c_str!("-s"), + c_str!("--exit-with-parent"), + c_str!("-v"), + c_str!("null"), + c_str!("size=1M"), + ]) + .unwrap(); + + assert_eq!(nbd.get_size().unwrap(), 1048576); +} diff --git a/rust/tests/test_400_pread.rs b/rust/tests/test_400_pread.rs new file mode 100644 index 0000000..9414379 --- /dev/null +++ b/rust/tests/test_400_pread.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)] + +mod nbdkit_pattern; +use byte_strings::c_str; +use nbdkit_pattern::PATTERN; + +#[test] +fn test_pread() { + let nbd = libnbd::Handle::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"), + ]) + .unwrap(); + + let mut buf = [0; 512]; + nbd.pread(&mut buf, 0, None).unwrap(); + assert_eq!(buf.as_slice(), PATTERN.as_slice()); +} diff --git a/rust/tests/test_405_pread_structured.rs b/rust/tests/test_405_pread_structured.rs new file mode 100644 index 0000000..432d4d0 --- /dev/null +++ b/rust/tests/test_405_pread_structured.rs @@ -0,0 +1,80 @@ +// 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; + +#[test] +fn test_pread_structured() { + let nbd = libnbd::Handle::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"), + ]) + .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, + ) + .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), + ) + .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), + ); + assert_eq!(res.unwrap_err().errno(), Some(42)); +} diff --git a/rust/tests/test_410_pwrite.rs b/rust/tests/test_410_pwrite.rs new file mode 100644 index 0000000..612f65d --- /dev/null +++ b/rust/tests/test_410_pwrite.rs @@ -0,0 +1,62 @@ +// 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::*; + +#[test] +fn test_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::Handle::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(), + ]) + .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).unwrap(); + + let mut buf_2 = [0; 512]; + nbd.pread(&mut buf_2, 0, None).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_460_block_status.rs b/rust/tests/test_460_block_status.rs new file mode 100644 index 0000000..58a2e7e --- /dev/null +++ b/rust/tests/test_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; +use std::sync::Arc; +use std::sync::Mutex; + +fn block_status_get_entries( + nbd: &libnbd::Handle, + count: u64, + offset: u64, + flags: Option<libnbd::CmdFlag>, +) -> Vec<u32> { + let entries = Arc::new(Mutex::new(None)); + let entries_clone = entries.clone(); + nbd.block_status( + count, + offset, + move |metacontext, _, entries, err| { + assert_eq!(*err, 0); + if metacontext == libnbd::CONTEXT_BASE_ALLOCATION { + *entries_clone.lock().unwrap() = Some(entries.to_vec()); + } + 0 + }, + flags, + ) + .unwrap(); + Arc::into_inner(entries) + .unwrap() + .into_inner() + .unwrap() + .unwrap() +} + +#[test] +fn test_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::Handle::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, + ]) + .unwrap(); + + assert_eq!( + block_status_get_entries(&nbd, 65536, 0, None).as_slice(), + &[8192, 0, 8192, 1, 16384, 3, 16384, 2, 16384, 0,] + ); + + assert_eq!( + block_status_get_entries(&nbd, 1024, 32256, None).as_slice(), + &[512, 3, 16384, 2] + ); + + assert_eq!( + block_status_get_entries( + &nbd, + 1024, + 32256, + Some(libnbd::CmdFlag::REQ_ONE) + ) + .as_slice(), + &[512, 3] + ); +} diff --git a/rust/tests/test_620_stats.rs b/rust/tests/test_620_stats.rs new file mode 100644 index 0000000..0e37729 --- /dev/null +++ b/rust/tests/test_620_stats.rs @@ -0,0 +1,76 @@ +// 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; + +#[test] +fn test_stats() { + let nbd = libnbd::Handle::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"), + ]) + .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).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); +} diff --git a/rust/tests/test_log/mod.rs b/rust/tests/test_log/mod.rs new file mode 100644 index 0000000..8dbcd79 --- /dev/null +++ b/rust/tests/test_log/mod.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 + +//! This module provides facilities for capturing log output and asserting that +//! it does or does not contain certain messages. The primary use of this module +//! is to assert that certain libnbd operations are or are not performed. + +#![allow(unused)] + +use std::sync::Mutex; + +/// Logger that stores all debug messages in a list. +pub struct DebugLogger { + /// All targets and messages logged. Wrapped in a mutex so that it can be + /// updated with an imutable reference to self. + entries: Mutex<Vec<(String, String)>>, + is_initialized: Mutex<bool>, +} + +impl DebugLogger { + const fn new() -> Self { + Self { + entries: Mutex::new(Vec::new()), + is_initialized: Mutex::new(false), + } + } + + /// Set this logger as the global logger. + pub fn init(&'static self) { + let mut is_initialized = self.is_initialized.lock().unwrap(); + if !*is_initialized { + log::set_logger(self).unwrap(); + log::set_max_level(log::LevelFilter::Debug); + *is_initialized = true; + } + } + + /// Check wether a specific message has been logged. + pub fn contains(&self, msg: &str) -> bool { + self.entries.lock().unwrap().iter().any(|(_, x)| x == msg) + } + + /// Print all logged messages, in no particular order. + /// + /// Only for debug purposes. Remember to run cargo test with the `-- + /// --nocapture` arguments. That is, from the rust directory run: + /// `./../run cargo test -- --nocapture` + pub fn print_messages(&self) { + for (target, msg) in self.entries.lock().unwrap().iter() { + eprintln!("{target}: {msg}"); + } + } +} + +/// A static global `DebugLogger`. Just call `.init()` on this to set it as the +/// global logger. +pub static DEBUG_LOGGER: DebugLogger = DebugLogger::new(); + +impl log::Log for DebugLogger { + fn enabled(&self, metadata: &log::Metadata<'_>) -> bool { + metadata.level() == log::Level::Debug + } + + fn log(&self, record: &log::Record<'_>) { + self.entries + .lock() + .unwrap() + .push((record.target().to_string(), record.args().to_string())); + } + + fn flush(&self) {} +} -- 2.41.0
Richard W.M. Jones
2023-Jul-19 14:50 UTC
[Libguestfs] [libnbd PATCH 07/10] rust: Add a couple of integration tests
On Wed, Jul 19, 2023 at 09:09:51AM +0000, Tage Johansson wrote:> +fn test_connect_command() { > + let nbd = libnbd::Handle::new().unwrap(); > + nbd.connect_command(&[ > + c_str!("nbdkit"), > + c_str!("-s"), > + c_str!("--exit-with-parent"), > + c_str!("-v"), > + c_str!("null"), > + ]) > + .unwrap();So this doesn't seem very natural Rust to me. For example standard library exec::execvp lets you just use: let err = exec::execvp("echo", &["echo", "foo"]); println!("Error: {}", err); (https://docs.rs/exec/latest/exec/fn.execvp.html) Is there a way to get rid of the c_str macro here? Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 100 libraries supported. http://fedoraproject.org/wiki/MinGW
Eric Blake
2023-Aug-29 21:59 UTC
[Libguestfs] [libnbd PATCH 07/10] rust: Add a couple of integration tests
On Wed, Jul 19, 2023 at 09:09:51AM +0000, Tage Johansson wrote:> A couple of integration tests are added in rust/tests. They are mostly > ported from the OCaml tests. > --- > rust/tests/test_210_opt_abort.rs | 39 +++++ > rust/tests/test_220_opt_list.rs | 85 +++++++++++I noticed something today while working on issues with nbd_opt_abort vs. nbd_shutdown:> diff --git a/rust/tests/test_220_opt_list.rs b/rust/tests/test_220_opt_list.rs > new file mode 100644 > index 0000000..5abec5f > --- /dev/null > +++ b/rust/tests/test_220_opt_list.rs > @@ -0,0 +1,85 @@> + > +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 } > + } > + > + fn connect(This function is modeled after the 'let conn' function in test_220_opt_list.ml; however,...> + &self, > + mode: u8, > + expected_exports: &[&CStr], > + ) -> libnbd::Result<()> { > + 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"), > + &self.script_path, > + &CString::new(format!("mode={mode}")).unwrap(), > + ]) > + .unwrap(); > + > + // Collect all exports in this list. > + let exports = Arc::new(Mutex::new(Vec::new())); > + let exports_clone = exports.clone(); > + let count = nbd.opt_list(move |name, _| { > + exports_clone.lock().unwrap().push(name.to_owned()); > + 0 > + })?; > + let exports = Arc::into_inner(exports).unwrap().into_inner().unwrap(); > + assert_eq!(exports.len(), count as usize); > + assert_eq!(exports.len(), expected_exports.len()); > + for (export, &expected) in exports.iter().zip(expected_exports) { > + assert_eq!(export.as_c_str(), expected); > + } > + Ok(())...the OCaml version calls 'NBD.opt_abort nbd' after verifying that exports match the expected list, while the Rust code does not. You can see the same thing in the Python tests (calling opt_abort before closing the handle). This means that for the Rust tests, the server is more likely to issue an error message about the client abruptly disconnecting, which doesn't really affect the test passing or failing, but adds spurious noise to the log files if we are ever trying to decipher whether two language bindings are doing the same thing. Also affected: 240, 245, and their async counterpart tests. -- Eric Blake, Principal Software Engineer Red Hat, Inc. Virtualization: qemu.org | libguestfs.org