150 lines
4.6 KiB
Python
Executable File
150 lines
4.6 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
# Copyright 2010 Daniel James.
|
|
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
"""Boostbook tests
|
|
|
|
Usage: python build_docs.py [--generate-gold]
|
|
"""
|
|
|
|
import difflib, getopt, os, re, sys
|
|
import lxml.ElementInclude
|
|
from lxml import etree
|
|
from collections import defaultdict
|
|
|
|
# Globals
|
|
|
|
def usage_and_exit():
|
|
print __doc__
|
|
sys.exit(2)
|
|
|
|
def main(argv):
|
|
script_directory = os.path.dirname(sys.argv[0])
|
|
boostbook_directory = os.path.join(script_directory, "../../xsl")
|
|
|
|
try:
|
|
opts, args = getopt.getopt(argv, "",
|
|
["generate-gold"])
|
|
if(len(args)): usage_and_exit()
|
|
except getopt.GetoptError:
|
|
usage_and_exit()
|
|
|
|
generate_gold = False
|
|
|
|
for opt, arg in opts:
|
|
if opt == '--generate-gold':
|
|
generate_gold = True
|
|
|
|
# Walk the test directory
|
|
|
|
parser = etree.XMLParser()
|
|
|
|
try:
|
|
boostbook_xsl = etree.XSLT(
|
|
etree.parse(os.path.join(boostbook_directory, "docbook.xsl"), parser)
|
|
)
|
|
except lxml.etree.XMLSyntaxError, error:
|
|
print "Error parsing boostbook xsl:"
|
|
print error
|
|
sys.exit(1)
|
|
|
|
for root, dirs, files in os.walk(os.path.join(script_directory, 'tests')):
|
|
success = True
|
|
for filename in files:
|
|
(base, ext) = os.path.splitext(filename)
|
|
if (ext == '.xml'):
|
|
for consistent_ids in [False, True]:
|
|
if not consistent_ids:
|
|
gold_ext = ".gold"
|
|
else:
|
|
gold_ext = ".gold2"
|
|
src_path = os.path.join(root, filename)
|
|
gold_path = os.path.join(root, base + gold_ext)
|
|
try:
|
|
if consistent_ids:
|
|
doc_text = run_boostbook_consistent_ids(parser, boostbook_xsl, src_path)
|
|
else:
|
|
doc_text = run_boostbook(parser, boostbook_xsl, src_path)
|
|
except:
|
|
# TODO: Need better error reporting here:
|
|
print "Error running boostbook for " + src_path
|
|
success = False
|
|
continue
|
|
|
|
if (generate_gold):
|
|
file = open(gold_path, 'w')
|
|
try:
|
|
file.write(doc_text)
|
|
finally: file.close()
|
|
else:
|
|
file = open(gold_path, 'r')
|
|
try:
|
|
gold_text = file.read()
|
|
finally:
|
|
file.close()
|
|
if not compare_xml(src_path, doc_text, gold_text):
|
|
success = False
|
|
if not success:
|
|
sys.exit(1)
|
|
|
|
|
|
def run_boostbook(parser, boostbook_xsl, file):
|
|
doc = boostbook_xsl(etree.parse(file, parser))
|
|
normalize_boostbook_ids(doc)
|
|
return etree.tostring(doc)
|
|
|
|
def run_boostbook_consistent_ids(parser, boostbook_xsl, file):
|
|
doc = boostbook_xsl(etree.parse(file, parser), **{
|
|
'generate.consistent.ids': '1'
|
|
})
|
|
return etree.tostring(doc)
|
|
|
|
def normalize_boostbook_ids(doc):
|
|
ids = {}
|
|
id_bases = defaultdict(int)
|
|
|
|
for node in doc.xpath("//*[starts-with(@id, 'id') or contains(@id, '_id')]"):
|
|
id = node.get('id')
|
|
|
|
if(id in ids):
|
|
print 'Duplicate id: ' + id
|
|
|
|
match = re.match("(.+_id|id)([-mp]?[\d_]+)((?:-bb)?)", id)
|
|
if(match):
|
|
# Truncate id name, as it sometimes has different lengths...
|
|
match2 = re.match("(.*?)([^.]*?)(_?id)", match.group(1))
|
|
base = match2.group(1) + match2.group(2)[:7] + match2.group(3)
|
|
count = id_bases[base] + 1
|
|
id_bases[base] = count
|
|
ids[id] = base + str(count) + match.group(3)
|
|
|
|
for node in doc.xpath("//*[@linkend or @id]"):
|
|
x = node.get('linkend')
|
|
if(x in ids): node.set('linkend', ids[x])
|
|
x = node.get('id')
|
|
if(x in ids): node.set('id', ids[x])
|
|
|
|
def compare_xml(file, doc_text, gold_text):
|
|
# Had hoped to use xmldiff but it turned out to be a pain to install.
|
|
# So instead just do a text diff.
|
|
|
|
if (doc_text != gold_text):
|
|
print "Error: " + file
|
|
print
|
|
sys.stdout.writelines(
|
|
difflib.unified_diff(
|
|
gold_text.splitlines(True),
|
|
doc_text.splitlines(True)
|
|
)
|
|
)
|
|
print
|
|
print
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
if __name__ == "__main__":
|
|
main(sys.argv[1:])
|