diff options
Diffstat (limited to 'tools/server-side/test_svn_server_log_parse.py')
-rwxr-xr-x | tools/server-side/test_svn_server_log_parse.py | 611 |
1 files changed, 611 insertions, 0 deletions
diff --git a/tools/server-side/test_svn_server_log_parse.py b/tools/server-side/test_svn_server_log_parse.py new file mode 100755 index 0000000..efc642c --- /dev/null +++ b/tools/server-side/test_svn_server_log_parse.py @@ -0,0 +1,611 @@ +#!/usr/bin/python + +# ==================================================================== +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ==================================================================== + +# Run this without arguments to run unit tests. +# Run with a path to a davautocheck ops log to test that it can parse that. + +import os +import re +import sys +import tempfile +try: + # Python >=3.0 + from urllib.parse import quote as urllib_parse_quote +except ImportError: + # Python <3.0 + from urllib import quote as urllib_parse_quote +import unittest + +import svn.core + +import svn_server_log_parse + +class TestCase(unittest.TestCase): + def setUp(self): + # Define a class to stuff everything passed to any handle_ + # method into self.result. + class cls(svn_server_log_parse.Parser): + def __getattr__(cls_self, attr): + if attr.startswith('handle_'): + return lambda *a: setattr(self, 'result', a) + raise AttributeError + self.parse = cls().parse + + def test_unknown(self): + line = 'unknown log line' + self.parse(line) + self.assertEqual(self.result, (line,)) + + def test_open(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, 'open') + self.assertRaises(svn_server_log_parse.Error, self.parse, 'open 2 cap / SVN/1.60. fooclient') + self.assertRaises(svn_server_log_parse.Error, self.parse, 'open a cap=() / SVN/1.60. fooclient') + self.assertEqual(self.parse('open 2 cap=() / SVN fooclient'), '') + self.assertEqual(self.result, (2, [], '/', 'SVN', 'fooclient')) + # TODO: Teach it about the capabilities, rather than allowing + # any words at all. + self.assertEqual(self.parse('open 2 cap=(foo) / SVN foo%20client'), '') + self.assertEqual(self.result, (2, ['foo'], '/', 'SVN', 'foo client')) + + def test_reparent(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, 'reparent') + self.assertEqual(self.parse('reparent /'), '') + self.assertEqual(self.result, ('/',)) + + def test_get_latest_rev(self): + self.assertEqual(self.parse('get-latest-rev'), '') + self.assertEqual(self.result, ()) + self.assertEqual(self.parse('get-latest-rev r3'), 'r3') + self.assertEqual(self.result, ()) + + def test_get_dated_rev(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, + 'get-dated-rev') + self.assertEqual(self.parse('get-dated-rev 2008-04-15T20:41:24.000000Z'), '') + self.assertEqual(self.result, ('2008-04-15T20:41:24.000000Z',)) + + def test_commit(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, 'commit') + self.assertRaises(svn_server_log_parse.Error, self.parse, 'commit 3') + self.assertEqual(self.parse('commit r3'), '') + self.assertEqual(self.result, (3,)) + self.assertEqual(self.parse('commit r3 leftover'), ' leftover') + self.assertEqual(self.result, (3,)) + + def test_get_dir(self): + self.get_dir_or_file('get-dir') + + def test_get_file(self): + self.get_dir_or_file('get-file') + + def get_dir_or_file(self, c): + self.assertRaises(svn_server_log_parse.Error, self.parse, c) + self.assertRaises(svn_server_log_parse.Error, self.parse, c + ' foo') + self.assertRaises(svn_server_log_parse.Error, self.parse, c + ' foo 3') + self.assertEqual(self.parse(c + ' /a/b/c r3 ...'), ' ...') + self.assertEqual(self.result, ('/a/b/c', 3, False, False)) + self.assertEqual(self.parse(c + ' / r3'), '') + self.assertEqual(self.result, ('/', 3, False, False)) + # path must be absolute + self.assertRaises(svn_server_log_parse.Error, + self.parse, c + ' a/b/c r3') + self.assertEqual(self.parse(c + ' /k r27 text'), '') + self.assertEqual(self.result, ('/k', 27, True, False)) + self.assertEqual(self.parse(c + ' /k r27 props'), '') + self.assertEqual(self.result, ('/k', 27, False, True)) + self.assertEqual(self.parse(c + ' /k r27 text props'), '') + self.assertEqual(self.result, ('/k', 27, True, True)) + # out of order not accepted + self.assertEqual(self.parse(c + ' /k r27 props text'), ' text') + self.assertEqual(self.result, ('/k', 27, False, True)) + + def test_lock(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, 'lock') + self.parse('lock (/foo)') + self.assertEqual(self.result, (['/foo'], False)) + self.assertEqual(self.parse('lock (/foo) steal ...'), ' ...') + self.assertEqual(self.result, (['/foo'], True)) + self.assertEqual(self.parse('lock (/foo) stear'), ' stear') + + def test_change_rev_prop(self): + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'change-rev-prop r3') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'change-rev-prop r svn:log') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'change-rev-prop rX svn:log') + self.assertEqual(self.parse('change-rev-prop r3 svn:log ...'), ' ...') + self.assertEqual(self.result, (3, 'svn:log')) + + def test_rev_proplist(self): + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'rev-proplist') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'rev-proplist r') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'rev-proplist rX') + self.assertEqual(self.parse('rev-proplist r3 ...'), ' ...') + self.assertEqual(self.result, (3,)) + + def test_rev_prop(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, 'rev-prop') + self.assertRaises(svn_server_log_parse.Error, self.parse, 'rev-prop r') + self.assertRaises(svn_server_log_parse.Error, self.parse, 'rev-prop rX') + self.assertEqual(self.parse('rev-prop r3 foo ...'), ' ...') + self.assertEqual(self.result, (3, 'foo')) + + def test_unlock(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, 'unlock') + self.parse('unlock (/foo)') + self.assertEqual(self.result, (['/foo'], False)) + self.assertEqual(self.parse('unlock (/foo) break ...'), ' ...') + self.assertEqual(self.result, (['/foo'], True)) + self.assertEqual(self.parse('unlock (/foo) bear'), ' bear') + + def test_get_lock(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, 'get-lock') + self.parse('get-lock /foo') + self.assertEqual(self.result, ('/foo',)) + + def test_get_locks(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, 'get-locks') + self.parse('get-locks /foo') + self.assertEqual(self.result, ('/foo',)) + + def test_get_locations(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, + 'get-locations') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'get-locations /foo 3') + self.assertEqual(self.parse('get-locations /foo (3 4) ...'), ' ...') + self.assertEqual(self.result, ('/foo', [3, 4])) + self.assertEqual(self.parse('get-locations /foo (3)'), '') + self.assertEqual(self.result, ('/foo', [3])) + + def test_get_location_segments(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, + 'get-location-segments') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'get-location-segments /foo 3') + self.assertEqual(self.parse('get-location-segments /foo@2 r3:4'), '') + self.assertEqual(self.result, ('/foo', 2, 3, 4)) + + def test_get_file_revs(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, 'get-file-revs') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'get-file-revs /foo 3') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'get-file-revs /foo 3:a') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'get-file-revs /foo r3:a') + self.assertEqual(self.parse('get-file-revs /foo r3:4 ...'), ' ...') + self.assertEqual(self.result, ('/foo', 3, 4, False)) + self.assertEqual(self.parse('get-file-revs /foo r3:4' + ' include-merged-revisions ...'), ' ...') + self.assertEqual(self.result, ('/foo', 3, 4, True)) + + def test_get_mergeinfo(self): + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'get-mergeinfo') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'get-mergeinfo /foo') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'get-mergeinfo (/foo') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'get-mergeinfo (/foo /bar') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'get-mergeinfo (/foo)') + self.assertRaises(svn_server_log_parse.BadMergeinfoInheritanceError, + self.parse, 'get-mergeinfo (/foo) bork') + self.assertEqual(self.parse('get-mergeinfo (/foo) explicit'), '') + self.assertEqual(self.result, (['/foo'], + svn.core.svn_mergeinfo_explicit, False)) + self.assertEqual(self.parse('get-mergeinfo (/foo /bar) inherited ...'), + ' ...') + self.assertEqual(self.result, (['/foo', '/bar'], + svn.core.svn_mergeinfo_inherited, False)) + self.assertEqual(self.result, (['/foo', '/bar'], + svn.core.svn_mergeinfo_inherited, False)) + + def test_log(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, 'log') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'log /foo') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'log (/foo)') + self.assertEqual(self.parse('log (/foo) r3:4' + ' include-merged-revisions'), '') + self.assertEqual(self.result, + (['/foo'], 3, 4, 0, False, False, True, [])) + self.assertEqual(self.parse('log (/foo /bar) r3:4 revprops=all ...'), + ' ...') + self.assertEqual(self.result, + (['/foo', '/bar'], 3, 4, 0, False, False, False, None)) + self.assertEqual(self.parse('log (/foo) r3:4 revprops=(a b) ...'), + ' ...') + self.assertEqual(self.result, + (['/foo'], 3, 4, 0, False, False, False, ['a', 'b'])) + self.assertEqual(self.parse('log (/foo) r8:1 limit=3'), '') + self.assertEqual(self.result, + (['/foo'], 8, 1, 3, False, False, False, [])) + + def test_check_path(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, 'check-path') + self.assertEqual(self.parse('check-path /foo@9'), '') + self.assertEqual(self.result, ('/foo', 9)) + + def test_stat(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, 'stat') + self.assertEqual(self.parse('stat /foo@9'), '') + self.assertEqual(self.result, ('/foo', 9)) + + def test_replay(self): + self.assertRaises(svn_server_log_parse.Error, self.parse, 'replay') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'replay /foo') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'replay (/foo) r9') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'replay (/foo) r9:10') + self.assertEqual(self.parse('replay /foo r9'), '') + self.assertEqual(self.result, ('/foo', 9)) + + def test_checkout_or_export(self): + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'checkout-or-export') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'checkout-or-export /foo') + self.assertEqual(self.parse('checkout-or-export /foo r9'), '') + self.assertEqual(self.result, ('/foo', 9, svn.core.svn_depth_unknown)) + self.assertRaises(svn_server_log_parse.BadDepthError, self.parse, + 'checkout-or-export /foo r9 depth=INVALID-DEPTH') + self.assertRaises(svn_server_log_parse.BadDepthError, self.parse, + 'checkout-or-export /foo r9 depth=bork') + self.assertEqual(self.parse('checkout-or-export /foo r9 depth=files .'), + ' .') + self.assertEqual(self.result, ('/foo', 9, svn.core.svn_depth_files)) + + def test_diff_1path(self): + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'diff') + self.assertEqual(self.parse('diff /foo r9:10'), '') + self.assertEqual(self.result, ('/foo', 9, 10, + svn.core.svn_depth_unknown, False)) + self.assertEqual(self.parse('diff /foo r9:10' + ' ignore-ancestry ...'), ' ...') + self.assertEqual(self.result, ('/foo', 9, 10, + svn.core.svn_depth_unknown, True)) + self.assertEqual(self.parse('diff /foo r9:10 depth=files'), '') + self.assertEqual(self.result, ('/foo', 9, 10, + svn.core.svn_depth_files, False)) + + def test_diff_2paths(self): + self.assertEqual(self.parse('diff /foo@9 /bar@10'), '') + self.assertEqual(self.result, ('/foo', 9, '/bar', 10, + svn.core.svn_depth_unknown, False)) + self.assertEqual(self.parse('diff /foo@9 /bar@10' + ' ignore-ancestry ...'), ' ...') + self.assertEqual(self.result, ('/foo', 9, '/bar', 10, + svn.core.svn_depth_unknown, True)) + self.assertEqual(self.parse('diff /foo@9 /bar@10' + ' depth=files ignore-ancestry'), '') + self.assertEqual(self.result, ('/foo', 9, '/bar', 10, + svn.core.svn_depth_files, True)) + + def test_status(self): + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'status') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'status /foo') + self.assertEqual(self.parse('status /foo r9'), '') + self.assertEqual(self.result, ('/foo', 9, svn.core.svn_depth_unknown)) + self.assertRaises(svn_server_log_parse.BadDepthError, self.parse, + 'status /foo r9 depth=INVALID-DEPTH') + self.assertRaises(svn_server_log_parse.BadDepthError, self.parse, + 'status /foo r9 depth=bork') + self.assertEqual(self.parse('status /foo r9 depth=files .'), + ' .') + self.assertEqual(self.result, ('/foo', 9, svn.core.svn_depth_files)) + + def test_switch(self): + self.assertEqual(self.parse('switch /foo /bar@10 ...'), ' ...') + self.assertEqual(self.result, ('/foo', '/bar', 10, + svn.core.svn_depth_unknown)) + self.assertEqual(self.parse('switch /foo /bar@10' + ' depth=files'), '') + self.assertEqual(self.result, ('/foo', '/bar', 10, + svn.core.svn_depth_files)) + + def test_update(self): + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'update') + self.assertRaises(svn_server_log_parse.Error, + self.parse, 'update /foo') + self.assertEqual(self.parse('update /foo r9'), '') + self.assertEqual(self.result, ('/foo', 9, svn.core.svn_depth_unknown, + False)) + self.assertRaises(svn_server_log_parse.BadDepthError, self.parse, + 'update /foo r9 depth=INVALID-DEPTH') + self.assertRaises(svn_server_log_parse.BadDepthError, self.parse, + 'update /foo r9 depth=bork') + self.assertEqual(self.parse('update /foo r9 depth=files .'), ' .') + self.assertEqual(self.result, ('/foo', 9, svn.core.svn_depth_files, + False)) + self.assertEqual(self.parse('update /foo r9 send-copyfrom-args .'), + ' .') + self.assertEqual(self.result, ('/foo', 9, svn.core.svn_depth_unknown, + True)) + +if __name__ == '__main__': + if len(sys.argv) == 1: + # No arguments so run the unit tests. + unittest.main() + sys.stderr.write('unittest.main failed to exit\n') + sys.exit(2) + + # Use the argument as the path to a log file to test against. + + def uri_encode(s): + # urllib.parse.quote encodes :&@ characters, svn does not. + return urllib_parse_quote(s, safe='/:&@') + + # Define a class to reconstruct the SVN-ACTION string. + class Test(svn_server_log_parse.Parser): + def handle_unknown(self, line): + sys.stderr.write('unknown log line at %d:\n%s\n' % (self.linenum, + line)) + sys.exit(2) + + def handle_open(self, protocol, capabilities, path, ra_client, client): + capabilities = ' '.join(capabilities) + if ra_client is None: + ra_client = '-' + if client is None: + client = '-' + path = uri_encode(path) + self.action = ('open %d cap=(%s) %s %s %s' + % (protocol, capabilities, path, ra_client, client)) + + def handle_reparent(self, path): + path = uri_encode(path) + self.action = 'reparent ' + path + + def handle_get_latest_rev(self): + self.action = 'get-latest-rev' + + def handle_get_dated_rev(self, date): + self.action = 'get-dated-rev ' + date + + def handle_commit(self, revision): + self.action = 'commit r%d' % (revision,) + + def handle_get_dir(self, path, revision, text, props): + path = uri_encode(path) + self.action = 'get-dir %s r%d' % (path, revision) + if text: + self.action += ' text' + if props: + self.action += ' props' + + def handle_get_file(self, path, revision, text, props): + path = uri_encode(path) + self.action = 'get-file %s r%d' % (path, revision) + if text: + self.action += ' text' + if props: + self.action += ' props' + + def handle_lock(self, paths, steal): + paths = [uri_encode(x) for x in paths] + self.action = 'lock (%s)' % (' '.join(paths),) + if steal: + self.action += ' steal' + + def handle_change_rev_prop(self, revision, revprop): + revprop = uri_encode(revprop) + self.action = 'change-rev-prop r%d %s' % (revision, revprop) + + def handle_rev_prop(self, revision, revprop): + revprop = uri_encode(revprop) + self.action = 'rev-prop r%d %s' % (revision, revprop) + + def handle_rev_proplist(self, revision): + self.action = 'rev-proplist r%d' % (revision,) + + def handle_unlock(self, paths, break_lock): + paths = [uri_encode(x) for x in paths] + self.action = 'unlock (%s)' % (' '.join(paths),) + if break_lock: + self.action += ' break' + + def handle_get_lock(self, path): + path = uri_encode(path) + self.action = 'get-lock ' + path + + def handle_get_locks(self, path): + self.action = 'get-locks ' + path + path = uri_encode(path) + + def handle_get_locations(self, path, revisions): + path = uri_encode(path) + self.action = ('get-locations %s (%s)' + % (path, ' '.join([str(x) for x in revisions]))) + + def handle_get_location_segments(self, path, peg, left, right): + path = uri_encode(path) + self.action = 'get-location-segments %s@%d r%d:%d' % (path, peg, + left, right) + + def handle_get_file_revs(self, path, left, right, + include_merged_revisions): + path = uri_encode(path) + self.action = 'get-file-revs %s r%d:%d' % (path, left, right) + if include_merged_revisions: + self.action += ' include-merged-revisions' + + def handle_get_mergeinfo(self, paths, inheritance, include_descendants): + paths = [uri_encode(x) for x in paths] + self.action = ('get-mergeinfo (%s) %s' + % (' '.join(paths), + svn.core.svn_inheritance_to_word(inheritance))) + if include_descendants: + self.action += ' include-descendants' + + def handle_log(self, paths, left, right, limit, discover_changed_paths, + strict, include_merged_revisions, revprops): + paths = [uri_encode(x) for x in paths] + self.action = 'log (%s) r%d:%d' % (' '.join(paths), + left, right) + if limit != 0: + self.action += ' limit=%d' % (limit,) + if discover_changed_paths: + self.action += ' discover-changed-paths' + if strict: + self.action += ' strict' + if include_merged_revisions: + self.action += ' include-merged-revisions' + if revprops is None: + self.action += ' revprops=all' + elif len(revprops) > 0: + revprops = [uri_encode(x) for x in revprops] + self.action += ' revprops=(%s)' % (' '.join(revprops),) + + def handle_check_path(self, path, revision): + path = uri_encode(path) + self.action = 'check-path %s@%d' % (path, revision) + + def handle_stat(self, path, revision): + path = uri_encode(path) + self.action = 'stat %s@%d' % (path, revision) + + def handle_replay(self, path, revision): + path = uri_encode(path) + self.action = 'replay %s r%d' % (path, revision) + + def maybe_depth(self, depth): + if depth != svn.core.svn_depth_unknown: + self.action += ' depth=%s' % ( + svn.core.svn_depth_to_word(depth),) + + def handle_checkout_or_export(self, path, revision, depth): + path = uri_encode(path) + self.action = 'checkout-or-export %s r%d' % (path, revision) + self.maybe_depth(depth) + + def handle_diff_1path(self, path, left, right, + depth, ignore_ancestry): + path = uri_encode(path) + self.action = 'diff %s r%d:%d' % (path, left, right) + self.maybe_depth(depth) + if ignore_ancestry: + self.action += ' ignore-ancestry' + + def handle_diff_2paths(self, from_path, from_rev, + to_path, to_rev, + depth, ignore_ancestry): + from_path = uri_encode(from_path) + to_path = uri_encode(to_path) + self.action = ('diff %s@%d %s@%d' + % (from_path, from_rev, to_path, to_rev)) + self.maybe_depth(depth) + if ignore_ancestry: + self.action += ' ignore-ancestry' + + def handle_status(self, path, revision, depth): + path = uri_encode(path) + self.action = 'status %s r%d' % (path, revision) + self.maybe_depth(depth) + + def handle_switch(self, from_path, to_path, to_rev, depth): + from_path = uri_encode(from_path) + to_path = uri_encode(to_path) + self.action = ('switch %s %s@%d' + % (from_path, to_path, to_rev)) + self.maybe_depth(depth) + + def handle_update(self, path, revision, depth, send_copyfrom_args): + path = uri_encode(path) + self.action = 'update %s r%d' % (path, revision) + self.maybe_depth(depth) + if send_copyfrom_args: + self.action += ' send-copyfrom-args' + + tmp = tempfile.mktemp() + try: + fp = open(tmp, 'w') + parser = Test() + parser.linenum = 0 + log_file = sys.argv[1] + log_type = None + for line in open(log_file): + if log_type is None: + # Figure out which log type we have. + if re.match(r'\d+ \d\d\d\d-', line): + log_type = 'svnserve' + elif re.match(r'\[\d\d/', line): + log_type = 'mod_dav_svn' + else: + sys.stderr.write("unknown log format in '%s'" + % (log_file,)) + sys.exit(3) + sys.stderr.write('parsing %s log...\n' % (log_type,)) + sys.stderr.flush() + + words = line.split() + if log_type == 'svnserve': + # Skip over PID, date, client address, username, and repos. + if words[5].startswith('ERR'): + # Skip error lines. + fp.write(line) + continue + leading = ' '.join(words[:5]) + action = ' '.join(words[5:]) + else: + # Find the SVN-ACTION string from the CustomLog format + # davautocheck.sh uses. If that changes, this will need + # to as well. Currently it's + # %t %u %{SVN-REPOS-NAME}e %{SVN-ACTION}e + leading = ' '.join(words[:4]) + action = ' '.join(words[4:]) + + # Parse the action and write the reconstructed action to + # the temporary file. Ignore the returned trailing text, + # as we have none in the davautocheck ops log. + parser.linenum += 1 + try: + parser.parse(action) + except svn_server_log_parse.Error: + sys.stderr.write('error at line %d: %s\n' + % (parser.linenum, action)) + raise + fp.write(leading + ' ' + parser.action + '\n') + fp.close() + # Check differences between original and reconstructed files + # (should be identical). + result = os.spawnlp(os.P_WAIT, 'diff', 'diff', '-u', log_file, tmp) + if result == 0: + sys.stderr.write('OK\n') + sys.exit(result) + finally: + try: + os.unlink(tmp) + except Exception as e: + sys.stderr.write('os.unlink(tmp): %s\n' % (e,)) |