Merge lp:ubuntu-automation-test-harness/dev into lp:ubuntu-automation-test-harness
- dev
- Merge into trunk
Proposed by
Max Brustkern
Status: | Merged |
---|---|
Approved by: | Max Brustkern |
Approved revision: | 249 |
Merged at revision: | 121 |
Proposed branch: | lp:ubuntu-automation-test-harness/dev |
Merge into: | lp:ubuntu-automation-test-harness |
Diff against target: |
5744 lines (+3299/-1710) 48 files modified
.profile (+0/-2) Makefile (+11/-0) TODO (+3/-3) client.py (+91/-87) debian/changelog (+825/-0) debian/conffiles (+0/-1) debian/control (+1/-7) debian/dirs (+1/-0) debian/install (+3/-1) debian/postinst (+9/-8) debian/rules (+5/-1) debian/source/format (+1/-0) debian/ubuntu-automation-test-harness-client.install (+0/-1) examples/provision.py (+0/-45) examples/run_test_vm.py (+105/-0) examples/run_uath_tests.py (+71/-0) shell-profile (+1/-0) uath/client/README (+1/-1) uath/client/common.py (+129/-113) uath/client/examples/master.run (+1/-1) uath/client/examples/uath_tests/test_one/tc_control (+1/-1) uath/client/exceptions.py (+7/-0) uath/client/result.py (+102/-79) uath/client/runner.py (+305/-267) uath/client/self_test.py (+496/-461) uath/client/state_agent.py (+70/-70) uath/client/testcase.py (+195/-192) uath/client/testsuite.py (+263/-250) uath/exceptions.py (+0/-6) uath/provisioning/catalog/__init__.py (+1/-1) uath/provisioning/catalog/base.py (+51/-14) uath/provisioning/catalog/sqlite.py (+13/-6) uath/provisioning/provisioning.py (+192/-18) uath/provisioning/vm/__init__.py (+2/-1) uath/provisioning/vm/kvm.py (+186/-42) uath/provisioning/vm/vm.py (+6/-19) uath_howto.txt (+21/-9) ubuntu-automation-test-harness-client/Makefile (+25/-0) ubuntu-automation-test-harness-client/debian/compat (+1/-0) ubuntu-automation-test-harness-client/debian/control (+15/-0) ubuntu-automation-test-harness-client/debian/copyright (+60/-0) ubuntu-automation-test-harness-client/debian/dirs (+2/-0) ubuntu-automation-test-harness-client/debian/install (+2/-0) ubuntu-automation-test-harness-client/debian/postinst (+4/-0) ubuntu-automation-test-harness-client/debian/rules (+9/-0) ubuntu-automation-test-harness-client/debian/source/format (+1/-0) ubuntu-automation-test-harness-client/getroot (+8/-0) uqt-vm-tools.conf (+3/-3) |
To merge this branch: | bzr merge lp:ubuntu-automation-test-harness/dev |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Max Brustkern (community) | Approve | ||
Review via email: mp+105151@code.launchpad.net |
Commit message
Description of the change
Merging working version into trunk for use in kernel backport testing.
To post a comment you must log in.
Revision history for this message
Max Brustkern (nuclearbob) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === removed file '.profile' |
2 | --- .profile 2012-04-06 20:13:15 +0000 |
3 | +++ .profile 1970-01-01 00:00:00 +0000 |
4 | @@ -1,2 +0,0 @@ |
5 | -export UQT_VM_TOOLS=/opt/canonical.com/ubuntu-qa-tools/vm-tools |
6 | -export DISPLAY="" |
7 | |
8 | === modified file 'Makefile' |
9 | --- Makefile 2012-03-29 20:14:50 +0000 |
10 | +++ Makefile 2012-05-09 00:33:18 +0000 |
11 | @@ -1,4 +1,15 @@ |
12 | PYC_PATTERN?=*.pyc |
13 | +all: client uath_sudoers |
14 | + |
15 | +client: |
16 | + cd ubuntu-automation-test-harness-client ; make |
17 | + |
18 | +uath_sudoers: |
19 | + ls examples | grep -v "~$$" | grep -v "\.pyc$$" | sed 's/^/ALL ALL = (uath) NOPASSWD: \/usr\/share\/uath\/examples\//' > uath_sudoers |
20 | |
21 | clean: |
22 | find . -name "$(PYC_PATTERN)" -delete |
23 | + cd ubuntu-automation-test-harness-client ; make clean |
24 | + rm uath_sudoers |
25 | + |
26 | +.PHONY: client clean |
27 | |
28 | === modified file 'TODO' |
29 | --- TODO 2012-04-09 19:43:57 +0000 |
30 | +++ TODO 2012-05-09 00:33:18 +0000 |
31 | @@ -1,6 +1,7 @@ |
32 | # Mon 26 Mar 2012 01:48:25 PM EDT Joe Talbott <joe.talbott@canonical.com> |
33 | |
34 | 1. Write/Implement |
35 | + * make sure a new Result object is created for each suite. |
36 | * add support for 'run_as'. |
37 | * add proper support for testcase types as we currently only support |
38 | userland. |
39 | @@ -18,9 +19,8 @@ |
40 | # proposed layout of UATH directory |
41 | /var/uath/ |
42 | master.run |
43 | - suite_one/ # to be created by runner |
44 | - # fetch suite_one into here (currently this is done one level up. |
45 | - suite_two/ # to be created by runner |
46 | + suite_one/ # the Runner will create this and run the fetch_cmd in here |
47 | + suite_two/ # the Runner will create this and run the fetch_cmd in here |
48 | suite_two/ # fetched |
49 | test_one/ |
50 | test_one.py |
51 | |
52 | === renamed file 'main.py' => 'client.py' |
53 | --- main.py 2012-04-10 20:42:15 +0000 |
54 | +++ client.py 2012-05-09 00:33:18 +0000 |
55 | @@ -1,109 +1,113 @@ |
56 | #!/usr/bin/env python |
57 | |
58 | import logging |
59 | +import platform |
60 | import sys |
61 | |
62 | -from uath.runner import Runner |
63 | -from uath.state_agent import StateAgentYAML |
64 | -from uath.result import Result, ResultYAML |
65 | +from uath.client.runner import Runner |
66 | +from uath.client.state_agent import StateAgentYAML |
67 | +from uath.client.result import Result, ResultYAML, ResultJSON |
68 | |
69 | -from uath.common import DEFAULT_STATE_FILE |
70 | -from uath import exceptions |
71 | +from uath.client.common import DEFAULT_STATE_FILE |
72 | +from uath.client import exceptions |
73 | |
74 | def process_cmdline_argparse(): |
75 | - """ |
76 | - Process the command line arguments. |
77 | - """ |
78 | - |
79 | - # requires Python2.7+ |
80 | - import argparse |
81 | - |
82 | - parser = argparse.ArgumentParser(description='Ubuntu Automation Testing Harness') |
83 | - parser.add_argument('--resume', action='store_true', help='Continue a previous run. Used after a reboot') |
84 | - parser.add_argument('-s', '--state-file', help='File to use for storing state (default "%s"' % DEFAULT_STATE_FILE) |
85 | - parser.add_argument('-f', '--format', choices=['text', 'yaml'], default='yaml', help='Output format (default "yaml")') |
86 | - parser.add_argument('-t', '--testdir', default='/var/uath', help='Main test directory') |
87 | - parser.add_argument('-r', '--runlist', default='master.run', help='runlist file name') |
88 | - parser.add_argument('-o', '--output', help='write output to this file') |
89 | - parser.add_argument('-a', '--append', action='store_true', help='append to output') |
90 | - parser.add_argument('-d', '--debug', action='store_true', help='Print debugging output') |
91 | - |
92 | - args = parser.parse_args() |
93 | - |
94 | - return args |
95 | + """ |
96 | + Process the command line arguments. |
97 | + """ |
98 | + |
99 | + # requires Python2.7+ |
100 | + import argparse |
101 | + |
102 | + parser = argparse.ArgumentParser(description='Ubuntu Automation Testing Harness') |
103 | + parser.add_argument('--resume', action='store_true', help='Continue a previous run. Used after a reboot') |
104 | + parser.add_argument('-s', '--state-file', help='File to use for storing state (default "%s"' % DEFAULT_STATE_FILE) |
105 | + parser.add_argument('-f', '--format', choices=['text', 'yaml', 'json'], default='yaml', help='Output format (default "yaml")') |
106 | + parser.add_argument('-t', '--testdir', default='/var/lib/uath', help='Main test directory') |
107 | + parser.add_argument('-r', '--runlist', default='master.run', help='runlist file name') |
108 | + parser.add_argument('-o', '--output', help='write output to this file') |
109 | + parser.add_argument('-a', '--append', action='store_true', help='append to output') |
110 | + parser.add_argument('-d', '--debug', action='store_true', help='Print debugging output') |
111 | + |
112 | + args = parser.parse_args() |
113 | + |
114 | + return args |
115 | |
116 | # TODO: write <2.7 optparse version and set based on version of python |
117 | # being used. |
118 | process_cmdline = process_cmdline_argparse |
119 | |
120 | def exit(returncode, msg): |
121 | - print msg |
122 | - sys.exit(returncode) |
123 | + print msg |
124 | + sys.exit(returncode) |
125 | |
126 | def setup_logging(debug=False): |
127 | - if debug: |
128 | - level = logging.DEBUG |
129 | - else: |
130 | - level = logging.WARNING |
131 | + if debug: |
132 | + level = logging.DEBUG |
133 | + else: |
134 | + level = logging.WARNING |
135 | |
136 | - logging.basicConfig(level=level, |
137 | - format='%(asctime)s - %(levelname)s - %(message)s') |
138 | + logging.basicConfig(level=level, |
139 | + format='%(asctime)s - %(levelname)s - %(message)s') |
140 | |
141 | def main(): |
142 | - """ |
143 | - The main driver for the 'uath' application. |
144 | - """ |
145 | - |
146 | - args = process_cmdline() |
147 | - |
148 | - setup_logging(args.debug) |
149 | - |
150 | - logging.debug(args) |
151 | - |
152 | - """ |
153 | - print args |
154 | - import sys |
155 | - sys.exit(0) |
156 | - """ |
157 | - resume = args.resume |
158 | - state_file = args.state_file |
159 | - testdir = args.testdir |
160 | - runlist = args.runlist |
161 | - output = args.output |
162 | - |
163 | - if args.append: |
164 | - open_flags = 'a' |
165 | - else: |
166 | - open_flags = 'w' |
167 | - |
168 | - # Redirect stdout to a file if requested. |
169 | - if output: |
170 | + """ |
171 | + The main driver for the 'uath' application. |
172 | + """ |
173 | + |
174 | + args = process_cmdline() |
175 | + |
176 | + setup_logging(args.debug) |
177 | + |
178 | + logging.debug(args) |
179 | + logging.info(platform.uname()) |
180 | + |
181 | + """ |
182 | + print args |
183 | + import sys |
184 | + sys.exit(0) |
185 | + """ |
186 | + resume = args.resume |
187 | + state_file = args.state_file |
188 | + testdir = args.testdir |
189 | + runlist = args.runlist |
190 | + output = args.output |
191 | + |
192 | + if args.append: |
193 | + open_flags = 'a' |
194 | + else: |
195 | + open_flags = 'w' |
196 | + |
197 | + # Redirect stdout to a file if requested. |
198 | + if output: |
199 | + try: |
200 | + sys.stdout = open(output, open_flags) |
201 | + except IOError as e: |
202 | + exit(e.errno, e) |
203 | + |
204 | + if args.format == 'text': |
205 | + result_class = Result |
206 | + elif args.format == 'json': |
207 | + result_class = ResultJSON |
208 | + else: |
209 | + result_class = ResultYAML |
210 | + |
211 | + state_agent = StateAgentYAML(state_file=state_file) |
212 | try: |
213 | - sys.stdout = open(output, open_flags) |
214 | - except IOError as e: |
215 | - exit(e.errno, e) |
216 | - |
217 | - if args.format == 'text': |
218 | - result_class = Result |
219 | - else: |
220 | - result_class = ResultYAML |
221 | - |
222 | - state_agent = StateAgentYAML(state_file=state_file) |
223 | - try: |
224 | - runner = Runner(state_agent=state_agent, result_class=result_class, |
225 | - runlist=runlist, resume=resume, testdir=testdir) |
226 | - except (exceptions.BadDir, exceptions.MissingFile) as e: |
227 | - errno = 1 |
228 | - |
229 | - if hasattr(e, 'errno'): |
230 | - errno = e.errno |
231 | - |
232 | - exit(errno, e) |
233 | - |
234 | - if resume: |
235 | - runner.load_state() |
236 | - |
237 | - runner.run() |
238 | + runner = Runner(state_agent=state_agent, result_class=result_class, |
239 | + runlist=runlist, resume=resume, testdir=testdir) |
240 | + except (exceptions.BadDir, exceptions.MissingFile) as e: |
241 | + errno = 1 |
242 | + |
243 | + if hasattr(e, 'errno'): |
244 | + errno = e.errno |
245 | + |
246 | + exit(errno, e) |
247 | + |
248 | + if resume: |
249 | + runner.load_state() |
250 | + |
251 | + runner.run() |
252 | |
253 | if __name__ == "__main__": |
254 | - main() |
255 | + main() |
256 | |
257 | === modified file 'debian/changelog' |
258 | --- debian/changelog 2012-04-11 13:22:05 +0000 |
259 | +++ debian/changelog 2012-05-09 00:33:18 +0000 |
260 | @@ -1,3 +1,828 @@ |
261 | +ubuntu-automation-test-harness (0.1ubuntu266) precise; urgency=low |
262 | + |
263 | + * vm-start runs fine on a started vm, so always run it as part of |
264 | + activecheck debcommit |
265 | + |
266 | + -- Max Brustkern <max@canonical.com> Thu, 03 May 2012 10:38:56 -0400 |
267 | + |
268 | +ubuntu-automation-test-harness (0.1ubuntu265) precise; urgency=low |
269 | + |
270 | + * Changed vm-wait to run all the time to make sure ssh is up |
271 | + |
272 | + -- Max Brustkern <max@canonical.com> Thu, 03 May 2012 09:28:44 -0400 |
273 | + |
274 | +ubuntu-automation-test-harness (0.1ubuntu264) precise; urgency=low |
275 | + |
276 | + * Removed permissions change from postinstall since it breaks SSH key |
277 | + |
278 | + -- Max Brustkern <max@canonical.com> Wed, 02 May 2012 17:51:54 -0400 |
279 | + |
280 | +ubuntu-automation-test-harness (0.1ubuntu263) precise; urgency=low |
281 | + |
282 | + * Fixed typo |
283 | + |
284 | + -- Max Brustkern <max@canonical.com> Wed, 02 May 2012 14:38:20 -0400 |
285 | + |
286 | +ubuntu-automation-test-harness (0.1ubuntu262) precise; urgency=low |
287 | + |
288 | + * Added file this time |
289 | + |
290 | + -- Max Brustkern <max@canonical.com> Wed, 02 May 2012 14:35:38 -0400 |
291 | + |
292 | +ubuntu-automation-test-harness (0.1ubuntu261) precise; urgency=low |
293 | + |
294 | + * Fixed exception import |
295 | + |
296 | + -- Max Brustkern <max@canonical.com> Wed, 02 May 2012 14:30:17 -0400 |
297 | + |
298 | +ubuntu-automation-test-harness (0.1ubuntu260) precise; urgency=low |
299 | + |
300 | + * Updated permissions to facilitate non-uath user use |
301 | + |
302 | + -- Max Brustkern <max@canonical.com> Wed, 02 May 2012 12:33:45 -0400 |
303 | + |
304 | +ubuntu-automation-test-harness (0.1ubuntu259) precise; urgency=low |
305 | + |
306 | + * Corrected exceptions |
307 | + |
308 | + -- Max Brustkern <max@canonical.com> Wed, 02 May 2012 10:29:46 -0400 |
309 | + |
310 | +ubuntu-automation-test-harness (0.1ubuntu258) precise; urgency=low |
311 | + |
312 | + * Resolved changes |
313 | + |
314 | + -- Max Brustkern <max@canonical.com> Wed, 02 May 2012 09:53:20 -0400 |
315 | + |
316 | +ubuntu-automation-test-harness (0.1ubuntu257) precise; urgency=low |
317 | + |
318 | + * Actually saved changes this time |
319 | + |
320 | + -- Max Brustkern <max@canonical.com> Wed, 02 May 2012 09:24:50 -0400 |
321 | + |
322 | +ubuntu-automation-test-harness (0.1ubuntu256) precise; urgency=low |
323 | + |
324 | + * Changed runargs to _runargs |
325 | + |
326 | + -- Max Brustkern <max@canonical.com> Wed, 02 May 2012 08:56:51 -0400 |
327 | + |
328 | +ubuntu-automation-test-harness (0.1ubuntu255) precise; urgency=low |
329 | + |
330 | + * Better handle missing tc_control files |
331 | + |
332 | + -- Joe Talbott <joe.talbott@canonical.com> Tue, 01 May 2012 17:30:58 -0400 |
333 | + |
334 | +ubuntu-automation-test-harness (0.1ubuntu254) precise; urgency=low |
335 | + |
336 | + * Updated client dependencies |
337 | + |
338 | + -- Max Brustkern <max@canonical.com> Tue, 01 May 2012 16:46:40 -0400 |
339 | + |
340 | +ubuntu-automation-test-harness (0.1ubuntu253) precise; urgency=low |
341 | + |
342 | + * Running client as root |
343 | + |
344 | + -- Max Brustkern <max@canonical.com> Tue, 01 May 2012 16:14:22 -0400 |
345 | + |
346 | +ubuntu-automation-test-harness (0.1ubuntu252) precise; urgency=low |
347 | + |
348 | + * Updated documentation a lot, got some of it into correct column |
349 | + limit; changed iso to image in test runner; added variant to test |
350 | + runner |
351 | + |
352 | + -- Max Brustkern <max@canonical.com> Tue, 01 May 2012 15:16:50 -0400 |
353 | + |
354 | +ubuntu-automation-test-harness (0.1ubuntu251) precise; urgency=low |
355 | + |
356 | + * Updated Catalog documentation |
357 | + |
358 | + -- Max Brustkern <max@canonical.com> Tue, 01 May 2012 13:18:30 -0400 |
359 | + |
360 | +ubuntu-automation-test-harness (0.1ubuntu250) precise; urgency=low |
361 | + |
362 | + * Updated documentation further and corrected classes to use private |
363 | + methods |
364 | + |
365 | + -- Max Brustkern <max@canonical.com> Tue, 01 May 2012 13:07:29 -0400 |
366 | + |
367 | +ubuntu-automation-test-harness (0.1ubuntu249) precise; urgency=low |
368 | + |
369 | + * Updated documentation further and corrected classes to use private |
370 | + methods |
371 | + |
372 | + -- Max Brustkern <max@canonical.com> Tue, 01 May 2012 13:06:44 -0400 |
373 | + |
374 | +ubuntu-automation-test-harness (0.1ubuntu248) precise; urgency=low |
375 | + |
376 | + * Updated documentation further and corrected classes to use private |
377 | + methods |
378 | + |
379 | + -- Max Brustkern <max@canonical.com> Tue, 01 May 2012 13:03:44 -0400 |
380 | + |
381 | +ubuntu-automation-test-harness (0.1ubuntu247) precise; urgency=low |
382 | + |
383 | + * Updated documentation further and corrected classes to use private |
384 | + methods |
385 | + |
386 | + -- Max Brustkern <max@canonical.com> Tue, 01 May 2012 13:00:06 -0400 |
387 | + |
388 | +ubuntu-automation-test-harness (0.1ubuntu246) precise; urgency=low |
389 | + |
390 | + * Updated documentation and moved methods around |
391 | + |
392 | + -- Max Brustkern <max@canonical.com> Tue, 01 May 2012 11:24:09 -0400 |
393 | + |
394 | +ubuntu-automation-test-harness (0.1ubuntu245) precise; urgency=low |
395 | + |
396 | + * Added exception when vm-wait times out |
397 | + |
398 | + -- Max Brustkern <max@canonical.com> Mon, 30 Apr 2012 18:50:36 -0400 |
399 | + |
400 | +ubuntu-automation-test-harness (0.1ubuntu244) precise; urgency=low |
401 | + |
402 | + * Added vm-wait check to VMToolsKVM startup |
403 | + |
404 | + -- Max Brustkern <max@canonical.com> Mon, 30 Apr 2012 17:38:10 -0400 |
405 | + |
406 | +ubuntu-automation-test-harness (0.1ubuntu243) precise; urgency=low |
407 | + |
408 | + * Made changes to update things to precise |
409 | + |
410 | + -- Max Brustkern <max@canonical.com> Fri, 27 Apr 2012 12:32:27 -0400 |
411 | + |
412 | +ubuntu-automation-test-harness (0.1ubuntu242) precise; urgency=low |
413 | + |
414 | + * Updated current example scripts and removed outdated ones |
415 | + |
416 | + -- Max Brustkern <max@canonical.com> Fri, 27 Apr 2012 09:29:30 -0400 |
417 | + |
418 | +ubuntu-automation-test-harness (0.1ubuntu241) precise; urgency=low |
419 | + |
420 | + * Corrected suggested command for no vm-tools config file |
421 | + |
422 | + -- Max Brustkern <max@canonical.com> Thu, 26 Apr 2012 17:03:07 -0400 |
423 | + |
424 | +ubuntu-automation-test-harness (0.1ubuntu240) precise; urgency=low |
425 | + |
426 | + * Corrected error handling for no hvm support |
427 | + |
428 | + -- Max Brustkern <max@canonical.com> Thu, 26 Apr 2012 16:58:36 -0400 |
429 | + |
430 | +ubuntu-automation-test-harness (0.1ubuntu239) precise; urgency=low |
431 | + |
432 | + * Updated vm-tools config to handle non-standard uath user home |
433 | + directory better |
434 | + |
435 | + -- Max Brustkern <max@canonical.com> Thu, 26 Apr 2012 16:56:26 -0400 |
436 | + |
437 | +ubuntu-automation-test-harness (0.1ubuntu238) precise; urgency=low |
438 | + |
439 | + * Fixed script to remove debug argument print |
440 | + |
441 | + -- Max Brustkern <max@canonical.com> Thu, 26 Apr 2012 16:46:58 -0400 |
442 | + |
443 | +ubuntu-automation-test-harness (0.1ubuntu237) precise; urgency=low |
444 | + |
445 | + * Updated postinst to handle non-standard uath user home directory |
446 | + better |
447 | + |
448 | + -- Max Brustkern <max@canonical.com> Thu, 26 Apr 2012 16:43:15 -0400 |
449 | + |
450 | +ubuntu-automation-test-harness (0.1ubuntu236) precise; urgency=low |
451 | + |
452 | + * Throw an exception when no vm-tools config file exists |
453 | + |
454 | + -- Max Brustkern <max@canonical.com> Thu, 26 Apr 2012 16:37:31 -0400 |
455 | + |
456 | +ubuntu-automation-test-harness (0.1ubuntu235) precise; urgency=low |
457 | + |
458 | + * Updated postinst to handle non-standard uath user home directory |
459 | + better |
460 | + |
461 | + -- Max Brustkern <max@canonical.com> Thu, 26 Apr 2012 16:18:44 -0400 |
462 | + |
463 | +ubuntu-automation-test-harness (0.1ubuntu234) precise; urgency=low |
464 | + |
465 | + * Updated script to better deal with failure to create virtual machine |
466 | + |
467 | + -- Max Brustkern <max@canonical.com> Thu, 26 Apr 2012 16:12:57 -0400 |
468 | + |
469 | +ubuntu-automation-test-harness (0.1ubuntu233) precise; urgency=low |
470 | + |
471 | + * Fixed no-destroy to no_destroy |
472 | + |
473 | + -- Max Brustkern <max@canonical.com> Thu, 26 Apr 2012 16:04:54 -0400 |
474 | + |
475 | +ubuntu-automation-test-harness (0.1ubuntu232) precise; urgency=low |
476 | + |
477 | + * Added check for no HVM support |
478 | + |
479 | + -- Max Brustkern <max@canonical.com> Thu, 26 Apr 2012 15:34:01 -0400 |
480 | + |
481 | +ubuntu-automation-test-harness (0.1ubuntu231) precise; urgency=low |
482 | + |
483 | + * Made server default install type |
484 | + |
485 | + -- Max Brustkern <max@canonical.com> Thu, 26 Apr 2012 14:58:38 -0400 |
486 | + |
487 | +ubuntu-automation-test-harness (0.1ubuntu230) precise; urgency=low |
488 | + |
489 | + * Added run_uath_tests.py master script |
490 | + |
491 | + -- Max Brustkern <max@canonical.com> Thu, 26 Apr 2012 14:52:11 -0400 |
492 | + |
493 | +ubuntu-automation-test-harness (0.1ubuntu229) precise; urgency=low |
494 | + |
495 | + * Initial draft of output processing for vm creation |
496 | + |
497 | + -- Max Brustkern <max@canonical.com> Wed, 25 Apr 2012 19:14:57 -0400 |
498 | + |
499 | +ubuntu-automation-test-harness (0.1ubuntu228) precise; urgency=low |
500 | + |
501 | + * Updated output handling |
502 | + |
503 | + -- Max Brustkern <max@canonical.com> Wed, 25 Apr 2012 16:47:53 -0400 |
504 | + |
505 | +ubuntu-automation-test-harness (0.1ubuntu227) precise; urgency=low |
506 | + |
507 | + * Removed shell=True |
508 | + |
509 | + -- Max Brustkern <max@canonical.com> Wed, 25 Apr 2012 13:13:29 -0400 |
510 | + |
511 | +ubuntu-automation-test-harness (0.1ubuntu226) precise; urgency=low |
512 | + |
513 | + * Updated package to use new vm-tools package which places all |
514 | + necessary scripts in /usr/bin |
515 | + |
516 | + -- Max Brustkern <max@canonical.com> Tue, 24 Apr 2012 17:59:21 -0400 |
517 | + |
518 | +ubuntu-automation-test-harness (0.1ubuntu225) precise; urgency=low |
519 | + |
520 | + * Removed DISPLAY clearing from .profile since vm-new has the -v |
521 | + option now |
522 | + |
523 | + -- Max Brustkern <max@canonical.com> Tue, 24 Apr 2012 17:31:33 -0400 |
524 | + |
525 | +ubuntu-automation-test-harness (0.1ubuntu224) precise; urgency=low |
526 | + |
527 | + * Added -v option to vm-new call |
528 | + |
529 | + -- Max Brustkern <max@canonical.com> Tue, 24 Apr 2012 17:27:24 -0400 |
530 | + |
531 | +ubuntu-automation-test-harness (0.1ubuntu223) precise; urgency=low |
532 | + |
533 | + * Changed exit status conditions for client |
534 | + |
535 | + -- Max Brustkern <max@canonical.com> Tue, 24 Apr 2012 16:43:45 -0400 |
536 | + |
537 | +ubuntu-automation-test-harness (0.1ubuntu222) precise; urgency=low |
538 | + |
539 | + * Added machine info to client script |
540 | + |
541 | + -- Max Brustkern <max@canonical.com> Tue, 24 Apr 2012 16:10:35 -0400 |
542 | + |
543 | +ubuntu-automation-test-harness (0.1ubuntu221) precise; urgency=low |
544 | + |
545 | + * Added machine info to client script |
546 | + |
547 | + -- Max Brustkern <max@canonical.com> Tue, 24 Apr 2012 16:10:27 -0400 |
548 | + |
549 | +ubuntu-automation-test-harness (0.1ubuntu220) oneiric; urgency=low |
550 | + |
551 | + * Adding source format files |
552 | + |
553 | + -- Max Brustkern <max@canonical.com> Mon, 23 Apr 2012 10:13:00 -0400 |
554 | + |
555 | +ubuntu-automation-test-harness (0.1ubuntu219) oneiric; urgency=low |
556 | + |
557 | + * Fixed argument order error when calling client |
558 | + |
559 | + -- Max Brustkern <max@canonical.com> Sat, 21 Apr 2012 01:05:54 -0400 |
560 | + |
561 | +ubuntu-automation-test-harness (0.1ubuntu218) oneiric; urgency=low |
562 | + |
563 | + * Changed indention in client script and added json output support |
564 | + |
565 | + -- Max Brustkern <max@canonical.com> Fri, 20 Apr 2012 17:59:23 -0400 |
566 | + |
567 | +ubuntu-automation-test-harness (0.1ubuntu217) oneiric; urgency=low |
568 | + |
569 | + * Removed sudoers file from install pending further testing |
570 | + |
571 | + -- Max Brustkern <max@canonical.com> Thu, 19 Apr 2012 17:54:20 -0400 |
572 | + |
573 | +ubuntu-automation-test-harness (0.1ubuntu216) oneiric; urgency=low |
574 | + |
575 | + * Updated example syntax |
576 | + |
577 | + -- Max Brustkern <max@canonical.com> Thu, 19 Apr 2012 17:51:39 -0400 |
578 | + |
579 | +ubuntu-automation-test-harness (0.1ubuntu215) oneiric; urgency=low |
580 | + |
581 | + * Fixed sudoers syntax |
582 | + |
583 | + -- Max Brustkern <max@canonical.com> Thu, 19 Apr 2012 17:45:37 -0400 |
584 | + |
585 | +ubuntu-automation-test-harness (0.1ubuntu214) oneiric; urgency=low |
586 | + |
587 | + * Implemented initial logging and updated example |
588 | + |
589 | + -- Max Brustkern <max@canonical.com> Thu, 19 Apr 2012 17:31:05 -0400 |
590 | + |
591 | +ubuntu-automation-test-harness (0.1ubuntu213) oneiric; urgency=low |
592 | + |
593 | + * Added sudoers file |
594 | + |
595 | + -- Max Brustkern <max@canonical.com> Thu, 19 Apr 2012 15:59:08 -0400 |
596 | + |
597 | +ubuntu-automation-test-harness (0.1ubuntu212) oneiric; urgency=low |
598 | + |
599 | + * Changed default iso type to mini since desktop seems to not run |
600 | + latecommand until a user logs in |
601 | + |
602 | + -- Max Brustkern <max@canonical.com> Wed, 18 Apr 2012 21:38:57 -0400 |
603 | + |
604 | +ubuntu-automation-test-harness (0.1ubuntu211) oneiric; urgency=low |
605 | + |
606 | + * Fixed error in test runner |
607 | + |
608 | + -- Max Brustkern <max@canonical.com> Wed, 18 Apr 2012 19:35:31 -0400 |
609 | + |
610 | +ubuntu-automation-test-harness (0.1ubuntu210) oneiric; urgency=low |
611 | + |
612 | + * Added new example script |
613 | + |
614 | + -- Max Brustkern <max@canonical.com> Wed, 18 Apr 2012 18:18:02 -0400 |
615 | + |
616 | +ubuntu-automation-test-harness (0.1ubuntu209) oneiric; urgency=low |
617 | + |
618 | + * Added timeout to example test case since it is now mandatory |
619 | + |
620 | + -- Max Brustkern <max@canonical.com> Wed, 18 Apr 2012 17:55:47 -0400 |
621 | + |
622 | +ubuntu-automation-test-harness (0.1ubuntu208) oneiric; urgency=low |
623 | + |
624 | + * Improved command handling in run function |
625 | + |
626 | + -- Max Brustkern <max@canonical.com> Wed, 18 Apr 2012 17:51:35 -0400 |
627 | + |
628 | +ubuntu-automation-test-harness (0.1ubuntu207) oneiric; urgency=low |
629 | + |
630 | + * Fixed indentation typos and added /var/log/uath directory to client |
631 | + installation |
632 | + |
633 | + -- Max Brustkern <max@canonical.com> Wed, 18 Apr 2012 17:19:06 -0400 |
634 | + |
635 | +ubuntu-automation-test-harness (0.1ubuntu206) oneiric; urgency=low |
636 | + |
637 | + * Updated example to not use precise mini iso |
638 | + |
639 | + -- Max Brustkern <max@canonical.com> Wed, 18 Apr 2012 16:06:31 -0400 |
640 | + |
641 | +ubuntu-automation-test-harness (0.1ubuntu205) oneiric; urgency=low |
642 | + |
643 | + * Updated includes and module init files |
644 | + |
645 | + -- Max Brustkern <max@canonical.com> Wed, 18 Apr 2012 16:05:52 -0400 |
646 | + |
647 | +ubuntu-automation-test-harness (0.1ubuntu204) oneiric; urgency=low |
648 | + |
649 | + * Fixed a typo and added creation of /var/log/uath to packaging |
650 | + |
651 | + -- Max Brustkern <max@canonical.com> Wed, 18 Apr 2012 14:56:37 -0400 |
652 | + |
653 | +ubuntu-automation-test-harness (0.1ubuntu203) oneiric; urgency=low |
654 | + |
655 | + * Made action requires in testcase |
656 | + |
657 | + -- Max Brustkern <max@canonical.com> Wed, 18 Apr 2012 10:54:43 -0400 |
658 | + |
659 | +ubuntu-automation-test-harness (0.1ubuntu202) oneiric; urgency=low |
660 | + |
661 | + * Cleaned up named arguments, removed env from command running, |
662 | + removed relative imports |
663 | + |
664 | + -- Max Brustkern <max@canonical.com> Wed, 18 Apr 2012 10:36:07 -0400 |
665 | + |
666 | +ubuntu-automation-test-harness (0.1ubuntu201) oneiric; urgency=low |
667 | + |
668 | + * Changed SUCCESS to PASS and FAILUE to FAIL |
669 | + |
670 | + -- Max Brustkern <max@canonical.com> Wed, 18 Apr 2012 10:16:52 -0400 |
671 | + |
672 | +ubuntu-automation-test-harness (0.1ubuntu200) oneiric; urgency=low |
673 | + |
674 | + * Made timeout for testcase mandatory |
675 | + |
676 | + -- Max Brustkern <max@canonical.com> Wed, 18 Apr 2012 09:51:43 -0400 |
677 | + |
678 | +ubuntu-automation-test-harness (0.1ubuntu199) oneiric; urgency=low |
679 | + |
680 | + * Updated indentation: Changed default spacing from 2 to 4 spaces, |
681 | + continued multiline function arguments after parentheses, spaces |
682 | + multiline lists after opening bracket, used 4 space to indent |
683 | + multiline dictionaries and tuples, 80 column limit exceed in some |
684 | + cases (may fix in another pass,) if you see anything you don't agree |
685 | + with, let me know and I'll keep it in mind in the future |
686 | + |
687 | + -- Max Brustkern <max@canonical.com> Wed, 18 Apr 2012 09:47:24 -0400 |
688 | + |
689 | +ubuntu-automation-test-harness (0.1ubuntu198) oneiric; urgency=low |
690 | + |
691 | + * Put response back in |
692 | + |
693 | + -- Max Brustkern <max@canonical.com> Tue, 17 Apr 2012 10:05:03 -0400 |
694 | + |
695 | +ubuntu-automation-test-harness (0.1ubuntu197) precise; urgency=low |
696 | + |
697 | + * Collapse client package installation and post-installation to one |
698 | + command |
699 | + |
700 | + -- Max Brustkern <max@canonical.com> Mon, 16 Apr 2012 19:50:27 -0400 |
701 | + |
702 | +ubuntu-automation-test-harness (0.1ubuntu196) precise; urgency=low |
703 | + |
704 | + * Set TinySQLiteCatalog to autocommit |
705 | + |
706 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 19:49:59 -0400 |
707 | + |
708 | +ubuntu-automation-test-harness (0.1ubuntu195) precise; urgency=low |
709 | + |
710 | + * Added batch mode to scp commands |
711 | + |
712 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 19:15:59 -0400 |
713 | + |
714 | +ubuntu-automation-test-harness (0.1ubuntu194) precise; urgency=low |
715 | + |
716 | + * Updated example and code based on testing |
717 | + |
718 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 18:18:46 -0400 |
719 | + |
720 | +ubuntu-automation-test-harness (0.1ubuntu193) precise; urgency=low |
721 | + |
722 | + * Updated example and code based on testing |
723 | + |
724 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 18:17:53 -0400 |
725 | + |
726 | +ubuntu-automation-test-harness (0.1ubuntu192) precise; urgency=low |
727 | + |
728 | + * Updated example and code based on testing |
729 | + |
730 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 17:45:10 -0400 |
731 | + |
732 | +ubuntu-automation-test-harness (0.1ubuntu191) precise; urgency=low |
733 | + |
734 | + * Cleaned up based on testing |
735 | + |
736 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 15:27:22 -0400 |
737 | + |
738 | +ubuntu-automation-test-harness (0.1ubuntu190) precise; urgency=low |
739 | + |
740 | + * Cleaned up syntax |
741 | + |
742 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 14:47:23 -0400 |
743 | + |
744 | +ubuntu-automation-test-harness (0.1ubuntu189) precise; urgency=low |
745 | + |
746 | + * Made some changes so most methods that aren't returning an object |
747 | + should be returning a boolean value and modified the example |
748 | + |
749 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 14:44:35 -0400 |
750 | + |
751 | +ubuntu-automation-test-harness (0.1ubuntu188) precise; urgency=low |
752 | + |
753 | + * Updated example and client package |
754 | + |
755 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 14:27:09 -0400 |
756 | + |
757 | +ubuntu-automation-test-harness (0.1ubuntu187) precise; urgency=low |
758 | + |
759 | + * Updated postinstall files |
760 | + |
761 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 14:17:30 -0400 |
762 | + |
763 | +ubuntu-automation-test-harness (0.1ubuntu186) precise; urgency=low |
764 | + |
765 | + * Updated example and setup vm-tools VMs to install python-yaml by |
766 | + default |
767 | + |
768 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 14:05:12 -0400 |
769 | + |
770 | +ubuntu-automation-test-harness (0.1ubuntu185) precise; urgency=low |
771 | + |
772 | + * Added example for provisioning a VM and running a test |
773 | + |
774 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 11:50:42 -0400 |
775 | + |
776 | +ubuntu-automation-test-harness (0.1ubuntu184) precise; urgency=low |
777 | + |
778 | + * Updated Makefile to create python modules properly with __init__.py |
779 | + |
780 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 11:37:00 -0400 |
781 | + |
782 | +ubuntu-automation-test-harness (0.1ubuntu183) precise; urgency=low |
783 | + |
784 | + * Changing make file to build uath module properly for client |
785 | + |
786 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 11:25:50 -0400 |
787 | + |
788 | +ubuntu-automation-test-harness (0.1ubuntu182) precise; urgency=low |
789 | + |
790 | + * Adjusting build to fix error when using pbuilder |
791 | + |
792 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 10:48:28 -0400 |
793 | + |
794 | +ubuntu-automation-test-harness (0.1ubuntu181) precise; urgency=low |
795 | + |
796 | + * Adjusting build to fix error when using pbuilder |
797 | + |
798 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 10:46:37 -0400 |
799 | + |
800 | +ubuntu-automation-test-harness (0.1ubuntu180) precise; urgency=low |
801 | + |
802 | + * Adjusting build to fix error when using pbuilder |
803 | + |
804 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 10:45:19 -0400 |
805 | + |
806 | +ubuntu-automation-test-harness (0.1ubuntu179) precise; urgency=low |
807 | + |
808 | + * Updated client packaging |
809 | + |
810 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 10:32:12 -0400 |
811 | + |
812 | +ubuntu-automation-test-harness (0.1ubuntu178) precise; urgency=low |
813 | + |
814 | + * Updating client packaging |
815 | + |
816 | + -- Max Brustkern <max@canonical.com> Fri, 13 Apr 2012 10:01:29 -0400 |
817 | + |
818 | +ubuntu-automation-test-harness (0.1ubuntu177) precise; urgency=low |
819 | + |
820 | + * Changed copy command for client examples |
821 | + |
822 | + -- Max Brustkern <max@canonical.com> Thu, 12 Apr 2012 18:01:06 -0400 |
823 | + |
824 | +ubuntu-automation-test-harness (0.1ubuntu176) precise; urgency=low |
825 | + |
826 | + * Updated examples in client deb |
827 | + |
828 | + -- Max Brustkern <max@canonical.com> Thu, 12 Apr 2012 17:57:43 -0400 |
829 | + |
830 | +ubuntu-automation-test-harness (0.1ubuntu175) precise; urgency=low |
831 | + |
832 | + * Made additional changes to downloadfiles, still broken |
833 | + |
834 | + -- Max Brustkern <max@canonical.com> Thu, 12 Apr 2012 17:43:51 -0400 |
835 | + |
836 | +ubuntu-automation-test-harness (0.1ubuntu174) precise; urgency=low |
837 | + |
838 | + * Changed client Makefile to install uath.client instead of just uath |
839 | + |
840 | + -- Max Brustkern <max@canonical.com> Thu, 12 Apr 2012 17:22:21 -0400 |
841 | + |
842 | +ubuntu-automation-test-harness (0.1ubuntu173) precise; urgency=low |
843 | + |
844 | + * Renamed copyfile to uploadfiles, began implementation of |
845 | + downloadfiles |
846 | + |
847 | + -- Max Brustkern <max@canonical.com> Thu, 12 Apr 2012 17:21:07 -0400 |
848 | + |
849 | +ubuntu-automation-test-harness (0.1ubuntu172) precise; urgency=low |
850 | + |
851 | + * Implemented installclient for VMToolsKVM |
852 | + |
853 | + -- Max Brustkern <max@canonical.com> Thu, 12 Apr 2012 16:58:39 -0400 |
854 | + |
855 | +ubuntu-automation-test-harness (0.1ubuntu171) precise; urgency=low |
856 | + |
857 | + * Add start_time and time_delta to results |
858 | + |
859 | + -- Joe Talbott <joe.talbott@canonical.com> Thu, 12 Apr 2012 16:55:48 -0400 |
860 | + |
861 | +ubuntu-automation-test-harness (0.1ubuntu170) precise; urgency=low |
862 | + |
863 | + * Update uath_howto.txt |
864 | + |
865 | + -- Joe Talbott <joe.talbott@canonical.com> Thu, 12 Apr 2012 16:38:31 -0400 |
866 | + |
867 | +ubuntu-automation-test-harness (0.1ubuntu169) precise; urgency=low |
868 | + |
869 | + * Move /var/uath to /var/lib/uath and fix client examples |
870 | + |
871 | + -- Joe Talbott <joe.talbott@canonical.com> Thu, 12 Apr 2012 16:30:09 -0400 |
872 | + |
873 | +ubuntu-automation-test-harness (0.1ubuntu168) precise; urgency=low |
874 | + |
875 | + * move client code into its own subdirectory |
876 | + |
877 | + -- Joe Talbott <joe.talbott@canonical.com> Thu, 12 Apr 2012 16:17:14 -0400 |
878 | + |
879 | +ubuntu-automation-test-harness (0.1ubuntu167) oneiric; urgency=low |
880 | + |
881 | + * Adding distribute tarball to source because buildds can't download |
882 | + it |
883 | + |
884 | + -- Max Brustkern <max@canonical.com> Thu, 12 Apr 2012 11:36:27 -0400 |
885 | + |
886 | +ubuntu-automation-test-harness (0.1ubuntu166) precise; urgency=low |
887 | + |
888 | + * Update debian/postinst for new name client.py and fix self_test.py |
889 | + to output nosetest instructions. |
890 | + |
891 | + -- Joe Talbott <joe.talbott@canonical.com> Thu, 12 Apr 2012 10:32:26 -0400 |
892 | + |
893 | +ubuntu-automation-test-harness (0.1ubuntu165) precise; urgency=low |
894 | + |
895 | + * Rename main.py to client.py and update README |
896 | + |
897 | + -- Joe Talbott <joe.talbott@canonical.com> Thu, 12 Apr 2012 10:27:36 -0400 |
898 | + |
899 | +ubuntu-automation-test-harness (0.1ubuntu164) precise; urgency=low |
900 | + |
901 | + * Cleanup /etc/rc.local handling |
902 | + |
903 | + -- Joe Talbott <joe.talbott@canonical.com> Thu, 12 Apr 2012 09:17:28 -0400 |
904 | + |
905 | +ubuntu-automation-test-harness (0.1ubuntu163) oneiric; urgency=low |
906 | + |
907 | + * Attempting to get build working in pbuilder |
908 | + |
909 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 18:45:10 -0400 |
910 | + |
911 | +ubuntu-automation-test-harness (0.1ubuntu162) oneiric; urgency=low |
912 | + |
913 | + * Attempting to get build working in pbuilder |
914 | + |
915 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 18:40:55 -0400 |
916 | + |
917 | +ubuntu-automation-test-harness (0.1ubuntu161) oneiric; urgency=low |
918 | + |
919 | + * Attempting to get build working in pbuilder |
920 | + |
921 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 18:37:45 -0400 |
922 | + |
923 | +ubuntu-automation-test-harness (0.1ubuntu160) oneiric; urgency=low |
924 | + |
925 | + * Attempting to use correct package name for build-dep |
926 | + |
927 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 18:11:56 -0400 |
928 | + |
929 | +ubuntu-automation-test-harness (0.1ubuntu159) oneiric; urgency=low |
930 | + |
931 | + * Adding debuild build-dep |
932 | + |
933 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 18:09:10 -0400 |
934 | + |
935 | +ubuntu-automation-test-harness (0.1ubuntu158) oneiric; urgency=low |
936 | + |
937 | + * Trying rule override to use python build system and Makefile |
938 | + |
939 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 18:05:50 -0400 |
940 | + |
941 | +ubuntu-automation-test-harness (0.1ubuntu157) oneiric; urgency=low |
942 | + |
943 | + * Additional changes to support inner package |
944 | + |
945 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 18:00:57 -0400 |
946 | + |
947 | +ubuntu-automation-test-harness (0.1ubuntu156) oneiric; urgency=low |
948 | + |
949 | + * Additional changes to support inner package |
950 | + |
951 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 17:50:46 -0400 |
952 | + |
953 | +ubuntu-automation-test-harness (0.1ubuntu155) oneiric; urgency=low |
954 | + |
955 | + * Made possibly final changes to inner package Makefile |
956 | + |
957 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 17:44:51 -0400 |
958 | + |
959 | +ubuntu-automation-test-harness (0.1ubuntu154) oneiric; urgency=low |
960 | + |
961 | + * Made changes to inner package Makefile |
962 | + |
963 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 17:37:56 -0400 |
964 | + |
965 | +ubuntu-automation-test-harness (0.1ubuntu153) oneiric; urgency=low |
966 | + |
967 | + * Made getroot executable |
968 | + |
969 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 17:35:25 -0400 |
970 | + |
971 | +ubuntu-automation-test-harness (0.1ubuntu152) oneiric; urgency=low |
972 | + |
973 | + * Made changes to inner package Makefile |
974 | + |
975 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 17:34:30 -0400 |
976 | + |
977 | +ubuntu-automation-test-harness (0.1ubuntu151) oneiric; urgency=low |
978 | + |
979 | + * Made changes to inner package Makefile |
980 | + |
981 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 17:32:57 -0400 |
982 | + |
983 | +ubuntu-automation-test-harness (0.1ubuntu150) oneiric; urgency=low |
984 | + |
985 | + * Added Makefile instead of Makefile~ |
986 | + |
987 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 17:28:33 -0400 |
988 | + |
989 | +ubuntu-automation-test-harness (0.1ubuntu149) oneiric; urgency=low |
990 | + |
991 | + * bzr removed file instead of just removing it |
992 | + |
993 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 17:26:51 -0400 |
994 | + |
995 | +ubuntu-automation-test-harness (0.1ubuntu148) oneiric; urgency=low |
996 | + |
997 | + * Created initial attempt at inner package |
998 | + |
999 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 17:25:53 -0400 |
1000 | + |
1001 | +ubuntu-automation-test-harness (0.1ubuntu147) oneiric; urgency=low |
1002 | + |
1003 | + * Moving back to a single package; will build the client package |
1004 | + separately from the Makefile |
1005 | + |
1006 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 17:17:13 -0400 |
1007 | + |
1008 | +ubuntu-automation-test-harness (0.1ubuntu146) precise; urgency=low |
1009 | + |
1010 | + * Treat timeouts as errors |
1011 | + |
1012 | + -- Joe Talbott <joe.talbott@canonical.com> Wed, 11 Apr 2012 16:27:31 -0400 |
1013 | + |
1014 | +ubuntu-automation-test-harness (0.1ubuntu145) oneiric; urgency=low |
1015 | + |
1016 | + * Trying different method of target-dependent override_dh_auto_build |
1017 | + |
1018 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 16:20:53 -0400 |
1019 | + |
1020 | +ubuntu-automation-test-harness (0.1ubuntu144) oneiric; urgency=low |
1021 | + |
1022 | + * Trying target-dependent override_dh_auto_build |
1023 | + |
1024 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 16:18:29 -0400 |
1025 | + |
1026 | +ubuntu-automation-test-harness (0.1ubuntu143) oneiric; urgency=low |
1027 | + |
1028 | + * Trying override_dh_auto_build again |
1029 | + |
1030 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 16:15:04 -0400 |
1031 | + |
1032 | +ubuntu-automation-test-harness (0.1ubuntu142) oneiric; urgency=low |
1033 | + |
1034 | + * Trying override_dh_auto_build again |
1035 | + |
1036 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 16:12:13 -0400 |
1037 | + |
1038 | +ubuntu-automation-test-harness (0.1ubuntu141) oneiric; urgency=low |
1039 | + |
1040 | + * Trying override_dh_auto_build |
1041 | + |
1042 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 16:09:25 -0400 |
1043 | + |
1044 | +ubuntu-automation-test-harness (0.1ubuntu140) oneiric; urgency=low |
1045 | + |
1046 | + * Trying override_dh_auto_install stanza for client package only |
1047 | + |
1048 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 15:55:34 -0400 |
1049 | + |
1050 | +ubuntu-automation-test-harness (0.1ubuntu139) oneiric; urgency=low |
1051 | + |
1052 | + * Trying override_dh_auto_install stanza |
1053 | + |
1054 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 15:44:03 -0400 |
1055 | + |
1056 | +ubuntu-automation-test-harness (0.1ubuntu138) oneiric; urgency=low |
1057 | + |
1058 | + * Attempting to add usr dir to install files |
1059 | + |
1060 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 15:39:01 -0400 |
1061 | + |
1062 | +ubuntu-automation-test-harness (0.1ubuntu137) oneiric; urgency=low |
1063 | + |
1064 | + * Retrying package split to get build differences |
1065 | + |
1066 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 15:28:49 -0400 |
1067 | + |
1068 | +ubuntu-automation-test-harness (0.1ubuntu136) oneiric; urgency=low |
1069 | + |
1070 | + * Attempting to get the package building correctly again |
1071 | + |
1072 | + -- Max Brustkern <max@canonical.com> Wed, 11 Apr 2012 15:22:27 -0400 |
1073 | + |
1074 | +ubuntu-automation-test-harness (0.1ubuntu135) precise; urgency=low |
1075 | + |
1076 | + * Add better results indication and tests |
1077 | + |
1078 | + -- Joe Talbott <joe.talbott@canonical.com> Wed, 11 Apr 2012 15:11:57 -0400 |
1079 | + |
1080 | +ubuntu-automation-test-harness (0.1ubuntu134) precise; urgency=low |
1081 | + |
1082 | + * clean up uath/self_test.py |
1083 | + |
1084 | + -- Joe Talbott <joe.talbott@canonical.com> Wed, 11 Apr 2012 11:48:14 -0400 |
1085 | + |
1086 | ubuntu-automation-test-harness (0.1ubuntu133) oneiric; urgency=low |
1087 | |
1088 | * First attempt at split binary configuration |
1089 | |
1090 | === renamed file 'debian/ubuntu-automation-test-harness.conffiles' => 'debian/conffiles' |
1091 | --- debian/ubuntu-automation-test-harness.conffiles 2012-04-11 13:22:05 +0000 |
1092 | +++ debian/conffiles 2012-05-09 00:33:18 +0000 |
1093 | @@ -1,2 +1,1 @@ |
1094 | -var/lib/uath/.profile |
1095 | var/lib/uath/.ssh/config |
1096 | |
1097 | === modified file 'debian/control' |
1098 | --- debian/control 2012-04-11 13:22:05 +0000 |
1099 | +++ debian/control 2012-05-09 00:33:18 +0000 |
1100 | @@ -3,7 +3,7 @@ |
1101 | X-Python-Version: >= 2.5 |
1102 | Priority: optional |
1103 | Maintainer: Max Brustkern <max@canonical.com> |
1104 | -Build-Depends: debhelper, python-all |
1105 | +Build-Depends: debhelper, python-all, devscripts |
1106 | Standards-Version: 3.9.2 |
1107 | Homepage: https://code.launchpad.net/ubuntu-automation-test-harness |
1108 | Vcs-Bzr: https://code.launchpad.net/ubuntu-automation-test-harness |
1109 | @@ -16,9 +16,3 @@ |
1110 | Conflicts: ubuntu-automation-test-harness-client |
1111 | Description: Ubuntu Automation Test Harness |
1112 | Automation framework for testing in Ubuntu |
1113 | - |
1114 | -Package: ubuntu-automation-test-harness-client |
1115 | -Architecture: all |
1116 | -Depends: ${python:Depends}, python-apt, python-yaml |
1117 | -Description: Ubuntu Automation Test Harness Client |
1118 | - Automation framework for testing in Ubuntu, client portion |
1119 | |
1120 | === renamed file 'debian/ubuntu-automation-test-harness.dirs' => 'debian/dirs' |
1121 | --- debian/ubuntu-automation-test-harness.dirs 2012-04-11 13:22:05 +0000 |
1122 | +++ debian/dirs 2012-05-09 00:33:18 +0000 |
1123 | @@ -1,2 +1,3 @@ |
1124 | var/cache/uath/iso/cache |
1125 | var/lib/uath/vm |
1126 | +var/log/uath |
1127 | |
1128 | === renamed file 'debian/ubuntu-automation-test-harness.install' => 'debian/install' |
1129 | --- debian/ubuntu-automation-test-harness.install 2012-04-11 13:22:05 +0000 |
1130 | +++ debian/install 2012-05-09 00:33:18 +0000 |
1131 | @@ -1,4 +1,6 @@ |
1132 | uqt-vm-tools.conf etc/uath |
1133 | -.profile var/lib/uath |
1134 | +shell-profile etc/uath |
1135 | config var/lib/uath/.ssh |
1136 | examples usr/share/uath |
1137 | +ubuntu-automation-test-harness-client_*_all.deb usr/share/uath |
1138 | +uath/client/examples/master.run usr/share/uath/examples |
1139 | |
1140 | === renamed file 'debian/ubuntu-automation-test-harness.postinst' => 'debian/postinst' |
1141 | --- debian/ubuntu-automation-test-harness.postinst 2012-04-11 13:22:05 +0000 |
1142 | +++ debian/postinst 2012-05-09 00:33:18 +0000 |
1143 | @@ -5,7 +5,7 @@ |
1144 | # Sane defaults: |
1145 | |
1146 | [ -z "$SERVER_HOME" ] && SERVER_HOME=/var/lib/uath |
1147 | - [ -z "$SERVER_DIRS" ] && SERVER_DIRS="$SERVER_HOME /var/cache/uath" |
1148 | + [ -z "$SERVER_DIRS" ] && SERVER_DIRS="$SERVER_HOME /var/cache/uath /var/log/uath" |
1149 | [ -z "$SERVER_USER" ] && SERVER_USER=uath |
1150 | [ -z "$SERVER_NAME" ] && SERVER_NAME="UATH" |
1151 | [ -z "$SERVER_GROUP" ] && SERVER_GROUP=uath |
1152 | @@ -24,8 +24,8 @@ |
1153 | do |
1154 | if ! dpkg-statoverride --list $DIR >/dev/null |
1155 | then |
1156 | - chown -R $SERVER_USER:adm $DIR |
1157 | - chmod 2755 $DIR |
1158 | + chown -R $SERVER_USER:$SERVER_GROUP $DIR |
1159 | + chmod 2775 $DIR |
1160 | fi |
1161 | done |
1162 | } |
1163 | @@ -50,18 +50,19 @@ |
1164 | |
1165 | usersetup |
1166 | |
1167 | -ln -sf /etc/uath/uqt-vm-tools.conf /var/lib/uath/.uqt-vm-tools.conf |
1168 | -ln -sf /usr/share/pyshared/uath/main.py /usr/bin/uath |
1169 | +ln -sf /etc/uath/uqt-vm-tools.conf ~uath/.uqt-vm-tools.conf |
1170 | +ln -sf /etc/uath/shell-profile ~uath/.profile |
1171 | +ln -sf /usr/share/pyshared/uath/client.py /usr/bin/uath |
1172 | |
1173 | -if ! ([ -f "/var/lib/uath/.ssh/uath" ] && [ -f "/var/lib/uath/.ssh/uath.pub" ]) |
1174 | +if ! ([ -f ~uath/.ssh/uath ] && [ -f ~uath/.ssh/uath.pub ]) |
1175 | then |
1176 | echo "Generating RSA keypair for uath user..." 1>&2 |
1177 | su - uath -c 'ssh-keygen -t rsa -f ~/.ssh/uath -q -P ""' |
1178 | fi |
1179 | |
1180 | -if ! [ -f "/var/lib/uath/.ssh/known_hosts" ] |
1181 | +if ! [ -f ~uath/.ssh/known_hosts ] |
1182 | then |
1183 | - su - uath -c "touch /var/lib/uath/.ssh/known_hosts" |
1184 | + su - uath -c "touch ~/.ssh/known_hosts" |
1185 | fi |
1186 | |
1187 | dnssetup |
1188 | |
1189 | === renamed file 'debian/ubuntu-automation-test-harness.postrm' => 'debian/postrm' |
1190 | === modified file 'debian/rules' |
1191 | --- debian/rules 2012-04-02 19:33:08 +0000 |
1192 | +++ debian/rules 2012-05-09 00:33:18 +0000 |
1193 | @@ -1,4 +1,8 @@ |
1194 | #!/usr/bin/make -f |
1195 | |
1196 | %: |
1197 | - dh $@ --buildsystem=python_distutils --with=python2 |
1198 | + dh $@ --buildsystem=python_distutils --with=python2 |
1199 | + |
1200 | +override_dh_auto_build: |
1201 | + dh_auto_build |
1202 | + make |
1203 | |
1204 | === added directory 'debian/source' |
1205 | === added file 'debian/source/format' |
1206 | --- debian/source/format 1970-01-01 00:00:00 +0000 |
1207 | +++ debian/source/format 2012-05-09 00:33:18 +0000 |
1208 | @@ -0,0 +1,1 @@ |
1209 | +3.0 (quilt) |
1210 | |
1211 | === removed file 'debian/ubuntu-automation-test-harness-client.install' |
1212 | --- debian/ubuntu-automation-test-harness-client.install 2012-04-11 13:22:05 +0000 |
1213 | +++ debian/ubuntu-automation-test-harness-client.install 1970-01-01 00:00:00 +0000 |
1214 | @@ -1,1 +0,0 @@ |
1215 | -examples usr/share/uath |
1216 | |
1217 | === added file 'distribute-0.6.25.tar.gz' |
1218 | Binary files distribute-0.6.25.tar.gz 1970-01-01 00:00:00 +0000 and distribute-0.6.25.tar.gz 2012-05-09 00:33:18 +0000 differ |
1219 | === removed file 'examples/provision.py' |
1220 | --- examples/provision.py 2012-04-09 20:46:17 +0000 |
1221 | +++ examples/provision.py 1970-01-01 00:00:00 +0000 |
1222 | @@ -1,45 +0,0 @@ |
1223 | -#!/usr/bin/python |
1224 | - |
1225 | -from uath import UATHException |
1226 | -import uath.provisioning |
1227 | -import os.path |
1228 | - |
1229 | -try: |
1230 | - # Create a catalog to manage VMs |
1231 | - # Default catalog configuration attempts to prevent multiple scripts from requesting the same resources |
1232 | - # We use a non-default db and lockfile to work around this for the example |
1233 | - # For normal use, omit the db and lockfile parameters |
1234 | - catalog = uath.provisioning.catalog.TinySQLiteCatalog(db = os.path.expanduser('~/.uath-example-sqlite-catalog'), lockfile = os.path.expanduser('~/.uath-example-catalog')) |
1235 | - |
1236 | - # Request a VM from the catalog |
1237 | - # arch can be i386 or amd64/x86_64 |
1238 | - # installtype is used for automatic iso selection (desktop, server, mini, alternate) |
1239 | - # prefix is used by vm-tools to allow opportunities on multiple simultaneous VMs |
1240 | - # default is uath, we use a non-default option to avoid collision with existing VMs |
1241 | - # machineid is appened to prefix to give unique names |
1242 | - # only mini currently works for precise, all should work on release |
1243 | - # series can be any supported series, should be all lowercase |
1244 | - # no arguments will give i386, desktop, precise, which currently fails to download, but should work on release |
1245 | - # For normal use, omit prefix, and the other parameters are optional |
1246 | - machine = catalog.request(arch='i386', installtype='mini', prefix='uath-example', series='precise') |
1247 | - |
1248 | - # Run a command |
1249 | - # This will check if the machine is provisioned, and provision it if it is not using vm-new |
1250 | - # Provisioning may take a while, and currently times out at a max of around 90 minutes |
1251 | - # After provision(), the machine will be stopped, so it will be automatically started before running the command |
1252 | - # machine.provision() and machine.run() could be run manually before this, but that is not required |
1253 | - machine.run('/sbin/ifconfig') |
1254 | - |
1255 | - # Destroy the VM |
1256 | - # This uses vm-remove to do a hard stop and then delete the machine |
1257 | - machine.destroy() |
1258 | - |
1259 | - # Remove the machine from the catalog and delete it |
1260 | - catalog.destroy(machine.machineid) |
1261 | - del machine |
1262 | - |
1263 | - # Delete the catalog |
1264 | - catalog.delete() |
1265 | - |
1266 | -except UATHException as error: |
1267 | - print('Exception: ' + error.msg) |
1268 | |
1269 | === added file 'examples/run_test_vm.py' |
1270 | --- examples/run_test_vm.py 1970-01-01 00:00:00 +0000 |
1271 | +++ examples/run_test_vm.py 2012-05-09 00:33:18 +0000 |
1272 | @@ -0,0 +1,105 @@ |
1273 | +#!/usr/bin/python |
1274 | + |
1275 | +import argparse |
1276 | +import getpass |
1277 | +import grp |
1278 | +import logging |
1279 | +import os |
1280 | +import sys |
1281 | +import time |
1282 | +import urllib |
1283 | + |
1284 | +from uath import UATHException |
1285 | +import uath.provisioning |
1286 | + |
1287 | +def check_user_group(group='uath'): |
1288 | + return grp.getgrnam(group)[2] in os.getgroups() |
1289 | + |
1290 | + |
1291 | +def get_parser(): |
1292 | + parser = argparse.ArgumentParser(description='Create a virtual machine and run a UATH runlist there.', epilog="For example:\n\t%(prog)s -s oneiric -t server -a i386 /usr/share/uath/examples/master.run 'http://people.canonical.com/~max/max_test.run'", formatter_class=argparse.RawDescriptionHelpFormatter) |
1293 | + parser.add_argument('runlists', metavar='runlist', nargs='+', help='URLs of runlist files to run') |
1294 | + parser.add_argument('-s', '--series', choices=('hardy', 'lucid', 'natty', 'oneiric', 'precise', 'quantal'), default='oneiric', help='Series to use for VM creation') |
1295 | + parser.add_argument('-t', '--type', choices=('desktop', 'server', 'mini', 'alternate'), default='server', help='Install type to use for VM creation') |
1296 | + parser.add_argument('-a', '--arch', choices=('i386', 'amd64'), default='i386', help='Architecture to use for VM creation') |
1297 | + parser.add_argument('-n', '--no-destroy', action='store_true', help='Preserve VM after tests have run') |
1298 | + parser.add_argument('-d', '--debug', action='store_true', help='Enable debug logging') |
1299 | + parser.add_argument('-j', '--json', action='store_true', help='Enable json logging (Default is YAML)') |
1300 | + return parser |
1301 | + |
1302 | + |
1303 | +def run_test_vm(args = None): |
1304 | + if not check_user_group(): |
1305 | + sys.stderr.write("Error: you are not in the UATH group.\n") |
1306 | + sys.stderr.write("If you believe you have properly configured your user account for UATH use, try:\n") |
1307 | + sys.stderr.write(' sudo usermod -a -G uath ' + getpass.getuser() + "\n") |
1308 | + sys.stderr.write("Otherwise, please run this script as the uath user, i.e.:\n") |
1309 | + argv = list(sys.argv) |
1310 | + argv[0] = os.path.abspath(__file__) |
1311 | + sys.stderr.write(" sudo su - uath -c '" + ' '.join(argv) + "'\n") |
1312 | + sys.exit(3) |
1313 | + |
1314 | + if args is None: |
1315 | + args = get_parser().parse_args() |
1316 | + |
1317 | + locallogs = [] |
1318 | + machine = None |
1319 | + |
1320 | + if args.json: |
1321 | + extraopts = ' -f json' |
1322 | + suffix = '.json' |
1323 | + else: |
1324 | + extraopts = '' |
1325 | + suffix = '.yaml' |
1326 | + |
1327 | + try: |
1328 | + catalog = uath.provisioning.catalog.TinySQLiteCatalog() |
1329 | + machine = catalog.request(arch=args.arch, debug=args.debug, installtype=args.type, new=True, dlpercentincrement=10, series=args.series) |
1330 | + for logger in [machine.logger] + machine.logger.handlers: |
1331 | + if logger.level > logging.INFO: |
1332 | + logger.setLevel(logging.INFO) |
1333 | + machine.installclient() |
1334 | + for runlist in args.runlists: |
1335 | + locallist = urllib.urlretrieve(runlist)[0] |
1336 | + listname = os.path.split(runlist)[1] |
1337 | + remotelog = os.path.normpath('/var/log/uath/' + machine.name + '_' + listname + '_tmp') |
1338 | + locallog = os.path.normpath('/var/log/uath/' + machine.name + '_' + listname + time.strftime('_%Y-%m-%d_%H-%m-%S', time.gmtime()) + suffix) |
1339 | + try: |
1340 | + machine.uploadfiles([locallist], os.path.normpath('/tmp')) |
1341 | + machine.run('uath' + extraopts + ' -r /tmp/' + os.path.split(locallist)[1] + ' -o ' + remotelog, root=True) |
1342 | + machine.downloadfiles([remotelog], locallog) |
1343 | + print('Test log copied to ' + locallog) |
1344 | + locallogs.append(locallog) |
1345 | + except UATHException as error: |
1346 | + print('Failed to run test: ' + error.msg) |
1347 | + finally: |
1348 | + try: |
1349 | + pass |
1350 | + machine.run('rm -f ' + remotelog) |
1351 | + except UATHException as error: |
1352 | + print('Failed to cleanup test: ' + error.msg) |
1353 | + |
1354 | + except UATHException as error: |
1355 | + print('Exception: ' + error.msg) |
1356 | + |
1357 | + finally: |
1358 | + urllib.urlcleanup() |
1359 | + if not args.no_destroy and machine is not None: |
1360 | + try: |
1361 | + machine.destroy() |
1362 | + except UATHException as error: |
1363 | + print('Failed to destroy machine: ' + error.msg) |
1364 | + finally: |
1365 | + try: |
1366 | + catalog.destroy(machine.machineid) |
1367 | + except UATHException as error: |
1368 | + print('Failed to update catalog: ' + error.msg) |
1369 | + finally: |
1370 | + del machine |
1371 | + if len(locallogs) != 0: |
1372 | + print('Test logs copied to the following files:') |
1373 | + print("\t" + "\n\t".join(locallogs)) |
1374 | + |
1375 | + |
1376 | +if __name__ == '__main__': |
1377 | + run_test_vm() |
1378 | |
1379 | === added file 'examples/run_uath_tests.py' |
1380 | --- examples/run_uath_tests.py 1970-01-01 00:00:00 +0000 |
1381 | +++ examples/run_uath_tests.py 2012-05-09 00:33:18 +0000 |
1382 | @@ -0,0 +1,71 @@ |
1383 | +#!/usr/bin/python |
1384 | + |
1385 | +import argparse |
1386 | +import getpass |
1387 | +import grp |
1388 | +import logging |
1389 | +import os |
1390 | +import sys |
1391 | +import time |
1392 | +import urllib |
1393 | + |
1394 | +from uath import UATHException |
1395 | +import uath.provisioning |
1396 | + |
1397 | +def check_user_group(group='uath'): |
1398 | + return grp.getgrnam(group)[2] in os.getgroups() |
1399 | + |
1400 | + |
1401 | +def get_parser(): |
1402 | + parser = argparse.ArgumentParser(description='Provision a machine and run one or more UATH runlists there.', epilog="For example:\n\t%(prog)s -s oneiric -t server -a i386 /usr/share/uath/examples/master.run 'http://people.canonical.com/~max/max_test.run'", formatter_class=argparse.RawDescriptionHelpFormatter) |
1403 | + parser.add_argument('runlists', metavar='runlist', nargs='+', help='URLs of runlist files to run') |
1404 | + parser.add_argument('-m', '--machinetype', choices=('physical', 'virtual'), default='virtual', help='Type of machine to provision') |
1405 | + parser.add_argument('-i', '--image', type=argparse.FileType('r'), help='Image/ISO file to use for installation') |
1406 | + parser.add_argument('-k', '--kernel', type=argparse.FileType('r'), help='Kernel file to use for installation') |
1407 | + parser.add_argument('-r', '--initrd', type=argparse.FileType('r'), help='InitRD file to use for installation') |
1408 | + parser.add_argument('-p', '--preseed', type=argparse.FileType('r'), help='Preseed file to use for installation') |
1409 | + parser.add_argument('-s', '--series', choices=('hardy', 'lucid', 'natty', 'oneiric', 'precise', 'quantal'), default='oneiric', help='Series to use for installation') |
1410 | + parser.add_argument('-t', '--type', choices=('desktop', 'server', 'mini', 'alternate'), default='server', help='Install type to use for installation') |
1411 | + parser.add_argument('-a', '--arch', choices=('i386', 'amd64', 'arm'), default='i386', help='Architecture to use for installation') |
1412 | + parser.add_argument('-v', '--variant', help='Variant of architecture, i.e., armel, armhf') |
1413 | + parser.add_argument('-n', '--no-destroy', action='store_true', help='Preserve machine after tests have run') |
1414 | + parser.add_argument('-d', '--debug', action='store_true', help='Enable debug logging') |
1415 | + parser.add_argument('-j', '--json', action='store_true', help='Enable json logging (Default is YAML)') |
1416 | + return parser |
1417 | + |
1418 | + |
1419 | +def run_uath_tests(args=None): |
1420 | + if not check_user_group(): |
1421 | + sys.stderr.write("Error: you are not in the UATH group.\n") |
1422 | + sys.stderr.write("If you believe you have properly configured your user account for UATH use, try:\n") |
1423 | + sys.stderr.write(' sudo usermod -a -G uath ' + getpass.getuser() + "\n") |
1424 | + sys.stderr.write("Otherwise, please run this script as the uath user, i.e.:\n") |
1425 | + argv = list(sys.argv) |
1426 | + argv[0] = os.path.abspath(__file__) |
1427 | + sys.stderr.write(" sudo su - uath -c '" + ' '.join(argv) + "'\n") |
1428 | + sys.exit(3) |
1429 | + |
1430 | + if args is None: |
1431 | + args = get_parser().parse_args() |
1432 | + |
1433 | + if args.machinetype == 'physical': |
1434 | + sys.stderr.write("Physical machine provisioning is not supported in this release.\n") |
1435 | + sys.stderr.write("Please check the roadmap, as it will be included in a future version.\n") |
1436 | + sys.exit(4) |
1437 | + |
1438 | + for option in [args.image, args.kernel, args.initrd, args.preseed]: |
1439 | + if option is not None: |
1440 | + sys.stderr.write("Custom VM provisioning is not supported in this release.\n") |
1441 | + sys.stderr.write("Please check the roadmap, as it will be included in a future version.\n") |
1442 | + sys.exit(4) |
1443 | + |
1444 | + if 'arm' in args.arch: |
1445 | + sys.stderr.write("ARM support is not included in this release.\n") |
1446 | + sys.stderr.write("Please check the roadmap, as it will be included in a future version.\n") |
1447 | + sys.exit(4) |
1448 | + |
1449 | + from run_test_vm import run_test_vm |
1450 | + run_test_vm(args=args) |
1451 | + |
1452 | +if __name__ == '__main__': |
1453 | + run_uath_tests() |
1454 | |
1455 | === added file 'shell-profile' |
1456 | --- shell-profile 1970-01-01 00:00:00 +0000 |
1457 | +++ shell-profile 2012-05-09 00:33:18 +0000 |
1458 | @@ -0,0 +1,1 @@ |
1459 | +umask 002 |
1460 | |
1461 | === added directory 'uath/client' |
1462 | === renamed file 'uath/README' => 'uath/client/README' |
1463 | --- uath/README 2012-04-10 20:49:15 +0000 |
1464 | +++ uath/client/README 2012-05-09 00:33:18 +0000 |
1465 | @@ -79,4 +79,4 @@ |
1466 | [command: <testcase command>] |
1467 | [tc_setup: <testcase setup command>] |
1468 | [tc_cleanup: <testcase cleanup command>] |
1469 | -[reboot: {always, success, never}] # defaults to 'never' |
1470 | +[reboot: {always, pass, never}] # defaults to 'never' |
1471 | |
1472 | === added file 'uath/client/__init__.py' |
1473 | === renamed file 'uath/common.py' => 'uath/client/common.py' |
1474 | --- uath/common.py 2012-04-04 21:52:30 +0000 |
1475 | +++ uath/client/common.py 2012-05-09 00:33:18 +0000 |
1476 | @@ -2,19 +2,20 @@ |
1477 | Common methods. |
1478 | """ |
1479 | |
1480 | +import datetime |
1481 | import os |
1482 | import signal |
1483 | import subprocess |
1484 | import yaml |
1485 | |
1486 | |
1487 | -SUCCESS=0 |
1488 | -FAILURE=1 |
1489 | +PASS=0 |
1490 | +FAIL=1 |
1491 | ERROR_TIMEOUT=-9 |
1492 | |
1493 | CONFIG = { |
1494 | - 'DEBUG': False, |
1495 | - 'TEST_DIR': '/var/uath', |
1496 | + 'DEBUG': False, |
1497 | + 'TEST_DIR': '/var/lib/uath', |
1498 | } |
1499 | |
1500 | # Default TestSuite filenames |
1501 | @@ -22,134 +23,149 @@ |
1502 | DEFAULT_TSCONTROL='ts_control' |
1503 | |
1504 | MASTER_RUNLIST="master.run" |
1505 | -UATH_DIR="/var/uath" |
1506 | +UATH_DIR=CONFIG['TEST_DIR'] |
1507 | DEFAULT_STATE_FILE=os.path.join(UATH_DIR, "state.yaml") |
1508 | |
1509 | +RETURN_CODES={'PASS': 0, 'ERROR': -1, 'FAIL': 1} |
1510 | + |
1511 | def do_nothing(obj=None): |
1512 | - """ |
1513 | - Do nothing method used a placeholder for save_state_callbacks. |
1514 | - """ |
1515 | - pass |
1516 | + """ |
1517 | + Do nothing method used a placeholder for save_state_callbacks. |
1518 | + """ |
1519 | + pass |
1520 | |
1521 | # Inspired by: |
1522 | # http://stackoverflow.com/a/3326559 |
1523 | def run_cmd(command, cwd=None, timeout=0): |
1524 | |
1525 | - if command is None: |
1526 | - return |
1527 | - |
1528 | - class TimeoutAlarm(Exception): |
1529 | - pass |
1530 | - |
1531 | - def alarm_handler(signum, frame): |
1532 | - raise TimeoutAlarm |
1533 | - |
1534 | - p = subprocess.Popen(command, shell=True, cwd=cwd, stdout=subprocess.PIPE, |
1535 | - stderr=subprocess.PIPE) |
1536 | - |
1537 | - if timeout != 0: |
1538 | - signal.signal(signal.SIGALRM, alarm_handler) |
1539 | - signal.alarm(timeout) |
1540 | - |
1541 | - try: |
1542 | - stdout, stderr = p.communicate() |
1543 | + if command is None: |
1544 | + return |
1545 | + |
1546 | + class TimeoutAlarm(Exception): |
1547 | + pass |
1548 | + |
1549 | + def alarm_handler(signum, frame): |
1550 | + raise TimeoutAlarm |
1551 | + |
1552 | + start_time = datetime.datetime.now() |
1553 | + p = subprocess.Popen(command, shell=True, cwd=cwd, stdout=subprocess.PIPE, |
1554 | + stderr=subprocess.PIPE) |
1555 | + |
1556 | if timeout != 0: |
1557 | - signal.alarm(0) |
1558 | - except TimeoutAlarm: |
1559 | - pids = [p.pid] |
1560 | - # Kill p's children too. |
1561 | - pids.extend(get_process_children(p.pid)) |
1562 | - |
1563 | - for pid in pids: |
1564 | - # process might have died before getting to this line |
1565 | - # so wrap to avoid OSError: no such process |
1566 | - try: |
1567 | - os.kill(pid, signal.SIGKILL) |
1568 | - except OSError: |
1569 | - pass |
1570 | - return make_result((command, ERROR_TIMEOUT, '', '')) |
1571 | - |
1572 | - return make_result((command, p.returncode, stdout, stderr)) |
1573 | + signal.signal(signal.SIGALRM, alarm_handler) |
1574 | + signal.alarm(timeout) |
1575 | + |
1576 | + try: |
1577 | + stdout, stderr = p.communicate() |
1578 | + if timeout != 0: |
1579 | + signal.alarm(0) |
1580 | + except TimeoutAlarm: |
1581 | + pids = [p.pid] |
1582 | + # Kill p's children too. |
1583 | + pids.extend(get_process_children(p.pid)) |
1584 | + |
1585 | + for pid in pids: |
1586 | + # process might have died before getting to this line |
1587 | + # so wrap to avoid OSError: no such process |
1588 | + try: |
1589 | + os.kill(pid, signal.SIGKILL) |
1590 | + except OSError: |
1591 | + pass |
1592 | + |
1593 | + time_delta = datetime.datetime.now() - start_time |
1594 | + |
1595 | + return make_result(command=command, |
1596 | + retcode=ERROR_TIMEOUT, |
1597 | + start_time=str(start_time), |
1598 | + time_delta=str(time_delta), |
1599 | + ) |
1600 | + |
1601 | + time_delta = datetime.datetime.now() - start_time |
1602 | + |
1603 | + return make_result(command=command, |
1604 | + retcode=p.returncode, |
1605 | + stdout=stdout, |
1606 | + stderr=stderr, |
1607 | + start_time=str(start_time), |
1608 | + time_delta=str(time_delta), |
1609 | + ) |
1610 | |
1611 | # TODO: it might make sense to have a result object that keeps track of |
1612 | -# test success, failure, error and serializes it's data. |
1613 | -def make_result(result): |
1614 | - """ |
1615 | - Turn a tuple of (returncode, stdout, stderr) into a dict. |
1616 | - """ |
1617 | - |
1618 | - # If result is not a tuple of size 3 return a blank result |
1619 | - if len(result) != 4: |
1620 | - result = (None, None, '', '') |
1621 | - |
1622 | - res = { |
1623 | - 'command': result[0], |
1624 | - 'returncode': result[1], |
1625 | - 'stdout': result[2], |
1626 | - 'stderr': result[3] |
1627 | - } |
1628 | - |
1629 | - return res |
1630 | +# test pass, fail, error and serializes it's data. |
1631 | +def make_result(command, retcode, stdout='', stderr='', start_time='', time_delta=''): |
1632 | + """ |
1633 | + Make a result dictionary. |
1634 | + """ |
1635 | + res = { |
1636 | + 'command': command, |
1637 | + 'returncode': retcode, |
1638 | + 'stdout': stdout, |
1639 | + 'stderr': stderr, |
1640 | + 'start_time': start_time, |
1641 | + 'time_delta': time_delta, |
1642 | + } |
1643 | + |
1644 | + return res |
1645 | |
1646 | def get_process_children(pid): |
1647 | - p = subprocess.Popen('ps --no-headers -o pid --ppid %d' % pid, shell=True, |
1648 | - stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
1649 | - stdout, stderr = p.communicate() |
1650 | + p = subprocess.Popen('ps --no-headers -o pid --ppid %d' % pid, shell=True, |
1651 | + stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
1652 | + stdout, stderr = p.communicate() |
1653 | |
1654 | - return [int(pid) for pid in stdout.split()] |
1655 | + return [int(pid) for pid in stdout.split()] |
1656 | |
1657 | |
1658 | def run_cmd_depricated(command): |
1659 | - """ |
1660 | - Runs 'command' using a shell, so be careful. |
1661 | - """ |
1662 | - |
1663 | - if command is None: |
1664 | - return |
1665 | - |
1666 | - rc = subprocess.call(command, shell=True) |
1667 | - if rc != 0: |
1668 | - raise Exception("command failed: %s: %d" % (command, rc)) |
1669 | + """ |
1670 | + Runs 'command' using a shell, so be careful. |
1671 | + """ |
1672 | + |
1673 | + if command is None: |
1674 | + return |
1675 | + |
1676 | + rc = subprocess.call(command, shell=True) |
1677 | + if rc != 0: |
1678 | + raise Exception("command failed: %s: %d" % (command, rc)) |
1679 | |
1680 | def parse_control_file(filename, required=None, optional=None): |
1681 | - """ |
1682 | - Parse a control file and only include the items in required and optional. |
1683 | + """ |
1684 | + Parse a control file and only include the items in required and optional. |
1685 | |
1686 | - If any required items are missing an exception is raised. Optional items |
1687 | - that are not in the control file are set to None. |
1688 | - """ |
1689 | - fp = open(filename, 'r') |
1690 | - |
1691 | - data = yaml.load(fp) |
1692 | - |
1693 | - fp.close() |
1694 | - |
1695 | - control_data = {} |
1696 | - |
1697 | - for item in required: |
1698 | - control_data[item] = data[item] |
1699 | - |
1700 | - for item in optional: |
1701 | - control_data[item] = data.get(item) |
1702 | - |
1703 | - if not required and not optional: |
1704 | - control_data = data |
1705 | - |
1706 | - return control_data |
1707 | + If any required items are missing an exception is raised. Optional items |
1708 | + that are not in the control file are set to None. |
1709 | + """ |
1710 | + fp = open(filename, 'r') |
1711 | + |
1712 | + data = yaml.load(fp) |
1713 | + |
1714 | + fp.close() |
1715 | + |
1716 | + control_data = {} |
1717 | + |
1718 | + for item in required: |
1719 | + control_data[item] = data[item] |
1720 | + |
1721 | + for item in optional: |
1722 | + control_data[item] = data.get(item) |
1723 | + |
1724 | + if not required and not optional: |
1725 | + control_data = data |
1726 | + |
1727 | + return control_data |
1728 | |
1729 | def debug_print(data, force=False): |
1730 | - """ |
1731 | - Print debugging information if CONFIG['DEBUG'] is defined and True |
1732 | - """ |
1733 | - try: |
1734 | - debug = CONFIG['DEBUG'] |
1735 | - except NameError: |
1736 | - debug = False |
1737 | - |
1738 | - debug = debug or force |
1739 | - |
1740 | - if debug: |
1741 | - print "DEBUG:", data |
1742 | - |
1743 | - # return True if we printed some output |
1744 | - return debug |
1745 | + """ |
1746 | + Print debugging information if CONFIG['DEBUG'] is defined and True |
1747 | + """ |
1748 | + try: |
1749 | + debug = CONFIG['DEBUG'] |
1750 | + except NameError: |
1751 | + debug = False |
1752 | + |
1753 | + debug = debug or force |
1754 | + |
1755 | + if debug: |
1756 | + print "DEBUG:", data |
1757 | + |
1758 | + # return True if we printed some output |
1759 | + return debug |
1760 | |
1761 | === renamed directory 'uath/examples' => 'uath/client/examples' |
1762 | === modified file 'uath/client/examples/master.run' |
1763 | --- uath/examples/master.run 2012-04-10 20:42:15 +0000 |
1764 | +++ uath/client/examples/master.run 2012-05-09 00:33:18 +0000 |
1765 | @@ -1,3 +1,3 @@ |
1766 | --- |
1767 | - name: uath_tests |
1768 | - fetch_cmd: rm -fr uath_tests && cp -r /usr/share/uath/examples/uath_tests . |
1769 | + fetch_cmd: rm -fr uath_tests && cp -r /usr/share/uath/client/examples/uath_tests . |
1770 | |
1771 | === modified file 'uath/client/examples/uath_tests/test_one/tc_control' |
1772 | --- uath/examples/uath_tests/test_one/tc_control 2012-04-10 20:23:50 +0000 |
1773 | +++ uath/client/examples/uath_tests/test_one/tc_control 2012-05-09 00:33:18 +0000 |
1774 | @@ -7,7 +7,7 @@ |
1775 | 1. Expected result 1 |
1776 | 2. Expected result 2 |
1777 | type: userland |
1778 | -#timeout: 200 |
1779 | +timeout: 200 |
1780 | build_cmd: echo "building for test_one" |
1781 | tc_setup: echo "setup for test_one" |
1782 | tc_cleanup: echo "cleanup for test_one" |
1783 | |
1784 | === added file 'uath/client/exceptions.py' |
1785 | --- uath/client/exceptions.py 1970-01-01 00:00:00 +0000 |
1786 | +++ uath/client/exceptions.py 2012-05-09 00:33:18 +0000 |
1787 | @@ -0,0 +1,7 @@ |
1788 | +#!/usr/bin/python |
1789 | + |
1790 | +class BadDir(Exception): |
1791 | + pass |
1792 | + |
1793 | +class MissingFile(Exception): |
1794 | + pass |
1795 | |
1796 | === renamed file 'uath/result.py' => 'uath/client/result.py' |
1797 | --- uath/result.py 2012-04-06 20:40:47 +0000 |
1798 | +++ uath/client/result.py 2012-05-09 00:33:18 +0000 |
1799 | @@ -1,87 +1,110 @@ |
1800 | +import json |
1801 | import sys |
1802 | import yaml |
1803 | -import datetime |
1804 | + |
1805 | +from common import debug_print |
1806 | |
1807 | RESULT_FIELD_COUNT = 4 |
1808 | |
1809 | class Result(object): |
1810 | - """ |
1811 | - Result collection class. |
1812 | - """ |
1813 | - def __init__(self, name=None): |
1814 | - self.results = [] |
1815 | - self.status = 'SUCCESS' |
1816 | - self.name = name |
1817 | - |
1818 | - def add_result(self, result): |
1819 | - """ |
1820 | - Add a result to the object. |
1821 | - |
1822 | - Note: 'result' is expected to be a dictionary like this: |
1823 | - { |
1824 | - 'command': '', |
1825 | - 'returncode': 0, |
1826 | - 'stdout': '', |
1827 | - 'stderr': '', |
1828 | - } |
1829 | - |
1830 | - """ |
1831 | - if result is None: |
1832 | - return |
1833 | - |
1834 | - result['date'] = str(datetime.datetime.now()) |
1835 | - self.results.append(result) |
1836 | - |
1837 | - if result['returncode'] == 1 and self.status != 'ERROR': |
1838 | - self.status = 'FAILURE' |
1839 | - |
1840 | - if result['returncode'] not in [0, 1]: |
1841 | - self.status = 'ERROR' |
1842 | - |
1843 | - def result(self, verbose=False): |
1844 | - """ |
1845 | - Output a text based result. |
1846 | - """ |
1847 | - sep = '-'*70 |
1848 | - |
1849 | - print sep |
1850 | - |
1851 | - if self.name: |
1852 | - print self.name |
1853 | - print sep |
1854 | - |
1855 | - for result in self.results: |
1856 | - print "command: %s" % result['command'] |
1857 | - print "returned: %d" % result['returncode'] |
1858 | - |
1859 | - if self.status != 'SUCCESS' or verbose and result['stdout'] != '': |
1860 | - print "stdout: " |
1861 | - print result['stdout'] |
1862 | - |
1863 | - if self.status != 'SUCCESS' or verbose and result['stderr'] != '': |
1864 | - print "stderr: " |
1865 | - print result['stderr'] |
1866 | - |
1867 | |
1868 | - self.clear_results() |
1869 | - |
1870 | - def clear_results(self): |
1871 | - # reset the list of results so this object can be reused. |
1872 | - self.results = [] |
1873 | - |
1874 | - # reset the status, this should be safe since the only places that |
1875 | - # should be calling clear_results() is testcase.run(), testsuite.run(), |
1876 | - # and runner after attempting to fetch the testsuite. |
1877 | - self.status = 'SUCCESS' |
1878 | - |
1879 | - def count_results(self): |
1880 | - return len(self.results) |
1881 | + """ |
1882 | + Result collection class. |
1883 | + """ |
1884 | + def __init__(self, name=None): |
1885 | + self.results = [] |
1886 | + self.status = 'PASS' |
1887 | + self.name = name |
1888 | + |
1889 | + def add_result(self, result): |
1890 | + """ |
1891 | + Add a result to the object. |
1892 | + |
1893 | + Note: 'result' is expected to be a dictionary like this: |
1894 | + { |
1895 | + 'command': '', |
1896 | + 'returncode': 0, |
1897 | + 'stdout': '', |
1898 | + 'stderr': '', |
1899 | + 'start_time': '', |
1900 | + 'time_delta': '', |
1901 | + } |
1902 | + |
1903 | + """ |
1904 | + if result is None: |
1905 | + return |
1906 | + |
1907 | + self.results.append(result) |
1908 | + |
1909 | + if result['returncode'] == 1 and self.status != 'ERROR': |
1910 | + self.status = 'FAIL' |
1911 | + |
1912 | + if result['returncode'] not in [0, 1]: |
1913 | + self.status = 'ERROR' |
1914 | + |
1915 | + def result(self, verbose=False): |
1916 | + """ |
1917 | + Output a text based result. |
1918 | + """ |
1919 | + status = self.status |
1920 | + sep = '-'*70 |
1921 | + |
1922 | + print sep |
1923 | + |
1924 | + if self.name: |
1925 | + print self.name |
1926 | + print sep |
1927 | + |
1928 | + for result in self.results: |
1929 | + print "command: %s" % result['command'] |
1930 | + print "returned: %d" % result['returncode'] |
1931 | + print "started: %s" % result['start_time'] |
1932 | + print "runtime: %s" % result['time_delta'] |
1933 | + |
1934 | + if self.status != 'PASS' or verbose and result['stdout'] != '': |
1935 | + print "stdout: " |
1936 | + print result['stdout'] |
1937 | + |
1938 | + if self.status != 'PASS' or verbose and result['stderr'] != '': |
1939 | + print "stderr: " |
1940 | + print result['stderr'] |
1941 | + |
1942 | |
1943 | + self.clear_results() |
1944 | + |
1945 | + return status |
1946 | + |
1947 | + def clear_results(self): |
1948 | + # reset the list of results so this object can be reused. |
1949 | + self.results = [] |
1950 | + |
1951 | + # reset the status, this should be safe since the only places that |
1952 | + # should be calling clear_results() is testcase.run(), testsuite.run(), |
1953 | + # and runner after attempting to fetch the testsuite. |
1954 | + self.status = 'PASS' |
1955 | + |
1956 | + def count_results(self): |
1957 | + return len(self.results) |
1958 | |
1959 | class ResultYAML(Result): |
1960 | - def result(self, verbose=False): |
1961 | - |
1962 | - if self.results: |
1963 | - print "---" |
1964 | - yaml.dump(self.results, sys.stdout, default_flow_style=False) |
1965 | - |
1966 | - self.clear_results() |
1967 | + def result(self, verbose=False): |
1968 | + |
1969 | + if self.results: |
1970 | + print "---" |
1971 | + yaml.dump(self.results, sys.stdout, default_flow_style=False) |
1972 | + |
1973 | + status = self.status |
1974 | + self.clear_results() |
1975 | + |
1976 | + return status |
1977 | + |
1978 | +class ResultJSON(Result): |
1979 | + def result(self, verbose=False): |
1980 | + |
1981 | + if self.results: |
1982 | + json.dump(self.results, sys.stdout, indent=4) |
1983 | + |
1984 | + status = self.status |
1985 | + self.clear_results() |
1986 | + |
1987 | + return status |
1988 | + |
1989 | |
1990 | === renamed file 'uath/runner.py' => 'uath/client/runner.py' |
1991 | --- uath/runner.py 2012-04-10 15:46:12 +0000 |
1992 | +++ uath/client/runner.py 2012-05-09 00:33:18 +0000 |
1993 | @@ -10,280 +10,318 @@ |
1994 | import yaml |
1995 | |
1996 | from common import MASTER_RUNLIST, UATH_DIR, DEFAULT_STATE_FILE |
1997 | +from common import RETURN_CODES |
1998 | |
1999 | RC_LOCAL='/etc/rc.local' |
2000 | RC_LOCAL_BACKUP='%s-uath.bak' % RC_LOCAL |
2001 | |
2002 | rc_local_content="""#!/bin/sh |
2003 | |
2004 | -cd /home/josepht/bazaar/uath-dev || exit 1 |
2005 | -python main.py --resume --append -o /var/uath/uath.out |
2006 | +/usr/bin/uath --resume --append -o /var/lib/uath/uath.out |
2007 | """ |
2008 | |
2009 | class Runner(object): |
2010 | - """ |
2011 | - The main runner. |
2012 | - |
2013 | - Parses a master runlist, builds a list of TestSuites, and runs them. |
2014 | - """ |
2015 | - |
2016 | - status = "NOTRUN" |
2017 | - uath_exec_path = '/usr/bin/uath' |
2018 | - |
2019 | - def __init__(self, runlist=MASTER_RUNLIST, result_class=Result, |
2020 | - testdir=UATH_DIR, state_agent=None, resume=False): |
2021 | - |
2022 | - self.master_runlist = runlist |
2023 | - self.testdir = testdir |
2024 | - self.suites = [] |
2025 | - self.result = result_class() |
2026 | - self.result_class = result_class |
2027 | - self.state_file = DEFAULT_STATE_FILE |
2028 | - self.fetched_suites = {} |
2029 | - |
2030 | - self.state_agent = state_agent or StateAgentYAML() |
2031 | - |
2032 | - # Cleanup the state file if this is supposed to be a fresh run. |
2033 | - if not resume: |
2034 | - self.state_agent.clean() |
2035 | - else: |
2036 | - self.reset_rc_local() |
2037 | - |
2038 | - try: |
2039 | - os.chdir(self.testdir) |
2040 | - except OSError as e: |
2041 | - raise exceptions.BadDir(str(e)) |
2042 | - |
2043 | - # needs to be run prior to process_master_runlist since that's where |
2044 | - # test suites are fetched. |
2045 | - self.fetched_suites = self.get_fetched_suites() |
2046 | - |
2047 | - self.process_master_runlist() |
2048 | - |
2049 | - def backup_rc_local(self): |
2050 | - """ |
2051 | - Backup /etc/rc.local if it exists. |
2052 | - """ |
2053 | - |
2054 | - cmd = None |
2055 | - |
2056 | - if os.path.exists(RC_LOCAL): |
2057 | - cmd = ['mv', RC_LOCAL, RC_LOCAL_BACKUP] |
2058 | - |
2059 | - if cmd: |
2060 | - subprocess.call(cmd) |
2061 | - |
2062 | - def reset_rc_local(self): |
2063 | - """ |
2064 | - Restore original /etc/rc.local if there is a backup otherwise remove it. |
2065 | - """ |
2066 | - cmd = None |
2067 | - |
2068 | - if os.path.exists(RC_LOCAL_BACKUP): |
2069 | - cmd = ['mv', RC_LOCAL_BACKUP, RC_LOCAL] |
2070 | - elif os.path.exists(RC_LOCAL): |
2071 | - cmd = ['rm', RC_LOCAL] |
2072 | - |
2073 | - if cmd: |
2074 | - subprocess.call(cmd) |
2075 | - |
2076 | - def run(self): |
2077 | - """ |
2078 | - Run the test suites we've parsed. |
2079 | - """ |
2080 | - |
2081 | - # Return value to indicate whether processing of a Runner should |
2082 | - # continue. This is to avoid a shutdown race on reboot cases. |
2083 | - keep_going = True |
2084 | - |
2085 | - self.status = "RUN" |
2086 | - for suite in self.suites: |
2087 | - # Start in self.testdir. |
2088 | - os.chdir(self.testdir) |
2089 | - |
2090 | - if not suite.is_done(): |
2091 | - keep_going = suite.run() |
2092 | - if not keep_going: |
2093 | - return keep_going |
2094 | - |
2095 | - self.status = "DONE" |
2096 | - self.save_state() |
2097 | - |
2098 | - return keep_going |
2099 | - |
2100 | - def add_suite(self, suite): |
2101 | - self.suites.append(suite) |
2102 | - |
2103 | - def get_fetched_suites(self): |
2104 | - """ |
2105 | - Return a list of fetched suites from the state_agent. |
2106 | - """ |
2107 | - state = self.state_agent.load_state() |
2108 | - |
2109 | - fetched_suites = [] |
2110 | + """ |
2111 | + The main runner. |
2112 | + |
2113 | + Parses a master runlist, builds a list of TestSuites, and runs them. |
2114 | + """ |
2115 | + |
2116 | + status = "NOTRUN" |
2117 | + uath_exec_path = '/usr/bin/uath' |
2118 | + |
2119 | + def __init__(self, runlist=MASTER_RUNLIST, result_class=Result, |
2120 | + testdir=UATH_DIR, state_agent=None, resume=False): |
2121 | + |
2122 | + self.master_runlist = runlist |
2123 | + self.testdir = testdir |
2124 | + self.suites = [] |
2125 | + self.result = result_class() |
2126 | + self.result_class = result_class |
2127 | + self.state_file = DEFAULT_STATE_FILE |
2128 | + self.fetched_suites = {} |
2129 | + |
2130 | + self.errors = 0 |
2131 | + self.passes = 0 |
2132 | + self.failures = 0 |
2133 | + self.fetch_errors = 0 |
2134 | + |
2135 | + self.state_agent = state_agent or StateAgentYAML() |
2136 | + |
2137 | + # Cleanup the state file if this is supposed to be a fresh run. |
2138 | + if not resume: |
2139 | + self.state_agent.clean() |
2140 | + else: |
2141 | + self.reset_rc_local() |
2142 | + |
2143 | + try: |
2144 | + os.chdir(self.testdir) |
2145 | + except OSError as e: |
2146 | + raise exceptions.BadDir(str(e)) |
2147 | + |
2148 | + # needs to be run prior to process_master_runlist since that's where |
2149 | + # test suites are fetched. |
2150 | + self.fetched_suites = self.get_fetched_suites() |
2151 | + |
2152 | + self.process_master_runlist() |
2153 | + |
2154 | + def backup_rc_local(self): |
2155 | + """ |
2156 | + Backup /etc/rc.local if it exists. |
2157 | + """ |
2158 | + |
2159 | + # Ignore permission denied errors since we only care if a reboot is |
2160 | + # pending whether or not we can write to RC_LOCAL(_BACKUP). |
2161 | + try: |
2162 | + os.rename(RC_LOCAL, RC_LOCAL_BACKUP) |
2163 | + except OSError as e: |
2164 | + if e.errno in [13]: |
2165 | + pass |
2166 | + |
2167 | + def reset_rc_local(self): |
2168 | + """ |
2169 | + Restore original /etc/rc.local if there is a backup otherwise remove it. |
2170 | + """ |
2171 | + |
2172 | + # Ignore permission denied errors since we only care if a reboot is |
2173 | + # pending whether or not we can write to RC_LOCAL(_BACKUP). |
2174 | + try: |
2175 | + if os.path.exists(RC_LOCAL_BACKUP): |
2176 | + os.rename(RC_LOCAL_BACKUP, RC_LOCAL) |
2177 | + elif os.path.exists(RC_LOCAL): |
2178 | + os.remove(RC_LOCAL) |
2179 | + except OSError as e: |
2180 | + if e.errno in [13]: |
2181 | + pass |
2182 | + |
2183 | + def setup_rc_local(self, rc_local=RC_LOCAL): |
2184 | + """ |
2185 | + Setup /etc/rc.local to kick-off a --resume on successful boot. |
2186 | + """ |
2187 | + |
2188 | + self.rc_local_content = rc_local_content |
2189 | + |
2190 | + fp = open(rc_local, 'w') |
2191 | + fp.write(self.rc_local_content) |
2192 | + fp.close() |
2193 | + |
2194 | + os.chmod(rc_local, stat.S_IRWXU | stat.S_IROTH | stat.S_IRGRP) |
2195 | + |
2196 | + def run(self): |
2197 | + """ |
2198 | + Run the test suites we've parsed. |
2199 | + """ |
2200 | + |
2201 | + # Return value to indicate whether processing of a Runner should |
2202 | + # continue. This is to avoid a shutdown race on reboot cases. |
2203 | + keep_going = True |
2204 | + |
2205 | + self.status = "RUN" |
2206 | + for suite in self.suites: |
2207 | + # Start in self.testdir. |
2208 | + os.chdir(self.testdir) |
2209 | + |
2210 | + if not suite.is_done(): |
2211 | + keep_going = suite.run() |
2212 | + if not keep_going: |
2213 | + return self.returncode() |
2214 | + |
2215 | + self.errors += suite.errors |
2216 | + self.passes += suite.passes |
2217 | + self.failures += suite.failures |
2218 | + |
2219 | + self.status = "DONE" |
2220 | + self.save_state() |
2221 | + |
2222 | + return self.returncode() |
2223 | + |
2224 | + def add_suite(self, suite): |
2225 | + self.suites.append(suite) |
2226 | + |
2227 | + def get_fetched_suites(self): |
2228 | + """ |
2229 | + Return a list of fetched suites from the state_agent. |
2230 | + """ |
2231 | + state = self.state_agent.load_state() |
2232 | + |
2233 | + fetched_suites = [] |
2234 | |
2235 | - if state and 'fetched_suites' in state: |
2236 | - fetched_suites = state['fetched_suites'] |
2237 | - |
2238 | - return fetched_suites |
2239 | - |
2240 | - def load_state(self): |
2241 | - state = self.state_agent.load_state() |
2242 | - |
2243 | - self.master_runlist = state['master_runlist'] |
2244 | - self.status = state['status'] |
2245 | - self.result_class = state['result_class'] |
2246 | - |
2247 | - self.suites = [] |
2248 | - for state_suite in state['suites']: |
2249 | - suite = TestSuite(name=state_suite['name'], |
2250 | - path=self.testdir, |
2251 | - result_class=self.result_class, |
2252 | - _save_state_callback=self.save_state, |
2253 | - _reboot_callback=self.reboot) |
2254 | - suite.load_state(state_suite) |
2255 | - self.suites.append(suite) |
2256 | - |
2257 | - self.fetched_suites = state['fetched_suites'] |
2258 | - |
2259 | - def save_state(self): |
2260 | - """ |
2261 | - Save the list of tests we are to run and whether we've run them. |
2262 | - """ |
2263 | - |
2264 | - state = { |
2265 | - 'master_runlist': self.master_runlist, |
2266 | - 'status': self.status, |
2267 | - 'result_class': self.result_class, |
2268 | - 'suites': [], |
2269 | - 'fetched_suites': self.fetched_suites, |
2270 | - } |
2271 | - |
2272 | - for suite in self.suites: |
2273 | - state['suites'].append(suite.save_state()) |
2274 | - |
2275 | - # hand the state dictionary off to the StateAgent to process |
2276 | - self.state_agent.save_state(state) |
2277 | - |
2278 | - return state |
2279 | - |
2280 | - def count_suites(self): |
2281 | - return len(self.suites) |
2282 | - |
2283 | - def count_tests(self): |
2284 | - tests = 0 |
2285 | - |
2286 | - for suite in self.suites: |
2287 | - tests += suite.count_tests() |
2288 | - |
2289 | - return tests |
2290 | - |
2291 | - def process_master_runlist(self): |
2292 | - """ |
2293 | - Parse a master runlist building a list of suites from the parsed data. |
2294 | - """ |
2295 | - |
2296 | - try: |
2297 | - fp = open(self.master_runlist, 'r') |
2298 | - data = yaml.load(fp) |
2299 | - fp.close() |
2300 | - except IOError as e: |
2301 | - raise exceptions.MissingFile(str(e)) |
2302 | - |
2303 | - seen = [] |
2304 | - |
2305 | - for suite in data: |
2306 | - fetch_cmd = suite['fetch_cmd'] |
2307 | - |
2308 | - name = suite['name'] |
2309 | - includes = suite.get('include_tests') |
2310 | - excludes = suite.get('exclude_tests') |
2311 | - |
2312 | - suite_runlist = suite.get('runlist', DEFAULT_TSLIST) |
2313 | - |
2314 | - if name in seen: |
2315 | - raise Exception("%s duplicated in runlist" % name) |
2316 | - |
2317 | - # Fetch the testsuite. |
2318 | - |
2319 | - if not os.path.exists(name): |
2320 | - os.mkdir(name) |
2321 | - |
2322 | - if name not in self.fetched_suites: |
2323 | - self.result.add_result(run_cmd(fetch_cmd, cwd=name)) |
2324 | - self.fetched_suites.append(name) |
2325 | - |
2326 | - # If fetch_cmd failed move on to the next testsuite. |
2327 | - if self.result.status != 'SUCCESS': |
2328 | - self.result.result() |
2329 | - continue |
2330 | - |
2331 | - # Create a TestSuite |
2332 | - # TODO: pass master.run overrides into the test suite. |
2333 | - s = TestSuite(name=name, runlist_file=suite_runlist, |
2334 | - includes=includes, excludes=excludes, result=self.result, |
2335 | - path=self.testdir, _save_state_callback=self.save_state, |
2336 | - _reboot_callback=self.reboot) |
2337 | - self.add_suite(s) |
2338 | - |
2339 | - self.result.result() |
2340 | - |
2341 | - def get_next_suite(self): |
2342 | - """ |
2343 | - Return the next suite to be run. |
2344 | - |
2345 | - Mainly used for debugging. |
2346 | - """ |
2347 | - |
2348 | - suite = None |
2349 | - |
2350 | - for s in self.suites: |
2351 | - if not s.is_done(): |
2352 | - suite = s |
2353 | - break |
2354 | - |
2355 | - return suite |
2356 | - |
2357 | - def get_next_test(self): |
2358 | - """ |
2359 | - Return the next test to be run. |
2360 | - |
2361 | - Mainly used for debugging. |
2362 | - """ |
2363 | - return self.get_next_suite().get_next_test() |
2364 | - |
2365 | - def setup_rc_local(self, rc_local='/etc/rc.local'): |
2366 | - """ |
2367 | - Setup /etc/rc.local to kick-off a --resume on successful boot. |
2368 | - """ |
2369 | - # temporary override while developing/testing |
2370 | - self.uath_exec_path = '/home/josepht/bazaar/uath-dev' |
2371 | - self.rc_local_content = """#!/bin/sh\n\ncd %s || exit 1\npython main.py --resume >>/tmp/uath.out 2>&1 \n""" % self.uath_exec_path |
2372 | - self.rc_local_content = """#!/bin/sh\n\necho "I'd be resuming here... >>/var/uath/uath.out 2>&1\n""" |
2373 | - |
2374 | - self.rc_local_content = rc_local_content |
2375 | - |
2376 | - fp = open(rc_local, 'w') |
2377 | - fp.write(self.rc_local_content) |
2378 | - fp.close() |
2379 | - |
2380 | - os.chmod(rc_local, stat.S_IRWXU | stat.S_IROTH | stat.S_IRGRP) |
2381 | - |
2382 | - def reboot(self): |
2383 | - """ |
2384 | - Reboot the machine. |
2385 | - |
2386 | - Save state, setup /etc/rc.local, and shutdown. |
2387 | - """ |
2388 | - |
2389 | - self.result.result() |
2390 | - |
2391 | - self.save_state() |
2392 | - |
2393 | - self.backup_rc_local() |
2394 | - self.setup_rc_local() |
2395 | - |
2396 | - import subprocess |
2397 | - subprocess.call(['shutdown', '-r', 'now']) |
2398 | - |
2399 | - # End of execution |
2400 | + if state and 'fetched_suites' in state: |
2401 | + fetched_suites = state['fetched_suites'] |
2402 | + |
2403 | + return fetched_suites |
2404 | + |
2405 | + def load_state(self): |
2406 | + state = self.state_agent.load_state() |
2407 | + |
2408 | + self.master_runlist = state['master_runlist'] |
2409 | + self.status = state['status'] |
2410 | + self.result_class = state['result_class'] |
2411 | + |
2412 | + self.suites = [] |
2413 | + for state_suite in state['suites']: |
2414 | + suite = TestSuite(name=state_suite['name'], |
2415 | + path=self.testdir, |
2416 | + result_class=self.result_class, |
2417 | + _save_state_callback=self.save_state, |
2418 | + _reboot_callback=self.reboot) |
2419 | + suite.load_state(state_suite) |
2420 | + self.suites.append(suite) |
2421 | + |
2422 | + self.fetched_suites = state['fetched_suites'] |
2423 | + |
2424 | + def save_state(self): |
2425 | + """ |
2426 | + Save the list of tests we are to run and whether we've run them. |
2427 | + """ |
2428 | + |
2429 | + state = { |
2430 | + 'master_runlist': self.master_runlist, |
2431 | + 'status': self.status, |
2432 | + 'passes': self.passes, |
2433 | + 'failures': self.failures, |
2434 | + 'errors': self.errors, |
2435 | + 'fetch_errors': self.fetch_errors, |
2436 | + 'result_class': self.result_class, |
2437 | + 'suites': [], |
2438 | + 'fetched_suites': self.fetched_suites, |
2439 | + } |
2440 | + |
2441 | + for suite in self.suites: |
2442 | + state['suites'].append(suite.save_state()) |
2443 | + |
2444 | + # hand the state dictionary off to the StateAgent to process |
2445 | + self.state_agent.save_state(state) |
2446 | + |
2447 | + return state |
2448 | + |
2449 | + def count_suites(self): |
2450 | + return len(self.suites) |
2451 | + |
2452 | + def count_tests(self): |
2453 | + tests = 0 |
2454 | + |
2455 | + for suite in self.suites: |
2456 | + tests += suite.count_tests() |
2457 | + |
2458 | + return tests |
2459 | + |
2460 | + def process_master_runlist(self): |
2461 | + """ |
2462 | + Parse a master runlist building a list of suites from the parsed data. |
2463 | + """ |
2464 | + |
2465 | + try: |
2466 | + fp = open(self.master_runlist, 'r') |
2467 | + data = yaml.load(fp) |
2468 | + fp.close() |
2469 | + except IOError as e: |
2470 | + raise exceptions.MissingFile(str(e)) |
2471 | + |
2472 | + seen = [] |
2473 | + |
2474 | + for suite in data: |
2475 | + fetch_cmd = suite['fetch_cmd'] |
2476 | + |
2477 | + name = suite['name'] |
2478 | + includes = suite.get('include_tests') |
2479 | + excludes = suite.get('exclude_tests') |
2480 | + |
2481 | + suite_runlist = suite.get('runlist', DEFAULT_TSLIST) |
2482 | + |
2483 | + if name in seen: |
2484 | + raise Exception("%s duplicated in runlist" % name) |
2485 | + |
2486 | + # Fetch the testsuite. |
2487 | + |
2488 | + if not os.path.exists(name): |
2489 | + os.mkdir(name) |
2490 | + |
2491 | + if name not in self.fetched_suites: |
2492 | + self.result.add_result(run_cmd(fetch_cmd, cwd=name)) |
2493 | + self.fetched_suites.append(name) |
2494 | + |
2495 | + # If fetch_cmd failed move on to the next testsuite. |
2496 | + if self.result.status != 'PASS': |
2497 | + self.result.result() |
2498 | + self.fetch_errors += 1 |
2499 | + continue |
2500 | + |
2501 | + # Create a TestSuite |
2502 | + # TODO: pass master.run overrides into the test suite. |
2503 | + s = TestSuite(name=name, runlist_file=suite_runlist, |
2504 | + includes=includes, excludes=excludes, result=self.result, |
2505 | + path=self.testdir, _save_state_callback=self.save_state, |
2506 | + _reboot_callback=self.reboot) |
2507 | + self.add_suite(s) |
2508 | + |
2509 | + self.result.result() |
2510 | + |
2511 | + def get_next_suite(self): |
2512 | + """ |
2513 | + Return the next suite to be run. |
2514 | + |
2515 | + Mainly used for debugging. |
2516 | + """ |
2517 | + |
2518 | + suite = None |
2519 | + |
2520 | + for s in self.suites: |
2521 | + if not s.is_done(): |
2522 | + suite = s |
2523 | + break |
2524 | + |
2525 | + return suite |
2526 | + |
2527 | + def get_next_test(self): |
2528 | + """ |
2529 | + Return the next test to be run. |
2530 | + |
2531 | + Mainly used for debugging. |
2532 | + """ |
2533 | + return self.get_next_suite().get_next_test() |
2534 | + |
2535 | + def reboot(self): |
2536 | + """ |
2537 | + Reboot the machine. |
2538 | + |
2539 | + Save state, setup /etc/rc.local, and shutdown. |
2540 | + """ |
2541 | + |
2542 | + self.result.result() |
2543 | + |
2544 | + self.save_state() |
2545 | + |
2546 | + self.backup_rc_local() |
2547 | + self.setup_rc_local() |
2548 | + |
2549 | + import subprocess |
2550 | + subprocess.call(['shutdown', '-r', 'now']) |
2551 | + |
2552 | + # End of execution |
2553 | + |
2554 | + def returncode(self): |
2555 | +# if self.errors > 0 or self.fetch_errors: |
2556 | +# In the code review, we determined that errors at the suite level should produce a non-zero exit status for the overall process |
2557 | + if self.fetch_errors > 0: |
2558 | + return RETURN_CODES['ERROR'] |
2559 | + elif self.failures > 0: |
2560 | + return RETURN_CODES['FAIL'] |
2561 | + else: |
2562 | + return RETURN_CODES['PASS'] |
2563 | + |
2564 | + def report(self): |
2565 | + tests_run = self.passes + self.errors + self.failures |
2566 | + |
2567 | + output = "total: %d, passes: %d, failure: %d, error: %d" % (tests_run, self.passes, self.failures, self.errors + self.fetch_errors) |
2568 | + |
2569 | +# if self.errors > 0 or self.fetch_errors: |
2570 | +# In the code review, we determined that errors at the suite level should produce a non-zero exit status for the overall process |
2571 | + if self.fetch_errors > 0: |
2572 | + result = "ERROR" |
2573 | + elif self.failures > 0: |
2574 | + result = "FAIL" |
2575 | + else: |
2576 | + result = "PASS" |
2577 | + |
2578 | + return "%s - %s" % (result, output) |
2579 | |
2580 | === renamed file 'uath/self_test.py' => 'uath/client/self_test.py' |
2581 | --- uath/self_test.py 2012-04-10 15:46:12 +0000 |
2582 | +++ uath/client/self_test.py 2012-05-09 00:33:18 +0000 |
2583 | @@ -4,6 +4,7 @@ |
2584 | from state_agent import StateAgent, StateAgentYAML |
2585 | |
2586 | from common import UATH_DIR, MASTER_RUNLIST |
2587 | +from common import RETURN_CODES |
2588 | |
2589 | import exceptions |
2590 | import os |
2591 | @@ -17,385 +18,444 @@ |
2592 | master_runlist_content="""# uath/self_test.py master runlist |
2593 | # needed for uath/self_test.py runs |
2594 | - name: examples |
2595 | - fetch_cmd: rsync -a /home/josepht/bazaar/uath-dev/uath/examples/ . |
2596 | + fetch_cmd: rsync -a /usr/share/uath/client/examples/examples . |
2597 | - name: uath_tests |
2598 | - fetch_cmd: git clone /home/josepht/git/uath_tests.git || sh -c 'cd uath_tests && git pull' |
2599 | + fetch_cmd: rsync -a /usr/share/uath/client/examples/uath_tests . |
2600 | - name: uath_tests_sample |
2601 | - fetch_cmd: git clone /home/josepht/git/uath_tests_sample.git || sh -c 'cd uath_tests_sample && git pull' |
2602 | + fetch_cmd: rsync -a /usr/share/uath/client/examples/uath_tests_sample . |
2603 | """ |
2604 | |
2605 | partial_state_file_content="""master_runlist: master.run |
2606 | status: RUN |
2607 | -result_class: !!python/name:uath.result.ResultYAML '' |
2608 | +result_class: !!python/name:uath.client.result.ResultYAML '' |
2609 | fetched_suites: |
2610 | - uath_tests |
2611 | - uath_tests_sample |
2612 | suites: |
2613 | - build_cmd: null |
2614 | - name: uath_tests |
2615 | - status: INPROGRESS |
2616 | - tests: |
2617 | - - build_cmd: echo "building for test_one" |
2618 | - command: python test_one.py |
2619 | - description: A first sample test case. |
2620 | - name: test_one |
2621 | - path: uath_tests/test_one |
2622 | - status: DONE |
2623 | - tc_cleanup: echo "cleanup for test_one" |
2624 | - tc_setup: echo "setup for test_one" |
2625 | - timeout: 3 |
2626 | - type: userland |
2627 | - - build_cmd: echo "building for test_two" |
2628 | - command: python test_two.py |
2629 | - description: A second sample test case. |
2630 | - name: test_two |
2631 | - path: uath_tests/test_two |
2632 | - status: SETUP |
2633 | - tc_cleanup: echo "cleanup for test_two" |
2634 | - tc_setup: echo "setup for test_two" |
2635 | - timeout: 300 |
2636 | - type: userland |
2637 | - timeout: 0 |
2638 | - ts_cleanup: null |
2639 | - ts_setup: null |
2640 | + name: uath_tests |
2641 | + status: INPROGRESS |
2642 | + tests: |
2643 | + - build_cmd: echo "building for test_one" |
2644 | + command: python test_one.py |
2645 | + description: A first sample test case. |
2646 | + name: test_one |
2647 | + path: uath_tests/test_one |
2648 | + status: DONE |
2649 | + tc_cleanup: echo "cleanup for test_one" |
2650 | + tc_setup: echo "setup for test_one" |
2651 | + timeout: 3 |
2652 | + type: userland |
2653 | + - build_cmd: echo "building for test_two" |
2654 | + command: python test_two.py |
2655 | + description: A second sample test case. |
2656 | + name: test_two |
2657 | + path: uath_tests/test_two |
2658 | + status: SETUP |
2659 | + tc_cleanup: echo "cleanup for test_two" |
2660 | + tc_setup: echo "setup for test_two" |
2661 | + timeout: 300 |
2662 | + type: userland |
2663 | + timeout: 0 |
2664 | + ts_cleanup: null |
2665 | + ts_setup: null |
2666 | - build_cmd: null |
2667 | - name: uath_tests_sample |
2668 | - status: DONE |
2669 | - tests: |
2670 | - - build_cmd: echo "building for sample_one" |
2671 | - command: python sample.py |
2672 | - description: A sample test case. |
2673 | - name: sample_one |
2674 | - path: uath_tests_sample/sample_one |
2675 | + name: uath_tests_sample |
2676 | status: DONE |
2677 | - tc_cleanup: echo "cleanup for sample_one" |
2678 | - tc_setup: echo "setup for sample_one" |
2679 | - timeout: 200 |
2680 | - type: userland |
2681 | - timeout: 0 |
2682 | - ts_cleanup: null |
2683 | - ts_setup: null |
2684 | + tests: |
2685 | + - build_cmd: echo "building for sample_one" |
2686 | + command: python sample.py |
2687 | + description: A sample test case. |
2688 | + name: sample_one |
2689 | + path: uath_tests_sample/sample_one |
2690 | + status: DONE |
2691 | + tc_cleanup: echo "cleanup for sample_one" |
2692 | + tc_setup: echo "setup for sample_one" |
2693 | + timeout: 200 |
2694 | + type: userland |
2695 | + timeout: 0 |
2696 | + ts_cleanup: null |
2697 | + ts_setup: null |
2698 | """ |
2699 | master_runlist = os.path.join(UATH_DIR, MASTER_RUNLIST) |
2700 | master_runlist_bak = master_runlist + ".bak" |
2701 | |
2702 | def setUp(): |
2703 | - """ |
2704 | - Set up a master.run file that will copy our 'examples' directory to the |
2705 | - testing directory (usually /var/uath). |
2706 | - """ |
2707 | - |
2708 | - # save a copy of the master runlist |
2709 | - subprocess.call(['mv', master_runlist, master_runlist_bak]) |
2710 | - |
2711 | - fp = open(master_runlist, 'w') |
2712 | - fp.write(master_runlist_content) |
2713 | - fp.close() |
2714 | + """ |
2715 | + Set up a master.run file that will copy our 'examples' directory to the |
2716 | + testing directory (usually /var/lib/uath). |
2717 | + """ |
2718 | + |
2719 | + # save a copy of the master runlist |
2720 | + if os.path.exists(master_runlist): |
2721 | + subprocess.call(['mv', master_runlist, master_runlist_bak]) |
2722 | + |
2723 | + fp = open(master_runlist, 'w') |
2724 | + fp.write(master_runlist_content) |
2725 | + fp.close() |
2726 | |
2727 | def tearDown(): |
2728 | - """ |
2729 | - Cleanup after ourselves. |
2730 | - """ |
2731 | + """ |
2732 | + Cleanup after ourselves. |
2733 | + """ |
2734 | |
2735 | - # restore the copy of the master runlist |
2736 | - subprocess.call(['mv', master_runlist_bak, master_runlist]) |
2737 | + # restore the copy of the master runlist |
2738 | + if os.path.exists(master_runlist_bak): |
2739 | + subprocess.call(['mv', master_runlist_bak, master_runlist]) |
2740 | + else: |
2741 | + os.remove(master_runlist) |
2742 | |
2743 | class TestTestSuite(unittest.TestCase): |
2744 | - """ |
2745 | - Tests for the TestSuite class. |
2746 | - """ |
2747 | - def setUp(self): |
2748 | - """ |
2749 | - Use the 'examples' test suite that's part of the UATH package. |
2750 | - """ |
2751 | - self.suite = testsuite.TestSuite(name='examples', path='/var/uath', result_class=ResultYAML, |
2752 | - _save_state_callback=state_saver) |
2753 | - |
2754 | - def test_run(self): |
2755 | - print "found %d tests" % self.suite.count_tests() |
2756 | - self.suite.run() |
2757 | - |
2758 | - def test_count_tests(self): |
2759 | - self.assertEquals(self.suite.count_tests(), 2) |
2760 | - |
2761 | - def test_next_test(self): |
2762 | - self.assertEquals(self.suite.get_next_test().name, 'test_one') |
2763 | - |
2764 | - def test_is_done(self): |
2765 | - self.assertFalse(self.suite.is_done()) |
2766 | - |
2767 | - def test_save_state(self): |
2768 | - state = self.suite.save_state() |
2769 | - |
2770 | - print state |
2771 | - self.assertEqual(state['status'], 'NOTRUN') |
2772 | - |
2773 | - tests = 0 |
2774 | - for test in state['tests']: |
2775 | - self.assertEqual(test['status'], 'NOTRUN') |
2776 | - tests += 1 |
2777 | - |
2778 | - self.assertEqual(tests, self.suite.count_tests()) |
2779 | + """ |
2780 | + Tests for the TestSuite class. |
2781 | + """ |
2782 | + def setUp(self): |
2783 | + """ |
2784 | + Use the 'examples' test suite that's part of the UATH package. |
2785 | + """ |
2786 | + self.suite = testsuite.TestSuite(name='examples', path='/var/lib/uath', result_class=ResultYAML, |
2787 | + _save_state_callback=state_saver) |
2788 | + |
2789 | + def test_run(self): |
2790 | + print "found %d tests" % self.suite.count_tests() |
2791 | + self.suite.run() |
2792 | + self.assertEqual(self.suite.result.status, 'PASS') |
2793 | + |
2794 | + def test_count_tests(self): |
2795 | + self.assertEquals(self.suite.count_tests(), 2) |
2796 | + |
2797 | + def test_next_test(self): |
2798 | + self.assertEquals(self.suite.get_next_test().name, 'test_one') |
2799 | + |
2800 | + def test_is_done(self): |
2801 | + self.assertFalse(self.suite.is_done()) |
2802 | + |
2803 | + def test_save_state(self): |
2804 | + state = self.suite.save_state() |
2805 | + |
2806 | + print state |
2807 | + self.assertEqual(state['status'], 'NOTRUN') |
2808 | + |
2809 | + tests = 0 |
2810 | + for test in state['tests']: |
2811 | + self.assertEqual(test['status'], 'NOTRUN') |
2812 | + tests += 1 |
2813 | + |
2814 | + self.assertEqual(tests, self.suite.count_tests()) |
2815 | |
2816 | class TestTestCase(unittest.TestCase): |
2817 | - """ |
2818 | - Tests for the TestCase class. |
2819 | - """ |
2820 | - def setUp(self): |
2821 | - self.timeout = 10 |
2822 | - self.command = 'echo "command"' |
2823 | - self.result = ResultYAML() |
2824 | - self.name = "test_one" |
2825 | - self.path = "examples/test_one" |
2826 | - self.suite_dir = os.path.join(UATH_DIR, 'examples') |
2827 | + """ |
2828 | + Tests for the TestCase class. |
2829 | + """ |
2830 | + def setUp(self): |
2831 | + self.timeout = 10 |
2832 | + self.command = 'echo "command"' |
2833 | + self.result = ResultYAML() |
2834 | + self.name = "test_one" |
2835 | + self.path = "examples/test_one" |
2836 | + self.suite_dir = os.path.join(UATH_DIR, 'examples') |
2837 | |
2838 | - # Change to the testsuite's working directory |
2839 | - os.chdir(self.suite_dir) |
2840 | - |
2841 | - self.case = testcase.TestCase( |
2842 | - name=self.name, |
2843 | - path=self.path, |
2844 | - command=self.command, |
2845 | - timeout=self.timeout, |
2846 | - result=self.result, |
2847 | - ) |
2848 | - |
2849 | - def test_run(self): |
2850 | - """ |
2851 | - Test that a TestCase can be run. |
2852 | - """ |
2853 | - self.case.run() |
2854 | - |
2855 | - # make sure after a testcase run that the cwd hasn't changed. |
2856 | - self.assertEqual(os.getcwd(), self.suite_dir) |
2857 | - self.assertTrue(self.case.is_done()) |
2858 | - |
2859 | - def test_timeout(self): |
2860 | - self.assertEqual(self.case.timeout, self.timeout) |
2861 | - |
2862 | - def test_command(self): |
2863 | - self.assertEqual(self.case.command, self.command) |
2864 | - |
2865 | - def test_save_state(self): |
2866 | - state = self.case.save_state() |
2867 | - |
2868 | - self.assertEqual(state['status'], 'NOTRUN') |
2869 | - self.assertEqual(state['command'], self.command) |
2870 | - self.assertEqual(state['timeout'], self.timeout) |
2871 | - |
2872 | - def test_save_and_load_state(self): |
2873 | - state = self.case.save_state() |
2874 | - |
2875 | - case = testcase.TestCase(name=self.name, path=self.path) |
2876 | - case.load_state(state) |
2877 | - |
2878 | - self.assertFalse(case.is_done()) |
2879 | - |
2880 | - self.assertEqual(case.name, self.name) |
2881 | - self.assertEqual(case.path, self.path) |
2882 | - self.assertEqual(case.command, self.command) |
2883 | - self.assertEqual(case.timeout, self.timeout) |
2884 | - |
2885 | - case.run() |
2886 | - |
2887 | - self.assertTrue(case.is_done()) |
2888 | + # Change to the testsuite's working directory |
2889 | + os.chdir(self.suite_dir) |
2890 | + |
2891 | + self.case = testcase.TestCase( |
2892 | + name=self.name, |
2893 | + path=self.path, |
2894 | + command=self.command, |
2895 | + timeout=self.timeout, |
2896 | + result=self.result, |
2897 | + ) |
2898 | + |
2899 | + def test_run(self): |
2900 | + """ |
2901 | + Test that a TestCase can be run. |
2902 | + """ |
2903 | + self.case.run() |
2904 | + |
2905 | + # make sure after a testcase run that the cwd hasn't changed. |
2906 | + self.assertEqual(os.getcwd(), self.suite_dir) |
2907 | + self.assertTrue(self.case.is_done()) |
2908 | + |
2909 | + def test_timeout(self): |
2910 | + self.assertEqual(self.case.timeout, self.timeout) |
2911 | + |
2912 | + def test_command(self): |
2913 | + self.assertEqual(self.case.command, self.command) |
2914 | + |
2915 | + def test_save_state(self): |
2916 | + state = self.case.save_state() |
2917 | + |
2918 | + self.assertEqual(state['status'], 'NOTRUN') |
2919 | + self.assertEqual(state['command'], self.command) |
2920 | + self.assertEqual(state['timeout'], self.timeout) |
2921 | + |
2922 | + def test_save_and_load_state(self): |
2923 | + state = self.case.save_state() |
2924 | + |
2925 | + case = testcase.TestCase(name=self.name, path=self.path) |
2926 | + case.load_state(state) |
2927 | + |
2928 | + self.assertFalse(case.is_done()) |
2929 | + |
2930 | + self.assertEqual(case.name, self.name) |
2931 | + self.assertEqual(case.path, self.path) |
2932 | + self.assertEqual(case.command, self.command) |
2933 | + self.assertEqual(case.timeout, self.timeout) |
2934 | + |
2935 | + case.run() |
2936 | + |
2937 | + self.assertTrue(case.is_done()) |
2938 | |
2939 | class TestResult(unittest.TestCase): |
2940 | - def setUp(self): |
2941 | - self.result_yaml = ResultYAML("result_yaml") |
2942 | - self.result_text = Result("result") |
2943 | - |
2944 | - self.result = { |
2945 | - 'command': 'echo hi', |
2946 | - 'returncode': 0, |
2947 | - 'stdout': 'hi', |
2948 | - 'stderr': '' |
2949 | - } |
2950 | - |
2951 | - self.result_fail = { |
2952 | - 'command': 'echo failure', |
2953 | - 'returncode': 2, |
2954 | - 'stdout': 'hi', |
2955 | - 'stderr': '' |
2956 | - } |
2957 | - |
2958 | - def test_add_result(self): |
2959 | - self.result_yaml.add_result(self.result) |
2960 | - |
2961 | - self.assertEqual(self.result_yaml.count_results(), 1) |
2962 | - |
2963 | - self.result_yaml.result() |
2964 | - |
2965 | - def test_result(self): |
2966 | - self.result_yaml.add_result(self.result) |
2967 | - |
2968 | - # check that the result was added. |
2969 | - self.assertEqual(self.result_yaml.count_results(), 1) |
2970 | - |
2971 | - self.result_yaml.result() |
2972 | - |
2973 | - # check that the result was processed and removed. |
2974 | - self.assertEqual(self.result_yaml.count_results(), 0) |
2975 | - |
2976 | - def test_result_success(self): |
2977 | - """ |
2978 | - Test that adding a successful result sets the Result object's status |
2979 | - to 'SUCCESS'. |
2980 | - """ |
2981 | - self.result_yaml.add_result(self.result) |
2982 | - |
2983 | - self.assertEqual(self.result_yaml.status, 'SUCCESS') |
2984 | - |
2985 | - def test_result_failure(self): |
2986 | - """ |
2987 | - Test that adding a failure result sets the Result object's status |
2988 | - to 'ERROR'. |
2989 | - """ |
2990 | - self.result_yaml.add_result(self.result_fail) |
2991 | - |
2992 | - self.assertEqual(self.result_yaml.status, 'ERROR') |
2993 | - |
2994 | - def test_result_yaml(self): |
2995 | - self.result_yaml.add_result(self.result) |
2996 | - |
2997 | - import sys |
2998 | - import StringIO |
2999 | - |
3000 | - str_output = StringIO.StringIO() |
3001 | - |
3002 | - # redirect output to a string |
3003 | - old_stdout = sys.stdout |
3004 | - sys.stdout = str_output |
3005 | - |
3006 | - self.result_yaml.result() |
3007 | - |
3008 | - sys.stdout = old_stdout |
3009 | - |
3010 | - result_str = str_output.getvalue() |
3011 | - |
3012 | - print("result_str: %s" % result_str) |
3013 | - |
3014 | - |
3015 | - data = yaml.load(result_str) |
3016 | - |
3017 | - self.assertEqual(len(data), 1) |
3018 | - |
3019 | - result = data[0] |
3020 | - |
3021 | - print("data: %s" % data) |
3022 | - |
3023 | - self.assertEqual(result['command'], self.result['command']) |
3024 | - self.assertEqual(result['returncode'], self.result['returncode']) |
3025 | - self.assertEqual(result['stdout'], self.result['stdout']) |
3026 | - self.assertEqual(result['stderr'], self.result['stderr']) |
3027 | + def setUp(self): |
3028 | + self.result_yaml = ResultYAML("result_yaml") |
3029 | + self.result_text = Result("result") |
3030 | + |
3031 | + self.result = { |
3032 | + 'command': 'echo hi', |
3033 | + 'returncode': 0, |
3034 | + 'stdout': 'hi', |
3035 | + 'stderr': '' |
3036 | + } |
3037 | + |
3038 | + self.result_fail = { |
3039 | + 'command': 'echo failure', |
3040 | + 'returncode': 2, |
3041 | + 'stdout': 'hi', |
3042 | + 'stderr': '' |
3043 | + } |
3044 | + |
3045 | + def test_add_result(self): |
3046 | + self.result_yaml.add_result(self.result) |
3047 | + |
3048 | + self.assertEqual(self.result_yaml.count_results(), 1) |
3049 | + |
3050 | + self.result_yaml.result() |
3051 | + |
3052 | + def test_result(self): |
3053 | + self.result_yaml.add_result(self.result) |
3054 | + |
3055 | + # check that the result was added. |
3056 | + self.assertEqual(self.result_yaml.count_results(), 1) |
3057 | + |
3058 | + self.result_yaml.result() |
3059 | + |
3060 | + # check that the result was processed and removed. |
3061 | + self.assertEqual(self.result_yaml.count_results(), 0) |
3062 | + |
3063 | + def test_result_pass(self): |
3064 | + """ |
3065 | + Test that adding a pass result sets the Result object's status |
3066 | + to 'PASS'. |
3067 | + """ |
3068 | + self.result_yaml.add_result(self.result) |
3069 | + |
3070 | + self.assertEqual(self.result_yaml.status, 'PASS') |
3071 | + |
3072 | + def test_result_failure(self): |
3073 | + """ |
3074 | + Test that adding a failure result sets the Result object's status |
3075 | + to 'ERROR'. |
3076 | + """ |
3077 | + self.result_yaml.add_result(self.result_fail) |
3078 | + |
3079 | + self.assertEqual(self.result_yaml.status, 'ERROR') |
3080 | + |
3081 | + def test_result_clear_result(self): |
3082 | + """ |
3083 | + Test that the result is actually cleared. |
3084 | + """ |
3085 | + self.result_yaml.add_result(self.result_fail) |
3086 | + self.assertEqual(self.result_yaml.status, 'ERROR') |
3087 | + |
3088 | + self.result_yaml.clear_results() |
3089 | + |
3090 | + self.assertEqual(self.result_yaml.status, 'PASS') |
3091 | + self.assertEqual(self.result_yaml.count_results(), 0) |
3092 | + |
3093 | + def test_result_result(self): |
3094 | + """ |
3095 | + Test that ERROR status is retained after adding pass |
3096 | + results. |
3097 | + """ |
3098 | + self.result_yaml.add_result(self.result_fail) |
3099 | + |
3100 | + self.assertEqual(self.result_yaml.status, 'ERROR') |
3101 | + |
3102 | + self.result_yaml.add_result(self.result) |
3103 | + |
3104 | + self.assertEqual(self.result_yaml.status, 'ERROR') |
3105 | + |
3106 | + |
3107 | + def test_result_yaml(self): |
3108 | + self.result_yaml.add_result(self.result) |
3109 | + |
3110 | + import sys |
3111 | + import StringIO |
3112 | + |
3113 | + str_output = StringIO.StringIO() |
3114 | + |
3115 | + # redirect output to a string |
3116 | + old_stdout = sys.stdout |
3117 | + sys.stdout = str_output |
3118 | + |
3119 | + self.result_yaml.result() |
3120 | + |
3121 | + sys.stdout = old_stdout |
3122 | + |
3123 | + result_str = str_output.getvalue() |
3124 | + |
3125 | + print("result_str: %s" % result_str) |
3126 | + |
3127 | + data = yaml.load(result_str) |
3128 | + |
3129 | + self.assertEqual(len(data), 1) |
3130 | + |
3131 | + result = data[0] |
3132 | + |
3133 | + print("data: %s" % data) |
3134 | + |
3135 | + self.assertEqual(result['command'], self.result['command']) |
3136 | + self.assertEqual(result['returncode'], self.result['returncode']) |
3137 | + self.assertEqual(result['stdout'], self.result['stdout']) |
3138 | + self.assertEqual(result['stderr'], self.result['stderr']) |
3139 | |
3140 | class TestRunner(unittest.TestCase): |
3141 | - def setUp(self): |
3142 | - self.result_class = ResultYAML |
3143 | - self.state_file = '/tmp/state.yaml' |
3144 | - self.state_agent = StateAgentYAML(state_file=self.state_file) |
3145 | - self.runner = Runner(result_class=self.result_class, |
3146 | - state_agent=self.state_agent) |
3147 | - self.bad_dir = "/tmp/does_not_exist" |
3148 | - |
3149 | - self.assertFalse(os.path.exists(self.bad_dir)) |
3150 | - |
3151 | - def tearDown(self): |
3152 | - |
3153 | - # Remove self.bad_dir if it got created. |
3154 | - try: |
3155 | - os.removedirs(self.bad_dir) |
3156 | - except OSError as e: |
3157 | - if e.errno == 2: |
3158 | - pass |
3159 | - else: |
3160 | - raise |
3161 | - |
3162 | - def test_rc_local(self): |
3163 | - tmp_rc_local = '/tmp/rc.local' |
3164 | - self.runner.setup_rc_local(tmp_rc_local) |
3165 | - |
3166 | - self.assertTrue(os.path.exists(tmp_rc_local)) |
3167 | - |
3168 | - def test_run(self): |
3169 | - self.runner.run() |
3170 | - |
3171 | - for suite in self.runner.suites: |
3172 | - self.assertTrue(suite.is_done()) |
3173 | - |
3174 | - def test_missing_testdir(self): |
3175 | - """ |
3176 | - Test that the runner fails gracefully on missing testdir. |
3177 | - """ |
3178 | - |
3179 | - self.assertRaises(exceptions.BadDir, |
3180 | - Runner, result_class=self.result_class, testdir=self.bad_dir) |
3181 | - |
3182 | - def test_missing_master_runlist(self): |
3183 | - """ |
3184 | - Test that the runner fails gracefully on missing 'master.run' |
3185 | - """ |
3186 | - |
3187 | - tmp_master_runlist = os.path.join(self.bad_dir, 'master.run') |
3188 | - |
3189 | - if not os.path.exists(self.bad_dir): |
3190 | - os.mkdir(self.bad_dir) |
3191 | - |
3192 | - # TODO: fix to use --master-runlist flag once it's been added. |
3193 | - if os.path.exists(tmp_master_runlist): |
3194 | - os.remove(tmp_master_runlist) |
3195 | - |
3196 | - self.assertRaises(exceptions.MissingFile, |
3197 | - Runner, result_class=self.result_class, testdir=self.bad_dir) |
3198 | - |
3199 | - def test_runlist(self): |
3200 | - """ |
3201 | - Test that passing a runlist to the Runner works properly. |
3202 | - """ |
3203 | - runlist = '/tmp/master_runlist_test' |
3204 | - |
3205 | - # fail if there is already a runlist there. |
3206 | - self.assertFalse(os.path.exists(runlist)) |
3207 | - |
3208 | - fp = open(runlist, 'w') |
3209 | - fp.write(master_runlist_content) |
3210 | - fp.close() |
3211 | - |
3212 | - runner = Runner(result_class=self.result_class, runlist=runlist) |
3213 | - |
3214 | - self.assertTrue(runner.count_tests(), 0) |
3215 | - |
3216 | - os.remove(runlist) |
3217 | + def setUp(self): |
3218 | + self.result_class = ResultYAML |
3219 | + self.state_file = '/tmp/state.yaml' |
3220 | + self.state_agent = StateAgentYAML(state_file=self.state_file) |
3221 | + self.runner = Runner(result_class=self.result_class, |
3222 | + state_agent=self.state_agent) |
3223 | + self.bad_dir = "/tmp/does_not_exist" |
3224 | + |
3225 | + self.assertFalse(os.path.exists(self.bad_dir)) |
3226 | + |
3227 | + def tearDown(self): |
3228 | + |
3229 | + # Remove self.bad_dir if it got created. |
3230 | + try: |
3231 | + os.removedirs(self.bad_dir) |
3232 | + except OSError as e: |
3233 | + if e.errno == 2: |
3234 | + pass |
3235 | + else: |
3236 | + raise |
3237 | + |
3238 | + def test_fetch_failed(self): |
3239 | + print "Starting test" |
3240 | + # exit 2 should be flagged as an error. |
3241 | + bad_runlist_content="""---\n- name: fake_tests\n fetch_cmd: exit 2\n""" |
3242 | + runlist = '/tmp/master_fetch_fails.run' |
3243 | + |
3244 | + self.assertFalse(os.path.exists(runlist), runlist) |
3245 | + |
3246 | + fp = open(runlist, 'w') |
3247 | + fp.write(bad_runlist_content) |
3248 | + fp.close() |
3249 | + |
3250 | + runner = Runner(result_class=self.result_class, |
3251 | + state_agent=self.state_agent, runlist=runlist) |
3252 | + |
3253 | + # remove the runlist as early as possible to avoid leaving it |
3254 | + # laying around if this test fails. |
3255 | + try: |
3256 | + os.remove(runlist) |
3257 | + except OSError: |
3258 | + pass |
3259 | + |
3260 | + runner.run() |
3261 | + print runner.report() |
3262 | + |
3263 | + self.assertEqual(runner.fetch_errors, 1) |
3264 | + |
3265 | + def test_rc_local(self): |
3266 | + tmp_rc_local = '/tmp/rc.local' |
3267 | + self.runner.setup_rc_local(tmp_rc_local) |
3268 | + |
3269 | + self.assertTrue(os.path.exists(tmp_rc_local)) |
3270 | + |
3271 | + def test_run(self): |
3272 | + retcode = self.runner.run() |
3273 | + |
3274 | + for suite in self.runner.suites: |
3275 | + self.assertTrue(suite.is_done()) |
3276 | + |
3277 | + self.assertEqual(retcode, RETURN_CODES['ERROR']) |
3278 | + |
3279 | + def test_missing_testdir(self): |
3280 | + """ |
3281 | + Test that the runner fails gracefully on missing testdir. |
3282 | + """ |
3283 | + |
3284 | + self.assertRaises(exceptions.BadDir, |
3285 | + Runner, result_class=self.result_class, testdir=self.bad_dir) |
3286 | + |
3287 | + def test_missing_master_runlist(self): |
3288 | + """ |
3289 | + Test that the runner fails gracefully on missing 'master.run' |
3290 | + """ |
3291 | + |
3292 | + tmp_master_runlist = os.path.join(self.bad_dir, 'master.run') |
3293 | + |
3294 | + if not os.path.exists(self.bad_dir): |
3295 | + os.mkdir(self.bad_dir) |
3296 | + |
3297 | + # TODO: fix to use --master-runlist flag once it's been added. |
3298 | + if os.path.exists(tmp_master_runlist): |
3299 | + os.remove(tmp_master_runlist) |
3300 | + |
3301 | + self.assertRaises(exceptions.MissingFile, |
3302 | + Runner, result_class=self.result_class, testdir=self.bad_dir) |
3303 | + |
3304 | + def test_runlist(self): |
3305 | + """ |
3306 | + Test that passing a runlist to the Runner works properly. |
3307 | + """ |
3308 | + runlist = '/tmp/master_runlist_test' |
3309 | + |
3310 | + # fail if there is already a runlist there. |
3311 | + self.assertFalse(os.path.exists(runlist), "%s exists" % runlist) |
3312 | + |
3313 | + fp = open(runlist, 'w') |
3314 | + fp.write(master_runlist_content) |
3315 | + fp.close() |
3316 | + |
3317 | + runner = Runner(result_class=self.result_class, runlist=runlist) |
3318 | + |
3319 | + self.assertTrue(runner.count_tests(), 0) |
3320 | + |
3321 | + os.remove(runlist) |
3322 | |
3323 | def state_saver(): |
3324 | - debug_print("Saving state", force=True) |
3325 | + debug_print("Saving state", force=True) |
3326 | |
3327 | class TestCommon(unittest.TestCase): |
3328 | |
3329 | - def test_run_cmd(self): |
3330 | - result = run_cmd('find . -name "*.py"') |
3331 | - |
3332 | - self.assertEqual(result['returncode'], 0) |
3333 | - |
3334 | - def test_debug_print(self): |
3335 | - """ |
3336 | - Test that debug_print doesn't print when DEBUG is False unless |
3337 | - 'debug' is passed in and is True. |
3338 | - """ |
3339 | - from common import CONFIG |
3340 | - |
3341 | - old_debug = CONFIG['DEBUG'] |
3342 | - |
3343 | - CONFIG['DEBUG'] = True |
3344 | - if not debug_print("testing debug_print"): |
3345 | - raise Exception("didn't print with CONFIG['DEBUG'] set to True") |
3346 | - |
3347 | - CONFIG['DEBUG'] = False |
3348 | - self.assertEqual(debug_print("shouldn't be printed unless common.CONFIG['DEBUG'] is True"), CONFIG['DEBUG'], "printed with CONFIG['DEBUG'] set to False") |
3349 | - |
3350 | - self.assertEqual(debug_print("should be printed (using 'force=True')", force=True), True) |
3351 | - |
3352 | - CONFIG['DEBUG'] = old_debug |
3353 | + def test_run_cmd(self): |
3354 | + result = run_cmd('find . -name "*.py"') |
3355 | + |
3356 | + self.assertEqual(result['returncode'], 0) |
3357 | + |
3358 | + def test_debug_print(self): |
3359 | + """ |
3360 | + Test that debug_print doesn't print when DEBUG is False unless |
3361 | + 'debug' is passed in and is True. |
3362 | + """ |
3363 | + from common import CONFIG |
3364 | + |
3365 | + old_debug = CONFIG['DEBUG'] |
3366 | + |
3367 | + CONFIG['DEBUG'] = True |
3368 | + if not debug_print("testing debug_print"): |
3369 | + raise Exception("didn't print with CONFIG['DEBUG'] set to True") |
3370 | + |
3371 | + CONFIG['DEBUG'] = False |
3372 | + self.assertEqual(debug_print("shouldn't be printed unless common.CONFIG['DEBUG'] is True"), CONFIG['DEBUG'], "printed with CONFIG['DEBUG'] set to False") |
3373 | + |
3374 | + self.assertEqual(debug_print("should be printed (using 'force=True')", force=True), True) |
3375 | + |
3376 | + CONFIG['DEBUG'] = old_debug |
3377 | |
3378 | ###################################################################### |
3379 | ### The tests after this comment really need to be moved into the |
3380 | @@ -404,123 +464,98 @@ |
3381 | |
3382 | |
3383 | class TestStateAgentYAML(unittest.TestCase): |
3384 | - def setUp(self): |
3385 | - """ |
3386 | - Create a state agent for testing. |
3387 | - """ |
3388 | - self.state_file = '/tmp/state.yaml' |
3389 | - |
3390 | - # Fail if there is already a test state file. |
3391 | - self.assertFalse(os.path.exists(self.state_file), "state file (%s) already exists" % self.state_file) |
3392 | - |
3393 | - self.state_agent = StateAgentYAML(state_file=self.state_file) |
3394 | - self.runner = Runner(result_class=ResultYAML, state_agent=self.state_agent) |
3395 | - self.partial_state_file_content = partial_state_file_content |
3396 | - |
3397 | - def tearDown(self): |
3398 | - """ |
3399 | - Clean up the test state file. |
3400 | - """ |
3401 | - try: |
3402 | - os.remove(self.state_file) |
3403 | - except OSError as e: |
3404 | - if e.errno == 2: # does not exist |
3405 | - pass |
3406 | - else: |
3407 | - raise |
3408 | - |
3409 | - def test_creation(self): |
3410 | - suite_count = self.runner.count_suites() |
3411 | - test_count = self.runner.count_tests() |
3412 | - |
3413 | - self.runner.run() |
3414 | - |
3415 | - self.assertTrue(os.path.exists(self.state_file)) |
3416 | - |
3417 | - def test_cleanup(self): |
3418 | - self.runner.save_state() |
3419 | - |
3420 | - self.assertTrue(os.path.exists(self.state_file), "Runner failed to save state") |
3421 | - |
3422 | - self.state_agent.clean() |
3423 | - |
3424 | - self.assertFalse(os.path.exists(self.state_file), "Failed to remove state file (%s)" % self.state_file) |
3425 | - |
3426 | - def test_status(self): |
3427 | - """ |
3428 | - Test that a runner properly saves its state during test runs. |
3429 | - """ |
3430 | - suite_count = self.runner.count_suites() |
3431 | - test_count = self.runner.count_tests() |
3432 | - |
3433 | - self.runner.run() |
3434 | - |
3435 | - self.assertTrue(os.path.exists(self.state_file)) |
3436 | - |
3437 | - self.runner.load_state() |
3438 | - |
3439 | - self.assertEqual(self.runner.count_suites(), suite_count) |
3440 | - self.assertEqual(self.runner.count_tests(), test_count) |
3441 | - |
3442 | - fp = open(self.state_file, 'r') |
3443 | - state_data = yaml.load(fp) |
3444 | - fp.close() |
3445 | - |
3446 | - self.assertEqual(state_data['status'], 'DONE') |
3447 | - |
3448 | - def test_load_state_partial(self): |
3449 | - """ |
3450 | - Test that a partially run state file can be resumed. |
3451 | - """ |
3452 | - fp = open(self.state_file, 'w') |
3453 | - fp.write(self.partial_state_file_content) |
3454 | - fp.close() |
3455 | - |
3456 | - state_agent = StateAgentYAML(state_file=self.state_file) |
3457 | - runner = Runner(state_agent=state_agent, resume=True) |
3458 | - runner.load_state() |
3459 | - |
3460 | - self.assertEqual(runner.status, 'RUN') |
3461 | - |
3462 | - next_test = runner.get_next_test() |
3463 | - |
3464 | - self.assertEqual(next_test.name, 'test_two') |
3465 | - |
3466 | - runner.run() |
3467 | + def setUp(self): |
3468 | + """ |
3469 | + Create a state agent for testing. |
3470 | + """ |
3471 | + self.state_file = '/tmp/state.yaml' |
3472 | + |
3473 | + # Fail if there is already a test state file. |
3474 | + self.assertFalse(os.path.exists(self.state_file), "state file (%s) already exists" % self.state_file) |
3475 | + |
3476 | + self.state_agent = StateAgentYAML(state_file=self.state_file) |
3477 | + self.runner = Runner(result_class=ResultYAML, state_agent=self.state_agent) |
3478 | + self.partial_state_file_content = partial_state_file_content |
3479 | + |
3480 | + def tearDown(self): |
3481 | + """ |
3482 | + Clean up the test state file. |
3483 | + """ |
3484 | + try: |
3485 | + os.remove(self.state_file) |
3486 | + except OSError as e: |
3487 | + if e.errno == 2: # does not exist |
3488 | + pass |
3489 | + else: |
3490 | + raise |
3491 | + |
3492 | + def test_creation(self): |
3493 | + suite_count = self.runner.count_suites() |
3494 | + test_count = self.runner.count_tests() |
3495 | + |
3496 | + self.runner.run() |
3497 | + |
3498 | + self.assertTrue(os.path.exists(self.state_file)) |
3499 | + |
3500 | + def test_cleanup(self): |
3501 | + self.runner.save_state() |
3502 | + |
3503 | + self.assertTrue(os.path.exists(self.state_file), "Runner failed to save state") |
3504 | + |
3505 | + self.state_agent.clean() |
3506 | + |
3507 | + self.assertFalse(os.path.exists(self.state_file), "Failed to remove state file (%s)" % self.state_file) |
3508 | + |
3509 | + def test_status(self): |
3510 | + """ |
3511 | + Test that a runner properly saves its state during test runs. |
3512 | + """ |
3513 | + suite_count = self.runner.count_suites() |
3514 | + test_count = self.runner.count_tests() |
3515 | + |
3516 | + self.runner.run() |
3517 | + |
3518 | + self.assertTrue(os.path.exists(self.state_file)) |
3519 | + |
3520 | + self.runner.load_state() |
3521 | + |
3522 | + self.assertEqual(self.runner.count_suites(), suite_count) |
3523 | + self.assertEqual(self.runner.count_tests(), test_count) |
3524 | + |
3525 | + fp = open(self.state_file, 'r') |
3526 | + state_data = yaml.load(fp) |
3527 | + fp.close() |
3528 | + |
3529 | + self.assertEqual(state_data['status'], 'DONE') |
3530 | + |
3531 | + def test_load_state_partial(self): |
3532 | + """ |
3533 | + Test that a partially run state file can be resumed. |
3534 | + """ |
3535 | + fp = open(self.state_file, 'w') |
3536 | + fp.write(self.partial_state_file_content) |
3537 | + fp.close() |
3538 | + |
3539 | + state_agent = StateAgentYAML(state_file=self.state_file) |
3540 | + runner = Runner(state_agent=state_agent, resume=True) |
3541 | + runner.load_state() |
3542 | + |
3543 | + self.assertEqual(runner.status, 'RUN') |
3544 | + |
3545 | + next_test = runner.get_next_test() |
3546 | + |
3547 | + self.assertEqual(next_test.name, 'test_two') |
3548 | + |
3549 | + runner.run() |
3550 | |
3551 | def main(): |
3552 | - """ |
3553 | - Run any tests listed in 'funcs'. Comment ones out if you want to skip them. |
3554 | - |
3555 | - Also you can run all tests via nose like this: |
3556 | - |
3557 | - nosetests main.py |
3558 | - |
3559 | - See nosetests(1) for more info. |
3560 | - |
3561 | - """ |
3562 | - funcs = [ |
3563 | - #test_testcase_default_results, |
3564 | - #test_run_cmd, |
3565 | - #test_testcase, |
3566 | - #test_testsuite, |
3567 | - #test_result_success, |
3568 | - #test_result_failure, |
3569 | - #test_result_yaml_success, |
3570 | - #test_result_yaml_failure, |
3571 | - #test_runner, |
3572 | - #test_debug_print, |
3573 | - #test_save_state, |
3574 | - #test_testcase_save_and_load_state, |
3575 | - #test_testsuite_save_and_load_state, |
3576 | - #test_state_agent_YAML, |
3577 | - #test_load_state_partial, |
3578 | - test_state_agent_cleanup, |
3579 | - test_testsuite_is_done, |
3580 | - ] |
3581 | - |
3582 | - for func in funcs: |
3583 | - func() |
3584 | - |
3585 | + print """ |
3586 | + You can run all tests via nose like this: |
3587 | + |
3588 | + nosetests self_test.py |
3589 | + |
3590 | + See nosetests(1) for more info. |
3591 | + |
3592 | + """ |
3593 | if __name__ == "__main__": |
3594 | - main() |
3595 | + main() |
3596 | |
3597 | === renamed file 'uath/state_agent.py' => 'uath/client/state_agent.py' |
3598 | --- uath/state_agent.py 2012-04-09 19:43:57 +0000 |
3599 | +++ uath/client/state_agent.py 2012-05-09 00:33:18 +0000 |
3600 | @@ -6,75 +6,75 @@ |
3601 | |
3602 | |
3603 | class StateAgent(object): |
3604 | - """ |
3605 | - State saving base class. |
3606 | - |
3607 | - Accepts a dictionary of state info and prints it to STDOUT. |
3608 | - """ |
3609 | - |
3610 | - state_file = DEFAULT_STATE_FILE |
3611 | - |
3612 | - def __init__(self, state_file=None): |
3613 | - if state_file is not None: |
3614 | - self.state_file = state_file |
3615 | - |
3616 | - def clean(self): |
3617 | - """ |
3618 | - Clean up the state file if it exists. |
3619 | - """ |
3620 | - import os |
3621 | - |
3622 | - # Don't fail if the state_file doesn't exists. |
3623 | - try: |
3624 | - os.remove(self.state_file) |
3625 | - except OSError as e: |
3626 | - if e.errno == 2: # file not found |
3627 | - pass |
3628 | - |
3629 | - def save_state(self, state): |
3630 | - """ |
3631 | - Save state to the state_file. |
3632 | - """ |
3633 | - |
3634 | - fp = open(self.state_file, 'w') |
3635 | - fp.write(str(state)) |
3636 | - fp.close() |
3637 | - |
3638 | - def load_state(self): |
3639 | - """ |
3640 | - Load state from the state_file. |
3641 | - """ |
3642 | - state = {} |
3643 | - |
3644 | - if os.path.exists(self.state_file): |
3645 | - fp = open(self.state_file, 'r') |
3646 | - state = fp.read() |
3647 | - fp.close() |
3648 | - |
3649 | - return state |
3650 | + """ |
3651 | + State saving base class. |
3652 | + |
3653 | + Accepts a dictionary of state info and prints it to STDOUT. |
3654 | + """ |
3655 | + |
3656 | + state_file = DEFAULT_STATE_FILE |
3657 | + |
3658 | + def __init__(self, state_file=None): |
3659 | + if state_file is not None: |
3660 | + self.state_file = state_file |
3661 | + |
3662 | + def clean(self): |
3663 | + """ |
3664 | + Clean up the state file if it exists. |
3665 | + """ |
3666 | + import os |
3667 | + |
3668 | + # Don't fail if the state_file doesn't exists. |
3669 | + try: |
3670 | + os.remove(self.state_file) |
3671 | + except OSError as e: |
3672 | + if e.errno == 2: # file not found |
3673 | + pass |
3674 | + |
3675 | + def save_state(self, state): |
3676 | + """ |
3677 | + Save state to the state_file. |
3678 | + """ |
3679 | + |
3680 | + fp = open(self.state_file, 'w') |
3681 | + fp.write(str(state)) |
3682 | + fp.close() |
3683 | + |
3684 | + def load_state(self): |
3685 | + """ |
3686 | + Load state from the state_file. |
3687 | + """ |
3688 | + state = {} |
3689 | + |
3690 | + if os.path.exists(self.state_file): |
3691 | + fp = open(self.state_file, 'r') |
3692 | + state = fp.read() |
3693 | + fp.close() |
3694 | + |
3695 | + return state |
3696 | |
3697 | class StateAgentYAML(StateAgent): |
3698 | - """ |
3699 | - YAML based state saver. |
3700 | - """ |
3701 | - |
3702 | - def save_state(self, state): |
3703 | - """ |
3704 | - Output the state as YAML. |
3705 | - """ |
3706 | - |
3707 | - super(StateAgentYAML, self).save_state(yaml.dump(state, default_flow_style=False)) |
3708 | - |
3709 | - def load_state(self): |
3710 | - """ |
3711 | - Load state from YAML state_file. |
3712 | - """ |
3713 | - |
3714 | - state = {} |
3715 | - |
3716 | - if os.path.exists(self.state_file): |
3717 | - fp = open(self.state_file, 'r') |
3718 | - state = yaml.load(fp) |
3719 | - fp.close() |
3720 | - |
3721 | - return state |
3722 | + """ |
3723 | + YAML based state saver. |
3724 | + """ |
3725 | + |
3726 | + def save_state(self, state): |
3727 | + """ |
3728 | + Output the state as YAML. |
3729 | + """ |
3730 | + |
3731 | + super(StateAgentYAML, self).save_state(yaml.dump(state, default_flow_style=False)) |
3732 | + |
3733 | + def load_state(self): |
3734 | + """ |
3735 | + Load state from YAML state_file. |
3736 | + """ |
3737 | + |
3738 | + state = {} |
3739 | + |
3740 | + if os.path.exists(self.state_file): |
3741 | + fp = open(self.state_file, 'r') |
3742 | + state = yaml.load(fp) |
3743 | + fp.close() |
3744 | + |
3745 | + return state |
3746 | |
3747 | === renamed file 'uath/testcase.py' => 'uath/client/testcase.py' |
3748 | --- uath/testcase.py 2012-04-06 15:44:09 +0000 |
3749 | +++ uath/client/testcase.py 2012-05-09 00:33:18 +0000 |
3750 | @@ -5,198 +5,201 @@ |
3751 | from common import run_cmd, parse_control_file, debug_print |
3752 | from common import do_nothing |
3753 | from result import Result |
3754 | +from exceptions import MissingFile |
3755 | |
3756 | class TestCase(object): |
3757 | - """ |
3758 | - The TestCase class. |
3759 | - |
3760 | - status is one of 'NOTRUN', 'BUILD', 'SETUP', 'RUN', 'CLEANUP', or 'DONE' |
3761 | - """ |
3762 | - |
3763 | - status = 'NOTRUN' |
3764 | - build_cmd = None |
3765 | - tc_setup = None |
3766 | - tc_cleanup = None |
3767 | - reboot = 'never' |
3768 | - reboot_callback = None |
3769 | - save_state_callback = do_nothing |
3770 | - |
3771 | - def __init__(self, name, path, command=None, result=None, timeout=0, |
3772 | - _control_data=None, _save_state_callback=None, _reboot_callback=None): |
3773 | - """ |
3774 | - Build a TestCase from a control file's data. |
3775 | - |
3776 | - 'name' is a directory where the test case resides. |
3777 | - """ |
3778 | - self.name = name |
3779 | - self.path = path |
3780 | - self.working_dir = path |
3781 | - self.filename = "%s/tc_control" % path |
3782 | - self.results = [] |
3783 | - self.result = result |
3784 | - self.timeout = timeout |
3785 | - self.command = command |
3786 | - self.reboot_callback = _reboot_callback |
3787 | - |
3788 | - if _save_state_callback is not None: |
3789 | - self.save_state_callback = _save_state_callback |
3790 | - |
3791 | - required_items = ['description', 'type'] |
3792 | - optional_items = ['build_cmd', |
3793 | - 'timeout', |
3794 | - 'command', |
3795 | - 'tc_setup', |
3796 | - 'tc_cleanup', |
3797 | - 'reboot', # 'always', 'success', or 'never' |
3798 | - ] |
3799 | - |
3800 | - |
3801 | - control_data = None |
3802 | - import os |
3803 | - if _control_data is None and os.path.exists(self.filename): |
3804 | - control_data = parse_control_file(self.filename, required=required_items, |
3805 | - optional=optional_items) |
3806 | - else: |
3807 | - control_data = _control_data |
3808 | - |
3809 | - if control_data is not None: |
3810 | - self.__dict__.update(control_data) |
3811 | - |
3812 | - # Set any values passed into __init__() as final values |
3813 | - if timeout != 0 or self.timeout == 0: |
3814 | - self.timeout = timeout |
3815 | - if command is not None: |
3816 | - self.command = command |
3817 | - |
3818 | - def __str__(self): |
3819 | - return "%s: %s, %s, %s" % (self.name, self.description, self.command, self.timeout) |
3820 | - |
3821 | - def set_status(self, status): |
3822 | - """ |
3823 | - Set the status for the test case and call the save state callback. |
3824 | - """ |
3825 | - self.status = status |
3826 | - self.save_state_callback() |
3827 | - |
3828 | - def build(self, result): |
3829 | - """ |
3830 | - Run build, but only if we haven't started a run yet. |
3831 | - """ |
3832 | - if self.status == 'NOTRUN': |
3833 | - self.set_status('BUILD') |
3834 | - result.add_result(run_cmd(self.build_cmd, cwd=self.working_dir)) |
3835 | - |
3836 | - def setup(self, result): |
3837 | - """ |
3838 | - Run tc_setup, but only if build() has just run successfully. |
3839 | - """ |
3840 | - if self.status == 'BUILD' and result.status == 'SUCCESS': |
3841 | - self.set_status('SETUP') |
3842 | - result.add_result(run_cmd(self.tc_setup, cwd=self.working_dir)) |
3843 | - |
3844 | - def cleanup(self, result): |
3845 | - """ |
3846 | - Run tc_cleanup after a run. |
3847 | - """ |
3848 | - if self.status == 'RUN': |
3849 | - self.set_status('CLEANUP') |
3850 | - result.add_result(run_cmd(self.tc_cleanup, cwd=self.working_dir)) |
3851 | - |
3852 | - def run(self, result=None): |
3853 | - """ |
3854 | - Run the test case; including any build, setup, and cleanup commands. |
3855 | - """ |
3856 | - |
3857 | - # Return value to indicate whether processing of a TestSuite should |
3858 | - # continue. This is to avoid a shutdown race on reboot cases. |
3859 | - keep_going = True |
3860 | - |
3861 | - # If no result object was passed in and the instance does not have |
3862 | - # one either then create a basic text result object. |
3863 | - if result is None: |
3864 | - if self.result is not None: |
3865 | - result = self.result |
3866 | - else: |
3867 | - result = Result(self.name) |
3868 | - |
3869 | - self.build(result) |
3870 | - |
3871 | - # if the build fails don't run the test command |
3872 | - if result.status != 'SUCCESS': |
3873 | - return result.result() |
3874 | - |
3875 | - self.setup(result) |
3876 | - |
3877 | - # if the setup fails don't run the test command |
3878 | - if result.status != 'SUCCESS': |
3879 | - return result.result() |
3880 | - |
3881 | - if self.status == 'SETUP': |
3882 | - timeout = self.timeout or 0 |
3883 | - self.status = "RUN" |
3884 | - self.save_state_callback() |
3885 | - result.add_result(run_cmd(self.command, timeout=timeout, cwd=self.working_dir)) |
3886 | - |
3887 | - # Clean up whether 'command' failed or not. |
3888 | - self.cleanup(result) |
3889 | - |
3890 | - need_reboot = False |
3891 | - if self.reboot == 'always' or self.reboot == 'success' and result.status == 'SUCCESS': |
3892 | - need_reboot = True |
3893 | - |
3894 | - result.result() |
3895 | - |
3896 | - if self.status == 'CLEANUP': |
3897 | - self.status = "DONE" |
3898 | - self.save_state_callback() |
3899 | - |
3900 | - if need_reboot and self.reboot_callback is not None: |
3901 | - #print("JAT: *** HERE I WOULD HAVE REBOOTED ***") |
3902 | - self.reboot_callback() |
3903 | - keep_going = False |
3904 | - |
3905 | - return keep_going |
3906 | - |
3907 | - def process_overrides(self, overrides): |
3908 | - """ |
3909 | - Sets override values from a TestSuite runlist for this test case. |
3910 | - """ |
3911 | - for item in overrides: |
3912 | - for key, value in item.iteritems(): |
3913 | - setattr(self, key, value) |
3914 | - |
3915 | - def load_state(self, state): |
3916 | - """ |
3917 | - Restore state from the supplied dictionary. |
3918 | - |
3919 | - Requires that 'state' has the same fieldnames as the TestCase class. |
3920 | - """ |
3921 | - self.__dict__.update(state) |
3922 | - |
3923 | - def save_state(self): |
3924 | - """ |
3925 | - Returns a dictionary representing the test's state. |
3926 | - """ |
3927 | - state = { |
3928 | - 'name': self.name, |
3929 | - 'path': self.path, |
3930 | - 'command': self.command, |
3931 | - 'timeout': self.timeout, |
3932 | - 'status': self.status, |
3933 | - 'build_cmd': self.build_cmd, |
3934 | - 'tc_setup': self.tc_setup, |
3935 | - 'tc_cleanup': self.tc_cleanup, |
3936 | - 'type': self.type, |
3937 | - 'description': self.description, |
3938 | - } |
3939 | - |
3940 | - return state |
3941 | - |
3942 | - def is_done(self): |
3943 | - """ |
3944 | - Determine if the case is done. This might mean that something has |
3945 | - failed. Used by suite to determine if the suite needs to be re-run |
3946 | - on resume. |
3947 | - """ |
3948 | - return self.status == 'DONE' or self.status == 'CLEANUP' |
3949 | + """ |
3950 | + The TestCase class. |
3951 | + |
3952 | + status is one of 'NOTRUN', 'BUILD', 'SETUP', 'RUN', 'CLEANUP', or 'DONE' |
3953 | + """ |
3954 | + |
3955 | + status = 'NOTRUN' |
3956 | + build_cmd = None |
3957 | + tc_setup = None |
3958 | + tc_cleanup = None |
3959 | + reboot = 'never' |
3960 | + reboot_callback = None |
3961 | + save_state_callback = do_nothing |
3962 | + type = 'userland' |
3963 | + |
3964 | + def __init__(self, name, path, command=None, result=None, timeout=0, |
3965 | + _control_data=None, _save_state_callback=None, _reboot_callback=None): |
3966 | + """ |
3967 | + Build a TestCase from a control file's data. |
3968 | + |
3969 | + 'name' is a directory where the test case resides. |
3970 | + """ |
3971 | + self.name = name |
3972 | + self.path = path |
3973 | + self.working_dir = path |
3974 | + self.filename = "%s/tc_control" % path |
3975 | + self.results = [] |
3976 | + self.result = result |
3977 | + self.timeout = timeout |
3978 | + self.command = command |
3979 | + self.reboot_callback = _reboot_callback |
3980 | + |
3981 | + if _save_state_callback is not None: |
3982 | + self.save_state_callback = _save_state_callback |
3983 | + |
3984 | + required_items = ['description', 'type', 'timeout', 'action'] |
3985 | + optional_items = ['build_cmd', |
3986 | + 'command', |
3987 | + 'tc_setup', |
3988 | + 'tc_cleanup', |
3989 | + 'reboot', # 'always', 'pass', or 'never' |
3990 | + ] |
3991 | + |
3992 | + |
3993 | + control_data = None |
3994 | + import os |
3995 | + if _control_data is None: |
3996 | + if os.path.exists(self.filename): |
3997 | + control_data = parse_control_file(self.filename, required=required_items, |
3998 | + optional=optional_items) |
3999 | + else: |
4000 | + raise MissingFile(self.filename) |
4001 | + else: |
4002 | + control_data = _control_data |
4003 | + |
4004 | + if control_data is not None: |
4005 | + self.__dict__.update(control_data) |
4006 | + |
4007 | + # Set any values passed into __init__() as final values |
4008 | + if timeout != 0 or self.timeout == 0: |
4009 | + self.timeout = timeout |
4010 | + if command is not None: |
4011 | + self.command = command |
4012 | + |
4013 | + def __str__(self): |
4014 | + return "%s: %s, %s, %s" % (self.name, self.description, self.command, self.timeout) |
4015 | + |
4016 | + def set_status(self, status): |
4017 | + """ |
4018 | + Set the status for the test case and call the save state callback. |
4019 | + """ |
4020 | + self.status = status |
4021 | + self.save_state_callback() |
4022 | + |
4023 | + def build(self, result): |
4024 | + """ |
4025 | + Run build, but only if we haven't started a run yet. |
4026 | + """ |
4027 | + if self.status == 'NOTRUN': |
4028 | + self.set_status('BUILD') |
4029 | + result.add_result(run_cmd(self.build_cmd, cwd=self.working_dir)) |
4030 | + |
4031 | + def setup(self, result): |
4032 | + """ |
4033 | + Run tc_setup, but only if build() has just passed. |
4034 | + """ |
4035 | + if self.status == 'BUILD' and result.status == 'PASS': |
4036 | + self.set_status('SETUP') |
4037 | + result.add_result(run_cmd(self.tc_setup, cwd=self.working_dir)) |
4038 | + |
4039 | + def cleanup(self, result): |
4040 | + """ |
4041 | + Run tc_cleanup after a run. |
4042 | + """ |
4043 | + if self.status == 'RUN': |
4044 | + self.set_status('CLEANUP') |
4045 | + result.add_result(run_cmd(self.tc_cleanup, cwd=self.working_dir)) |
4046 | + |
4047 | + def run(self, result=None): |
4048 | + """ |
4049 | + Run the test case; including any build, setup, and cleanup commands. |
4050 | + """ |
4051 | + |
4052 | + # Return value to indicate whether processing of a TestSuite should |
4053 | + # continue. This is to avoid a shutdown race on reboot cases. |
4054 | + keep_going = True |
4055 | + |
4056 | + # If no result object was passed in and the instance does not have |
4057 | + # one either then create a basic text result object. |
4058 | + if result is None: |
4059 | + if self.result is not None: |
4060 | + result = self.result |
4061 | + else: |
4062 | + result = Result(self.name) |
4063 | + |
4064 | + self.build(result) |
4065 | + |
4066 | + # if the build fails don't run the test command |
4067 | + if result.status != 'PASS': |
4068 | + return result.result() |
4069 | + |
4070 | + self.setup(result) |
4071 | + |
4072 | + # if the setup fails don't run the test command |
4073 | + if result.status != 'PASS': |
4074 | + return result.result() |
4075 | + |
4076 | + if self.status == 'SETUP': |
4077 | + timeout = self.timeout or 0 |
4078 | + self.status = "RUN" |
4079 | + self.save_state_callback() |
4080 | + result.add_result(run_cmd(self.command, timeout=timeout, cwd=self.working_dir)) |
4081 | + |
4082 | + # Clean up whether 'command' failed or not. |
4083 | + self.cleanup(result) |
4084 | + |
4085 | + need_reboot = False |
4086 | + if self.reboot == 'always' or self.reboot == 'pass' and result.status == 'PASS': |
4087 | + need_reboot = True |
4088 | + |
4089 | + self.run_status = result.result() |
4090 | + |
4091 | + if self.status == 'CLEANUP': |
4092 | + self.status = "DONE" |
4093 | + self.save_state_callback() |
4094 | + |
4095 | + if need_reboot and self.reboot_callback is not None: |
4096 | + self.reboot_callback() |
4097 | + keep_going = False |
4098 | + |
4099 | + return keep_going |
4100 | + |
4101 | + def process_overrides(self, overrides): |
4102 | + """ |
4103 | + Sets override values from a TestSuite runlist for this test case. |
4104 | + """ |
4105 | + for item in overrides: |
4106 | + for key, value in item.iteritems(): |
4107 | + setattr(self, key, value) |
4108 | + |
4109 | + def load_state(self, state): |
4110 | + """ |
4111 | + Restore state from the supplied dictionary. |
4112 | + |
4113 | + Requires that 'state' has the same fieldnames as the TestCase class. |
4114 | + """ |
4115 | + self.__dict__.update(state) |
4116 | + |
4117 | + def save_state(self): |
4118 | + """ |
4119 | + Returns a dictionary representing the test's state. |
4120 | + """ |
4121 | + state = { |
4122 | + 'name': self.name, |
4123 | + 'path': self.path, |
4124 | + 'command': self.command, |
4125 | + 'timeout': self.timeout, |
4126 | + 'status': self.status, |
4127 | + 'build_cmd': self.build_cmd, |
4128 | + 'tc_setup': self.tc_setup, |
4129 | + 'tc_cleanup': self.tc_cleanup, |
4130 | + 'type': self.type, |
4131 | + 'description': self.description, |
4132 | + } |
4133 | + |
4134 | + return state |
4135 | + |
4136 | + def is_done(self): |
4137 | + """ |
4138 | + Determine if the case is done. This might mean that something has |
4139 | + failed. Used by suite to determine if the suite needs to be re-run |
4140 | + on resume. |
4141 | + """ |
4142 | + return self.status == 'DONE' or self.status == 'CLEANUP' |
4143 | |
4144 | |
4145 | === renamed file 'uath/testsuite.py' => 'uath/client/testsuite.py' |
4146 | --- uath/testsuite.py 2012-04-10 20:23:50 +0000 |
4147 | +++ uath/client/testsuite.py 2012-05-09 00:33:18 +0000 |
4148 | @@ -12,255 +12,268 @@ |
4149 | import yaml |
4150 | |
4151 | def parse_runlist_file(runlist_file): |
4152 | - """ |
4153 | - Parse a tslist.run runlist file for a list of test cases. |
4154 | - """ |
4155 | - |
4156 | - fp = open(runlist_file, 'r') |
4157 | - data = yaml.load(fp) |
4158 | - fp.close() |
4159 | - |
4160 | - return data |
4161 | + """ |
4162 | + Parse a tslist.run runlist file for a list of test cases. |
4163 | + """ |
4164 | + |
4165 | + fp = open(runlist_file, 'r') |
4166 | + data = yaml.load(fp) |
4167 | + fp.close() |
4168 | + |
4169 | + return data |
4170 | |
4171 | class TestSuite(object): |
4172 | - """ |
4173 | - The TestSuite class. |
4174 | - """ |
4175 | - |
4176 | - build_cmd = None |
4177 | - timeout = 0 |
4178 | - ts_setup = None |
4179 | - ts_cleanup = None |
4180 | - control_file = None |
4181 | - # status is one of 'NOTRUN', 'BUILD', 'SETUP', 'INPROGRESS', 'CLEANUP', and 'DONE' |
4182 | - status = "NOTRUN" |
4183 | - save_state_callback = do_nothing |
4184 | - |
4185 | - |
4186 | - def __init__(self, name, path, runlist_file=DEFAULT_TSLIST, |
4187 | - control_file=DEFAULT_TSCONTROL, includes=None, excludes=None, |
4188 | - result=None, result_class=Result, |
4189 | - _control_data=None, _runlist_data=None, |
4190 | - _save_state_callback=None, _reboot_callback=None): |
4191 | - |
4192 | - self.path = os.path.join(path, name) |
4193 | - self.reboot_callback = _reboot_callback |
4194 | - |
4195 | - # work from within the testsuite's directory. eg. /var/uath/examples |
4196 | - old_cwd = os.getcwd() |
4197 | - os.chdir(self.path) |
4198 | - """ |
4199 | - Build a TestSuite from a control file's data. |
4200 | - """ |
4201 | - control_file = os.path.join(name, control_file) |
4202 | - |
4203 | - if os.path.exists(control_file): |
4204 | - self.control_file = control_file |
4205 | - |
4206 | - self.runlist_file = os.path.join(name, runlist_file) |
4207 | - self.tests = [] |
4208 | - self.name = name |
4209 | - |
4210 | - if _save_state_callback is not None: |
4211 | - self.save_state_callback = _save_state_callback |
4212 | - |
4213 | - # Either use the result object passed in or create one. |
4214 | - if result is None: |
4215 | - self.result = result_class() |
4216 | - else: |
4217 | - self.result = result |
4218 | - |
4219 | - required_items = [] |
4220 | - optional_items = ['build_cmd', |
4221 | - 'timeout', |
4222 | - 'ts_setup', |
4223 | - 'ts_cleanup', |
4224 | - ] |
4225 | - |
4226 | - control_data = None |
4227 | - |
4228 | - if _control_data is not None: |
4229 | - control_data = _control_data |
4230 | - elif self.control_file is not None: |
4231 | - control_data = parse_control_file(self.control_file, |
4232 | - required=required_items, optional=optional_items) |
4233 | - |
4234 | - if control_data is not None: |
4235 | - self.__dict__.update(control_data) |
4236 | - |
4237 | - if _runlist_data is not None: |
4238 | - runlist_data = _runlist_data |
4239 | - elif os.path.exists(self.runlist_file): |
4240 | - runlist_data = parse_runlist_file(self.runlist_file) |
4241 | - else: |
4242 | - runlist_data = [] |
4243 | - |
4244 | - for test in runlist_data: |
4245 | - name = test['test'] |
4246 | - test_path = os.path.join(self.name, name) |
4247 | - |
4248 | - if includes is not None and name not in includes: |
4249 | - continue |
4250 | - |
4251 | - if excludes is not None and name in excludes: |
4252 | - continue |
4253 | - |
4254 | - command = test.get('command') |
4255 | - |
4256 | - tc = TestCase(name=name, path=test_path, |
4257 | - command=command, timeout=self.timeout, |
4258 | - result=self.result, |
4259 | - _save_state_callback=self.save_state_callback, |
4260 | - _reboot_callback=self.reboot_callback) |
4261 | - if 'overrides' in test: |
4262 | - tc.process_overrides(test['overrides']) |
4263 | - |
4264 | - self.tests.append(tc) |
4265 | - |
4266 | - # restore the old working directory |
4267 | - os.chdir(old_cwd) |
4268 | - |
4269 | - def __str__(self): |
4270 | - return "%s: %s" % (self.control_file, self.runlist_file) |
4271 | - |
4272 | - def set_status(self, status): |
4273 | - """ |
4274 | - Set the status for the test suite and call the save state callback. |
4275 | - """ |
4276 | - self.status = status |
4277 | - self.save_state_callback() |
4278 | - |
4279 | - def build(self, result): |
4280 | - """ |
4281 | - Run build, but only if we haven't started a run yet. |
4282 | - """ |
4283 | - if self.status == 'NOTRUN': |
4284 | - self.set_status('BUILD') |
4285 | - result.add_result(run_cmd(self.build_cmd, cwd=self.name)) |
4286 | - |
4287 | - def setup(self, result): |
4288 | - """ |
4289 | - Run ts_setup, but only if build() has just run successfully. |
4290 | - """ |
4291 | - if self.status == 'BUILD' and result.status == 'SUCCESS': |
4292 | - self.set_status('SETUP') |
4293 | - result.add_result(run_cmd(self.ts_setup, cwd=self.name)) |
4294 | - |
4295 | - def cleanup(self, result): |
4296 | - """ |
4297 | - Run ts_cleanup after a run. |
4298 | - """ |
4299 | - if self.status == 'INPROGRESS': |
4300 | - self.set_status('CLEANUP') |
4301 | - result.add_result(run_cmd(self.ts_cleanup, cwd=self.name)) |
4302 | - |
4303 | - |
4304 | - def run(self): |
4305 | - """ |
4306 | - Run the test cases; including any build, setup, and cleanup commands. |
4307 | - """ |
4308 | - |
4309 | - # Return value to indicate whether processing of a Runner should |
4310 | - # continue. This is to avoid a shutdown race on reboot cases. |
4311 | - keep_going = True |
4312 | - |
4313 | - # Work from the testsuite directory |
4314 | - os.chdir(self.path) |
4315 | - |
4316 | - result = self.result |
4317 | - |
4318 | - self.build(result) |
4319 | - self.setup(result) |
4320 | - |
4321 | - # Always enter this loop since test.run() will handle checking if |
4322 | - # the test has already been run. |
4323 | - self.status = "INPROGRESS" |
4324 | - if result.status == 'SUCCESS': |
4325 | - for test in self.tests: |
4326 | - keep_going = test.run(self.result) |
4327 | - |
4328 | - if not keep_going: |
4329 | - return keep_going |
4330 | - |
4331 | - self.cleanup(result) |
4332 | - |
4333 | - if self.status == 'CLEANUP': |
4334 | - result.result() |
4335 | - self.set_status("DONE") |
4336 | - |
4337 | - return keep_going |
4338 | - |
4339 | - def add_test(self, tests): |
4340 | - """ |
4341 | - Add a single test or list of tests to this suite. |
4342 | - """ |
4343 | - if isinstance(tests, TestCase): |
4344 | - self.tests.append(tests) |
4345 | - else: |
4346 | - try: |
4347 | - for test in tests: |
4348 | - self.add_test(test) |
4349 | - except TypeError: |
4350 | - pass |
4351 | - |
4352 | - def count_tests(self): |
4353 | - return len(self.tests) |
4354 | - |
4355 | - def load_state(self, state): |
4356 | - """ |
4357 | - Restore our state from the supplied dictionary. |
4358 | - |
4359 | - Requires that the fieldnames in the dictionary match the class |
4360 | - properties. |
4361 | - """ |
4362 | - # XXX: Should this be done explicitly? |
4363 | - self.__dict__.update(state) |
4364 | - |
4365 | - self.tests = [] |
4366 | - for state_test in state['tests']: |
4367 | - test = TestCase(name=state_test['name'], path=state_test['path'], |
4368 | - result=self.result, |
4369 | - _save_state_callback=self.save_state_callback, |
4370 | - _reboot_callback=self.reboot_callback) |
4371 | - test.load_state(state_test) |
4372 | - self.tests.append(test) |
4373 | - |
4374 | - def save_state(self): |
4375 | - state = { |
4376 | - 'name': self.name, |
4377 | - 'status': self.status, |
4378 | - 'tests': [], |
4379 | - 'ts_setup': self.ts_setup, |
4380 | - 'ts_cleanup': self.ts_cleanup, |
4381 | - 'build_cmd': self.build_cmd, |
4382 | - 'timeout': self.timeout, |
4383 | - } |
4384 | - |
4385 | - for test in self.tests: |
4386 | - state['tests'].append(test.save_state()) |
4387 | - |
4388 | - return state |
4389 | - |
4390 | - def is_done(self): |
4391 | - """ |
4392 | - Determine if the suite is done. This might mean that something has |
4393 | - failed. Used by Runner to determine if the suite needs to be re-run |
4394 | - on resume. |
4395 | - """ |
4396 | - return self.status == 'DONE' or self.status == 'CLEANUP' |
4397 | - |
4398 | - def get_next_test(self): |
4399 | - """ |
4400 | - Return the next test to be run. |
4401 | - |
4402 | - Mainly used for debugging. |
4403 | - """ |
4404 | - |
4405 | - test = None |
4406 | - |
4407 | - for t in self.tests: |
4408 | - if not t.is_done(): |
4409 | - test = t |
4410 | - break |
4411 | - |
4412 | - return test |
4413 | + """ |
4414 | + The TestSuite class. |
4415 | + """ |
4416 | + |
4417 | + build_cmd = None |
4418 | + timeout = 0 |
4419 | + ts_setup = None |
4420 | + ts_cleanup = None |
4421 | + control_file = None |
4422 | + # status is one of 'NOTRUN', 'BUILD', 'SETUP', 'INPROGRESS', 'CLEANUP', and 'DONE' |
4423 | + status = "NOTRUN" |
4424 | + save_state_callback = do_nothing |
4425 | + |
4426 | + |
4427 | + def __init__(self, name, path, runlist_file=DEFAULT_TSLIST, |
4428 | + control_file=DEFAULT_TSCONTROL, includes=None, excludes=None, |
4429 | + result=None, result_class=Result, |
4430 | + _control_data=None, _runlist_data=None, |
4431 | + _save_state_callback=None, _reboot_callback=None): |
4432 | + |
4433 | + self.path = os.path.join(path, name) |
4434 | + self.reboot_callback = _reboot_callback |
4435 | + self.passes = 0 |
4436 | + self.failures = 0 |
4437 | + self.errors = 0 |
4438 | + |
4439 | + # work from within the testsuite's directory. eg. /var/lib/uath/examples |
4440 | + old_cwd = os.getcwd() |
4441 | + os.chdir(self.path) |
4442 | + """ |
4443 | + Build a TestSuite from a control file's data. |
4444 | + """ |
4445 | + control_file = os.path.join(name, control_file) |
4446 | + |
4447 | + if os.path.exists(control_file): |
4448 | + self.control_file = control_file |
4449 | + |
4450 | + self.runlist_file = os.path.join(name, runlist_file) |
4451 | + self.tests = [] |
4452 | + self.name = name |
4453 | + |
4454 | + if _save_state_callback is not None: |
4455 | + self.save_state_callback = _save_state_callback |
4456 | + |
4457 | + # Either use the result object passed in or create one. |
4458 | + if result is None: |
4459 | + self.result = result_class() |
4460 | + else: |
4461 | + self.result = result |
4462 | + |
4463 | + required_items = [] |
4464 | + optional_items = ['build_cmd', |
4465 | + 'timeout', |
4466 | + 'ts_setup', |
4467 | + 'ts_cleanup', |
4468 | + ] |
4469 | + |
4470 | + control_data = None |
4471 | + |
4472 | + if _control_data is not None: |
4473 | + control_data = _control_data |
4474 | + elif self.control_file is not None: |
4475 | + control_data = parse_control_file(self.control_file, |
4476 | + required=required_items, optional=optional_items) |
4477 | + |
4478 | + if control_data is not None: |
4479 | + self.__dict__.update(control_data) |
4480 | + |
4481 | + if _runlist_data is not None: |
4482 | + runlist_data = _runlist_data |
4483 | + elif os.path.exists(self.runlist_file): |
4484 | + runlist_data = parse_runlist_file(self.runlist_file) |
4485 | + else: |
4486 | + runlist_data = [] |
4487 | + |
4488 | + for test in runlist_data: |
4489 | + name = test['test'] |
4490 | + test_path = os.path.join(self.name, name) |
4491 | + |
4492 | + if includes is not None and name not in includes: |
4493 | + continue |
4494 | + |
4495 | + if excludes is not None and name in excludes: |
4496 | + continue |
4497 | + |
4498 | + command = test.get('command') |
4499 | + |
4500 | + tc = TestCase(name=name, path=test_path, |
4501 | + command=command, timeout=self.timeout, |
4502 | + result=self.result, |
4503 | + _save_state_callback=self.save_state_callback, |
4504 | + _reboot_callback=self.reboot_callback) |
4505 | + if 'overrides' in test: |
4506 | + tc.process_overrides(test['overrides']) |
4507 | + |
4508 | + self.tests.append(tc) |
4509 | + |
4510 | + # restore the old working directory |
4511 | + os.chdir(old_cwd) |
4512 | + |
4513 | + def __str__(self): |
4514 | + return "%s: %s" % (self.control_file, self.runlist_file) |
4515 | + |
4516 | + def set_status(self, status): |
4517 | + """ |
4518 | + Set the status for the test suite and call the save state callback. |
4519 | + """ |
4520 | + self.status = status |
4521 | + self.save_state_callback() |
4522 | + |
4523 | + def build(self, result): |
4524 | + """ |
4525 | + Run build, but only if we haven't started a run yet. |
4526 | + """ |
4527 | + if self.status == 'NOTRUN': |
4528 | + self.set_status('BUILD') |
4529 | + result.add_result(run_cmd(self.build_cmd, cwd=self.name)) |
4530 | + |
4531 | + def setup(self, result): |
4532 | + """ |
4533 | + Run ts_setup, but only if build() has just passed. |
4534 | + """ |
4535 | + if self.status == 'BUILD' and result.status == 'PASS': |
4536 | + self.set_status('SETUP') |
4537 | + result.add_result(run_cmd(self.ts_setup, cwd=self.name)) |
4538 | + |
4539 | + def cleanup(self, result): |
4540 | + """ |
4541 | + Run ts_cleanup after a run. |
4542 | + """ |
4543 | + if self.status == 'INPROGRESS': |
4544 | + self.set_status('CLEANUP') |
4545 | + result.add_result(run_cmd(self.ts_cleanup, cwd=self.name)) |
4546 | + |
4547 | + |
4548 | + def run(self): |
4549 | + """ |
4550 | + Run the test cases; including any build, setup, and cleanup commands. |
4551 | + """ |
4552 | + |
4553 | + # Return value to indicate whether processing of a Runner should |
4554 | + # continue. This is to avoid a shutdown race on reboot cases. |
4555 | + keep_going = True |
4556 | + |
4557 | + # Work from the testsuite directory |
4558 | + os.chdir(self.path) |
4559 | + |
4560 | + result = self.result |
4561 | + |
4562 | + self.build(result) |
4563 | + self.setup(result) |
4564 | + |
4565 | + # Always enter this loop since test.run() will handle checking if |
4566 | + # the test has already been run. |
4567 | + self.status = "INPROGRESS" |
4568 | + if result.status == 'PASS': |
4569 | + for test in self.tests: |
4570 | + keep_going = test.run(self.result) |
4571 | + |
4572 | + if test.run_status == 'PASS': |
4573 | + self.passes += 1 |
4574 | + elif test.run_status == 'FAIL': |
4575 | + self.failures += 1 |
4576 | + elif test.run_status == 'ERROR': |
4577 | + self.errors += 1 |
4578 | + |
4579 | + if not keep_going: |
4580 | + return keep_going |
4581 | + |
4582 | + self.cleanup(result) |
4583 | + |
4584 | + if self.status == 'CLEANUP': |
4585 | + result.result() |
4586 | + self.set_status("DONE") |
4587 | + |
4588 | + return keep_going |
4589 | + |
4590 | + def add_test(self, tests): |
4591 | + """ |
4592 | + Add a single test or list of tests to this suite. |
4593 | + """ |
4594 | + if isinstance(tests, TestCase): |
4595 | + self.tests.append(tests) |
4596 | + else: |
4597 | + try: |
4598 | + for test in tests: |
4599 | + self.add_test(test) |
4600 | + except TypeError: |
4601 | + pass |
4602 | + |
4603 | + def count_tests(self): |
4604 | + return len(self.tests) |
4605 | + |
4606 | + def load_state(self, state): |
4607 | + """ |
4608 | + Restore our state from the supplied dictionary. |
4609 | + |
4610 | + Requires that the fieldnames in the dictionary match the class |
4611 | + properties. |
4612 | + """ |
4613 | + # XXX: Should this be done explicitly? |
4614 | + self.__dict__.update(state) |
4615 | + |
4616 | + self.tests = [] |
4617 | + for state_test in state['tests']: |
4618 | + test = TestCase(name=state_test['name'], path=state_test['path'], |
4619 | + result=self.result, |
4620 | + _save_state_callback=self.save_state_callback, |
4621 | + _reboot_callback=self.reboot_callback) |
4622 | + test.load_state(state_test) |
4623 | + self.tests.append(test) |
4624 | + |
4625 | + def save_state(self): |
4626 | + state = { |
4627 | + 'name': self.name, |
4628 | + 'status': self.status, |
4629 | + 'passes': self.passes, |
4630 | + 'failures': self.failures, |
4631 | + 'errors': self.errors, |
4632 | + 'tests': [], |
4633 | + 'ts_setup': self.ts_setup, |
4634 | + 'ts_cleanup': self.ts_cleanup, |
4635 | + 'build_cmd': self.build_cmd, |
4636 | + 'timeout': self.timeout, |
4637 | + } |
4638 | + |
4639 | + for test in self.tests: |
4640 | + state['tests'].append(test.save_state()) |
4641 | + |
4642 | + return state |
4643 | + |
4644 | + def is_done(self): |
4645 | + """ |
4646 | + Determine if the suite is done. This might mean that something has |
4647 | + failed. Used by Runner to determine if the suite needs to be re-run |
4648 | + on resume. |
4649 | + """ |
4650 | + return self.status == 'DONE' or self.status == 'CLEANUP' |
4651 | + |
4652 | + def get_next_test(self): |
4653 | + """ |
4654 | + Return the next test to be run. |
4655 | + |
4656 | + Mainly used for debugging. |
4657 | + """ |
4658 | + |
4659 | + test = None |
4660 | + |
4661 | + for t in self.tests: |
4662 | + if not t.is_done(): |
4663 | + test = t |
4664 | + break |
4665 | + |
4666 | + return test |
4667 | |
4668 | === modified file 'uath/exceptions.py' |
4669 | --- uath/exceptions.py 2012-04-05 15:39:41 +0000 |
4670 | +++ uath/exceptions.py 2012-05-09 00:33:18 +0000 |
4671 | @@ -6,9 +6,3 @@ |
4672 | """ |
4673 | def __init__(self, msg): |
4674 | self.msg = msg |
4675 | - |
4676 | -class BadDir(Exception): |
4677 | - pass |
4678 | - |
4679 | -class MissingFile(Exception): |
4680 | - pass |
4681 | |
4682 | === modified file 'uath/provisioning/catalog/__init__.py' |
4683 | --- uath/provisioning/catalog/__init__.py 2012-04-06 15:23:15 +0000 |
4684 | +++ uath/provisioning/catalog/__init__.py 2012-05-09 00:33:18 +0000 |
4685 | @@ -1,3 +1,3 @@ |
4686 | +from .exceptions import * |
4687 | from .base import * |
4688 | -from .exceptions import * |
4689 | from .sqlite import * |
4690 | |
4691 | === modified file 'uath/provisioning/catalog/base.py' |
4692 | --- uath/provisioning/catalog/base.py 2012-04-06 21:35:03 +0000 |
4693 | +++ uath/provisioning/catalog/base.py 2012-05-09 00:33:18 +0000 |
4694 | @@ -1,20 +1,29 @@ |
4695 | #!/usr/bin/python |
4696 | |
4697 | import os |
4698 | -from .exceptions import * |
4699 | +from uath.provisioning.catalog.exceptions import * |
4700 | |
4701 | class Catalog(object): |
4702 | """ |
4703 | - Provide a generic class for an arbitrary catlog of machines. |
4704 | + Provide a generic class for an arbitrary catalog of machines. |
4705 | |
4706 | - Raise exceptions for most methods. |
4707 | + Raise exceptions for most methods, since they should be defined by |
4708 | + subclasses. |
4709 | + Except for special read-only cases, subclasses should provide request(). |
4710 | + Subclasses will generally also need to provide delete() for cleanup |
4711 | + purposes. |
4712 | + All other methods (read, write, release, destroy) are optional. |
4713 | """ |
4714 | - def __init__(self, uniqueid, lockfile = os.path.expanduser('~/.uath-catalog')): |
4715 | + def __init__(self, uniqueid, lockfile=os.path.expanduser('~/.uath-catalog')): |
4716 | """ |
4717 | - To avoid provisioning machines to multiple requestors simultaneously, Catalog implements a lock file. |
4718 | - Each catalog type should provide a unique id, which is combined with the class name to generate a catalog identifier. |
4719 | - This identifier is stored in the lock file and checked on class instantiation to ensure the same catalog is running. |
4720 | - Subclasses can circumvent this by not calling the superclass __init__ function if necessary. |
4721 | + To avoid provisioning machines to multiple requestors simultaneously, |
4722 | + Catalog implements a lock file. |
4723 | + Each catalog type should provide a unique id, which is combined with |
4724 | + the class name to generate a catalog identifier. |
4725 | + This identifier is stored in the lock file and checked on class |
4726 | + instantiation to ensure the same catalog is running. |
4727 | + Subclasses can circumvent this by not calling the superclass __init__ |
4728 | + function if necessary. |
4729 | """ |
4730 | catalog = self.__class__.__name__ + str(uniqueid) |
4731 | self.lockfile = lockfile |
4732 | @@ -31,22 +40,50 @@ |
4733 | |
4734 | def delete(self): |
4735 | """ |
4736 | - Deleting a catalog removes the lock file so another catalog can be created. |
4737 | + Delete this catalog and remove the lockfile to facilitate use of |
4738 | + another one. |
4739 | + Subclasses can call this via super to delete the lockfile, or |
4740 | + implement lockfile deletion in their delete() method. |
4741 | """ |
4742 | os.unlink(self.lockfile) |
4743 | del self |
4744 | |
4745 | def read(self): |
4746 | - raise UATHProvisioningCatalogException('Method not defined for this catalog') |
4747 | + """ |
4748 | + Read information about the catalog from any available sources. |
4749 | + Primarily intended to force a synchronization of new external data if |
4750 | + any exists. |
4751 | + """ |
4752 | + return False |
4753 | |
4754 | def write(self): |
4755 | - raise UATHProvisioningCatalogException('Method not defined for this catalog') |
4756 | + """ |
4757 | + Force a commit all current catalog data in memory to internal or |
4758 | + external storage. |
4759 | + """ |
4760 | + return False |
4761 | |
4762 | - def provision(self): |
4763 | + def request(self, machinetype=None, *args, **kw): |
4764 | + """ |
4765 | + Return a Machine object that can be provisioned and used to run tests. |
4766 | + May interpret arguments and call the appropriate constructor with the |
4767 | + appropriate arguments, or may take a Machine class as an argument and |
4768 | + send all arguments to that constructor. |
4769 | + """ |
4770 | raise UATHProvisioningCatalogException('Method not defined for this catalog') |
4771 | |
4772 | def release(self): |
4773 | - raise UATHProvisioningCatalogException('Method not defined for this catalog') |
4774 | + """ |
4775 | + Indicate that a machine is not now in use for tests, but may still have |
4776 | + a working installed system, potentially suitable for further use. |
4777 | + """ |
4778 | + return False |
4779 | |
4780 | def destroy(self): |
4781 | - raise UATHProvisioningCatalogException('Method not defined for this catalog') |
4782 | + """ |
4783 | + Remove a machine from a catalog so that no further use of it will be |
4784 | + requested or expected. |
4785 | + Current implementations do not call machine.destroy(), requiring that |
4786 | + do be done separately. |
4787 | + """ |
4788 | + return False |
4789 | |
4790 | === modified file 'uath/provisioning/catalog/sqlite.py' |
4791 | --- uath/provisioning/catalog/sqlite.py 2012-04-09 20:29:51 +0000 |
4792 | +++ uath/provisioning/catalog/sqlite.py 2012-05-09 00:33:18 +0000 |
4793 | @@ -2,15 +2,15 @@ |
4794 | |
4795 | import sqlite3 |
4796 | import os |
4797 | -from .exceptions import * |
4798 | -from .base import Catalog |
4799 | +from uath.provisioning.catalog.exceptions import * |
4800 | +from uath.provisioning.catalog.base import Catalog |
4801 | from uath.provisioning.vm import VMToolsKVM |
4802 | |
4803 | class SQLiteCatalog(Catalog): |
4804 | """ |
4805 | Base class for SQLite catalog, opens a database connection and sets up a cursor. |
4806 | """ |
4807 | - def __init__(self, db = os.path.expanduser('~/.uath-sqlite-catalog'), *args, **kw): |
4808 | + def __init__(self, db=os.path.expanduser('~/.uath-sqlite-catalog'), *args, **kw): |
4809 | super(SQLiteCatalog, self).__init__(uniqueid = db, *args, **kw) |
4810 | self.db = db |
4811 | self.connection = sqlite3.connect(self.db) |
4812 | @@ -31,9 +31,10 @@ |
4813 | Initialize simple database. |
4814 | """ |
4815 | super(TinySQLiteCatalog, self).__init__(*args, **kw) |
4816 | + self.connection.isolation_level = None |
4817 | self.connection.execute('CREATE TABLE IF NOT EXISTS machines(machineid INTEGER PRIMARY KEY, state TEXT)') |
4818 | |
4819 | - def request(self, machinetype = VMToolsKVM, *args, **kw): |
4820 | + def request(self, machinetype=VMToolsKVM, *args, **kw): |
4821 | """ |
4822 | Takes a Machine class as machinetype, and passes the newly generated machineid along with all other arguments to that class's constructor, returning the resulting object. |
4823 | """ |
4824 | @@ -46,10 +47,16 @@ |
4825 | """ |
4826 | Updates the database to indicate the machine is available. |
4827 | """ |
4828 | - return self.connection.execute("UPDATE machines SET state='available' WHERE machineid=?", [machineid]) |
4829 | + if self.connection.execute("UPDATE machines SET state='available' WHERE machineid=?", [machineid]): |
4830 | + return True |
4831 | + else: |
4832 | + return False |
4833 | |
4834 | def destroy(self, machineid): |
4835 | """ |
4836 | Updates the database to indicate the machine is destroyed, but does not destroy the machine. |
4837 | """ |
4838 | - return self.connection.execute("UPDATE machines SET state='destroyed' WHERE machineid=?", [machineid]) |
4839 | + if self.connection.execute("UPDATE machines SET state='destroyed' WHERE machineid=?", [machineid]): |
4840 | + return True |
4841 | + else: |
4842 | + return False |
4843 | |
4844 | === modified file 'uath/provisioning/provisioning.py' |
4845 | --- uath/provisioning/provisioning.py 2012-04-05 17:14:32 +0000 |
4846 | +++ uath/provisioning/provisioning.py 2012-05-09 00:33:18 +0000 |
4847 | @@ -1,18 +1,52 @@ |
4848 | #!/usr/bin/python |
4849 | |
4850 | -from .exceptions import * |
4851 | +import logging |
4852 | +import logging.handlers |
4853 | +import os.path |
4854 | +import socket |
4855 | +import sys |
4856 | + |
4857 | +import apt.cache |
4858 | + |
4859 | +from uath.provisioning.exceptions import * |
4860 | |
4861 | class Machine(object): |
4862 | """ |
4863 | Provide a generic class to provision an arbitrary machine. |
4864 | |
4865 | - Raise exceptions for most methods. |
4866 | + Raise exceptions for most methods, since subclasses should provide them. |
4867 | + A fully implemented subclass will provide all public methods except read, |
4868 | + I.E.: |
4869 | + provisioncheck, activecheck, getclientdeb, installclient |
4870 | + (all these can be used from Machine or replaced.) |
4871 | + destroy, stop, uploadfiles, downloadfiles, run |
4872 | + (these must be implemented separately.) |
4873 | """ |
4874 | - def __init__(self, arch = None, initrd = None, installtype = None, iso = None, kernel = None, machineid = None, name = None, new = False, preseed = None, series = None, template = None): |
4875 | + def __init__(self, arch=None, debug=False, image=None, initrd=None, |
4876 | + installtype=None, kernel=None, machineid=None, name=None, |
4877 | + new=False, preseed=None, series=None, template=None): |
4878 | + """ |
4879 | + Initialize the object representing the machine. |
4880 | + One of these groups of arguments should be included to specify the |
4881 | + provisioning method: |
4882 | + series, installtype, arch: Download an ISO from the mirrors and use |
4883 | + it to install the machine. |
4884 | + image, kernel, initrd, preseed: Install the machine from a |
4885 | + specified image/ISO file, with an optional kernel, initrd, and |
4886 | + preseed. |
4887 | + template: Clone the machine from a template or existing machine. |
4888 | + name: Request a specific machine. Combine with other groups to |
4889 | + reinstall a specific machine. |
4890 | + Other arguments: |
4891 | + debug: Enable debug logging. |
4892 | + new: Request a new machine (or a reinstall if a specific machine |
4893 | + was requested.) |
4894 | + """ |
4895 | self.arch = arch |
4896 | + self.debug = debug |
4897 | + self.image = image |
4898 | self.initrd = initrd |
4899 | self.installtype = installtype |
4900 | - self.iso = iso |
4901 | self.kernel = kernel |
4902 | self.machineid = machineid |
4903 | self.name = name |
4904 | @@ -22,36 +56,176 @@ |
4905 | self.template = template |
4906 | self.provisioned = False |
4907 | self.active = False |
4908 | + self.logger = logging.getLogger('uath.provisioning' + self.name) |
4909 | + self._loggersetup() |
4910 | + self.logger.debug('Machine init finished') |
4911 | + |
4912 | + def _loggersetup(self): |
4913 | + """ |
4914 | + Initialize the logging for the machine. |
4915 | + Subclasses can override or supplement to customize logging. |
4916 | + """ |
4917 | + self.consolehandler = logging.StreamHandler(stream = sys.stderr) |
4918 | + self.consolehandler.setFormatter(logging.Formatter('%(levelname)s: %(message)s')) |
4919 | + self.consolehandler.setLevel(logging.WARNING) |
4920 | + self.filehandler = logging.handlers.WatchedFileHandler('/var/log/uath/' + socket.gethostname() + '.log') |
4921 | + self.filehandler.setFormatter(logging.Formatter('%(asctime)s ' + self.name + ' %(levelname)s: %(message)s')) |
4922 | + self.filehandler.setLevel(logging.DEBUG) |
4923 | + self.logger.addHandler(self.consolehandler) |
4924 | + self.logger.addHandler(self.filehandler) |
4925 | + if self.debug: |
4926 | + self.logger.setLevel(logging.DEBUG) |
4927 | + else: |
4928 | + self.logger.setLevel(logging.INFO) |
4929 | |
4930 | def provisioncheck(self): |
4931 | """ |
4932 | - Make sure the machine is provisioned. |
4933 | + Check if the machine is provisioned, provision it if necessary, and |
4934 | + raise an exception if it cannot be provisioned. |
4935 | """ |
4936 | + self.logger.debug('Checking if machine is provisioned') |
4937 | if not self.provisioned: |
4938 | - self.provision() |
4939 | + self._provision() |
4940 | |
4941 | def activecheck(self): |
4942 | """ |
4943 | - Make sure the machine is running. |
4944 | + Check if the machine is running and able to accept comamnds, start it |
4945 | + if necessary, and raise an exception if it cannot be started. |
4946 | """ |
4947 | + self.logger.debug('Checking if machine is active') |
4948 | if not self.active: |
4949 | - self.start() |
4950 | - |
4951 | - def provision(self): |
4952 | + self._start() |
4953 | + |
4954 | + def getclientdeb(self): |
4955 | + """ |
4956 | + Return the path of the .deb file for the |
4957 | + ubuntu-automation-test-harness-client installed as part of the main |
4958 | + package. |
4959 | + """ |
4960 | + debpath = os.path.normpath(os.path.join('/usr','share','uath','ubuntu-automation-test-harness-client_' + apt.cache.Cache()['ubuntu-automation-test-harness'].installedVersion + '_all.deb')) |
4961 | + self.logger.debug('Client deb path is ' + debpath) |
4962 | + return debpath |
4963 | + |
4964 | + def installclient(self): |
4965 | + """ |
4966 | + Install the ubuntu-automation-test-harness-client package on the |
4967 | + machine, and raise an exception if this fails. |
4968 | + """ |
4969 | + self.logger.info('Installing client deb on machine') |
4970 | + tmppath = os.path.normpath('/tmp') |
4971 | + if self.uploadfiles([self.getclientdeb()], tmppath): |
4972 | + if self.run('DEBIAN_FRONTEND=noninteractive dpkg -i ' + os.path.join(tmppath, os.path.split(self.getclientdeb())[1]) + ' || apt-get -y -f install', root = True) == 0: |
4973 | + return True |
4974 | + else: |
4975 | + self.logger.error('Failed to install and configure ' + self.getclientdeb()) |
4976 | + raise UATHProvisioningException('Failed to install and configure ' + self.getclientdeb()) |
4977 | + else: |
4978 | + self.logger.error('Failed to copy ' + self.getclientdeb() + ' to machine') |
4979 | + raise UATHProvisioningException('Failed to copy ' + self.getclientdeb() + ' to machine') |
4980 | + |
4981 | + def _provision(self): |
4982 | + """ |
4983 | + Ready the machine for use, and set provisioned to True. |
4984 | + If new=True or the machine is not installed, this should install the |
4985 | + machine, possibly by calling _create(). |
4986 | + If an existing machine is requested, this should make the machine |
4987 | + available, possibly by caling _load(). |
4988 | + Should generally not be called directly outside of the class; |
4989 | + provisioncheck() or activecheck() should be used. |
4990 | + """ |
4991 | raise UATHProvisioningException('Method not defined for this machine type') |
4992 | |
4993 | - def create(self): |
4994 | + def _create(self): |
4995 | + """ |
4996 | + Install the system on the machine, and return True on success. |
4997 | + Can take arguments if provided by provisioncheck(), or can read |
4998 | + internal variables populated by __init__(). |
4999 | + Should generally not be called directly outside of the class; |
5000 | + provisioncheck() or activecheck() should be used. |
The diff has been truncated for viewing.