/******************************************************************** * utest-Transaction.c: GLib g_test test suite for Transaction.c. * * Copyright 2012 John Ralls * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License* * along with this program; if not, you can retrieve it from * * https://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * or contact: * * * * Free Software Foundation Voice: +1-617-542-5942 * * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * ********************************************************************/ #include extern "C" { #include #include #include /* Add specific headers for this class */ #include "../Transaction.h" #include "../TransactionP.h" #include "../Split.h" #include "../Account.h" #include "../gnc-lot.h" #include "../gnc-event.h" #include #if defined(__clang__) && (__clang_major__ == 5 || (__clang_major__ == 3 && __clang_minor__ < 5)) #define USE_CLANG_FUNC_SIG 1 #endif static const gchar *suitename = "/engine/Transaction"; void test_suite_transaction ( void ); } #include #include /* Copied from Transaction.c. Changing these values will break * existing databases, which is a good reason to fail a test. */ static const char *trans_notes_str = "notes"; static const char *void_reason_str = "void-reason"; static const char *void_time_str = "void-time"; static const char *void_former_notes_str = "void-former-notes"; const char *trans_is_closing_str = "book_closing"; #define TRANS_DATE_DUE_KVP "trans-date-due" #define TRANS_TXN_TYPE_KVP "trans-txn-type" #define TRANS_READ_ONLY_REASON "trans-read-only" #define TRANS_REVERSED_BY "reversed-by" #define ISO_DATELENGTH 32 /* length of an iso 8601 date string. */ typedef struct { Transaction *txn; Account *acc1; Account *acc2; gnc_commodity *curr; gnc_commodity *comm; TransTestFunctions *func; GSList *hdlrs; } Fixture; typedef struct { Fixture base; Transaction *gains_txn; Account *gains_acc; } GainsFixture; class TransMockBackend : public QofBackend { public: TransMockBackend() : QofBackend(), m_last_call{"Constructor"}, m_result_err{ERR_BACKEND_NO_ERR} {} void session_begin(QofSession*, const char*, SessionOpenMode) override { m_last_call = "session_begin"; } void session_end() override { m_last_call = "session_end"; } void load(QofBook*, QofBackendLoadType) override { m_last_call = "load"; } void sync(QofBook*) override { m_last_call = "sync"; } void safe_sync(QofBook*) override { m_last_call = "safe_sync"; } void rollback(QofInstance*) override { set_error(m_result_err); m_last_call = "rollback"; } void inject_error(QofBackendError err) { m_result_err = err; } std::string m_last_call; private: QofBackendError m_result_err; }; static void setup (Fixture *fixture, gconstpointer pData) { QofBook *book = qof_book_new (); TransMockBackend *mbe = new TransMockBackend; Transaction *txn; time64 entered = gnc_dmy2time64 (20, 4, 2012); time64 posted = gnc_dmy2time64 (21, 4, 2012); auto frame = new KvpFrame (); qof_book_set_backend (book, mbe); auto split1 = xaccMallocSplit (book); auto split2 = xaccMallocSplit (book); txn = xaccMallocTransaction (book); fixture->txn = txn; fixture->curr = gnc_commodity_new (book, "Gnu Rand", "CURRENCY", "GNR", "", 240); fixture->comm = gnc_commodity_new (book, "Wildebeest Fund", "FUND", "WBFXX", "", 1000); fixture->acc1 = xaccMallocAccount (book); fixture->acc2 = xaccMallocAccount (book); xaccAccountSetCommodity (fixture->acc1, fixture->comm); xaccAccountSetCommodity (fixture->acc2, fixture->curr); txn->date_posted = posted; txn->date_entered = entered; split1->memo = CACHE_INSERT ("foo"); split1->action = CACHE_INSERT ("bar"); split1->amount = gnc_numeric_create (100000, 1000); split1->value = gnc_numeric_create (3200, 240); split2->amount = gnc_numeric_create (-3200, 240); split2->value = gnc_numeric_create (-3200, 240); split1->acc = fixture->acc1; split2->acc = fixture->acc2; txn->num = CACHE_INSERT ("123"); txn->description = CACHE_INSERT ("Waldo Pepper"); xaccTransBeginEdit (txn); { xaccTransSetCurrency (txn, fixture->curr); xaccSplitSetParent (split1, txn); xaccSplitSetParent (split2, txn); frame->set({trans_notes_str}, new KvpValue(g_strdup ("Salt pork sausage"))); frame->set_path({"qux", "quux", "corge"}, new KvpValue(123.456)); qof_instance_set_slots (QOF_INSTANCE (txn), frame); } xaccTransCommitEdit (txn); xaccAccountSortSplits(fixture->acc1, FALSE); xaccAccountSortSplits(fixture->acc2, FALSE); xaccAccountRecomputeBalance(fixture->acc1); xaccAccountRecomputeBalance(fixture->acc2); qof_instance_mark_clean (QOF_INSTANCE (split1)); qof_instance_mark_clean (QOF_INSTANCE (split2)); qof_instance_mark_clean (QOF_INSTANCE (txn)); fixture->func = _utest_trans_fill_functions(); fixture->hdlrs = NULL; } static void setup_with_gains (GainsFixture *fixture, gconstpointer pData) { QofBook *book; Fixture *base = &(fixture->base); setup (base, NULL); book = qof_instance_get_book (QOF_INSTANCE (base->txn)); fixture->gains_txn = xaccMallocTransaction (book); fixture->gains_acc = xaccMallocAccount (book); xaccAccountSetCommodity (fixture->gains_acc, base->curr); auto gains_split1 = xaccMallocSplit (book); auto gains_split2 = xaccMallocSplit (book); gains_split1->acc = base->acc1; gains_split2->acc = fixture->gains_acc; gains_split1->amount = gnc_numeric_create (30, 240); gains_split1->value = gnc_numeric_create (30, 240); gains_split2->amount = gnc_numeric_create (-30, 240); gains_split2->value = gnc_numeric_create (-30, 240); xaccTransBeginEdit (fixture->gains_txn); { xaccTransSetCurrency (fixture->gains_txn, base->curr); xaccSplitSetParent (gains_split1, fixture->gains_txn); xaccSplitSetParent (gains_split2, fixture->gains_txn); } xaccTransCommitEdit (fixture->gains_txn); auto base_split = static_cast(g_list_nth_data (base->txn->splits, 1)); base_split->gains_split = gains_split1; } /* Add a log handler to the handlers list to be cleared at teardown */ static void teardown (Fixture *fixture, gconstpointer pData) { QofBook *book = qof_instance_get_book (QOF_INSTANCE (fixture->txn)); auto mbe = static_cast(qof_book_get_backend (book)); test_destroy (fixture->txn); test_destroy (fixture->acc1); test_destroy (fixture->acc2); test_destroy (fixture->curr); test_destroy (fixture->comm); delete mbe; qof_book_destroy(book); g_slist_free_full (fixture->hdlrs, test_free_log_handler); test_clear_error_list(); } static void teardown_with_gains (GainsFixture *fixture, gconstpointer pData) { Fixture *base = &(fixture->base); test_destroy (fixture->gains_acc); teardown (base, NULL); } /* check_open void check_open (const Transaction *trans)// Local: 1:0:0 */ static void test_check_open (Fixture *fixture, gconstpointer pData) { auto msg = g_strdup_printf ("[check_open()] transaction %p not open for editing", fixture->txn); auto loglevel = static_cast(G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL); auto check = test_error_struct_new ("gnc.engine", loglevel, msg); g_free (msg); fixture->hdlrs = test_log_set_fatal_handler (fixture->hdlrs, check, (GLogFunc)test_checked_handler); check_open (fixture->txn); g_assert_cmpint (check->hits, ==, 1); xaccTransBeginEdit (fixture->txn); check_open (fixture->txn); g_assert_cmpint (check->hits, ==, 1); /* Don't commit the edit, there's nothing to balance! */ xaccTransRollbackEdit (fixture->txn); } /* xaccTransStillHasSplit gboolean xaccTransStillHasSplit(const Transaction *trans, const Split *s)// C: 8 in 3 Local: 7:0:0 */ static void test_xaccTransStillHasSplit (Fixture *fixture, gconstpointer pData) { QofBook *book = qof_instance_get_book (QOF_INSTANCE (fixture->txn)); auto split = xaccMallocSplit (book); g_assert (!xaccTransStillHasSplit (fixture->txn, split)); xaccSplitSetParent (split, fixture->txn); g_assert (xaccTransStillHasSplit (fixture->txn, split)); qof_instance_set_destroying (split, TRUE); g_assert (!xaccTransStillHasSplit (fixture->txn, split)); qof_instance_set_destroying (split, FALSE); g_assert (xaccTransStillHasSplit (fixture->txn, split)); xaccSplitSetParent (split, NULL); g_assert (!xaccTransStillHasSplit (fixture->txn, split)); test_destroy (split); } // Make Static /* mark_trans void mark_trans (Transaction *trans)// Local: 3:0:0 */ #define check_split_dirty(xsplit, test) \ { \ gboolean sort_dirty, balance_dirty; \ auto split = xsplit; \ g_object_get (split->acc, \ "sort-dirty", &sort_dirty, \ "balance-dirty", &balance_dirty, \ NULL); \ g_assert_cmpint (sort_dirty, ==, test); \ g_assert_cmpint (balance_dirty, ==, test); \ } static void test_mark_trans (Fixture *fixture, gconstpointer pData) { gboolean dirty_split = FALSE; GList *splits = NULL; for (splits = (fixture->txn)->splits; splits; splits = splits->next) { if (!splits->data) continue; g_assert (!qof_instance_get_dirty_flag (splits->data)); check_split_dirty (static_cast(splits->data), FALSE); } fixture->func->mark_trans (fixture->txn); g_assert (!qof_instance_get_dirty_flag (fixture->txn)); for (splits = (fixture->txn)->splits; splits; splits = splits->next) { if (!splits->data) continue; g_assert (!qof_instance_get_dirty_flag (splits->data)); check_split_dirty (static_cast(splits->data), TRUE); } } /* gen_event_trans void gen_event_trans (Transaction *trans)// Local: 2:0:0 */ static void test_gen_event_trans (Fixture *fixture, gconstpointer pData) { auto split = static_cast(fixture->txn->splits->data); GNCLot *lot = gnc_lot_new (qof_instance_get_book (QOF_INSTANCE (fixture->txn))); TestSignal sig1 = test_signal_new (QOF_INSTANCE (fixture->acc1), GNC_EVENT_ITEM_CHANGED, split); TestSignal sig2 = test_signal_new (QOF_INSTANCE (lot), QOF_EVENT_MODIFY, NULL); gnc_lot_add_split (lot, split); test_signal_assert_hits (sig1, 1); test_signal_assert_hits (sig2, 3); fixture->func->gen_event_trans (fixture->txn); test_signal_assert_hits (sig1, 2); test_signal_assert_hits (sig2, 4); test_signal_free (sig1); test_signal_free (sig2); test_destroy (lot); } /* gnc_transaction_init G_DEFINE_TYPE(Transaction, gnc_transaction, QOF_TYPE_INSTANCE) static void gnc_transaction_init(Transaction* trans)*/ static void test_gnc_transaction_init () { auto txn = static_cast(g_object_new (GNC_TYPE_TRANSACTION, NULL)); g_assert_cmpstr (txn->num, ==, ""); g_assert_cmpstr (txn->description, ==, ""); g_assert (txn->common_currency == NULL); g_assert (txn->splits == NULL); g_assert_cmpint (txn->date_entered, ==, 0); g_assert_cmpint (txn->date_posted, ==, 0); g_assert_cmpint (txn->marker, ==, 0); g_assert (txn->orig == NULL); test_destroy (txn); } /* gnc_transaction_dispose static void gnc_transaction_dispose(GObject *txnp)*/ static void test_gnc_transaction_dispose () { QofBook *book = qof_book_new (); auto txn = static_cast(g_object_new (GNC_TYPE_TRANSACTION, "book", book, NULL)); auto split = static_cast(g_object_new (GNC_TYPE_SPLIT, "book", book, NULL)); auto s_ref = split; gnc_commodity *curr = gnc_commodity_new (book, "Gnu Rand", "CURRENCY", "GNR", "", 240), *t_curr = NULL; gnc_commodity *c_ref = curr; g_object_add_weak_pointer (G_OBJECT (split), (gpointer*) &s_ref); g_object_add_weak_pointer (G_OBJECT (curr), (gpointer*) &c_ref); txn->splits = g_list_append (txn->splits, split); txn->common_currency = curr; g_assert (txn->splits != NULL); g_assert (s_ref != NULL); g_assert (c_ref != NULL); g_object_run_dispose (G_OBJECT (txn)); /* If gnc_transaction_dispose was written correctly, txn->splits and * txn->curr would be null and all of the splits would be destroyed, * so all of these would be equal instead of unequal. */ g_assert (txn->splits != NULL); g_assert (txn->common_currency != NULL); g_assert (s_ref != NULL); g_assert (c_ref != NULL); /* And these would be unnecessary -- in fact, they would assert */ test_destroy (split); test_destroy (curr); test_destroy (txn); qof_book_destroy (book); } /* gnc_transaction_finalize static void gnc_transaction_finalize(GObject* txnp)*/ static void test_gnc_transaction_finalize () { auto txn = static_cast(g_object_new (GNC_TYPE_TRANSACTION, NULL)); test_destroy (txn); } /* gnc_transaction_get_property * gnc_transaction_set_property static void gnc_transaction_set_property(GObject* object,*/ static void test_gnc_transaction_set_get_property (Fixture *fixture, gconstpointer pData) { QofBook *book = qof_book_new (); auto txn = static_cast(g_object_new (GNC_TYPE_TRANSACTION, "book", book, NULL)); auto num = "42", desc = "The Answer"; gchar *t_num = NULL, *t_desc = NULL; gnc_commodity *curr = gnc_commodity_new (book, "Gnu Rand", "CURRENCY", "GNR", "", 240), *t_curr = NULL; time64 now = gnc_time(NULL); Time64 *t_entered, *t_posted; g_assert_cmpstr (txn->num, ==, ""); g_assert_cmpstr (txn->description, ==, ""); g_assert (txn->common_currency == NULL); g_assert_cmpint (txn->date_entered, ==, 0); g_assert_cmpint (txn->date_posted, ==, 0); /* Kick up the edit counter to keep from committing */ xaccTransBeginEdit (txn); g_object_set (G_OBJECT (txn), "num", num, "description", desc, "currency", curr, "post-date", &now, "enter-date", &now, NULL); g_assert_cmpstr (txn->num, ==, num); g_assert_cmpstr (txn->description, ==, desc); g_assert (txn->common_currency == curr); g_assert (txn->date_entered == now); g_assert (txn->date_posted == now); g_object_get (G_OBJECT (txn), "num", &t_num, "description", &t_desc, "currency", &t_curr, "post-date", &t_posted, "enter-date", &t_entered, NULL); g_assert_cmpstr (t_num, ==, num); g_assert_cmpstr (t_desc, ==, desc); g_assert (t_curr == curr); g_assert_cmpint (t_entered->t, ==, now); g_assert_cmpint (t_posted->t, ==, now); xaccTransRollbackEdit (txn); test_destroy (txn); test_destroy (curr); qof_book_destroy (book); } /* gnc_transaction_class_init * xaccInitTransaction No way to really test class_init directly -- though the above tests cover everything pretty well indirectly. xaccInitTransaction is a useless one-line function that sets the book in the parent QofInstance. */ static void test_xaccMallocTransaction (Fixture *fixture, gconstpointer pData) { QofBook *book = qof_book_new (); TestSignal sig1 = test_signal_new (NULL, QOF_EVENT_CREATE,NULL); Transaction *txn; #ifdef USE_CLANG_FUNC_SIG #define _func "Transaction *xaccMallocTransaction(QofBook *)" #else #define _func "xaccMallocTransaction" #endif auto msg = _func ": assertion 'book' failed"; #undef _func auto logdomain = "gnc.engine"; auto loglevel = static_cast(G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL); auto check = test_error_struct_new ("gnc.engine", loglevel, msg); fixture->hdlrs = test_log_set_fatal_handler (fixture->hdlrs, check, (GLogFunc)test_checked_handler); test_signal_assert_hits (sig1, 0); txn = xaccMallocTransaction (NULL); g_assert (txn == NULL); g_assert_cmpint (check->hits, ==, 1); test_signal_assert_hits (sig1, 0); txn = xaccMallocTransaction (book); g_assert (txn != NULL); g_assert_cmpint (check->hits, ==, 1); test_signal_assert_hits (sig1, 1); test_destroy (txn); qof_book_destroy (book); test_signal_free (sig1); } /* xaccTransSortSplits void xaccTransSortSplits (Transaction *trans)// Local: 1:0:0 */ static void test_xaccTransSortSplits (Fixture *fixture, gconstpointer pData) { Transaction *txn = fixture->txn; QofBook *book = qof_instance_get_book (QOF_INSTANCE (txn)); auto split1 = static_cast(txn->splits->data); auto split2 = static_cast(txn->splits->next->data); Split *split[3]; guint i; GList *node; gnc_numeric values[3]; values[0] = gnc_numeric_create (100, 240); values[1] = gnc_numeric_create (75, 240); values[2] = gnc_numeric_create (-125, 240); /* Prevent xaccTransCommitEdit in xaccSplitSetParent from doing anything */ xaccTransBeginEdit (txn); for (i = 0; i < G_N_ELEMENTS (split); i++) { split[i] = xaccMallocSplit (book); split[i]->value = values[i]; split[i]->acc = fixture->acc1; xaccSplitSetParent (split[i], txn); } node = txn->splits; g_assert (node->data == split1); node = g_list_next (node); g_assert (node->data == split2); node = g_list_next (node); g_assert (node->data == split[0]); node = g_list_next (node); g_assert (node->data == split[1]); node = g_list_next (node); g_assert (node->data == split[2]); xaccTransSortSplits (txn); node = txn->splits; g_assert (node->data == split1); node = g_list_next (node); g_assert (node->data == split[0]); node = g_list_next (node); g_assert (node->data == split[1]); node = g_list_next (node); g_assert (node->data == split2); node = g_list_next (node); g_assert (node->data == split[2]); xaccTransCommitEdit (txn); } /* dupe_trans static Transaction * dupe_trans (const Transaction *from)// Local: 1:0:0 */ static void test_dupe_trans (Fixture *fixture, gconstpointer pData) { time64 posted = gnc_dmy2time64 (12, 7, 2011); time64 entered = gnc_dmy2time64 (14, 7, 2011); Transaction *newtxn = NULL, *oldtxn = fixture->txn; QofBook *old_book = qof_instance_get_book (QOF_INSTANCE (oldtxn)); GList *newnode, *oldnode = oldtxn->splits; oldtxn->date_posted = posted; oldtxn->date_entered = entered; oldtxn->inst.kvp_data->set({"foo", "bar", "baz"}, new KvpValue(g_strdup ("The Great Waldo Pepper"))); newtxn = fixture->func->dupe_trans (oldtxn); g_assert_cmpstr (newtxn->num, ==, oldtxn->num); g_assert_cmpstr (newtxn->description, ==, oldtxn->description); for (newnode = newtxn->splits; newnode && oldnode; newnode = g_list_next (newnode)) { g_assert (xaccSplitEqual (static_cast(newnode->data), static_cast(oldnode->data), TRUE, FALSE, TRUE)); oldnode = g_list_next (oldnode); } g_assert (newnode == NULL); g_assert (oldnode == NULL); g_assert (newtxn->date_posted == posted); g_assert (newtxn->date_entered == entered); g_assert (qof_instance_version_cmp (QOF_INSTANCE (newtxn), QOF_INSTANCE (oldtxn)) == 0); g_assert (newtxn->orig == NULL); g_assert (newtxn->common_currency == fixture->curr); g_assert (newtxn->inst.e_type == NULL); g_assert (guid_equal (qof_instance_get_guid (QOF_INSTANCE (newtxn)), guid_null ())); g_assert (qof_instance_get_book (QOF_INSTANCE (newtxn)) == old_book); g_assert (compare (oldtxn->inst.kvp_data, newtxn->inst.kvp_data) == 0); test_destroy (newtxn); } /* xaccTransClone Transaction * xaccTransClone (const Transaction *from)// C: 1 Local: 1:0:0 */ static void test_xaccTransClone (Fixture *fixture, gconstpointer pData) { time64 posted = gnc_dmy2time64 (12, 7, 2011); time64 entered = gnc_dmy2time64 (14, 7, 2011); Transaction *newtxn = NULL, *oldtxn = fixture->txn; QofBook *old_book = qof_instance_get_book (QOF_INSTANCE (oldtxn)); GList *newnode, *oldnode; int foo, bar; oldtxn->date_posted = posted; oldtxn->date_entered = entered; newtxn = xaccTransClone (oldtxn); g_assert_cmpstr (newtxn->num, ==, oldtxn->num); g_assert_cmpstr (newtxn->description, ==, oldtxn->description); g_assert_cmpint (xaccTransCountSplits (oldtxn), ==, xaccTransCountSplits (newtxn)); xaccTransSortSplits (newtxn); xaccTransSortSplits (oldtxn); oldnode = oldtxn->splits; for (newnode = newtxn->splits; newnode && oldnode; newnode = g_list_next (newnode)) { g_assert (xaccSplitEqual (static_cast(newnode->data), static_cast(oldnode->data), FALSE, FALSE, FALSE)); oldnode = g_list_next (oldnode); } g_assert (newnode == NULL); g_assert (oldnode == NULL); g_assert (newtxn->date_posted == posted); g_assert (newtxn->date_entered == entered); g_assert (qof_instance_version_cmp (QOF_INSTANCE (newtxn), QOF_INSTANCE (oldtxn)) == 0); g_assert_cmpint (qof_instance_get_version_check (newtxn), ==, qof_instance_get_version_check (oldtxn)); g_assert (newtxn->orig == NULL); g_assert (newtxn->common_currency == fixture->curr); g_assert (qof_instance_get_book (QOF_INSTANCE (newtxn)) == old_book); g_assert (compare (oldtxn->inst.kvp_data, newtxn->inst.kvp_data) == 0); test_destroy (newtxn); } /* xaccTransCopyOnto void xaccTransCopyOnto (const Transaction *from_trans, Transaction *to_trans)//Register 2 convenience function for xaccTransCopyFromClipboard (from_trans, to_trans, NULL, NULL, TRUE) */ /* xaccTransCopyFromClipboard void xaccTransCopyFromClipboard (const Transaction *from_trans, Transaction *to_trans, const Account *from_acc, Account *to_acc, gboolean no_date) // Register 2 */ static void test_xaccTransCopyFromClipBoard (Fixture *fixture, gconstpointer pData) { Transaction *txn = fixture->txn; QofBook *book = qof_instance_get_book (QOF_INSTANCE (txn)); Account *acc1 = xaccMallocAccount (book); Transaction *to_txn = xaccMallocTransaction (book); time64 now = gnc_time (nullptr); time64 never = 0; auto to_frame = to_txn->inst.kvp_data; xaccAccountSetCommodity (acc1, fixture->comm); xaccTransCopyFromClipBoard (txn, to_txn, fixture->acc1, acc1, FALSE); g_assert (gnc_commodity_equal (txn->common_currency, to_txn->common_currency)); g_assert (to_txn->date_entered == now); g_assert (to_txn->date_posted == txn->date_posted); g_assert_cmpstr (txn->num, ==, to_txn->num); /* Notes also tests that KVP is copied */ g_assert_cmpstr (xaccTransGetNotes (txn), ==, xaccTransGetNotes (to_txn)); g_assert_cmpstr (xaccTransGetDescription (txn), ==, xaccTransGetDescription (to_txn)); g_assert_cmpstr (xaccTransGetNotes (txn), ==, xaccTransGetNotes (to_txn)); g_assert_cmpint (xaccTransCountSplits (txn), ==, xaccTransCountSplits (to_txn)); } static void test_xaccTransCopyFromClipBoard_no_start (Fixture *fixture, gconstpointer pData) { Transaction *txn = fixture->txn; QofBook *book = qof_instance_get_book (QOF_INSTANCE (txn)); Account *acc1 = xaccMallocAccount (book); Transaction *to_txn = xaccMallocTransaction (book); time64 now = gnc_time (nullptr); time64 never = 0; xaccAccountSetCommodity (acc1, fixture->comm); xaccTransCopyFromClipBoard (txn, to_txn, fixture->acc1, acc1, TRUE); g_assert (gnc_commodity_equal (txn->common_currency, to_txn->common_currency)); g_assert (to_txn->date_entered == now); g_assert (to_txn->date_posted == never); g_assert_cmpstr (to_txn->num, ==, txn->num); /* Notes also tests that KVP is copied */ g_assert_cmpstr (xaccTransGetNotes (txn), ==, xaccTransGetNotes (to_txn)); g_assert_cmpstr (xaccTransGetDescription (txn), ==, xaccTransGetDescription (to_txn)); g_assert_cmpstr (xaccTransGetNotes (txn), ==, xaccTransGetNotes (to_txn)); g_assert_cmpint (xaccTransCountSplits (txn), ==, xaccTransCountSplits (to_txn)); } /* xaccFreeTransaction static void xaccFreeTransaction (Transaction *trans)// Local: 4:0:0 */ static void test_xaccFreeTransaction (Fixture *fixture, gconstpointer pData) { Transaction *txn = fixture->txn; Transaction *orig = xaccMallocTransaction (qof_instance_get_book (QOF_INSTANCE (txn))); auto split = static_cast(txn->splits->data); auto txn_num = "321"; g_object_add_weak_pointer (G_OBJECT (txn->splits->data), reinterpret_cast(&split)); /* so the "free" doesn't, leaving the structure for us to test */ g_object_ref (txn); g_object_ref (orig); orig->num = CACHE_INSERT (txn_num); txn->orig = orig; fixture->func->xaccFreeTransaction (txn); g_assert (split == NULL); g_assert (txn->splits == NULL); g_assert_cmpint (GPOINTER_TO_INT(txn->num), ==, 1); g_assert (txn->description == NULL); g_assert_cmpint (txn->date_entered, ==, 0); g_assert_cmpint (txn->date_posted, ==, 0); g_assert_cmpint (GPOINTER_TO_INT(orig->num), ==, 1); g_assert (txn->orig == NULL); test_destroy (orig); g_test_log_set_fatal_handler ((GTestLogFatalFunc) test_log_handler, NULL); } /* compare_split_guids static gint compare_split_guids (gconstpointer a, gconstpointer b)// Local: 0:1:0 Pass-through function, test with TransEqual */ /* xaccTransEqual gboolean xaccTransEqual(const Transaction *ta, const Transaction *tb,// C: 2 in 2 Local: 0:0:0 */ #define DATE_BUF_SIZE 100 static void test_xaccTransEqual (Fixture *fixture, gconstpointer pData) { QofBook *book = qof_instance_get_book (QOF_INSTANCE (fixture->txn)); QofBook *book2 = qof_book_new (); Transaction *txn0 = fixture->txn; Transaction *clone = xaccTransClone (txn0); Transaction *txn1 = xaccTransClone (txn0); const GncGUID *guid_f_txn = qof_instance_get_guid (txn0); gchar entered[DATE_BUF_SIZE], posted[DATE_BUF_SIZE]; auto msg1 = "[xaccTransEqual] one is NULL"; gchar *msg2 = NULL; auto cleanup_fmt = "[trans_cleanup_commit] get rid of rollback trans=%p"; gchar split_guid0[GUID_ENCODING_LENGTH + 1]; gchar split_guid1[GUID_ENCODING_LENGTH + 1]; auto logdomain = "gnc.engine"; auto loglevel = static_cast(G_LOG_LEVEL_INFO); auto check = test_error_struct_new (logdomain, loglevel, msg1); auto check2 = test_error_struct_new(logdomain, loglevel, msg2); auto check3 = test_error_struct_new(logdomain, loglevel, ""); auto cleanup = test_error_struct_new (logdomain, loglevel, ""); auto split0 = xaccTransGetSplit (txn0, 0); qof_log_set_level (GNC_MOD_ENGINE, QOF_LOG_INFO); test_add_error (check); test_add_error (check2); test_add_error (cleanup); fixture->hdlrs = test_log_set_handler (fixture->hdlrs, check, (GLogFunc)test_list_handler); /* Booleans are check_guids, check_splits, check_balances, assume_ordered */ g_assert (xaccTransEqual (NULL, NULL, TRUE, TRUE, TRUE, TRUE)); g_assert (!xaccTransEqual (txn0, NULL, TRUE, TRUE, TRUE, TRUE)); g_assert (!xaccTransEqual (NULL, txn0, TRUE, TRUE, TRUE, TRUE)); g_assert (xaccTransEqual (txn0, txn0, TRUE, TRUE, TRUE, TRUE)); qof_instance_set_book (txn1, book2); qof_instance_set_guid (txn1, guid_f_txn); g_assert_cmpint (check->hits, ==, 2); check->hits = 0; g_assert_cmpint (xaccTransCountSplits (txn0), ==, xaccTransCountSplits (txn1)); g_free (check->msg); check->msg = g_strdup ("[xaccTransEqual] GUIDs differ"); g_assert (!xaccTransEqual (clone, txn0, TRUE, TRUE, TRUE, TRUE)); qof_instance_set_guid (clone, guid_f_txn); g_assert (xaccTransEqual (clone, txn0, TRUE, FALSE, TRUE, TRUE)); g_assert_cmpint (check->hits, ==, 1); xaccTransBeginEdit (clone); cleanup->msg = g_strdup_printf (cleanup_fmt, clone->orig); /* This changes the amount and value of the first split */ xaccTransSetCurrency (clone, fixture->comm); xaccTransCommitEdit (clone); g_free (cleanup->msg); g_free (check->msg); check->msg = g_strdup_printf ("[xaccTransEqual] commodities differ %s vs %s", gnc_commodity_get_unique_name (fixture->comm), gnc_commodity_get_unique_name (fixture->curr)); g_assert (!xaccTransEqual (clone, txn0, TRUE, FALSE, TRUE, TRUE)); g_assert_cmpint (check->hits, ==, 2); gnc_time64_to_iso8601_buff (clone->date_posted, posted); gnc_time64_to_iso8601_buff (clone->date_entered, entered); xaccTransBeginEdit (clone); cleanup->msg = g_strdup_printf (cleanup_fmt, clone->orig); /* This puts the value of the first split back, but leaves the amount changed */ xaccTransSetCurrency (clone, fixture->curr); clone->date_posted = txn0->date_entered; xaccTransCommitEdit (clone); g_free (cleanup->msg); g_free (check->msg); check->msg = g_strdup_printf ("[xaccTransEqual] date posted differs: '%s' vs '%s'", entered, posted); g_assert (!xaccTransEqual (clone, txn0, TRUE, FALSE, TRUE, TRUE)); g_assert_cmpint (check->hits, ==, 3); xaccTransBeginEdit (clone); cleanup->msg = g_strdup_printf (cleanup_fmt, clone->orig); clone->date_posted = txn0->date_posted; clone->date_entered = txn0->date_posted; xaccTransCommitEdit (clone); g_free (cleanup->msg); g_free (check->msg); check->msg = g_strdup_printf ("[xaccTransEqual] date entered differs: '%s' vs '%s'", posted, entered); g_assert (!xaccTransEqual (clone, txn0, TRUE, FALSE, TRUE, TRUE)); g_assert_cmpint (check->hits, ==, 4); xaccTransBeginEdit (clone); cleanup->msg = g_strdup_printf (cleanup_fmt, clone->orig); clone->date_entered = txn0->date_entered; clone->num = g_strdup("123"); xaccTransCommitEdit (clone); g_free (cleanup->msg); g_free (check->msg); check->msg = g_strdup ("[xaccTransEqual] num differs: 123 vs 123"); g_assert (!xaccTransEqual (clone, txn0, TRUE, FALSE, TRUE, TRUE)); g_assert_cmpint (check->hits, ==, 5); g_assert (xaccTransEqual (txn1, clone, TRUE, FALSE, TRUE, TRUE)); g_assert_cmpint (check->hits, ==, 5); txn1->num = CACHE_INSERT("321"); g_free (check->msg); check->msg = g_strdup ("[xaccTransEqual] num differs: 321 vs 123"); g_assert (!xaccTransEqual (txn1, txn0, TRUE, FALSE, TRUE, TRUE)); g_assert_cmpint (check->hits, ==, 6); g_free ((char*)clone->num); clone->num = CACHE_INSERT("123"); CACHE_REMOVE(txn1->num); txn1->num = g_strdup("123"); clone->description = CACHE_INSERT("salt pork"); g_free (check->msg); check->msg = g_strdup ("[xaccTransEqual] descriptions differ: salt pork vs Waldo Pepper"); g_assert (!xaccTransEqual (clone, txn0, TRUE, FALSE, TRUE, TRUE)); g_assert_cmpint (check->hits, ==, 7); g_assert (xaccTransEqual (txn1, txn0, TRUE, FALSE, TRUE, TRUE)); g_assert_cmpint (check->hits, ==, 7); g_assert (!xaccTransEqual (clone, txn1, TRUE, FALSE, TRUE, TRUE)); g_assert_cmpint (check->hits, ==, 8); xaccTransBeginEdit (clone); cleanup->msg = g_strdup_printf (cleanup_fmt, clone->orig); CACHE_REMOVE(clone->description); clone->description = CACHE_INSERT ("Waldo Pepper"); auto frame = qof_instance_get_slots (QOF_INSTANCE (clone)); frame->set({"qux", "quux", "corge"}, new KvpValue(654.321)); xaccTransCommitEdit (clone); g_free (cleanup->msg); g_free (check->msg); check->msg = g_strdup ("[xaccTransEqual] kvp frames differ:\nnotes/Salt pork sausage (char *)\nqux/quux/corge/654.321 (double)\n\n\n\n\nvs\n\nnotes/Salt pork sausage (char *)\nqux/quux/corge/123.456 (double)\n\n\n"); g_assert (!xaccTransEqual (clone, txn0, TRUE, FALSE, TRUE, TRUE)); g_assert_cmpint (check->hits, ==, 9); xaccTransBeginEdit (clone); cleanup->msg = g_strdup_printf (cleanup_fmt, clone->orig); clone->description = CACHE_INSERT ("Waldo Pepper"); frame->set({"qux", "quux", "corge"}, new KvpValue(123.456)); xaccTransCommitEdit (clone); g_free (cleanup->msg); g_free (check->msg); check->msg = g_strdup ("[xaccSplitEqual] GUIDs differ"); auto split1 = xaccTransGetSplit (clone, 0); guid_to_string_buff (qof_instance_get_guid (split0), split_guid0); guid_to_string_buff (qof_instance_get_guid (split1), split_guid1); check2->msg = g_strdup_printf ( "[xaccTransEqual] splits %s and %s differ", split_guid1, split_guid0); g_assert (!xaccTransEqual (clone, txn0, TRUE, TRUE, TRUE, TRUE)); g_assert (xaccTransEqual (clone, txn0, FALSE, FALSE, FALSE, TRUE)); g_assert_cmpint (check->hits, ==, 10); g_assert_cmpint (check2->hits, ==, 1); g_free (check2->msg); check2->msg = g_strdup_printf ( "[xaccTransEqual] splits %s and %s differ", split_guid0, split_guid0); qof_instance_set_guid (split1, qof_instance_get_guid (split0)); g_assert (!xaccTransEqual (clone, txn0, TRUE, TRUE, TRUE, TRUE)); g_assert (xaccTransEqual (clone, txn0, TRUE, FALSE, FALSE, TRUE)); g_assert_cmpint (check->hits, ==, 10); g_assert_cmpint (check2->hits, ==, 2); qof_instance_set_guid (xaccTransGetSplit (txn1, 0), qof_instance_get_guid (split0)); qof_instance_set_guid (xaccTransGetSplit (txn1, 1), qof_instance_get_guid (xaccTransGetSplit (txn0, 1))); { Split* split00 = xaccTransGetSplit (txn0, 0); Split* split01 = xaccTransGetSplit (txn0, 1); Split* split10 = xaccTransGetSplit (txn1, 0); Split* split11 = xaccTransGetSplit (txn1, 1); auto bal00 = gnc_numeric_to_string (split00->balance); auto bal01 = gnc_numeric_to_string (split01->balance); auto bal10 = gnc_numeric_to_string (split10->balance); auto bal11 = gnc_numeric_to_string (split11->balance); g_free (check->msg); check->msg = g_strdup_printf("[xaccSplitEqualCheckBal] balances differ: %s vs %s", bal10, bal00); check3->msg = g_strdup_printf("[xaccSplitEqualCheckBal] balances differ: %s vs %s", bal11, bal01); test_add_error (check3); g_assert (!xaccTransEqual (txn1, txn0, TRUE, TRUE, TRUE, TRUE)); g_assert (xaccTransEqual (txn1, txn0, TRUE, TRUE, FALSE, TRUE)); g_assert_cmpint (check->hits, ==, 11); g_assert_cmpint (check2->hits, ==, 3); g_assert_cmpint (check3->hits, ==, 0); split10->balance = split00->balance; split11->balance = split01->balance; split10->noclosing_balance = split00->noclosing_balance; split11->noclosing_balance = split01->noclosing_balance; g_assert (xaccTransEqual (txn1, txn0, TRUE, TRUE, TRUE, TRUE)); } g_free (check3->msg); g_free (check2->msg); } /* xaccTransUseTradingAccounts xaccTransUseTradingAccounts Returns true if the transaction should include trading account splits if it involves more than one commodity. gboolean xaccTransUseTradingAccounts(const Transaction *trans)// C: 10 in 7 Local: 2:0:0 Pass-through, no need to test. */ /* xaccTransLookup Transaction * xaccTransLookup (const GncGUID *guid, QofBook *book)// C: 22 in 7 Local: 1:0:0 */ static void test_xaccTransLookup (Fixture *fixture, gconstpointer pData) { Transaction *txn = fixture->txn; QofInstance *inst = QOF_INSTANCE (txn); g_assert (xaccTransLookup (qof_instance_get_guid (inst), qof_instance_get_book (inst)) == txn); } /* xaccTransGetImbalanceValue gnc_numeric xaccTransGetImbalanceValue (const Transaction * trans)// C: 11 in 5 Local: 1:1:0 */ static void test_xaccTransGetImbalanceValue (Fixture *fixture, gconstpointer pData) { QofBook *book = qof_instance_get_book (QOF_INSTANCE (fixture->txn)); auto split1 = xaccMallocSplit (book); g_assert (gnc_numeric_equal (xaccTransGetImbalanceValue (fixture->txn), gnc_numeric_zero ())); split1->acc = fixture->acc1; split1->memo = CACHE_INSERT ("foo"); split1->action = CACHE_INSERT ("bar"); split1->amount = gnc_numeric_create (100000, 1000); split1->value = gnc_numeric_create (3200, 240); xaccTransBeginEdit (fixture->txn); xaccSplitSetParent (split1, fixture->txn); g_assert (gnc_numeric_equal (xaccTransGetImbalanceValue (fixture->txn), split1->value)); xaccTransCommitEdit (fixture->txn); } /* xaccTransGetImbalance MonetaryList * xaccTransGetImbalance (const Transaction * trans)// C: 15 in 6 Local: 1:0:0 */ static void test_xaccTransGetImbalance (Fixture *fixture, gconstpointer pData) { QofBook *book = qof_instance_get_book (QOF_INSTANCE (fixture->txn)); auto split1 = xaccMallocSplit (book); MonetaryList *mlist; g_assert (xaccTransGetImbalance (NULL) == NULL); mlist = xaccTransGetImbalance (fixture->txn); g_assert_cmpint (g_list_length (mlist), ==, 0); split1->acc = fixture->acc1; split1->memo = CACHE_INSERT ("foo"); split1->action = CACHE_INSERT ("bar"); split1->amount = gnc_numeric_create (100000, 1000); split1->value = gnc_numeric_create (3200, 240); xaccTransBeginEdit (fixture->txn); xaccSplitSetParent (split1, fixture->txn); mlist = xaccTransGetImbalance (fixture->txn); g_assert_cmpint (g_list_length (mlist), ==, 1); xaccTransCommitEdit (fixture->txn); gnc_monetary_list_free (mlist); } static void test_xaccTransGetImbalance_trading (Fixture *fixture, gconstpointer pData) { QofBook *book = qof_instance_get_book (QOF_INSTANCE (fixture->txn)); auto split1 = xaccMallocSplit (book); auto split2 = xaccMallocSplit (book); Account *acc1 = xaccMallocAccount (book); Account *acc2 = xaccMallocAccount (book); gnc_numeric value; MonetaryList *mlist; qof_book_begin_edit (book); qof_instance_set (QOF_INSTANCE (book), "trading-accts", "t", NULL); qof_book_commit_edit (book); /* Without trading splits, the list is unbalanced */ mlist = xaccTransGetImbalance (fixture->txn); g_assert_cmpint (g_list_length (mlist), ==, 2); gnc_monetary_list_free (mlist); xaccAccountSetCommodity (acc1, fixture->comm); xaccAccountSetCommodity (acc2, fixture->curr); xaccAccountSetType (acc1, ACCT_TYPE_TRADING); xaccAccountSetType (acc2, ACCT_TYPE_TRADING); /* The setup transaction is unbalanced in a trading-accounts environment. */ g_assert (!xaccTransIsBalanced (fixture->txn)); /* Make it look like a proper trading accounts transactionm */ split1->acc = acc1; split1->memo = CACHE_INSERT ("foo"); split1->action = CACHE_INSERT ("bar"); split1->amount = gnc_numeric_create (-10000, 100); split1->value = gnc_numeric_create (-3200, 240); split2->acc = acc2; split2->memo = CACHE_INSERT ("foo"); split2->action = CACHE_INSERT ("bar"); split2->amount = gnc_numeric_create (3000, 240); split2->value = gnc_numeric_create (3200, 240); xaccTransBeginEdit (fixture->txn); xaccSplitSetParent (split1, fixture->txn); mlist = xaccTransGetImbalance (fixture->txn); g_assert_cmpint (g_list_length (mlist), ==, 1); gnc_monetary_list_free (mlist); xaccSplitSetParent (split2, fixture->txn); mlist = xaccTransGetImbalance (fixture->txn); g_assert_cmpint (g_list_length (mlist), ==, 1); gnc_monetary_list_free (mlist); split2->amount = gnc_numeric_create (3000, 240); split2->value = gnc_numeric_create (3000, 240); mlist = xaccTransGetImbalance (fixture->txn); g_assert_cmpint (g_list_length (mlist), ==, 1); gnc_monetary_list_free (mlist); split2->amount = gnc_numeric_create (3200, 240); split2->value = gnc_numeric_create (3200, 240); mlist = xaccTransGetImbalance (fixture->txn); g_assert_cmpint (g_list_length (mlist), ==, 0); gnc_monetary_list_free (mlist); xaccTransCommitEdit (fixture->txn); test_destroy (acc1); test_destroy (acc2); } /* xaccTransIsBalanced gboolean xaccTransIsBalanced (const Transaction *trans)// C: 4 in 4 Local: 1:0:0 */ static void test_xaccTransIsBalanced (Fixture *fixture, gconstpointer pData) { QofBook *book = qof_instance_get_book (QOF_INSTANCE (fixture->txn)); auto split1 = xaccMallocSplit (book); g_assert (!xaccTransIsBalanced (NULL)); g_assert (xaccTransIsBalanced (fixture->txn)); split1->acc = fixture->acc1; split1->memo = CACHE_INSERT ("foo"); split1->action = CACHE_INSERT ("bar"); split1->amount = gnc_numeric_create (100000, 1000); split1->value = gnc_numeric_create (3200, 240); xaccTransBeginEdit (fixture->txn); xaccSplitSetParent (split1, fixture->txn); g_assert (! xaccTransIsBalanced (fixture->txn)); xaccTransCommitEdit (fixture->txn); } static void test_xaccTransIsBalanced_trading (Fixture *fixture, gconstpointer pData) { QofBook *book = qof_instance_get_book (QOF_INSTANCE (fixture->txn)); auto split1 = xaccMallocSplit (book); auto split2 = xaccMallocSplit (book); Account *acc1 = xaccMallocAccount (book); Account *acc2 = xaccMallocAccount (book); qof_book_begin_edit (book); qof_instance_set (QOF_INSTANCE (book), "trading-accts", "t", NULL); qof_book_commit_edit (book); xaccAccountSetCommodity (acc1, fixture->curr); xaccAccountSetCommodity (acc2, fixture->comm); xaccAccountSetType (acc1, ACCT_TYPE_TRADING); xaccAccountSetType (acc2, ACCT_TYPE_TRADING); /* The setup transaction is unbalanced in a trading-accounts environment. */ g_assert (!xaccTransIsBalanced (fixture->txn)); split1->acc = acc1; split1->memo = CACHE_INSERT ("foo"); split1->action = CACHE_INSERT ("bar"); split1->amount = gnc_numeric_create (3200, 240); split1->value = gnc_numeric_create (3200, 240); split2->acc = acc2; split2->memo = CACHE_INSERT ("foo"); split2->action = CACHE_INSERT ("bar"); split2->amount = gnc_numeric_create (-10000, 100); split2->value = gnc_numeric_create (-3000, 240); xaccTransBeginEdit (fixture->txn); xaccSplitSetParent (split1, fixture->txn); g_assert (!xaccTransIsBalanced (fixture->txn)); xaccSplitSetParent (split2, fixture->txn); g_assert (!xaccTransIsBalanced (fixture->txn)); split2->amount = gnc_numeric_create (-11000, 100); split2->value = gnc_numeric_create (-3200, 240); g_assert (!xaccTransIsBalanced (fixture->txn)); split2->amount = gnc_numeric_create (-10000, 100); split2->value = gnc_numeric_create (-3200, 240); g_assert (xaccTransIsBalanced (fixture->txn)); xaccTransRollbackEdit (fixture->txn); test_destroy (acc2); test_destroy (acc1); } /* xaccTransGetAccountValue gnc_numeric xaccTransGetAccountValue (const Transaction *trans,// SCM: 6 in 6 Local: 0:0:0 */ static void test_xaccTransGetAccountValue (Fixture *fixture, gconstpointer pData) { gnc_numeric val1 = {3200, 240}, val2 = {-3200, 240}; g_assert (gnc_numeric_zero_p (xaccTransGetAccountValue (fixture->txn, NULL))); g_assert (gnc_numeric_zero_p (xaccTransGetAccountValue (NULL, fixture->acc1))); g_assert (gnc_numeric_eq (xaccTransGetAccountValue (fixture->txn, fixture->acc1), val1)); g_assert (gnc_numeric_eq (xaccTransGetAccountValue (fixture->txn, fixture->acc2), val2)); } /* xaccTransGetAccountAmount gnc_numeric xaccTransGetAccountAmount (const Transaction *trans, const Account *acc)// C: 2 in 1 Local: 0:0:0 */ static void test_xaccTransGetAccountAmount (Fixture *fixture, gconstpointer pData) { gnc_numeric amt1 = {100000, 1000}, amt2 = {-3200, 240}; g_assert (gnc_numeric_zero_p (xaccTransGetAccountAmount (fixture->txn, NULL))); g_assert (gnc_numeric_zero_p (xaccTransGetAccountAmount (NULL, fixture->acc1))); g_assert (gnc_numeric_eq (xaccTransGetAccountAmount (fixture->txn, fixture->acc1), amt1)); g_assert (gnc_numeric_eq (xaccTransGetAccountAmount (fixture->txn, fixture->acc2), amt2)); } /* xaccTransGetRateForCommodity gboolean xaccTransGetRateForCommodity(const Transaction *trans, const gnc_commodity *split_com, const Split *split, gnc_numeric *rate) */ static void test_xaccTransGetRateForCommodity (Fixture *fixture, gconstpointer pData) { gnc_numeric rate = gnc_numeric_zero (); QofBook *book = qof_instance_get_book (QOF_INSTANCE (fixture->txn)); auto split0 = xaccMallocSplit (book); auto split1 = xaccTransFindSplitByAccount(fixture->txn, fixture->acc1); g_assert (!xaccTransGetRateForCommodity (NULL, fixture->comm, split0, &rate)); g_assert (!xaccTransGetRateForCommodity (fixture->txn, NULL, split0, &rate)); g_assert (!xaccTransGetRateForCommodity (fixture->txn, fixture->comm, NULL, &rate)); g_assert (xaccTransGetRateForCommodity (fixture->txn, fixture->curr, split0, &rate)); g_assert (gnc_numeric_equal (rate, gnc_numeric_create (1, 1))); rate = gnc_numeric_zero (); g_assert (!xaccTransGetRateForCommodity (fixture->txn, fixture->comm, split0, &rate)); g_assert (gnc_numeric_zero_p (rate)); g_assert (xaccTransGetRateForCommodity (fixture->txn, fixture->comm, split1, &rate)); g_assert (gnc_numeric_equal (rate, gnc_numeric_create (1800, 240))); } /* xaccTransGetAccountConvRate gnc_numeric xaccTransGetAccountConvRate(const Transaction *txn, const Account *acc)// C: 5 in 4 Local: 0:0:0 */ static void test_xaccTransGetAccountConvRate (Fixture *fixture, gconstpointer pData) { auto msg1 = "[xaccTransGetAccountConvRate()] How can amount be nonzero and value be zero?"; auto loglevel = static_cast(G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL); auto check = test_error_struct_new ("gnc.engine", loglevel, msg1); auto split1 = xaccTransFindSplitByAccount(fixture->txn, fixture->acc1); gnc_numeric rate; fixture->hdlrs = test_log_set_fatal_handler (fixture->hdlrs, check, (GLogFunc)test_checked_handler); g_assert (gnc_numeric_equal (xaccTransGetAccountConvRate (fixture->txn, fixture->acc2), gnc_numeric_create (1, 1))); g_assert (gnc_numeric_equal (xaccTransGetAccountConvRate (fixture->txn, fixture->acc1), gnc_numeric_create (1800, 240))); g_assert_cmpint (check->hits, ==, 0); split1->value = gnc_numeric_zero(); rate = xaccTransGetAccountConvRate (fixture->txn, fixture->acc1); g_assert_cmpint (gnc_numeric_check (rate), ==, GNC_ERROR_OVERFLOW); g_assert_cmpint (check->hits, ==, 1); } /* xaccTransGetAccountBalance gnc_numeric xaccTransGetAccountBalance (const Transaction *trans,// C: 1 Local: 0:0:0 */ static void test_xaccTransGetAccountBalance (Fixture *fixture, gconstpointer pData) { #ifdef USE_CLANG_FUNC_SIG #define _func "gnc_numeric xaccTransGetAccountBalance(const Transaction *, const Account *)" #else #define _func "xaccTransGetAccountBalance" #endif auto msg1 = _func ": assertion 'account && trans' failed"; #undef _func auto loglevel = static_cast(G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL); auto check = test_error_struct_new ("gnc.engine", loglevel, msg1); auto split1 = xaccTransFindSplitByAccount(fixture->txn, fixture->acc1); gnc_numeric rate; fixture->hdlrs = test_log_set_fatal_handler (fixture->hdlrs, check, (GLogFunc)test_checked_handler); rate = xaccTransGetAccountBalance (NULL, fixture->acc1); g_assert_cmpint (gnc_numeric_check (rate), ==, GNC_ERROR_ARG); g_assert_cmpint (check->hits, ==, 1); rate = xaccTransGetAccountBalance (fixture->txn, NULL); g_assert_cmpint (gnc_numeric_check (rate), ==, GNC_ERROR_ARG); g_assert_cmpint (check->hits, ==, 2); rate = xaccTransGetAccountBalance (fixture->txn, fixture->acc1); g_assert (gnc_numeric_equal (rate, gnc_numeric_create (100000, 1000))); g_assert_cmpint (check->hits, ==, 2); rate = xaccTransGetAccountBalance (fixture->txn, fixture->acc2); g_assert (gnc_numeric_equal (rate, gnc_numeric_create (-3200, 240))); g_assert_cmpint (check->hits, ==, 2); } /* xaccTransGetCurrency gnc_commodity * xaccTransGetCurrency (const Transaction *trans)// C: 33 in 17 SCM: 34 in 26 Local: 2:0:0 Simple Getter. No need to test. */ /* xaccTransSetCurrency void xaccTransSetCurrency (Transaction *trans, gnc_commodity *curr)// C: 22 in 18 SCM: 3 in 3 Local: 1:0:0 */ static void test_xaccTransSetCurrency (Fixture *fixture, gconstpointer pData) { QofBook *book = qof_instance_get_book (QOF_INSTANCE (fixture->txn)); gnc_commodity *curr = gnc_commodity_new (book, "Japanese Yen", "CURRENCY", "JPY", "¥", 1); auto split1 = xaccTransFindSplitByAccount (fixture->txn, fixture->acc1); gnc_numeric old_val = xaccSplitGetValue (split1); /* Prevent commit in xaccTransSetCurrency() */ xaccTransBeginEdit(fixture->txn); xaccTransSetCurrency (fixture->txn, curr); g_assert (fixture->txn->common_currency == curr); g_assert_cmpint (xaccSplitGetValue (split1).denom, ==, gnc_commodity_get_fraction (curr)); g_assert_cmpint (xaccSplitGetValue (split1).num, ==, old_val.num / old_val.denom); } /* xaccTransBeginEdit void xaccTransBeginEdit (Transaction *trans)// C: 72 in 28 SCM: 5 in 5 Local: 16:0:0 */ static void test_xaccTransBeginEdit () { QofBook *book = qof_book_new (); Transaction *txn = xaccMallocTransaction (book); Transaction *dupe = NULL; auto msg1 = "[xaccOpenLog] Attempt to open disabled transaction log"; auto msg2 = "[xaccTransWriteLog] Attempt to write disabled transaction log"; auto loglevel = static_cast(G_LOG_LEVEL_INFO); auto logdomain = "gnc.translog"; auto check1 = test_error_struct_new (logdomain, loglevel, msg1); auto check2 = test_error_struct_new (logdomain, loglevel, msg2); guint hdlr = g_log_set_handler (logdomain, loglevel, (GLogFunc)test_list_handler, NULL); qof_log_set_level (logdomain, QOF_LOG_INFO); test_add_error (check1); test_add_error (check2); g_assert_cmpint (0, ==, qof_instance_get_editlevel (QOF_INSTANCE (txn))); g_assert (txn->orig == NULL); xaccTransBeginEdit (txn); g_assert_cmpint (1, ==, qof_instance_get_editlevel (QOF_INSTANCE (txn))); dupe = txn->orig; g_assert (txn->orig != NULL); g_assert_cmpint (1, ==, check1->hits); g_assert_cmpint (1, ==, check2->hits); xaccTransBeginEdit (txn); g_assert_cmpint (2, ==, qof_instance_get_editlevel (QOF_INSTANCE (txn))); g_assert (txn->orig == dupe); g_assert_cmpint (1, ==, check1->hits); g_assert_cmpint (1, ==, check2->hits); xaccTransRollbackEdit (txn); xaccTransRollbackEdit (txn); g_assert_cmpint (0, ==, qof_instance_get_editlevel (QOF_INSTANCE (txn))); g_assert (txn->orig == NULL); qof_book_mark_readonly (book); xaccTransBeginEdit (txn); dupe = txn->orig; g_assert_cmpint (1, ==, qof_instance_get_editlevel (QOF_INSTANCE (txn))); g_assert (txn->orig == dupe); g_assert_cmpint (1, ==, check1->hits); g_assert_cmpint (2, ==, check2->hits); xaccTransRollbackEdit (txn); g_assert_cmpint (0, ==, qof_instance_get_editlevel (QOF_INSTANCE (txn))); g_assert (txn->orig == NULL); g_log_remove_handler (logdomain, hdlr); test_clear_error_list (); test_error_struct_free (check1); test_error_struct_free (check2); xaccTransDestroy (txn); qof_book_destroy (book); } /* xaccTransDestroy void xaccTransDestroy (Transaction *trans)// C: 26 in 15 SCM: 4 in 4 Local: 3:0:0 */ static void test_xaccTransDestroy (Fixture *fixture, gconstpointer pData) { Transaction *txn = fixture->txn; QofBook *book = qof_instance_get_book (QOF_INSTANCE (txn)); Transaction *dupe = xaccTransClone (txn); xaccTransBeginEdit (txn); g_assert (!qof_instance_get_destroying (QOF_INSTANCE (txn))); g_assert (xaccTransEqual (txn, dupe, FALSE, TRUE, FALSE, TRUE)); xaccTransDestroy (txn); g_assert (qof_instance_get_destroying (QOF_INSTANCE (txn))); g_assert (xaccTransEqual (txn, dupe, FALSE, TRUE, FALSE, TRUE)); xaccTransRollbackEdit (txn); qof_book_mark_readonly (book); xaccTransBeginEdit (txn); xaccTransDestroy (txn); g_assert (qof_instance_get_destroying (QOF_INSTANCE (txn))); g_assert (xaccTransEqual (txn, dupe, FALSE, TRUE, FALSE, TRUE)); xaccTransRollbackEdit (txn); test_destroy (dupe); } /* destroy_gains static void destroy_gains (Transaction *trans)// Local: 1:0:0 -- from do_destroy */ static void test_destroy_gains (GainsFixture *fixture, gconstpointer pData) { /* Don't try to test with a NULL transaction, this is an internal * function that isn't protected. */ Fixture *base = &(fixture->base); auto base_split = static_cast(g_list_nth_data (base->txn->splits, 1)); xaccTransBeginEdit (fixture->gains_txn); /* Protect it from being actually destroyed */ base->func->destroy_gains (base->txn); g_assert (qof_instance_get_destroying (QOF_INSTANCE (fixture->gains_txn))); g_assert (base_split->gains_split == NULL); xaccTransCommitEdit (fixture->gains_txn); } /* do_destroy static void do_destroy (Transaction *trans)// Local: 1:1:0 callback passed to qof_commit_edit_part2 in XaccTransCommitEdit NB: This function has a weird three-step process for destroying and freeing the splits, which isn't really testable. */ static void test_do_destroy (GainsFixture *fixture, gconstpointer pData) { Fixture *base = &(fixture->base); auto base_split = static_cast(g_list_nth_data (base->txn->splits, 1)); QofBook *book = qof_instance_get_book (base->txn); TestSignal sig = test_signal_new (QOF_INSTANCE (base->txn), QOF_EVENT_DESTROY, NULL); g_object_add_weak_pointer (G_OBJECT (base->txn->splits->data), reinterpret_cast(&base_split)); g_object_ref (base->txn); g_object_ref (fixture->gains_txn); base->func->do_destroy (base->txn); g_assert_cmpint (test_signal_return_hits (sig), ==, 1); g_assert (base->txn->description == NULL); g_assert_cmpint (GPOINTER_TO_INT(base->txn->num), ==, 1); g_assert (qof_instance_get_destroying (QOF_INSTANCE (fixture->gains_txn))); g_assert (base_split == NULL); test_signal_free (sig); } /* xaccEnableDataScrubbing * xaccDisableDataScrubbing Trivial setters */ /* was_trans_emptied static gboolean was_trans_emptied(Transaction *trans)// Local: 1:0:0 xaccTransCommitEdit */ static void test_was_trans_emptied (Fixture *fixture, gconstpointer pData) { GList *list = fixture->txn->splits; g_assert (!fixture->func->was_trans_emptied (fixture->txn)); fixture->txn->splits = NULL; g_assert (fixture->func->was_trans_emptied (fixture->txn)); /* Restore the list so teardown can free the splits */ fixture->txn->splits = list; } /* trans_on_error static void trans_on_error(Transaction *trans, QofBackendError errcode)// Local: 0:1:0 callback for qof_commit_edit_part2, xaccTransCommitEdit */ static QofBackendError errorvalue = ERR_BACKEND_NO_ERR; static void commit_error_cb (gpointer data, QofBackendError errcode) { errorvalue = errcode; } static void test_trans_on_error (Fixture *fixture, gconstpointer pData) { QofBackendError errcode = ERR_BACKEND_MODIFIED; auto msg = "[trans_on_error()] Another user has modified this transaction\n" "\tjust a moment ago. Please look at their changes,\n" "\tand try again, if needed.\n"; auto logdomain = "gnc.engine"; auto loglevel = static_cast(G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL); auto check = test_error_struct_new (logdomain, loglevel, msg); fixture->hdlrs = test_log_set_fatal_handler (fixture->hdlrs, check, (GLogFunc)test_checked_handler); gnc_engine_add_commit_error_callback ((EngineCommitErrorCallback)commit_error_cb, NULL); xaccTransBeginEdit (fixture->txn); g_assert_cmpint (qof_instance_get_editlevel (fixture->txn), ==, 1); fixture->func->trans_on_error (fixture->txn, errcode); g_assert_cmpint (check->hits, ==, 1); g_assert_cmpint ((guint)errorvalue, ==, (guint)errcode); g_assert_cmpint (qof_instance_get_editlevel (fixture->txn), ==, 0); errorvalue = ERR_BACKEND_NO_ERR; } /* trans_cleanup_commit static void trans_cleanup_commit(Transaction *trans)// Local: 0:1:0 callback for qof_commit_edit_part2, xaccTransCommitEdit */ static void test_trans_cleanup_commit (Fixture *fixture, gconstpointer pData) { QofBook *book = qof_instance_get_book (QOF_INSTANCE (fixture->txn)); auto destr_split = xaccMallocSplit (book); auto bogus_split = xaccMallocSplit (book); auto split0 = static_cast(fixture->txn->splits->data); Account *acct0 = split0->acc; Transaction *orig = NULL; auto sig_d_remove = test_signal_new (QOF_INSTANCE (destr_split), QOF_EVENT_REMOVE, NULL); auto sig_b_remove = test_signal_new (QOF_INSTANCE (bogus_split), QOF_EVENT_REMOVE, NULL); auto sig_d_destroy = test_signal_new (QOF_INSTANCE (destr_split), QOF_EVENT_DESTROY, NULL); auto sig_b_modify = test_signal_new (QOF_INSTANCE (bogus_split), QOF_EVENT_MODIFY, NULL); auto sig_t_modify = test_signal_new (QOF_INSTANCE (fixture->txn), QOF_EVENT_MODIFY, NULL); auto sig_a_changed = test_signal_new (QOF_INSTANCE (acct0), GNC_EVENT_ITEM_CHANGED, NULL); xaccTransBeginEdit (fixture->txn); orig = fixture->txn->orig; g_object_ref (orig); /* Check the txn-isn't-the-parent path */ fixture->txn->splits = g_list_prepend (fixture->txn->splits, destr_split); fixture->txn->splits = g_list_prepend (fixture->txn->splits, bogus_split); qof_instance_set_dirty (QOF_INSTANCE (destr_split)); qof_instance_set_dirty (QOF_INSTANCE (bogus_split)); qof_instance_set_destroying (QOF_INSTANCE (destr_split), TRUE); /*Reverse the splits list so we can check later that it got sorted */ fixture->txn->splits = g_list_reverse (fixture->txn->splits); g_assert (fixture->txn->splits->data != split0); fixture->func->trans_cleanup_commit (fixture->txn); g_assert_cmpint (test_signal_return_hits (sig_d_remove), ==, 1); g_assert_cmpint (test_signal_return_hits (sig_b_remove), ==, 1); g_assert_cmpint (test_signal_return_hits (sig_d_destroy), ==, 0); g_assert_cmpint (test_signal_return_hits (sig_b_modify), ==, 0); g_assert_cmpint (test_signal_return_hits (sig_t_modify), ==, 1); g_assert_cmpint (test_signal_return_hits (sig_a_changed), ==, 1); g_assert_cmpint (g_list_index (fixture->txn->splits, destr_split), ==, -1); g_assert_cmpint (g_list_index (fixture->txn->splits, bogus_split), ==, -1); g_assert (fixture->txn->orig == NULL); g_assert (fixture->txn->splits->data == split0); g_assert (qof_instance_get_destroying (destr_split)); /* Note that the function itself aborts if qof_instance_editlevel != 0 */ /* load things back up and test the txn-is-the-parent path */ qof_instance_increase_editlevel (fixture->txn); destr_split->parent = fixture->txn; bogus_split->parent = fixture->txn; fixture->txn->splits = g_list_prepend (fixture->txn->splits, destr_split); fixture->txn->splits = g_list_prepend (fixture->txn->splits, bogus_split); fixture->txn->orig = orig; orig->num = fixture->txn->num; g_object_ref (orig); fixture->func->trans_cleanup_commit (fixture->txn); g_assert_cmpint (test_signal_return_hits (sig_d_remove), ==, 2); g_assert_cmpint (test_signal_return_hits (sig_b_remove), ==, 1); g_assert_cmpint (test_signal_return_hits (sig_d_destroy), ==, 1); g_assert_cmpint (test_signal_return_hits (sig_b_modify), ==, 1); g_assert_cmpint (test_signal_return_hits (sig_t_modify), ==, 2); g_assert_cmpint (test_signal_return_hits (sig_a_changed), ==, 2); g_assert_cmpint (g_list_index (fixture->txn->splits, destr_split), ==, -1); g_assert_cmpint (g_list_index (fixture->txn->splits, bogus_split), ==, 0); g_assert_cmpint (GPOINTER_TO_INT(orig->num), ==, 1); test_destroy (orig); } /* xaccTransCommitEdit void xaccTransCommitEdit (Transaction *trans)// C: 88 in 28 SCM: 5 in 5 Local: 16:0:0 Setup has to run transCommitEdit, so we have to do our own setup for this function. */ static void test_xaccTransCommitEdit (void) { QofBook *book = qof_book_new (); auto split1 = xaccMallocSplit (book); auto split2 = xaccMallocSplit (book); Transaction *txn = xaccMallocTransaction (book); Account *acc1 = xaccMallocAccount (book); Account *acc2 = xaccMallocAccount (book); gnc_commodity *curr = gnc_commodity_new (book, "Gnu Rand", "CURRENCY", "GNR", "", 240); gnc_commodity *comm = gnc_commodity_new (book, "Wildebeest Fund", "FUND", "WBFXX", "", 1000); time64 posted = gnc_dmy2time64 (21, 4, 2012); auto sig_1_modify = test_signal_new (QOF_INSTANCE (split1), QOF_EVENT_MODIFY, NULL); auto sig_2_modify = test_signal_new (QOF_INSTANCE (split2), QOF_EVENT_MODIFY, NULL); auto sig_txn_destroy = test_signal_new (QOF_INSTANCE (txn), QOF_EVENT_DESTROY, NULL); xaccAccountSetCommodity (acc1, comm); xaccAccountSetCommodity (acc2, curr); txn->date_posted = posted; split1->memo = CACHE_INSERT ("foo"); split1->action = CACHE_INSERT ("bar"); split1->amount = gnc_numeric_create (100000, 1000); split1->value = gnc_numeric_create (3200, 240); /* Note, deliberately imblanced to force xaccTransScrubImbalance * to create a balance split, thus showing that it got called. */ split2->amount = gnc_numeric_create (-3000, 240); split2->value = gnc_numeric_create (-3000, 240); split1->acc = acc1; split2->acc = acc2; txn->num = CACHE_INSERT ("123"); txn->description = CACHE_INSERT ("Waldo Pepper"); xaccTransBeginEdit (txn); { xaccTransSetCurrency (txn, curr); xaccSplitSetParent (split1, txn); xaccSplitSetParent (split2, txn); } /* Setup's done, now test: */ xaccTransCommitEdit (txn); g_assert_cmpint (txn->date_entered, !=, 0); /* Signals make sure that trans_cleanup_commit got called */ g_assert_cmpint (test_signal_return_hits (sig_1_modify), ==, 1); g_assert_cmpint (test_signal_return_hits (sig_2_modify), ==, 1); g_assert_cmpint (g_list_length (txn->splits), ==, 3); xaccTransBeginEdit (txn); g_list_free (txn->splits); txn->splits = NULL; xaccTransCommitEdit (txn); g_assert_cmpint (test_signal_return_hits (sig_txn_destroy), ==, 1); test_signal_free (sig_1_modify); test_signal_free (sig_2_modify); test_signal_free (sig_txn_destroy); test_destroy (split1); test_destroy (split2); test_destroy (acc1); test_destroy (acc2); test_destroy (curr); test_destroy (comm); qof_book_destroy (book); } /* xaccTransRollbackEdit void xaccTransRollbackEdit (Transaction *trans)// C: 2 in 2 Local: 1:0:0 */ static void test_xaccTransRollbackEdit (Fixture *fixture, gconstpointer pData) { Transaction *txn = fixture->txn; Transaction *orig = NULL; QofBook *book = qof_instance_get_book (txn); time64 new_post = gnc_time (nullptr); time64 new_entered = time64CanonicalDayTime (new_post); time64 orig_post = txn->date_posted; time64 orig_entered = txn->date_entered; KvpFrame *base_frame = NULL; auto sig_account = test_signal_new (QOF_INSTANCE (fixture->acc1), GNC_EVENT_ITEM_CHANGED, NULL); auto mbe = static_cast(qof_book_get_backend (book)); auto split_00 = static_cast(txn->splits->data); auto split_01 = static_cast(txn->splits->next->data); auto split_02 = xaccMallocSplit (book); xaccTransBeginEdit (txn); qof_instance_set_destroying (txn, TRUE); orig = txn->orig; base_frame = orig->inst.kvp_data; /* DupeTransaction copies the kvp_frame */ g_object_ref (orig); /* Keep rollback from actually freeing it */ txn->num = CACHE_INSERT("321"); txn->description = CACHE_INSERT("salt peanuts"); txn->common_currency = NULL; txn->inst.kvp_data = NULL; txn->date_entered = new_entered; txn->date_posted = new_post; txn->splits->data = split_01; txn->splits->next->data = split_00; qof_instance_set_dirty (QOF_INSTANCE (split_01)); xaccSplitSetParent (split_02, txn); g_object_ref (split_02); auto split_10 = xaccDupeSplit(static_cast(orig->splits->data)); g_object_ref (split_10); auto split_11 = xaccDupeSplit(static_cast(orig->splits->next->data)); g_object_ref (split_11); qof_instance_increase_editlevel (QOF_INSTANCE (txn)); /* So it's 2 */ xaccTransRollbackEdit (txn); g_assert (txn->orig == orig); qof_instance_reset_editlevel (QOF_INSTANCE (txn)); /* Now it's 0 */ xaccTransRollbackEdit (txn); g_assert (txn->orig == orig); qof_instance_increase_editlevel (QOF_INSTANCE (txn)); /* And back to 1 */ xaccTransRollbackEdit (txn); g_assert (txn->orig == NULL); g_assert_cmpstr (txn->num, ==, "123"); g_assert_cmpint (GPOINTER_TO_INT(orig->num), ==, 1); g_assert_cmpstr (txn->description, ==, "Waldo Pepper"); g_assert (txn->inst.kvp_data == base_frame); g_assert (txn->common_currency == fixture->curr); g_assert (txn->date_posted == orig_post); g_assert (txn->date_entered == orig_entered); g_assert_cmpuint (test_signal_return_hits (sig_account), ==, 1); g_assert_cmpuint (g_list_length (txn->splits), ==, 2); g_assert_cmpint (GPOINTER_TO_INT(split_02->memo), ==, 1); g_assert (xaccSplitEqual (static_cast(txn->splits->data), split_10, FALSE, FALSE, FALSE)); g_assert (xaccSplitEqual (static_cast(txn->splits->next->data), split_10, FALSE, FALSE, FALSE)); g_assert_cmpstr (mbe->m_last_call.c_str(), ==, "rollback"); g_assert_cmpuint (qof_instance_get_editlevel (QOF_INSTANCE (txn)), ==, 0); g_assert (qof_instance_get_destroying (txn) == FALSE); test_signal_free (sig_account); g_object_unref (split_10); g_object_unref (split_11); g_object_unref (split_02); g_object_unref (orig); } /* A second xaccTransRollbackEdit test to check the backend error handling */ static void test_xaccTransRollbackEdit_BackendErrors (Fixture *fixture, gconstpointer pData) { auto mbe = static_cast(qof_book_get_backend (qof_instance_get_book (fixture->txn))); auto loglevel = static_cast(G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL); auto msg = "[xaccTransRollbackEdit()] Rollback Failed. Ouch!"; auto check = test_error_struct_new ("gnc.engine", loglevel, msg); fixture->hdlrs = test_log_set_fatal_handler (fixture->hdlrs, check, (GLogFunc)test_checked_handler); g_object_ref (fixture->txn); xaccTransBeginEdit (fixture->txn); mbe->inject_error(ERR_BACKEND_MODIFIED); xaccTransRollbackEdit (fixture->txn); g_assert_cmpint (check->hits, ==, 1); g_assert_cmpstr (mbe->m_last_call.c_str(), ==, "rollback"); mbe->m_last_call.clear(); xaccTransBeginEdit (fixture->txn); mbe->inject_error (ERR_BACKEND_MOD_DESTROY); xaccTransRollbackEdit (fixture->txn); g_assert_cmpint (GPOINTER_TO_INT(fixture->txn->num), ==, 1); g_assert_cmpstr (mbe->m_last_call.c_str(), ==, "rollback"); } /* xaccTransIsOpen C: 23 in 7 SCM: 1 Local: 0:0:0 * xaccTransOrder C: 2 in 2 SCM: 12 in 12 Local: 0:1:0 * Simple convenience functions. No test required. */ /* xaccTransOrder_num_action int xaccTransOrder_num_action (const Transaction *ta, const char *actna, const Transaction *tb, const char *actnb)// C: 1 Local: 1:0:0 */ static void test_xaccTransOrder_num_action (Fixture *fixture, gconstpointer pData) { Transaction *txnA = fixture->txn; Transaction *txnB = fixture->func->dupe_trans (txnA); g_assert_cmpint (xaccTransOrder_num_action (txnA, NULL, NULL, NULL), ==, -1); g_assert_cmpint (xaccTransOrder_num_action (NULL, NULL, txnA, NULL), ==, 1); g_assert_cmpint (xaccTransOrder_num_action (NULL, NULL, NULL, NULL), ==, 0); g_assert_cmpint (xaccTransOrder_num_action (txnA, NULL, txnB, NULL), ==, qof_instance_guid_compare (txnA, txnB)); txnB->description = CACHE_INSERT ("Salt Peanuts"); g_assert_cmpint (xaccTransOrder_num_action (txnA, NULL, txnB, NULL), >=, 1); txnB->date_entered += 1; g_assert_cmpint (xaccTransOrder_num_action (txnA, NULL, txnB, NULL), ==, -1); txnB->num = CACHE_INSERT ("101"); g_assert_cmpint (xaccTransOrder_num_action (txnA, NULL, txnB, NULL), ==, 1); txnA->num = CACHE_INSERT ("12a"); g_assert_cmpint (xaccTransOrder_num_action (txnA, NULL, txnB, NULL), ==, -1); txnB->num = CACHE_INSERT ("12c"); g_assert_cmpint (xaccTransOrder_num_action (txnA, NULL, txnB, NULL), ==, -1); txnB->num = CACHE_INSERT ("12"); g_assert_cmpint (xaccTransOrder_num_action (txnA, NULL, txnB, NULL), ==, 1); txnB->num = CACHE_INSERT ("one-oh-one"); g_assert_cmpint (xaccTransOrder_num_action (txnA, NULL, txnB, NULL), ==, -1); g_assert_cmpint (xaccTransOrder_num_action (txnA, "24", txnB, "42"), ==, -1); txnB->date_posted -= 1; g_assert_cmpint (xaccTransOrder_num_action (txnA, "24", txnB, "42"), ==, 1); fixture->func->xaccFreeTransaction (txnB); } /* xaccTransSetDateInternal Local: 7:0:0 * set_gains_date_dirty Local: 4:0:0 * xaccTransSetDatePostedSecs C: 17 in 13 Local: 0:0:0 * xaccTransSetDatePostedGDate C: 1 Local: 1:0:0 * xaccTransSetDateEnteredSecs C: 10 in 9 Local: 0:0:0 * xaccTransSetDate C: 43 in 23 SCM: 2 in 2 Local: 0:0:0 * xaccTransSetTxnType C: 4 in 3 Local: 0:0:0 * xaccTransClearReadOnly C: 4 in 2 Local: 1:0:0 * xaccTransSetReadOnly C: 2 in 2 Local: 1:0:0 * qofTransSetNum Local: 0:1:0 * xaccTransSetNum C: 13 in 12 SCM: 3 in 3 Local: 2:0:0 * qofTransSetDescription Local: 0:0:0 * xaccTransSetDescription C: 20 in 18 SCM: 5 in 3 Local: 2:0:0 * qofTransSetNotes Local: 0:0:0 * xaccTransSetNotes C: 5 in 5 SCM: 3 in 3 Local: 1:0:0 * xaccTransSetIsClosingTxn C: 1 Local: 0:0:0 * xaccTransGetSplit C: 57 in 24 SCM: 30 in 21 Local: 0:0:0 * xaccTransGetSplitIndex C: 7 in 2 Local: 0:0:0 * xaccTransGetSplitList C: 23 in 15 SCM: 19 in 15 Local: 2:1:0 * xaccTransCountSplits C: 17 in 9 SCM: 2 in 2 Local: 0:0:0 * xaccTransGetNum C: 15 in 12 SCM: 13 in 13 Local: 0:1:0 * xaccTransGetDescription C: 43 in 23 SCM: 9 in 9 Local: 0:2:0 * xaccTransGetNotes C: 8 in 6 SCM: 7 in 7 Local: 0:1:0 * xaccTransGetIsClosingTxn SCM: 1 Local: 0:1:0 * xaccTransGetDate C: 42 in 19 Local: 0:0:0 * xaccTransGetDatePostedTS C: 6 in 5 Local: 1:0:0 * xaccTransGetDateEnteredTS C: 1 Local: 0:0:0 * xaccTransRetDatePostedTS C: 10 in 6 Local: 1:1:0 * xaccTransGetDatePostedGDate C: 1 Local: 1:0:0 * xaccTransRetDateEnteredTS C: 1 Local: 0:1:0 * xaccTransGetDateDueTS C: 1 Local: 1:0:0 * xaccTransRetDateDueTS C: 1 SCM: 2 in 2 Local: 0:1:0 * xaccTransGetTxnType C: 3 in 2 SCM: 12 in 6 Local: 0:1:0*/ static void test_xaccTransGetTxnType (Fixture *fixture, gconstpointer pData) { const char i = 'I'; const char p = 'P'; auto txn = fixture->txn; xaccTransSetTxnType(txn, i); g_assert_cmpint (i, ==, xaccTransGetTxnType(txn)); xaccTransSetTxnType(txn, p); g_assert_cmpint (p, ==, xaccTransGetTxnType(txn)); } /* xaccTransGetReadOnly C: 7 in 5 Local: 1:0:0 * xaccTransIsReadonlyByPostedDate C: 2 in 2 Local: 0:0:0 * xaccTransHasReconciledSplitsByAccount Local: 1:0:0 * xaccTransHasReconciledSplits C: 4 in 3 Local: 0:0:0 * xaccTransHasSplitsInStateByAccount Local: 1:0:0 * xaccTransHasSplitsInState C: 4 in 1 Local: 0:0:0 * counter_thunk Local: 0:1:0 * gnc_book_count_transactions C: 3 in 2 Local: 0:0:0 * xaccTransGetVoidStatus C: 3 in 2 SCM: 1 Local: 0:1:0 * An absurdly long list of trivial accessors which don't need to be tested. */ /* xaccTransVoid void xaccTransVoid(Transaction *trans, const char *reason)// C: 1 SCM: 2 in 2 Local: 0:0:0 * xaccTransUnvoid void xaccTransUnvoid (Transaction *trans)// C: 1 Local: 0:0:0 */ static void test_xaccTransSetDocLink (Fixture *fixture, gconstpointer pData) { auto trans = fixture->txn; g_assert_cmpstr (xaccTransGetDocLink (trans), ==, NULL); xaccTransSetDocLink (trans, "doclink"); g_assert_cmpstr (xaccTransGetDocLink (trans), ==, "doclink"); xaccTransSetDocLink (trans, "unset"); g_assert_cmpstr (xaccTransGetDocLink (trans), ==, "unset"); xaccTransSetDocLink (trans, ""); g_assert_cmpstr (xaccTransGetDocLink (trans), ==, NULL); xaccTransSetDocLink (trans, NULL); g_assert_cmpstr (xaccTransGetDocLink (trans), ==, NULL); } static void test_xaccTransVoid (Fixture *fixture, gconstpointer pData) { /* Actual function variables start here. */ auto frame = fixture->txn->inst.kvp_data; auto void_reason = "Voided for Unit Test"; auto txn_notes = g_strdup (frame->get_slot({trans_notes_str})->get()); time64 now = gnc_time(NULL); char iso8601_str[ISO_DATELENGTH + 1] = ""; GList *split = NULL; xaccTransVoid (fixture->txn, void_reason); g_assert_cmpstr (frame->get_slot({trans_notes_str})->get(), ==, "Voided transaction"); g_assert_cmpstr (frame->get_slot({void_former_notes_str})->get(), ==, txn_notes); g_assert_cmpstr (frame->get_slot({void_reason_str})->get(), ==, void_reason); gnc_time64_to_iso8601_buff (now, iso8601_str); g_assert_cmpstr (frame->get_slot({void_time_str})->get(), ==, iso8601_str); g_assert_cmpstr (frame->get_slot({TRANS_READ_ONLY_REASON})->get(), ==, "Transaction Voided"); for (split = fixture->txn->splits; split; split=g_list_next (split)) { g_assert (gnc_numeric_zero_p (((Split*)(split->data))->value)); g_assert (gnc_numeric_zero_p (((Split*)(split->data))->amount)); } xaccTransUnvoid (fixture->txn); g_assert_cmpstr (frame->get_slot({trans_notes_str})->get(), ==, txn_notes); g_assert (frame->get_slot({void_former_notes_str}) == NULL); g_assert (frame->get_slot({void_reason_str}) == NULL); g_assert (frame->get_slot({void_time_str}) == NULL); g_assert (frame->get_slot({TRANS_READ_ONLY_REASON}) == NULL); for (split = fixture->txn->splits; split; split=g_list_next (split)) { g_assert (!gnc_numeric_zero_p (((Split*)(split->data))->value)); g_assert (!gnc_numeric_zero_p (((Split*)(split->data))->amount)); } g_free (txn_notes); } /* xaccTransReverse Transaction * xaccTransReverse (Transaction *orig)// C: 2 in 2 Local: 0:0:0 */ static void test_xaccTransReverse (Fixture *fixture, gconstpointer pData) { Transaction *rev = xaccTransReverse (fixture->txn); auto frame = fixture->txn->inst.kvp_data; GList *orig_splits = NULL, *rev_splits = NULL; g_assert (guid_equal (frame->get_slot({TRANS_REVERSED_BY})->get(), xaccTransGetGUID (rev))); g_assert (!qof_instance_is_dirty (QOF_INSTANCE (rev))); //Cleared by commit g_assert_cmpint (g_list_length (fixture->txn->splits), ==, g_list_length (rev->splits)); for (orig_splits = fixture->txn->splits, rev_splits = g_list_reverse (rev->splits); orig_splits && rev_splits; orig_splits = g_list_next (orig_splits), rev_splits = g_list_next (rev_splits)) { auto orig_split = static_cast(orig_splits->data); auto rev_split = static_cast(rev_splits->data); g_assert (gnc_numeric_equal (orig_split->amount, gnc_numeric_neg (rev_split->amount))); g_assert (gnc_numeric_equal (orig_split->value, gnc_numeric_neg (rev_split->value))); g_assert_cmpint (xaccSplitGetReconcile (rev_split), ==, NREC); } fixture->func->xaccFreeTransaction (rev); } /* xaccTransGetReversedBy C: 2 in 2 Local: 0:0:0 * Trivial getter. */ /* xaccTransScrubSplits C: 1 Local: 0:0:0 * Trivial pass-through. */ /* xaccTransScrubGainsDate static void xaccTransScrubGainsDate (Transaction *trans)// Local: 1:0:0 */ static void test_xaccTransScrubGainsDate_no_dirty (GainsFixture *fixture, gconstpointer pData) { auto base_split = static_cast(g_list_nth_data (fixture->base.txn->splits, 1)); auto gains_split = base_split->gains_split; base_split->gains = GAINS_STATUS_GAINS; gains_split->gains = GAINS_STATUS_GAINS; fixture->base.func->xaccTransScrubGainsDate (fixture->base.txn); g_assert (fixture->base.txn->date_posted != fixture->gains_txn->date_posted); g_assert_cmphex (base_split->gains & GAINS_STATUS_DATE_DIRTY, ==, 0); g_assert_cmphex (base_split->gains_split->gains & GAINS_STATUS_DATE_DIRTY, ==, 0); } static void test_xaccTransScrubGainsDate_base_dirty (GainsFixture *fixture, gconstpointer pData) { auto base_split = static_cast(g_list_nth_data (fixture->base.txn->splits, 1)); auto gains_split = base_split->gains_split; base_split->gains = GAINS_STATUS_GAINS | GAINS_STATUS_DATE_DIRTY; gains_split->gains = GAINS_STATUS_GAINS; fixture->base.func->xaccTransScrubGainsDate (fixture->base.txn); g_assert (fixture->base.txn->date_posted == fixture->gains_txn->date_posted); g_assert_cmphex (base_split->gains & GAINS_STATUS_DATE_DIRTY, ==, 0); g_assert_cmphex (base_split->gains_split->gains & GAINS_STATUS_DATE_DIRTY, ==, 0); } static void test_xaccTransScrubGainsDate_gains_dirty (GainsFixture *fixture, gconstpointer pData) { auto base_split = static_cast(g_list_nth_data (fixture->base.txn->splits, 1)); auto gains_split = base_split->gains_split; base_split->gains = GAINS_STATUS_GAINS; gains_split->gains = GAINS_STATUS_GAINS | GAINS_STATUS_DATE_DIRTY; fixture->base.func->xaccTransScrubGainsDate (fixture->base.txn); g_assert (fixture->base.txn->date_posted == fixture->gains_txn->date_posted); g_assert_cmphex (base_split->gains & GAINS_STATUS_DATE_DIRTY, ==, 0); g_assert_cmphex (base_split->gains_split->gains & GAINS_STATUS_DATE_DIRTY, ==, 0); } /* xaccTransScrubGains Local: 1:0:0 * Non-trivial, but it passes through selected splits to functions in * cap-gains.c and Scrub3.c that are beyond the scope of this test * program. */ /* xaccTransFindSplitByAccount C: 7 in 5 Local: 0:0:0 * destroy_tx_on_book_close Local: 0:1:0 * gnc_transaction_book_end Local: 0:1:0 * trans_is_balanced_p Local: 0:1:0 * Trivial pass-through. */ void test_suite_transaction (void) { GNC_TEST_ADD (suitename, "check open", Fixture, NULL, setup, test_check_open, teardown); GNC_TEST_ADD (suitename, "xaccTransStillHasSplit", Fixture, NULL, setup, test_xaccTransStillHasSplit, teardown); GNC_TEST_ADD (suitename, "mark trans", Fixture, NULL, setup, test_mark_trans, teardown); GNC_TEST_ADD (suitename, "gen event trans", Fixture, NULL, setup, test_gen_event_trans, teardown); GNC_TEST_ADD_FUNC (suitename, "gnc transaction init", test_gnc_transaction_init); GNC_TEST_ADD_FUNC (suitename, "gnc transaction dispose", test_gnc_transaction_dispose); GNC_TEST_ADD_FUNC (suitename, "gnc transaction finalize", test_gnc_transaction_finalize); GNC_TEST_ADD (suitename, "gnc transaction set/get property", Fixture, NULL, setup, test_gnc_transaction_set_get_property, teardown); GNC_TEST_ADD (suitename, "xaccMallocTransaction", Fixture, NULL, setup, test_xaccMallocTransaction, teardown); GNC_TEST_ADD (suitename, "xaccTransSortSplits", Fixture, NULL, setup, test_xaccTransSortSplits, teardown); GNC_TEST_ADD (suitename, "dupe_trans", Fixture, NULL, setup, test_dupe_trans, teardown); GNC_TEST_ADD (suitename, "xaccTransClone", Fixture, NULL, setup, test_xaccTransClone, teardown); GNC_TEST_ADD (suitename, "xaccTransCopyFromClipBoard", Fixture, NULL, setup, test_xaccTransCopyFromClipBoard, teardown); GNC_TEST_ADD (suitename, "xaccTransCopyFromClipBoard No-Start", Fixture, NULL, setup, test_xaccTransCopyFromClipBoard_no_start, teardown); GNC_TEST_ADD (suitename, "xaccFreeTransaction", Fixture, NULL, setup, test_xaccFreeTransaction, teardown); // GNC_TEST_ADD (suitename, "compare split guids", Fixture, NULL, setup, test_compare_split_guids, teardown); GNC_TEST_ADD (suitename, "xaccTransEqual", Fixture, NULL, setup, test_xaccTransEqual, teardown); GNC_TEST_ADD (suitename, "xaccTransLookup", Fixture, NULL, setup, test_xaccTransLookup, teardown); GNC_TEST_ADD (suitename, "xaccTransGetImbalanceValue", Fixture, NULL, setup, test_xaccTransGetImbalanceValue, teardown); GNC_TEST_ADD (suitename, "xaccTransGetImbalance", Fixture, NULL, setup, test_xaccTransGetImbalance, teardown); GNC_TEST_ADD (suitename, "xaccTransGetImbalance Trading Accounts", Fixture, NULL, setup, test_xaccTransGetImbalance_trading, teardown); GNC_TEST_ADD (suitename, "xaccTransIsBalanced", Fixture, NULL, setup, test_xaccTransIsBalanced, teardown); GNC_TEST_ADD (suitename, "xaccTransIsBalanced Trading Accounts", Fixture, NULL, setup, test_xaccTransIsBalanced_trading, teardown); GNC_TEST_ADD (suitename, "xaccTransGetAccountValue", Fixture, NULL, setup, test_xaccTransGetAccountValue, teardown); GNC_TEST_ADD (suitename, "xaccTransGetRateForCommodity", Fixture, NULL, setup, test_xaccTransGetRateForCommodity, teardown); GNC_TEST_ADD (suitename, "xaccTransGetAccountAmount", Fixture, NULL, setup, test_xaccTransGetAccountAmount, teardown); GNC_TEST_ADD (suitename, "xaccTransGetAccountConvRate", Fixture, NULL, setup, test_xaccTransGetAccountConvRate, teardown); GNC_TEST_ADD (suitename, "xaccTransGetAccountBalance", Fixture, NULL, setup, test_xaccTransGetAccountBalance, teardown); GNC_TEST_ADD (suitename, "xaccTransSetCurrency", Fixture, NULL, setup, test_xaccTransSetCurrency, teardown); GNC_TEST_ADD_FUNC (suitename, "xaccTransBeginEdit", test_xaccTransBeginEdit); GNC_TEST_ADD (suitename, "xaccTransDestroy", Fixture, NULL, setup, test_xaccTransDestroy, teardown); GNC_TEST_ADD (suitename, "destroy gains", GainsFixture, NULL, setup_with_gains, test_destroy_gains, teardown_with_gains); GNC_TEST_ADD (suitename, "do destroy", GainsFixture, NULL, setup_with_gains, test_do_destroy, teardown_with_gains); GNC_TEST_ADD (suitename, "was trans emptied", Fixture, NULL, setup, test_was_trans_emptied, teardown); GNC_TEST_ADD (suitename, "trans on error", Fixture, NULL, setup, test_trans_on_error, teardown); GNC_TEST_ADD (suitename, "trans cleanup commit", Fixture, NULL, setup, test_trans_cleanup_commit, teardown); GNC_TEST_ADD_FUNC (suitename, "xaccTransCommitEdit", test_xaccTransCommitEdit); GNC_TEST_ADD (suitename, "xaccTransRollbackEdit", Fixture, NULL, setup, test_xaccTransRollbackEdit, teardown); GNC_TEST_ADD (suitename, "xaccTransRollbackEdit - Backend Errors", Fixture, NULL, setup, test_xaccTransRollbackEdit_BackendErrors, teardown); GNC_TEST_ADD (suitename, "xaccTransOrder_num_action", Fixture, NULL, setup, test_xaccTransOrder_num_action, teardown); GNC_TEST_ADD (suitename, "xaccTransGetTxnType", Fixture, NULL, setup, test_xaccTransGetTxnType, teardown); GNC_TEST_ADD (suitename, "xaccTransSetDocLink", Fixture, NULL, setup, test_xaccTransSetDocLink, teardown); GNC_TEST_ADD (suitename, "xaccTransVoid", Fixture, NULL, setup, test_xaccTransVoid, teardown); GNC_TEST_ADD (suitename, "xaccTransReverse", Fixture, NULL, setup, test_xaccTransReverse, teardown); GNC_TEST_ADD (suitename, "xaccTransScrubGainsDate_no_dirty", GainsFixture, NULL, setup_with_gains, test_xaccTransScrubGainsDate_no_dirty, teardown_with_gains); GNC_TEST_ADD (suitename, "xaccTransScrubGainsDate_base_dirty", GainsFixture, NULL, setup_with_gains, test_xaccTransScrubGainsDate_base_dirty, teardown_with_gains); GNC_TEST_ADD (suitename, "xaccTransScrubGainsDate_gains_dirty", GainsFixture, NULL, setup_with_gains, test_xaccTransScrubGainsDate_gains_dirty, teardown_with_gains); }