summaryrefslogtreecommitdiffstats
path: root/source-builder/sb/asciidoc/tests/testasciidoc.py
diff options
context:
space:
mode:
Diffstat (limited to 'source-builder/sb/asciidoc/tests/testasciidoc.py')
-rwxr-xr-xsource-builder/sb/asciidoc/tests/testasciidoc.py420
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)