summary refs log tree commit diff stats
path: root/rust/qemu-api/tests
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2024-12-21 08:06:50 -0500
committerStefan Hajnoczi <stefanha@redhat.com>2024-12-21 08:06:50 -0500
commite3a207722b783675b362db4ae22a449f42a26b24 (patch)
tree02593a73b81cc459bd7e645e688c1296b47cb741 /rust/qemu-api/tests
parent9863d46a5a25bfff7d2195ad5e3127ab3bae0a2b (diff)
parentbf9987c06eb8274c2503174b944b8fbe94cc24d7 (diff)
downloadfocaccia-qemu-e3a207722b783675b362db4ae22a449f42a26b24.tar.gz
focaccia-qemu-e3a207722b783675b362db4ae22a449f42a26b24.zip
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* qdev: second part of Property cleanups
* rust: second part of QOM rework
* rust: callbacks wrapper
* rust: pl011 bugfixes
* kvm: cleanup errors in kvm_convert_memory()

 # -----BEGIN PGP SIGNATURE-----
 #
 # iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmdkaEkUHHBib256aW5p
 # QHJlZGhhdC5jb20ACgkQv/vSX3jHroN0/wgAgIJg8BrlRKfmiz14NZfph8/jarSj
 # TOWYVxL2v4q98KBuL5pta2ucObgzwqyqSyc02S2DGSOIMQCIiBB5MaCk1iMjx+BO
 # pmVU8gNlD8faO8SSmnnr+jDQt+G+bQ/nRgQJOAReF8oVw3O2aC/FaVKpitMzWtvv
 # PLnJWdrqqpGq14OzX8iNCzSujxppAuyjrhT4lNlekzDoDfdTez72r+rXkvg4GzZL
 # QC3xLYg/LrT8Rs+zgOhm/AaIyS4bOyMlkU9Du1rQ6Tyne45ey2FCwKVzBKrJdGcw
 # sVbzEclxseLenoTbZqYK6JTzLdDoThVUbY2JwoCGUaIm+74P4NjEsUsTVg==
 # =TuQM
 # -----END PGP SIGNATURE-----
 # gpg: Signature made Thu 19 Dec 2024 13:39:05 EST
 # gpg:                using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
 # gpg:                issuer "pbonzini@redhat.com"
 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full]
 # gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>" [full]
 # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4  E2F7 7E15 100C CD36 69B1
 #      Subkey fingerprint: F133 3857 4B66 2389 866C  7682 BFFB D25F 78C7 AE83

* tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (42 commits)
  rust: pl011: simplify handling of the FIFO enabled bit in LCR
  rust: pl011: fix migration stream
  rust: pl011: extend registers to 32 bits
  rust: pl011: fix break errors and definition of Data struct
  rust: pl011: always use reset() method on registers
  rust: pl011: match break logic of C version
  rust: pl011: fix declaration of LineControl bits
  target/i386: Reset TSCs of parked vCPUs too on VM reset
  kvm: consistently return 0/-errno from kvm_convert_memory
  rust: qemu-api: add a module to wrap functions and zero-sized closures
  rust: qom: add initial subset of methods on Object
  rust: qom: add casting functionality
  rust: tests: allow writing more than one test
  bql: add a "mock" BQL for Rust unit tests
  rust: re-export C types from qemu-api submodules
  rust: rename qemu-api modules to follow C code a bit more
  rust: qom: add possibility of overriding unparent
  rust: qom: put class_init together from multiple ClassInitImpl<>
  Constify all opaque Property pointers
  hw/core/qdev-properties: Constify Property argument to PropertyInfo.print
  ...

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'rust/qemu-api/tests')
-rw-r--r--rust/qemu-api/tests/tests.rs185
1 files changed, 138 insertions, 47 deletions
diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs
index 278efe967f..1d2825b098 100644
--- a/rust/qemu-api/tests/tests.rs
+++ b/rust/qemu-api/tests/tests.rs
@@ -2,69 +2,160 @@
 // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
 // SPDX-License-Identifier: GPL-2.0-or-later
 
-use std::ffi::CStr;
+use std::{
+    ffi::CStr,
+    os::raw::c_void,
+    ptr::{addr_of, addr_of_mut},
+};
 
 use qemu_api::{
-    bindings::*, c_str, declare_properties, define_property, definitions::ObjectImpl,
-    device_class::DeviceImpl, impl_device_class, prelude::*, zeroable::Zeroable,
+    bindings::*,
+    c_str,
+    cell::{self, BqlCell},
+    declare_properties, define_property,
+    prelude::*,
+    qdev::{DeviceImpl, DeviceState, Property},
+    qom::ObjectImpl,
+    vmstate::VMStateDescription,
+    zeroable::Zeroable,
 };
 
-#[test]
-fn test_device_decl_macros() {
-    // Test that macros can compile.
-    pub static VMSTATE: VMStateDescription = VMStateDescription {
-        name: c_str!("name").as_ptr(),
-        unmigratable: true,
-        ..Zeroable::ZERO
-    };
-
-    #[derive(qemu_api_macros::offsets)]
-    #[repr(C)]
-    #[derive(qemu_api_macros::Object)]
-    pub struct DummyState {
-        pub _parent: DeviceState,
-        pub migrate_clock: bool,
-    }
+// Test that macros can compile.
+pub static VMSTATE: VMStateDescription = VMStateDescription {
+    name: c_str!("name").as_ptr(),
+    unmigratable: true,
+    ..Zeroable::ZERO
+};
+
+#[derive(qemu_api_macros::offsets)]
+#[repr(C)]
+#[derive(qemu_api_macros::Object)]
+pub struct DummyState {
+    parent: DeviceState,
+    migrate_clock: bool,
+}
+
+qom_isa!(DummyState: Object, DeviceState);
+
+declare_properties! {
+    DUMMY_PROPERTIES,
+        define_property!(
+            c_str!("migrate-clk"),
+            DummyState,
+            migrate_clock,
+            unsafe { &qdev_prop_bool },
+            bool
+        ),
+}
+
+unsafe impl ObjectType for DummyState {
+    type Class = <DeviceState as ObjectType>::Class;
+    const TYPE_NAME: &'static CStr = c_str!("dummy");
+}
 
-    #[repr(C)]
-    pub struct DummyClass {
-        pub _parent: DeviceClass,
+impl ObjectImpl for DummyState {
+    type ParentType = DeviceState;
+    const ABSTRACT: bool = false;
+}
+
+impl DeviceImpl for DummyState {
+    fn properties() -> &'static [Property] {
+        &DUMMY_PROPERTIES
+    }
+    fn vmsd() -> Option<&'static VMStateDescription> {
+        Some(&VMSTATE)
     }
+}
+
+fn init_qom() {
+    static ONCE: BqlCell<bool> = BqlCell::new(false);
 
-    declare_properties! {
-        DUMMY_PROPERTIES,
-            define_property!(
-                c_str!("migrate-clk"),
-                DummyState,
-                migrate_clock,
-                unsafe { &qdev_prop_bool },
-                bool
-            ),
+    cell::bql_start_test();
+    if !ONCE.get() {
+        unsafe {
+            module_call_init(module_init_type::MODULE_INIT_QOM);
+        }
+        ONCE.set(true);
     }
+}
 
-    unsafe impl ObjectType for DummyState {
-        type Class = DummyClass;
-        const TYPE_NAME: &'static CStr = c_str!("dummy");
+#[test]
+/// Create and immediately drop an instance.
+fn test_object_new() {
+    init_qom();
+    unsafe {
+        object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast());
     }
+}
 
-    impl ObjectImpl for DummyState {
-        type ParentType = DeviceState;
-        const ABSTRACT: bool = false;
+#[test]
+/// Try invoking a method on an object.
+fn test_typename() {
+    init_qom();
+    let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() };
+    let p_ref: &DummyState = unsafe { &*p };
+    assert_eq!(p_ref.typename(), "dummy");
+    unsafe {
+        object_unref(p_ref.as_object_mut_ptr().cast::<c_void>());
     }
+}
 
-    impl DeviceImpl for DummyState {
-        fn properties() -> &'static [Property] {
-            &DUMMY_PROPERTIES
-        }
-        fn vmsd() -> Option<&'static VMStateDescription> {
-            Some(&VMSTATE)
-        }
+// a note on all "cast" tests: usually, especially for downcasts the desired
+// class would be placed on the right, for example:
+//
+//    let sbd_ref = p.dynamic_cast::<SysBusDevice>();
+//
+// Here I am doing the opposite to check that the resulting type is correct.
+
+#[test]
+#[allow(clippy::shadow_unrelated)]
+/// Test casts on shared references.
+fn test_cast() {
+    init_qom();
+    let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() };
+
+    let p_ref: &DummyState = unsafe { &*p };
+    let obj_ref: &Object = p_ref.upcast();
+    assert_eq!(addr_of!(*obj_ref), p.cast());
+
+    let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast();
+    assert!(sbd_ref.is_none());
+
+    let dev_ref: Option<&DeviceState> = obj_ref.downcast();
+    assert_eq!(addr_of!(*dev_ref.unwrap()), p.cast());
+
+    // SAFETY: the cast is wrong, but the value is only used for comparison
+    unsafe {
+        let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast();
+        assert_eq!(addr_of!(*sbd_ref), p.cast());
+
+        object_unref(p_ref.as_object_mut_ptr().cast::<c_void>());
     }
+}
+
+#[test]
+#[allow(clippy::shadow_unrelated)]
+/// Test casts on mutable references.
+fn test_cast_mut() {
+    init_qom();
+    let p: *mut DummyState = unsafe { object_new(DummyState::TYPE_NAME.as_ptr()).cast() };
+
+    let p_ref: &mut DummyState = unsafe { &mut *p };
+    let obj_ref: &mut Object = p_ref.upcast_mut();
+    assert_eq!(addr_of_mut!(*obj_ref), p.cast());
+
+    let sbd_ref: Result<&mut SysBusDevice, &mut Object> = obj_ref.dynamic_cast_mut();
+    let obj_ref = sbd_ref.unwrap_err();
 
-    impl_device_class!(DummyState);
+    let dev_ref: Result<&mut DeviceState, &mut Object> = obj_ref.downcast_mut();
+    let dev_ref = dev_ref.unwrap();
+    assert_eq!(addr_of_mut!(*dev_ref), p.cast());
 
+    // SAFETY: the cast is wrong, but the value is only used for comparison
     unsafe {
-        module_call_init(module_init_type::MODULE_INIT_QOM);
-        object_unref(object_new(DummyState::TYPE_NAME.as_ptr()).cast());
+        let sbd_ref: &mut SysBusDevice = obj_ref.unsafe_cast_mut();
+        assert_eq!(addr_of_mut!(*sbd_ref), p.cast());
+
+        object_unref(p_ref.as_object_mut_ptr().cast::<c_void>());
     }
 }