Skip to content

Commit

Permalink
cxx_qt::Constructor: Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
LeonMatthesKDAB authored and ahayzen-kdab committed Jul 21, 2023
1 parent 956c74f commit 26332e1
Show file tree
Hide file tree
Showing 16 changed files with 852 additions and 140 deletions.
311 changes: 290 additions & 21 deletions crates/cxx-qt-gen/src/generator/cpp/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ fn default_constructor(
r#"
{class_name}::{class_name}(QObject* parent)
: {base_class}(parent)
, m_rustObj({namespace_internals}::createRs())
{initializers}
{{
}}
, m_rustObj(::{namespace_internals}::createRs()){initializers}
{{ }}
"#,
class_name = qobject.ident,
base_class = qobject.base_class,
Expand Down Expand Up @@ -60,15 +58,15 @@ fn expand_arguments(arguments: &[Type], cxx_mappings: &ParsedCxxMappings) -> Res

pub fn generate(
qobject: &GeneratedCppQObject,
constructors: &Vec<Constructor>,
constructors: &[Constructor],
member_initializers: &[String],
cxx_mappings: &ParsedCxxMappings,
) -> Result<GeneratedCppQObjectBlocks> {
let initializers = member_initializers
.iter()
.map(|initializer| format!(", {initializer}"))
.map(|initializer| format!("\n , {initializer}"))
.collect::<Vec<_>>()
.join("\n");
.join("");

if constructors.is_empty() {
return Ok(default_constructor(qobject, initializers));
Expand All @@ -88,17 +86,17 @@ pub fn generate(
source: formatdoc! {
r#"
{class_name}::{class_name}({argument_list})
: {class_name}({namespace_internals}::routeArguments{index}({move_arguments}))
{{ }}
: {class_name}(::{namespace_internals}::routeArguments{index}({move_arguments}))
{{ }}
"#,
move_arguments = constructor_argument_names.iter().map(|arg| format!("std::move({arg})")).collect::<Vec<_>>().join(", "),
move_arguments = constructor_argument_names.iter().map(|arg| format!("::std::move({arg})")).collect::<Vec<_>>().join(", "),
},
});

let base_args = if let Some(base_args) = &constructor.base_arguments {
argument_names(base_args)
let base_args = if !constructor.base_arguments.is_empty() {
argument_names(&constructor.base_arguments)
.into_iter()
.map(|arg| format!("std::move(args.baseArguments.{arg})"))
.map(|arg| format!("::std::move(args.base.{arg})"))
.collect::<Vec<_>>()
.join(", ")
} else {
Expand All @@ -113,21 +111,292 @@ pub fn generate(
// can use it.
generated.private_methods.push(CppFragment::Pair {
header: format!(
"explicit {class_name}({namespace_internals}::CxxQtConstructorArguments{index}&& args);"
"explicit {class_name}(::{namespace_internals}::CxxQtConstructorArguments{index}&& args);"
),
source: formatdoc! {
r#"
{class_name}::{class_name}({namespace_internals}::CxxQtConstructorArguments{index}&& args)
: {base_class}({base_args})
, m_rustObj({namespace_internals}::newRs{index}(std::move(args.newArguments)))
{initializers}
{{
{namespace_internals}::initialize{index}(*this, std::move(args.initializeArguments));
}}
{class_name}::{class_name}(::{namespace_internals}::CxxQtConstructorArguments{index}&& args)
: {base_class}({base_args})
, m_rustObj(::{namespace_internals}::newRs{index}(::std::move(args.new_))){initializers}
{{
::{namespace_internals}::initialize{index}(*this, ::std::move(args.initialize));
}}
"#,
},
})
}

Ok(generated)
}

#[cfg(test)]
mod tests {
use super::*;

use std::assert_eq;
use syn::parse_quote;

fn qobject_for_testing() -> GeneratedCppQObject {
GeneratedCppQObject {
ident: "MyObject".to_string(),
rust_ident: "MyObjectQt".to_string(),
namespace_internals: "rust".to_string(),
base_class: "BaseClass".to_string(),
blocks: GeneratedCppQObjectBlocks::default(),
locking: true,
}
}

fn mock_constructor() -> Constructor {
Constructor {
arguments: vec![],
base_arguments: vec![],
new_arguments: vec![],
initialize_arguments: vec![],
imp: parse_quote! { impl X {} },
}
}

fn assert_empty_blocks(blocks: &GeneratedCppQObjectBlocks) {
assert!(blocks.members.is_empty());
assert!(blocks.metaobjects.is_empty());
assert!(blocks.forward_declares.is_empty());
assert!(blocks.deconstructors.is_empty());
}

#[test]
fn default_constructor_with_initializers() {
let blocks = generate(
&qobject_for_testing(),
&[],
&["member1(1)".to_string(), "member2{ 2 }".to_string()],
&ParsedCxxMappings::default(),
)
.unwrap();

assert_empty_blocks(&blocks);
assert!(blocks.private_methods.is_empty());
assert_eq!(
blocks.methods,
vec![CppFragment::Pair {
header: "explicit MyObject(QObject* parent = nullptr);".to_string(),
source: formatdoc!(
"
MyObject::MyObject(QObject* parent)
: BaseClass(parent)
, m_rustObj(::rust::createRs())
, member1(1)
, member2{{ 2 }}
{{ }}
"
),
}]
);
}
#[test]
fn default_constructor_without_initializers() {
let blocks = generate(
&qobject_for_testing(),
&[],
&[],
&ParsedCxxMappings::default(),
)
.unwrap();

assert_empty_blocks(&blocks);
assert!(blocks.private_methods.is_empty());
assert_eq!(
blocks.methods,
vec![CppFragment::Pair {
header: "explicit MyObject(QObject* parent = nullptr);".to_string(),
source: formatdoc!(
"
MyObject::MyObject(QObject* parent)
: BaseClass(parent)
, m_rustObj(::rust::createRs())
{{ }}
"
),
}]
);
}

#[test]
fn constructor_without_base_arguments() {
let blocks = generate(
&qobject_for_testing(),
&[Constructor {
arguments: vec![parse_quote! { i32 }, parse_quote! { *mut QObject }],
..mock_constructor()
}],
&[],
&ParsedCxxMappings::default(),
)
.unwrap();

assert_empty_blocks(&blocks);
assert_eq!(
blocks.private_methods,
vec![CppFragment::Pair {
header: "explicit MyObject(::rust::CxxQtConstructorArguments0&& args);".to_string(),
source: formatdoc!(
"
MyObject::MyObject(::rust::CxxQtConstructorArguments0&& args)
: BaseClass()
, m_rustObj(::rust::newRs0(::std::move(args.new_)))
{{
::rust::initialize0(*this, ::std::move(args.initialize));
}}
"
),
}]
);
assert_eq!(
blocks.methods,
vec![CppFragment::Pair {
header: "explicit MyObject(::std::int32_t arg0, QObject* arg1);".to_string(),
source: formatdoc!(
"
MyObject::MyObject(::std::int32_t arg0, QObject* arg1)
: MyObject(::rust::routeArguments0(::std::move(arg0), ::std::move(arg1)))
{{ }}
"
),
}]
);
}

#[test]
fn constructor_with_all_arguments() {
let blocks = generate(
&qobject_for_testing(),
&[Constructor {
arguments: vec![parse_quote! { i8 }, parse_quote! { i16 }],
new_arguments: vec![parse_quote! { i16}, parse_quote! { i32 }],
initialize_arguments: vec![parse_quote! { i32 }, parse_quote! { i64 }],
base_arguments: vec![parse_quote! { i64 }, parse_quote! { *mut QObject }],
..mock_constructor()
}],
&["initializer".to_string()],
&ParsedCxxMappings::default(),
)
.unwrap();

assert_empty_blocks(&blocks);
assert_eq!(
blocks.methods,
vec![CppFragment::Pair {
header: "explicit MyObject(::std::int8_t arg0, ::std::int16_t arg1);".to_string(),
source: formatdoc!(
"
MyObject::MyObject(::std::int8_t arg0, ::std::int16_t arg1)
: MyObject(::rust::routeArguments0(::std::move(arg0), ::std::move(arg1)))
{{ }}
"
)
}]
);
assert_eq!(
blocks.private_methods,
vec![CppFragment::Pair {
header: "explicit MyObject(::rust::CxxQtConstructorArguments0&& args);".to_string(),
source: formatdoc!(
"
MyObject::MyObject(::rust::CxxQtConstructorArguments0&& args)
: BaseClass(::std::move(args.base.arg0), ::std::move(args.base.arg1))
, m_rustObj(::rust::newRs0(::std::move(args.new_)))
, initializer
{{
::rust::initialize0(*this, ::std::move(args.initialize));
}}
"
)
}]
);
}

#[test]
fn multiple_constructors() {
let blocks = generate(
&qobject_for_testing(),
&[
Constructor {
arguments: vec![],
..mock_constructor()
},
Constructor {
arguments: vec![parse_quote! { *mut QObject }],
base_arguments: vec![parse_quote! { *mut QObject }],
..mock_constructor()
},
],
&["initializer".to_string()],
&ParsedCxxMappings::default(),
)
.unwrap();

assert_empty_blocks(&blocks);
assert_eq!(blocks.methods.len(), 2);
assert_eq!(
blocks.methods,
vec![
CppFragment::Pair {
header: "explicit MyObject();".to_string(),
source: formatdoc!(
"
MyObject::MyObject()
: MyObject(::rust::routeArguments0())
{{ }}
"
),
},
CppFragment::Pair {
header: "explicit MyObject(QObject* arg0);".to_string(),
source: formatdoc! {
"
MyObject::MyObject(QObject* arg0)
: MyObject(::rust::routeArguments1(::std::move(arg0)))
{{ }}
"
}
}
]
);
assert_eq!(blocks.private_methods.len(), 2);
assert_eq!(
blocks.private_methods,
vec![
CppFragment::Pair {
header: "explicit MyObject(::rust::CxxQtConstructorArguments0&& args);"
.to_string(),
source: formatdoc!(
"
MyObject::MyObject(::rust::CxxQtConstructorArguments0&& args)
: BaseClass()
, m_rustObj(::rust::newRs0(::std::move(args.new_)))
, initializer
{{
::rust::initialize0(*this, ::std::move(args.initialize));
}}
"
)
},
CppFragment::Pair {
header: "explicit MyObject(::rust::CxxQtConstructorArguments1&& args);"
.to_string(),
source: formatdoc!(
"
MyObject::MyObject(::rust::CxxQtConstructorArguments1&& args)
: BaseClass(::std::move(args.base.arg0))
, m_rustObj(::rust::newRs1(::std::move(args.new_)))
, initializer
{{
::rust::initialize1(*this, ::std::move(args.initialize));
}}
"
)
}
]
);
}
}
1 change: 1 addition & 0 deletions crates/cxx-qt-gen/src/generator/cpp/fragment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0

#[derive(PartialEq, Eq, Debug)]
pub enum CppFragment {
Pair { header: String, source: String },
Header(String),
Expand Down
21 changes: 13 additions & 8 deletions crates/cxx-qt-gen/src/generator/naming/qobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,24 @@ pub struct QObjectName {

impl From<&ParsedQObject> for QObjectName {
fn from(qobject: &ParsedQObject) -> Self {
let ident_left = &qobject.qobject_ty.ident_left;
Self::from_idents(
qobject.qobject_ty.ident_left.clone(),
qobject.qobject_ty.ident_right.clone(),
)
}
}

impl QObjectName {
pub fn from_idents(ident_left: Ident, ident_right: Ident) -> Self {
Self {
ident: ident_left.clone(),
// TODO: later we may support cxx_name or rust_name so keep these are CombinedIdents for now
cpp_class: CombinedIdent::from_ident(ident_left.clone()),
rust_struct: CombinedIdent::from_ident(qobject.qobject_ty.ident_right.clone()),
cxx_qt_thread_class: cxx_qt_thread_class_from_ident(ident_left),
cxx_qt_thread_queued_fn_struct: cxx_qt_thread_queued_fn_struct_from_ident(ident_left),
rust_struct: CombinedIdent::from_ident(ident_right),
cxx_qt_thread_class: cxx_qt_thread_class_from_ident(&ident_left),
cxx_qt_thread_queued_fn_struct: cxx_qt_thread_queued_fn_struct_from_ident(&ident_left),
ident: ident_left,
}
}
}

impl QObjectName {
/// For a given ident generate the mangled threading suffix ident
pub fn cxx_qt_thread_method(&self, suffix: &str) -> Ident {
format_ident!(
Expand Down
Loading

0 comments on commit 26332e1

Please sign in to comment.