2011-02-08 Hayato Ito <hayato@chromium.org>
[WebKit-https.git] / Tools / Scripts / webkitpy / layout_tests / test_types / test_type_base.py
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 are
6 # met:
7 #
8 #     * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 #     * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
13 # distribution.
14 #     * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 """Defines the interface TestTypeBase which other test types inherit from.
31 """
32
33 import cgi
34 import errno
35 import logging
36
37 _log = logging.getLogger("webkitpy.layout_tests.test_types.test_type_base")
38
39
40 # Python bug workaround.  See the wdiff code in WriteOutputFiles for an
41 # explanation.
42 _wdiff_available = True
43
44
45 class TestTypeBase(object):
46
47     # Filename pieces when writing failures to the test results directory.
48     FILENAME_SUFFIX_ACTUAL = "-actual"
49     FILENAME_SUFFIX_EXPECTED = "-expected"
50     FILENAME_SUFFIX_DIFF = "-diff"
51     FILENAME_SUFFIX_WDIFF = "-wdiff.html"
52     FILENAME_SUFFIX_PRETTY_PATCH = "-pretty-diff.html"
53     FILENAME_SUFFIX_COMPARE = "-diff.png"
54
55     def __init__(self, port, root_output_dir):
56         """Initialize a TestTypeBase object.
57
58         Args:
59           port: object implementing port-specific information and methods
60           root_output_dir: The unix style path to the output dir.
61         """
62         self._root_output_dir = root_output_dir
63         self._port = port
64
65     def _make_output_directory(self, filename):
66         """Creates the output directory (if needed) for a given test
67         filename."""
68         fs = self._port._filesystem
69         output_filename = fs.join(self._root_output_dir,
70             self._port.relative_test_filename(filename))
71         fs.maybe_make_directory(fs.dirname(output_filename))
72
73     def output_filename(self, filename, modifier):
74         """Returns a filename inside the output dir that contains modifier.
75
76         For example, if filename is c:/.../fast/dom/foo.html and modifier is
77         "-expected.txt", the return value is
78         c:/cygwin/tmp/layout-test-results/fast/dom/foo-expected.txt
79
80         Args:
81           filename: absolute filename to test file
82           modifier: a string to replace the extension of filename with
83
84         Return:
85           The absolute windows path to the output filename
86         """
87         fs = self._port._filesystem
88         output_filename = fs.join(self._root_output_dir,
89             self._port.relative_test_filename(filename))
90         return fs.splitext(output_filename)[0] + modifier
91
92     def compare_output(self, port, filename, options, actual_driver_output,
93                         expected_driver_output):
94         """Method that compares the output from the test with the
95         expected value.
96
97         This is an abstract method to be implemented by all sub classes.
98
99         Args:
100           port: object implementing port-specific information and methods
101           filename: absolute filename to test file
102           options: command line argument object from optparse
103           actual_driver_output: a DriverOutput object which represents actual test
104               output
105           expected_driver_output: a ExpectedDriverOutput object which represents a
106               expected test output
107
108         Return:
109           a list of TestFailure objects, empty if the test passes
110         """
111         raise NotImplementedError
112
113     def _write_into_file_at_path(self, file_path, contents, encoding):
114         """This method assumes that byte_array is already encoded
115         into the right format."""
116         fs = self._port._filesystem
117         if encoding is None:
118             fs.write_binary_file(file_path, contents)
119             return
120         fs.write_text_file(file_path, contents)
121
122     def write_output_files(self, filename, file_type,
123                            output, expected, encoding,
124                            print_text_diffs=False):
125         """Writes the test output, the expected output and optionally the diff
126         between the two to files in the results directory.
127
128         The full output filename of the actual, for example, will be
129           <filename>-actual<file_type>
130         For instance,
131           my_test-actual.txt
132
133         Args:
134           filename: The test filename
135           file_type: A string describing the test output file type, e.g. ".txt"
136           output: A string containing the test output
137           expected: A string containing the expected test output
138           print_text_diffs: True for text diffs. (FIXME: We should be able to get this from the file type?)
139         """
140         self._make_output_directory(filename)
141         actual_filename = self.output_filename(filename, self.FILENAME_SUFFIX_ACTUAL + file_type)
142         expected_filename = self.output_filename(filename, self.FILENAME_SUFFIX_EXPECTED + file_type)
143         # FIXME: This function is poorly designed.  We should be passing in some sort of
144         # encoding information from the callers.
145         if output:
146             self._write_into_file_at_path(actual_filename, output, encoding)
147         if expected:
148             self._write_into_file_at_path(expected_filename, expected, encoding)
149
150         if not output or not expected:
151             return
152
153         if not print_text_diffs:
154             return
155
156         # Note: We pass encoding=None for all diff writes, as we treat diff
157         # output as binary.  Diff output may contain multiple files in
158         # conflicting encodings.
159         diff = self._port.diff_text(expected, output, expected_filename, actual_filename)
160         diff_filename = self.output_filename(filename, self.FILENAME_SUFFIX_DIFF + file_type)
161         self._write_into_file_at_path(diff_filename, diff, encoding=None)
162
163         # Shell out to wdiff to get colored inline diffs.
164         wdiff = self._port.wdiff_text(expected_filename, actual_filename)
165         wdiff_filename = self.output_filename(filename, self.FILENAME_SUFFIX_WDIFF)
166         self._write_into_file_at_path(wdiff_filename, wdiff, encoding=None)
167
168         # Use WebKit's PrettyPatch.rb to get an HTML diff.
169         pretty_patch = self._port.pretty_patch_text(diff_filename)
170         pretty_patch_filename = self.output_filename(filename, self.FILENAME_SUFFIX_PRETTY_PATCH)
171         self._write_into_file_at_path(pretty_patch_filename, pretty_patch, encoding=None)