summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/CMakeLists.txt2
-rwxr-xr-xtest/LedgerHarness.py2
-rwxr-xr-xtest/RegressTests.py6
-rw-r--r--test/baseline/cmd-accounts.test6
-rw-r--r--test/baseline/cmd-commodities.test4
-rw-r--r--test/baseline/cmd-convert.test4
-rw-r--r--test/baseline/feat-convert-with-diretives.dat3
-rw-r--r--test/baseline/feat-convert-with-diretives.test28
-rw-r--r--test/baseline/opt-aux-date.test2
-rw-r--r--test/baseline/opt-count.test6
-rw-r--r--test/baseline/opt-file.test2
-rw-r--r--test/garbage-input.dat793
-rw-r--r--test/input/demo.ledger63
-rw-r--r--test/regress/0161EB1E.test15
-rw-r--r--test/regress/25A099C9.test42
-rw-r--r--test/regress/BF3C1F82-2.test12
-rw-r--r--test/regress/BF3C1F82.test19
-rw-r--r--test/regress/CAE63F5C-a.test17
-rw-r--r--test/regress/CAE63F5C-b.test15
-rw-r--r--test/regress/CAE63F5C-c.test15
-rw-r--r--test/regress/CMakeLists.txt20
-rw-r--r--test/regress/xact_code.dat3
-rw-r--r--test/regress/xact_code.py4
-rw-r--r--test/regress/xact_code_py.test3
-rw-r--r--test/unit/t_amount.cc54
25 files changed, 1085 insertions, 55 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index b5d8cf09..94ce0a0a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -22,7 +22,7 @@ macro(add_ledger_harness_tests _class)
foreach(TestFile ${${_class}_TESTS})
get_filename_component(TestFile_Name ${TestFile} NAME_WE)
string(FIND ${TestFile_Name} "_py" TestFile_IsPythonTest)
- if((NOT TestFile_IsPythonTest) OR HAVE_BOOST_PYTHON)
+ if((TestFile_IsPythonTest EQUAL -1) OR HAVE_BOOST_PYTHON)
add_test(${_class}Test_${TestFile_Name}
${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/test/RegressTests.py
${LEDGER_LOCATION} ${PROJECT_SOURCE_DIR}
diff --git a/test/LedgerHarness.py b/test/LedgerHarness.py
index b8900971..39a31286 100755
--- a/test/LedgerHarness.py
+++ b/test/LedgerHarness.py
@@ -116,12 +116,14 @@ class LedgerHarness:
def success(self):
sys.stdout.write(".")
+ sys.stdout.flush()
self.succeeded += 1
def failure(self, name=None):
sys.stdout.write("E")
if name:
sys.stdout.write("[%s]" % name)
+ sys.stdout.flush()
self.failed += 1
def exit(self):
diff --git a/test/RegressTests.py b/test/RegressTests.py
index 7d67eb21..01e14191 100755
--- a/test/RegressTests.py
+++ b/test/RegressTests.py
@@ -98,8 +98,10 @@ class RegressFile(object):
def run_test(self, test):
use_stdin = False
- if test['command'].find("-f - ") != -1:
- use_stdin = True
+ if test['command'].find("-f ") != -1:
+ test['command'] = '$ledger ' + test['command']
+ if re.search("-f (-|/dev/stdin)(\s|$)", test['command']):
+ use_stdin = True
else:
test['command'] = (('$ledger -f "%s" ' %
os.path.abspath(self.filename)) +
diff --git a/test/baseline/cmd-accounts.test b/test/baseline/cmd-accounts.test
index be6365fd..2f0310da 100644
--- a/test/baseline/cmd-accounts.test
+++ b/test/baseline/cmd-accounts.test
@@ -15,12 +15,12 @@
Assets:Testing123ÕßDone
test accounts
+Assets:AAA
Assets:Bank
-Equity:Opening balance
+Assets:Testing123ÕßDone
Assets:XXX
-Assets:AAA
Assets:♚
-Assets:Testing123ÕßDone
+Equity:Opening balance
end test
test accounts assets:a
diff --git a/test/baseline/cmd-commodities.test b/test/baseline/cmd-commodities.test
index 0ce6f7a0..719b6798 100644
--- a/test/baseline/cmd-commodities.test
+++ b/test/baseline/cmd-commodities.test
@@ -15,10 +15,10 @@
Income:Rewards
test commodities
-GBP
-AAA
"AA2"
"M&M"
+AAA
+GBP
end test
test commodities Assets:Rewards
diff --git a/test/baseline/cmd-convert.test b/test/baseline/cmd-convert.test
index d3fbeae5..d444da52 100644
--- a/test/baseline/cmd-convert.test
+++ b/test/baseline/cmd-convert.test
@@ -17,7 +17,7 @@ end test
test -f /dev/null --input-date-format "%m/%d/%Y" convert test/baseline/cmd-convert3.dat -> 1
__ERROR__
-While parsing file "test/baseline/cmd-convert3.dat", line 1:
+While parsing file "$sourcepath/test/baseline/cmd-convert3.dat", line 1:
While parsing CSV line:
01/01/2011,,
@@ -26,7 +26,7 @@ end test
test -f /dev/null convert test/baseline/cmd-convert4.dat -> 1
__ERROR__
-While parsing file "test/baseline/cmd-convert4.dat", line 1:
+While parsing file "$sourcepath/test/baseline/cmd-convert4.dat", line 1:
While parsing CSV line:
bogus,$10,
diff --git a/test/baseline/feat-convert-with-diretives.dat b/test/baseline/feat-convert-with-diretives.dat
new file mode 100644
index 00000000..ac13ff81
--- /dev/null
+++ b/test/baseline/feat-convert-with-diretives.dat
@@ -0,0 +1,3 @@
+date,payee,amount
+2012/01/01,KFC,$10
+2012/01/02,"REWE SAGT DANKE 123454321",10€
diff --git a/test/baseline/feat-convert-with-diretives.test b/test/baseline/feat-convert-with-diretives.test
new file mode 100644
index 00000000..2f6e0102
--- /dev/null
+++ b/test/baseline/feat-convert-with-diretives.test
@@ -0,0 +1,28 @@
+account Expenses:Food
+ payee KFC
+ payee REWE
+
+payee REWE
+ alias REWE SAGT DANKE
+
+# When reading csv file without directives:
+test -f /dev/null convert test/baseline/feat-convert-with-diretives.dat
+2012/01/01 * KFC
+ Expenses:Unknown $10
+ Equity:Unknown
+
+2012/01/02 * REWE SAGT DANKE 123454321
+ Expenses:Unknown 10€
+ Equity:Unknown
+end test
+
+# When reading csv file with directives:
+test --account "Assets:Cash" convert test/baseline/feat-convert-with-diretives.dat
+2012/01/01 * KFC
+ Expenses:Food $10
+ Assets:Cash
+
+2012/01/02 * REWE
+ Expenses:Food 10€
+ Assets:Cash
+end test
diff --git a/test/baseline/opt-aux-date.test b/test/baseline/opt-aux-date.test
index 9d1e73d0..495bb7e6 100644
--- a/test/baseline/opt-aux-date.test
+++ b/test/baseline/opt-aux-date.test
@@ -10,7 +10,7 @@
Expenses:Books $20.00
Assets:Cash
-test reg --effective
+test reg --aux-date
08-Jan-01 January Expenses:Books $10.00 $10.00
Assets:Cash $-10.00 0
08-Feb-01 End of January Expenses:Books $10.00 $10.00
diff --git a/test/baseline/opt-count.test b/test/baseline/opt-count.test
index 9c5495c8..7c935c7a 100644
--- a/test/baseline/opt-count.test
+++ b/test/baseline/opt-count.test
@@ -17,14 +17,14 @@
Assets:Cash -30.00 EUR
test accounts --count
-2 Expenses:Phone
4 Assets:Cash
+2 Expenses:Phone
2 Expenses:Rent
end test
test commodities --count
-4 GBP
4 EUR
+4 GBP
end test
test payees --count
@@ -33,8 +33,8 @@ test payees --count
end test
test commodities :rent --count
-1 GBP
1 EUR
+1 GBP
end test
test payees tag bnb --count
diff --git a/test/baseline/opt-file.test b/test/baseline/opt-file.test
index e01d929d..66d0ab1b 100644
--- a/test/baseline/opt-file.test
+++ b/test/baseline/opt-file.test
@@ -1,6 +1,6 @@
test -f opt-file-does-not-exist.dat bal -> 1
__ERROR__
-Error: Cannot read journal file "opt-file-does-not-exist.dat"
+Error: Cannot read journal file "$sourcepath/opt-file-does-not-exist.dat"
end test
test -f test/baseline/opt-file1.dat -f test/baseline/opt-file2.dat bal
diff --git a/test/garbage-input.dat b/test/garbage-input.dat
new file mode 100644
index 00000000..1b7d2101
--- /dev/null
+++ b/test/garbage-input.dat
@@ -0,0 +1,793 @@
+/*
+ * Copyright (c) 2003-2012, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @defgroup math Mathematical objects
+ */
+
+/**
+ * @file amount.h
+ * @author John Wiegley
+ *
+ * @ingroup math
+ *
+ * @brief Basic type for handling commoditized math: amount_t
+ *
+ * An amount is the most basic numerical type in Ledger, and relies on
+ * commodity.h to represent commoditized amounts, which allows Ledger to
+ * handle mathematical expressions involving disparate commodities.
+ *
+ * Amounts can be of virtually infinite size and precision. When
+ * division or multiplication is performed, the precision is
+ * automatically expanded to include as many extra digits as necessary
+ * to avoid losing information.
+ */
+#ifndef _AMOUNT_H
+#define _AMOUNT_H
+
+#include "utils.h"
+#include "times.h"
+#include "flags.h"
+
+namespace ledger {
+
+class commodity_t;
+struct annotation_t;
+struct keep_details_t;
+
+DECLARE_EXCEPTION(amount_error, std::runtime_error);
+
+enum parse_flags_enum_t {
+ PARSE_DEFAULT = 0x00,
+ PARSE_PARTIAL = 0x01,
+ PARSE_SINGLE = 0x02,
+ PARSE_NO_MIGRATE = 0x04,
+ PARSE_NO_REDUCE = 0x08,
+ PARSE_NO_ASSIGN = 0x10,
+ PARSE_NO_ANNOT = 0x20,
+ PARSE_OP_CONTEXT = 0x40,
+ PARSE_SOFT_FAIL = 0x80
+};
+
+typedef basic_flags_t<parse_flags_enum_t, uint_least8_t> parse_flags_t;
+
+/**
+ * @brief Encapsulate infinite-precision commoditized amounts
+ *
+ * Used to represent commoditized infinite-precision numbers, and
+ * uncommoditized, plain numbers. In the commoditized case, commodities
+ * keep track of how they are used, and are always displayed back to the
+ * user after the same fashion. For uncommoditized numbers, no display
+ * truncation is ever done. In both cases, internal precision is always
+ * kept to an excessive degree.
+ */
+class amount_t
+ : public ordered_field_operators<amount_t,
+ ordered_field_operators<amount_t, double,
+ ordered_field_operators<amount_t, unsigned long,
+ ordered_field_operators<amount_t, long> > > >
+{
+public:
+ /** Ready the amount subsystem for use.
+ @note Normally called by session_t::initialize(). */
+ static void initialize();
+ /** Shutdown the amount subsystem and free all resources.
+ @note Normally called by session_t::shutdown(). */
+ static void shutdown();
+
+ static bool is_initialized;
+
+ /** The amount's decimal precision. */
+ typedef uint_least16_t precision_t;
+
+ /** Number of places of precision by which values are extended to
+ avoid losing precision during division and multiplication. */
+ static const std::size_t extend_by_digits = 6U;
+
+ /** If amounts should be streamed using to_fullstring() rather than
+ to_string(), so that complete precision is always displayed no matter
+ what the precision of an individual commodity may be. */
+ static bool stream_fullstrings;
+
+protected:
+ void _copy(const amount_t& amt);
+ void _dup();
+ void _clear();
+ void _release();
+
+ struct bigint_t;
+
+ bigint_t * quantity;
+ commodity_t * commodity_;
+
+public:
+ /** @name Constructors
+ @{ */
+
+ /** Creates a value for which is_null() is true, and which has no
+ value or commodity. If used in a value expression it evaluates to
+ zero, and its commodity equals \c commodity_t::null_commodity. */
+ amount_t() : quantity(NULL), commodity_(NULL) {
+ TRACE_CTOR(amount_t, "");
+ }
+
+ /** Convert a double to an amount. As much precision as possible is
+ decoded from the binary floating point number. */
+ amount_t(const double val);
+
+ /** Convert an unsigned long to an amount. It's precision is zero. */
+ amount_t(const unsigned long val);
+
+ /** Convert a long to an amount. It's precision is zero, and the sign
+ is preserved. */
+ amount_t(const long val);
+
+ /** Parse a string as an (optionally commoditized) amount. If no
+ commodity is present, the resulting commodity is \c
+ commodity_t::null_commodity. The number may be of infinite
+ precision. */
+ explicit amount_t(const string& val) : quantity(NULL) {
+ parse(val);
+ TRACE_CTOR(amount_t, "const string&");
+ }
+ /** Parse a pointer to a C string as an (optionally commoditized)
+ amount. If no commodity is present, the resulting commodity is \c
+ commodity_t::null_commodity. The number may be of infinite
+ precision. */
+ explicit amount_t(const char * val) : quantity(NULL) {
+ assert(val);
+ parse(val);
+ TRACE_CTOR(amount_t, "const char *");
+ }
+
+ /*@}*/
+
+ /** Create an amount whose display precision is never truncated, even
+ if the amount uses a commodity (which normally causes "round on
+ streaming" to occur). This function is mostly used by debugging
+ code and unit tests. This is the proper way to specify \c
+ $100.005, where display of the extra digit precision is required.
+ If a regular constructor were used, the amount would stream as \c
+ $100.01, even though its internal value equals \c $100.005. */
+ static amount_t exact(const string& value);
+
+ /** Release the reference count held for the underlying \c
+ amount_t::bigint_t object. */
+ ~amount_t() {
+ TRACE_DTOR(amount_t);
+ if (quantity)
+ _release();
+ }
+
+ /** @name Assignment and copy
+ @{*/
+
+ /** Copy an amount object. Copies are very efficient, using a
+ copy-on-write model. Until the copy is changed, it refers to the
+ same memory used by the original via reference counting. The \c
+ amount_t::bigint_t class in amount.cc maintains the reference. */
+ amount_t(const amount_t& amt) : quantity(NULL) {
+ if (amt.quantity)
+ _copy(amt);
+ else
+ commodity_ = NULL;
+ TRACE_CTOR(amount_t, "copy");
+ }
+ /** Copy an amount object, applying the given commodity annotation
+ details afterward. This is equivalent to doing a normal copy
+ (@see amount_t(const amount_t&)) and then calling
+ amount_t::annotate(). */
+ amount_t(const amount_t& amt, const annotation_t& details) : quantity(NULL) {
+ assert(amt.quantity);
+ _copy(amt);
+ annotate(details);
+ TRACE_CTOR(amount_t, "const amount_t&, const annotation_t&");
+ }
+ /** Assign an amount object. This is like copying if the amount was
+ null beforehand, otherwise the previous value's reference is must
+ be freed. */
+ amount_t& operator=(const amount_t& amt);
+
+ amount_t& operator=(const double val) {
+ return *this = amount_t(val);
+ }
+ amount_t& operator=(const unsigned long val) {
+ return *this = amount_t(val);
+ }
+ amount_t& operator=(const long val) {
+ return *this = amount_t(val);
+ }
+
+ /* Assign a string to an amount. This causes the contents of the
+ string to be parsed, look for a commoditized or uncommoditized
+ amount specifier. */
+ amount_t& operator=(const string& str) {
+ return *this = amount_t(str);
+ }
+ amount_t& operator=(const char * str) {
+ assert(str);
+ return *this = amount_t(str);
+ }
+
+ /*@}*/
+
+ /** @name Comparison
+ @{ */
+
+ /** Compare two amounts, returning a number less than zero if \p amt
+ is greater, exactly zero if they are equal, and greater than zero
+ if \p amt is less. This method is used to implement all of the
+ other comparison methods.*/
+ int compare(const amount_t& amt) const;
+
+ /** Test two amounts for equality. First the commodity pointers are
+ quickly tested, then the multi-precision values themselves must be
+ compared. */
+ bool operator==(const amount_t& amt) const;
+
+ template <typename T>
+ bool operator==(const T& val) const {
+ return compare(val) == 0;
+ }
+ template <typename T>
+ bool operator<(const T& amt) const {
+ return compare(amt) < 0;
+ }
+ template <typename T>
+ bool operator>(const T& amt) const {
+ return compare(amt) > 0;
+ }
+
+ /*@}*/
+
+ /** @name Binary arithmetic
+ */
+ /*@{*/
+
+ amount_t& operator+=(const amount_t& amt);
+ amount_t& operator-=(const amount_t& amt);
+ amount_t& operator*=(const amount_t& amt) {
+ return multiply(amt);
+ }
+ amount_t& multiply(const amount_t& amt, bool ignore_commodity = false);
+
+ /** Divide two amounts while extending the precision to preserve the
+ accuracy of the result. For example, if \c 10 is divided by \c 3,
+ the result ends up having a precision of \link
+ amount_t::extend_by_digits \endlink place to avoid losing internal
+ resolution. */
+ amount_t& operator/=(const amount_t& amt);
+
+ /*@}*/
+
+ /** @name Unary arithmetic
+ @{ */
+
+ /** Return an amount's internal precision. To find the precision it
+ should be displayed at -- assuming it was not created using
+ amount_t::exact() -- use the following expression instead:
+ @code
+ amount.commodity().precision()
+ @endcode */
+ precision_t precision() const;
+ bool keep_precision() const;
+ void set_keep_precision(const bool keep = true) const;
+ precision_t display_precision() const;
+
+ /** Returns the negated value of an amount.
+ @see operator-()
+ */
+ amount_t negated() const {
+ amount_t temp(*this);
+ temp.in_place_negate();
+ return temp;
+ }
+ void in_place_negate();
+
+ amount_t operator-() const {
+ return negated();
+ }
+
+ /** Returns the absolute value of an amount. Equivalent to:
+ @code
+ (x < * 0) ? - x : x
+ @endcode
+ */
+ amount_t abs() const {
+ if (sign() < 0)
+ return negated();
+ return *this;
+ }
+
+ amount_t inverted() const {
+ amount_t temp(*this);
+ temp.in_place_invert();
+ return temp;
+ }
+ void in_place_invert();
+
+ /** Yields an amount whose display precision when output is truncated
+ to the display precision of its commodity. This is normally the
+ default state of an amount, but if one has become unrounded, this
+ sets the "keep precision" state back to false.
+ @see set_keep_precision */
+ amount_t rounded() const {
+ amount_t temp(*this);
+ temp.in_place_round();
+ return temp;
+ }
+ void in_place_round();
+
+ /** Yields an amount which has lost all of its extra precision, beyond what
+ the display precision of the commodity would have printed. */
+ amount_t truncated() const {
+ amount_t temp(*this);
+ temp.in_place_truncate();
+ return temp;
+ }
+ void in_place_truncate();
+
+ /** Yields an amount which has lost all of its extra precision, beyond what
+ the display precision of the commodity would have printed. */
+ amount_t floored() const {
+ amount_t temp(*this);
+ temp.in_place_floor();
+ return temp;
+ }
+ void in_place_floor();
+
+ /** Yields an amount which has lost all of its extra precision, beyond what
+ the display precision of the commodity would have printed. */
+ amount_t ceilinged() const {
+ amount_t temp(*this);
+ temp.in_place_ceiling();
+ return temp;
+ }
+ void in_place_ceiling();
+
+ /** Yields an amount whose display precision is never truncated, even
+ though its commodity normally displays only rounded values. */
+ amount_t unrounded() const {
+ amount_t temp(*this);
+ temp.in_place_unround();
+ return temp;
+ }
+ void in_place_unround();
+
+ /** reduces a value to its most basic commodity form, for amounts that
+ utilize "scaling commodities". For example, an amount of \c 1h
+ after reduction will be \c 3600s.
+ */
+ amount_t reduced() const {
+ amount_t temp(*this);
+ temp.in_place_reduce();
+ return temp;
+ }
+ void in_place_reduce();
+
+ /** unreduce(), if used with a "scaling commodity", yields the most
+ compact form greater than one. That is, \c 3599s will unreduce to
+ \c 59.98m, while \c 3601 unreduces to \c 1h.
+ */
+ amount_t unreduced() const {
+ amount_t temp(*this);
+ temp.in_place_unreduce();
+ return temp;
+ }
+ void in_place_unreduce();
+
+ /** Returns the historical value for an amount -- the default moment
+ returns the most recently known price -- based on the price history
+ for the given commodity (or determined automatically, if none is
+ provided). For example, if the amount were <tt>10 AAPL</tt>, and
+ on Apr 10, 2000 each share of \c AAPL was worth \c $10, then
+ calling value() for that moment in time would yield the amount \c
+ $100.00.
+ */
+ optional<amount_t>
+ value(const datetime_t& moment = datetime_t(),
+ const commodity_t * in_terms_of = NULL) const;
+
+ optional<amount_t> price() const;
+
+ /*@}*/
+
+ /** @name Truth tests
+ */
+ /*@{*/
+
+ /** Truth tests. An amount may be truth test in several ways:
+
+ sign() returns an integer less than, greater than, or equal to
+ zero depending on whether the amount is negative, zero, or
+ greater than zero. Note that this function tests the actual
+ value of the amount -- using its internal precision -- and not
+ the display value. To test its display value, use:
+ `round().sign()'.
+
+ is_nonzero(), or operator bool, returns true if an amount's
+ display value is not zero.
+
+ is_zero() returns true if an amount's display value is zero.
+ Thus, $0.0001 is considered zero if the current display precision
+ for dollars is two decimal places.
+
+ is_realzero() returns true if an amount's actual value is zero.
+ Thus, $0.0001 is never considered realzero.
+
+ is_null() returns true if an amount has no value and no
+ commodity. This only occurs if an uninitialized amount has never
+ been assigned a value.
+ */
+ int sign() const;
+
+ operator bool() const {
+ return is_nonzero();
+ }
+ bool is_nonzero() const {
+ return ! is_zero();
+ }
+
+ bool is_zero() const;
+ bool is_realzero() const {
+ return sign() == 0;
+ }
+
+ bool is_null() const {
+ if (! quantity) {
+ assert(! commodity_);
+ return true;
+ }
+ return false;
+ }
+
+ /*@}*/
+
+ /** @name Conversion
+ */
+ /*@{*/
+
+ /** Conversion methods. An amount may be converted to the same types
+ it can be constructed from -- with the exception of unsigned
+ long. Implicit conversions are not allowed in C++ (though they
+ are in Python), rather the following conversion methods must be
+ called explicitly:
+
+ to_double([bool]) returns an amount as a double. If the optional
+ boolean argument is true (the default), an exception is thrown if
+ the conversion would lose information.
+
+ to_long([bool]) returns an amount as a long integer. If the
+ optional boolean argument is true (the default), an exception is
+ thrown if the conversion would lose information.
+
+ fits_in_long() returns true if to_long() would not lose
+ precision.
+
+ to_string() returns an amount'ss "display value" as a string --
+ after rounding the value according to the commodity's default
+ precision. It is equivalent to: `round().to_fullstring()'.
+
+ to_fullstring() returns an amount's "internal value" as a string,
+ without any rounding.
+
+ quantity_string() returns an amount's "display value", but
+ without any commodity. Note that this is different from
+ `number().to_string()', because in that case the commodity has
+ been stripped and the full, internal precision of the amount
+ would be displayed.
+ */
+ double to_double() const;
+ long to_long() const;
+ bool fits_in_long() const;
+
+ operator string() const {
+ return to_string();
+ }
+ string to_string() const;
+ string to_fullstring() const;
+ string quantity_string() const;
+
+ /*@}*/
+
+ /** @name Commodity methods
+ */
+ /*@{*/
+
+ /** The following methods relate to an
+ amount's commodity:
+
+ commodity() returns an amount's commodity. If the amount has no
+ commodity, the value returned is the `null_commodity'.
+
+ has_commodity() returns true if the amount has a commodity.
+
+ set_commodity(commodity_t) sets an amount's commodity to the
+ given value. Note that this merely sets the current amount to
+ that commodity, it does not "observe" the amount for possible
+ changes in the maximum display precision of the commodity, the
+ way that `parse' does.
+
+ clear_commodity() sets an amount's commodity to null, such that
+ has_commodity() afterwards returns false.
+
+ number() returns a commodity-less version of an amount. This is
+ useful for accessing just the numeric portion of an amount.
+ */
+ commodity_t * commodity_ptr() const;
+ commodity_t& commodity() const {
+ return *commodity_ptr();
+ }
+
+ bool has_commodity() const;
+ void set_commodity(commodity_t& comm) {
+ if (! quantity)
+ *this = 0L;
+ commodity_ = &comm;
+ }
+ amount_t with_commodity(const commodity_t& comm) const {
+ if (commodity_ == &comm) {
+ return *this;
+ } else {
+ amount_t tmp(*this);
+ tmp.set_commodity(const_cast<commodity_t&>(comm));
+ return tmp;
+ }
+ }
+ void clear_commodity() {
+ commodity_ = NULL;
+ }
+
+ amount_t number() const {
+ if (! has_commodity())
+ return *this;
+
+ amount_t temp(*this);
+ temp.clear_commodity();
+ return temp;
+ }
+
+ /*@}*/
+
+ /** @name Commodity annotations
+ */
+ /*@{*/
+
+ /** An amount's commodity may be annotated with special details, such as the
+ price it was purchased for, when it was acquired, or an arbitrary note,
+ identifying perhaps the lot number of an item.
+
+ annotate_commodity(amount_t price, [datetime_t date, string tag])
+ sets the annotations for the current amount's commodity. Only
+ the price argument is required, although it can be passed as
+ `none' if no price is desired.
+
+ commodity_annotated() returns true if an amount's commodity has
+ any annotation details associated with it.
+
+ annotation_details() returns all of the details of an annotated
+ commodity's annotations. The structure returns will evaluate as
+ boolean false if there are no details.
+
+ strip_annotations() returns an amount whose commodity's annotations have
+ been stripped.
+ */
+ void annotate(const annotation_t& details);
+ bool has_annotation() const;
+
+ annotation_t& annotation();
+ const annotation_t& annotation() const {
+ return const_cast<amount_t&>(*this).annotation();
+ }
+
+ /** If the lot price is considered whenever working with commoditized
+ values.
+
+ Let's say a user adds two values of the following form:
+ @code
+ 10 AAPL + 10 AAPL {$20}
+ @endcode
+
+ This expression adds ten shares of Apple stock with another ten
+ shares that were purchased for \c $20 a share. If \c keep_price
+ is false, the result of this expression is an amount equal to
+ <tt>20 AAPL</tt>. If \c keep_price is \c true the expression
+ yields an exception for adding amounts with different commodities.
+ In that case, a \link balance_t \endlink object must be used to
+ store the combined sum. */
+ amount_t strip_annotations(const keep_details_t& what_to_keep) const;
+
+ /*@}*/
+
+ /** @name Parsing
+ */
+ /*@{*/
+
+ /** The `flags' argument of both parsing may be one or more of the
+ following:
+
+ PARSE_NO_MIGRATE means to not pay attention to the way an
+ amount is used. Ordinarily, if an amount were $100.001, for
+ example, it would cause the default display precision for $ to be
+ "widened" to three decimal places. If PARSE_NO_MIGRATE is
+ used, the commodity's default display precision is not changed.
+
+ PARSE_NO_REDUCE means not to call in_place_reduce() on the
+ resulting amount after it is parsed.
+
+ These parsing methods observe the amounts they parse (unless
+ PARSE_NO_MIGRATE is true), and set the display details of
+ the corresponding commodity accordingly. This way, amounts do
+ not require commodities to be pre-defined in any way, but merely
+ displays them back to the user in the same fashion as it saw them
+ used.
+
+ There is also a static convenience method called
+ `parse_conversion' which can be used to define a relationship
+ between scaling commodity values. For example, Ledger uses it to
+ define the relationships among various time values:
+
+ @code
+ amount_t::parse_conversion("1.0m", "60s"); // a minute is 60 seconds
+ amount_t::parse_conversion("1.0h", "60m"); // an hour is 60 minutes
+ @endcode
+
+ The method parse() is used to parse an amount from an input stream
+ or a string. A global operator>>() is also defined which simply
+ calls parse on the input stream. The parse() method has two forms:
+
+ parse(istream, flags_t) parses an amount from the given input
+ stream.
+
+ parse(string, flags_t) parses an amount from the given string.
+
+ parse(string, flags_t) also parses an amount from a string.
+ */
+ bool parse(std::istream& in,
+ const parse_flags_t& flags = PARSE_DEFAULT);
+ bool parse(const string& str,
+ const parse_flags_t& flags = PARSE_DEFAULT) {
+ std::istringstream stream(str);
+ bool result = parse(stream, flags);
+ return result;
+ }
+
+ static void parse_conversion(const string& larger_str,
+ const string& smaller_str);
+
+ /*@}*/
+
+ /** @name Printing
+ */
+ /*@{*/
+
+ /** An amount may be output to a stream using the `print' method. There is
+ also a global operator<< defined which simply calls print for an amount
+ on the given stream. There is one form of the print method, which takes
+ one required argument and two arguments with default values:
+
+ print(ostream, bool omit_commodity = false, bool full_precision = false)
+ prints an amounts to the given output stream, using its commodity's
+ default display characteristics. If `omit_commodity' is true, the
+ commodity will not be displayed, only the amount (although the
+ commodity's display precision is still used). If `full_precision' is
+ true, the full internal precision of the amount is displayed, regardless
+ of its commodity's display precision.
+ */
+#define AMOUNT_PRINT_NO_FLAGS 0x00
+#define AMOUNT_PRINT_RIGHT_JUSTIFY 0x01
+#define AMOUNT_PRINT_COLORIZE 0x02
+#define AMOUNT_PRINT_NO_COMPUTED_ANNOTATIONS 0x04
+#define AMOUNT_PRINT_ELIDE_COMMODITY_QUOTES 0x08
+
+ void print(std::ostream& out,
+ const uint_least8_t flags = AMOUNT_PRINT_NO_FLAGS) const;
+
+ /*@}*/
+
+ /** @name Debugging
+ */
+ /*@{*/
+
+ /** There are two methods defined to help with debugging:
+
+ dump(ostream) dumps an amount to an output stream. There is
+ little different from print(), it simply surrounds the display
+ value with a marker, for example "AMOUNT($1.00)". This code is
+ used by other dumping code elsewhere in Ledger.
+
+ valid() returns true if an amount is valid. This ensures that if
+ an amount has a commodity, it has a valid value pointer, for
+ example, even if that pointer simply points to a zero value.
+ */
+ void dump(std::ostream& out) const {
+ out << "AMOUNT(";
+ print(out);
+ out << ")";
+ }
+
+ bool valid() const;
+
+#if HAVE_BOOST_SERIALIZATION
+private:
+ /** Serialization. */
+
+ friend class boost::serialization::access;
+
+ template<class Archive>
+ void serialize(Archive& ar, const unsigned int /* version */);
+#endif // HAVE_BOOST_SERIALIZATION
+
+ /*@}*/
+};
+
+inline amount_t amount_t::exact(const string& value) {
+ amount_t temp;
+ temp.parse(value, PARSE_NO_MIGRATE);
+ return temp;
+}
+
+inline string amount_t::to_string() const {
+ std::ostringstream bufstream;
+ print(bufstream);
+ return bufstream.str();
+}
+
+inline string amount_t::to_fullstring() const {
+ std::ostringstream bufstream;
+ unrounded().print(bufstream);
+ return bufstream.str();
+}
+
+inline string amount_t::quantity_string() const {
+ std::ostringstream bufstream;
+ number().print(bufstream);
+ return bufstream.str();
+}
+
+inline std::ostream& operator<<(std::ostream& out, const amount_t& amt) {
+ if (amount_t::stream_fullstrings)
+ amt.unrounded().print(out);
+ else
+ amt.print(out);
+ return out;
+}
+inline std::istream& operator>>(std::istream& in, amount_t& amt) {
+ amt.parse(in);
+ return in;
+}
+
+void put_amount(property_tree::ptree& pt, const amount_t& amt,
+ bool wrap = true, bool commodity_details = false);
+
+} // namespace ledger
+
+#endif // _AMOUNT_H
diff --git a/test/input/demo.ledger b/test/input/demo.ledger
new file mode 100644
index 00000000..dbf47c2a
--- /dev/null
+++ b/test/input/demo.ledger
@@ -0,0 +1,63 @@
+2010/12/01 * Checking balance
+ Assets:Checking $1,000.00
+ Equity:Opening Balances
+
+2010/12/20 * Organic Co-op
+ Expenses:Food:Groceries $ 37.50 ; [=2011/01/01]
+ Expenses:Food:Groceries $ 37.50 ; [=2011/02/01]
+ Expenses:Food:Groceries $ 37.50 ; [=2011/03/01]
+ Expenses:Food:Groceries $ 37.50 ; [=2011/04/01]
+ Expenses:Food:Groceries $ 37.50 ; [=2011/05/01]
+ Expenses:Food:Groceries $ 37.50 ; [=2011/06/01]
+ Assets:Checking $ -225.00
+
+2010/12/28 Acme Mortgage
+ Liabilities:Mortgage:Principal $ 200.00
+ Expenses:Interest:Mortgage $ 500.00
+ Expenses:Escrow $ 300.00
+ * Assets:Checking $ -1000.00
+
+2011/01/02 Grocery Store
+ Expenses:Food:Groceries $ 65.00
+ * Assets:Checking
+
+2011/01/05 Employer
+ * Assets:Checking $ 2000.00
+ Income:Salary
+
+2011/01/14 Bank
+ ; Regular monthly savings transfer
+ Assets:Savings $ 300.00
+ Assets:Checking
+
+2011/01/19 Grocery Store
+ Expenses:Food:Groceries $ 44.00 ; hastag: not block
+ Assets:Checking
+
+2011/01/25 Bank
+ ; Transfer to cover car purchase
+ Assets:Checking $ 5,500.00
+ Assets:Savings
+ ; :nobudget:
+
+2011/01/25 Tom's Used Cars
+ Expenses:Auto $ 5,500.00
+ ; :nobudget:
+ Assets:Checking
+
+2011/01/27 Book Store
+ Expenses:Books $20.00
+ Liabilities:MasterCard
+
+2011/04/25 Tom's Used Cars
+ Expenses:Auto $ 5,500.00
+ ; :nobudget:
+ Assets:Checking
+
+2011/04/27 Bookstore
+ Expenses:Books $20.00
+ Assets:Checking
+
+2011/12/01 Sale
+ Assets:Checking $ 30.00
+ Income:Sales
diff --git a/test/regress/0161EB1E.test b/test/regress/0161EB1E.test
new file mode 100644
index 00000000..93498ad5
--- /dev/null
+++ b/test/regress/0161EB1E.test
@@ -0,0 +1,15 @@
+bucket Assets:Checking
+2011/04/25 Tom's Used Cars
+ Auto $ 5,500.00
+ ; :nobudget:
+
+A Assets:Checking
+2011/04/27 Book Store
+ Books $20.00
+
+test reg
+11-Apr-25 Tom's Used Cars Auto $ 5,500.00 $ 5,500.00
+ Assets:Checking $ -5,500.00 0
+11-Apr-27 Book Store Books $ 20.00 $ 20.00
+ Assets:Checking $ -20.00 0
+end test
diff --git a/test/regress/25A099C9.test b/test/regress/25A099C9.test
index 1ef5ebef..e511c799 100644
--- a/test/regress/25A099C9.test
+++ b/test/regress/25A099C9.test
@@ -1,43 +1,43 @@
-test -f src/amount.h reg -> 20
+test -f test/garbage-input.dat reg -> 20
__ERROR__
-While parsing file "src/amount.h", line 2:
+While parsing file "$sourcepath/test/garbage-input.dat", line 2:
Error: Unexpected whitespace at beginning of line
-While parsing file "src/amount.h", line 33:
+While parsing file "$sourcepath/test/garbage-input.dat", line 33:
Error: Unexpected whitespace at beginning of line
-While parsing file "src/amount.h", line 37:
+While parsing file "$sourcepath/test/garbage-input.dat", line 37:
Error: Unexpected whitespace at beginning of line
-While parsing file "src/amount.h", line 66:
+While parsing file "$sourcepath/test/garbage-input.dat", line 66:
Error: No quantity specified for amount
-While parsing file "src/amount.h", line 69:
+While parsing file "$sourcepath/test/garbage-input.dat", line 69:
Error: Unexpected whitespace at beginning of line
-While parsing file "src/amount.h", line 83:
+While parsing file "$sourcepath/test/garbage-input.dat", line 83:
Error: Unexpected whitespace at beginning of line
-While parsing file "src/amount.h", line 93:
+While parsing file "$sourcepath/test/garbage-input.dat", line 93:
Error: Unexpected whitespace at beginning of line
-While parsing file "src/amount.h", line 99:
+While parsing file "$sourcepath/test/garbage-input.dat", line 99:
Error: Unexpected whitespace at beginning of line
-While parsing file "src/amount.h", line 121:
+While parsing file "$sourcepath/test/garbage-input.dat", line 121:
Error: Unexpected whitespace at beginning of line
-While parsing file "src/amount.h", line 132:
+While parsing file "$sourcepath/test/garbage-input.dat", line 132:
Error: Unexpected whitespace at beginning of line
-While parsing file "src/amount.h", line 702:
+While parsing file "$sourcepath/test/garbage-input.dat", line 711:
Error: Unexpected whitespace at beginning of line
-While parsing file "src/amount.h", line 732:
+While parsing file "$sourcepath/test/garbage-input.dat", line 741:
Error: Unexpected whitespace at beginning of line
-While parsing file "src/amount.h", line 740:
+While parsing file "$sourcepath/test/garbage-input.dat", line 749:
Error: Unexpected whitespace at beginning of line
-While parsing file "src/amount.h", line 743:
+While parsing file "$sourcepath/test/garbage-input.dat", line 752:
Error: Invalid date/time: line amount_t amoun
-While parsing file "src/amount.h", line 749:
+While parsing file "$sourcepath/test/garbage-input.dat", line 758:
Error: Invalid date/time: line string amount_
-While parsing file "src/amount.h", line 755:
+While parsing file "$sourcepath/test/garbage-input.dat", line 764:
Error: Invalid date/time: line string amount_
-While parsing file "src/amount.h", line 761:
+While parsing file "$sourcepath/test/garbage-input.dat", line 770:
Error: Invalid date/time: line string amount_
-While parsing file "src/amount.h", line 767:
+While parsing file "$sourcepath/test/garbage-input.dat", line 776:
Error: Invalid date/time: line std::ostream&
-While parsing file "src/amount.h", line 774:
+While parsing file "$sourcepath/test/garbage-input.dat", line 783:
Error: Invalid date/time: line std::istream&
-While parsing file "src/amount.h", line 780:
+While parsing file "$sourcepath/test/garbage-input.dat", line 789:
Error: Unexpected whitespace at beginning of line
end test
diff --git a/test/regress/BF3C1F82-2.test b/test/regress/BF3C1F82-2.test
new file mode 100644
index 00000000..453151ce
--- /dev/null
+++ b/test/regress/BF3C1F82-2.test
@@ -0,0 +1,12 @@
+; Check that include directives are relative for "-f /dev/stdin"
+include non-existent-ledger-file-BF3C1F82
+test -f - reg -> 1
+__ERROR__
+While parsing file "", line 2:
+Error: File to include was not found: "./non-existent-ledger-file-BF3C1F82"
+end test
+test -f /dev/stdin reg -> 1
+__ERROR__
+While parsing file "", line 2:
+Error: File to include was not found: "./non-existent-ledger-file-BF3C1F82"
+end test
diff --git a/test/regress/BF3C1F82.test b/test/regress/BF3C1F82.test
new file mode 100644
index 00000000..50f4106f
--- /dev/null
+++ b/test/regress/BF3C1F82.test
@@ -0,0 +1,19 @@
+; Check that error reporting works for "-f -"
+
+2012/02/30 * Test
+ a 1
+ b
+test -f - reg -> 1
+__ERROR__
+While parsing file "", line 3:
+While parsing transaction:
+<no source context>
+Error: Day of month is not valid for year
+end test
+test -f /dev/stdin reg -> 1
+__ERROR__
+While parsing file "", line 3:
+While parsing transaction:
+<no source context>
+Error: Day of month is not valid for year
+end test
diff --git a/test/regress/CAE63F5C-a.test b/test/regress/CAE63F5C-a.test
new file mode 100644
index 00000000..4465bd2f
--- /dev/null
+++ b/test/regress/CAE63F5C-a.test
@@ -0,0 +1,17 @@
+2011/03/01 test1
+ a 4.00 €
+ b
+
+2011/03/02 test2
+ a 4.00 €
+ b
+
+2011/03/03 test2
+ a 4.00 €
+ b
+
+test reg a
+11-Mar-01 test1 a 4.00 € 4.00 €
+11-Mar-02 test2 a 4.00 € 8.00 €
+11-Mar-03 test2 a 4.00 € 12.00 €
+end test
diff --git a/test/regress/CAE63F5C-b.test b/test/regress/CAE63F5C-b.test
new file mode 100644
index 00000000..c0b7efd8
--- /dev/null
+++ b/test/regress/CAE63F5C-b.test
@@ -0,0 +1,15 @@
+2012/08/22 Payment
+ Accrued €208.00 {=$1.3109} @ $1.2799
+ Expenses €4.16 {=$1.2798689} @ $1.2799
+ Assets $-271.54
+ Income:Currency Conversion $-6.45
+
+test bal -X $
+ $272.67 Accrued
+ $-271.54 Assets
+ $6.45 Equity:Capital Gains
+ $5.32 Expenses
+ $-6.45 Income:Currency Conversion
+--------------------
+ $6.45
+end test
diff --git a/test/regress/CAE63F5C-c.test b/test/regress/CAE63F5C-c.test
new file mode 100644
index 00000000..ae2d7d10
--- /dev/null
+++ b/test/regress/CAE63F5C-c.test
@@ -0,0 +1,15 @@
+2012/08/22 Payment
+ Accrued €208.00 {=$1.3109} @ $1.2798689
+ Expenses €4.16 {=$1.2798689} @ $1.2798689
+ Assets $-271.54
+ Income:Currency Conversion $-6.45
+
+test bal -X $
+ $272.67 Accrued
+ $-271.54 Assets
+ $6.45 Equity:Capital Gains
+ $5.32 Expenses
+ $-6.45 Income:Currency Conversion
+--------------------
+ $6.45
+end test
diff --git a/test/regress/CMakeLists.txt b/test/regress/CMakeLists.txt
index 4b6232dd..26f55e84 100644
--- a/test/regress/CMakeLists.txt
+++ b/test/regress/CMakeLists.txt
@@ -1,19 +1 @@
-if(HAVE_BOOST_PYTHON)
- set(TEST_PYTHON_FLAGS "--python")
-endif()
-
-if(PYTHONINTERP_FOUND)
- file(GLOB REGRESSION_TESTS *.test)
- foreach(TestFile ${REGRESSION_TESTS})
- get_filename_component(TestFile_Name ${TestFile} NAME_WE)
- string(FIND ${TestFile_Name} "_py" TestFile_IsPythonTest)
- if((NOT TestFile_IsPythonTest) OR HAVE_BOOST_PYTHON)
- add_test(RegressionTest_${TestFile_Name}
- ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/test/RegressTests.py
- ${LEDGER_LOCATION} ${PROJECT_SOURCE_DIR}
- ${TestFile} ${TEST_PYTHON_FLAGS})
- set_target_properties(check
- PROPERTIES DEPENDS RegressionTest_${TestFile_Name})
- endif()
- endforeach()
-endif()
+add_ledger_harness_tests(Regress)
diff --git a/test/regress/xact_code.dat b/test/regress/xact_code.dat
new file mode 100644
index 00000000..60956a23
--- /dev/null
+++ b/test/regress/xact_code.dat
@@ -0,0 +1,3 @@
+2012-11-10 (C0-d3) Payee
+ Assets:Checking € -12,45
+ Expenses:Expenditure
diff --git a/test/regress/xact_code.py b/test/regress/xact_code.py
new file mode 100644
index 00000000..64abb17d
--- /dev/null
+++ b/test/regress/xact_code.py
@@ -0,0 +1,4 @@
+import ledger
+
+for post in ledger.read_journal('test/regress/xact_code.dat').query('expenses'):
+ print post.xact.code
diff --git a/test/regress/xact_code_py.test b/test/regress/xact_code_py.test
new file mode 100644
index 00000000..c22158e0
--- /dev/null
+++ b/test/regress/xact_code_py.test
@@ -0,0 +1,3 @@
+test python test/regress/xact_code.py
+C0-d3
+end test
diff --git a/test/unit/t_amount.cc b/test/unit/t_amount.cc
index 07cde8f3..b82de510 100644
--- a/test/unit/t_amount.cc
+++ b/test/unit/t_amount.cc
@@ -1109,6 +1109,60 @@ BOOST_AUTO_TEST_CASE(testCommodityAbs)
BOOST_CHECK(x2.valid());
}
+BOOST_AUTO_TEST_CASE(testFloor)
+{
+ amount_t x0;
+ amount_t x1("123.123");
+ amount_t x2("-123.123");
+
+ BOOST_CHECK_THROW(x0.floored(), amount_error);
+ BOOST_CHECK_EQUAL(amount_t(123L), x1.floored());
+ BOOST_CHECK_EQUAL(amount_t(-124L), x2.floored());
+
+ BOOST_CHECK(x0.valid());
+ BOOST_CHECK(x1.valid());
+ BOOST_CHECK(x2.valid());
+}
+
+BOOST_AUTO_TEST_CASE(testCommodityFloor)
+{
+ amount_t x1("$1234.56");
+ amount_t x2("$-1234.56");
+
+ BOOST_CHECK_EQUAL(amount_t("$1234"), x1.floored());
+ BOOST_CHECK_EQUAL(amount_t("$-1235"), x2.floored());
+
+ BOOST_CHECK(x1.valid());
+ BOOST_CHECK(x2.valid());
+}
+
+BOOST_AUTO_TEST_CASE(testCeiling)
+{
+ amount_t x0;
+ amount_t x1("123.123");
+ amount_t x2("-123.123");
+
+ BOOST_CHECK_THROW(x0.ceilinged(), amount_error);
+ BOOST_CHECK_EQUAL(amount_t(124L), x1.ceilinged());
+ BOOST_CHECK_EQUAL(amount_t(-123L), x2.ceilinged());
+
+ BOOST_CHECK(x0.valid());
+ BOOST_CHECK(x1.valid());
+ BOOST_CHECK(x2.valid());
+}
+
+BOOST_AUTO_TEST_CASE(testCommodityCeiling)
+{
+ amount_t x1("$1234.56");
+ amount_t x2("$-1234.56");
+
+ BOOST_CHECK_EQUAL(amount_t("$1235"), x1.ceilinged());
+ BOOST_CHECK_EQUAL(amount_t("$-1234"), x2.ceilinged());
+
+ BOOST_CHECK(x1.valid());
+ BOOST_CHECK(x2.valid());
+}
+
#ifndef NOT_FOR_PYTHON
#if 0
BOOST_AUTO_TEST_CASE(testReduction)