• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2
3# Copyright 2010 Daniel James.
4# Distributed under the Boost Software License, Version 1.0. (See accompanying
5# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6
7"""Boostbook tests
8
9Usage: python build_docs.py [--generate-gold]
10"""
11
12import difflib, getopt, os, re, sys
13import lxml.ElementInclude
14from lxml import etree
15from collections import defaultdict
16
17# Globals
18
19def usage_and_exit():
20    print __doc__
21    sys.exit(2)
22
23def main(argv):
24    script_directory = os.path.dirname(sys.argv[0])
25    boostbook_directory = os.path.join(script_directory, "../../xsl")
26
27    try:
28        opts, args = getopt.getopt(argv, "",
29            ["generate-gold"])
30        if(len(args)): usage_and_exit()
31    except getopt.GetoptError:
32        usage_and_exit()
33
34    generate_gold = False
35
36    for opt, arg in opts:
37        if opt == '--generate-gold':
38            generate_gold = True
39
40    # Walk the test directory
41
42    parser = etree.XMLParser()
43
44    try:
45        boostbook_xsl = etree.XSLT(
46            etree.parse(os.path.join(boostbook_directory, "docbook.xsl"), parser)
47        )
48    except lxml.etree.XMLSyntaxError, error:
49        print "Error parsing boostbook xsl:"
50        print error
51        sys.exit(1)
52
53    for root, dirs, files in os.walk(os.path.join(script_directory, 'tests')):
54        success = True
55        for filename in files:
56            (base, ext) = os.path.splitext(filename)
57            if (ext == '.xml'):
58                for consistent_ids in [False, True]:
59                    if not consistent_ids:
60                        gold_ext = ".gold"
61                    else:
62                        gold_ext = ".gold2"
63                    src_path = os.path.join(root, filename)
64                    gold_path = os.path.join(root, base + gold_ext)
65                    try:
66                        if consistent_ids:
67                            doc_text = run_boostbook_consistent_ids(parser, boostbook_xsl, src_path)
68                        else:
69                            doc_text = run_boostbook(parser, boostbook_xsl, src_path)
70                    except:
71                        # TODO: Need better error reporting here:
72                        print "Error running boostbook for " + src_path
73                        success = False
74                        continue
75
76                    if (generate_gold):
77                        file = open(gold_path, 'w')
78                        try:
79                            file.write(doc_text)
80                        finally: file.close()
81                    else:
82                        file = open(gold_path, 'r')
83                        try:
84                            gold_text = file.read()
85                        finally:
86                            file.close()
87                        if not compare_xml(src_path, doc_text, gold_text):
88                            success = False
89    if not success:
90        sys.exit(1)
91
92
93def run_boostbook(parser, boostbook_xsl, file):
94    doc = boostbook_xsl(etree.parse(file, parser))
95    normalize_boostbook_ids(doc)
96    return etree.tostring(doc)
97
98def run_boostbook_consistent_ids(parser, boostbook_xsl, file):
99    doc = boostbook_xsl(etree.parse(file, parser), **{
100        'generate.consistent.ids': '1'
101    })
102    return etree.tostring(doc)
103
104def normalize_boostbook_ids(doc):
105    ids = {}
106    id_bases = defaultdict(int)
107
108    for node in doc.xpath("//*[starts-with(@id, 'id') or contains(@id, '_id')]"):
109        id = node.get('id')
110
111        if(id in ids):
112            print 'Duplicate id: ' + id
113
114        match = re.match("(.+_id|id)([-mp]?[\d_]+)((?:-bb)?)", id)
115        if(match):
116            # Truncate id name, as it sometimes has different lengths...
117            match2 = re.match("(.*?)([^.]*?)(_?id)", match.group(1))
118            base = match2.group(1) + match2.group(2)[:7] + match2.group(3)
119            count = id_bases[base] + 1
120            id_bases[base] = count
121            ids[id] = base + str(count) + match.group(3)
122
123    for node in doc.xpath("//*[@linkend or @id]"):
124        x = node.get('linkend')
125        if(x in ids): node.set('linkend', ids[x])
126        x = node.get('id')
127        if(x in ids): node.set('id', ids[x])
128
129def compare_xml(file, doc_text, gold_text):
130    # Had hoped to use xmldiff but it turned out to be a pain to install.
131    # So instead just do a text diff.
132
133    if (doc_text != gold_text):
134        print "Error: " + file
135        print
136        sys.stdout.writelines(
137            difflib.unified_diff(
138                gold_text.splitlines(True),
139                doc_text.splitlines(True)
140            )
141        )
142        print
143        print
144        return False
145    else:
146        return True
147
148if __name__ == "__main__":
149    main(sys.argv[1:])
150