diff options
author | John Wiegley <johnw@newartisans.com> | 2009-02-27 03:58:43 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2009-02-27 03:58:43 -0400 |
commit | 6b62be59fbb8a0b8fd4274fa46ca7295f1be0919 (patch) | |
tree | a14d7a1c3962d901cc26510fbb124380c0181a56 /src/generate.cc | |
parent | 645e43ef75b1af78ef6a4013684d76fd7d6e7118 (diff) | |
download | fork-ledger-6b62be59fbb8a0b8fd4274fa46ca7295f1be0919.tar.gz fork-ledger-6b62be59fbb8a0b8fd4274fa46ca7295f1be0919.tar.bz2 fork-ledger-6b62be59fbb8a0b8fd4274fa46ca7295f1be0919.zip |
Added generate command, --seed, and GenerateTests
Diffstat (limited to 'src/generate.cc')
-rw-r--r-- | src/generate.cc | 384 |
1 files changed, 384 insertions, 0 deletions
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 |