1#!/usr/bin/python 2# CSS Test Source Manipulation Library 3# Initial code by fantasai, joint copyright 2010 W3C and Microsoft 4# Licensed under BSD 3-Clause: <http://www.w3.org/Consortium/Legal/2008/03-bsd-license> 5 6import re 7import os 8from os.path import join, exists, splitext, dirname, basename 9from Sources import XHTMLSource, HTMLSource, SVGSource, SourceTree 10 11class ExtensionMap: 12 """ Given a file extension mapping (e.g. {'.xht' : '.htm'}), provides 13 a translate function for paths. 14 """ 15 def __init__(self, extMap): 16 self.extMap = extMap 17 18 def translate(self, path): 19 for ext in self.extMap: 20 if path.endswith(ext): 21 return splitext(path)[0] + self.extMap[ext] 22 return path 23 24class BasicFormat: 25 """Base class. A Format manages all the conversions and location 26 transformations (e.g. subdirectory for all tests in that format) 27 associated with a test suite format. 28 29 The base class implementation performs no conversions or 30 format-specific location transformations.""" 31 formatDirName = None 32 indexExt = '.htm' 33 convert = True # XXX hack to supress format conversion in support dirs, need to clean up output code to make this cleaner 34 35 def __init__(self, destroot, sourceTree, extMap=None, outputDirName=None): 36 """Creates format root of the output tree. `destroot` is the root path 37 of the output tree. 38 39 extMap provides a file extension mapping, e.g. {'.xht' : '.htm'} 40 """ 41 self.root = join(destroot, outputDirName) if outputDirName else destroot 42 self.sourceTree = sourceTree 43 self.formatDirName = outputDirName 44 if not exists(self.root): 45 os.makedirs(self.root) 46 self.extMap = ExtensionMap(extMap or {}) 47 self.subdir = None 48 49 def setSubDir(self, name=None): 50 """Sets format to write into group subdirectory `name`. 51 """ 52 self.subdir = name 53 54 def destDir(self): 55 return join(self.root, self.subdir) if self.subdir else self.root 56 57 def dest(self, relpath): 58 """Returns final destination of relpath in this format and ensures that the 59 parent directory exists.""" 60 # Translate path 61 if (self.convert): 62 relpath = self.extMap.translate(relpath) 63 if (self.sourceTree.isReferenceAnywhere(relpath)): 64 relpath = join('reference', basename(relpath)) 65 # XXX when forcing support files into support path, need to account for support/support 66 dest = join(self.root, self.subdir, relpath) if self.subdir \ 67 else join(self.root, relpath) 68 # Ensure parent 69 parent = dirname(dest) 70 if not exists(parent): 71 os.makedirs(parent) 72 73 return dest 74 75 def write(self, source): 76 """Write FileSource to destination, following all necessary 77 conversion methods.""" 78 source.write(self, source) 79 80 testTransform = False 81 # def testTransform(self, outputString, source) if needed 82 83class XHTMLFormat(BasicFormat): 84 """Base class for XHTML test suite format. Builds into 'xhtml1' subfolder 85 of root. 86 """ 87 indexExt = '.xht' 88 89 def __init__(self, destroot, sourceTree, extMap=None, outputDirName='xhtml1'): 90 if not extMap: 91 extMap = {'.htm' : '.xht', '.html' : '.xht', '.xhtml' : '.xht' } 92 BasicFormat.__init__(self, destroot, sourceTree, extMap, outputDirName) 93 94 def write(self, source): 95 # skip HTMLonly tests 96 if hasattr(source, 'hasFlag') and source.hasFlag('HTMLonly'): 97 return 98 if isinstance(source, HTMLSource) and self.convert: 99 source.write(self, source.serializeXHTML()) 100 else: 101 source.write(self) 102 103class HTMLFormat(BasicFormat): 104 """Base class for HTML test suite format. Builds into 'html4' subfolder 105 of root. 106 """ 107 108 def __init__(self, destroot, sourceTree, extMap=None, outputDirName='html4'): 109 if not extMap: 110 extMap = {'.xht' : '.htm', '.xhtml' : '.htm', '.html' : '.htm' } 111 BasicFormat.__init__(self, destroot, sourceTree, extMap, outputDirName) 112 113 def write(self, source): 114 # skip nonHTML tests 115 if hasattr(source, 'hasFlag') and source.hasFlag('nonHTML'): 116 return 117 if isinstance(source, XHTMLSource) and self.convert: 118 source.write(self, source.serializeHTML()) 119 else: 120 source.write(self) 121 122 123class HTML5Format(HTMLFormat): 124 def __init__(self, destroot, sourceTree, extMap=None, outputDirName='html'): 125 HTMLFormat.__init__(self, destroot, sourceTree, extMap, outputDirName) 126 127 def write(self, source): 128 # skip nonHTML tests 129 if hasattr(source, 'hasFlag') and source.hasFlag('nonHTML'): 130 return 131 if isinstance(source, XHTMLSource) and self.convert: 132 source.write(self, source.serializeHTML()) 133 else: 134 source.write(self) 135 136 137class SVGFormat(BasicFormat): 138 def __init__(self, destroot, sourceTree, extMap=None, outputDirName='svg'): 139 if not extMap: 140 extMap = {'.svg' : '.svg' } 141 BasicFormat.__init__(self, destroot, sourceTree, extMap, outputDirName) 142 143 def write(self, source): 144 # skip non SVG tests 145 if isinstance(source, SVGSource): 146 source.write(self) 147 148 149class XHTMLPrintFormat(XHTMLFormat): 150 """Base class for XHTML Print test suite format. Builds into 'xhtml1print' 151 subfolder of root. 152 """ 153 154 def __init__(self, destroot, sourceTree, testSuiteName, extMap=None, outputDirName='xhtml1print'): 155 if not extMap: 156 extMap = {'.htm' : '.xht', '.html' : '.xht', '.xhtml' : '.xht' } 157 BasicFormat.__init__(self, destroot, sourceTree, extMap, outputDirName) 158 self.testSuiteName = testSuiteName 159 160 def write(self, source): 161 if (isinstance(source, XHTMLSource)): 162 if not source.hasFlag('HTMLonly'): 163 source.write(self, self.testTransform(source)) 164 else: 165 XHTMLFormat.write(self, source) 166 167 def testTransform(self, source): 168 assert isinstance(source, XHTMLSource) 169 output = source.serializeXHTML('xhtml10') 170 171 headermeta = {'suitename' : self.testSuiteName, 172 'testid' : source.name(), 173 'margin' : '', 174 } 175 if re.search('@page\s*{[^}]*@', output): 176 # Don't use headers and footers when page tests margin boxes 177 output = re.sub('(<body[^>]*>)', 178 '\1\n' + self.__htmlstart % headermeta, 179 output); 180 output = re.sub('(</body[^>]*>)', 181 '\1\n' + self.__htmlend % headermeta, 182 output); 183 else: 184 # add margin rule only when @page statement does not exist 185 if not re.search('@page', output): 186 headermeta['margin'] = self.__margin 187 output = re.sub('</title>', 188 '</title>\n <style type="text/css">%s</style>' % \ 189 (self.__css % headermeta), 190 output); 191 return output; 192 193 # template bits 194 __margin = 'margin: 7%;'; 195 __font = 'font: italic 8pt sans-serif; color: gray;' 196 __css = """ 197 @page { %s 198 %%(margin)s 199 counter-increment: page; 200 @top-left { content: "%%(suitename)s"; } 201 @top-right { content: "Test %%(testid)s"; } 202 @bottom-right { content: counter(page); } 203 } 204""" % __font 205 __htmlstart = '<p style="%s">Start of %%(suitename)s %%(testid)s.</p>' % __font 206 __htmlend = '<p style="%s">End of %%(suitename)s %%(testid)s.</p>' % __font 207 208