diff options
author | John Wiegley <johnw@newartisans.com> | 2007-05-09 10:02:56 +0000 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2008-04-13 03:38:42 -0400 |
commit | 8e20c378d6ee6eb36f8c6866f8c9ec52f8600c58 (patch) | |
tree | f610a289a997487d89f6b24dc48f5e1ffce35628 | |
parent | 623e6e024cf43fc855c889314b8da8802c2f0449 (diff) | |
download | fork-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/TODO | 5 | ||||
-rw-r--r-- | src/amount.cc | 103 | ||||
-rw-r--r-- | src/amount.h | 19 | ||||
-rw-r--r-- | src/binary.cc | 30 | ||||
-rw-r--r-- | src/binary.h | 22 | ||||
-rw-r--r-- | tests/numerics/t_amount.cc | 412 | ||||
-rw-r--r-- | tests/numerics/t_amount.h | 2 | ||||
-rw-r--r-- | tests/numerics/t_commodity.cc | 4 |
8 files changed, 398 insertions, 199 deletions
@@ -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 ©); 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()); |