Implement operators >> and << on GncOption.

This commit is contained in:
John Ralls 2019-11-22 15:45:42 -08:00
parent e583c84f4e
commit 883127a59d
6 changed files with 817 additions and 45 deletions

View File

@ -24,9 +24,11 @@
//#include "options.h" //#include "options.h"
#include "gnc-option.hpp" #include "gnc-option.hpp"
#include <gnc-datetime.hpp> #include <gnc-datetime.hpp>
#include <guid.hpp>
extern "C" extern "C"
{ {
#include "gnc-accounting-period.h" #include "gnc-accounting-period.h"
#include "gnc-ui-util.h"
} }
bool bool
@ -129,6 +131,72 @@ GncOptionDateValue::get_value() const
set_day_and_time(now, m_type == DateType::STARTING); set_day_and_time(now, m_type == DateType::STARTING);
return static_cast<time64>(GncDateTime(now)); return static_cast<time64>(GncDateTime(now));
} }
static const char* date_type_str[] {"absolute", "relative"};
static const std::array<const char*, 16> date_period_str
{
"today", "today",
"start-this-month", "end-this-month",
"start-prev-month", "end-prev-month",
"start-current-quarter", "end-current-quarter",
"start-prev-quarter", "end-prev-quarter",
"start-cal-year", "end-cal-year",
"start-prev-year", "end-prev-year",
"start-prev-fin-year", "end-prev-fin-year"
};
std::ostream&
GncOptionDateValue::out_stream(std::ostream& oss) const noexcept
{
if (m_type == DateType::ABSOLUTE)
oss << date_type_str[0] << " . " << m_date;
else
{
int n{ m_type == DateType::STARTING ? 0 : 1};
n += 2 * static_cast<int>(m_period);
oss << date_type_str[1] << " . " << date_period_str[n];
}
return oss;
}
std::istream&
GncOptionDateValue::in_stream(std::istream& iss)
{
std::string type_str;
std::getline(iss, type_str, '.');
if (type_str == "absolute ")
{
time64 time;
iss >> time;
set_value(time);
}
else if (type_str == "relative ")
{
std::string period_str;
iss >> period_str;
auto period = std::find(date_period_str.begin(), date_period_str.end(),
period_str);
if (period == date_period_str.end())
{
std::string err{"Unknown period string in date option: '"};
err += period_str;
err += "'";
throw std::invalid_argument(err);
}
int64_t index = period - date_period_str.begin();
DateType type = index % 2 ? DateType::ENDING : DateType::STARTING;
set_value(std::make_pair(type, index / 2));
}
else
{
std::string err{"Unknown date type string in date option: '"};
err += type_str;
err += "'";
throw std::invalid_argument{err};
}
return iss;
}
void void
GncOptionDateValue::set_value(DateSetterValue value) GncOptionDateValue::set_value(DateSetterValue value)
@ -145,3 +213,57 @@ GncOptionDateValue::set_value(DateSetterValue value)
m_period = static_cast<RelativeDatePeriod>(val); m_period = static_cast<RelativeDatePeriod>(val);
m_date = 0; m_date = 0;
} }
QofInstance*
qof_instance_from_string(const std::string& str, GncOptionUIType type)
{
auto guid{static_cast<GncGUID>(gnc::GUID::from_string(str))};
QofIdType qof_type;
switch(type)
{
case GncOptionUIType::CURRENCY:
case GncOptionUIType::COMMODITY:
qof_type = "Commodity";
break;
case GncOptionUIType::BUDGET:
qof_type = "Budget";
break;
case GncOptionUIType::OWNER:
qof_type = "gncOwner";
break;
case GncOptionUIType::CUSTOMER:
qof_type = "gncCustomer";
break;
case GncOptionUIType::VENDOR:
qof_type = "gncVendor";
break;
case GncOptionUIType::EMPLOYEE:
qof_type = "gncEmployee";
break;
case GncOptionUIType::INVOICE:
qof_type = "gncInvoice";
break;
case GncOptionUIType::TAX_TABLE:
qof_type = "gncTaxtable";
break;
case GncOptionUIType::QUERY:
qof_type = "gncQuery";
break;
case GncOptionUIType::ACCOUNT_LIST:
case GncOptionUIType::ACCOUNT_SEL:
default:
qof_type = "Account";
break;
}
auto book{gnc_get_current_book()};
auto col{qof_book_get_collection(book, qof_type)};
return QOF_INSTANCE(qof_collection_lookup_entity(col, &guid));
}
std::string
qof_instance_to_string(const QofInstance* inst)
{
gnc::GUID guid{*qof_instance_get_guid(inst)};
return guid.to_string();
}

View File

@ -33,7 +33,6 @@ extern "C"
#include <gnc-commodity.h> #include <gnc-commodity.h>
} }
#include <gnc-datetime.hpp> #include <gnc-datetime.hpp>
#include <gnc-numeric.hpp>
#include <libguile.h> #include <libguile.h>
#include <string> #include <string>
#include <utility> #include <utility>
@ -41,6 +40,7 @@ extern "C"
#include <exception> #include <exception>
#include <functional> #include <functional>
#include <variant> #include <variant>
#include <iostream>
/* /*
* Unused base class to document the structure of the current Scheme option * Unused base class to document the structure of the current Scheme option
@ -176,6 +176,28 @@ private:
GncOptionUIType m_ui_type; GncOptionUIType m_ui_type;
}; };
/* These will work when m_value is a built-in class; GnuCash class and container
* values will need specialization unless they happen to define operators << and
* >>.
* Note that SWIG 3.0.12 chokes on the typename = std::enable_if_t<> form so we
* have to use the non-type parameter form.
*/
template<class OptionValueClass, typename std::enable_if_t<std::is_base_of_v<OptionClassifier, std::decay_t<OptionValueClass>>, int> = 0>
std::ostream& operator<<(std::ostream& oss, const OptionValueClass& opt)
{
oss << opt.get_value();
return oss;
}
template<class OptionValueClass, typename std::enable_if_t<std::is_base_of_v<OptionClassifier, std::decay_t<OptionValueClass>>, int> = 0>
std::istream& operator>>(std::istream& iss, OptionValueClass& opt)
{
std::decay_t<decltype(opt.get_value())> value;
iss >> value;
opt.set_value(value);
return iss;
}
template <typename ValueType> template <typename ValueType>
class GncOptionValue : class GncOptionValue :
public OptionClassifier, public OptionUIItem public OptionClassifier, public OptionUIItem
@ -195,11 +217,52 @@ public:
ValueType get_value() const { return m_value; } ValueType get_value() const { return m_value; }
ValueType get_default_value() const { return m_default_value; } ValueType get_default_value() const { return m_default_value; }
void set_value(ValueType new_value) { m_value = new_value; } void set_value(ValueType new_value) { m_value = new_value; }
bool is_changed() const noexcept { return m_value != m_default_value; }
private: private:
ValueType m_value; ValueType m_value;
ValueType m_default_value; ValueType m_default_value;
}; };
QofInstance* qof_instance_from_string(const std::string& str,
GncOptionUIType type);
std::string qof_instance_to_string(const QofInstance* inst);
template<> inline std::ostream&
operator<< <GncOptionValue<bool>>(std::ostream& oss,
const GncOptionValue<bool>& opt)
{
oss << (opt.get_value() ? "#t" : "#f");
return oss;
}
template<> inline std::ostream&
operator<< <GncOptionValue<QofInstance*>>(std::ostream& oss,
const GncOptionValue<QofInstance*>& opt)
{
oss << qof_instance_to_string(opt.get_value());
return oss;
}
template<> inline std::istream&
operator>> <GncOptionValue<bool>>(std::istream& iss,
GncOptionValue<bool>& opt)
{
std::string instr;
iss >> instr;
opt.set_value(instr == "#t" ? true : false);
return iss;
}
template<> inline std::istream&
operator>> <GncOptionValue<QofInstance*>>(std::istream& iss,
GncOptionValue<QofInstance*>& opt)
{
std::string instr;
iss >> instr;
opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
return iss;
}
template <typename ValueType> template <typename ValueType>
class GncOptionValidatedValue : class GncOptionValidatedValue :
public OptionClassifier, public OptionUIItem public OptionClassifier, public OptionUIItem
@ -243,6 +306,7 @@ public:
else else
throw std::invalid_argument("Validation failed, value not set."); throw std::invalid_argument("Validation failed, value not set.");
} }
bool is_changed() const noexcept { return m_value != m_default_value; }
private: private:
ValueType m_value; ValueType m_value;
ValueType m_default_value; ValueType m_default_value;
@ -250,6 +314,25 @@ private:
ValueType m_validation_data; ValueType m_validation_data;
}; };
template<> inline std::ostream&
operator<< <GncOptionValidatedValue<QofInstance*>>(std::ostream& oss,
const GncOptionValidatedValue<QofInstance*>& opt)
{
oss << qof_instance_to_string(opt.get_value());
std::cerr << qof_instance_to_string(opt.get_value());
return oss;
}
template<> inline std::istream&
operator>> <GncOptionValidatedValue<QofInstance*>>(std::istream& iss,
GncOptionValidatedValue<QofInstance*>& opt)
{
std::string instr;
iss >> instr;
opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
return iss;
}
/** /**
* Used for numeric ranges and plot sizes. * Used for numeric ranges and plot sizes.
*/ */
@ -283,6 +366,7 @@ public:
else else
throw std::invalid_argument("Validation failed, value not set."); throw std::invalid_argument("Validation failed, value not set.");
} }
bool is_changed() const noexcept { return m_value != m_default_value; }
private: private:
ValueType m_value; ValueType m_value;
ValueType m_default_value; ValueType m_default_value;
@ -376,6 +460,7 @@ public:
{ {
return std::get<2>(m_choices.at(index)); return std::get<2>(m_choices.at(index));
} }
bool is_changed() const noexcept { return m_value != m_default_value; }
private: private:
std::size_t find_key (const std::string& key) const noexcept std::size_t find_key (const std::string& key) const noexcept
{ {
@ -462,13 +547,41 @@ public:
//throw! //throw!
m_value = values; m_value = values;
} }
bool is_changed() const noexcept { return m_value != m_default_value; }
private: private:
GncOptionAccountList m_value; GncOptionAccountList m_value;
GncOptionAccountList m_default_value; GncOptionAccountList m_default_value;
GncOptionAccountTypeList m_allowed; GncOptionAccountTypeList m_allowed;
}; };
template<> inline std::ostream&
operator<< <GncOptionAccountValue>(std::ostream& oss,
const GncOptionAccountValue& opt)
{
auto values{opt.get_value()};
for (auto value : values)
oss << qof_instance_to_string(QOF_INSTANCE(value)) << " ";
return oss;
}
template<> inline std::istream&
operator>> <GncOptionAccountValue>(std::istream& iss,
GncOptionAccountValue& opt)
{
GncOptionAccountList values;
while (true)
{
std::string str;
std::getline(iss, str, ' ');
if (!str.empty())
values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type()));
else
break;
}
opt.set_value(values);
iss.clear();
return iss;
}
/** Date options /** Date options
* A legal date value is a pair of either and a RelativeDatePeriod, the absolute * A legal date value is a pair of either and a RelativeDatePeriod, the absolute
* flag and a time64, or for legacy purposes the absolute flag and a timespec. * flag and a time64, or for legacy purposes the absolute flag and a timespec.
@ -503,7 +616,7 @@ enum class RelativeDatePeriod : int64_t
ACCOUNTING_PERIOD ACCOUNTING_PERIOD
}; };
using DateSetterValue = std::pair<DateType, int64_t>; using DateSetterValue = std::tuple<DateType, int64_t>;
class GncOptionDateValue : public OptionClassifier, public OptionUIItem class GncOptionDateValue : public OptionClassifier, public OptionUIItem
{ {
public: public:
@ -519,23 +632,39 @@ public:
GncOptionDateValue& operator=(GncOptionDateValue&&) = default; GncOptionDateValue& operator=(GncOptionDateValue&&) = default;
time64 get_value() const; time64 get_value() const;
time64 get_default_value() const { return static_cast<time64>(GncDateTime()); } time64 get_default_value() const { return static_cast<time64>(GncDateTime()); }
std::ostream& out_stream(std::ostream& oss) const noexcept;
std::istream& in_stream(std::istream& iss);
void set_value(DateSetterValue); void set_value(DateSetterValue);
void set_value(time64 time) { void set_value(time64 time) {
m_type = DateType::ABSOLUTE; m_type = DateType::ABSOLUTE;
m_period = RelativeDatePeriod::TODAY; m_period = RelativeDatePeriod::TODAY;
m_date = time; m_date = time;
} }
bool is_changed() const noexcept { return true; }
private: private:
DateType m_type; DateType m_type;
RelativeDatePeriod m_period; RelativeDatePeriod m_period;
time64 m_date; time64 m_date;
}; };
template<> inline std::ostream&
operator<< <GncOptionDateValue>(std::ostream& oss,
const GncOptionDateValue& opt)
{
return opt.out_stream(oss);
}
template<> inline std::istream&
operator>> <GncOptionDateValue>(std::istream& iss,
GncOptionDateValue& opt)
{
return opt.in_stream(iss);
}
using GncOptionVariant = std::variant<GncOptionValue<std::string>, using GncOptionVariant = std::variant<GncOptionValue<std::string>,
GncOptionValue<bool>, GncOptionValue<bool>,
GncOptionValue<int64_t>, GncOptionValue<int64_t>,
GncOptionValue<QofInstance*>, GncOptionValue<QofInstance*>,
GncOptionValue<QofQuery*>,
GncOptionAccountValue, GncOptionAccountValue,
GncOptionMultichoiceValue, GncOptionMultichoiceValue,
GncOptionRangeValue<int>, GncOptionRangeValue<int>,
@ -631,9 +760,43 @@ public:
option.make_internal(); option.make_internal();
}, m_option); }, m_option);
} }
bool is_changed()
{
return std::visit([](const auto& option)->bool {
return option.is_changed();
}, m_option);
}
std::ostream& out_stream(std::ostream& oss) const
{
return std::visit([&oss](auto& option) -> std::ostream& {
oss << option;
return oss;
}, m_option);
}
std::istream& in_stream(std::istream& iss)
{
return std::visit([&iss](auto& option) -> std::istream& {
iss >> option;
return iss;
}, m_option);
}
GncOptionVariant& _get_option() const { return m_option; } GncOptionVariant& _get_option() const { return m_option; }
private: private:
mutable GncOptionVariant m_option; mutable GncOptionVariant m_option;
}; };
inline std::ostream&
operator<<(std::ostream& oss, const GncOption& opt)
{
return opt.out_stream(oss);
}
inline std::istream&
operator>>(std::istream& iss, GncOption& opt)
{
return opt.in_stream(iss);
}
#endif //GNC_OPTION_HPP_ #endif //GNC_OPTION_HPP_

View File

@ -138,9 +138,9 @@ GncOptionDB::find_section(const char* section)
} }
std::optional<std::reference_wrapper<GncOption>> std::optional<std::reference_wrapper<GncOption>>
GncOptionDB::find_option(const char* section, const char* name) GncOptionDB::find_option(const char* section, const char* name) const
{ {
auto db_section = find_section(section); auto db_section = const_cast<GncOptionDB*>(this)->find_section(section);
if (!db_section) if (!db_section)
return std::nullopt; return std::nullopt;
auto db_opt = std::find_if( auto db_opt = std::find_if(
@ -173,6 +173,36 @@ GncOptionDB::make_internal(const char* section, const char* name)
db_opt->get().make_internal(); db_opt->get().make_internal();
} }
std::ostream&
GncOptionDB::serialize_option_scheme(std::ostream& oss,
const char* option_prolog,
const char* section, const char* name) const noexcept
{
auto db_opt = find_option(section, name);
if (!db_opt || !db_opt->get().is_changed())
return oss;
oss << c_scheme_serialization_elements[0] << option_prolog;
oss << c_scheme_serialization_elements[1] << section;
oss << c_scheme_serialization_elements[1] << name; //repeats, not an error!
// oss << c_scheme_serialization_elements[2] << db_opt->get();
oss << c_scheme_serialization_elements[3];
return oss;
}
std::ostream&
GncOptionDB::serialize_option_key_value(std::ostream& oss,
const char* section,
const char* name) const noexcept
{
auto db_opt = find_option(section, name);
if (!db_opt || !db_opt->get().is_changed())
return oss;
oss << section << ":" << name << "=" /* << db_opt->get() */ << ";";
return oss;
}
void void
GncOptionDB::commit() GncOptionDB::commit()
{ {
@ -304,7 +334,7 @@ gnc_register_account_list_limited_option(const GncOptionDBPtr& db,
} }
catch (const std::invalid_argument& err) catch (const std::invalid_argument& err)
{ {
std::cerr << "Account List Limited Option, value failed validation, option not registerd.\n"; std::cerr << "Account List Limited Option, value failed validation, option not registered.\n";
} }
} }
@ -405,7 +435,7 @@ gnc_register_query_option(const GncOptionDBPtr& db, const char* section,
const char* name, const char* key, const char* name, const char* key,
const char* doc_string, QofQuery* value) const char* doc_string, QofQuery* value)
{ {
GncOption option{section, name, key, doc_string, value, GncOption option{section, name, key, doc_string, QOF_INSTANCE(value),
GncOptionUIType::INTERNAL}; GncOptionUIType::INTERNAL};
db->register_option(section, std::move(option)); db->register_option(section, std::move(option));
} }

View File

@ -28,6 +28,7 @@
#include <functional> #include <functional>
#include <exception> #include <exception>
#include <optional> #include <optional>
#include <iostream>
extern "C" extern "C"
{ {
#include <gncInvoice.h> #include <gncInvoice.h>
@ -83,14 +84,33 @@ public:
void make_internal(const char* section, const char* name); void make_internal(const char* section, const char* name);
void commit(); void commit();
std::optional<std::reference_wrapper<GncOptionSection>> find_section(const char* section); std::optional<std::reference_wrapper<GncOptionSection>> find_section(const char* section);
std::optional<std::reference_wrapper<GncOption>> find_option(const char* section, const char* name); std::optional<std::reference_wrapper<GncOption>> find_option(const char* section, const char* name) {
return static_cast<const GncOptionDB&>(*this).find_option(section, name);
}
std::optional<std::reference_wrapper<GncOption>> find_option(const char* section, const char* name) const;
private: private:
std::ostream& serialize_option_scheme(std::ostream& oss,
const char* option_prolog,
const char* section, const char* name) const noexcept;
std::ostream& serialize_option_key_value(std::ostream& oss,
const char* section,
const char* name) const noexcept;
void load_option_scheme(std::istream iss);
void load_option_key_value(std::istream iss);
std::optional<std::reference_wrapper<GncOptionSection>> m_default_section; std::optional<std::reference_wrapper<GncOptionSection>> m_default_section;
std::vector<GncOptionSection> m_sections; std::vector<GncOptionSection> m_sections;
bool m_dirty = false; bool m_dirty = false;
std::function<GncOptionUIItem*()> m_get_ui_value; std::function<GncOptionUIItem*()> m_get_ui_value;
std::function<void(GncOptionUIItem*)> m_set_ui_value; std::function<void(GncOptionUIItem*)> m_set_ui_value;
static constexpr char const* const c_scheme_serialization_elements[]
{
"(let ((option (gnc:lookup-option ",
"\n ",
")))\n ((lambda (o) (if o (gnc:option-set-value o",
"))) option))\n\n"
};
}; };
/** /**

View File

@ -337,5 +337,3 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
GncOption_set_value_from_scm(&(db_opt->get()), new_value); GncOption_set_value_from_scm(&(db_opt->get()), new_value);
} }
%} %}
*/

View File

@ -23,10 +23,13 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <gnc-option.hpp> #include <gnc-option.hpp>
#include <guid.hpp>
extern "C" extern "C"
{ {
#include <gnc-date.h> #include <gnc-date.h>
#include <time.h> #include <time.h>
#include <gnc-ui-util.h>
#include <gnc-session.h>
} }
TEST(GncOption, test_string_ctor) TEST(GncOption, test_string_ctor)
@ -64,6 +67,23 @@ TEST(GncOption, test_string_value)
}); });
} }
TEST(GncOption, test_string_stream_out)
{
GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
std::ostringstream oss;
oss << option;
EXPECT_EQ(oss.str(), option.get_value<std::string>());
}
TEST(GncOption, test_string_stream_in)
{
GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
std::string pepper{"pepper"};
std::istringstream iss{pepper};
iss >> option;
EXPECT_EQ(pepper, option.get_value<std::string>());
}
TEST(GncOption, test_int64_t_value) TEST(GncOption, test_int64_t_value)
{ {
GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789)); GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789));
@ -72,35 +92,81 @@ TEST(GncOption, test_int64_t_value)
EXPECT_EQ(INT64_C(987654321), option.get_value<int64_t>()); EXPECT_EQ(INT64_C(987654321), option.get_value<int64_t>());
} }
TEST(GNCOption, test_budget_ctor) TEST(GncOption, test_int64_stream_out)
{ {
auto book = qof_book_new(); GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789));
auto budget = gnc_budget_new(book); std::ostringstream oss;
oss << option;
EXPECT_STREQ(oss.str().c_str(), "123456789");
}
TEST(GncOption, test_int64_stream_in)
{
GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789));
std::string number{"987654321"};
std::istringstream iss{number};
iss >> option;
EXPECT_EQ(INT64_C(987654321), option.get_value<int64_t>());
}
TEST(GncOption, test_bool_stream_out)
{
GncOption option("foo", "bar", "baz", "Phony Option", false);
std::ostringstream oss;
oss << option;
EXPECT_STREQ(oss.str().c_str(), "#f");
oss.str("");
option.set_value(true);
oss << option;
EXPECT_STREQ(oss.str().c_str(), "#t");
}
TEST(GncOption, test_bool_stream_in)
{
GncOption option("foo", "bar", "baz", "Phony Option", false);
std::istringstream iss("#t");
iss >> option;
EXPECT_TRUE(option.get_value<bool>());
iss.str("#f");
iss >> option;
EXPECT_FALSE(option.get_value<bool>());
}
class GncOptionTest : public ::testing::Test
{
protected:
GncOptionTest() : m_session{gnc_get_current_session()}, m_book{gnc_get_current_book()} {}
~GncOptionTest() { gnc_clear_current_session(); }
QofSession* m_session;
QofBook* m_book;
};
TEST_F(GncOptionTest, test_budget_ctor)
{
auto budget = gnc_budget_new(m_book);
EXPECT_NO_THROW({ EXPECT_NO_THROW({
GncOption option("foo", "bar", "baz", "Phony Option", GncOption option("foo", "bar", "baz", "Phony Option",
QOF_INSTANCE(budget)); QOF_INSTANCE(budget));
}); });
gnc_budget_destroy(budget); gnc_budget_destroy(budget);
qof_book_destroy(book);
} }
TEST(GNCOption, test_commodity_ctor) TEST_F(GncOptionTest, test_commodity_ctor)
{ {
auto book = qof_book_new(); auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.",
"NYSE", "HPE", NULL, 1); "NYSE", "HPE", NULL, 1);
EXPECT_NO_THROW({ EXPECT_NO_THROW({
GncOption option("foo", "bar", "baz", "Phony Option", GncOption option("foo", "bar", "baz", "Phony Option",
QOF_INSTANCE(hpe)); QOF_INSTANCE(hpe));
}); });
gnc_commodity_destroy(hpe); gnc_commodity_destroy(hpe);
qof_book_destroy(book);
} }
static GncOption static GncOption
make_currency_option (const char* section, const char* name, make_currency_option (const char* section, const char* name,
const char* key, const char* doc_string, const char* key, const char* doc_string,
gnc_commodity *value) gnc_commodity *value, bool is_currency=false)
{ {
GncOption option{GncOptionValidatedValue<QofInstance*>{ GncOption option{GncOptionValidatedValue<QofInstance*>{
section, name, key, doc_string, QOF_INSTANCE(value), section, name, key, doc_string, QOF_INSTANCE(value),
@ -108,52 +174,48 @@ make_currency_option (const char* section, const char* name,
{ {
return GNC_IS_COMMODITY (new_value) && return GNC_IS_COMMODITY (new_value) &&
gnc_commodity_is_currency(GNC_COMMODITY(new_value)); gnc_commodity_is_currency(GNC_COMMODITY(new_value));
} }, is_currency ? GncOptionUIType::CURRENCY : GncOptionUIType::COMMODITY}
}}; };
return option; return option;
} }
TEST(GNCOption, test_currency_ctor) TEST_F(GncOptionTest, test_currency_ctor)
{ {
auto book = qof_book_new();
auto table = gnc_commodity_table_new(); auto table = gnc_commodity_table_new();
qof_book_set_data(book, GNC_COMMODITY_TABLE, table); qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table);
auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.", auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
"NYSE", "HPE", NULL, 1); "NYSE", "HPE", NULL, 1);
EXPECT_THROW({ EXPECT_THROW({
auto option = make_currency_option("foo", "bar", "baz", auto option = make_currency_option("foo", "bar", "baz",
"Phony Option", hpe); "Phony Option", hpe, false);
}, std::invalid_argument); }, std::invalid_argument);
gnc_commodity_destroy(hpe); gnc_commodity_destroy(hpe);
auto eur = gnc_commodity_new(book, "Euro", "ISO4217", "EUR", NULL, 100); auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100);
EXPECT_NO_THROW({ EXPECT_NO_THROW({
auto option = make_currency_option("foo", "bar", "baz", auto option = make_currency_option("foo", "bar", "baz",
"Phony Option", eur); "Phony Option", eur, true);
}); });
gnc_commodity_destroy(eur); gnc_commodity_destroy(eur);
auto usd = gnc_commodity_new(book, "United States Dollar", auto usd = gnc_commodity_new(m_book, "United States Dollar",
"CURRENCY", "USD", NULL, 100); "CURRENCY", "USD", NULL, 100);
EXPECT_NO_THROW({ EXPECT_NO_THROW({
auto option = make_currency_option("foo", "bar", "baz", auto option = make_currency_option("foo", "bar", "baz",
"Phony Option",usd); "Phony Option", usd, true);
}); });
gnc_commodity_destroy(usd); gnc_commodity_destroy(usd);
qof_book_set_data(book, GNC_COMMODITY_TABLE, nullptr); qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr);
gnc_commodity_table_destroy(table); gnc_commodity_table_destroy(table);
qof_book_destroy(book);
} }
TEST(GNCOption, test_currency_setter) TEST_F(GncOptionTest, test_currency_setter)
{ {
auto book = qof_book_new();
auto table = gnc_commodity_table_new(); auto table = gnc_commodity_table_new();
qof_book_set_data(book, GNC_COMMODITY_TABLE, table); qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table);
auto hpe = gnc_commodity_new(book, "Hewlett Packard Enterprise, Inc.", auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
"NYSE", "HPE", NULL, 1); "NYSE", "HPE", NULL, 1);
auto eur = gnc_commodity_new(book, "Euro", "ISO4217", "EUR", NULL, 100); auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100);
auto option = make_currency_option("foo", "bar", "baz", auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true);
"Phony Option",eur); auto usd = gnc_commodity_new(m_book, "United States Dollar",
auto usd = gnc_commodity_new(book, "United States Dollar",
"CURRENCY", "USD", NULL, 100); "CURRENCY", "USD", NULL, 100);
EXPECT_NO_THROW({ EXPECT_NO_THROW({
option.set_value(QOF_INSTANCE(usd)); option.set_value(QOF_INSTANCE(usd));
@ -166,9 +228,52 @@ TEST(GNCOption, test_currency_setter)
gnc_commodity_destroy(hpe); gnc_commodity_destroy(hpe);
gnc_commodity_destroy(usd); gnc_commodity_destroy(usd);
gnc_commodity_destroy(eur); gnc_commodity_destroy(eur);
qof_book_set_data(book, GNC_COMMODITY_TABLE, nullptr); qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr);
gnc_commodity_table_destroy(table);
}
TEST_F(GncOptionTest, test_qofinstance_out)
{
auto table = gnc_commodity_table_new();
qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table);
auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100);
auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true);
std::string eur_guid{gnc::GUID{*qof_instance_get_guid(eur)}.to_string()};
std::ostringstream oss;
oss << option;
EXPECT_EQ(eur_guid, oss.str());
gnc_commodity_destroy(eur);
qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr);
gnc_commodity_table_destroy(table);
}
TEST_F(GncOptionTest, test_qofinstance_in)
{
auto table = gnc_commodity_table_new();
qof_book_set_data(m_book, GNC_COMMODITY_TABLE, table);
auto eur = gnc_commodity_new(m_book, "Euro", "ISO4217", "EUR", NULL, 100);
auto usd = gnc_commodity_new(m_book, "United States Dollar",
"CURRENCY", "USD", NULL, 100);
auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
"NYSE", "HPE", NULL, 1);
auto option = make_currency_option("foo", "bar", "baz", "Phony Option", eur, true);
EXPECT_THROW({
std::string hpe_guid{gnc::GUID{*qof_instance_get_guid(hpe)}.to_string()};
std::istringstream iss{hpe_guid};
iss >> option;
}, std::invalid_argument);
EXPECT_NO_THROW({
std::string usd_guid{gnc::GUID{*qof_instance_get_guid(usd)}.to_string()};
std::istringstream iss{usd_guid};
iss >> option;
EXPECT_EQ(QOF_INSTANCE(usd), option.get_value<QofInstance*>());
});
gnc_commodity_destroy(eur);
gnc_commodity_destroy(usd);
qof_book_set_data(m_book, GNC_COMMODITY_TABLE, nullptr);
gnc_commodity_table_destroy(table); gnc_commodity_table_destroy(table);
qof_book_destroy(book);
} }
class GncUIItem class GncUIItem
@ -246,6 +351,24 @@ TEST_F(GncRangeOption, test_setter)
EXPECT_EQ(1.5, m_doubleoption.get_default_value()); EXPECT_EQ(1.5, m_doubleoption.get_default_value());
} }
TEST_F(GncRangeOption, test_range_out)
{
std::ostringstream oss;
oss << "Integer " << m_intoption << " Double " << m_doubleoption << ".";
EXPECT_STREQ("Integer 15 Double 1.5.", oss.str().c_str());
}
TEST_F(GncRangeOption, test_range_in)
{
std::istringstream iss{std::string{"45 4.5 20 2.0"}};
EXPECT_THROW({ iss >> m_intoption; }, std::invalid_argument);
EXPECT_THROW({ iss >> m_doubleoption; }, std::invalid_argument);
EXPECT_NO_THROW({ iss >> m_intoption; });
EXPECT_NO_THROW({ iss >> m_doubleoption; });
EXPECT_EQ(20, m_intoption.get_value());
EXPECT_EQ(2.0, m_doubleoption.get_value());
}
using AccountPair = std::pair<GncOptionAccountList&, using AccountPair = std::pair<GncOptionAccountList&,
const GncOptionAccountTypeList&>; const GncOptionAccountTypeList&>;
static void static void
@ -264,7 +387,8 @@ class GncOptionAccountTest : public ::testing::Test
{ {
protected: protected:
GncOptionAccountTest() : GncOptionAccountTest() :
m_book{qof_book_new()}, m_root{gnc_account_create_root(m_book)} m_session{gnc_get_current_session()}, m_book{gnc_get_current_book()},
m_root{gnc_account_create_root(m_book)}
{ {
auto create_account = [this](Account* parent, GNCAccountType type, auto create_account = [this](Account* parent, GNCAccountType type,
const char* name)->Account* { const char* name)->Account* {
@ -296,7 +420,7 @@ protected:
{ {
xaccAccountBeginEdit(m_root); xaccAccountBeginEdit(m_root);
xaccAccountDestroy(m_root); //It does the commit xaccAccountDestroy(m_root); //It does the commit
qof_book_destroy(m_book); gnc_clear_current_session();
} }
GncOptionAccountList list_of_types(const GncOptionAccountTypeList& types) GncOptionAccountList list_of_types(const GncOptionAccountTypeList& types)
{ {
@ -307,6 +431,7 @@ protected:
return list; return list;
} }
QofSession* m_session;
QofBook* m_book; QofBook* m_book;
Account* m_root; Account* m_root;
}; };
@ -389,6 +514,65 @@ TEST_F(GncOptionAccountTest, test_option_value_limited_constructor)
EXPECT_EQ(false, option.validate(acclistbad)); EXPECT_EQ(false, option.validate(acclistbad));
} }
TEST_F(GncOptionAccountTest, test_account_list_out)
{
GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
GncOptionUIType::ACCOUNT_LIST, acclist};
std::ostringstream oss;
std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()};
acc_guids += " ";
acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
acc_guids += " ";
oss << option;
EXPECT_EQ(acc_guids, oss.str());
GncOptionAccountList accsel{acclist[0]};
GncOptionAccountValue sel_option("foo", "bar", "baz", "Bogus Option",
GncOptionUIType::ACCOUNT_LIST,
accsel, {ACCT_TYPE_BANK});
acc_guids = gnc::GUID{*qof_instance_get_guid(accsel[0])}.to_string();
acc_guids += " ";
oss.str("");
oss << sel_option;
EXPECT_EQ(acc_guids, oss.str());
}
TEST_F(GncOptionAccountTest, test_account_list_in)
{
GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
GncOptionAccountValue option{"foo", "bar", "baz", "Bogus Option",
GncOptionUIType::ACCOUNT_LIST, acclist};
std::string acc_guids{gnc::GUID{*qof_instance_get_guid(acclist[0])}.to_string()};
acc_guids += " ";
acc_guids += gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
acc_guids += " ";
std::istringstream iss{acc_guids};
iss >> option;
EXPECT_EQ(acclist, option.get_value());
GncOptionAccountList accsel{acclist[0]};
GncOptionAccountValue sel_option("foo", "bar", "baz", "Bogus Option",
GncOptionUIType::ACCOUNT_LIST,
accsel, {ACCT_TYPE_BANK});
GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
acc_guids = gnc::GUID{*qof_instance_get_guid(acclistbad[1])}.to_string();
acc_guids += " ";
iss.str(acc_guids);
iss >> sel_option;
EXPECT_EQ(accsel, sel_option.get_value());
acc_guids = gnc::GUID{*qof_instance_get_guid(acclist[1])}.to_string();
EXPECT_NO_THROW({
iss.str(acc_guids);
iss >> sel_option;
});
EXPECT_EQ(acclist[1], sel_option.get_value()[0]);
}
class GncOptionMultichoiceTest : public ::testing::Test class GncOptionMultichoiceTest : public ::testing::Test
{ {
@ -611,3 +795,258 @@ TEST_F(GncDateOption, test_set_and_get_prev_year_end)
EXPECT_EQ(time1, m_option.get_value()); EXPECT_EQ(time1, m_option.get_value());
} }
TEST_F(GncDateOption, test_stream_out)
{
time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
DateSetterValue value1{DateType::ABSOLUTE, time1};
m_option.set_value(value1);
std::ostringstream oss;
oss << time1;
std::string timestr{"absolute . "};
timestr += oss.str();
oss.str("");
oss << m_option;
EXPECT_EQ(oss.str(), timestr);
DateSetterValue value2{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::TODAY)};
m_option.set_value(value2);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . today");
DateSetterValue value3{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::TODAY)};
m_option.set_value(value3);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . today");
DateSetterValue value4{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::THIS_MONTH)};
m_option.set_value(value4);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . start-this-month");
DateSetterValue value5{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::THIS_MONTH)};
m_option.set_value(value5);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . end-this-month");
DateSetterValue value6{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_MONTH)};
m_option.set_value(value6);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-month");
DateSetterValue value7{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_MONTH)};
m_option.set_value(value7);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-month");
DateSetterValue value8{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::CURRENT_QUARTER)};
m_option.set_value(value8);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . start-current-quarter");
DateSetterValue value9{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::CURRENT_QUARTER)};
m_option.set_value(value9);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . end-current-quarter");
DateSetterValue value10{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_QUARTER)};
m_option.set_value(value10);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-quarter");
DateSetterValue value11{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_QUARTER)};
m_option.set_value(value11);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-quarter");
DateSetterValue value12{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::CAL_YEAR)};
m_option.set_value(value12);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . start-cal-year");
DateSetterValue value13{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::CAL_YEAR)};
m_option.set_value(value13);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . end-cal-year");
DateSetterValue value14{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::PREV_YEAR)};
m_option.set_value(value14);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-year");
DateSetterValue value15{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::PREV_YEAR)};
m_option.set_value(value15);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-year");
DateSetterValue value16{DateType::STARTING, static_cast<int64_t>(RelativeDatePeriod::ACCOUNTING_PERIOD)};
m_option.set_value(value16);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . start-prev-fin-year");
DateSetterValue value17{DateType::ENDING, static_cast<int64_t>(RelativeDatePeriod::ACCOUNTING_PERIOD)};
m_option.set_value(value17);
oss.str("");
oss << m_option;
EXPECT_STREQ(oss.str().c_str(), "relative . end-prev-fin-year");
}
TEST_F(GncDateOption, test_stream_in_absolute)
{
time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
std::ostringstream oss;
oss << time1;
std::string timestr{"absolute . "};
timestr += oss.str();
std::istringstream iss{timestr};
iss >> m_option;
EXPECT_EQ(time1, m_option.get_value());
}
TEST_F(GncDateOption, test_stream_in_month_start)
{
GDate month_start;
g_date_set_time_t(&month_start, time(nullptr));
gnc_gdate_set_month_start(&month_start);
time64 time1{time64_from_gdate(&month_start, DayPart::start)};
std::istringstream iss{"relative . start-this-month"};
iss >> m_option;
EXPECT_EQ(time1, m_option.get_value());
}
TEST_F(GncDateOption, test_stream_in_month_end)
{
GDate month_end;
g_date_set_time_t(&month_end, time(nullptr));
gnc_gdate_set_month_end(&month_end);
time64 time1{time64_from_gdate(&month_end, DayPart::end)};
std::istringstream iss{"relative . end-this-month"};
iss >> m_option;
EXPECT_EQ(time1, m_option.get_value());
}
TEST_F(GncDateOption, test_stream_in_prev_month_start)
{
GDate prev_month_start;
g_date_set_time_t(&prev_month_start, time(nullptr));
gnc_gdate_set_prev_month_start(&prev_month_start);
time64 time1{time64_from_gdate(&prev_month_start, DayPart::start)};
std::istringstream iss{"relative . start-prev-month"};
iss >> m_option;
EXPECT_EQ(time1, m_option.get_value());
}
TEST_F(GncDateOption, test_stream_in_prev_month_end)
{
GDate prev_month_end;
g_date_set_time_t(&prev_month_end, time(nullptr));
gnc_gdate_set_prev_month_end(&prev_month_end);
time64 time1{time64_from_gdate(&prev_month_end, DayPart::end)};
std::istringstream iss{"relative . end-prev-month"};
iss >> m_option;
EXPECT_EQ(time1, m_option.get_value());
}
TEST_F(GncDateOption, test_stream_in_quarter_start)
{
GDate quarter_start;
g_date_set_time_t(&quarter_start, time(nullptr));
gnc_gdate_set_quarter_start(&quarter_start);
time64 time1{time64_from_gdate(&quarter_start, DayPart::start)};
std::istringstream iss{"relative . start-current-quarter"};
iss >> m_option;
EXPECT_EQ(time1, m_option.get_value());
}
TEST_F(GncDateOption, test_stream_in_quarter_end)
{
GDate quarter_end;
g_date_set_time_t(&quarter_end, time(nullptr));
gnc_gdate_set_quarter_end(&quarter_end);
time64 time1{time64_from_gdate(&quarter_end, DayPart::end)};
std::istringstream iss{"relative . end-current-quarter"};
iss >> m_option;
EXPECT_EQ(time1, m_option.get_value());
}
TEST_F(GncDateOption, test_stream_in_prev_quarter_start)
{
GDate prev_quarter_start;
g_date_set_time_t(&prev_quarter_start, time(nullptr));
gnc_gdate_set_prev_quarter_start(&prev_quarter_start);
time64 time1{time64_from_gdate(&prev_quarter_start, DayPart::start)};
std::istringstream iss{"relative . start-prev-quarter"};
iss >> m_option;
EXPECT_EQ(time1, m_option.get_value());
}
TEST_F(GncDateOption, test_stream_in_prev_quarter_end)
{
GDate prev_quarter_end;
g_date_set_time_t(&prev_quarter_end, time(nullptr));
gnc_gdate_set_prev_quarter_end(&prev_quarter_end);
time64 time1{time64_from_gdate(&prev_quarter_end, DayPart::end)};
std::istringstream iss{"relative . end-prev-quarter"};
iss >> m_option;
EXPECT_EQ(time1, m_option.get_value());
}
TEST_F(GncDateOption, test_stream_in_year_start)
{
GDate year_start;
g_date_set_time_t(&year_start, time(nullptr));
gnc_gdate_set_year_start(&year_start);
time64 time1{time64_from_gdate(&year_start, DayPart::start)};
std::istringstream iss{"relative . start-cal-year"};
iss >> m_option;
EXPECT_EQ(time1, m_option.get_value());
}
TEST_F(GncDateOption, test_stream_in_year_end)
{
GDate year_end;
g_date_set_time_t(&year_end, time(nullptr));
gnc_gdate_set_year_end(&year_end);
time64 time1{time64_from_gdate(&year_end, DayPart::end)};
std::istringstream iss{"relative . end-cal-year"};
iss >> m_option;
EXPECT_EQ(time1, m_option.get_value());
}
TEST_F(GncDateOption, test_stream_in_prev_year_start)
{
GDate prev_year_start;
g_date_set_time_t(&prev_year_start, time(nullptr));
gnc_gdate_set_prev_year_start(&prev_year_start);
time64 time1{time64_from_gdate(&prev_year_start, DayPart::start)};
std::istringstream iss{"relative . start-prev-year"};
iss >> m_option;
EXPECT_EQ(time1, m_option.get_value());
}
TEST_F(GncDateOption, test_stream_in_prev_year_end)
{
GDate prev_year_end;
g_date_set_time_t(&prev_year_end, time(nullptr));
gnc_gdate_set_prev_year_end(&prev_year_end);
time64 time1{time64_from_gdate(&prev_year_end, DayPart::end)};
std::istringstream iss{"relative . end-prev-year"};
iss >> m_option;
EXPECT_EQ(time1, m_option.get_value());
}