OLD | NEW |
---|---|
1 # Copyright 2012 the V8 project authors. All rights reserved. | 1 # Copyright 2012 the V8 project authors. All rights reserved. |
2 # Redistribution and use in source and binary forms, with or without | 2 # Redistribution and use in source and binary forms, with or without |
3 # modification, are permitted provided that the following conditions are | 3 # modification, are permitted provided that the following conditions are |
4 # met: | 4 # met: |
5 # | 5 # |
6 # * Redistributions of source code must retain the above copyright | 6 # * Redistributions of source code must retain the above copyright |
7 # notice, this list of conditions and the following disclaimer. | 7 # notice, this list of conditions and the following disclaimer. |
8 # * Redistributions in binary form must reproduce the above | 8 # * Redistributions in binary form must reproduce the above |
9 # copyright notice, this list of conditions and the following | 9 # copyright notice, this list of conditions and the following |
10 # disclaimer in the documentation and/or other materials provided | 10 # disclaimer in the documentation and/or other materials provided |
11 # with the distribution. | 11 # with the distribution. |
12 # * Neither the name of Google Inc. nor the names of its | 12 # * Neither the name of Google Inc. nor the names of its |
13 # contributors may be used to endorse or promote products derived | 13 # contributors may be used to endorse or promote products derived |
14 # from this software without specific prior written permission. | 14 # from this software without specific prior written permission. |
15 # | 15 # |
16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
19 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 19 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
20 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 20 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | 27 |
28 | 28 |
29 import multiprocessing | |
30 import os | 29 import os |
31 import threading | |
32 import time | 30 import time |
33 | 31 |
32 from pool import Pool | |
34 from . import commands | 33 from . import commands |
35 from . import utils | 34 from . import utils |
36 | 35 |
37 | 36 |
38 BREAK_NOW = -1 | |
39 EXCEPTION = -2 | |
40 | |
41 | |
42 class Job(object): | 37 class Job(object): |
43 def __init__(self, command, dep_command, test_id, timeout, verbose): | 38 def __init__(self, command, dep_command, test_id, timeout, verbose): |
44 self.command = command | 39 self.command = command |
45 self.dep_command = dep_command | 40 self.dep_command = dep_command |
46 self.id = test_id | 41 self.id = test_id |
47 self.timeout = timeout | 42 self.timeout = timeout |
48 self.verbose = verbose | 43 self.verbose = verbose |
49 | 44 |
50 | 45 |
51 def RunTest(job): | 46 def RunTest(job): |
52 try: | 47 start_time = time.time() |
53 start_time = time.time() | 48 if job.dep_command is not None: |
54 if job.dep_command is not None: | 49 dep_output = commands.Execute(job.dep_command, job.verbose, job.timeout) |
55 dep_output = commands.Execute(job.dep_command, job.verbose, job.timeout) | 50 # TODO(jkummerow): We approximate the test suite specific function |
56 # TODO(jkummerow): We approximate the test suite specific function | 51 # IsFailureOutput() by just checking the exit code here. Currently |
57 # IsFailureOutput() by just checking the exit code here. Currently | 52 # only cctests define dependencies, for which this simplification is |
58 # only cctests define dependencies, for which this simplification is | 53 # correct. |
59 # correct. | 54 if dep_output.exit_code != 0: |
60 if dep_output.exit_code != 0: | 55 return (job.id, dep_output, time.time() - start_time) |
61 return (job.id, dep_output, time.time() - start_time) | 56 output = commands.Execute(job.command, job.verbose, job.timeout) |
62 output = commands.Execute(job.command, job.verbose, job.timeout) | 57 return (job.id, output, time.time() - start_time) |
63 return (job.id, output, time.time() - start_time) | |
64 except KeyboardInterrupt: | |
65 return (-1, BREAK_NOW, 0) | |
66 except Exception, e: | |
67 print(">>> EXCEPTION: %s" % e) | |
68 return (-1, EXCEPTION, 0) | |
69 | |
70 | 58 |
71 class Runner(object): | 59 class Runner(object): |
72 | 60 |
73 def __init__(self, suites, progress_indicator, context): | 61 def __init__(self, suites, progress_indicator, context): |
74 self.tests = [ t for s in suites for t in s.tests ] | 62 self.tests = [ t for s in suites for t in s.tests ] |
75 self._CommonInit(len(self.tests), progress_indicator, context) | 63 self._CommonInit(len(self.tests), progress_indicator, context) |
76 | 64 |
77 def _CommonInit(self, num_tests, progress_indicator, context): | 65 def _CommonInit(self, num_tests, progress_indicator, context): |
78 self.indicator = progress_indicator | 66 self.indicator = progress_indicator |
79 progress_indicator.runner = self | 67 progress_indicator.runner = self |
80 self.context = context | 68 self.context = context |
81 self.succeeded = 0 | 69 self.succeeded = 0 |
82 self.total = num_tests | 70 self.total = num_tests |
83 self.remaining = num_tests | 71 self.remaining = num_tests |
84 self.failed = [] | 72 self.failed = [] |
85 self.crashed = 0 | 73 self.crashed = 0 |
86 self.terminate = False | |
87 self.lock = threading.Lock() | |
88 | 74 |
89 def Run(self, jobs): | 75 def Run(self, jobs): |
90 self.indicator.Starting() | 76 self.indicator.Starting() |
91 self._RunInternal(jobs) | 77 self._RunInternal(jobs) |
92 self.indicator.Done() | 78 self.indicator.Done() |
93 if self.failed or self.remaining: | 79 if self.failed or self.remaining: |
94 return 1 | 80 return 1 |
95 return 0 | 81 return 0 |
96 | 82 |
97 def _RunInternal(self, jobs): | 83 def _RunInternal(self, jobs): |
98 pool = multiprocessing.Pool(processes=jobs) | 84 pool = Pool(jobs) |
99 test_map = {} | 85 test_map = {} |
86 # TODO(machenbach): Instead of filling the queue completely before | |
87 # pool.imap_unordered, make this a generator that already starts testing | |
88 # while the queue is filled. | |
Jakob Kummerow
2014/05/14 09:07:37
Have you measured whether that would make a differ
Michael Achenbach
2014/05/14 12:05:58
It takes 0.5 seconds... I rather leave the todo an
| |
100 queue = [] | 89 queue = [] |
101 queued_exception = None | 90 queued_exception = None |
102 for test in self.tests: | 91 for test in self.tests: |
103 assert test.id >= 0 | 92 assert test.id >= 0 |
104 test_map[test.id] = test | 93 test_map[test.id] = test |
105 try: | 94 try: |
106 command = self.GetCommand(test) | 95 command = self.GetCommand(test) |
107 except Exception, e: | 96 except Exception, e: |
108 # If this failed, save the exception and re-raise it later (after | 97 # If this failed, save the exception and re-raise it later (after |
109 # all other tests have had a chance to run). | 98 # all other tests have had a chance to run). |
110 queued_exception = e | 99 queued_exception = e |
111 continue | 100 continue |
112 timeout = self.context.timeout | 101 timeout = self.context.timeout |
113 if ("--stress-opt" in test.flags or | 102 if ("--stress-opt" in test.flags or |
114 "--stress-opt" in self.context.mode_flags or | 103 "--stress-opt" in self.context.mode_flags or |
115 "--stress-opt" in self.context.extra_flags): | 104 "--stress-opt" in self.context.extra_flags): |
116 timeout *= 4 | 105 timeout *= 4 |
117 if test.dependency is not None: | 106 if test.dependency is not None: |
118 dep_command = [ c.replace(test.path, test.dependency) for c in command ] | 107 dep_command = [ c.replace(test.path, test.dependency) for c in command ] |
119 else: | 108 else: |
120 dep_command = None | 109 dep_command = None |
121 job = Job(command, dep_command, test.id, timeout, self.context.verbose) | 110 job = Job(command, dep_command, test.id, timeout, self.context.verbose) |
122 queue.append(job) | 111 queue.append([job]) |
123 try: | 112 try: |
124 kChunkSize = 1 | 113 it = pool.imap_unordered(RunTest, queue) |
125 it = pool.imap_unordered(RunTest, queue, kChunkSize) | |
126 for result in it: | 114 for result in it: |
127 test_id = result[0] | 115 test = test_map[result[0]] |
128 if test_id < 0: | |
129 if result[1] == BREAK_NOW: | |
130 self.terminate = True | |
131 else: | |
132 continue | |
133 if self.terminate: | |
134 pool.terminate() | |
135 pool.join() | |
136 raise BreakNowException("User pressed Ctrl+C or IO went wrong") | |
137 test = test_map[test_id] | |
138 self.indicator.AboutToRun(test) | 116 self.indicator.AboutToRun(test) |
139 test.output = result[1] | 117 test.output = result[1] |
140 test.duration = result[2] | 118 test.duration = result[2] |
141 has_unexpected_output = test.suite.HasUnexpectedOutput(test) | 119 has_unexpected_output = test.suite.HasUnexpectedOutput(test) |
142 if has_unexpected_output: | 120 if has_unexpected_output: |
143 self.failed.append(test) | 121 self.failed.append(test) |
144 if test.output.HasCrashed(): | 122 if test.output.HasCrashed(): |
145 self.crashed += 1 | 123 self.crashed += 1 |
146 else: | 124 else: |
147 self.succeeded += 1 | 125 self.succeeded += 1 |
148 self.remaining -= 1 | 126 self.remaining -= 1 |
149 self.indicator.HasRun(test, has_unexpected_output) | 127 self.indicator.HasRun(test, has_unexpected_output) |
150 except KeyboardInterrupt: | 128 finally: |
151 pool.terminate() | 129 pool.terminate() |
152 pool.join() | |
153 raise | |
154 except Exception, e: | |
155 print("Exception: %s" % e) | |
156 pool.terminate() | |
157 pool.join() | |
158 raise | |
159 if queued_exception: | 130 if queued_exception: |
160 raise queued_exception | 131 raise queued_exception |
161 return | |
162 | 132 |
163 | 133 |
164 def GetCommand(self, test): | 134 def GetCommand(self, test): |
165 d8testflag = [] | 135 d8testflag = [] |
166 shell = test.suite.shell() | 136 shell = test.suite.shell() |
167 if shell == "d8": | 137 if shell == "d8": |
168 d8testflag = ["--test"] | 138 d8testflag = ["--test"] |
169 if utils.IsWindows(): | 139 if utils.IsWindows(): |
170 shell += ".exe" | 140 shell += ".exe" |
171 cmd = (self.context.command_prefix + | 141 cmd = (self.context.command_prefix + |
172 [os.path.abspath(os.path.join(self.context.shell_dir, shell))] + | 142 [os.path.abspath(os.path.join(self.context.shell_dir, shell))] + |
173 d8testflag + | 143 d8testflag + |
174 ["--random-seed=%s" % self.context.random_seed] + | 144 ["--random-seed=%s" % self.context.random_seed] + |
175 test.suite.GetFlagsForTestCase(test, self.context) + | 145 test.suite.GetFlagsForTestCase(test, self.context) + |
176 self.context.extra_flags) | 146 self.context.extra_flags) |
177 return cmd | 147 return cmd |
178 | 148 |
179 | 149 |
180 class BreakNowException(Exception): | 150 class BreakNowException(Exception): |
181 def __init__(self, value): | 151 def __init__(self, value): |
182 self.value = value | 152 self.value = value |
183 def __str__(self): | 153 def __str__(self): |
184 return repr(self.value) | 154 return repr(self.value) |
OLD | NEW |