tests.rs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // Copyright 2024, Linaro Limited
  2. // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
  3. // SPDX-License-Identifier: GPL-2.0-or-later
  4. use std::{ffi::CStr, ptr::addr_of};
  5. use qemu_api::{
  6. bindings::{module_call_init, module_init_type, qdev_prop_bool},
  7. c_str,
  8. cell::{self, BqlCell},
  9. declare_properties, define_property,
  10. prelude::*,
  11. qdev::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl},
  12. qom::{ObjectImpl, ParentField},
  13. sysbus::SysBusDevice,
  14. vmstate::VMStateDescription,
  15. zeroable::Zeroable,
  16. };
  17. mod vmstate_tests;
  18. // Test that macros can compile.
  19. pub static VMSTATE: VMStateDescription = VMStateDescription {
  20. name: c_str!("name").as_ptr(),
  21. unmigratable: true,
  22. ..Zeroable::ZERO
  23. };
  24. #[derive(qemu_api_macros::offsets)]
  25. #[repr(C)]
  26. #[derive(qemu_api_macros::Object)]
  27. pub struct DummyState {
  28. parent: ParentField<DeviceState>,
  29. migrate_clock: bool,
  30. }
  31. qom_isa!(DummyState: Object, DeviceState);
  32. pub struct DummyClass {
  33. parent_class: <DeviceState as ObjectType>::Class,
  34. }
  35. impl DummyClass {
  36. pub fn class_init<T: DeviceImpl>(self: &mut DummyClass) {
  37. self.parent_class.class_init::<T>();
  38. }
  39. }
  40. declare_properties! {
  41. DUMMY_PROPERTIES,
  42. define_property!(
  43. c_str!("migrate-clk"),
  44. DummyState,
  45. migrate_clock,
  46. unsafe { &qdev_prop_bool },
  47. bool
  48. ),
  49. }
  50. unsafe impl ObjectType for DummyState {
  51. type Class = DummyClass;
  52. const TYPE_NAME: &'static CStr = c_str!("dummy");
  53. }
  54. impl ObjectImpl for DummyState {
  55. type ParentType = DeviceState;
  56. const ABSTRACT: bool = false;
  57. const CLASS_INIT: fn(&mut DummyClass) = DummyClass::class_init::<Self>;
  58. }
  59. impl ResettablePhasesImpl for DummyState {}
  60. impl DeviceImpl for DummyState {
  61. fn properties() -> &'static [Property] {
  62. &DUMMY_PROPERTIES
  63. }
  64. fn vmsd() -> Option<&'static VMStateDescription> {
  65. Some(&VMSTATE)
  66. }
  67. }
  68. #[derive(qemu_api_macros::offsets)]
  69. #[repr(C)]
  70. #[derive(qemu_api_macros::Object)]
  71. pub struct DummyChildState {
  72. parent: ParentField<DummyState>,
  73. }
  74. qom_isa!(DummyChildState: Object, DeviceState, DummyState);
  75. pub struct DummyChildClass {
  76. parent_class: <DummyState as ObjectType>::Class,
  77. }
  78. unsafe impl ObjectType for DummyChildState {
  79. type Class = DummyChildClass;
  80. const TYPE_NAME: &'static CStr = c_str!("dummy_child");
  81. }
  82. impl ObjectImpl for DummyChildState {
  83. type ParentType = DummyState;
  84. const ABSTRACT: bool = false;
  85. const CLASS_INIT: fn(&mut DummyChildClass) = DummyChildClass::class_init::<Self>;
  86. }
  87. impl ResettablePhasesImpl for DummyChildState {}
  88. impl DeviceImpl for DummyChildState {}
  89. impl DummyChildClass {
  90. pub fn class_init<T: DeviceImpl>(self: &mut DummyChildClass) {
  91. self.parent_class.class_init::<T>();
  92. }
  93. }
  94. fn init_qom() {
  95. static ONCE: BqlCell<bool> = BqlCell::new(false);
  96. cell::bql_start_test();
  97. if !ONCE.get() {
  98. unsafe {
  99. module_call_init(module_init_type::MODULE_INIT_QOM);
  100. }
  101. ONCE.set(true);
  102. }
  103. }
  104. #[test]
  105. /// Create and immediately drop an instance.
  106. fn test_object_new() {
  107. init_qom();
  108. drop(DummyState::new());
  109. drop(DummyChildState::new());
  110. }
  111. #[test]
  112. #[allow(clippy::redundant_clone)]
  113. /// Create, clone and then drop an instance.
  114. fn test_clone() {
  115. init_qom();
  116. let p = DummyState::new();
  117. assert_eq!(p.clone().typename(), "dummy");
  118. drop(p);
  119. }
  120. #[test]
  121. /// Try invoking a method on an object.
  122. fn test_typename() {
  123. init_qom();
  124. let p = DummyState::new();
  125. assert_eq!(p.typename(), "dummy");
  126. }
  127. // a note on all "cast" tests: usually, especially for downcasts the desired
  128. // class would be placed on the right, for example:
  129. //
  130. // let sbd_ref = p.dynamic_cast::<SysBusDevice>();
  131. //
  132. // Here I am doing the opposite to check that the resulting type is correct.
  133. #[test]
  134. #[allow(clippy::shadow_unrelated)]
  135. /// Test casts on shared references.
  136. fn test_cast() {
  137. init_qom();
  138. let p = DummyState::new();
  139. let p_ptr: *mut DummyState = p.as_mut_ptr();
  140. let p_ref: &mut DummyState = unsafe { &mut *p_ptr };
  141. let obj_ref: &Object = p_ref.upcast();
  142. assert_eq!(addr_of!(*obj_ref), p_ptr.cast());
  143. let sbd_ref: Option<&SysBusDevice> = obj_ref.dynamic_cast();
  144. assert!(sbd_ref.is_none());
  145. let dev_ref: Option<&DeviceState> = obj_ref.downcast();
  146. assert_eq!(addr_of!(*dev_ref.unwrap()), p_ptr.cast());
  147. // SAFETY: the cast is wrong, but the value is only used for comparison
  148. unsafe {
  149. let sbd_ref: &SysBusDevice = obj_ref.unsafe_cast();
  150. assert_eq!(addr_of!(*sbd_ref), p_ptr.cast());
  151. }
  152. }