2144.1.1
by patrick crews
Overhaul of code. We can run rabbitmq : ) We now better encapsulate a per-executor working environment = one step closer to --parallel >: ) using subprocess goodness for server control |
1 |
#! /usr/bin/env python
|
2235.5.2
by Stewart Smith
dbqp source (and all its libs) should use emacs python mode, not emacs C mode |
2 |
# -*- mode: python; indent-tabs-mode: nil; -*-
|
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
3 |
# vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
|
4 |
#
|
|
5 |
# Copyright (C) 2010 Patrick Crews
|
|
6 |
#
|
|
2121.3.2
by patrick crews
Updated license verbiage |
7 |
# This program is free software; you can redistribute it and/or modify
|
8 |
# it under the terms of the GNU General Public License as published by
|
|
9 |
# the Free Software Foundation; either version 2 of the License, or
|
|
10 |
# (at your option) any later version.
|
|
11 |
#
|
|
12 |
# This program is distributed in the hope that it will be useful,
|
|
13 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15 |
# GNU General Public License for more details.
|
|
16 |
#
|
|
17 |
# You should have received a copy of the GNU General Public License
|
|
18 |
# along with this program; if not, write to the Free Software
|
|
19 |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
2121.3.1
by patrick crews
Added licensing text to dbqp files |
20 |
|
2194.2.1
by patrick crews
Integrated randgen with dbqp. We now have mode=randgen and a set of randgen test suites (very basic now). Output = same as dtr : ) We also have mode=cleanup to kill any servers we have started. Docs updates too. Gendata utility allows us to populate test servers |
21 |
""" test_management:
|
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
22 |
code related to the gathering / analysis / management of
|
23 |
the test cases
|
|
24 |
ie - collecting the list of tests in each suite, then
|
|
25 |
gathering additional, relevant information for the test-runner's dtr
|
|
26 |
mode. (traditional diff-based testing)
|
|
27 |
||
28 |
"""
|
|
29 |
||
30 |
# imports
|
|
31 |
import os |
|
32 |
import re |
|
33 |
import sys |
|
34 |
import thread |
|
35 |
||
2194.2.1
by patrick crews
Integrated randgen with dbqp. We now have mode=randgen and a set of randgen test suites (very basic now). Output = same as dtr : ) We also have mode=cleanup to kill any servers we have started. Docs updates too. Gendata utility allows us to populate test servers |
36 |
class testManager(object): |
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
37 |
"""Deals with scanning test directories, gathering test cases, and
|
38 |
collecting per-test information (opt files, etc) for use by the
|
|
39 |
test-runner
|
|
40 |
||
41 |
"""
|
|
42 |
||
2337.1.1
by patrick crews
Cleanup of option handling + test modes. Initial work for expanding dbqp capabilities to do neat things |
43 |
def __init__( self, variables, system_manager): |
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
44 |
|
45 |
self.system_manager = system_manager |
|
2088.9.18
by patrick crews
Updates to allow for timing of test cases and reporting and whatnot |
46 |
self.time_manager = system_manager.time_manager |
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
47 |
self.logging = system_manager.logging |
2337.1.1
by patrick crews
Cleanup of option handling + test modes. Initial work for expanding dbqp capabilities to do neat things |
48 |
if variables['verbose']: |
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
49 |
self.logging.verbose("Initializing test manager...") |
50 |
||
51 |
self.skip_keys = [ 'system_manager' |
|
2337.1.1
by patrick crews
Cleanup of option handling + test modes. Initial work for expanding dbqp capabilities to do neat things |
52 |
, 'verbose' |
53 |
, 'debug' |
|
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
54 |
]
|
55 |
self.test_list = [] |
|
2088.9.18
by patrick crews
Updates to allow for timing of test cases and reporting and whatnot |
56 |
self.first_test = 1 |
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
57 |
self.total_test_count = 0 |
58 |
self.executed_tests = {} # We have a hash of 'status':[test_name..] |
|
59 |
self.executing_tests = {} |
|
2337.1.1
by patrick crews
Cleanup of option handling + test modes. Initial work for expanding dbqp capabilities to do neat things |
60 |
self.verbose = variables['verbose'] |
61 |
self.debug = variables['debug'] |
|
62 |
self.default_engine = variables['defaultengine'] |
|
63 |
self.dotest = variables['dotest'] |
|
64 |
if self.dotest: |
|
65 |
self.dotest = self.dotest.strip() |
|
66 |
self.skiptest = variables['skiptest'] |
|
67 |
if self.skiptest: |
|
68 |
self.skiptest = self.skiptest.strip() |
|
69 |
self.reorder = variables['reorder'] |
|
70 |
self.suitelist = variables['suitelist'] |
|
71 |
self.mode = variables['mode'] |
|
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
72 |
|
2337.1.12
by patrick crews
Additional work on allowing dbqp to handle multiple server types / versions (even simultaneously) |
73 |
self.suitepaths = variables['suitepaths'] |
74 |
self.testdir = variables['testdir'] |
|
2337.1.1
by patrick crews
Cleanup of option handling + test modes. Initial work for expanding dbqp capabilities to do neat things |
75 |
self.desired_tests = variables['test_cases'] |
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
76 |
|
2337.1.4
by patrick crews
Tweak of logging behavior (no more 'if debug' checks...) and related code cleanup. Also groundwork for allowing multiple basedirs (server code bases) to the test runner) |
77 |
self.logging.debug_class(self) |
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
78 |
|
79 |
def add_test(self, new_test_case): |
|
80 |
""" Add a new testCase to our self.test_list """
|
|
2337.1.4
by patrick crews
Tweak of logging behavior (no more 'if debug' checks...) and related code cleanup. Also groundwork for allowing multiple basedirs (server code bases) to the test runner) |
81 |
|
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
82 |
self.test_list.append(new_test_case) |
83 |
||
84 |
def gather_tests(self): |
|
85 |
self.logging.info("Processing test suites...") |
|
86 |
# BEGIN terrible hack to accomodate the fact that
|
|
87 |
# our 'main' suite is also our testdir : /
|
|
2194.2.1
by patrick crews
Integrated randgen with dbqp. We now have mode=randgen and a set of randgen test suites (very basic now). Output = same as dtr : ) We also have mode=cleanup to kill any servers we have started. Docs updates too. Gendata utility allows us to populate test servers |
88 |
if self.suitelist is None and self.mode=='dtr': |
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
89 |
self.suitepaths = [self.testdir] |
90 |
self.suitelist = ['main'] |
|
91 |
# END horrible hack
|
|
92 |
for suite in self.suitelist: |
|
93 |
suite_path = self.find_suite_path(suite) |
|
94 |
if suite_path: |
|
95 |
self.process_suite(suite_path) |
|
96 |
else: |
|
97 |
self.logging.error("Could not find suite: %s in any of paths: %s" %(suite, ", ".join(self.suitepaths))) |
|
98 |
self.process_gathered_tests() |
|
99 |
||
100 |
def process_gathered_tests(self): |
|
101 |
""" We do some post-gathering analysis and whatnot
|
|
102 |
Report an error if there were desired_tests but no tests
|
|
103 |
were found. Otherwise just report what we found
|
|
104 |
|
|
105 |
"""
|
|
106 |
||
2121.6.1
by patrick crews
Updates to allow for reorder option. Also added placeholder for --fast (it is one in test-run.pl as well), and a minor fix of output regarding libtool |
107 |
# See if we need to reorder our test cases
|
108 |
if self.reorder: |
|
109 |
self.sort_testcases() |
|
110 |
||
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
111 |
if self.desired_tests and not self.test_list: |
112 |
# We wanted tests, but found none
|
|
113 |
# Probably need to make this smarter at some point
|
|
114 |
# To maybe make sure that we found all of the desired tests...
|
|
115 |
# However, this is a start / placeholder code
|
|
116 |
self.logging.error("Unable to locate any of the desired tests: %s" %(" ,".join(self.desired_tests))) |
|
117 |
self.total_test_count = len(self.test_list) |
|
118 |
self.logging.info("Found %d test(s) for execution" %(self.total_test_count)) |
|
119 |
||
2337.1.4
by patrick crews
Tweak of logging behavior (no more 'if debug' checks...) and related code cleanup. Also groundwork for allowing multiple basedirs (server code bases) to the test runner) |
120 |
self.logging.debug("Found tests:") |
121 |
self.logging.debug("%s" %(self.print_test_list())) |
|
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
122 |
|
123 |
def find_suite_path(self, suitename): |
|
124 |
""" We have a suitename, we need to locate the path to
|
|
125 |
the juicy suitedir in one of our suitepaths.
|
|
126 |
||
127 |
Theoretically, we could have multiple matches, but
|
|
128 |
such things should never be allowed, so we don't
|
|
129 |
code for it. We return the first match.
|
|
130 |
|
|
131 |
testdir can either be suitepath/suitename or
|
|
132 |
suitepath/suitename/tests. We test and return the
|
|
133 |
existing path. Return None if no match found
|
|
134 |
||
135 |
"""
|
|
136 |
# BEGIN horrible hack to accomodate bad location of main suite
|
|
2194.2.1
by patrick crews
Integrated randgen with dbqp. We now have mode=randgen and a set of randgen test suites (very basic now). Output = same as dtr : ) We also have mode=cleanup to kill any servers we have started. Docs updates too. Gendata utility allows us to populate test servers |
137 |
if self.mode == 'dtr': |
138 |
if self.suitepaths == [self.testdir] or suitename == 'main': |
|
139 |
# We treat this as the 'main' suite
|
|
140 |
return self.testdir |
|
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
141 |
# END horrible hack
|
142 |
for suitepath in self.suitepaths: |
|
143 |
suite_path = self.system_manager.find_path([ os.path.join(suitepath,suitename,'tests'), |
|
144 |
os.path.join(suitepath,suitename) ], required = 0 ) |
|
145 |
if suite_path: |
|
146 |
return suite_path |
|
147 |
return suite_path |
|
148 |
||
149 |
def process_suite(self,suite_dir): |
|
150 |
"""Process a test suite.
|
|
151 |
This includes searching for tests in test_list and only
|
|
152 |
working with the named tests (all tests in suite is the default)
|
|
153 |
Further processing includes reading the disabled.def file
|
|
154 |
to know which tests to skip, processing the suite.opt file,
|
|
155 |
and processing the individual test cases for data relevant
|
|
156 |
to the rest of the test-runner
|
|
157 |
|
|
158 |
"""
|
|
2337.1.5
by patrick crews
Additonal code cleanup |
159 |
self.logging.verbose("Processing suite: %s" %(suite)) |
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
160 |
|
161 |
def has_tests(self): |
|
162 |
"""Return 1 if we have tests in our testlist, 0 otherwise"""
|
|
163 |
||
164 |
return len(self.test_list) |
|
165 |
||
166 |
def get_testCase(self, requester): |
|
167 |
"""return a testCase """
|
|
2088.9.18
by patrick crews
Updates to allow for timing of test cases and reporting and whatnot |
168 |
if self.first_test: |
169 |
# we start our timer
|
|
170 |
self.time_manager.start('total_time','total_time') |
|
171 |
self.first_test = 0 |
|
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
172 |
test_case = None |
173 |
if self.has_tests(): |
|
174 |
test_case = self.test_list.pop(0) |
|
175 |
self.record_test_executor(requester, test_case.fullname) |
|
176 |
return test_case |
|
177 |
||
178 |
def record_test_executor(self, requester, test_name): |
|
179 |
""" We record the test case and executor name as this could be useful
|
|
180 |
We don't *know* this is needed, but we can always change this
|
|
181 |
later
|
|
182 |
|
|
183 |
"""
|
|
184 |
||
185 |
self.executing_tests[test_name] = requester |
|
186 |
||
2088.9.18
by patrick crews
Updates to allow for timing of test cases and reporting and whatnot |
187 |
def record_test_result(self, test_case, test_status, output, exec_time): |
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
188 |
""" Accept the results of an executed testCase for further
|
189 |
processing.
|
|
190 |
|
|
191 |
"""
|
|
192 |
if test_status not in self.executed_tests: |
|
193 |
self.executed_tests[test_status] = [test_case] |
|
194 |
else: |
|
195 |
self.executed_tests[test_status].append(test_case) |
|
2317.1.1
by patrick crews
Updates to dbqp to add a sysbench mode. Created test suites to duplicate readonly / readwrite drizzle-automation tests. Still needs some work, but tests execute |
196 |
# report. If the test failed, we print any additional
|
197 |
# output returned by the test executor
|
|
198 |
# We may want to report additional output at other times
|
|
199 |
if test_status != 'pass': |
|
200 |
report_output = True |
|
201 |
else: |
|
202 |
report_output = False |
|
2088.9.18
by patrick crews
Updates to allow for timing of test cases and reporting and whatnot |
203 |
self.logging.test_report( test_case.fullname, test_status |
2317.1.1
by patrick crews
Updates to dbqp to add a sysbench mode. Created test suites to duplicate readonly / readwrite drizzle-automation tests. Still needs some work, but tests execute |
204 |
, exec_time, output, report_output) |
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
205 |
|
206 |
||
207 |
def print_test_list(self): |
|
208 |
test_names = [] |
|
209 |
for test in self.test_list: |
|
210 |
test_names.append(test.fullname) |
|
211 |
return "[ %s ]" %(", ".join(test_names)) |
|
212 |
||
213 |
def statistical_report(self): |
|
214 |
""" Report out various testing statistics:
|
|
215 |
Failed/Passed %success
|
|
216 |
list of failed test cases
|
|
217 |
|
|
218 |
"""
|
|
2088.9.18
by patrick crews
Updates to allow for timing of test cases and reporting and whatnot |
219 |
# This is probably hacky, but I'll think of a better
|
220 |
# location later. When we are ready to see our
|
|
221 |
# statistical report, we know to stop the total time timer
|
|
2194.2.1
by patrick crews
Integrated randgen with dbqp. We now have mode=randgen and a set of randgen test suites (very basic now). Output = same as dtr : ) We also have mode=cleanup to kill any servers we have started. Docs updates too. Gendata utility allows us to populate test servers |
222 |
if not self.first_test: |
223 |
total_exec_time = self.time_manager.stop('total_time') |
|
224 |
self.logging.write_thick_line() |
|
225 |
self.logging.info("Test execution complete in %d seconds" %(total_exec_time)) |
|
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
226 |
self.logging.info("Summary report:") |
227 |
self.report_executed_tests() |
|
2088.9.18
by patrick crews
Updates to allow for timing of test cases and reporting and whatnot |
228 |
self.report_test_statuses() |
2194.2.1
by patrick crews
Integrated randgen with dbqp. We now have mode=randgen and a set of randgen test suites (very basic now). Output = same as dtr : ) We also have mode=cleanup to kill any servers we have started. Docs updates too. Gendata utility allows us to populate test servers |
229 |
if not self.first_test: |
230 |
self.time_manager.summary_report() |
|
2088.9.18
by patrick crews
Updates to allow for timing of test cases and reporting and whatnot |
231 |
|
232 |
def report_test_statuses(self): |
|
233 |
""" Method to report out various test statuses we
|
|
234 |
care about
|
|
235 |
||
236 |
"""
|
|
237 |
test_statuses = [ 'fail' |
|
238 |
, 'timeout' |
|
239 |
, 'skipped' |
|
240 |
, 'disabled' |
|
241 |
]
|
|
242 |
for test_status in test_statuses: |
|
243 |
self.report_tests_by_status(test_status) |
|
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
244 |
|
245 |
def get_executed_test_count(self): |
|
246 |
""" Return how many tests were executed """
|
|
247 |
total_count = 0 |
|
248 |
for test_list in self.executed_tests.values(): |
|
249 |
total_count = total_count + len(test_list) |
|
250 |
return total_count |
|
251 |
||
252 |
def report_executed_tests(self): |
|
253 |
""" Report out tests by status """
|
|
254 |
total_executed_count = self.get_executed_test_count() |
|
2088.9.15
by patrick crews
Removed innodb_bug53756.test as it was using MTR2 functionality we don't have. Tweaks to error handling for certain cases (non-existent suite) |
255 |
if self.total_test_count: |
256 |
executed_ratio = (float(total_executed_count)/float(self.total_test_count)) |
|
257 |
executed_percent = executed_ratio * 100 |
|
258 |
else: |
|
259 |
# We prevent division by 0 if we didn't find any tests to execute
|
|
260 |
executed_ratio = 0 |
|
261 |
executed_percent = 0 |
|
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
262 |
self.logging.info("Executed %s/%s test cases, %.2f percent" %( total_executed_count |
263 |
, self.total_test_count |
|
264 |
, executed_percent)) |
|
265 |
||
266 |
for test_status in self.executed_tests.keys(): |
|
267 |
status_count = self.get_count_by_status(test_status) |
|
268 |
test_percent = (float(status_count)/float(total_executed_count))*100 |
|
2088.9.8
by patrick crews
Modified test_manager summary report + updated server startup options. Main suite passes now |
269 |
self.logging.info("STATUS: %s, %d/%d test cases, %.2f percent executed" %( test_status.upper() |
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
270 |
, status_count |
271 |
, total_executed_count |
|
272 |
, test_percent |
|
2088.9.8
by patrick crews
Modified test_manager summary report + updated server startup options. Main suite passes now |
273 |
))
|
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
274 |
|
275 |
||
2088.9.6
by patrick crews
Updates to reporting |
276 |
def report_tests_by_status(self, status): |
277 |
matching_tests = [] |
|
278 |
if status in self.executed_tests: |
|
279 |
for testcase in self.executed_tests[status]: |
|
280 |
matching_tests.append(testcase.fullname) |
|
281 |
self.logging.info("%s tests: %s" %(status.upper(), ", ".join(matching_tests))) |
|
2088.9.1
by patrick crews
Updated tree so that test-run.pl and test-run.py may live together in peace for a time |
282 |
|
283 |
def get_count_by_status(self, test_status): |
|
284 |
""" Return how many tests are in a given test_status """
|
|
285 |
if test_status in self.executed_tests: |
|
286 |
return len(self.executed_tests[test_status]) |
|
287 |
else: |
|
288 |
return 0 |
|
289 |
||
2121.6.1
by patrick crews
Updates to allow for reorder option. Also added placeholder for --fast (it is one in test-run.pl as well), and a minor fix of output regarding libtool |
290 |
def sort_testcases(self): |
291 |
""" Sort testcases to optimize test execution.
|
|
292 |
This can be very mode-specific
|
|
293 |
||
294 |
"""
|
|
295 |
||
296 |
self.logging.verbose("Reordering testcases to optimize test execution...") |
|
297 |
||
2151.8.1
by patrick crews
Updates to allow several dbqp's run run on one system (via uuid) and fix to return a relevant code post-execution (ie 1 if tests failed or timed out)) |
298 |
def has_failing_tests(self): |
299 |
return (self.get_count_by_status('fail') + self.get_count_by_status('timeout')) |
|
300 |