mirror of https://github.com/Gnucash/gnucash
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
335 lines
10 KiB
335 lines
10 KiB
/********************************************************************
|
|
* test-autoclear.c: test suite for Auto-Clear *
|
|
* Copyright 2020 Cristian Klein <cristian@kleinlabs.eu> *
|
|
* *
|
|
* 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 "config.h"
|
|
#include <glib.h>
|
|
// GoogleTest is written in C++, however, the function we test in C.
|
|
#include "../gnc-autoclear.h"
|
|
#include <memory>
|
|
#include <cstdint>
|
|
#include <Account.h>
|
|
#include <Split.h>
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wcpp"
|
|
#include <gtest/gtest.h>
|
|
#pragma GCC diagnostic pop
|
|
|
|
|
|
static const int64_t DENOM = 100; //< Denominator is always 100 for simplicity.
|
|
|
|
struct SplitDatum {
|
|
const char *memo;
|
|
gint64 amount; //< Numerator of amount.
|
|
bool cleared;
|
|
};
|
|
|
|
struct Tests {
|
|
gint64 amount;
|
|
const char *expectedErr;
|
|
};
|
|
|
|
struct TestCase {
|
|
std::vector<SplitDatum> splits;
|
|
std::vector<Tests> tests;
|
|
};
|
|
|
|
TestCase easyTestCase = {
|
|
.splits = {
|
|
{ "Memo 01", - 8234, true },
|
|
{ "Memo 02", -156326, true },
|
|
{ "Memo 03", - 4500, true },
|
|
{ "Memo 04", -694056, true },
|
|
{ "Memo 05", - 7358, true },
|
|
{ "Memo 06", - 11700, true },
|
|
{ "Memo 07", - 20497, true },
|
|
{ "Memo 08", - 11900, true },
|
|
{ "Memo 09", - 8275, true },
|
|
{ "Memo 10", - 58700, true },
|
|
{ "Memo 11", +100000, true },
|
|
{ "Memo 12", - 13881, true },
|
|
{ "Memo 13", - 5000, true },
|
|
{ "Memo 14", +200000, true },
|
|
{ "Memo 15", - 16800, true },
|
|
{ "Memo 16", -152000, true },
|
|
{ "Memo 17", +160000, false },
|
|
{ "Memo 18", - 63610, false },
|
|
{ "Memo 19", - 2702, false },
|
|
{ "Memo 20", - 15400, false },
|
|
{ "Memo 21", - 3900, false },
|
|
{ "Memo 22", - 22042, false },
|
|
{ "Memo 23", - 2900, false },
|
|
{ "Memo 24", - 10900, false },
|
|
{ "Memo 25", - 44400, false },
|
|
{ "Memo 26", - 9200, false },
|
|
{ "Memo 27", - 7900, false },
|
|
{ "Memo 28", - 1990, false },
|
|
{ "Memo 29", - 7901, false },
|
|
{ "Memo 30", - 61200, false },
|
|
},
|
|
.tests = {
|
|
{ 0, "The selected amount cannot be cleared.", },
|
|
{ -869227, "Account is already at Auto-Clear Balance." }, // No splits need to be cleared.
|
|
{ -869300, "The selected amount cannot be cleared." },
|
|
{ -869230, NULL },
|
|
{ -963272, NULL }, // All splits need to be cleared.
|
|
},
|
|
};
|
|
|
|
TestCase ambiguousTestCase = {
|
|
.splits = {
|
|
{ "Memo 01", -10, false },
|
|
{ "Memo 02", -10, false },
|
|
{ "Memo 03", -10, false },
|
|
},
|
|
.tests = {
|
|
{ -10, "Cannot uniquely clear splits. Found multiple possibilities." },
|
|
{ -20, "Cannot uniquely clear splits. Found multiple possibilities." },
|
|
|
|
// -30 can be cleared by returning all three -10 splits
|
|
{ -30, nullptr },
|
|
},
|
|
};
|
|
|
|
|
|
TestCase sequentialTestCase1 =
|
|
{
|
|
.splits = {
|
|
{ "Memo 01", 2, false },
|
|
{ "Memo 02", 3, false },
|
|
{ "Memo 03", 5, false },
|
|
},
|
|
.tests = {
|
|
{ 1, "The selected amount cannot be cleared." },
|
|
{ 4, "The selected amount cannot be cleared." },
|
|
{ 5, "Cannot uniquely clear splits. Found multiple possibilities." },
|
|
{ 6, "The selected amount cannot be cleared." },
|
|
{ 9, "The selected amount cannot be cleared." },
|
|
{ 11, "The selected amount cannot be cleared." },
|
|
{ 12, "The selected amount cannot be cleared." },
|
|
{ 2, nullptr },
|
|
{ 7, nullptr },
|
|
{ 10, nullptr },
|
|
},
|
|
};
|
|
|
|
TestCase sequentialTestCase2 =
|
|
{
|
|
.splits = {
|
|
{ "Memo 01", 2, false },
|
|
{ "Memo 02", 3, false },
|
|
{ "Memo 03", 5, false },
|
|
},
|
|
.tests = {
|
|
{ 3, nullptr },
|
|
{ 8, nullptr },
|
|
{ 10, nullptr },
|
|
},
|
|
};
|
|
|
|
|
|
TestCase sequentialTestCase3 =
|
|
{
|
|
.splits = {
|
|
{ "Memo 01", 2, false },
|
|
{ "Memo 02", 3, false },
|
|
{ "Memo 03", 5, false },
|
|
{ "Memo 04", 5, false },
|
|
},
|
|
.tests = {
|
|
{ 5, "Cannot uniquely clear splits. Found multiple possibilities." },
|
|
{ 7, "Cannot uniquely clear splits. Found multiple possibilities." },
|
|
{ 10, "Cannot uniquely clear splits. Found multiple possibilities." },
|
|
{ 2, nullptr },
|
|
{ 12, nullptr },
|
|
{ 15, nullptr },
|
|
},
|
|
};
|
|
|
|
TestCase sequentialTestCase4 =
|
|
{
|
|
.splits = {
|
|
{ "Memo 01", 2, false },
|
|
{ "Memo 02", 3, false },
|
|
{ "Memo 03", 5, false },
|
|
{ "Memo 04", 5, false },
|
|
},
|
|
.tests = {
|
|
{ 3, nullptr },
|
|
{ 13, nullptr },
|
|
{ 15, nullptr },
|
|
},
|
|
};
|
|
|
|
TestCase slowTestCase =
|
|
{
|
|
.splits = {
|
|
{ "Memo 01", 1, false },
|
|
{ "Memo 02", 2, false },
|
|
{ "Memo 03", 4, false },
|
|
{ "Memo 03", 8, false },
|
|
{ "Memo 03", 16, false },
|
|
{ "Memo 03", 32, false },
|
|
{ "Memo 03", 64, false },
|
|
{ "Memo 03", 128, false },
|
|
{ "Memo 03", 256, false },
|
|
{ "Memo 03", 512, false },
|
|
{ "Memo 03", 1024, false },
|
|
{ "Memo 03", 2048, false },
|
|
{ "Memo 03", 4096, false },
|
|
{ "Memo 03", 8192, false },
|
|
{ "Memo 03", 16384, false },
|
|
{ "Memo 03", 32768, false },
|
|
{ "Memo 03", 65536, false },
|
|
{ "Memo 03", 131072, false },
|
|
{ "Memo 03", 262144, false },
|
|
{ "Memo 03", 524288, false },
|
|
{ "Memo 03", 1048576, false },
|
|
},
|
|
.tests = {
|
|
{ 6000000, "The selected amount cannot be cleared." },
|
|
{ 5000000, "The selected amount cannot be cleared." },
|
|
{ 4000000, "The selected amount cannot be cleared." },
|
|
{ 3000000, "The selected amount cannot be cleared." },
|
|
{ 2000000, nullptr },
|
|
},
|
|
};
|
|
|
|
TestCase mixedSignsTestCase =
|
|
{
|
|
.splits = {
|
|
|
|
{ "memo 01", 30000 },
|
|
{ "memo 01", -20000 },
|
|
{ "memo 01", 10000 },
|
|
{ "memo 01", 500 },
|
|
{ "memo 01", -400 },
|
|
{ "memo 01", 300 }
|
|
},
|
|
.tests = {
|
|
{ 30300, nullptr },
|
|
},
|
|
};
|
|
|
|
TestCase testwithzerosTestCase =
|
|
{
|
|
.splits = {
|
|
{ "memo 01", 10000 },
|
|
{ "memo 01", 0 },
|
|
{ "memo 01", 10000 },
|
|
{ "memo 01", 5000 },
|
|
},
|
|
.tests = {
|
|
{ 19999, "The selected amount cannot be cleared." },
|
|
{ 20001, "The selected amount cannot be cleared." },
|
|
{ 20000, nullptr },
|
|
},
|
|
};
|
|
|
|
|
|
class AutoClearTest : public ::testing::TestWithParam<TestCase *> {
|
|
protected:
|
|
std::shared_ptr<QofBook> m_book;
|
|
Account *m_account; // owned by m_book
|
|
TestCase &m_testCase;
|
|
|
|
public:
|
|
AutoClearTest() :
|
|
m_book(qof_book_new(), qof_book_destroy),
|
|
m_account(xaccMallocAccount(m_book.get())),
|
|
m_testCase(*GetParam())
|
|
{
|
|
std::cout << "\n\ncreating new account with splits";
|
|
xaccAccountSetName(m_account, "Test Account");
|
|
xaccAccountBeginEdit(m_account);
|
|
for (auto &d : m_testCase.splits) {
|
|
Split *split = xaccMallocSplit(m_book.get());
|
|
xaccSplitSetMemo(split, d.memo);
|
|
xaccSplitSetAmount(split, gnc_numeric_create(d.amount, DENOM));
|
|
xaccSplitSetReconcile(split, d.cleared ? CREC : NREC);
|
|
xaccSplitSetAccount(split, m_account);
|
|
std::cout << ' ' << d.amount;
|
|
|
|
gnc_account_insert_split(m_account, split);
|
|
}
|
|
xaccAccountCommitEdit(m_account);
|
|
std::cout << std::endl;
|
|
}
|
|
};
|
|
|
|
TEST_P(AutoClearTest, DoesAutoClear) {
|
|
for (auto &t : m_testCase.tests) {
|
|
gnc_numeric amount_to_clear = gnc_numeric_create(t.amount, DENOM);
|
|
GError *error = nullptr;
|
|
|
|
GList *splits_to_clear = gnc_account_get_autoclear_splits
|
|
(m_account, amount_to_clear, INT64_MAX, &error, 0);
|
|
|
|
auto err = error ? error->message : nullptr;
|
|
|
|
std::cout << "testing clearing " << t.amount
|
|
<< " should be [" << std::string (err ? err : "ok") << ']'
|
|
<< std::endl;
|
|
|
|
// Actually clear splits
|
|
for (GList *node = splits_to_clear; node; node = node->next) {
|
|
Split *split = (Split *)node->data;
|
|
xaccSplitSetReconcile(split, CREC);
|
|
}
|
|
|
|
g_list_free (splits_to_clear);
|
|
|
|
EXPECT_STREQ(err, t.expectedErr);
|
|
if (t.expectedErr == NULL) {
|
|
gnc_numeric c = xaccAccountGetClearedBalance(m_account);
|
|
EXPECT_EQ(c.num, t.amount);
|
|
EXPECT_EQ(c.denom, DENOM);
|
|
}
|
|
if (error)
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
#ifndef INSTANTIATE_TEST_SUITE_P
|
|
// Silence "no previous declaration for" (treated as error due to -Werror) when building with GoogleTest < 1.8.1
|
|
static testing::internal::ParamGenerator<TestCase*> gtest_InstantiationAutoClearTestAutoClearTest_EvalGenerator_();
|
|
static std::string gtest_InstantiationAutoClearTestAutoClearTest_EvalGenerateName_(const testing::TestParamInfo<TestCase*>&);
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
#else // INSTANTIATE_TEST_SUITE_P
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
#endif // INSTANTIATE_TEST_SUITE_P
|
|
InstantiationAutoClearTest,
|
|
AutoClearTest,
|
|
::testing::Values(
|
|
&easyTestCase,
|
|
&sequentialTestCase1,
|
|
&sequentialTestCase2,
|
|
&sequentialTestCase3,
|
|
&sequentialTestCase4,
|
|
&slowTestCase,
|
|
&mixedSignsTestCase,
|
|
&testwithzerosTestCase,
|
|
&ambiguousTestCase
|
|
)
|
|
);
|