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