summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2007-05-09 10:02:56 +0000
committerJohn Wiegley <johnw@newartisans.com>2008-04-13 03:38:42 -0400
commit8e20c378d6ee6eb36f8c6866f8c9ec52f8600c58 (patch)
treef610a289a997487d89f6b24dc48f5e1ffce35628
parent623e6e024cf43fc855c889314b8da8802c2f0449 (diff)
downloadfork-ledger-8e20c378d6ee6eb36f8c6866f8c9ec52f8600c58.tar.gz
fork-ledger-8e20c378d6ee6eb36f8c6866f8c9ec52f8600c58.tar.bz2
fork-ledger-8e20c378d6ee6eb36f8c6866f8c9ec52f8600c58.zip
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.
-rw-r--r--src/TODO5
-rw-r--r--src/amount.cc103
-rw-r--r--src/amount.h19
-rw-r--r--src/binary.cc30
-rw-r--r--src/binary.h22
-rw-r--r--tests/numerics/t_amount.cc412
-rw-r--r--tests/numerics/t_amount.h2
-rw-r--r--tests/numerics/t_commodity.cc4
8 files changed, 398 insertions, 199 deletions
diff --git a/src/TODO b/src/TODO
index 5ad232dd..3ea808d5 100644
--- a/src/TODO
+++ b/src/TODO
@@ -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.
diff --git a/src/amount.cc b/src/amount.cc
index 70d1ac4f..0025fbd9 100644
--- a/src/amount.cc
+++ b/src/amount.cc
@@ -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));
}
diff --git a/src/amount.h b/src/amount.h
index 2f360f72..10659f1f 100644
--- a/src/amount.h
+++ b/src/amount.h
@@ -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());
diff --git a/src/binary.cc b/src/binary.cc
index 0d4bfb80..d29f4d26 100644
--- a/src/binary.cc
+++ b/src/binary.cc
@@ -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);
diff --git a/src/binary.h b/src/binary.h
index 194c675e..5d699f7b 100644
--- a/src/binary.h
+++ b/src/binary.h
@@ -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;
diff --git a/tests/numerics/t_amount.cc b/tests/numerics/t_amount.cc
index 51d7a2f7..a70959a4 100644
--- a/tests/numerics/t_amount.cc
+++ b/tests/numerics/t_amount.cc
@@ -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);
+}
diff --git a/tests/numerics/t_amount.h b/tests/numerics/t_amount.h
index 9b3d36f0..2d5a327a 100644
--- a/tests/numerics/t_amount.h
+++ b/tests/numerics/t_amount.h
@@ -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);
diff --git a/tests/numerics/t_commodity.cc b/tests/numerics/t_commodity.cc
index 8b4d4fe8..f9892287 100644
--- a/tests/numerics/t_commodity.cc
+++ b/tests/numerics/t_commodity.cc
@@ -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());