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 |