Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright (C) 2014 Google Inc. All rights reserved. | |
| 2 # | |
| 3 # Redistribution and use in source and binary forms, with or without | |
| 4 # modification, are permitted provided that the following conditions are | |
| 5 # met: | |
| 6 # | |
| 7 # * Redistributions of source code must retain the above copyright | |
| 8 # notice, this list of conditions and the following disclaimer. | |
| 9 # * Redistributions in binary form must reproduce the above | |
| 10 # copyright notice, this list of conditions and the following disclaimer | |
| 11 # in the documentation and/or other materials provided with the | |
| 12 # distribution. | |
| 13 # * Neither the Google name nor the names of its | |
| 14 # contributors may be used to endorse or promote products derived from | |
| 15 # this software without specific prior written permission. | |
| 16 # | |
| 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 28 | |
| 29 from webkitpy.layout_tests.port import driver | |
| 30 import time | |
| 31 | |
| 32 | |
| 33 class BrowserTestDriver(driver.Driver): | |
| 34 """Object for running print preview test(s) using browser_tests.""" | |
| 35 def __init__(self, port, worker_number, pixel_tests, no_timeout=False): | |
| 36 """Invokes the constructor of driver.Driver.""" | |
| 37 super(BrowserTestDriver, self).__init__(port, worker_number, pixel_tests , no_timeout) | |
| 38 | |
| 39 def start(self, pixel_tests, per_test_args, deadline): | |
| 40 """Same as Driver.start() however, it has an extra step. It waits for | |
| 41 a path to a file to be used for stdin to be printed by the browser test. | |
| 42 If a path is found by the deadline test test will open the file and | |
| 43 assign it to the stdin of the process that is owned by this driver's | |
| 44 server process. | |
| 45 """ | |
| 46 # FIXME(ivandavid): Need to handle case where the layout test doesn't | |
| 47 # get a file name. | |
| 48 new_cmd_line = self.cmd_line(pixel_tests, per_test_args) | |
| 49 if not self._server_process or new_cmd_line != self._current_cmd_line: | |
| 50 self._start(pixel_tests, per_test_args) | |
| 51 self._run_post_start_tasks() | |
| 52 path, found = self._read_stdin_path(time.time() + int(deadline) / 10 00.0) | |
| 53 if found: | |
| 54 self._server_process._proc.stdin = open(path, 'w', 0) | |
|
Dirk Pranke
2014/07/10 23:37:13
I am worried that this approach won't actually wor
ivandavid
2014/07/16 21:29:01
I changed it to 'wb' so it can work on windows. Wh
| |
| 55 | |
| 56 def cmd_line(self, pixel_tests, per_test_args): | |
| 57 """Command line arguments to run the browser test.""" | |
| 58 cmd = self._command_wrapper(self._port.get_option('wrapper')) | |
| 59 cmd.append(self._port._path_to_driver()) | |
| 60 cmd.append('--gtest_filter=PrintPreviewPdfGeneratedBrowserTest.MANUAL_Du mmyTest') | |
| 61 cmd.append('--run-manual') | |
| 62 cmd.append('--single_process') | |
| 63 cmd.extend(per_test_args) | |
| 64 return cmd | |
| 65 | |
| 66 def run_test(self, driver_input, stop_when_done): | |
| 67 """Run a single test and return the results. | |
| 68 | |
| 69 Note that it is okay if a test times out or crashes and leaves | |
| 70 the driver in an indeterminate state. The upper layers of the program | |
| 71 are responsible for cleaning up and ensuring things are okay. | |
| 72 | |
| 73 Returns a DriverOutput object. | |
| 74 | |
| 75 This function was overriden because the new start function had to be | |
| 76 called. | |
|
Dirk Pranke
2014/07/10 23:37:13
I would modify Driver.start() to take an (optional
ivandavid
2014/07/16 21:29:01
Done.
| |
| 77 """ | |
| 78 start_time = time.time() | |
| 79 self.start(driver_input.should_run_pixel_test, driver_input.args, int(dr iver_input.timeout)) | |
| 80 test_begin_time = time.time() | |
| 81 self.error_from_test = str() | |
| 82 self.err_seen_eof = False | |
| 83 | |
| 84 command = self._command_from_driver_input(driver_input) | |
| 85 deadline = test_begin_time + int(driver_input.timeout) / 1000.0 | |
| 86 | |
| 87 self._server_process.write(command) | |
| 88 text, audio = self._read_first_block(deadline) # First block is either text or audio | |
| 89 image, actual_image_hash = self._read_optional_image_block(deadline) # The second (optional) block is image data. | |
| 90 | |
| 91 crashed = self.has_crashed() | |
| 92 timed_out = self._server_process.timed_out | |
| 93 pid = self._server_process.pid() | |
| 94 leaked = self._leaked | |
| 95 | |
| 96 if stop_when_done or crashed or timed_out or leaked: | |
| 97 # We call stop() even if we crashed or timed out in order to get any remaining stdout/stderr output. | |
| 98 # In the timeout case, we kill the hung process as well. | |
| 99 out, err = self._server_process.stop(self._port.driver_stop_timeout( ) if stop_when_done else 0.0) | |
| 100 if out: | |
| 101 text += out | |
| 102 if err: | |
| 103 self.error_from_test += err | |
| 104 self._server_process = None | |
| 105 | |
| 106 crash_log = None | |
| 107 if crashed: | |
| 108 self.error_from_test, crash_log = self._get_crash_log(text, self.err or_from_test, newer_than=start_time) | |
| 109 | |
| 110 # If we don't find a crash log use a placeholder error message inste ad. | |
| 111 if not crash_log: | |
| 112 pid_str = str(self._crashed_pid) if self._crashed_pid else "unkn own pid" | |
| 113 crash_log = 'No crash log found for %s:%s.\n' % (self._crashed_p rocess_name, pid_str) | |
| 114 # If we were unresponsive append a message informing there may n ot have been a crash. | |
| 115 if self._subprocess_was_unresponsive: | |
| 116 crash_log += 'Process failed to become responsive before tim ing out.\n' | |
| 117 | |
| 118 # Print stdout and stderr to the placeholder crash log; we want as much context as possible. | |
| 119 if self.error_from_test: | |
| 120 crash_log += '\nstdout:\n%s\nstderr:\n%s\n' % (text, self.er ror_from_test) | |
| 121 return driver.DriverOutput(text, image, actual_image_hash, audio, | |
| 122 crash=crashed, test_time=time.time() - test_begin_time, measurements =self._measurements, | |
| 123 timeout=timed_out, error=self.error_from_test, | |
| 124 crashed_process_name=self._crashed_process_name, | |
| 125 crashed_pid=self._crashed_pid, crash_log=crash_log, | |
| 126 leak=leaked, leak_log=self._leak_log, | |
| 127 pid=pid) | |
| 128 | |
| 129 def _read_stdin_path(self, deadline): | |
| 130 """Reads the file path to be used for stdin.""" | |
| 131 block = self._read_block(deadline) | |
| 132 if block.stdin_path: | |
| 133 return (block.stdin_path, True) | |
| 134 return (None, False) | |
| 135 | |
| 136 def _process_stdout_line(self, block, line): | |
| 137 """Additional header read to look for the stdin path.""" | |
| 138 if (self._read_header(block, line, 'StdinPath: ', 'stdin_path')): | |
| 139 return | |
| 140 super(BrowserTestDriver, self)._process_stdout_line(block, line) | |
| 141 | |
| 142 def _read_block(self, deadline, wait_for_stderr_eof=False): | |
| 143 """Same as Driver._read_block() however rather than using ContentBlock, | |
| 144 ContentBlockStdin is used, as ContentBlock does not have the member | |
| 145 variable to store the stdin path. | |
| 146 """ | |
| 147 block = ContentBlockStdin() | |
|
Dirk Pranke
2014/07/10 23:37:13
If this is the only difference from Driver._read_b
ivandavid
2014/07/16 21:29:01
Done.
| |
| 148 out_seen_eof = False | |
| 149 | |
| 150 while not self.has_crashed(): | |
| 151 if out_seen_eof and (self.err_seen_eof or not wait_for_stderr_eof): | |
| 152 break | |
| 153 | |
| 154 if self.err_seen_eof: | |
| 155 out_line = self._server_process.read_stdout_line(deadline) | |
| 156 err_line = None | |
| 157 elif out_seen_eof: | |
| 158 out_line = None | |
| 159 err_line = self._server_process.read_stderr_line(deadline) | |
| 160 else: | |
| 161 out_line, err_line = self._server_process.read_either_stdout_or_ stderr_line(deadline) | |
| 162 if self._server_process.timed_out or self.has_crashed(): | |
| 163 break | |
| 164 | |
| 165 if out_line: | |
| 166 assert not out_seen_eof | |
| 167 out_line, out_seen_eof = self._strip_eof(out_line) | |
| 168 if err_line: | |
| 169 assert not self.err_seen_eof | |
| 170 err_line, self.err_seen_eof = self._strip_eof(err_line) | |
| 171 | |
| 172 if out_line: | |
| 173 if out_line[-1] != "\n": | |
| 174 driver._log.error("Last character read from DRT stdout line was not a newline! This indicates either a NRWT or DRT bug.") | |
| 175 content_length_before_header_check = block._content_length | |
| 176 self._process_stdout_line(block, out_line) | |
| 177 # FIXME: Unlike HTTP, DRT dumps the content right after printing a Content-Length header. | |
| 178 # Don't wait until we're done with headers, just read the binary blob right now. | |
| 179 if content_length_before_header_check != block._content_length: | |
| 180 if block._content_length > 0: | |
| 181 block.content = self._server_process.read_stdout(deadlin e, block._content_length) | |
| 182 else: | |
| 183 driver._log.error("Received content of type %s with Cont ent-Length of 0! This indicates a bug in %s.", | |
| 184 block.content_type, self._server_process.name ()) | |
| 185 | |
| 186 if err_line: | |
| 187 if self._check_for_driver_crash(err_line): | |
| 188 break | |
| 189 if self._check_for_leak(err_line): | |
| 190 break | |
| 191 self.error_from_test += err_line | |
| 192 | |
| 193 block.decode_content() | |
| 194 return block | |
| 195 | |
| 196 | |
| 197 class ContentBlockStdin(driver.ContentBlock): | |
| 198 """Extends ContentBlock by adding another member variable that stores a path | |
| 199 for stdin. | |
| 200 """ | |
| 201 def __init__(self): | |
| 202 self.stdin_path = None | |
| 203 super(ContentBlockStdin, self).__init__() | |
| OLD | NEW |