summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/tools/w3ctestlib/Indexer.py
blob: f7e2eb2a17318298bf1a979800c11229e73b61db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#!/usr/bin/python
# CSS Test Suite Manipulation Library
# Initial code by fantasai, joint copyright 2010 W3C and Microsoft
# Licensed under BSD 3-Clause: <http://www.w3.org/Consortium/Legal/2008/03-bsd-license>

# Define contains vmethod for Template Toolkit
from template.stash import list_op
@list_op("contains")
def list_contains(l, x):
  return x in l

import sys
import re
import os
import codecs
from os.path import join, exists, abspath
from template import Template
import w3ctestlib
from Utils import listfiles, escapeToNamedASCII
from OutputFormats import ExtensionMap
import shutil

class Section:
  def __init__(self, uri, title, numstr):
    self.uri = uri
    self.title = title
    self.numstr = numstr
    self.tests = []
  def __cmp__(self, other):
    return cmp(self.natsortkey(), other.natsortkey())
  def chapterNum(self):
    return self.numstr.partition('.')[0]
  def natsortkey(self):
    chunks = self.numstr.partition('.#')[0].split('.')
    for index in range(len(chunks)):
      if chunks[index].isdigit():
        # wrap in tuple with '0' to explicitly specify numbers come first
        chunks[index] = (0, int(chunks[index]))
      else:
        chunks[index] = (1, chunks[index])
    return (chunks, self.numstr)

class Indexer:

  def __init__(self, suite, sections, suites, flags, splitChapter=False, templatePathList=None,
               extraData=None, overviewTmplNames=None, overviewCopyExts=('.css', 'htaccess')):
    """Initialize indexer with TestSuite `suite` toc data file
       `tocDataPath` and additional template paths in list `templatePathList`.

       The toc data file should be list of tab-separated records, one
       per line, of each spec section's uri, number/letter, and title.
       `splitChapter` selects a single page index if False, chapter 
       indicies if True.
       `extraData` can be a dictionary whose data gets passed to the templates.
       `overviewCopyExts` lists file extensions that should be found
       and copied from the template path into the main build directory.
       The default value is ['.css', 'htaccess'].
       `overviewTemplateNames` lists template names that should be
       processed from the template path into the main build directory.
       The '.tmpl' extension, if any, is stripped from the output filename.
       The default value is ['index.htm.tmpl', 'index.xht.tmpl', 'testinfo.data.tmpl']
    """
    self.suite        = suite
    self.splitChapter = splitChapter
    self.extraData    = extraData
    self.overviewCopyExtPat = re.compile('.*(%s)$' % '|'.join(overviewCopyExts))
    self.overviewTmplNames = overviewTmplNames if overviewTmplNames is not None \
      else ['index.htm.tmpl', 'index.xht.tmpl', 'testinfo.data.tmpl',
            'implementation-report-TEMPLATE.data.tmpl']

    # Initialize template engine
    self.templatePath = [join(w3ctestlib.__path__[0], 'templates')]
    if templatePathList:
      self.templatePath.extend(templatePathList)
    self.templatePath = [abspath(path) for path in self.templatePath]
    self.tt = Template({
       'INCLUDE_PATH': self.templatePath,
       'ENCODING'    : 'utf-8',
       'PRE_CHOMP'   : 1,
       'POST_CHOMP'  : 0,
    })

    # Load toc data
    self.sections = {}
    for uri, numstr, title in sections:
      uri = intern(uri.encode('utf-8'))
      uriKey = intern(self._normalizeScheme(uri))
      numstr = escapeToNamedASCII(numstr)
      title = escapeToNamedASCII(title) if title else None
      self.sections[uriKey] = Section(uri, title, numstr)
    
    self.suites = suites
    self.flags = flags

    # Initialize storage
    self.errors = {}
    self.contributors = {}
    self.alltests = []

  def _normalizeScheme(self, uri):
    if (uri and uri.startswith('http:')):
      return 'https:' + uri[5:]
    return uri

  def indexGroup(self, group):
    for test in group.iterTests():
      data = test.getMetadata()
      if data: # Shallow copy for template output
        data = dict(data)
        data['file'] = '/'.join((group.name, test.relpath)) \
                       if group.name else test.relpath
        if (data['scripttest']):
            data['flags'].append(intern('script'))
        self.alltests.append(data)
        for uri in data['links']:
          uri = self._normalizeScheme(uri)
          uri = uri.replace(self._normalizeScheme(self.suite.draftroot), self._normalizeScheme(self.suite.specroot))
          if self.sections.has_key(uri):
            testlist = self.sections[uri].tests.append(data)
        for credit in data['credits']:
          self.contributors[credit[0]] = credit[1]
      else:
        self.errors[test.sourcepath] = test.errors

  def __writeTemplate(self, template, data, outfile):
    o = self.tt.process(template, data)
    with open(outfile, 'w') as f:
      f.write(o.encode('utf-8'))

  def writeOverview(self, destDir, errorOut=sys.stderr, addTests=[]):
    """Write format-agnostic pages such as test suite overview pages,
       test data files, and error reports.

       Indexed errors are reported to errorOut, which must be either
       an output handle such as sys.stderr, a tuple of
       (template filename string, output filename string)
       or None to suppress error output.

       `addTests` is a list of additional test paths, relative to the
       overview root; it is intended for indexing raw tests
    """

    # Set common values
    data = self.extraData.copy()
    data['suitetitle']   = self.suite.title
    data['suite']        = self.suite.name
    data['specroot']     = self.suite.specroot
    data['draftroot']    = self.suite.draftroot
    data['contributors'] = self.contributors
    data['tests']        = self.alltests
    data['extmap']       = ExtensionMap({'.xht':'', '.html':'', '.htm':'', '.svg':''})
    data['formats']      = self.suite.formats
    data['addtests']     = addTests
    data['suites']       = self.suites
    data['flagInfo']     = self.flags
    data['formatInfo']   = { 'html4': { 'report': True, 'path': 'html4', 'ext': 'htm', 'filter': 'nonHTML'},
                             'html5': { 'report': True, 'path': 'html', 'ext': 'htm', 'filter': 'nonHTML' },
                             'xhtml1': { 'report': True, 'path': 'xhtml1', 'ext': 'xht', 'filter': 'HTMLonly' },
                             'xhtml1print': { 'report': False, 'path': 'xhtml1print', 'ext': 'xht', 'filter': 'HTMLonly' },
                             'svg': { 'report': True, 'path': 'svg', 'ext': 'svg', 'filter': 'HTMLonly' }
                           }

    # Copy simple copy files
    for tmplDir in reversed(self.templatePath):
      files = listfiles(tmplDir)
      for file in files:
        if self.overviewCopyExtPat.match(file):
          shutil.copy(join(tmplDir, file), join(destDir, file))

    # Generate indexes
    for tmpl in self.overviewTmplNames:
      out = tmpl[0:-5] if tmpl.endswith('.tmpl') else tmpl
      self.__writeTemplate(tmpl, data, join(destDir, out))

    # Report errors
    if (self.errors):
        if type(errorOut) is type(('tmpl','out')):
            data['errors'] = errors
            self.__writeTemplate(errorOut[0], data, join(destDir, errorOut[1]))
        else:
            sys.stdout.flush()
            for errorLocation in self.errors:
                print >> errorOut, "Error in %s: %s" % \
                               (errorLocation, ' '.join([str(error) for error in self.errors[errorLocation]]))

  def writeIndex(self, format):
    """Write indices into test suite build output through format `format`.
    """

    # Set common values
    data = self.extraData.copy()
    data['suitetitle'] = self.suite.title
    data['suite']      = self.suite.name
    data['specroot']   = self.suite.specroot
    data['draftroot']  = self.suite.draftroot
    
    data['indexext']   = format.indexExt
    data['isXML']      = format.indexExt.startswith('.x')
    data['formatdir']  = format.formatDirName
    data['extmap']     = format.extMap
    data['tests']      = self.alltests
    data['suites']     = self.suites
    data['flagInfo']   = self.flags

    # Generate indices:

    # Reftest indices
    self.__writeTemplate('reftest-toc.tmpl', data,
                         format.dest('reftest-toc%s' % format.indexExt))
    self.__writeTemplate('reftest.tmpl', data,
                         format.dest('reftest.list'))

    # Table of Contents
    sectionlist = sorted(self.sections.values())
    if self.splitChapter:
      # Split sectionlist into chapters
      chapters = []
      lastChapNum = '$' # some nonmatching initial char
      chap = None
      for section in sectionlist:
        if (section.title and (section.chapterNum() != lastChapNum)):
          lastChapNum = section.chapterNum()
          chap = section
          chap.sections = []
          chap.testcount = 0
          chap.testnames = set()
          chapters.append(chap)
        chap.testnames.update([test['name'] for test in section.tests])
        chap.testcount = len(chap.testnames)
        chap.sections.append(section)

      # Generate main toc
      data['chapters'] = chapters
      self.__writeTemplate('chapter-toc.tmpl', data,
                           format.dest('toc%s' % format.indexExt))
      del data['chapters']

      # Generate chapter tocs
      for chap in chapters:
        data['chaptertitle'] = chap.title
        data['testcount']    = chap.testcount
        data['sections']     = chap.sections
        self.__writeTemplate('test-toc.tmpl', data, format.dest('chapter-%s%s' \
                             % (chap.numstr, format.indexExt)))

    else: # not splitChapter
      data['chapters'] = sectionlist
      self.__writeTemplate('test-toc.tmpl', data,
                           format.dest('toc%s' % format.indexExt))
      del data['chapters']