Tage Johansson
2023-Aug-03 15:36 UTC
[Libguestfs] [libnbd PATCH v5 02/12] 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 | 22 +++ rust/run-tests.sh.in | 4 +- 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 | 53 +++++++ rust/tests/test_130_private_data.rs | 28 ++++ rust/tests/test_140_explicit_close.rs | 31 ++++ rust/tests/test_200_connect_command.rs | 32 ++++ rust/tests/test_210_opt_abort.rs | 31 ++++ rust/tests/test_220_opt_list.rs | 86 +++++++++++ rust/tests/test_230_opt_info.rs | 120 +++++++++++++++ rust/tests/test_240_opt_list_meta.rs | 147 +++++++++++++++++++ rust/tests/test_245_opt_list_meta_queries.rs | 93 ++++++++++++ rust/tests/test_250_opt_set_meta.rs | 123 ++++++++++++++++ rust/tests/test_255_opt_set_meta_queries.rs | 109 ++++++++++++++ rust/tests/test_300_get_size.rs | 35 +++++ rust/tests/test_400_pread.rs | 39 +++++ rust/tests/test_405_pread_structured.rs | 79 ++++++++++ rust/tests/test_410_pwrite.rs | 58 ++++++++ rust/tests/test_460_block_status.rs | 92 ++++++++++++ rust/tests/test_620_stats.rs | 75 ++++++++++ rust/tests/test_log/mod.rs | 86 +++++++++++ 24 files changed, 1432 insertions(+), 1 deletion(-) 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 c745972..b498930 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -47,3 +47,7 @@ libc = "0.2.147" [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 8615794..db8fc66 100644 --- a/rust/Makefile.am +++ b/rust/Makefile.am @@ -36,6 +36,27 @@ source_files = \ cargo_test/Cargo.toml \ cargo_test/src/lib.rs \ cargo_test/README.md \ + tests/nbdkit_pattern/mod.rs \ + tests/test_100_handle.rs \ + tests/test_110_defaults.rs \ + tests/test_120_set_non_defaults.rs \ + tests/test_130_private_data.rs \ + tests/test_140_explicit_close.rs \ + tests/test_200_connect_command.rs \ + tests/test_210_opt_abort.rs \ + tests/test_220_opt_list.rs \ + tests/test_230_opt_info.rs \ + tests/test_240_opt_list_meta.rs \ + tests/test_245_opt_list_meta_queries.rs \ + tests/test_250_opt_set_meta.rs \ + tests/test_255_opt_set_meta_queries.rs \ + tests/test_300_get_size.rs \ + tests/test_400_pread.rs \ + tests/test_405_pread_structured.rs \ + tests/test_410_pwrite.rs \ + tests/test_460_block_status.rs \ + tests/test_620_stats.rs \ + tests/test_log/mod.rs \ run-tests.sh.in \ $(NULL) @@ -63,6 +84,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.in b/rust/run-tests.sh.in index f4d220c..d45b1bf 100755 --- a/rust/run-tests.sh.in +++ b/rust/run-tests.sh.in @@ -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 +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..ac1e29c --- /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().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..7f1edab --- /dev/null +++ b/rust/tests/test_120_set_non_defaults.rs @@ -0,0 +1,53 @@ +// 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_set_non_defaults() { + let nbd = libnbd::Handle::new().unwrap(); + + nbd.set_export_name("name").unwrap(); + assert_eq!(nbd.get_export_name().unwrap(), b"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..8338650 --- /dev/null +++ b/rust/tests/test_200_connect_command.rs @@ -0,0 +1,32 @@ +// 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_connect_command() { + let nbd = libnbd::Handle::new().unwrap(); + nbd.connect_command(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "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..c59805f --- /dev/null +++ b/rust/tests/test_210_opt_abort.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)] + +#[test] +fn test_opt_abort() { + let nbd = libnbd::Handle::new().unwrap(); + nbd.set_opt_mode(true).unwrap(); + nbd.connect_command(&["nbdkit", "-s", "--exit-with-parent", "-v", "null"]) + .unwrap(); + assert_eq!(nbd.get_protocol().unwrap(), b"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..180a95b --- /dev/null +++ b/rust/tests/test_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 std::env; +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: String, +} + +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 + String::from_utf8(script_path.into_os_string().into_vec()).unwrap(); + Self { script_path } + } + + fn connect( + &self, + mode: u8, + expected_exports: &[&str], + ) -> libnbd::Result<()> { + let nbd = libnbd::Handle::new().unwrap(); + nbd.set_opt_mode(true).unwrap(); + nbd.connect_command(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "sh", + &self.script_path, + &format!("mode={mode}"), + ]) + .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(String::from_utf8(name.to_owned()).unwrap()); + 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, expected); + } + Ok(()) + } +} + +#[test] +fn test_opt_list() { + let conn_tester = ConnTester::new(); + assert!(conn_tester.connect(0, &[]).is_err()); + assert!(conn_tester.connect(1, &["a", "b"]).is_ok()); + assert!(conn_tester.connect(2, &[]).is_ok()); + assert!(conn_tester.connect(3, &["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..00e5fb0 --- /dev/null +++ b/rust/tests/test_230_opt_info.rs @@ -0,0 +1,120 @@ +// 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 libnbd::CONTEXT_BASE_ALLOCATION; +use std::env; +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 = script_path.to_str().unwrap(); + + let nbd = libnbd::Handle::new().unwrap(); + nbd.set_opt_mode(true).unwrap(); + nbd.connect_command(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "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("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("a").unwrap(); + assert!(nbd.opt_info().is_err()); + + // info for a different export, with automatic meta_context disabled + nbd.set_export_name("b").unwrap(); + nbd.set_request_meta_context(false).unwrap(); + nbd.opt_info().unwrap(); + // idempotent name change is no-op + nbd.set_export_name("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("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("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("a").is_err()); + assert_eq!(nbd.get_export_name().unwrap(), b"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(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "sh", + &script_path, + ]) + .unwrap(); + nbd.add_meta_context("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..5598458 --- /dev/null +++ b/rust/tests/test_240_opt_list_meta.rs @@ -0,0 +1,147 @@ +// 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::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(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "memory", + "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("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("base:allocation").unwrap(); + assert_eq!(nbd.get_nr_meta_contexts().unwrap(), 2); + assert_eq!(nbd.get_meta_context(1).unwrap(), b"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("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("base:allocation").unwrap()); + nbd.clear_meta_contexts().unwrap(); + nbd.add_meta_context("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("base:allocation").unwrap()); + + // Final pass: "base:" query should get at least "base:allocation" + nbd.add_meta_context("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(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "memory", + "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..da5c674 --- /dev/null +++ b/rust/tests/test_245_opt_list_meta_queries.rs @@ -0,0 +1,93 @@ +// 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::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: &[&[u8]], +) -> 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(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "memory", + "size=1M", + ]) + .unwrap(); + + // First pass: empty query should give at least "base:allocation". + nbd.add_meta_context("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, &[b"x-nosuch:"]).unwrap(), + CtxInfo { + count: 0, + has_alloc: false + } + ); + + // Third pass: specific query should have one match. + assert_eq!( + list_meta_ctxs(&nbd, &[b"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..c7a8144 --- /dev/null +++ b/rust/tests/test_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 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(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "memory", + "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("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("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("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..143a2f1 --- /dev/null +++ b/rust/tests/test_255_opt_set_meta_queries.rs @@ -0,0 +1,109 @@ +// 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 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_queries( + nbd: &libnbd::Handle, + queries: &[impl AsRef<[u8]>], +) -> 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(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "memory", + "size=1M", + ]) + .unwrap(); + + // nbdkit does not match wildcard for SET, even though it does for LIST + assert_eq!( + set_meta_ctxs_queries(&nbd, &["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, &[] as &[&str]).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, + &[b"x-nosuch:context".as_slice(), 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..c830164 --- /dev/null +++ b/rust/tests/test_300_get_size.rs @@ -0,0 +1,35 @@ +// 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_get_size() { + let nbd = libnbd::Handle::new().unwrap(); + nbd.connect_command(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "null", + "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..93b826c --- /dev/null +++ b/rust/tests/test_400_pread.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)] + +mod nbdkit_pattern; +use nbdkit_pattern::PATTERN; + +#[test] +fn test_pread() { + let nbd = libnbd::Handle::new().unwrap(); + nbd.connect_command(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "pattern", + "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..39a7fd5 --- /dev/null +++ b/rust/tests/test_405_pread_structured.rs @@ -0,0 +1,79 @@ +// 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 nbdkit_pattern::PATTERN; + +#[test] +fn test_pread_structured() { + let nbd = libnbd::Handle::new().unwrap(); + nbd.connect_command(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "pattern", + "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..d4d6b99 --- /dev/null +++ b/rust/tests/test_410_pwrite.rs @@ -0,0 +1,58 @@ +// 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::fs::{self, File}; + +#[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(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "file", + data_file_path.to_str().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..7cdcb34 --- /dev/null +++ b/rust/tests/test_460_block_status.rs @@ -0,0 +1,92 @@ +// 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::env; +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 = script_path.to_str().unwrap(); + let nbd = libnbd::Handle::new().unwrap(); + nbd.add_meta_context(libnbd::CONTEXT_BASE_ALLOCATION) + .unwrap(); + nbd.connect_command(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "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..134d59a --- /dev/null +++ b/rust/tests/test_620_stats.rs @@ -0,0 +1,75 @@ +// 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_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(&[ + "nbdkit", + "-s", + "--exit-with-parent", + "-v", + "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
Eric Blake
2023-Aug-16 21:35 UTC
[Libguestfs] [libnbd PATCH v5 02/12] rust: Add a couple of integration tests
On Thu, Aug 03, 2023 at 03:36:06PM +0000, Tage Johansson wrote:> A couple of integration tests are added in rust/tests. They are mostly > ported from the OCaml tests. > ---Overall, this looked like a nice counterpart to the OCaml unit tests, and I was able to easily figure out how to amend my own tests in for the unit tests I added in my 64-bit extension work. Keeping similar unit test numbers across language bindings has been a big boon :-) Style question:> +++ b/rust/tests/test_250_opt_set_meta.rs> + > +/// 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, > +}Here, you use a trailing comma,> + > +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) > +} > +...> + > + // nbdkit does not match wildcard for SET, even though it does for LIST > + nbd.clear_meta_contexts().unwrap(); > + nbd.add_meta_context("base:").unwrap(); > + assert_eq!( > + set_meta_ctxs(&nbd).unwrap(), > + CtxInfo { > + count: 0, > + has_alloc: false > + }whereas here, you did not. Does it make a difference? 'make check' still passes, and rustfmt doesn't complain, if I temporarily add a trailing comma here. I also note that 'rustfmt --check rust/tests/*.rs' flags a few style suggestions. I had started work on automating gofmt for all checked-in *.go files; maybe I should revive that patch and extend it to also automate rustfmt on all checked-in *.rs files. Here's what rustfmt suggested to me (rustfmt 1.5.2-stable ( ) on Fedora 38): Diff in /home/eblake/libnbd/rust/tests/test_200_connect_command.rs at line 17: #![deny(warnings)] - #[test] fn test_connect_command() { let nbd = libnbd::Handle::new().unwrap(); Diff in /home/eblake/libnbd/rust/tests/test_200_connect_command.rs at line 24: - nbd.connect_command(&[ - "nbdkit", - "-s", - "--exit-with-parent", - "-v", - "null", - ]) - .unwrap(); + nbd.connect_command(&["nbdkit", "-s", "--exit-with-parent", "-v", "null"]) + .unwrap(); } Diff in /home/eblake/libnbd/rust/tests/test_300_get_size.rs at line 17: #![deny(warnings)] - #[test] fn test_get_size() { let nbd = libnbd::Handle::new().unwrap(); Diff in /home/eblake/libnbd/rust/tests/test_620_stats.rs at line 17: #![deny(warnings)] - #[test] fn test_stats() { let nbd = libnbd::Handle::new().unwrap(); Diff in /home/eblake/libnbd/rust/tests/test_620_stats.rs at line 31: // 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(&[ - "nbdkit", - "-s", - "--exit-with-parent", - "-v", - "null", - ]) - .unwrap(); + nbd.connect_command(&["nbdkit", "-s", "--exit-with-parent", "-v", "null"]) + .unwrap(); let bs1 = nbd.stats_bytes_sent(); let cs1 = nbd.stats_chunks_sent(); -- Eric Blake, Principal Software Engineer Red Hat, Inc. Virtualization: qemu.org | libguestfs.org