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