Beispiel #1
0
    def assertEqualEntries(self, expected_entries, actual_entries):
        """Compare two lists of entries exactly and print missing entries verbosely if
        they occur.

        Args:
          expected_entries: Either a list of directives or a string, in which case the
            string is run through beancount.parser.parse_string() and the resulting
            list is used.
          actual_entries: Same treatment as expected_entries, the other list of
            directives to compare to.
        Raises:
          AssertionError: If the exception fails.
        """
        expected_entries = read_string_or_entries(expected_entries)
        actual_entries = read_string_or_entries(actual_entries)

        same, expected_missing, actual_missing = compare.compare_entries(expected_entries,
                                                                         actual_entries)
        if not same:
            assert expected_missing or actual_missing, "Missing is missing: {}, {}".format(
                expected_missing, actual_missing)
            oss = io.StringIO()
            if expected_missing:
                oss.write("Present in expected set and not in actual set:\n\n")
                for entry in expected_missing:
                    oss.write(printer.format_entry(entry))
                    oss.write('\n')
            if actual_missing:
                oss.write("Present in actual set and not in expected set:\n\n")
                for entry in actual_missing:
                    oss.write(printer.format_entry(entry))
                    oss.write('\n')
            self.fail(oss.getvalue())
Beispiel #2
0
    def test_compare_entries(self):
        entries1, _, __ = loader.load_string(TEST_INPUT)
        entries2, _, __ = loader.load_string(TEST_INPUT)

        # Check two equal sets.
        same, missing1, missing2 = compare.compare_entries(entries1, entries2)
        self.assertTrue(same)
        self.assertFalse(missing1)
        self.assertFalse(missing2)

        # First > Second.
        same, missing1, missing2 = compare.compare_entries(
            entries1, entries2[:-1])
        self.assertFalse(same)
        self.assertTrue(missing1)
        self.assertFalse(missing2)
        self.assertEqual(1, len(missing1))
        self.assertTrue(isinstance(missing1.pop(), data.Close))

        # First < Second.
        same, missing1, missing2 = compare.compare_entries(
            entries1[:-1], entries2)
        self.assertFalse(same)
        self.assertFalse(missing1)
        self.assertTrue(missing2)
        self.assertEqual(1, len(missing2))
        self.assertTrue(isinstance(missing2.pop(), data.Close))

        # Both have missing.
        same, missing1, missing2 = compare.compare_entries(
            entries1[1:], entries2[:-1])
        self.assertFalse(same)
        self.assertTrue(missing1)
        self.assertTrue(missing2)
        self.assertEqual(1, len(missing1))
        self.assertTrue(isinstance(missing1.pop(), data.Close))
        self.assertEqual(1, len(missing2))
        self.assertTrue(isinstance(missing2.pop(), data.Open))
Beispiel #3
0
def assertEqualEntries(expected_entries,
                       actual_entries,
                       failfunc=DEFAULT_FAILFUNC,
                       allow_incomplete=False):
    """Compare two lists of entries exactly and print missing entries verbosely if
    they occur.

    Args:
      expected_entries: Either a list of directives or a string, in which case the
        string is run through beancount.parser.parse_string() and the resulting
        list is used.
      actual_entries: Same treatment as expected_entries, the other list of
        directives to compare to.
      failfunc: A function to call on failure.
      allow_incomplete: A boolean, true if we allow incomplete inputs and perform
        light-weight booking.
    Raises:
      AssertionError: If the exception fails.
    """
    expected_entries = read_string_or_entries(expected_entries,
                                              allow_incomplete)
    actual_entries = read_string_or_entries(actual_entries, allow_incomplete)

    same, expected_missing, actual_missing = compare.compare_entries(
        expected_entries, actual_entries)
    if not same:
        assert expected_missing or actual_missing, "Missing is missing: {}, {}".format(
            expected_missing, actual_missing)
        oss = io.StringIO()
        if expected_missing:
            oss.write("Present in expected set and not in actual set:\n\n")
            for entry in expected_missing:
                oss.write(printer.format_entry(entry))
                oss.write('\n')
        if actual_missing:
            oss.write("Present in actual set and not in expected set:\n\n")
            for entry in actual_missing:
                oss.write(printer.format_entry(entry))
                oss.write('\n')
        failfunc(oss.getvalue())
Beispiel #4
0
    def assertEqualEntries(self, expected_entries, actual_entries, allow_incomplete=False):
        """Check that two lists of entries are equal.

        Entries can be provided either as a list of directives or as a
        string.  In the latter case, the string is parsed with
        beancount.parser.parse_string() and the resulting directives
        list is used. If allow_incomplete is True, light-weight
        booking is performed before comparing the directive lists,
        allowing to compare transactions with incomplete postings.

        Args:
          expected_entries: Expected entries.
          actual_entries: Actual entries.
          allow_incomplete: Perform booking before comparison.

        Raises:
          AssertionError: If the exception fails.

        """
        expected_entries = read_string_or_entries(expected_entries, allow_incomplete)
        actual_entries = read_string_or_entries(actual_entries, allow_incomplete)

        same, expected_missing, actual_missing = \
            compare.compare_entries(expected_entries, actual_entries)
        if not same:
            assert expected_missing or actual_missing, \
                "Missing is missing: {}, {}".format(expected_missing, actual_missing)
            oss = io.StringIO()
            if expected_missing:
                oss.write("Present in expected set and not in actual set:\n\n")
                for entry in expected_missing:
                    oss.write(printer.format_entry(entry))
                    oss.write('\n')
            if actual_missing:
                oss.write("Present in actual set and not in expected set:\n\n")
                for entry in actual_missing:
                    oss.write(printer.format_entry(entry))
                    oss.write('\n')
            self.fail(oss.getvalue())
Beispiel #5
0
def do_roundtrip(filename, unused_args):
    """Round-trip test on arbitrary Ledger.

    Read a Ledger's transactions, print them out, re-read them again and compare
    them. Both sets of parsed entries should be equal. Both printed files are
    output to disk, so you can also run diff on them yourself afterwards.

    Args:
      filename: A string, the Beancount input filename.
    """
    from beancount.parser import printer
    from beancount.core import compare
    from beancount import loader

    round1_filename = round2_filename = None
    try:
        logging.basicConfig(level=logging.INFO,
                            format='%(levelname)-8s: %(message)s')
        logging.info("Read the entries")
        entries, errors, options_map = loader.load_file(filename)
        printer.print_errors(errors, file=sys.stderr)

        logging.info("Print them out to a file")
        basename, extension = path.splitext(filename)
        round1_filename = ''.join([basename, '.roundtrip1', extension])
        with open(round1_filename, 'w') as outfile:
            printer.print_entries(entries, file=outfile)

        logging.info("Read the entries from that file")

        # Note that we don't want to run any of the auto-generation here, but
        # parsing now returns incomplete objects and we assume idempotence on a
        # file that was output from the printer after having been processed, so
        # it shouldn't add anything new. That is, a processed file printed and
        # resolve when parsed again should contain the same entries, i.e.
        # nothing new should be generated.
        entries_roundtrip, errors, options_map = loader.load_file(
            round1_filename)

        # Print out the list of errors from parsing the results.
        if errors:
            print(
                ',----------------------------------------------------------------------'
            )
            printer.print_errors(errors, file=sys.stdout)
            print(
                '`----------------------------------------------------------------------'
            )

        logging.info("Print what you read to yet another file")
        round2_filename = ''.join([basename, '.roundtrip2', extension])
        with open(round2_filename, 'w') as outfile:
            printer.print_entries(entries_roundtrip, file=outfile)

        logging.info("Compare the original entries with the re-read ones")
        same, missing1, missing2 = compare.compare_entries(
            entries, entries_roundtrip)
        if same:
            logging.info('Entries are the same. Congratulations.')
        else:
            logging.error('Entries differ!')
            print()
            print('\n\nMissing from original:')
            for entry in entries:
                print(entry)
                print(compare.hash_entry(entry))
                print(printer.format_entry(entry))
                print()

            print('\n\nMissing from round-trip:')
            for entry in missing2:
                print(entry)
                print(compare.hash_entry(entry))
                print(printer.format_entry(entry))
                print()
    finally:
        for rfilename in (round1_filename, round2_filename):
            if path.exists(rfilename):
                os.remove(rfilename)