Home | History | Annotate | Download | only in w3c
      1 #!/usr/bin/env python
      2 
      3 # Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
      4 #
      5 # Redistribution and use in source and binary forms, with or without
      6 # modification, are permitted provided that the following conditions
      7 # are met:
      8 #
      9 # 1. Redistributions of source code must retain the above
     10 #    copyright notice, this list of conditions and the following
     11 #    disclaimer.
     12 # 2. Redistributions in binary form must reproduce the above
     13 #    copyright notice, this list of conditions and the following
     14 #    disclaimer in the documentation and/or other materials
     15 #    provided with the distribution.
     16 #
     17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
     18 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     20 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
     21 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
     22 # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     23 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     24 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
     26 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
     27 # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     28 # SUCH DAMAGE.
     29 
     30 import logging
     31 import re
     32 
     33 from webkitpy.common.host import Host
     34 from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup as Parser
     35 
     36 
     37 _log = logging.getLogger(__name__)
     38 
     39 
     40 class TestParser(object):
     41 
     42     def __init__(self, options, filename):
     43         self.options = options
     44         self.filename = filename
     45         self.host = Host()
     46         self.filesystem = self.host.filesystem
     47 
     48         self.test_doc = None
     49         self.ref_doc = None
     50         self.load_file(filename)
     51 
     52     def load_file(self, filename):
     53         if self.filesystem.isfile(filename):
     54             try:
     55                 self.test_doc = Parser(self.filesystem.read_binary_file(filename))
     56             except:
     57                 # FIXME: Figure out what to do if we can't parse the file.
     58                 _log.error("Failed to parse %s", filename)
     59                 self.test_doc is None
     60         else:
     61             if self.filesystem.isdir(filename):
     62                 # FIXME: Figure out what is triggering this and what to do about it.
     63                 _log.error("Trying to load %s, which is a directory", filename)
     64             self.test_doc = None
     65         self.ref_doc = None
     66 
     67     def analyze_test(self, test_contents=None, ref_contents=None):
     68         """ Analyzes a file to determine if it's a test, what type of test, and what reference or support files it requires. Returns all of the test info """
     69 
     70         test_info = None
     71 
     72         if test_contents is None and self.test_doc is None:
     73             return test_info
     74 
     75         if test_contents is not None:
     76             self.test_doc = Parser(test_contents)
     77 
     78         if ref_contents is not None:
     79             self.ref_doc = Parser(ref_contents)
     80 
     81         # First check if it's a reftest
     82 
     83         matches = self.reference_links_of_type('match') + self.reference_links_of_type('mismatch')
     84         if matches:
     85             if len(matches) > 1:
     86                 # FIXME: Is this actually true? We should fix this.
     87                 _log.warning('Multiple references are not supported. Importing the first ref defined in %s',
     88                              self.filesystem.basename(self.filename))
     89 
     90             try:
     91                 ref_file = self.filesystem.join(self.filesystem.dirname(self.filename), matches[0]['href'])
     92             except KeyError as e:
     93                 # FIXME: Figure out what to do w/ invalid test files.
     94                 _log.error('%s has a reference link but is missing the "href"', self.filesystem)
     95                 return None
     96 
     97             if self.ref_doc is None:
     98                 self.ref_doc = self.load_file(ref_file)
     99 
    100             test_info = {'test': self.filename, 'reference': ref_file}
    101 
    102             # If the ref file path is relative, we need to check it for
    103             # relative paths also because when it lands in WebKit, it will be
    104             # moved down into the test dir.
    105             #
    106             # Note: The test files themselves are not checked for support files
    107             # outside their directories as the convention in the CSSWG is to
    108             # put all support files in the same dir or subdir as the test.
    109             #
    110             # All non-test files in the test's directory tree are normally
    111             # copied as part of the import as they are assumed to be required
    112             # support files.
    113             #
    114             # *But*, there is exactly one case in the entire css2.1 suite where
    115             # a test depends on a file that lives in a different directory,
    116             # which depends on another file that lives outside of its
    117             # directory. This code covers that case :)
    118             if matches[0]['href'].startswith('..'):
    119                 support_files = self.support_files(self.ref_doc)
    120                 test_info['refsupport'] = support_files
    121 
    122         elif self.is_jstest():
    123             test_info = {'test': self.filename, 'jstest': True}
    124         elif self.options['all'] is True and not('-ref' in self.filename) and not('reference' in self.filename):
    125             test_info = {'test': self.filename}
    126 
    127         return test_info
    128 
    129     def reference_links_of_type(self, reftest_type):
    130         return self.test_doc.findAll(rel=reftest_type)
    131 
    132     def is_jstest(self):
    133         """Returns whether the file appears to be a jstest, by searching for usage of W3C-style testharness paths."""
    134         return bool(self.test_doc.find(src=re.compile('[\'\"/]?/resources/testharness')))
    135 
    136     def support_files(self, doc):
    137         """ Searches the file for all paths specified in url()'s, href or src attributes."""
    138         support_files = []
    139 
    140         if doc is None:
    141             return support_files
    142 
    143         elements_with_src_attributes = doc.findAll(src=re.compile('.*'))
    144         elements_with_href_attributes = doc.findAll(href=re.compile('.*'))
    145 
    146         url_pattern = re.compile('url\(.*\)')
    147         urls = []
    148         for url in doc.findAll(text=url_pattern):
    149             url = re.search(url_pattern, url)
    150             url = re.sub('url\([\'\"]?', '', url.group(0))
    151             url = re.sub('[\'\"]?\)', '', url)
    152             urls.append(url)
    153 
    154         src_paths = [src_tag['src'] for src_tag in elements_with_src_attributes]
    155         href_paths = [href_tag['href'] for href_tag in elements_with_href_attributes]
    156 
    157         paths = src_paths + href_paths + urls
    158         for path in paths:
    159             if not(path.startswith('http:')) and not(path.startswith('mailto:')):
    160                 support_files.append(path)
    161 
    162         return support_files
    163