Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6)

Side by Side Diff: tests/selenium/test_runner.py

Issue 212031: Changed selenium tests to recover from test crashes/hangs. Divided perceptual... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/o3d/
Patch Set: '' Created 11 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tests/selenium/selenium_utilities.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python2.4
2 # Copyright 2009, Google Inc.
3 # 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 are
7 # met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following disclaimer
13 # in the documentation and/or other materials provided with the
14 # distribution.
15 # * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31
32 """Test runners and associated classes.
33
34 Each test runner has its own thread, which attempts to perform a given test.
35 If a test hangs, the test runner can be aborted or exited.
36
37 """
38
39 import os
40 import sys
41
42 import socket
43 import subprocess
44 import threading
45 import time
46 import unittest
47 import gflags
48 import selenium
49 import selenium_constants
50 import Queue
51 import thread
52 import copy
53
54 class StringBuffer:
55 """Primitive string buffer.
56
57 Members:
58 data: the contents of the buffer
59 """
60 def __init__(self):
61 self.data = ""
62 def write(self, data):
63 self.data += str(data)
64 def writeln(self, data=None):
65 if data is not None:
66 self.write(data)
67 self.write("\n")
68 def get(self):
69 get_data = self.data
70 self.data = ""
71 return get_data
72
73 class TestResult(unittest.TestResult):
74 """A specialized class that prints formatted text results to a stream.
75
76 """
77 separator1 = "=" * 70
78 separator2 = "-" * 70
79
80 def __init__(self, stream, browser):
81 unittest.TestResult.__init__(self)
82 self.stream = stream
83 # Dictionary of start times
84 self.start_times = {}
85 # Dictionary of results
86 self.results = {}
87 self.browser = browser
88
89 def getDescription(self, test):
90 """Gets description of test."""
91 return test.shortDescription() or str(test)
92
93 def startTest(self, test):
94 """Starts test."""
95 # Records the start time
96 self.start_times[test] = time.time()
97 # Default testresult if success not called
98 self.results[test] = "FAIL"
99 unittest.TestResult.startTest(self, test)
100 self.stream.writeln()
101 self.stream.writeln(self.separator2)
102 self.stream.write(self.getDescription(test))
103 self.stream.writeln(" ... ")
104
105 def stopTest(self, test):
106 """Called when test is ended."""
107 time_taken = time.time() - self.start_times[test]
108 result = self.results[test]
109 self.stream.writeln("SELENIUMRESULT %s <%s> [%.3fs]: %s"
110 % (test, self.browser, time_taken, result))
111
112 def addSuccess(self, test):
113 """Adds success result to TestResult."""
114 unittest.TestResult.addSuccess(self, test)
115 self.results[test] = "PASS"
116
117 def addError(self, test, err):
118 """Adds error result to TestResult."""
119 unittest.TestResult.addError(self, test, err)
120 self.results[test] = "FAIL"
121
122 def addFailure(self, test, err):
123 """Adds failure result to TestResult."""
124 unittest.TestResult.addFailure(self, test, err)
125 self.results[test] = "FAIL"
126
127 def noResponse(self, test):
128 """Configures the result for a test that did not respond."""
129 self.results[test] = "FAIL"
130 self.testsRun += 1
131 self.errors.append("No response from test")
132
133 self.stream.writeln()
134 self.stream.writeln(self.separator2)
135 self.stream.write(self.getDescription(test))
136 self.stream.writeln(" ... ")
137 self.stream.writeln("SELENIUMRESULT %s <%s> [?s]: FAIL (HUNG?)"
138 % (test, self.browser))
139 self.stream.writeln()
140
141 def printErrors(self):
142 """Prints all errors and failures."""
143 self.stream.writeln()
144 self.printErrorList("ERROR", self.errors)
145 self.printErrorList("FAIL", self.failures)
146
147 def printErrorList(self, flavour, errors):
148 """Prints a given list of errors."""
149 for test, err in errors:
150 self.stream.writeln("%s:" % flavour)
151 self.stream.writeln("%s" % err)
152
153 def printAll(self, stream):
154 """Prints the entire stream to the given stream."""
155 stream.write(self.stream.data)
156
157 def merge(self, result):
158 """Merges the given result into this resultl."""
159 self.testsRun += result.testsRun
160 for key, entry in result.results.iteritems():
161 self.results[key] = entry
162 for error in result.errors:
163 self.errors.append(error)
164 for failure in result.failures:
165 self.failures.append(failure)
166 self.stream.write(result.stream)
167
168
169 class TestRunnerThread(threading.Thread):
170 """Abstract test runner class. Launches its own thread for running tests.
171 Formats test results.
172
173 Members:
174 completely_done_event: event that occurs just before thread exits.
175 test: the currently running test.
176 browser: selenium_name of browser that will be tested.
177 """
178 def __init__(self):
179 threading.Thread.__init__(self)
180 # This thread is a daemon so that the program can exit even if the
181 # thread has not finished.
182 self.setDaemon(True)
183 self.completely_done_event = threading.Event()
184 self.test_copy = None
185 self.browser = "default_browser"
186
187 def IsCompletelyDone(self):
188 """Returns true if this test runner is completely done."""
189 return self.completely_done_event.isSet()
190
191 def run(self):
192 pass
193
194 def SetBrowser(self, browser):
195 """Sets the browser name."""
196 self.browser = browser
197
198 def GetNoResponseResult(self):
199 """Returns a generic no response result for last test."""
200 result = TestResult(StringBuffer(), self.browser)
201 result.noResponse(self.test)
202 return result
203
204 def RunTest(self, test):
205 "Run the given test case or test suite."
206 self.test = test
207
208 stream = StringBuffer()
209 result = TestResult(stream, self.browser)
210 startTime = time.time()
211 test(result)
212 stopTime = time.time()
213 timeTaken = stopTime - startTime
214 result.printErrors()
215 run = result.testsRun
216 stream.writeln("Took %.2fs" % timeTaken)
217 stream.writeln()
218 return result
219
220
221 class PDiffTestRunner(TestRunnerThread):
222 """Test runner for Perceptual Diff tests. Polls a test queue and launches
223 given tests. Adds result to given queue.
224
225 Members:
226 pdiff_queue: list of tests to run, when they arrive.
227 result_queue: queue of our tests results.
228 browser: selenium name of browser to be tested.
229 end_testing_event: event that occurs when we are guaranteed no more tests
230 will be added to the queue.
231 """
232 def __init__(self, pdiff_queue, result_queue, browser):
233 TestRunnerThread.__init__(self)
234 self.pdiff_queue = pdiff_queue
235 self.result_queue = result_queue
236 self.browser = browser
237
238 self.end_testing_event = threading.Event()
239
240 def EndTesting(self):
241 """Called to notify thread that no more tests will be added to the test
242 queue."""
243 self.end_testing_event.set()
244
245 def run(self):
246 while True:
247 try:
248 test = self.pdiff_queue.get_nowait()
249
250 result = self.RunTest(test)
251
252 self.result_queue.put(result)
253
254 except Queue.Empty:
255 if self.end_testing_event.isSet() and self.pdiff_queue.empty():
256 break
257 else:
258 time.sleep(1)
259
260 self.completely_done_event.set()
261
262
263 class SeleniumTestRunner(TestRunnerThread):
264 """Test runner for Selenium tests. Takes a test from a test queue and launches
265 it. Tries to handle hung/crashed tests gracefully.
266
267 Members:
268 testing_event: event that occurs when the runner is testing.
269 finished_event: event that occurs when thread has finished testing and
270 before it starts its next test.
271 can_continue_lock: lock for |can_continue|.
272 can_continue: is True when main thread permits the test runner to continue.
273 sel_builder: builder that constructs new selenium sessions, as needed.
274 browser: selenium name of browser to be tested.
275 session: current selenium session being used in tests, can be None.
276 test_queue: queue of tests to run.
277 pdiff_queue: queue of perceptual diff tests to run. We add a perceptual
278 diff test to the queue when the related selenium test passes.
279 deadline: absolute time of when the test should be done.
280 """
281 def __init__(self, sel_builder, browser, test_queue, pdiff_queue):
282 TestRunnerThread.__init__(self)
283
284 # Synchronization.
285 self.testing_event = threading.Event()
286 self.finished_event = threading.Event()
287 self.can_continue_lock = threading.Lock()
288 self.can_continue = False
289
290 # Selenium variables.
291 self.sel_builder = sel_builder
292 self.browser = browser
293
294 # Test variables.
295 self.test_queue = test_queue
296 self.pdiff_queue = pdiff_queue
297
298 self.deadline = 0
299
300 def IsPastDeadline(self):
301 if time.time() > self.deadline:
302 return True
303 return False
304
305 def IsTesting(self):
306 return self.testing_event.isSet()
307
308 def DidFinishTest(self):
309 return self.finished_event.isSet()
310
311 def Continue(self):
312 """Signals to thread to continue testing.
313
314 Returns:
315 result: the result for the recently finished test.
316 """
317
318 self.finished_event.clear()
319
320 self.can_continue_lock.acquire()
321 self.can_continue = True
322 result = self.result
323 self.can_continue_lock.release()
324
325 return result
326
327 def AbortTest(self):
328 self._StopSession()
329 self._StartSession()
330
331 def _StartSession(self):
332 self.session = self.sel_builder.NewSeleniumSession(self.browser)
333 # Copy the session so we can shut down safely on a different thread.
334 self.shutdown_session = copy.deepcopy(self.session)
335
336 def _StopSession(self):
337 if self.session is not None:
338 self.session = None
339 try:
340 # This can cause an exception on some browsers.
341 # Silenly disregard the exception.
342 self.shutdown_session.stop()
343 except:
344 pass
345
346 def run(self):
347 self._StartSession()
348
349 while not self.test_queue.empty():
350 try:
351 # Grab test from queue.
352 test_obj = self.test_queue.get_nowait()
353 if type(test_obj) == tuple:
354 test = test_obj[0]
355 pdiff_test = test_obj[1]
356 else:
357 test = test_obj
358 pdiff_test = None
359
360 self.can_continue = False
361
362 # Deadline is the time to load page timeout plus a constant.
363 self.deadline = (time.time() + (test.GetTestTimeout() / 1000.0) +
364 selenium_constants.MAX_SELENIUM_TEST_TIME)
365 # Supply test with necessary selenium session.
366 test.SetSession(self.session)
367
368 # Run test.
369 self.testing_event.set()
370 self.result = self.RunTest(test)
371
372 if time.time() > self.deadline:
373 self.result = self.GetNoResponseResult()
374
375 self.testing_event.clear()
376 self.finished_event.set()
377
378 # Wait for instruction from the main thread.
379 while True:
380 self.can_continue_lock.acquire()
381 can_continue = self.can_continue
382 self.can_continue_lock.release()
383 if can_continue:
384 break
385 time.sleep(.5)
386
387 if self.pdiff_queue is not None and pdiff_test is not None:
388 if self.result.wasSuccessful():
389 # Add the dependent perceptual diff test.
390 self.pdiff_queue.put(pdiff_test)
391
392 except Queue.Empty:
393 break
394
395 self._StopSession()
396 self.completely_done_event.set()
397
398
OLDNEW
« no previous file with comments | « tests/selenium/selenium_utilities.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698