Home | History | Annotate | Download | only in net
      1 #!/usr/bin/env python
      2 # Copyright (C) 2010 Google Inc. All rights reserved.
      3 #
      4 # Redistribution and use in source and binary forms, with or without
      5 # modification, are permitted provided that the following conditions
      6 # are met:
      7 #
      8 # 1.  Redistributions of source code must retain the above copyright
      9 #     notice, this list of conditions and the following disclaimer.
     10 # 2.  Redistributions in binary form must reproduce the above copyright
     11 #     notice, this list of conditions and the following disclaimer in the
     12 #     documentation and/or other materials provided with the distribution.
     13 #
     14 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     15 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     16 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     17 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     18 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     19 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     20 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     21 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     23 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24 
     25 import os
     26 import re
     27 
     28 
     29 class NaiveImageDiffer(object):
     30     def same_image(self, img1, img2):
     31         return img1 == img2
     32 
     33 
     34 class TestOutput(object):
     35     """Represents the output that a single layout test generates when it is run
     36     on a particular platform.
     37     Note that this is the raw output that is produced when the layout test is
     38     run, not the results of the subsequent comparison between that output and
     39     the expected output."""
     40     def __init__(self, platform, output_type, files):
     41         self._output_type = output_type
     42         self._files = files
     43         file = files[0]  # Pick some file to do test name calculation.
     44         self._name = self._extract_test_name(file.name())
     45         self._is_actual = '-actual.' in file.name()
     46 
     47         self._platform = platform or self._extract_platform(file.name())
     48 
     49     def _extract_platform(self, filename):
     50         """Calculates the platform from the name of the file if it isn't known already"""
     51         path = filename.split(os.path.sep)
     52         if 'platform' in path:
     53             return path[path.index('platform') + 1]
     54         return None
     55 
     56     def _extract_test_name(self, filename):
     57         path = filename.split(os.path.sep)
     58         if 'LayoutTests' in path:
     59             path = path[1 + path.index('LayoutTests'):]
     60         if 'layout-test-results' in path:
     61             path = path[1 + path.index('layout-test-results'):]
     62         if 'platform' in path:
     63             path = path[2 + path.index('platform'):]
     64 
     65         filename = path[-1]
     66         filename = re.sub('-expected\..*$', '', filename)
     67         filename = re.sub('-actual\..*$', '', filename)
     68         path[-1] = filename
     69         return os.path.sep.join(path)
     70 
     71     def save_to(self, path):
     72         """Have the files in this TestOutput write themselves to the disk at the specified location."""
     73         for file in self._files:
     74             file.save_to(path)
     75 
     76     def is_actual(self):
     77         """Is this output the actual output of a test? (As opposed to expected output.)"""
     78         return self._is_actual
     79 
     80     def name(self):
     81         """The name of this test (doesn't include extension)"""
     82         return self._name
     83 
     84     def __eq__(self, other):
     85         return (other != None and
     86                 self.name() == other.name() and
     87                 self.type() == other.type() and
     88                 self.platform() == other.platform() and
     89                 self.is_actual() == other.is_actual() and
     90                 self.same_content(other))
     91 
     92     def __hash__(self):
     93         return hash(str(self.name()) + str(self.type()) + str(self.platform()))
     94 
     95     def is_new_baseline_for(self, other):
     96         return (self.name() == other.name() and
     97                 self.type() == other.type() and
     98                 self.platform() == other.platform() and
     99                 self.is_actual() and
    100                 (not other.is_actual()))
    101 
    102     def __str__(self):
    103         actual_str = '[A] ' if self.is_actual() else ''
    104         return "TestOutput[%s/%s] %s%s" % (self._platform, self._output_type, actual_str, self.name())
    105 
    106     def type(self):
    107         return self._output_type
    108 
    109     def platform(self):
    110         return self._platform
    111 
    112     def _path_to_platform(self):
    113         """Returns the path that tests for this platform are stored in."""
    114         if self._platform is None:
    115             return ""
    116         else:
    117             return os.path.join("self._platform", self._platform)
    118 
    119     def _save_expected_result(self, file, path):
    120         path = os.path.join(path, self._path_to_platform())
    121         extension = os.path.splitext(file.name())[1]
    122         filename = self.name() + '-expected' + extension
    123         file.save_to(path, filename)
    124 
    125     def save_expected_results(self, path_to_layout_tests):
    126         """Save the files of this TestOutput to the appropriate directory
    127         inside the LayoutTests directory. Typically this means that these files
    128         will be saved in "LayoutTests/platform/<platform>/, or simply
    129         LayoutTests if the platform is None."""
    130         for file in self._files:
    131             self._save_expected_result(file, path_to_layout_tests)
    132 
    133     def delete(self):
    134         """Deletes the files that comprise this TestOutput from disk. This
    135         fails if the files are virtual files (eg: the files may reside inside a
    136         remote zip file)."""
    137         for file in self._files:
    138             file.delete()
    139 
    140 
    141 class TextTestOutput(TestOutput):
    142     """Represents a text output of a single test on a single platform"""
    143     def __init__(self, platform, text_file):
    144         self._text_file = text_file
    145         TestOutput.__init__(self, platform, 'text', [text_file])
    146 
    147     def same_content(self, other):
    148         return self._text_file.contents() == other._text_file.contents()
    149 
    150     def retarget(self, platform):
    151         return TextTestOutput(platform, self._text_file)
    152 
    153 
    154 class ImageTestOutput(TestOutput):
    155     image_differ = NaiveImageDiffer()
    156     """Represents an image output of a single test on a single platform"""
    157     def __init__(self, platform, image_file, checksum_file):
    158         self._checksum_file = checksum_file
    159         self._image_file = image_file
    160         files = filter(bool, [self._checksum_file, self._image_file])
    161         TestOutput.__init__(self, platform, 'image', files)
    162 
    163     def has_checksum(self):
    164         return self._checksum_file is not None
    165 
    166     def same_content(self, other):
    167         # FIXME This should not assume that checksums are up to date.
    168         if self.has_checksum() and other.has_checksum():
    169             return self._checksum_file.contents() == other._checksum_file.contents()
    170         else:
    171             self_contents = self._image_file.contents()
    172             other_contents = other._image_file.contents()
    173             return ImageTestOutput.image_differ.same_image(self_contents, other_contents)
    174 
    175     def retarget(self, platform):
    176         return ImageTestOutput(platform, self._image_file, self._checksum_file)
    177 
    178     def checksum(self):
    179         return self._checksum_file.contents()
    180