diff --git a/gnucash/import-export/test/gmock-Account.cpp b/gnucash/import-export/test/gmock-Account.cpp new file mode 100644 index 0000000000..5152d88662 --- /dev/null +++ b/gnucash/import-export/test/gmock-Account.cpp @@ -0,0 +1,114 @@ +#include + +#include "gmock-Account.h" + + +struct _MockAccountClass +{ + QofInstanceClass parent_class; +}; +typedef struct _MockAccountClass MockAccountClass; + +G_DEFINE_TYPE(MockAccount, gnc_mock_account, QOF_TYPE_INSTANCE); + +static void +gnc_mock_account_init (MockAccount *inst) +{ + // function is unused, initialization is done in the MockAccount's constructor +} + +static void +gnc_mock_account_class_init(MockAccountClass *klass) +{ + // function is unused, class functions are defined in C++ code +} + +void +xaccAccountBeginEdit (Account *account) +{ + g_return_if_fail(GNC_IS_MOCK_ACCOUNT(account)); + ((MockAccount*)account)->beginEdit(); +} + +void +xaccAccountCommitEdit (Account *account) +{ + g_return_if_fail(GNC_IS_MOCK_ACCOUNT(account)); + ((MockAccount*)account)->commitEdit(); +} + +QofBook * +gnc_account_get_book(const Account *account) +{ + g_return_val_if_fail(GNC_IS_MOCK_ACCOUNT(account), NULL); + return ((MockAccount*)account)->getBook(); +} + +gint +xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc, + void *data) +{ + g_return_val_if_fail(GNC_IS_MOCK_ACCOUNT(acc), 0); + return ((MockAccount*)acc)->forEachTransaction(proc, data); +} + +GncImportMatchMap * +gnc_account_imap_create_imap (Account *acc) +{ + g_return_val_if_fail(GNC_IS_MOCK_ACCOUNT(acc), NULL); + return ((MockAccount*)acc)->imapCreateImap(); +} + +Account* +gnc_account_imap_find_account ( + GncImportMatchMap *imap, + const char* category, + const char *key) +{ + return ((GncMockImportMatchMap*)imap)->findAccount(category, key); +} + +void +gnc_account_imap_add_account ( + GncImportMatchMap *imap, + const char *category, + const char *key, + Account *acc) +{ + // not used at the moment + ((GncMockImportMatchMap*)imap)->addAccount(category, key, acc); +} + +Account* +gnc_account_imap_find_account_bayes ( + GncImportMatchMap *imap, + GList *tokens) +{ + // \todo use std::list instead of std::vector, since GList is a double-linked list like std::list + std::vector tokenVec; + + for (auto token = tokens; token; token = token->next) + { + tokenVec.push_back(static_cast (token->data)); + } + + return ((GncMockImportMatchMap*)imap)->findAccountBayes(tokenVec); +} + +void +gnc_account_imap_add_account_bayes ( + GncImportMatchMap *imap, + GList *tokens, + Account *acc) +{ + // \todo use std::list instead of std::vector, since GList is a double-linked list like std::list + std::vector tokenVec; + + for (auto token = tokens; token; token = token->next) + { + tokenVec.push_back(static_cast (token->data)); + } + + ((GncMockImportMatchMap*)imap)->addAccountBayes(tokenVec, acc); +} + diff --git a/gnucash/import-export/test/gmock-Account.h b/gnucash/import-export/test/gmock-Account.h new file mode 100644 index 0000000000..2a39efe6fa --- /dev/null +++ b/gnucash/import-export/test/gmock-Account.h @@ -0,0 +1,69 @@ +#ifndef GMOCK_ACCOUNT_H +#define GMOCK_ACCOUNT_H + +#include + +#include +#include + +#include "gmock-qofbook.h" +#include "gmock-gobject.h" + + +GType gnc_mock_account_get_type(void); + +#define GNC_TYPE_MOCK_ACCOUNT (gnc_mock_account_get_type ()) +#define GNC_IS_MOCK_ACCOUNT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNC_TYPE_MOCK_ACCOUNT)) + + +// mock up for Account +class MockAccount : public Account +{ +public: + MockAccount() {} + void* operator new(size_t size) + { + return mock_g_object_new (GNC_TYPE_MOCK_ACCOUNT, NULL, size); + } + + // define separate free() function since destructor is protected + void free() + { + delete this; + } + void operator delete(void* acc, size_t size) + { + mock_g_object_unref(acc, size); + } + + MOCK_METHOD0(beginEdit, void()); + MOCK_METHOD0(commitEdit, void()); + MOCK_METHOD0(getBook, QofMockBook*()); + MOCK_METHOD2(forEachTransaction, gint(TransactionCallback, void*)); + MOCK_METHOD0(imapCreateImap, GncImportMatchMap*()); + +protected: + // Protect destructor to avoid MockAccount objects to be created on stack. MockAccount + // objects can only be dynamically created, since they are derived from GObject. + ~MockAccount() {} +}; + + +class GncMockImportMatchMap : public GncImportMatchMap +{ +public: + GncMockImportMatchMap(MockAccount* account) + { + g_return_if_fail(GNC_IS_MOCK_ACCOUNT(account)); + + acc = account; + book = account->getBook(); + }; + + MOCK_METHOD2(findAccount, Account *(const char*, const char*)); + MOCK_METHOD3(addAccount, void(const char*, const char*, Account*)); + MOCK_METHOD1(findAccountBayes, Account *(std::vector)); + MOCK_METHOD2(addAccountBayes, void(std::vector, Account*)); +}; + +#endif diff --git a/gnucash/import-export/test/gmock-Split.cpp b/gnucash/import-export/test/gmock-Split.cpp new file mode 100644 index 0000000000..c6ec5ecb79 --- /dev/null +++ b/gnucash/import-export/test/gmock-Split.cpp @@ -0,0 +1,142 @@ +#include + +#include "gmock-Split.h" +#include "gmock-qofbook.h" +#include "gmock-Account.h" +#include "gmock-Transaction.h" + + +struct _MockSplitClass +{ + QofInstanceClass parent_class; +}; +typedef struct _MockSplitClass MockSplitClass; + +G_DEFINE_TYPE(MockSplit, gnc_mock_split, QOF_TYPE_INSTANCE); + +static void +gnc_mock_split_init (MockSplit *inst) +{ + // function is unused since it's overwritten by MockSplit's constructor anyway +} + +static void +gnc_mock_split_class_init (MockSplitClass *klass) +{ + // function is unused, class functions are defined in C++ code +} + + +Split * +xaccMallocSplit (QofBook *book) +{ + g_return_val_if_fail(QOF_IS_MOCK_BOOK(book), NULL); + return ((QofMockBook*)book)->mallocSplit(); +} + +QofBook * +xaccSplitGetBook (const Split *split) +{ + g_return_val_if_fail(GNC_IS_MOCK_SPLIT(split), NULL); + return ((MockSplit*)split)->getBook(); +} + +Account * +xaccSplitGetAccount (const Split *split) +{ + g_return_val_if_fail(GNC_IS_MOCK_SPLIT(split), NULL); + return ((MockSplit*)split)->getAccount(); +} + +void +xaccSplitSetAccount (Split *split, Account *acc) +{ + g_return_if_fail(GNC_IS_MOCK_SPLIT(split)); + g_return_if_fail(GNC_IS_MOCK_ACCOUNT(acc)); + ((MockSplit*)split)->setAccount(acc); +} + +gnc_numeric +xaccSplitGetAmount (const Split *split) +{ + g_return_val_if_fail(GNC_IS_MOCK_SPLIT(split), gnc_numeric_zero()); + return ((MockSplit*)split)->getAmount(); +} + +void +xaccSplitSetAmount (Split *split, gnc_numeric amt) +{ + g_return_if_fail(GNC_IS_MOCK_SPLIT(split)); + ((MockSplit*)split)->setAmount(amt); +} + +gnc_numeric +xaccSplitGetValue (const Split *split) +{ + g_return_val_if_fail(GNC_IS_MOCK_SPLIT(split), gnc_numeric_zero()); + return ((MockSplit*)split)->getValue(); +} + +void +xaccSplitSetValue (Split *split, gnc_numeric val) +{ + g_return_if_fail(GNC_IS_MOCK_SPLIT(split)); + ((MockSplit*)split)->setValue(val); +} + +const char * +xaccSplitGetMemo (const Split *split) +{ + g_return_val_if_fail(GNC_IS_MOCK_SPLIT(split), NULL); + return ((MockSplit*)split)->getMemo(); +} + +char +xaccSplitGetReconcile (const Split *split) +{ + g_return_val_if_fail(GNC_IS_MOCK_SPLIT(split), VREC); + return ((MockSplit*)split)->getReconcile(); +} + +void +xaccSplitSetReconcile (Split *split, char recn) +{ + g_return_if_fail(GNC_IS_MOCK_SPLIT(split)); + ((MockSplit*)split)->setReconcile(recn); +} + +void +xaccSplitSetDateReconciledSecs (Split *split, time64 secs) +{ + g_return_if_fail(GNC_IS_MOCK_SPLIT(split)); + ((MockSplit*)split)->setDateReconciledSecs(secs); +} + +const char * +xaccSplitGetAction (const Split *split) +{ + g_return_val_if_fail(GNC_IS_MOCK_SPLIT(split), NULL); + return ((MockSplit*)split)->getAction(); +} + +Split * +xaccSplitGetOtherSplit (const Split *split) +{ + g_return_val_if_fail(GNC_IS_MOCK_SPLIT(split), NULL); + return ((MockSplit*)split)->getOtherSplit(); +} + +Transaction * +xaccSplitGetParent (const Split *split) +{ + g_return_val_if_fail(GNC_IS_MOCK_SPLIT(split), NULL); + return ((MockSplit*)split)->getParent(); +} + +void +xaccSplitSetParent(Split *split, Transaction *trans) +{ + g_return_if_fail(GNC_IS_MOCK_SPLIT(split)); + g_return_if_fail(GNC_IS_MOCK_TRANSACTION(trans)); + ((MockSplit*)split)->setParent(trans); +} diff --git a/gnucash/import-export/test/gmock-Split.h b/gnucash/import-export/test/gmock-Split.h new file mode 100644 index 0000000000..569d2fce10 --- /dev/null +++ b/gnucash/import-export/test/gmock-Split.h @@ -0,0 +1,85 @@ +#ifndef GMOCK_SPLIT_H +#define GMOCK_SPLIT_H + +#include + +#include +#include + +#include "gmock-qofbook.h" +#include "gmock-gobject.h" + + +GType gnc_mock_split_get_type(void); + +#define GNC_TYPE_MOCK_SPLIT (gnc_mock_split_get_type ()) +#define GNC_IS_MOCK_SPLIT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNC_TYPE_MOCK_SPLIT)) + + +// mock up for Split +class MockSplit : public Split +{ +public: + MockSplit() + { + acc = nullptr; + orig_acc = nullptr; + parent = nullptr; + orig_parent = nullptr; + lot = nullptr; + + action = nullptr; + memo = nullptr; + reconciled = VREC; + amount = gnc_numeric_zero(); + value = gnc_numeric_zero(); + + date_reconciled = 0; + + balance = gnc_numeric_zero(); + cleared_balance = gnc_numeric_zero(); + reconciled_balance = gnc_numeric_zero(); + noclosing_balance = gnc_numeric_zero(); + + gains = GAINS_STATUS_UNKNOWN; + gains_split = nullptr; + } + void* operator new(size_t size) + { + return mock_g_object_new (GNC_TYPE_MOCK_SPLIT, NULL, size); + } + + // define separate free() function since destructor is protected + void free() + { + delete this; + } + void operator delete(void* split, size_t size) + { + mock_g_object_unref(split, size); + } + + MOCK_METHOD0(init, void()); + MOCK_METHOD0(getBook, QofBook *()); + MOCK_METHOD0(getAccount, Account *()); + MOCK_METHOD1(setAccount, void(Account*)); + MOCK_METHOD0(getAmount, gnc_numeric()); + MOCK_METHOD1(setAmount, void(gnc_numeric)); + MOCK_METHOD0(getValue, gnc_numeric()); + MOCK_METHOD1(setValue, void(gnc_numeric)); + MOCK_METHOD0(getMemo, const char *()); + MOCK_METHOD0(getReconcile, char()); + MOCK_METHOD1(setReconcile, void(char)); + MOCK_METHOD1(setDateReconciledSecs, void(time64)); + MOCK_METHOD0(getAction, const char *()); + MOCK_METHOD0(getOtherSplit, Split *()); + MOCK_METHOD0(getParent, Transaction *()); + MOCK_METHOD1(setParent, void(Transaction*)); + +protected: + // Protect destructor to avoid MockSplit objects to be created on stack. MockSplit + // objects can only be dynamically created, since they are derived from GObject. + ~MockSplit() {} +}; + +#endif diff --git a/gnucash/import-export/test/gmock-Transaction.cpp b/gnucash/import-export/test/gmock-Transaction.cpp new file mode 100644 index 0000000000..dd02f6ea80 --- /dev/null +++ b/gnucash/import-export/test/gmock-Transaction.cpp @@ -0,0 +1,125 @@ +#include + +#include "gmock-Transaction.h" +#include "gmock-Account.h" + + +struct _MockTransactionClass +{ + QofInstanceClass parent_class; +}; +typedef struct _MockTransactionClass MockTransactionClass; + +G_DEFINE_TYPE(MockTransaction, gnc_mock_transaction, QOF_TYPE_INSTANCE); + +static void +gnc_mock_transaction_init (MockTransaction *inst) +{ + // function is unused, initialization is done in the MockTransaction's constructor +} + +static void +gnc_mock_transaction_class_init(MockTransactionClass *klass) +{ + // function is unused, class functions are defined in C++ code +} + + +void +xaccTransBeginEdit (Transaction *trans) +{ + g_return_if_fail(GNC_IS_MOCK_TRANSACTION(trans)); + ((MockTransaction*)trans)->beginEdit(); +} + +void +xaccTransCommitEdit (Transaction *trans) +{ + g_return_if_fail(GNC_IS_MOCK_TRANSACTION(trans)); + ((MockTransaction*)trans)->commitEdit(); +} + +Split * +xaccTransGetSplit (const Transaction *trans, int i) +{ + g_return_val_if_fail(GNC_IS_MOCK_TRANSACTION(trans), NULL); + return ((MockTransaction*)trans)->getSplit(i); +} + +Split * +xaccTransFindSplitByAccount(const Transaction *trans, const Account *acc) +{ + g_return_val_if_fail(GNC_IS_MOCK_TRANSACTION(trans), NULL); + g_return_val_if_fail(GNC_IS_MOCK_ACCOUNT(acc), NULL); + return ((MockTransaction*)trans)->findSplitByAccount(acc); +} + +time64 +xaccTransGetDate (const Transaction *trans) +{ + g_return_val_if_fail(GNC_IS_MOCK_TRANSACTION(trans), 0); + return ((MockTransaction*)trans)->getDate(); +} + +void +xaccTransSetDatePostedSecsNormalized (Transaction *trans, time64 time) +{ + g_return_if_fail(GNC_IS_MOCK_TRANSACTION(trans)); + ((MockTransaction*)trans)->setDatePostedSecsNormalized(time); +} + +const char * +xaccTransGetDescription (const Transaction *trans) +{ + g_return_val_if_fail(GNC_IS_MOCK_TRANSACTION(trans), NULL); + return ((MockTransaction*)trans)->getDescription(); +} + +void +xaccTransSetDescription (Transaction *trans, const char *desc) +{ + g_return_if_fail(GNC_IS_MOCK_TRANSACTION(trans)); + ((MockTransaction*)trans)->setDescription(desc); +} + +const char * +xaccTransGetNotes (const Transaction *trans) +{ + g_return_val_if_fail(GNC_IS_MOCK_TRANSACTION(trans), NULL); + return ((MockTransaction*)trans)->getNotes(); +} + +void +xaccTransSetNotes (Transaction *trans, const char *notes) +{ + g_return_if_fail(GNC_IS_MOCK_TRANSACTION(trans)); + ((MockTransaction*)trans)->setDescription(notes); +} + +gnc_numeric +xaccTransGetImbalanceValue (const Transaction * trans) +{ + g_return_val_if_fail(GNC_IS_MOCK_TRANSACTION(trans), gnc_numeric_zero()); + return ((MockTransaction*)trans)->getImbalanceValue(); +} + +const char * +xaccTransGetNum (const Transaction *trans) +{ + g_return_val_if_fail(GNC_IS_MOCK_TRANSACTION(trans), NULL); + return ((MockTransaction*)trans)->getNum(); +} + +gboolean +xaccTransIsOpen (const Transaction *trans) +{ + g_return_val_if_fail(GNC_IS_MOCK_TRANSACTION(trans), FALSE); + return ((MockTransaction*)trans)->isOpen(); +} + +void +xaccTransDestroy (Transaction *trans) +{ + g_return_if_fail(GNC_IS_MOCK_TRANSACTION(trans)); + ((MockTransaction*)trans)->destroy(); +} diff --git a/gnucash/import-export/test/gmock-Transaction.h b/gnucash/import-export/test/gmock-Transaction.h new file mode 100644 index 0000000000..0790a7c03f --- /dev/null +++ b/gnucash/import-export/test/gmock-Transaction.h @@ -0,0 +1,73 @@ +#ifndef GMOCK_TRANSACTION_H +#define GMOCK_TRANSACTION_H + +#include + +#include +#include + +#include "gmock-qofbook.h" +#include "gmock-gobject.h" + + +GType gnc_mock_transaction_get_type(void); + +#define GNC_TYPE_MOCK_TRANSACTION (gnc_mock_transaction_get_type ()) +#define GNC_IS_MOCK_TRANSACTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNC_TYPE_MOCK_TRANSACTION)) + + +// mock up for Transaction +class MockTransaction : public Transaction +{ +public: + MockTransaction() + { + num = nullptr; + description = nullptr; + common_currency = nullptr; + splits = nullptr; + date_entered = 0; + date_posted = 0; + marker = 0; + orig = nullptr; + readonly_reason = nullptr; + reason_cache_valid = FALSE; + isClosingTxn_cached = -1; + } + void* operator new(size_t size) + { + return mock_g_object_new (GNC_TYPE_MOCK_TRANSACTION, NULL, size); + } + + // define separate free() function since destructor is protected + void free() + { + delete this; + } + void operator delete(void* trans, size_t size) + { + mock_g_object_unref(trans, size); + } + + MOCK_METHOD0(beginEdit, void()); + MOCK_METHOD0(commitEdit, void()); + MOCK_METHOD1(getSplit, Split *(int)); + MOCK_METHOD1(findSplitByAccount, Split *(const Account*)); + MOCK_METHOD0(getDate, time64()); + MOCK_METHOD1(setDatePostedSecsNormalized, void(time64)); + MOCK_METHOD0(getDescription, const char *()); + MOCK_METHOD1(setDescription, void(const char*)); + MOCK_METHOD0(getNotes, const char *()); + MOCK_METHOD1(setNotes, void(const char*)); + MOCK_METHOD0(getImbalanceValue, gnc_numeric()); + MOCK_METHOD0(getNum, const char *()); + MOCK_METHOD0(isOpen, gboolean()); + MOCK_METHOD0(destroy, void()); + +protected: + // Protect destructor to avoid MockTransaction objects to be created on stack. MockTransaction + // objects can only be dynamically created, since they are derived from GObject. + ~MockTransaction() {} +}; + +#endif diff --git a/gnucash/import-export/test/gmock-gobject.h b/gnucash/import-export/test/gmock-gobject.h new file mode 100644 index 0000000000..fa09c8dcf0 --- /dev/null +++ b/gnucash/import-export/test/gmock-gobject.h @@ -0,0 +1,27 @@ +#ifndef GMOCK_GOBJECT_H +#define GMOCK_GOBJECT_H + +#include + +static gpointer +mock_g_object_new (GType object_type, const gchar *first_property_name, size_t size) +{ + GTypeQuery query; + + g_type_query(object_type, &query); + g_assert(size == query.instance_size); + return g_object_new (object_type, first_property_name); +} + +static void +mock_g_object_unref (gpointer object, size_t size) +{ + GType object_type = G_OBJECT_TYPE(object); + GTypeQuery query; + + g_type_query(object_type, &query); + g_assert(size == query.instance_size); + g_object_unref(object); +} + +#endif diff --git a/gnucash/import-export/test/gmock-qofbook.cpp b/gnucash/import-export/test/gmock-qofbook.cpp new file mode 100644 index 0000000000..7c3c6d7c7b --- /dev/null +++ b/gnucash/import-export/test/gmock-qofbook.cpp @@ -0,0 +1,29 @@ +#include "gmock-qofbook.h" + +struct _QofMockBookClass +{ + QofInstanceClass parent_class; +}; +typedef struct _QofMockBookClass QofMockBookClass; + +G_DEFINE_TYPE(QofMockBook, qof_mock_book, QOF_TYPE_INSTANCE); + +static void +qof_mock_book_init (QofMockBook *inst) +{ + // function is unused, initialization is done in the QofMockBook's constructor +} + +static void +qof_mock_book_class_init(QofMockBookClass *klass) +{ + // function is unused, class functions are defined in C++ code +} + +gboolean +qof_book_use_split_action_for_num_field (const QofBook *book) +{ + g_return_val_if_fail(QOF_IS_MOCK_BOOK(book), FALSE); + return ((QofMockBook*)book)->useSplitActionForNumField(); +} + diff --git a/gnucash/import-export/test/gmock-qofbook.h b/gnucash/import-export/test/gmock-qofbook.h new file mode 100644 index 0000000000..e4d4536b3c --- /dev/null +++ b/gnucash/import-export/test/gmock-qofbook.h @@ -0,0 +1,62 @@ +#ifndef GMOCK_QOFBOOK_H +#define GMOCK_QOFBOOK_H + +#include + +#include +#include + +#include "gmock-gobject.h" +#include "gmock-Split.h" + + +GType qof_mock_book_get_type(void); + +#define QOF_TYPE_MOCK_BOOK (qof_mock_book_get_type ()) +#define QOF_IS_MOCK_BOOK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), QOF_TYPE_MOCK_BOOK)) + + +// mock up for QofBook +class QofMockBook : public QofBook +{ +public: + QofMockBook() + { + hash_of_collections = nullptr; + data_tables = nullptr; + data_table_finalizers = nullptr; + + book_open = 'n'; + read_only = TRUE; + session_dirty = FALSE; + + version = 0; + + cached_num_field_source_isvalid = FALSE; + cached_num_days_autoreadonly_isvalid = FALSE; + } + void* operator new(size_t size) + { + return mock_g_object_new (QOF_TYPE_MOCK_BOOK, NULL, size); + } + + // define separate free() function since destructor is protected + void free() + { + delete this; + } + void operator delete(void* book, size_t size) + { + mock_g_object_unref(book, size); + } + + MOCK_METHOD0(mallocSplit, Split *()); + MOCK_METHOD0(useSplitActionForNumField, gboolean()); + +protected: + // Protect destructor to avoid MockQofBook objects to be created on stack. MockQofBook + // objects can only be dynamically created, since they are derived from GObject. + ~QofMockBook() {} +}; + +#endif diff --git a/gnucash/import-export/test/gmock-qofinstance.cpp b/gnucash/import-export/test/gmock-qofinstance.cpp new file mode 100644 index 0000000000..9669ef4af9 --- /dev/null +++ b/gnucash/import-export/test/gmock-qofinstance.cpp @@ -0,0 +1,48 @@ +#include + +extern "C" +{ +#include +} +#include + + +G_DEFINE_TYPE(QofInstance, qof_instance, G_TYPE_OBJECT); + +static void +qof_instance_init (QofInstance *inst) +{ + // function is unused, initialization is done in the constructor of the derived mock class +} + +static void +qof_instance_class_init(QofInstanceClass *klass) +{ + // function is unused, class functions are defined in C++ code +} + +// This is a reimplementation of the function from qofinstance.cpp +void +qof_instance_get (const QofInstance *inst, const gchar *first_prop, ...) +{ + va_list ap; + g_return_if_fail (QOF_IS_INSTANCE (inst)); + + va_start (ap, first_prop); + g_object_get_valist (G_OBJECT (inst), first_prop, ap); + va_end (ap); +} + +// This is a reimplementation of the function from qofinstance.cpp +// with calling qof_instance_set_dirty() +void +qof_instance_set (QofInstance *inst, const gchar *first_prop, ...) +{ + va_list ap; + g_return_if_fail (QOF_IS_INSTANCE (inst)); + + va_start (ap, first_prop); + g_object_set_valist (G_OBJECT (inst), first_prop, ap); + va_end (ap); +} +