ledger/test/DocTests.py
Alexis Hildebrandt fbbb379fe0 Check examples in documentation when running tests
The DocTests.py script will parse a given texinfo file for specially
marked examples, run the ledger command from the example, and check
the result against the example output from the documentation.
2014-02-07 00:21:38 +01:00

139 lines
4.3 KiB
Python
Executable file

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import re
import sys
import hashlib
import subprocess
from difflib import unified_diff
class DocTests:
def __init__(self, argv):
if not os.path.isfile(argv[1]):
print "Cannot find ledger at '%s'" % argv[1]
sys.exit(1)
if not os.path.isfile(argv[2]):
print "Cannot find source path at '%s'" % argv[2]
sys.exit(1)
self.ledger = os.path.abspath(argv[1])
self.sourcepath = os.path.abspath(argv[2])
scriptpath = os.path.dirname(os.path.realpath(__file__))
self.verbose = False
self.debug = False
self.examples = dict()
self.testin_token = 'command'
self.testout_token = 'output'
def read_example(self):
endexample = re.compile(r'^@end\s+smallexample\s*$')
example = str()
while True:
line = self.file.readline()
self.current_line += 1
if len(line) <= 0 or endexample.match(line): break
example += line
return example
def test_id(self, example):
return hashlib.sha1(example.rstrip()).hexdigest()[0:7].upper()
def find_examples(self):
startexample = re.compile(r'^@smallexample\s+@c\s+(%s|%s)(?::([\dA-Fa-f]+))?' % (self.testin_token, self.testout_token))
while True:
line = self.file.readline()
self.current_line += 1
if len(line) <= 0: break
startmatch = startexample.match(line)
if (startmatch):
test_begin_pos = self.file.tell()
test_begin_line = self.current_line
test_kind = startmatch.group(1)
test_id = startmatch.group(2)
example = self.read_example()
test_end_pos = self.file.tell()
test_end_line = self.current_line
if not test_id:
print >> sys.stderr, 'Example', test_kind, 'in line', test_begin_line, 'is missing id.'
test_id = self.test_id(example)
if test_kind == self.testin_token:
print >> sys.stderr, 'Use', self.test_id(example)
elif test_kind == self.testin_token and test_id != self.test_id(example):
print >> sys.stderr, 'Expected test id', test_id, 'for example' \
, test_kind, 'on line', test_begin_line, 'to be', self.test_id(example)
try:
self.examples[test_id]
except KeyError:
self.examples[test_id] = dict()
self.examples[test_id][test_kind] = {
'bpos': test_begin_pos,
'epos': test_end_pos,
'blin': test_begin_line,
'elin': test_end_line,
test_kind: example,
}
def test_examples(self):
failed = 0
for test_id in self.examples:
example = self.examples[test_id]
try:
command = example[self.testin_token][self.testin_token]
except KeyError:
command = None
try:
output = example[self.testout_token][self.testout_token]
except KeyError:
output = None
if command and output:
command = command.rstrip().split()
if command[0] == '$': command.remove('$')
index = command.index('ledger')
command[index] = self.ledger
command.insert(index+1, '--init-file')
command.insert(index+2, '/dev/null')
scriptpath = os.path.dirname(os.path.realpath(__file__))
test_input_dir = scriptpath + '/../test/input/'
for i, arg in enumerate(command):
if '.dat' in arg or '.ledger' in arg:
if os.path.exists(test_input_dir + arg):
command[i] = test_input_dir + arg
try:
verify = subprocess.check_output(command)#.decode('utf-8')
except:
verify = str()
valid = (output == verify)
if self.verbose:
print test_id, ':', u'Passed' if valid else u'FAILED'
else:
sys.stdout.write('.' if valid else 'E')
if not valid:
failed += 1
if self.debug:
print ' '.join(command)
for line in unified_diff(output.split('\n'), verify.split('\n'), fromfile='generated', tofile='expected'):
print(line)
print
print
return failed
def main(self):
self.file = open(self.sourcepath)
self.current_line = 0
self.find_examples()
failed_examples = self.test_examples()
self.file.close()
return failed_examples
if __name__ == "__main__":
script = DocTests(sys.argv)
status = script.main()
sys.exit(status)