diff --git a/gnucash/import-export/test/CMakeLists.txt b/gnucash/import-export/test/CMakeLists.txt index 11e92a624f..8d197e57c7 100644 --- a/gnucash/import-export/test/CMakeLists.txt +++ b/gnucash/import-export/test/CMakeLists.txt @@ -22,5 +22,22 @@ gnc_add_test(test-link-generic-import test-link.c gnc_add_test(test-import-pending-matches test-import-pending-matches.cpp GENERIC_IMPORT_TEST_INCLUDE_DIRS GENERIC_IMPORT_TEST_LIBS ) + +set(IMPORT_ACCOUNT_MATCHER_TEST_INCLUDE_DIRS + ${CMAKE_BINARY_DIR}/common # for config.h + ${CMAKE_SOURCE_DIR}/gnucash/import-export + ${CMAKE_SOURCE_DIR}/libgnucash/engine + ${CMAKE_SOURCE_DIR}/libgnucash/app-utils + ${CMAKE_SOURCE_DIR}/gnucash/gnome-utils + ${GLIB2_INCLUDE_DIRS} + ${GTK3_INCLUDE_DIRS} + ${GTEST_INCLUDE_DIR} + ) + +set(IMPORT_ACCOUNT_MATCHER_TEST_LIBS gncmod-generic-import gncmod-engine test-core ${GTEST_LIB}) +gnc_add_test(test-import-account-matcher gtest-import-account-matcher.cpp + IMPORT_ACCOUNT_MATCHER_TEST_INCLUDE_DIRS IMPORT_ACCOUNT_MATCHER_TEST_LIBS) + set_dist_list(test_generic_import_DIST CMakeLists.txt - test-link.c test-import-parse.c test-import-pending-matches.cpp) + test-link.c test-import-parse.c test-import-pending-matches.cpp + gtest-import-account-matcher.cpp) diff --git a/gnucash/import-export/test/gtest-import-account-matcher.cpp b/gnucash/import-export/test/gtest-import-account-matcher.cpp new file mode 100644 index 0000000000..468b935c8b --- /dev/null +++ b/gnucash/import-export/test/gtest-import-account-matcher.cpp @@ -0,0 +1,182 @@ +/******************************************************************** + * gtest-import-account-matcher.cpp -- * + * unit tests import-account-matcher. * + * Copyright (C) 2020 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, 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 +#include +#include +#include +} +#include + +using AccountV = std::vector; +using AccountTypeV = std::vector; +using AccountPair = std::pair; + +class ImportMatcherTest : public ::testing::Test +{ +protected: + ImportMatcherTest() : + m_book{gnc_get_current_book()}, m_root{gnc_account_create_root(m_book)} + { + auto create_account = [this](Account* parent, GNCAccountType type, + const char* name, + const char* online)->Account* { + auto account = xaccMallocAccount(this->m_book); + xaccAccountBeginEdit(account); + xaccAccountSetType(account, type); + xaccAccountSetName(account, name); + xaccAccountBeginEdit(parent); + gnc_account_append_child(parent, account); + if (online) + qof_instance_set(QOF_INSTANCE(account), "online-id", online, NULL); + xaccAccountCommitEdit(parent); + xaccAccountCommitEdit(account); + return account; + }; + auto assets = create_account(m_root, ACCT_TYPE_ASSET, + "Assets", nullptr); + auto liabilities = create_account(m_root, ACCT_TYPE_LIABILITY, + "Liabilities", nullptr); + auto expenses = create_account(m_root, ACCT_TYPE_EXPENSE, + "Expenses", nullptr); + create_account(assets, ACCT_TYPE_BANK, "Bank", "Bank"); + auto broker = create_account(assets, ACCT_TYPE_ASSET, + "Broker", "Broker"); + auto stocks = create_account(broker, ACCT_TYPE_STOCK, + "Stocks", "BrokerStocks"); + create_account(stocks, ACCT_TYPE_STOCK, "AAPL", "BrokerStocksAAPL"); + create_account(stocks, ACCT_TYPE_STOCK, "MSFT", "BrokerStocksMSFT "); + create_account(stocks, ACCT_TYPE_STOCK, "HPE", "BrokerStocksHPE"); + create_account(broker, ACCT_TYPE_BANK, "Cash Management", + "BrokerCash Management"); + create_account(expenses, ACCT_TYPE_EXPENSE, "Food", nullptr); + create_account(expenses, ACCT_TYPE_EXPENSE, "Gas", nullptr); + create_account(expenses, ACCT_TYPE_EXPENSE, "Rent", nullptr); + } + ~ImportMatcherTest() + { + xaccAccountBeginEdit(m_root); + xaccAccountDestroy(m_root); //It does the commit + gnc_clear_current_session(); + } + + QofBook* m_book; + Account* m_root; +}; + +TEST_F(ImportMatcherTest, test_simple_match) +{ + auto found = gnc_import_select_account(nullptr, "Bank", FALSE, nullptr, + nullptr, ACCT_TYPE_NONE, nullptr, + nullptr); + ASSERT_NE(nullptr, found); + EXPECT_STREQ("Bank", xaccAccountGetName(found)); +} + +TEST_F(ImportMatcherTest, test_noisy_match) +{ + auto found = gnc_import_select_account(nullptr, "BankUSD", FALSE, nullptr, + nullptr, ACCT_TYPE_NONE, nullptr, + nullptr); + ASSERT_NE(nullptr, found); + EXPECT_STREQ("Bank", xaccAccountGetName(found)); +} + +TEST_F(ImportMatcherTest, test_match_with_subaccounts) +{ + auto found = gnc_import_select_account(nullptr, "BrokerStocks", FALSE, + nullptr, nullptr, ACCT_TYPE_NONE, + nullptr, nullptr); + ASSERT_NE(nullptr, found); + // Should be equal + EXPECT_STRNE("Stocks", xaccAccountGetName(found)); +} + +TEST_F(ImportMatcherTest, test_subaccount_match) +{ + auto found = gnc_import_select_account(nullptr, "BrokerStocksHPE", FALSE, + nullptr, nullptr, ACCT_TYPE_NONE, + nullptr, nullptr); + ASSERT_NE(nullptr, found); + //should be equal + EXPECT_STRNE("HPE", xaccAccountGetName(found)); +} + +TEST_F(ImportMatcherTest, test_subaccount_match_trailing_noise) +{ + auto found = gnc_import_select_account(nullptr, "BrokerStocksHPEUSD", FALSE, + nullptr, nullptr, ACCT_TYPE_NONE, + nullptr, nullptr); + // Should be equal + EXPECT_STRNE("HPE", xaccAccountGetName(found)); +} + +TEST_F(ImportMatcherTest, test_subaccount_no_match) +{ + auto found = gnc_import_select_account(nullptr, "BrokerStocksINTC", FALSE, + nullptr, nullptr, ACCT_TYPE_NONE, + nullptr, nullptr); +/* We really want nullptr in this case, but the algorithm can't tell the + * difference between trailing noise and a new security that needs a new + * account. + */ + ASSERT_NE(nullptr, found); + EXPECT_STREQ("Stocks", xaccAccountGetName(found)); +} + +TEST_F(ImportMatcherTest, test_subaccount_match_trailing_space) +{ + auto found = gnc_import_select_account(nullptr, "BrokerStocksMSFT ", FALSE, + nullptr, nullptr, ACCT_TYPE_NONE, + nullptr, nullptr); + ASSERT_NE(nullptr, found); + //Should be equal + EXPECT_STRNE("MSFT", xaccAccountGetName(found)); +} + +TEST_F(ImportMatcherTest, test_subaccount_match_trim_trailing_space) +{ + auto found = gnc_import_select_account(nullptr, "BrokerStocksMSFT", FALSE, + nullptr, nullptr, ACCT_TYPE_NONE, + nullptr, nullptr); + ASSERT_NE(nullptr, found); + //Should be equal + EXPECT_STRNE("MSFT", xaccAccountGetName(found)); +} + +TEST_F(ImportMatcherTest, test_subaccount_match_internal_space) +{ + auto found = gnc_import_select_account(nullptr, "BrokerCash Management", + FALSE, nullptr, nullptr, + ACCT_TYPE_NONE, nullptr, nullptr); + ASSERT_NE(nullptr, found); + // Should be equal, of course + EXPECT_STRNE("Cash Management", xaccAccountGetName(found)); +}