diff options
-rw-r--r-- | Makefile.am | 12 | ||||
-rw-r--r-- | doc/ledger.1 | 2 | ||||
-rw-r--r-- | src/generate.cc | 384 | ||||
-rw-r--r-- | src/generate.h | 131 | ||||
-rw-r--r-- | src/report.cc | 16 | ||||
-rw-r--r-- | src/report.h | 2 | ||||
-rw-r--r-- | src/system.hh | 4 | ||||
-rwxr-xr-x | test/GenerateTests.py | 162 | ||||
-rwxr-xr-x | tools/prove.sh | 11 |
9 files changed, 723 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index 403a0511..c3bffeab 100644 --- a/Makefile.am +++ b/Makefile.am @@ -65,6 +65,7 @@ libledger_data_la_LDFLAGS = -release $(VERSION).0 libledger_report_la_SOURCES = \ src/quotes.cc \ + src/generate.cc \ src/derive.cc \ src/emacs.cc \ src/output.cc \ @@ -121,6 +122,7 @@ pkginclude_HEADERS = \ src/chain.h \ src/precmd.h \ src/derive.h \ + src/generate.h \ src/output.h \ src/emacs.h \ src/quotes.h \ @@ -240,7 +242,7 @@ endif TESTS = if HAVE_PYTHON -TESTS += RegressTests BaselineTests ConfirmTests +TESTS += RegressTests BaselineTests ConfirmTests GenerateTests endif if HAVE_CPPUNIT @@ -382,6 +384,14 @@ ConfirmTests: $(srcdir)/test/ConfirmTests.py echo "$(PYTHON) $(srcdir)/test/ConfirmTests.py $(top_builddir)/ledger$(EXEEXT) $(srcdir)/test/input" > $@ chmod 755 $@ +GenerateTests_SOURCES = test/GenerateTests.py + +EXTRA_DIST += test/input + +GenerateTests: $(srcdir)/test/GenerateTests.py + echo "$(PYTHON) $(srcdir)/test/GenerateTests.py $(top_builddir)/ledger$(EXEEXT) $(srcdir)/test/input" > $@ + chmod 755 $@ + FULLCHECK=$(srcdir)/test/fullcheck.sh if HAVE_CPPUNIT diff --git a/doc/ledger.1 b/doc/ledger.1 index 1c6d6d62..aa7ab24e 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -61,6 +61,7 @@ The synonym .Nm lisp is also accepted. .It Nm equity Oo Ar query Oc +.It Nm generate .It Nm prices Oo Ar query Oc .It Nm pricesdb Oo Ar query Oc .It Nm print Oo Ar query Oc @@ -188,6 +189,7 @@ appeared in the original journal file. .It Fl \-revalued .It Fl \-revalued-only .It Fl \-revalued-total Ar EXPR +.It Fl \-seed Ar INT .It Fl \-script .It Fl \-set-account Ar EXPR .It Fl \-set-payee Ar EXPR diff --git a/src/generate.cc b/src/generate.cc new file mode 100644 index 00000000..cb4b5f43 --- /dev/null +++ b/src/generate.cc @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2003-2009, 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. + */ + +#include "generate.h" +#include "session.h" + +namespace ledger { + +generate_posts_iterator::generate_posts_iterator + (session_t& _session, + unsigned int _seed, + std::size_t _quantity, + bool _allow_invalid) + : session(_session), seed(_seed), quantity(_quantity), + allow_invalid(_allow_invalid), + + rnd_gen(seed == 0 ? static_cast<unsigned int>(std::time(0)) : seed), + + year_range(1900, 2300), year_gen(rnd_gen, year_range), + mon_range(1, 12), mon_gen(rnd_gen, mon_range), + day_range(1, 28), day_gen(rnd_gen, day_range), + + upchar_range('A', 'Z'), upchar_gen(rnd_gen, upchar_range), + downchar_range('a', 'z'), downchar_gen(rnd_gen, downchar_range), + numchar_range('0', '9'), numchar_gen(rnd_gen, numchar_range), + + truth_range(0, 1), truth_gen(rnd_gen, truth_range), + three_range(1, 3), three_gen(rnd_gen, three_range), + six_range(1, 6), six_gen(rnd_gen, six_range), + two_six_range(2, 6), two_six_gen(rnd_gen, two_six_range), + strlen_range(1, 40), strlen_gen(rnd_gen, strlen_range), + + neg_number_range(-1000000, -1), neg_number_gen(rnd_gen, neg_number_range), + pos_number_range(1, 1000000), pos_number_gen(rnd_gen, pos_number_range) +{ + TRACE_CTOR(generate_posts_iterator, "bool"); + + std::ostringstream next_date_buf; + generate_date(next_date_buf); + next_date = parse_date(next_date_buf.str()); + + std::ostringstream next_eff_date_buf; + generate_date(next_eff_date_buf); + next_eff_date = parse_date(next_eff_date_buf.str()); + +} + +void generate_posts_iterator::generate_string(std::ostream& out, int len, + bool only_alpha) +{ + DEBUG("generate.post.string", + "Generating string of length " << len << ", only alpha " << only_alpha); + + int last; + bool first = true; + for (int i = 0; i < len; i++) { + int next = only_alpha ? 3 : three_gen(); + bool output = true; + switch (next) { + case 1: // colon + if (! first && last == 3 && strlen_gen() % 10 == 0 && i + 1 != len) + out << ':'; + else { + i--; + output = false; + } + break; + case 2: // space + if (! first && last == 3 && strlen_gen() % 20 == 0 && i + 1 != len) + out << ' '; + else { + i--; + output = false; + } + break; + case 3: // character + switch (three_gen()) { + case 1: // uppercase + out << char(upchar_gen()); + break; + case 2: // lowercase + out << char(downchar_gen()); + break; + case 3: // number + if (! only_alpha && ! first) + out << char(numchar_gen()); + else { + i--; + output = false; + } + break; + } + break; + } + if (output) { + last = next; + first = false; + } + } +} + +bool generate_posts_iterator::generate_account(std::ostream& out, + bool no_virtual) +{ + bool must_balance = true; + bool is_virtual = false; + + if (! no_virtual) { + switch (three_gen()) { + case 1: + out << '['; + is_virtual = true; + break; + case 2: + out << '('; + must_balance = false; + is_virtual = true; + break; + case 3: + break; + } + } + + generate_string(out, strlen_gen()); + + if (is_virtual) { + if (must_balance) + out << ']'; + else + out << ')'; + } + + return must_balance; +} + +void generate_posts_iterator::generate_commodity(std::ostream& out) +{ + string comm; + do { + std::ostringstream buf; + generate_string(buf, six_gen(), true); + comm = buf.str(); + } + while (comm == "h" || comm == "m" || comm == "s" || + comm == "and" || comm == "div" || comm == "false" || + comm == "or" || comm == "not" || comm == "true"); + + out << comm; +} + +string generate_posts_iterator::generate_amount(std::ostream& out, + value_t not_this_amount, + bool no_negative) +{ + std::ostringstream buf; + + if (truth_gen()) { // commodity goes in front + generate_commodity(buf); + if (truth_gen()) + buf << ' '; + if (no_negative || truth_gen()) + buf << pos_number_gen(); + else + buf << neg_number_gen(); + } else { + if (no_negative || truth_gen()) + buf << pos_number_gen(); + else + buf << neg_number_gen(); + if (truth_gen()) + buf << ' '; + generate_commodity(buf); + } + +#if 0 + // Possibly generate an annotized commodity, but make it rarer + if (! no_negative && three_gen() == 1) { + if (truth_gen()) { + out << " {"; + generate_amount(out, value_t(), true); + out << '}'; + } + if (truth_gen()) { + out << " ["; + generate_date(out); + out << ']'; + } + if (truth_gen()) { + out << " ("; + generate_string(out, strlen_gen()); + out << ')'; + } + } +#endif + + if (! not_this_amount.is_null() && + value_t(buf.str()).as_amount().commodity() == + not_this_amount.as_amount().commodity()) + return ""; + + out << buf.str(); + + return buf.str(); +} + +bool generate_posts_iterator::generate_post(std::ostream& out, bool no_amount) +{ + out << " "; + bool must_balance = generate_account(out, no_amount); + out << " "; + + if (! no_amount) { + value_t amount(generate_amount(out)); + if (truth_gen()) + generate_cost(out, amount); + } + if (truth_gen()) + generate_note(out); + out << '\n'; + + return must_balance; +} + +void generate_posts_iterator::generate_cost(std::ostream& out, value_t amount) +{ + std::ostringstream buf; + + if (truth_gen()) + buf << " @ "; + else + buf << " @@ "; + + if (! generate_amount(buf, amount, true).empty()) + out << buf.str(); +} + +void generate_posts_iterator::generate_date(std::ostream& out) +{ + out.width(4); + out.fill('0'); + out << year_gen(); + + out.width(1); + out << '/'; + + out.width(2); + out.fill('0'); + out << mon_gen(); + + out.width(1); + out << '/'; + + out.width(2); + out.fill('0'); + out << day_gen(); +} + +void generate_posts_iterator::generate_state(std::ostream& out) +{ + switch (three_gen()) { + case 1: + out << "* "; + break; + case 2: + out << "! "; + break; + case 3: + out << ""; + break; + } +} + +void generate_posts_iterator::generate_code(std::ostream& out) +{ + out << '('; + generate_string(out, six_gen()); + out << ") "; +} + +void generate_posts_iterator::generate_payee(std::ostream& out) +{ + generate_string(out, strlen_gen()); +} + +void generate_posts_iterator::generate_note(std::ostream& out) +{ + out << "\n ; "; + generate_string(out, strlen_gen()); +} + +void generate_posts_iterator::generate_xact(std::ostream& out) +{ + out << format_date(next_date, string("%Y/%m/%d")); + next_date += gregorian::days(six_gen()); + if (truth_gen()) { + out << '='; + out << format_date(next_eff_date, string("%Y/%m/%d")); + next_eff_date += gregorian::days(six_gen()); + } + out << ' '; + + generate_state(out); + generate_code(out); + generate_payee(out); + if (truth_gen()) + generate_note(out); + out << '\n'; + + int count = three_gen() * 2; + bool has_must_balance = false; + for (int i = 0; i < count; i++) { + if (generate_post(out)) + has_must_balance = true; + } + if (has_must_balance) + generate_post(out, true); + + out << '\n'; +} + +post_t * generate_posts_iterator::operator()() +{ + post_t * post = posts(); + if (post == NULL && quantity > 0) { + std::ostringstream buf; + generate_xact(buf); + + DEBUG("generate.post", "The post we intend to parse:\n" << buf.str()); + + std::istringstream in(buf.str()); + try { + if (session.journal->parse(in, session) != 0) { + VERIFY(session.journal->xacts.back()->valid()); + posts.reset(*session.journal->xacts.back()); + post = posts(); + } + } + catch (std::exception& err) { + add_error_context(_("While parsing generated transaction (seed %1):" + << seed)); + add_error_context(buf.str()); + throw; + } + catch (int status) { + add_error_context(_("While parsing generated transaction (seed %1):" + << seed)); + add_error_context(buf.str()); + throw; + } + + quantity--; + } + return post; +} + +} // namespace ledger diff --git a/src/generate.h b/src/generate.h new file mode 100644 index 00000000..7a837bfb --- /dev/null +++ b/src/generate.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2003-2009, 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. + */ + +/** + * @addtogroup generate + */ + +/** + * @file generate.h + * @author John Wiegley + * + * @ingroup report + * + * @brief Brief + * + * Long. + */ +#ifndef _GENERATE_H +#define _GENERATE_H + +#include "iterators.h" + +namespace ledger { + +class generate_posts_iterator : public posts_iterator +{ + session_t& session; + unsigned int seed; + std::size_t quantity; + bool allow_invalid; + date_t next_date; + date_t next_eff_date; + + mt19937 rnd_gen; + + typedef variate_generator<mt19937&, uniform_int<> > int_generator_t; + typedef variate_generator<mt19937&, uniform_real<> > real_generator_t; + + uniform_int<> year_range; + int_generator_t year_gen; + uniform_int<> mon_range; + int_generator_t mon_gen; + uniform_int<> day_range; + int_generator_t day_gen; + + uniform_int<> upchar_range; + int_generator_t upchar_gen; + uniform_int<> downchar_range; + int_generator_t downchar_gen; + uniform_int<> numchar_range; + int_generator_t numchar_gen; + + uniform_int<> truth_range; + int_generator_t truth_gen; + uniform_int<> three_range; + int_generator_t three_gen; + uniform_int<> six_range; + int_generator_t six_gen; + uniform_int<> two_six_range; + int_generator_t two_six_gen; + + uniform_int<> strlen_range; + int_generator_t strlen_gen; + + uniform_real<> neg_number_range; + real_generator_t neg_number_gen; + uniform_real<> pos_number_range; + real_generator_t pos_number_gen; + + xact_posts_iterator posts; + +public: + generate_posts_iterator(session_t& _session, + unsigned int _seed = 0, + std::size_t _quantity = 100, + bool _allow_invalid = false); + + virtual ~generate_posts_iterator() throw() { + TRACE_DTOR(generate_posts_iterator); + } + + virtual post_t * operator()(); + +protected: + void generate_string(std::ostream& out, int len, bool only_alpha = false); + bool generate_account(std::ostream& out, bool no_virtual = false); + void generate_commodity(std::ostream& out); + string generate_amount(std::ostream& out, + value_t not_this_amount = NULL_VALUE, + bool no_negative = false); + bool generate_post(std::ostream& out, bool no_amount = false); + void generate_cost(std::ostream& out, value_t amount); + void generate_date(std::ostream& out); + void generate_state(std::ostream& out); + void generate_code(std::ostream& out); + void generate_payee(std::ostream& out); + void generate_note(std::ostream& out); + void generate_xact(std::ostream& out); +}; + +} // namespace ledger + +#endif // _GENERATE_H diff --git a/src/report.cc b/src/report.cc index a969a2b8..5e4ba48d 100644 --- a/src/report.cc +++ b/src/report.cc @@ -32,6 +32,7 @@ #include "report.h" #include "interactive.h" #include "iterators.h" +#include "generate.h" #include "filters.h" #include "chain.h" #include "output.h" @@ -48,6 +49,14 @@ void report_t::posts_report(post_handler_ptr handler) session.clean_posts(); } +void report_t::generate_report(post_handler_ptr handler) +{ + generate_posts_iterator walker + (session, HANDLED(seed_) ? + static_cast<unsigned int>(HANDLER(seed_).value.to_long()) : 0); + pass_down_posts(chain_post_handlers(*this, handler), walker); +} + void report_t::xact_report(post_handler_ptr handler, xact_t& xact) { xact_posts_iterator walker(xact); @@ -536,6 +545,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) else OPT(sort_xacts_); else OPT_(subtotal); else OPT(start_of_week_); + else OPT(seed_); break; case 't': OPT_CH(amount_); @@ -709,6 +719,12 @@ expr_t::ptr_op_t report_t::lookup(const string& name) if (is_eq(q, "format")) return WRAP_FUNCTOR(format_command); break; + case 'g': + if (is_eq(q, "generate")) + return expr_t::op_t::wrap_functor + (reporter<post_t, post_handler_ptr, &report_t::generate_report> + (new format_posts(*this, report_format(HANDLER(print_format_)), + false), *this)); case 'h': if (is_eq(q, "hello") && maybe_import("ledger.hello")) return session.lookup(string(PRECMD_PREFIX) + "hello"); diff --git a/src/report.h b/src/report.h index 74a49052..28dd0fb4 100644 --- a/src/report.h +++ b/src/report.h @@ -122,6 +122,7 @@ public: } void posts_report(post_handler_ptr handler); + void generate_report(post_handler_ptr handler); void xact_report(post_handler_ptr handler, xact_t& xact); void accounts_report(acct_handler_ptr handler); void commodities_report(post_handler_ptr handler); @@ -582,6 +583,7 @@ public: set_expr(args[0].to_string()); }); + OPTION(report_t, seed_); OPTION(report_t, set_account_); OPTION(report_t, set_payee_); OPTION(report_t, set_price_); diff --git a/src/system.hh b/src/system.hh index 899c2f2a..15976459 100644 --- a/src/system.hh +++ b/src/system.hh @@ -170,6 +170,10 @@ typedef std::ostream::pos_type ostream_pos_type; #include <boost/operators.hpp> #include <boost/optional.hpp> #include <boost/ptr_container/ptr_list.hpp> +#include <boost/random/mersenne_twister.hpp> +#include <boost/random/uniform_int.hpp> +#include <boost/random/uniform_real.hpp> +#include <boost/random/variate_generator.hpp> #include <boost/regex.hpp> #include <boost/variant.hpp> #include <boost/version.hpp> diff --git a/test/GenerateTests.py b/test/GenerateTests.py new file mode 100755 index 00000000..88c223cd --- /dev/null +++ b/test/GenerateTests.py @@ -0,0 +1,162 @@ +#!/usr/bin/python + +# This script confirms both that the register report "adds up", and that its +# final balance is the same as what the balance report shows. + +import sys +import os +import re + +from subprocess import Popen, PIPE +from difflib import ndiff + +ledger = sys.argv[1] +succeeded = 0 +failed = 0 + +if not os.path.isfile(ledger): + sys.exit(1) + +def normalize(line): + match = re.match("((\s*)([A-Za-z]+)?(\s*)([-0-9.]+)(\s*)([A-Za-z]+)?)( (.+))?$", line) + if match: + if match.group(3): + prefix = match.group(3) + " " + match.group(5) + if match.group(8): + return prefix + match.group(8) + return prefix + elif match.group(7): + prefix = match.group(7) + " " + match.group(5) + if match.group(8): + return prefix + match.group(8) + return prefix + return line + +def generation_test(seed): + global succeeded, failed + + p_gen = Popen("%s --args-only --actual --seed=%d generate" % (ledger, seed), + shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, + close_fds=True) + cout = "" + cout_data = os.read(p_gen.stdout.fileno(), 8192) + while cout_data: + if cout_data: + cout += cout_data + cout_data = os.read(p_gen.stdout.fileno(), 8192) + if cout_data: + cout += cout_data + + if p_gen.wait() != 0: + print "Generation for seed %d failed due to error:" % seed + print p_gen.stderr.read() + del p_gen + return False + del p_gen + + p_print = Popen("%s --args-only --actual -f - print" % ledger, shell=True, + stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) + p_print.stdin.write(cout) + p_print.stdin.close() + p_print_out = p_print.stdout.read() + + if p_print.wait() != 0: + print "Print for seed %d failed due to error:" % seed + print p_print.stderr.read() + del p_print + return False + del p_print + + #p_cerr_bal = Popen("%s --args-only -f - bal" % ledger, shell=True, + # stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) + #p_cerr_bal.stdin.write(cerr) + #p_cerr_bal.stdin.close() + # + #cerr_lines = [normalize(line) for line in p_cerr_bal.stdout.readlines()] + # + #if p_cerr_bal.wait() != 0: + # print "Stderr balance for seed %d failed due to error:" % seed + # print p_cerr_bal.stderr.read() + # del p_cerr_bal + # return False + #del p_cerr_bal + + p_cout_bal = Popen("%s --args-only -f - bal" % ledger, shell=True, + stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) + p_cout_bal.stdin.write(cout) + p_cout_bal.stdin.close() + + cout_lines = p_cout_bal.stdout.readlines() + norm_cout_lines = [normalize(line) for line in cout_lines] + + if p_cout_bal.wait() != 0: + print "Stdout balance for seed %d failed due to error:" % seed + print p_cout_bal.stderr.read() + del p_cout_bal + return False + del p_cout_bal + + p_print_bal = Popen("%s --args-only -f - bal" % ledger, shell=True, + stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) + p_print_bal.stdin.write(p_print_out) + p_print_bal.stdin.close() + + print_lines = p_print_bal.stdout.readlines() + + if p_print_bal.wait() != 0: + print "Print balance for seed %d failed due to error:" % seed + print p_print_bal.stderr.read() + del p_print_bal + return False + del p_print_bal + + success = True + #printed = False + #for line in ndiff(cerr_lines, norm_cout_lines, charjunk=None): + # if line[:2] == " ": + # continue + # if not printed: + # if success: print + # print "Generation failure in output from seed %d (cerr vs. cout):" % seed + # if success: failed += 1 + # success = False + # printed = True + # print " ", line + + printed = False + for line in ndiff(cout_lines, print_lines, charjunk=None): + if line[:2] == " ": + continue + if not printed: + if success: print + print "Generation failure in output from seed %d (cout vs. print):" % seed + if success: failed += 1 + success = False + printed = True + print " ", line + + return success + +beg_range = 1 +end_range = 20 +if len(sys.argv) > 3: + beg_range = int(sys.argv[2]) + end_range = int(sys.argv[3]) + +for i in range(beg_range, end_range): + if generation_test(i): + sys.stdout.write(".") + succeeded += 1 + else: + sys.stdout.write("E") + failed += 1 + +print +if succeeded > 0: + print "OK (%d) " % succeeded, +if failed > 0: + print "FAILED (%d)" % failed, +print + +sys.exit(failed) + diff --git a/tools/prove.sh b/tools/prove.sh new file mode 100755 index 00000000..c62c4988 --- /dev/null +++ b/tools/prove.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +if [ -n "$1" ]; then + ./ledger --seed=$1 --actual --args-only generate > /tmp/cout +else + ./ledger --actual --args-only generate > /tmp/cout +fi + +ledger -f /tmp/cout --actual --args-only print > /tmp/print + +diff -w -U3 /tmp/cout /tmp/print |