diff --git a/crates/cxx-qt-gen/src/generator/cpp/property/signal.rs b/crates/cxx-qt-gen/src/generator/cpp/property/signal.rs index 35c5b3a77..5bb265e0b 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/property/signal.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/property/signal.rs @@ -25,7 +25,7 @@ pub fn generate(idents: &QPropertyNames, qobject_name: &Name) -> Option); }; - Some(ParsedSignal::parse(method, CaseConversion::none()).unwrap()) + Some(ParsedSignal::parse_rust_qt_signal(method, CaseConversion::none()).unwrap()) } else { None } diff --git a/crates/cxx-qt-gen/src/generator/cpp/signal.rs b/crates/cxx-qt-gen/src/generator/cpp/signal.rs index 1dc4808da..7a6d15b53 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/signal.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/signal.rs @@ -51,6 +51,7 @@ fn parameter_types_and_values( parameters: &[ParsedFunctionParameter], type_names: &TypeNames, self_ty: &Name, + mutable: bool, ) -> Result { let mut parameter_named_types_with_self = vec![]; let mut parameter_types_with_self = vec![]; @@ -66,10 +67,11 @@ fn parameter_types_and_values( let parameter_named_types = parameter_named_types_with_self.join(", "); + let is_const = if mutable { "" } else { " const" }; // Insert the extra argument into the closure let self_ty = self_ty.cxx_qualified(); - parameter_named_types_with_self.insert(0, format!("{self_ty}& self")); - parameter_types_with_self.insert(0, format!("{self_ty}&")); + parameter_named_types_with_self.insert(0, format!("{self_ty}{is_const}& self")); + parameter_types_with_self.insert(0, format!("{self_ty}{is_const}&")); parameter_values_with_self.insert(0, "self".to_owned()); Ok(Parameters { @@ -109,7 +111,8 @@ pub fn generate_cpp_signal( let free_connect_ident_cpp = idents_helper.connect_name.cxx_unqualified(); // Retrieve the parameters for the signal - let parameters = parameter_types_and_values(&signal.parameters, type_names, qobject_name)?; + let parameters = + parameter_types_and_values(&signal.parameters, type_names, qobject_name, signal.mutable)?; let parameters_named_types = parameters.named_types; let parameters_named_types_with_self = parameters.named_types_with_self; let parameter_types_with_self = parameters.types_with_self; @@ -121,6 +124,8 @@ pub fn generate_cpp_signal( let signal_handler_call = idents_helper.function_call; let signal_handler_drop = idents_helper.function_drop; let namespace = idents_helper.namespace; + let reference_type = if signal.mutable { "&" } else { " const &" }; + let is_const = if signal.mutable { "" } else { " const" }; let signal_handler_type = format!("SignalHandler<::{namespace}::{param_struct} *>"); @@ -135,7 +140,7 @@ pub fn generate_cpp_signal( // Generate the Q_SIGNAL if this is not an existing signal if !signal.inherit { generated.methods.push(CppFragment::Header(format!( - "Q_SIGNAL void {signal_ident}({parameters_named_types});" + "Q_SIGNAL void {signal_ident}({parameters_named_types}){is_const};" ))); } @@ -144,7 +149,7 @@ pub fn generate_cpp_signal( r#" namespace {namespace} {{ ::QMetaObject::Connection - {free_connect_ident_cpp}({qobject_ident_namespaced}& self, {signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type); + {free_connect_ident_cpp}({qobject_ident_namespaced}{reference_type} self, {signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type); }} // namespace {namespace} "# }, @@ -177,7 +182,7 @@ pub fn generate_cpp_signal( namespace {namespace} {{ ::QMetaObject::Connection - {free_connect_ident_cpp}({qobject_ident_namespaced}& self, {signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type) + {free_connect_ident_cpp}({qobject_ident_namespaced}{reference_type} self, {signal_handler_alias_namespaced} closure, ::Qt::ConnectionType type) {{ return ::QObject::connect( &self, diff --git a/crates/cxx-qt-gen/src/generator/rust/property/signal.rs b/crates/cxx-qt-gen/src/generator/rust/property/signal.rs index 76f2381e5..bf57c6819 100644 --- a/crates/cxx-qt-gen/src/generator/rust/property/signal.rs +++ b/crates/cxx-qt-gen/src/generator/rust/property/signal.rs @@ -28,7 +28,7 @@ pub fn generate(idents: &QPropertyNames, qobject_names: &QObjectNames) -> Option fn #notify_rust(self: Pin<&mut #cpp_class_rust>); }; - Some(ParsedSignal::parse(method, CaseConversion::none()).unwrap()) + Some(ParsedSignal::parse_rust_qt_signal(method, CaseConversion::none()).unwrap()) } else { None } diff --git a/crates/cxx-qt-gen/src/generator/rust/signals.rs b/crates/cxx-qt-gen/src/generator/rust/signals.rs index 497e6a955..6b9050d94 100644 --- a/crates/cxx-qt-gen/src/generator/rust/signals.rs +++ b/crates/cxx-qt-gen/src/generator/rust/signals.rs @@ -85,9 +85,7 @@ pub fn generate_rust_signal( let self_type_cxx = if signal.mutable { parse_quote_spanned! {span => Pin<&mut #qobject_name_rust> } } else { - // CODECOV_EXCLUDE_START - unreachable!("Signals cannot be immutable right now so this cannot be reached") - // CODECOV_EXCLUDE_STOP + parse_quote_spanned! {span => &#qobject_name_rust } }; let self_type_qualified = syn_type_cxx_bridge_to_qualified(&self_type_cxx, type_names)?; let qualified_impl = qobject_name.rust_qualified(); diff --git a/crates/cxx-qt-gen/src/parser/externcxxqt.rs b/crates/cxx-qt-gen/src/parser/externcxxqt.rs index 4364c9027..8235ffc80 100644 --- a/crates/cxx-qt-gen/src/parser/externcxxqt.rs +++ b/crates/cxx-qt-gen/src/parser/externcxxqt.rs @@ -108,7 +108,7 @@ impl ParsedExternCxxQt { if attribute_get_path(&foreign_fn.attrs, &["inherit"]).is_some() { return Err(Error::new(foreign_fn.span(), "#[inherit] is not allowed or necessary in extern \"C++Qt\" blocks, as all signals are inherited by default")); } - let mut signal = ParsedSignal::parse(foreign_fn, auto_case)?; + let mut signal = ParsedSignal::parse_with_mutability(foreign_fn, auto_case, true)?; // extern "C++Qt" signals are always inherit = true // as they always exist on an existing QObject signal.inherit = true; diff --git a/crates/cxx-qt-gen/src/parser/externrustqt.rs b/crates/cxx-qt-gen/src/parser/externrustqt.rs index 6da520bdf..c3d054f7f 100644 --- a/crates/cxx-qt-gen/src/parser/externrustqt.rs +++ b/crates/cxx-qt-gen/src/parser/externrustqt.rs @@ -91,7 +91,8 @@ impl ParsedExternRustQt { ) -> Result<()> { // Test if the function is a signal if attribute_get_path(&foreign_fn.attrs, &["qsignal"]).is_some() { - let parsed_signal_method = ParsedSignal::parse(foreign_fn.clone(), auto_case)?; + let parsed_signal_method = + ParsedSignal::parse_rust_qt_signal(foreign_fn.clone(), auto_case)?; if parsed_signal_method.inherit && foreign_fn.sig.unsafety.is_none() && self.unsafety.is_none() diff --git a/crates/cxx-qt-gen/src/parser/signals.rs b/crates/cxx-qt-gen/src/parser/signals.rs index 81e264ebb..dcf6bc91c 100644 --- a/crates/cxx-qt-gen/src/parser/signals.rs +++ b/crates/cxx-qt-gen/src/parser/signals.rs @@ -9,7 +9,8 @@ use crate::{ }; use core::ops::Deref; use std::ops::DerefMut; -use syn::{spanned::Spanned, Attribute, Error, ForeignItemFn, Result, Visibility}; +use syn::spanned::Spanned; +use syn::{Attribute, Error, ForeignItemFn, Result, Visibility}; #[derive(Clone)] /// Describes an individual Signal @@ -33,19 +34,24 @@ impl ParsedSignal { #[cfg(test)] /// Test fn for creating a mocked signal from a method body pub fn mock(method: &ForeignItemFn) -> Self { - Self::parse(method.clone(), CaseConversion::none()).unwrap() + Self::parse_rust_qt_signal(method.clone(), CaseConversion::none()).unwrap() } - pub fn parse(method: ForeignItemFn, auto_case: CaseConversion) -> Result { + pub fn parse_with_mutability( + method: ForeignItemFn, + auto_case: CaseConversion, + immutable_allowed: bool, + ) -> Result { let docs = extract_docs(&method.attrs); let cfgs = extract_cfgs(&method.attrs); let fields = MethodFields::parse(method, auto_case)?; let attrs = require_attributes(&fields.method.attrs, &Self::ALLOWED_ATTRS)?; - if !fields.mutable { + // TODO: add proper checks + if !fields.mutable && !immutable_allowed { return Err(Error::new( fields.method.span(), - "signals must be mutable, use Pin<&mut T> instead of T for the self type", + "immutable signals can only be used in `unsafe extern \"C++Qt\"` blocks, use Pin<&mut T> instead of T for the self type, or change the type of this extern block", )); } @@ -65,6 +71,10 @@ impl ParsedSignal { cfgs, }) } + + pub fn parse_rust_qt_signal(method: ForeignItemFn, auto_case: CaseConversion) -> Result { + Self::parse_with_mutability(method, auto_case, false) + } } impl Deref for ParsedSignal { @@ -94,20 +104,18 @@ mod tests { #[test] fn test_parse_signal_invalid() { assert_parse_errors! { - |input| ParsedSignal::parse(input, CaseConversion::none()) => + |input| ParsedSignal::parse_rust_qt_signal(input, CaseConversion::none()) => - // No immutable signals - { fn ready(self: &MyObject); } + // No namespaces { - // No namespaces #[namespace = "disallowed_namespace"] fn ready(self: Pin<&mut MyObject>); } // Missing self { fn ready(x: f64); } - // Self needs to be receiver like self: &T instead of &self + // Immutable signals must be in "C++Qt" blocks { fn ready(&self); } - } + }; } #[test] @@ -115,7 +123,8 @@ mod tests { let method: ForeignItemFn = parse_quote! { fn ready(self: Pin<&mut MyObject>); }; - let signal = ParsedSignal::parse(method.clone(), CaseConversion::none()).unwrap(); + let signal = + ParsedSignal::parse_rust_qt_signal(method.clone(), CaseConversion::none()).unwrap(); assert_eq!(signal.method, method); assert_eq!(signal.qobject_ident, format_ident!("MyObject")); assert!(signal.mutable); @@ -132,7 +141,7 @@ mod tests { #[cxx_name = "cppReady"] fn ready(self: Pin<&mut MyObject>); }; - let signal = ParsedSignal::parse(method, CaseConversion::none()).unwrap(); + let signal = ParsedSignal::parse_rust_qt_signal(method, CaseConversion::none()).unwrap(); let expected_method: ForeignItemFn = parse_quote! { #[cxx_name = "cppReady"] @@ -154,7 +163,8 @@ mod tests { #[inherit] fn ready(self: Pin<&mut MyObject>); }; - let signal = ParsedSignal::parse(method.clone(), CaseConversion::none()).unwrap(); + let signal = + ParsedSignal::parse_rust_qt_signal(method.clone(), CaseConversion::none()).unwrap(); assert_eq!(signal.method, method); assert_eq!(signal.qobject_ident, format_ident!("MyObject")); @@ -171,7 +181,8 @@ mod tests { let method: ForeignItemFn = parse_quote! { fn ready(self: Pin<&mut MyObject>, x: f64, y: f64); }; - let signal = ParsedSignal::parse(method.clone(), CaseConversion::none()).unwrap(); + let signal = + ParsedSignal::parse_rust_qt_signal(method.clone(), CaseConversion::none()).unwrap(); assert_eq!(signal.method, method); assert_eq!(signal.qobject_ident, format_ident!("MyObject")); assert!(signal.mutable); @@ -191,7 +202,8 @@ mod tests { let method: ForeignItemFn = parse_quote! { pub(self) fn ready(self: Pin<&mut MyObject>); }; - let signal = ParsedSignal::parse(method.clone(), CaseConversion::none()).unwrap(); + let signal = + ParsedSignal::parse_rust_qt_signal(method.clone(), CaseConversion::none()).unwrap(); assert_eq!(signal.method, method); assert_eq!(signal.qobject_ident, format_ident!("MyObject")); assert!(signal.mutable); @@ -207,7 +219,8 @@ mod tests { let method: ForeignItemFn = parse_quote! { unsafe fn ready(self: Pin<&mut MyObject>); }; - let signal = ParsedSignal::parse(method.clone(), CaseConversion::none()).unwrap(); + let signal = + ParsedSignal::parse_rust_qt_signal(method.clone(), CaseConversion::none()).unwrap(); assert_eq!(signal.method, method); assert_eq!(signal.qobject_ident, format_ident!("MyObject")); assert!(signal.mutable); diff --git a/crates/cxx-qt-gen/test_inputs/signals.rs b/crates/cxx-qt-gen/test_inputs/signals.rs index ec298cca7..e7c67f66f 100644 --- a/crates/cxx-qt-gen/test_inputs/signals.rs +++ b/crates/cxx-qt-gen/test_inputs/signals.rs @@ -17,6 +17,11 @@ mod ffi { /// When the QTimer timeout occurs #[qsignal] pub(self) fn timeout(self: Pin<&mut Self>); + + /// A constant signal for when the timer is ready + #[qsignal] + fn const_ready(&self); + } unsafe extern "RustQt" { diff --git a/crates/cxx-qt-gen/test_outputs/signals.cpp b/crates/cxx-qt-gen/test_outputs/signals.cpp index 3e0b51c48..2e510cc77 100644 --- a/crates/cxx-qt-gen/test_outputs/signals.cpp +++ b/crates/cxx-qt-gen/test_outputs/signals.cpp @@ -56,6 +56,60 @@ QTimer_timeoutConnect( } } // namespace cxx_qt::my_object::rust::cxxqtgen1 +// Define namespace otherwise we hit a GCC bug +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 +namespace rust::cxxqt1 { +template<> +SignalHandler<::cxx_qt::my_object::rust::cxxqtgen1:: + QTimerCxxQtSignalParamsconst_ready*>::~SignalHandler() noexcept +{ + if (data[0] == nullptr && data[1] == nullptr) { + return; + } + + drop_QTimer_signal_handler_const_ready(::std::move(*this)); +} + +template<> +template<> +void +SignalHandler< + ::cxx_qt::my_object::rust::cxxqtgen1::QTimerCxxQtSignalParamsconst_ready*>:: +operator()( + cxx_qt::my_object::QTimer const& self) +{ + call_QTimer_signal_handler_const_ready(*this, self); +} + +static_assert(alignof(SignalHandler<::cxx_qt::my_object::rust::cxxqtgen1:: + QTimerCxxQtSignalParamsconst_ready*>) <= + alignof(::std::size_t), + "unexpected aligment"); +static_assert(sizeof(SignalHandler<::cxx_qt::my_object::rust::cxxqtgen1:: + QTimerCxxQtSignalParamsconst_ready*>) == + sizeof(::std::size_t[2]), + "unexpected size"); +} // namespace rust::cxxqt1 + +namespace cxx_qt::my_object::rust::cxxqtgen1 { +::QMetaObject::Connection +QTimer_const_readyConnect( + cxx_qt::my_object::QTimer const& self, + ::cxx_qt::my_object::rust::cxxqtgen1::QTimerCxxQtSignalHandlerconst_ready + closure, + ::Qt::ConnectionType type) +{ + return ::QObject::connect( + &self, + &cxx_qt::my_object::QTimer::const_ready, + &self, + [&, closure = ::std::move(closure)]() mutable { + closure.template operator()(self); + }, + type); +} +} // namespace cxx_qt::my_object::rust::cxxqtgen1 + // Define namespace otherwise we hit a GCC bug // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 namespace rust::cxxqt1 { diff --git a/crates/cxx-qt-gen/test_outputs/signals.h b/crates/cxx-qt-gen/test_outputs/signals.h index bad91af1b..450a86c85 100644 --- a/crates/cxx-qt-gen/test_outputs/signals.h +++ b/crates/cxx-qt-gen/test_outputs/signals.h @@ -29,6 +29,11 @@ using QTimerCxxQtSignalHandlertimeout = ::rust::cxxqt1::SignalHandler; } // namespace cxx_qt::my_object::rust::cxxqtgen1 +namespace cxx_qt::my_object::rust::cxxqtgen1 { +using QTimerCxxQtSignalHandlerconst_ready = + ::rust::cxxqt1::SignalHandler; +} // namespace cxx_qt::my_object::rust::cxxqtgen1 + #include "directory/file_ident.cxx.h" namespace cxx_qt::my_object::rust::cxxqtgen1 { @@ -39,6 +44,15 @@ QTimer_timeoutConnect( ::Qt::ConnectionType type); } // namespace cxx_qt::my_object::rust::cxxqtgen1 +namespace cxx_qt::my_object::rust::cxxqtgen1 { +::QMetaObject::Connection +QTimer_const_readyConnect( + cxx_qt::my_object::QTimer const& self, + ::cxx_qt::my_object::rust::cxxqtgen1::QTimerCxxQtSignalHandlerconst_ready + closure, + ::Qt::ConnectionType type); +} // namespace cxx_qt::my_object::rust::cxxqtgen1 + namespace cxx_qt::my_object::rust::cxxqtgen1 { ::QMetaObject::Connection MyObject_readyConnect( diff --git a/crates/cxx-qt-gen/test_outputs/signals.rs b/crates/cxx-qt-gen/test_outputs/signals.rs index 9c30d80ea..5b5064a0f 100644 --- a/crates/cxx-qt-gen/test_outputs/signals.rs +++ b/crates/cxx-qt-gen/test_outputs/signals.rs @@ -224,6 +224,38 @@ mod ffi { self_value: Pin<&mut QTimer>, ); } + unsafe extern "C++" { + #[cxx_name = "const_ready"] + #[doc = " A constant signal for when the timer is ready"] + #[namespace = "cxx_qt::my_object"] + fn const_ready(self: &QTimer); + } + unsafe extern "C++" { + #[doc(hidden)] + #[namespace = "cxx_qt::my_object::rust::cxxqtgen1"] + type QTimerCxxQtSignalHandlerconst_ready<'a> = cxx_qt::signalhandler::CxxQtSignalHandler< + 'a, + super::QTimerCxxQtSignalClosureconst_ready, + >; + #[doc(hidden)] + #[namespace = "cxx_qt::my_object::rust::cxxqtgen1"] + #[cxx_name = "QTimer_const_readyConnect"] + fn QTimer_connect_const_ready( + self_value: &QTimer, + signal_handler: QTimerCxxQtSignalHandlerconst_ready, + conn_type: CxxQtConnectionType, + ) -> CxxQtQMetaObjectConnection; + } + #[namespace = "cxx_qt::my_object::rust::cxxqtgen1"] + extern "Rust" { + #[doc(hidden)] + fn drop_QTimer_signal_handler_const_ready(handler: QTimerCxxQtSignalHandlerconst_ready); + #[doc(hidden)] + fn call_QTimer_signal_handler_const_ready( + handler: &mut QTimerCxxQtSignalHandlerconst_ready, + self_value: &QTimer, + ); + } extern "C++" { #[doc(hidden)] #[namespace = ""] @@ -559,3 +591,62 @@ cxx_qt::static_assertions::assert_eq_size!( cxx_qt::signalhandler::CxxQtSignalHandler, [usize; 2] ); +impl ffi::QTimer { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "const_ready"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + pub fn connect_const_ready<'a, F: FnMut(&ffi::QTimer) + 'a + Send>( + self: &ffi::QTimer, + closure: F, + conn_type: cxx_qt::ConnectionType, + ) -> cxx_qt::QScopedMetaObjectConnectionGuard<'a> { + cxx_qt::QScopedMetaObjectConnectionGuard::from(ffi::QTimer_connect_const_ready( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::::new( + Box::new(closure), + ), + conn_type, + )) + } +} +impl ffi::QTimer { + #[doc = "Connect the given function pointer to the signal "] + #[doc = "const_ready"] + #[doc = ", so that when the signal is emitted the function pointer is executed."] + #[doc = "\n"] + #[doc = "Note that this method uses a AutoConnection connection type."] + pub fn on_const_ready<'a, F: FnMut(&ffi::QTimer) + 'a + Send>( + self: &ffi::QTimer, + closure: F, + ) -> cxx_qt::QScopedMetaObjectConnectionGuard<'a> { + cxx_qt::QScopedMetaObjectConnectionGuard::from(ffi::QTimer_connect_const_ready( + self, + cxx_qt::signalhandler::CxxQtSignalHandler::::new( + Box::new(closure), + ), + cxx_qt::ConnectionType::AutoConnection, + )) + } +} +#[doc(hidden)] +pub struct QTimerCxxQtSignalClosureconst_ready {} +impl cxx_qt::signalhandler::CxxQtSignalHandlerClosure for QTimerCxxQtSignalClosureconst_ready { + type Id = + cxx::type_id!("::cxx_qt::my_object::rust::cxxqtgen1::QTimerCxxQtSignalHandlerconst_ready"); + type FnType<'a> = dyn FnMut(&ffi::QTimer) + 'a + Send; +} +use core::mem::drop as drop_QTimer_signal_handler_const_ready; +fn call_QTimer_signal_handler_const_ready( + handler: &mut cxx_qt::signalhandler::CxxQtSignalHandler, + self_value: &ffi::QTimer, +) { + handler.closure()(self_value); +} +cxx_qt::static_assertions::assert_eq_align!( + cxx_qt::signalhandler::CxxQtSignalHandler, + usize +); +cxx_qt::static_assertions::assert_eq_size!( + cxx_qt::signalhandler::CxxQtSignalHandler, + [usize; 2] +); diff --git a/examples/qml_features/cpp/external_qobject.cpp b/examples/qml_features/cpp/external_qobject.cpp index d3b051b01..eb39b3cd9 100644 --- a/examples/qml_features/cpp/external_qobject.cpp +++ b/examples/qml_features/cpp/external_qobject.cpp @@ -16,6 +16,7 @@ ExternalQObject::trigger(::std::uint32_t amount) { for (::std::uint32_t i = 0; i < amount; i++) { Q_EMIT triggered(); + Q_EMIT triggeredConstSignal(); Q_EMIT triggeredPrivateSignal(QPrivateSignal()); } } diff --git a/examples/qml_features/cpp/external_qobject.h b/examples/qml_features/cpp/external_qobject.h index c4a4b712f..6289545c2 100644 --- a/examples/qml_features/cpp/external_qobject.h +++ b/examples/qml_features/cpp/external_qobject.h @@ -22,4 +22,5 @@ class ExternalQObject : public QObject Q_SIGNALS: void triggered(); void triggeredPrivateSignal(QPrivateSignal); + void triggeredConstSignal() const; }; diff --git a/examples/qml_features/rust/src/externcxxqt.rs b/examples/qml_features/rust/src/externcxxqt.rs index efc9fd04b..bc1447072 100644 --- a/examples/qml_features/rust/src/externcxxqt.rs +++ b/examples/qml_features/rust/src/externcxxqt.rs @@ -22,6 +22,11 @@ pub mod ffi { #[qsignal] fn triggered(self: Pin<&mut Self>); + /// const signal that is emitted when trigger is fired + #[qsignal] + #[rust_name = "triggered_const_signal"] + fn triggeredConstSignal(&self); + /// Private signal that is emitted when trigger is fired #[qsignal] #[rust_name = "triggered_private_signal"] @@ -90,6 +95,19 @@ impl ffi::ExternalCxxQtHelper { .unwrap(); }) .release(); + + let qt_thread = self.qt_thread(); + pinned_external + .as_mut() + .on_triggered_const_signal(move |_| { + qt_thread + .queue(|qobject| { + let const_count = qobject.count(); + println!("count from const signal: {}", const_count); + }) + .unwrap(); + }) + .release(); } } diff --git a/examples/qml_features/tests/tst_externcxxqt.qml b/examples/qml_features/tests/tst_externcxxqt.qml index 47e7f1aef..1407302ba 100644 --- a/examples/qml_features/tests/tst_externcxxqt.qml +++ b/examples/qml_features/tests/tst_externcxxqt.qml @@ -46,11 +46,16 @@ TestCase { signalName: "triggeredPrivateSignal", target: obj, }); + const triggeredConstSpy = createTemporaryObject(componentSpy, null, { + signalName: "triggeredConstSignal", + target: obj, + }); helper.connectToExternal(obj); compare(triggeredSpy.count, 0); compare(triggeredPrivateSpy.count, 0); + compare(triggeredConstSpy.count, 0); compare(helper.count, 0); compare(helper.privateCount, 0); @@ -58,6 +63,7 @@ TestCase { tryCompare(triggeredSpy, "count", 2); tryCompare(triggeredPrivateSpy, "count", 2); + tryCompare(triggeredConstSpy, "count", 2); tryCompare(helper, "count", 2); tryCompare(helper, "privateCount", 2); }