diff options
Diffstat (limited to 'source-builder/sb/asciidoc/tests/testasciidoc.py')
-rwxr-xr-x | source-builder/sb/asciidoc/tests/testasciidoc.py | 420 |
1 files changed, 0 insertions, 420 deletions
diff --git a/source-builder/sb/asciidoc/tests/testasciidoc.py b/source-builder/sb/asciidoc/tests/testasciidoc.py deleted file mode 100755 index 679ad35..0000000 --- a/source-builder/sb/asciidoc/tests/testasciidoc.py +++ /dev/null @@ -1,420 +0,0 @@ -#!/usr/bin/env python - -USAGE = '''Usage: testasciidoc.py [OPTIONS] COMMAND - -Run AsciiDoc conformance tests specified in configuration FILE. - -Commands: - list List tests - run [NUMBER] [BACKEND] Execute tests - update [NUMBER] [BACKEND] Regenerate and update test data - -Options: - -f, --conf-file=CONF_FILE - Use configuration file CONF_FILE (default configuration file is - testasciidoc.conf in testasciidoc.py directory) - --force - Update all test data overwriting existing data''' - - -__version__ = '0.1.1' -__copyright__ = 'Copyright (C) 2009 Stuart Rackham' - - -import os, sys, re, difflib - -if sys.platform[:4] == 'java': - # Jython cStringIO is more compatible with CPython StringIO. - import cStringIO as StringIO -else: - import StringIO - -import asciidocapi - - -BACKENDS = ('html4','xhtml11','docbook','wordpress','html5') # Default backends. -BACKEND_EXT = {'html4':'.html', 'xhtml11':'.html', 'docbook':'.xml', - 'wordpress':'.html','slidy':'.html','html5':'.html'} - - -def iif(condition, iftrue, iffalse=None): - """ - Immediate if c.f. ternary ?: operator. - False value defaults to '' if the true value is a string. - False value defaults to 0 if the true value is a number. - """ - if iffalse is None: - if isinstance(iftrue, basestring): - iffalse = '' - if type(iftrue) in (int, float): - iffalse = 0 - if condition: - return iftrue - else: - return iffalse - -def message(msg=''): - print >>sys.stderr, msg - -def strip_end(lines): - """ - Strip blank strings from the end of list of strings. - """ - for i in range(len(lines)-1,-1,-1): - if not lines[i]: - del lines[i] - else: - break - -def normalize_data(lines): - """ - Strip comments and trailing blank strings from lines. - """ - result = [ s for s in lines if not s.startswith('#') ] - strip_end(result) - return result - - -class AsciiDocTest(object): - - def __init__(self): - self.number = None # Test number (1..). - self.name = '' # Optional test name. - self.title = '' # Optional test name. - self.description = [] # List of lines followoing title. - self.source = None # AsciiDoc test source file name. - self.options = [] - self.attributes = {} - self.backends = BACKENDS - self.datadir = None # Where output files are stored. - self.disabled = False - - def backend_filename(self, backend): - """ - Return the path name of the backend output file that is generated from - the test name and output file type. - """ - return '%s-%s%s' % ( - os.path.normpath(os.path.join(self.datadir, self.name)), - backend, - BACKEND_EXT[backend]) - - def parse(self, lines, confdir, datadir): - """ - Parse conf file test section from list of text lines. - """ - self.__init__() - self.confdir = confdir - self.datadir = datadir - lines = Lines(lines) - while not lines.eol(): - l = lines.read_until(r'^%') - if l: - if not l[0].startswith('%'): - if l[0][0] == '!': - self.disabled = True - self.title = l[0][1:] - else: - self.title = l[0] - self.description = l[1:] - continue - reo = re.match(r'^%\s*(?P<directive>[\w_-]+)', l[0]) - if not reo: - raise (ValueError, 'illegal directive: %s' % l[0]) - directive = reo.groupdict()['directive'] - data = normalize_data(l[1:]) - if directive == 'source': - if data: - self.source = os.path.normpath(os.path.join( - self.confdir, os.path.normpath(data[0]))) - elif directive == 'options': - self.options = eval(' '.join(data)) - for i,v in enumerate(self.options): - if isinstance(v, basestring): - self.options[i] = (v,None) - elif directive == 'attributes': - self.attributes = eval(' '.join(data)) - elif directive == 'backends': - self.backends = eval(' '.join(data)) - elif directive == 'name': - self.name = data[0].strip() - else: - raise (ValueError, 'illegal directive: %s' % l[0]) - if not self.title: - self.title = self.source - if not self.name: - self.name = os.path.basename(os.path.splitext(self.source)[0]) - - def is_missing(self, backend): - """ - Returns True if there is no output test data file for backend. - """ - return not os.path.isfile(self.backend_filename(backend)) - - def is_missing_or_outdated(self, backend): - """ - Returns True if the output test data file is missing or out of date. - """ - return self.is_missing(backend) or ( - os.path.getmtime(self.source) - > os.path.getmtime(self.backend_filename(backend))) - - def get_expected(self, backend): - """ - Return expected test data output for backend. - """ - f = open(self.backend_filename(backend)) - try: - result = f.readlines() - # Strip line terminators. - result = [ s.rstrip() for s in result ] - finally: - f.close() - return result - - def generate_expected(self, backend): - """ - Generate and return test data output for backend. - """ - asciidoc = asciidocapi.AsciiDocAPI() - asciidoc.options.values = self.options - asciidoc.attributes = self.attributes - infile = self.source - outfile = StringIO.StringIO() - asciidoc.execute(infile, outfile, backend) - return outfile.getvalue().splitlines() - - def update_expected(self, backend): - """ - Generate and write backend data. - """ - lines = self.generate_expected(backend) - if not os.path.isdir(self.datadir): - print('CREATING: %s' % self.datadir) - os.mkdir(self.datadir) - f = open(self.backend_filename(backend),'w+') - try: - print('WRITING: %s' % f.name) - f.writelines([ s + os.linesep for s in lines]) - finally: - f.close() - - def update(self, backend=None, force=False): - """ - Regenerate and update expected test data outputs. - """ - if backend is None: - backends = self.backends - else: - backends = [backend] - for backend in backends: - if force or self.is_missing_or_outdated(backend): - self.update_expected(backend) - - def run(self, backend=None): - """ - Execute test. - Return True if test passes. - """ - if backend is None: - backends = self.backends - else: - backends = [backend] - result = True # Assume success. - self.passed = self.failed = self.skipped = 0 - print('%d: %s' % (self.number, self.title)) - if self.source and os.path.isfile(self.source): - print('SOURCE: asciidoc: %s' % self.source) - for backend in backends: - fromfile = self.backend_filename(backend) - if not self.is_missing(backend): - expected = self.get_expected(backend) - strip_end(expected) - got = self.generate_expected(backend) - strip_end(got) - lines = [] - for line in difflib.unified_diff(got, expected, n=0): - lines.append(line) - if lines: - result = False - self.failed +=1 - lines = lines[3:] - print('FAILED: %s: %s' % (backend, fromfile)) - message('+++ %s' % fromfile) - message('--- got') - for line in lines: - message(line) - message() - else: - self.passed += 1 - print('PASSED: %s: %s' % (backend, fromfile)) - else: - self.skipped += 1 - print('SKIPPED: %s: %s' % (backend, fromfile)) - else: - self.skipped += len(backends) - if self.source: - msg = 'MISSING: %s' % self.source - else: - msg = 'NO ASCIIDOC SOURCE FILE SPECIFIED' - print(msg) - print('') - return result - - -class AsciiDocTests(object): - - def __init__(self, conffile): - """ - Parse configuration file. - """ - self.conffile = os.path.normpath(conffile) - # All file names are relative to configuration file directory. - self.confdir = os.path.dirname(self.conffile) - self.datadir = self.confdir # Default expected files directory. - self.tests = [] # List of parsed AsciiDocTest objects. - self.globals = {} - f = open(self.conffile) - try: - lines = Lines(f.readlines()) - finally: - f.close() - first = True - while not lines.eol(): - s = lines.read_until(r'^%+$') - s = [ l for l in s if l] # Drop blank lines. - # Must be at least one non-blank line in addition to delimiter. - if len(s) > 1: - # Optional globals precede all tests. - if first and re.match(r'^%\s*globals$',s[0]): - self.globals = eval(' '.join(normalize_data(s[1:]))) - if 'datadir' in self.globals: - self.datadir = os.path.join( - self.confdir, - os.path.normpath(self.globals['datadir'])) - else: - test = AsciiDocTest() - test.parse(s[1:], self.confdir, self.datadir) - self.tests.append(test) - test.number = len(self.tests) - first = False - - def run(self, number=None, backend=None): - """ - Run all tests. - If number is specified run test number (1..). - """ - self.passed = self.failed = self.skipped = 0 - for test in self.tests: - if (not test.disabled or number) and (not number or number == test.number) and (not backend or backend in test.backends): - test.run(backend) - self.passed += test.passed - self.failed += test.failed - self.skipped += test.skipped - if self.passed > 0: - print('TOTAL PASSED: %s' % self.passed) - if self.failed > 0: - print('TOTAL FAILED: %s' % self.failed) - if self.skipped > 0: - print('TOTAL SKIPPED: %s' % self.skipped) - - def update(self, number=None, backend=None, force=False): - """ - Regenerate expected test data and update configuratio file. - """ - for test in self.tests: - if (not test.disabled or number) and (not number or number == test.number): - test.update(backend, force=force) - - def list(self): - """ - Lists tests to stdout. - """ - for test in self.tests: - print '%d: %s%s' % (test.number, iif(test.disabled,'!'), test.title) - - -class Lines(list): - """ - A list of strings. - Adds eol() and read_until() to list type. - """ - - def __init__(self, lines): - super(Lines, self).__init__() - self.extend([s.rstrip() for s in lines]) - self.pos = 0 - - def eol(self): - return self.pos >= len(self) - - def read_until(self, regexp): - """ - Return a list of lines from current position up until the next line - matching regexp. - Advance position to matching line. - """ - result = [] - if not self.eol(): - result.append(self[self.pos]) - self.pos += 1 - while not self.eol(): - if re.match(regexp, self[self.pos]): - break - result.append(self[self.pos]) - self.pos += 1 - return result - - -def usage(msg=None): - if msg: - message(msg + '\n') - message(USAGE) - - -if __name__ == '__main__': - # Process command line options. - import getopt - try: - opts,args = getopt.getopt(sys.argv[1:], 'f:', ['force']) - except getopt.GetoptError: - usage('illegal command options') - sys.exit(1) - if len(args) == 0: - usage() - sys.exit(1) - conffile = os.path.join(os.path.dirname(sys.argv[0]), 'testasciidoc.conf') - force = False - for o,v in opts: - if o == '--force': - force = True - if o in ('-f','--conf-file'): - conffile = v - if not os.path.isfile(conffile): - message('missing CONF_FILE: %s' % conffile) - sys.exit(1) - tests = AsciiDocTests(conffile) - cmd = args[0] - number = None - backend = None - for arg in args[1:3]: - try: - number = int(arg) - except ValueError: - backend = arg - if backend and backend not in BACKENDS: - message('illegal BACKEND: %s' % backend) - sys.exit(1) - if number is not None and number not in range(1, len(tests.tests)+1): - message('illegal test NUMBER: %d' % number) - sys.exit(1) - if cmd == 'run': - tests.run(number, backend) - if tests.failed: - exit(1) - elif cmd == 'update': - tests.update(number, backend, force=force) - elif cmd == 'list': - tests.list() - else: - usage('illegal COMMAND: %s' % cmd) |