The unit tests for amount.cc now cover every part of the code except

for two: those concerning annotated commodities (which will be covered
in the t_commodity.cc tests) and reading of optimized amounts in the
binary journal reader.
This commit is contained in:
John Wiegley 2007-05-09 10:02:56 +00:00
parent 623e6e024c
commit 8e20c378d6
8 changed files with 398 additions and 199 deletions

View file

@ -1,3 +1,8 @@
- Add tracing code for functions that records call count and total
time spent, as well as average time per call. This would implement
selective profiling.
- Make sure that if any constructors cause memory to be allocated,
that the memory is held by an auto_ptr until the constructor is
done; otherwise, an exception raised from within the constructor
will not call the destructor to free the memory.

View file

@ -131,20 +131,6 @@ void amount_t::shutdown()
}
}
void amount_t::_init()
{
// This is only called on an initialized amount by amount_t::parse.
if (! quantity) {
quantity = new bigint_t;
}
else if (quantity->ref > 1) {
_release();
quantity = new bigint_t;
}
commodity_ = NULL;
}
void amount_t::_copy(const amount_t& amt)
{
if (quantity != amt.quantity) {
@ -183,13 +169,9 @@ void amount_t::_resize(precision_t prec)
_dup();
if (prec < quantity->prec) {
mpz_ui_pow_ui(divisor, 10, quantity->prec - prec);
mpz_tdiv_q(MPZ(quantity), MPZ(quantity), divisor);
} else {
mpz_ui_pow_ui(divisor, 10, prec - quantity->prec);
mpz_mul(MPZ(quantity), MPZ(quantity), divisor);
}
assert(prec > quantity->prec);
mpz_ui_pow_ui(divisor, 10, prec - quantity->prec);
mpz_mul(MPZ(quantity), MPZ(quantity), divisor);
quantity->prec = prec;
}
@ -359,7 +341,7 @@ int amount_t::compare(const amount_t& amt) const
return mpz_cmp(MPZ(quantity), MPZ(amt.quantity));
}
else if (quantity->prec < amt.quantity->prec) {
amount_t t = *this;
amount_t t(*this);
t._resize(amt.quantity->prec);
return mpz_cmp(MPZ(t.quantity), MPZ(amt.quantity));
}
@ -607,11 +589,11 @@ amount_t& amount_t::in_place_negate()
amount_t amount_t::round(precision_t prec) const
{
amount_t t = *this;
if (! quantity)
throw_(amount_error, "Cannot round an uninitialized amount");
amount_t t(*this);
if (quantity->prec <= prec) {
if (quantity && quantity->has_flags(BIGINT_KEEP_PREC)) {
t._dup();
@ -637,7 +619,7 @@ amount_t amount_t::unround() const
else if (quantity->has_flags(BIGINT_KEEP_PREC))
return *this;
amount_t t = *this;
amount_t t(*this);
t._dup();
t.quantity->add_flags(BIGINT_KEEP_PREC);
@ -915,7 +897,23 @@ void amount_t::parse(std::istream& in, flags_t flags)
if (quant.empty())
throw_(amount_error, "No quantity specified for amount");
_init(); // this will reuse a current value
// Allocate memory for the amount's quantity value. We have to
// monitor the allocation in an auto_ptr because this function gets
// called sometimes from amount_t's constructor; and if there is an
// exeception thrown by any of the function calls after this point,
// the destructor will never be called and the memory never freed.
std::auto_ptr<bigint_t> safe_holder;
if (! quantity) {
quantity = new bigint_t;
safe_holder.reset(quantity);
}
else if (quantity->ref > 1) {
_release();
quantity = new bigint_t;
safe_holder.reset(quantity);
}
// Create the commodity if has not already been seen, and update the
// precision if something greater was used for the quantity.
@ -965,9 +963,9 @@ void amount_t::parse(std::istream& in, flags_t flags)
// Set the commodity's flags and precision accordingly
if (commodity_ &&
(newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE))) {
if (commodity_ && (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE))) {
commodity().add_flags(comm_flags);
if (quantity->prec > commodity().precision())
commodity().set_precision(quantity->prec);
}
@ -1004,6 +1002,8 @@ void amount_t::parse(std::istream& in, flags_t flags)
if (! (flags & AMOUNT_PARSE_NO_REDUCE))
in_place_reduce();
safe_holder.release(); // `this->quantity' owns the pointer
}
void amount_t::parse_conversion(const string& larger_str,
@ -1201,8 +1201,6 @@ void amount_t::print(std::ostream& _out, bool omit_commodity,
// entire amount string, and not just the first part.
_out << out.str();
return;
}
@ -1225,7 +1223,7 @@ void amount_t::read(std::istream& in)
else if (ident == 0)
commodity_ = current_pool->null_commodity;
else {
commodity_ = current_pool->find(ident - 1);
commodity_ = current_pool->find(ident);
assert(commodity_);
}
@ -1234,10 +1232,7 @@ void amount_t::read(std::istream& in)
char byte;
in.read(&byte, sizeof(byte));
if (byte == 0) {
quantity = NULL;
}
else if (byte == 1) {
if (byte < 3) {
quantity = new bigint_t;
unsigned short len;
@ -1263,7 +1258,7 @@ void amount_t::read(std::istream& in)
}
}
void amount_t::read(char *& data)
void amount_t::read(const char *& data)
{
// Read in the commodity for this amount
@ -1274,7 +1269,7 @@ void amount_t::read(char *& data)
else if (ident == 0)
commodity_ = current_pool->null_commodity;
else {
commodity_ = current_pool->find(ident - 1);
commodity_ = current_pool->find(ident);
assert(commodity_);
}
@ -1282,12 +1277,13 @@ void amount_t::read(char *& data)
char byte = *data++;;
if (byte == 0) {
quantity = NULL;
}
else if (byte == 1) {
quantity = new((bigint_t *)bigints_next) bigint_t;
bigints_next += sizeof(bigint_t);
if (byte < 3) {
if (byte == 2) {
quantity = new((bigint_t *)bigints_next) bigint_t;
bigints_next += sizeof(bigint_t);
} else {
quantity = new bigint_t;
}
unsigned short len = *((unsigned short *) data);
data += sizeof(unsigned short);
@ -1303,7 +1299,9 @@ void amount_t::read(char *& data)
data += sizeof(precision_t);
quantity->set_flags(*((flags_t *) data));
data += sizeof(flags_t);
quantity->add_flags(BIGINT_BULK_ALLOC);
if (byte == 2)
quantity->add_flags(BIGINT_BULK_ALLOC);
} else {
uint_fast32_t index = *((uint_fast32_t *) data);
data += sizeof(uint_fast32_t);
@ -1315,7 +1313,7 @@ void amount_t::read(char *& data)
}
}
void amount_t::write(std::ostream& out) const
void amount_t::write(std::ostream& out, bool optimized) const
{
// Write out the commodity for this amount
@ -1331,11 +1329,14 @@ void amount_t::write(std::ostream& out) const
char byte;
if (quantity->index == 0) {
quantity->index = ++bigints_index;
bigints_count++;
byte = 1;
if (! optimized || quantity->index == 0) {
if (optimized) {
quantity->index = ++bigints_index; // if !optimized, this is garbage
bigints_count++;
byte = 2;
} else {
byte = 1;
}
out.write(&byte, sizeof(byte));
std::size_t size;
@ -1359,7 +1360,7 @@ void amount_t::write(std::ostream& out) const
// Since this value has already been written, we simply write
// out a reference to which one it was.
byte = 2;
byte = 3;
out.write(&byte, sizeof(byte));
out.write((char *)&quantity->index, sizeof(quantity->index));
}

View file

@ -141,7 +141,6 @@ public:
static bool stream_fullstrings;
protected:
void _init();
void _copy(const amount_t& amt);
void _dup();
void _resize(precision_t prec);
@ -326,7 +325,7 @@ public:
precision_t precision() const;
amount_t negate() const {
amount_t temp = *this;
amount_t temp(*this);
temp.in_place_negate();
return temp;
}
@ -575,6 +574,7 @@ public:
void parse(const string& str, flags_t flags = 0) {
std::istringstream stream(str);
parse(stream, flags);
assert(stream.eof());
}
static void parse_conversion(const string& larger_str,
@ -616,12 +616,17 @@ public:
* an input stream into a buffer. It advances the pointer passed in
* to the end of the deserialized amount.
*
* write(ostream) writes an amount to an output stream in a compact
* binary format.
* write(ostream, [bool]) writes an amount to an output stream in a
* compact binary format. If the second parameter is true,
* quantities with multiple reference counts will be written in an
* optimized fashion. NOTE: This form of usage is valid only for
* the binary journal writer, it should not be used otherwise, as it
* has strict requirements for reading that only the binary reader
* knows about.
*/
void read(std::istream& in);
void read(char *& data);
void write(std::ostream& out) const;
void read(const char *& data);
void write(std::ostream& out, bool optimize = false) const;
/**
* Debugging methods. There are two methods defined to help with
@ -691,6 +696,8 @@ inline bool amount_t::operator==(const amount_t& amt) const {
}
inline amount_t amount_t::round() const {
if (! quantity)
throw_(amount_error, "Cannot round an uninitialized amount");
if (! has_commodity())
return *this;
return round(commodity().precision());

View file

@ -68,7 +68,7 @@ void read_binary_bool(std::istream& in, bool& num)
read_binary_guard(in, 0x2006);
}
void read_binary_bool(char *& data, bool& num)
void read_binary_bool(const char *& data, bool& num)
{
read_binary_guard(data, 0x2005);
unsigned char val = *((unsigned char *) data);
@ -104,7 +104,7 @@ void read_binary_string(std::istream& in, string& str)
read_binary_guard(in, 0x3002);
}
void read_binary_string(char *& data, string& str)
void read_binary_string(const char *& data, string& str)
{
read_binary_guard(data, 0x3001);
@ -127,7 +127,7 @@ void read_binary_string(char *& data, string& str)
read_binary_guard(data, 0x3002);
}
void read_binary_string(char *& data, string * str)
void read_binary_string(const char *& data, string * str)
{
read_binary_guard(data, 0x3001);
@ -151,7 +151,7 @@ void read_binary_string(char *& data, string * str)
}
#if 0
inline void read_binary_value(char *& data, value_t& val)
inline void read_binary_value(const char *& data, value_t& val)
{
val.type = static_cast<value_t::type_t>(read_binary_long<int>(data));
@ -176,7 +176,7 @@ inline void read_binary_value(char *& data, value_t& val)
}
}
inline void read_binary_mask(char *& data, mask_t *& mask)
inline void read_binary_mask(const char *& data, mask_t *& mask)
{
bool exclude;
read_binary_number(data, exclude);
@ -187,7 +187,7 @@ inline void read_binary_mask(char *& data, mask_t *& mask)
mask->exclude = exclude;
}
inline void read_binary_transaction(char *& data, transaction_t * xact)
inline void read_binary_transaction(const char *& data, transaction_t * xact)
{
read_binary_number(data, xact->_date);
read_binary_number(data, xact->_date_eff);
@ -230,7 +230,7 @@ inline void read_binary_transaction(char *& data, transaction_t * xact)
xact->data = NULL;
}
inline void read_binary_entry_base(char *& data, entry_base_t * entry,
inline void read_binary_entry_base(const char *& data, entry_base_t * entry,
transaction_t *& xact_pool, bool& finalize)
{
read_binary_long(data, entry->src_idx);
@ -253,7 +253,7 @@ inline void read_binary_entry_base(char *& data, entry_base_t * entry,
}
}
inline void read_binary_entry(char *& data, entry_t * entry,
inline void read_binary_entry(const char *& data, entry_t * entry,
transaction_t *& xact_pool, bool& finalize)
{
entry->data =
@ -266,7 +266,7 @@ inline void read_binary_entry(char *& data, entry_t * entry,
read_binary_string(data, &entry->payee);
}
inline void read_binary_auto_entry(char *& data, auto_entry_t * entry,
inline void read_binary_auto_entry(const char *& data, auto_entry_t * entry,
transaction_t *& xact_pool)
{
bool ignore;
@ -277,7 +277,7 @@ inline void read_binary_auto_entry(char *& data, auto_entry_t * entry,
entry->predicate.parse(pred_str);
}
inline void read_binary_period_entry(char *& data, period_entry_t * entry,
inline void read_binary_period_entry(const char *& data, period_entry_t * entry,
transaction_t *& xact_pool, bool& finalize)
{
read_binary_entry_base(data, entry, xact_pool, finalize);
@ -286,7 +286,7 @@ inline void read_binary_period_entry(char *& data, period_entry_t * entry,
entry->period.parse(stream);
}
inline commodity_base_t * read_binary_commodity_base(char *& data)
inline commodity_base_t * read_binary_commodity_base(const char *& data)
{
commodity_base_t * commodity = new commodity_base_t;
*base_commodities_next++ = commodity;
@ -300,7 +300,7 @@ inline commodity_base_t * read_binary_commodity_base(char *& data)
return commodity;
}
inline void read_binary_commodity_base_extra(char *& data,
inline void read_binary_commodity_base_extra(const char *& data,
commodity_t::ident_t ident)
{
commodity_base_t * commodity = base_commodities[ident];
@ -339,7 +339,7 @@ inline void read_binary_commodity_base_extra(char *& data,
}
}
inline commodity_t * read_binary_commodity(char *& data)
inline commodity_t * read_binary_commodity(const char *& data)
{
commodity_t * commodity = new commodity_t;
*commodities_next++ = commodity;
@ -353,7 +353,7 @@ inline commodity_t * read_binary_commodity(char *& data)
return commodity;
}
inline commodity_t * read_binary_commodity_annotated(char *& data)
inline commodity_t * read_binary_commodity_annotated(const char *& data)
{
annotated_commodity_t * commodity = new annotated_commodity_t;
*commodities_next++ = commodity;
@ -381,7 +381,7 @@ inline commodity_t * read_binary_commodity_annotated(char *& data)
}
inline
account_t * read_binary_account(char *& data, journal_t * journal,
account_t * read_binary_account(const char *& data, journal_t * journal,
account_t * master = NULL)
{
account_t * acct = new account_t(NULL);

View file

@ -55,7 +55,7 @@ inline void read_binary_number_nocheck(std::istream& in, T& num) {
}
template <typename T>
inline void read_binary_number_nocheck(char *& data, T& num) {
inline void read_binary_number_nocheck(const char *& data, T& num) {
num = *((T *) data);
data += sizeof(T);
}
@ -68,7 +68,7 @@ inline T read_binary_number_nocheck(std::istream& in) {
}
template <typename T>
inline T read_binary_number_nocheck(char *& data) {
inline T read_binary_number_nocheck(const char *& data) {
T num;
read_binary_number_nocheck(data, num);
return num;
@ -90,7 +90,7 @@ inline void read_binary_number(std::istream& in, T& num) {
}
template <typename T>
inline void read_binary_number(char *& data, T& num) {
inline void read_binary_number(const char *& data, T& num) {
read_binary_guard(data, 0x2003);
num = *((T *) data);
data += sizeof(T);
@ -105,14 +105,14 @@ inline T read_binary_number(std::istream& in) {
}
template <typename T>
inline T read_binary_number(char *& data) {
inline T read_binary_number(const char *& data) {
T num;
read_binary_number(data, num);
return num;
}
void read_binary_bool(std::istream& in, bool& num);
void read_binary_bool(char *& data, bool& num);
void read_binary_bool(const char *& data, bool& num);
inline bool read_binary_bool(std::istream& in) {
bool num;
@ -120,7 +120,7 @@ inline bool read_binary_bool(std::istream& in) {
return num;
}
inline bool read_binary_bool(char *& data) {
inline bool read_binary_bool(const char *& data) {
bool num;
read_binary_bool(data, num);
return num;
@ -156,7 +156,7 @@ void read_binary_long(std::istream& in, T& num)
}
template <typename T>
void read_binary_long(char *& data, T& num)
void read_binary_long(const char *& data, T& num)
{
read_binary_guard(data, 0x2001);
@ -192,15 +192,15 @@ inline T read_binary_long(std::istream& in) {
}
template <typename T>
inline T read_binary_long(char *& data) {
inline T read_binary_long(const char *& data) {
T num;
read_binary_long(data, num);
return num;
}
void read_binary_string(std::istream& in, string& str);
void read_binary_string(char *& data, string& str);
void read_binary_string(char *& data, string * str);
void read_binary_string(const char *& data, string& str);
void read_binary_string(const char *& data, string * str);
inline string read_binary_string(std::istream& in) {
string temp;
@ -208,7 +208,7 @@ inline string read_binary_string(std::istream& in) {
return temp;
}
inline string read_binary_string(char *& data) {
inline string read_binary_string(const char *& data) {
string temp;
read_binary_string(data, temp);
return temp;

View file

@ -30,16 +30,51 @@ void AmountTestCase::testParser()
amount_t x5(x4);
amount_t x6(x4);
amount_t x7(x4);
amount_t x8("$123.456");
amount_t x8("$123.45");
amount_t x9(x8);
amount_t x10(x8);
amount_t x11(x8);
amount_t x12("$100");
assertEqual(amount_t::precision_t(3), x12.commodity().precision());
assertEqual(amount_t::precision_t(2), x12.commodity().precision());
string buf("$100...");
std::istringstream input(buf);
amount_t x13;
x13.parse(input);
assertEqual(x12, x13);
amount_t x14;
assertThrow(x14.parse("DM"), amount_error);
amount_t x15("$1.000.000,00"); // parsing this switches us to European
amount_t x16("$2000");
assertEqual(string("$2.000,00"), x16.to_string());
x16.parse("$2000,00");
assertEqual(string("$2.000,00"), x16.to_string());
// Since European-ness is an additive quality, we must switch back
// to American-ness manually
x15.commodity().drop_flags(COMMODITY_STYLE_EUROPEAN);
amount_t x17("$1,000,000.00"); // parsing this switches back to American
amount_t x18("$2000");
assertEqual(string("$2,000.00"), x18.to_string());
x18.parse("$2,000");
assertEqual(string("$2,000.00"), x18.to_string());
assertEqual(x15, x17);
amount_t x19("EUR 1000");
amount_t x20("EUR 1000");
assertEqual(string("EUR 1000"), x19.to_string());
assertEqual(string("EUR 1000"), x20.to_string());
x1.parse("$100.0000", AMOUNT_PARSE_NO_MIGRATE);
assertEqual(amount_t::precision_t(3), x12.commodity().precision());
assertEqual(amount_t::precision_t(2), x12.commodity().precision());
assertEqual(x1.commodity(), x12.commodity());
assertEqual(x1, x12);
@ -71,18 +106,18 @@ void AmountTestCase::testParser()
x11.parse("$100.00", AMOUNT_PARSE_NO_MIGRATE | AMOUNT_PARSE_NO_REDUCE);
assertEqual(x11, x12);
assertTrue(x0.valid());
assertTrue(x1.valid());
assertTrue(x2.valid());
assertTrue(x3.valid());
assertTrue(x5.valid());
assertTrue(x6.valid());
assertTrue(x7.valid());
assertTrue(x8.valid());
assertTrue(x9.valid());
assertTrue(x10.valid());
assertTrue(x11.valid());
assertTrue(x12.valid());
assertValid(x0);
assertValid(x1);
assertValid(x2);
assertValid(x3);
assertValid(x5);
assertValid(x6);
assertValid(x7);
assertValid(x8);
assertValid(x9);
assertValid(x10);
assertValid(x11);
assertValid(x12);
}
void AmountTestCase::testConstructors()
@ -111,17 +146,17 @@ void AmountTestCase::testConstructors()
assertEqual(x10, x3);
assertEqual(x10, x9);
assertTrue(x0.valid());
assertTrue(x1.valid());
assertTrue(x2.valid());
assertTrue(x3.valid());
assertTrue(x5.valid());
assertTrue(x6.valid());
assertTrue(x7.valid());
assertTrue(x8.valid());
assertTrue(x9.valid());
assertTrue(x10.valid());
assertTrue(x11.valid());
assertValid(x0);
assertValid(x1);
assertValid(x2);
assertValid(x3);
assertValid(x5);
assertValid(x6);
assertValid(x7);
assertValid(x8);
assertValid(x9);
assertValid(x10);
assertValid(x11);
}
void AmountTestCase::testCommodityConstructors()
@ -215,16 +250,16 @@ void AmountTestCase::testAssignment()
assertTrue(x0.is_null());
assertTrue(x1.is_null());
assertTrue(x0.valid());
assertTrue(x1.valid());
assertTrue(x2.valid());
assertTrue(x3.valid());
assertTrue(x5.valid());
assertTrue(x6.valid());
assertTrue(x7.valid());
assertTrue(x8.valid());
assertTrue(x9.valid());
assertTrue(x10.valid());
assertValid(x0);
assertValid(x1);
assertValid(x2);
assertValid(x3);
assertValid(x5);
assertValid(x6);
assertValid(x7);
assertValid(x8);
assertValid(x9);
assertValid(x10);
}
void AmountTestCase::testCommodityAssignment()
@ -290,12 +325,19 @@ void AmountTestCase::testEquality()
assertTrue(x4 == x5);
assertTrue(x4 == x6);
assertTrue(x1.valid());
assertTrue(x2.valid());
assertTrue(x3.valid());
assertTrue(x4.valid());
assertTrue(x5.valid());
assertTrue(x6.valid());
assertTrue(x1 == 123456L);
assertTrue(123456L == x1);
assertTrue(x1 == 123456UL);
assertTrue(123456UL == x1);
assertTrue(x1 == 123456.0);
assertTrue(123456.0 == x1);
assertValid(x1);
assertValid(x2);
assertValid(x3);
assertValid(x4);
assertValid(x5);
assertValid(x6);
}
void AmountTestCase::testCommodityEquality()
@ -367,19 +409,19 @@ void AmountTestCase::testComparisons()
assertTrue(x3 < x4);
assertTrue(x1 < 100L);
assertTrue(x1 < 100UL);
assertTrue(x1 < 100.0);
assertTrue(100L > x1);
assertTrue(x1 < 100UL);
assertTrue(100UL > x1);
assertTrue(x1 < 100.0);
assertTrue(100.0 > x1);
assertTrue(x0.valid());
assertTrue(x1.valid());
assertTrue(x2.valid());
assertTrue(x3.valid());
assertTrue(x4.valid());
assertTrue(x5.valid());
assertTrue(x6.valid());
assertValid(x0);
assertValid(x1);
assertValid(x2);
assertValid(x3);
assertValid(x4);
assertValid(x5);
assertValid(x6);
}
void AmountTestCase::testCommodityComparisons()
@ -390,6 +432,7 @@ void AmountTestCase::testCommodityComparisons()
amount_t x4(internalAmount("$123.4544"));
amount_t x5("$-123.45");
amount_t x6("$123.45");
amount_t x7("DM 123.45");
assertTrue(x1 > x3);
assertTrue(x3 <= x5);
@ -398,6 +441,8 @@ void AmountTestCase::testCommodityComparisons()
assertFalse(x3 == x5);
assertTrue(x3 < x1);
assertTrue(x3 < x4);
assertFalse(x6 == x7);
assertThrow(x6 < x7, amount_error);
assertValid(x1);
assertValid(x2);
@ -409,6 +454,7 @@ void AmountTestCase::testCommodityComparisons()
void AmountTestCase::testIntegerAddition()
{
amount_t x0;
amount_t x1(123L);
amount_t y1(456L);
@ -425,9 +471,10 @@ void AmountTestCase::testIntegerAddition()
assertEqual(amount_t("246913578246913578246913578"), x4 + x4);
assertTrue(x1.valid());
assertTrue(y1.valid());
assertTrue(x4.valid());
assertValid(x0);
assertValid(x1);
assertValid(y1);
assertValid(x4);
}
void AmountTestCase::testFractionalAddition()
@ -450,9 +497,9 @@ void AmountTestCase::testFractionalAddition()
assertEqual(amount_t("246913578246913578.246913578246913578"), x2 + x2);
assertTrue(x1.valid());
assertTrue(y1.valid());
assertTrue(x2.valid());
assertValid(x1);
assertValid(y1);
assertValid(x2);
}
void AmountTestCase::testCommodityAddition()
@ -474,6 +521,8 @@ void AmountTestCase::testCommodityAddition()
assertEqual(string("$246.91"), (x1 + x2).to_string());
assertThrow(x1 + x0, amount_error);
assertThrow(x0 + x1, amount_error);
assertThrow(x0 + x0, amount_error);
assertThrow(x1 + x3, amount_error);
assertThrow(x1 + x4, amount_error);
assertThrow(x1 + x5, amount_error);
@ -530,10 +579,10 @@ void AmountTestCase::testIntegerSubtraction()
assertEqual(amount_t("123456789115218063137220803"), x4 - y4);
assertEqual(amount_t("-123456789115218063137220803"), y4 - x4);
assertTrue(x1.valid());
assertTrue(y1.valid());
assertTrue(x4.valid());
assertTrue(y4.valid());
assertValid(x1);
assertValid(y1);
assertValid(x4);
assertValid(y4);
}
void AmountTestCase::testFractionalSubtraction()
@ -557,10 +606,10 @@ void AmountTestCase::testFractionalSubtraction()
assertEqual(amount_t("123446916777474329.874482549545456789"), x2 - y2);
assertEqual(amount_t("-123446916777474329.874482549545456789"), y2 - x2);
assertTrue(x1.valid());
assertTrue(y1.valid());
assertTrue(x2.valid());
assertTrue(y2.valid());
assertValid(x1);
assertValid(y1);
assertValid(x2);
assertValid(y2);
}
void AmountTestCase::testCommoditySubtraction()
@ -586,6 +635,8 @@ void AmountTestCase::testCommoditySubtraction()
assertEqual(string("$-0.01"), (x1 - x2).to_string());
assertThrow(x1 - x0, amount_error);
assertThrow(x0 - x1, amount_error);
assertThrow(x0 - x0, amount_error);
assertThrow(x1 - x3, amount_error);
assertThrow(x1 - x4, amount_error);
assertThrow(x1 - x5, amount_error);
@ -672,9 +723,9 @@ void AmountTestCase::testIntegerMultiplication()
assertEqual(amount_t("15241578780673678546105778281054720515622620750190521"),
x4 * x4);
assertTrue(x1.valid());
assertTrue(y1.valid());
assertTrue(x4.valid());
assertValid(x1);
assertValid(y1);
assertValid(x4);
}
void AmountTestCase::testFractionalMultiplication()
@ -709,13 +760,14 @@ void AmountTestCase::testFractionalMultiplication()
assertEqual(amount_t("15241578780673678546105778311537878.046486820281054720515622620750190521"),
x2 * x2);
assertTrue(x1.valid());
assertTrue(y1.valid());
assertTrue(x2.valid());
assertValid(x1);
assertValid(y1);
assertValid(x2);
}
void AmountTestCase::testCommodityMultiplication()
{
amount_t x0;
amount_t x1("$123.12");
amount_t y1("$456.45");
amount_t x2(internalAmount("$123.456789"));
@ -741,6 +793,9 @@ void AmountTestCase::testCommodityMultiplication()
assertEqual(string("$15200.00"), (x1 * x2).to_string());
assertEqual(string("$15199.99986168"), (x2 * x1).to_string());
assertThrow(x1 * x0, amount_error);
assertThrow(x0 * x1, amount_error);
assertThrow(x0 * x0, amount_error);
assertThrow(x1 * x3, amount_error);
assertThrow(x1 * x4, amount_error);
assertThrow(x1 * x5, amount_error);
@ -799,10 +854,10 @@ void AmountTestCase::testIntegerDivision()
assertEqual(amount_t(1L), x4 / x4);
assertEqual(amount_t("2204585520061728377204585.517857"), x4 / y4);
assertTrue(x1.valid());
assertTrue(y1.valid());
assertTrue(x4.valid());
assertTrue(y4.valid());
assertValid(x1);
assertValid(y1);
assertValid(x4);
assertValid(y4);
}
void AmountTestCase::testFractionalDivision()
@ -838,14 +893,15 @@ void AmountTestCase::testFractionalDivision()
assertEqual(amount_t(1.0), x4 / x4);
assertEqual(amount_t("21739560323910.7554497273748437197344556164046"), x4 / y4);
assertTrue(x1.valid());
assertTrue(y1.valid());
assertTrue(x4.valid());
assertTrue(y4.valid());
assertValid(x1);
assertValid(y1);
assertValid(x4);
assertValid(y4);
}
void AmountTestCase::testCommodityDivision()
{
amount_t x0;
amount_t x1("$123.12");
amount_t y1("$456.45");
amount_t x2(internalAmount("$123.456789"));
@ -871,6 +927,9 @@ void AmountTestCase::testCommodityDivision()
assertEqual(string("$1.00"), (x1 / x2).to_string());
assertEqual(string("$1.00273545321637426901"), (x2 / x1).to_string());
assertThrow(x1 / x0, amount_error);
assertThrow(x0 / x1, amount_error);
assertThrow(x0 / x0, amount_error);
assertThrow(x1 / x3, amount_error);
assertThrow(x1 / x4, amount_error);
assertThrow(x1 / x5, amount_error);
@ -905,6 +964,7 @@ void AmountTestCase::testCommodityDivision()
void AmountTestCase::testNegation()
{
amount_t x0;
amount_t x1(-123456L);
amount_t x3(-123.456);
amount_t x5("-123456");
@ -913,6 +973,7 @@ void AmountTestCase::testNegation()
amount_t x8(string("-123.456"));
amount_t x9(- x3);
assertThrow(x0.negate(), amount_error);
assertEqual(x5, x1);
assertEqual(x7, x1);
assertEqual(x6, x3);
@ -924,14 +985,14 @@ void AmountTestCase::testNegation()
assertEqual(x3, x10);
assertTrue(x1.valid());
assertTrue(x3.valid());
assertTrue(x5.valid());
assertTrue(x6.valid());
assertTrue(x7.valid());
assertTrue(x8.valid());
assertTrue(x9.valid());
assertTrue(x10.valid());
assertValid(x1);
assertValid(x3);
assertValid(x5);
assertValid(x6);
assertValid(x7);
assertValid(x8);
assertValid(x9);
assertValid(x10);
}
void AmountTestCase::testCommodityNegation()
@ -999,9 +1060,9 @@ void AmountTestCase::testAbs()
assertEqual(amount_t(1234L), x1.abs());
assertEqual(amount_t(1234L), x2.abs());
assertTrue(x0.valid());
assertTrue(x1.valid());
assertTrue(x2.valid());
assertValid(x0);
assertValid(x1);
assertValid(x2);
}
void AmountTestCase::testCommodityAbs()
@ -1018,15 +1079,45 @@ void AmountTestCase::testCommodityAbs()
void AmountTestCase::testFractionalRound()
{
amount_t x0;
amount_t x1("1234.567890");
assertEqual(amount_t("1234.56789"), x1.round(6));
assertEqual(amount_t("1234.56789"), x1.round(5));
assertEqual(amount_t("1234.5679"), x1.round(4));
assertEqual(amount_t("1234.568"), x1.round(3));
assertEqual(amount_t("1234.57"), x1.round(2));
assertEqual(amount_t("1234.6"), x1.round(1));
assertEqual(amount_t("1235"), x1.round(0));
assertThrow(x0.precision(), amount_error);
assertThrow(x0.round(), amount_error);
assertThrow(x0.round(2), amount_error);
assertThrow(x0.unround(), amount_error);
assertEqual(amount_t::precision_t(6), x1.precision());
amount_t x1b(x1.unround());
assertEqual(x1b.precision(), x1b.unround().precision());
amount_t y7(x1.round(7));
amount_t y6(x1.round(6));
amount_t y5(x1.round(5));
amount_t y4(x1.round(4));
amount_t y3(x1.round(3));
amount_t y2(x1.round(2));
amount_t y1(x1.round(1));
amount_t y0(x1.round(0));
assertEqual(amount_t::precision_t(6), y7.precision());
assertEqual(amount_t::precision_t(6), y6.precision());
assertEqual(amount_t::precision_t(5), y5.precision());
assertEqual(amount_t::precision_t(4), y4.precision());
assertEqual(amount_t::precision_t(3), y3.precision());
assertEqual(amount_t::precision_t(2), y2.precision());
assertEqual(amount_t::precision_t(1), y1.precision());
assertEqual(amount_t::precision_t(0), y0.precision());
assertEqual(amount_t("1234.56789"), y7);
assertEqual(amount_t("1234.56789"), y6);
assertEqual(amount_t("1234.56789"), y5);
assertEqual(amount_t("1234.5679"), y4);
assertEqual(amount_t("1234.568"), y3);
assertEqual(amount_t("1234.57"), y2);
assertEqual(amount_t("1234.6"), y1);
assertEqual(amount_t("1235"), y0);
amount_t x2("9876.543210");
@ -1064,11 +1155,11 @@ void AmountTestCase::testFractionalRound()
x5.round(37));
assertEqual(amount_t(0L), x5.round(36));
assertTrue(x1.valid());
assertTrue(x2.valid());
assertTrue(x3.valid());
assertTrue(x4.valid());
assertTrue(x5.valid());
assertValid(x1);
assertValid(x2);
assertValid(x3);
assertValid(x4);
assertValid(x5);
}
void AmountTestCase::testCommodityRound()
@ -1153,6 +1244,7 @@ void AmountTestCase::testCommodityDisplayRound()
void AmountTestCase::testReduction()
{
amount_t x0;
amount_t x1("60s");
amount_t x2("600s");
amount_t x3("6000s");
@ -1166,9 +1258,12 @@ void AmountTestCase::testReduction()
amount_t x11("1000h"); // 3600000s
amount_t x12("10000h"); // 36000000s
assertThrow(x0.reduce(), amount_error);
assertThrow(x0.unreduce(), amount_error);
assertEqual(x2, x5);
assertEqual(x3, x6);
assertEqual(x4, x10);
assertEqual(string("100.0h"), x4.unreduce().to_string());
}
void AmountTestCase::testSign()
@ -1185,11 +1280,11 @@ void AmountTestCase::testSign()
assertTrue(x3.sign() > 0);
assertTrue(x4.sign() < 0);
assertTrue(x0.valid());
assertTrue(x1.valid());
assertTrue(x2.valid());
assertTrue(x3.valid());
assertTrue(x4.valid());
assertValid(x0);
assertValid(x1);
assertValid(x2);
assertValid(x3);
assertValid(x4);
}
void AmountTestCase::testCommoditySign()
@ -1221,9 +1316,9 @@ void AmountTestCase::testTruth()
assertTrue(x1);
assertTrue(x2);
assertTrue(x0.valid());
assertTrue(x1.valid());
assertTrue(x2.valid());
assertValid(x0);
assertValid(x1);
assertValid(x2);
}
void AmountTestCase::testCommodityTruth()
@ -1256,8 +1351,8 @@ void AmountTestCase::testForZero()
assertFalse(x1.is_zero());
assertFalse(x1.is_realzero());
assertTrue(x0.valid());
assertTrue(x1.valid());
assertValid(x0);
assertValid(x1);
}
void AmountTestCase::testCommodityForZero()
@ -1273,27 +1368,35 @@ void AmountTestCase::testCommodityForZero()
void AmountTestCase::testIntegerConversion()
{
amount_t x0;
amount_t x1(123456L);
amount_t x2("12345682348723487324");
assertThrow(x0.to_long(), amount_error);
assertThrow(x0.to_double(), amount_error);
assertFalse(x2.fits_in_long());
assertEqual(123456L, x1.to_long());
assertEqual(123456.0, x1.to_double());
assertEqual(string("123456"), x1.to_string());
assertEqual(string("123456"), x1.quantity_string());
assertTrue(x1.valid());
assertValid(x1);
}
void AmountTestCase::testFractionalConversion()
{
amount_t x1(1234.56);
amount_t x2("1234.5683787634678348734");
assertThrow(x1.to_long(), amount_error); // loses precision
assertThrow(x2.to_double(), amount_error); // loses precision
assertFalse(x2.fits_in_double());
assertEqual(1234L, x1.to_long(true));
assertEqual(1234.56, x1.to_double());
assertEqual(string("1234.56"), x1.to_string());
assertEqual(string("1234.56"), x1.quantity_string());
assertTrue(x1.valid());
assertValid(x1);
}
void AmountTestCase::testCommodityConversion()
@ -1327,8 +1430,8 @@ void AmountTestCase::testPrinting()
bufstr.str());
}
assertTrue(x0.valid());
assertTrue(x1.valid());
assertValid(x0);
assertValid(x1);
}
void AmountTestCase::testCommodityPrinting()
@ -1362,3 +1465,80 @@ void AmountTestCase::testCommodityPrinting()
assertValid(x1);
assertValid(x2);
}
void AmountTestCase::testSerialization()
{
amount_t x0;
amount_t x1("$8,192.34");
amount_t x2("8192.34");
amount_t x3("8192.34");
amount_t x4("-8192.34");
amount_t x5(x4);
// Force x3's pointer to actually be set to null_commodity
x3.set_commodity(*x3.current_pool->null_commodity);
std::string buf;
{
std::ostringstream storage;
assertThrow(x0.write(storage), amount_error);
x1.write(storage);
x2.write(storage);
x3.write(storage);
x4.write(storage);
x5.write(storage);
buf = storage.str();
}
amount_t x1b;
amount_t x2b;
amount_t x3b;
amount_t x4b;
amount_t x5b;
{
std::istringstream storage(buf);
x1b.read(storage);
x2b.read(storage);
x3b.read(storage);
x4b.read(storage);
x5b.read(storage);
}
assertEqual(x1, x1b);
assertEqual(x2, x2b);
assertEqual(x3, x3b);
assertEqual(x4, x4b);
const char * ptr = buf.c_str();
amount_t x1c;
amount_t x2c;
amount_t x3c;
amount_t x4c;
amount_t x5c;
{
x1c.read(ptr);
x2c.read(ptr);
x3c.read(ptr);
x4c.read(ptr);
x5c.read(ptr);
}
assertEqual(x1, x1b);
assertEqual(x2, x2b);
assertEqual(x3, x3b);
assertEqual(x4, x4b);
assertValid(x1);
assertValid(x2);
assertValid(x3);
assertValid(x4);
assertValid(x1b);
assertValid(x2b);
assertValid(x3b);
assertValid(x4b);
assertValid(x1c);
assertValid(x2c);
assertValid(x3c);
assertValid(x4c);
}

View file

@ -47,6 +47,7 @@ class AmountTestCase : public CPPUNIT_NS::TestCase
CPPUNIT_TEST(testCommodityConversion);
CPPUNIT_TEST(testPrinting);
CPPUNIT_TEST(testCommodityPrinting);
CPPUNIT_TEST(testSerialization);
CPPUNIT_TEST_SUITE_END();
@ -99,6 +100,7 @@ public:
void testCommodityConversion();
void testPrinting();
void testCommodityPrinting();
void testSerialization();
private:
AmountTestCase(const AmountTestCase &copy);

View file

@ -19,8 +19,12 @@ void CommodityTestCase::testPriceHistory()
ptime apr15_07 = parse_datetime("2007/04/15 13:00:00");
// jww (2007-04-17): tbd
amount_t x0;
amount_t x1("100.10 AAPL");
assertThrow(x0.value(), amount_error);
assertFalse(x1.value());
// Commodities cannot be constructed by themselves, since a great
// deal of their state depends on how they were seen to be used.
commodity_t& aapl(x1.commodity());