Skip to content

Commit e4e0564

Browse files
LeonMatthesKDABBe-ing
authored andcommitted
cxx_qt::Constructor: Add tests
1 parent d897640 commit e4e0564

File tree

5 files changed

+250
-40
lines changed

5 files changed

+250
-40
lines changed

crates/cxx-qt-gen/src/generator/cpp/constructor.rs

Lines changed: 151 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ fn default_constructor(
2727
r#"
2828
{class_name}::{class_name}(QObject* parent)
2929
: {base_class}(parent)
30-
, m_rustObj({namespace_internals}::createRs())
31-
{initializers}
30+
, m_rustObj({namespace_internals}::createRs()){initializers}
3231
{{
3332
}}
3433
"#,
@@ -62,15 +61,15 @@ fn expand_arguments(arguments: &[Type], cxx_mappings: &ParsedCxxMappings) -> Res
6261

6362
pub fn generate(
6463
qobject: &GeneratedCppQObject,
65-
constructors: &Vec<Constructor>,
64+
constructors: &[Constructor],
6665
member_initializers: &[String],
6766
cxx_mappings: &ParsedCxxMappings,
6867
) -> Result<GeneratedCppQObjectBlocks> {
6968
let initializers = member_initializers
7069
.iter()
71-
.map(|initializer| format!(", {initializer}"))
70+
.map(|initializer| format!("\n , {initializer}"))
7271
.collect::<Vec<_>>()
73-
.join("\n");
72+
.join("");
7473

7574
if constructors.is_empty() {
7675
return Ok(default_constructor(qobject, initializers));
@@ -90,15 +89,15 @@ pub fn generate(
9089
source: formatdoc! {
9190
r#"
9291
{class_name}::{class_name}({argument_list})
93-
: {class_name}({namespace_internals}::routeArguments{index}({move_arguments}))
94-
{{ }}
92+
: {class_name}({namespace_internals}::routeArguments{index}({move_arguments}))
93+
{{ }}
9594
"#,
9695
move_arguments = constructor_argument_names.iter().map(|arg| format!("std::move({arg})")).collect::<Vec<_>>().join(", "),
9796
},
9897
});
9998

100-
let base_args = if let Some(base_args) = &constructor.base_arguments {
101-
argument_names(base_args)
99+
let base_args = if !constructor.base_arguments.is_empty() {
100+
argument_names(&constructor.base_arguments)
102101
.into_iter()
103102
.map(|arg| format!("std::move(args.baseArguments.{arg})"))
104103
.collect::<Vec<_>>()
@@ -120,16 +119,153 @@ pub fn generate(
120119
source: formatdoc! {
121120
r#"
122121
{class_name}::{class_name}({namespace_internals}::CxxQtConstructorArguments{index}&& args)
123-
: {base_class}({base_args})
124-
, m_rustObj({namespace_internals}::newRs{index}(std::move(args.newArguments)))
125-
{initializers}
126-
{{
127-
{namespace_internals}::initialize{index}(*this, std::move(args.initializeArguments));
128-
}}
122+
: {base_class}({base_args})
123+
, m_rustObj({namespace_internals}::newRs{index}(std::move(args.newArguments))){initializers}
124+
{{
125+
{namespace_internals}::initialize{index}(*this, std::move(args.initializeArguments));
126+
}}
129127
"#,
130128
},
131129
})
132130
}
133131

134132
Ok(generated)
135133
}
134+
135+
#[cfg(test)]
136+
mod tests {
137+
use super::*;
138+
139+
use std::assert_eq;
140+
use syn::parse_quote;
141+
142+
fn qobject_for_testing() -> GeneratedCppQObject {
143+
GeneratedCppQObject {
144+
ident: "MyObject".to_string(),
145+
rust_ident: "MyObjectQt".to_string(),
146+
namespace_internals: "rust".to_string(),
147+
base_class: "BaseClass".to_string(),
148+
blocks: GeneratedCppQObjectBlocks::default(),
149+
locking: true,
150+
}
151+
}
152+
153+
#[test]
154+
fn test_default_constructor() {
155+
let blocks = generate(
156+
&qobject_for_testing(),
157+
&[],
158+
&["member1(1)".to_string(), "member2{ 2 }".to_string()],
159+
&ParsedCxxMappings::default(),
160+
)
161+
.unwrap();
162+
163+
assert!(blocks.members.is_empty());
164+
assert!(blocks.private_methods.is_empty());
165+
assert!(blocks.metaobjects.is_empty());
166+
assert!(blocks.forward_declares.is_empty());
167+
assert!(blocks.deconstructors.is_empty());
168+
assert_eq!(
169+
blocks.methods,
170+
vec![CppFragment::Pair {
171+
header: "explicit MyObject(QObject* parent = nullptr);".to_string(),
172+
source: formatdoc!(
173+
"
174+
MyObject::MyObject(QObject* parent)
175+
: BaseClass(parent)
176+
, m_rustObj(rust::createRs())
177+
, member1(1)
178+
, member2{{ 2 }}
179+
{{
180+
}}
181+
"
182+
),
183+
}]
184+
);
185+
}
186+
#[test]
187+
fn test_default_constructor_no_initializers() {
188+
let blocks = generate(
189+
&qobject_for_testing(),
190+
&[],
191+
&[],
192+
&ParsedCxxMappings::default(),
193+
)
194+
.unwrap();
195+
196+
assert!(blocks.members.is_empty());
197+
assert!(blocks.private_methods.is_empty());
198+
assert!(blocks.metaobjects.is_empty());
199+
assert!(blocks.forward_declares.is_empty());
200+
assert!(blocks.deconstructors.is_empty());
201+
assert_eq!(
202+
blocks.methods,
203+
vec![CppFragment::Pair {
204+
header: "explicit MyObject(QObject* parent = nullptr);".to_string(),
205+
source: formatdoc!(
206+
"
207+
MyObject::MyObject(QObject* parent)
208+
: BaseClass(parent)
209+
, m_rustObj(rust::createRs())
210+
{{
211+
}}
212+
"
213+
),
214+
}]
215+
);
216+
}
217+
218+
#[test]
219+
fn test_single_constructor() {
220+
let blocks = generate(
221+
&qobject_for_testing(),
222+
&[Constructor {
223+
arguments: vec![parse_quote! { i32 }, parse_quote! { *mut QObject }],
224+
new_arguments: vec![],
225+
base_arguments: vec![],
226+
initialize_arguments: vec![],
227+
// dummy
228+
imp: parse_quote! {
229+
impl X {}
230+
},
231+
}],
232+
&[],
233+
&ParsedCxxMappings::default(),
234+
)
235+
.unwrap();
236+
237+
assert!(blocks.members.is_empty());
238+
assert!(blocks.forward_declares.is_empty());
239+
assert!(blocks.deconstructors.is_empty());
240+
assert!(blocks.metaobjects.is_empty());
241+
assert_eq!(
242+
blocks.private_methods,
243+
vec![CppFragment::Pair {
244+
header: "explicit MyObject(rust::CxxQtConstructorArguments0&& args);".to_string(),
245+
source: formatdoc!(
246+
"
247+
MyObject::MyObject(rust::CxxQtConstructorArguments0&& args)
248+
: BaseClass()
249+
, m_rustObj(rust::newRs0(std::move(args.newArguments)))
250+
{{
251+
rust::initialize0(*this, std::move(args.initializeArguments));
252+
}}
253+
"
254+
),
255+
}]
256+
);
257+
assert_eq!(
258+
blocks.methods,
259+
vec![CppFragment::Pair {
260+
header: "explicit MyObject(::std::int32_t arg0, QObject* arg1);".to_string(),
261+
source: formatdoc!(
262+
"
263+
MyObject::MyObject(::std::int32_t arg0, QObject* arg1)
264+
: MyObject(rust::routeArguments0(std::move(arg0), std::move(arg1)))
265+
{{ }}
266+
"
267+
),
268+
}]
269+
);
270+
}
271+
}

crates/cxx-qt-gen/src/generator/cpp/fragment.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
use crate::generator::cpp::types::CppType;
77

8+
#[derive(PartialEq, Eq, Debug)]
89
pub enum CppFragment {
910
Pair { header: String, source: String },
1011
Header(String),

crates/cxx-qt-gen/src/generator/rust/constructor.rs

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,11 @@ const BASE_ARGUMENTS: &str = "CxxQtConstructorBaseArguments";
2121
const NEW_ARGUMENTS: &str = "CxxQtConstructorNewArguments";
2222
const INITIALIZE_ARGUMENTS: &str = "CxxQtConstructorInitializeArguments";
2323

24-
fn map_types<F: FnMut((usize, &Type)) -> TokenStream>(
25-
args: &Option<Vec<Type>>,
26-
f: F,
27-
) -> Vec<TokenStream> {
28-
args.as_ref()
29-
.map(|args| args.iter().enumerate().map(f).collect())
30-
.unwrap_or_default()
24+
fn map_types<F: FnMut((usize, &Type)) -> TokenStream>(args: &[Type], f: F) -> Vec<TokenStream> {
25+
args.iter().enumerate().map(f).collect()
3126
}
3227

33-
fn extract_arguments_from_tuple(args: &Option<Vec<Type>>, tuple_name: Ident) -> Vec<TokenStream> {
28+
fn extract_arguments_from_tuple(args: &[Type], tuple_name: Ident) -> Vec<TokenStream> {
3429
map_types(args, |(index, _ty)| {
3530
let arg_name = format_ident!("arg{index}");
3631
let index = syn::LitInt::new(index.to_string().as_str(), Span::call_site());
@@ -40,7 +35,7 @@ fn extract_arguments_from_tuple(args: &Option<Vec<Type>>, tuple_name: Ident) ->
4035
})
4136
}
4237

43-
fn extract_arguments_from_struct(args: &Option<Vec<Type>>, struct_name: Ident) -> Vec<TokenStream> {
38+
fn extract_arguments_from_struct(args: &[Type], struct_name: Ident) -> Vec<TokenStream> {
4439
map_types(args, |(index, _ty)| {
4540
let arg_name = format_ident!("arg{index}");
4641
quote! {
@@ -49,7 +44,7 @@ fn extract_arguments_from_struct(args: &Option<Vec<Type>>, struct_name: Ident) -
4944
})
5045
}
5146

52-
fn argument_members(args: &Option<Vec<Type>>) -> Vec<TokenStream> {
47+
fn argument_members(args: &[Type]) -> Vec<TokenStream> {
5348
map_types(args, |(index, ty)| {
5449
let arg_name = format_ident!("arg{index}");
5550
quote! {
@@ -89,13 +84,13 @@ fn generate_default_constructor(
8984
fn generate_arguments_struct(
9085
namespace_internals: &str,
9186
struct_name: &CombinedIdent,
92-
argument_list: &Option<Vec<Type>>,
87+
argument_list: &[Type],
9388
) -> Item {
9489
let argument_members = argument_members(argument_list);
95-
let not_empty = if argument_list.as_ref().is_some_and(|list| !list.is_empty()) {
96-
None
97-
} else {
90+
let not_empty = if argument_list.is_empty() {
9891
Some(quote! { not_empty: i8 })
92+
} else {
93+
None
9994
};
10095
let rust_name = &struct_name.rust;
10196
// use to_string here, as the cxx_name needs to be in quotes for the attribute macro.
@@ -115,14 +110,14 @@ fn generate_arguments_struct(
115110
fn generate_arguments_initialization(
116111
struct_name: &Ident,
117112
instance_name: Ident,
118-
argument_list: &Option<Vec<Type>>,
113+
argument_list: &[Type],
119114
) -> Expr {
120115
let init_arguments = extract_arguments_from_tuple(argument_list, instance_name);
121116
println!("init_arguments: {:?}", init_arguments);
122-
let not_empty = if argument_list.as_ref().is_some_and(|list| !list.is_empty()) {
123-
None
124-
} else {
117+
let not_empty = if argument_list.is_empty() {
125118
Some(quote! { not_empty: 0 })
119+
} else {
120+
None
126121
};
127122

128123
parse_quote! {

0 commit comments

Comments
 (0)