00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 PKG = 'qualification'
00039 import roslib
00040 roslib.load_manifest(PKG)
00041
00042 import sys, os
00043
00044 from qualification.test import *
00045
00046 import time
00047 from time import strftime
00048 from PIL import Image
00049 from cStringIO import StringIO
00050
00051 import tarfile, tempfile
00052 import socket
00053
00054 import smtplib
00055 from email.mime.multipart import MIMEMultipart
00056 from email.mime.text import MIMEText
00057 from email.mime.base import MIMEBase
00058 from email import Encoders
00059
00060 import wg_invent_client
00061
00062 from datetime import datetime
00063
00064 import shutil
00065
00066 import result_dir
00067 TEMP_DIR = result_dir.TEMP_DIR
00068 RESULTS_DIR = result_dir.RESULTS_DIR
00069
00070
00071 def write_temp_tar_file(results_dir):
00072 temp_tar_file = tempfile.NamedTemporaryFile()
00073
00074 tar_st = tarfile.open(temp_tar_file.name, 'w:')
00075 for filename in os.listdir(results_dir):
00076 tar_st.add(os.path.join(results_dir, filename), arcname=filename)
00077
00078 tar_st.close()
00079
00080 return temp_tar_file
00081
00082
00083 class TestScriptResult(object):
00084
00085 def __init__(self, test_script, srv_result):
00086 self.name = test_script.get_name()
00087 self.launch = test_script.launch_file
00088
00089 self._result = srv_result.result
00090 self.msg = srv_result.failure_msg
00091
00092 def get_name(self):
00093 return self.name
00094
00095 def get_launch(self):
00096 return os.path.basename(self.launch)
00097
00098 def get_pass_bool(self):
00099 return self._result == 0
00100
00101 def has_error(self):
00102 return self._result == 2
00103
00104 def get_result_msg(self):
00105 result_dict = { 0: "OK", 1: "Fail", 2: "Error"}
00106 return result_dict[self._result]
00107
00108 def get_msg(self):
00109 return self.msg
00110
00111
00112
00113
00114
00115 class SubTestResultType(object):
00116 __result_types = {
00117 0: "Pass",
00118 1: "Fail",
00119 2: "Human required",
00120 3: "Manual Failure",
00121 4: "Manual Pass",
00122 5: "Retry",
00123
00124 6: "Error" ,
00125 7: "Canceled" }
00126
00127
00128 def __init__(self, result):
00129 self._result = result
00130
00131 def cancel(self):
00132 self._result = 7
00133
00134 def retry(self):
00135 self._result = 5
00136
00137 def manual_pass(self):
00138
00139 if self._result == 0:
00140 return
00141 self._result = 4
00142
00143 def manual_fail(self):
00144
00145 if self._result == 1:
00146 return
00147 self._result = 3
00148
00149 def error(self):
00150 self._result = 6
00151
00152 def get_msg(self):
00153 return self.__result_types[self._result]
00154
00155 def get_html_msg(self):
00156 if self._result == 0:
00157 status_html ='<div class="pass">%s</div>' % self.get_msg()
00158 elif self._result == 3 or self._result == 2 or self._result == 5:
00159 status_html ='<div class="warn">%s</div>' % self.get_msg()
00160 else:
00161 status_html ='<div class="error">%s</div>' % self.get_msg()
00162
00163 return status_html
00164
00165 def get_pass_bool(self, manual_ok = True):
00166 if manual_ok:
00167 return self._result == 0 or self._result == 4
00168 return self._result == 0
00169
00170 def is_human_required(self):
00171 return self._result == 2
00172
00173 def is_manual(self):
00174 return self._result == 3 or self._result == 4
00175
00176 def is_retry(self):
00177 return self._result == 5
00178
00179 def is_error(self):
00180 return self._result == 6
00181
00182 def is_cancel(self):
00183 return self._result == 7
00184
00185
00186
00187
00188
00189
00190 class SubTestResult(object):
00191
00192
00193 def __init__(self, subtest, msg):
00194 self._subtest = subtest
00195
00196 self._result = SubTestResultType(msg.result)
00197
00198 self._text_result = msg.html_result
00199 if msg.text_summary is not None:
00200 self._summary = msg.text_summary
00201 else:
00202 self._summary = ''
00203
00204 self._subresult_note = ''
00205
00206 self._plots = []
00207 if msg.plots is not None:
00208 for plt in msg.plots:
00209 self._plots.append(plt)
00210
00211 self._retry_suffix = ''
00212 self._retry_name = ''
00213
00214 self.write_images()
00215
00216 self._params = msg.params
00217 self._values = msg.values
00218
00219 self._temp_image_files = []
00220
00221 def export_data(self):
00222 data = wg_invent_client.SubtestData(self.get_name(), self.get_result())
00223 data.set_note(self.get_note())
00224 for p in self.get_params():
00225 data.set_parameter(p.key, p.value)
00226 for m in self.get_values():
00227 data.set_measurement(m.key, m.value, m.min, m.max)
00228
00229 return data
00230
00231 def close(self):
00232 for file in self._temp_image_files:
00233 file.close()
00234
00235 def get_result(self):
00236 return self._result.get_msg()
00237
00238 def get_params(self):
00239 return self._params
00240
00241 def get_values(self):
00242 return self._values
00243
00244 def get_plots(self):
00245 return self._plots
00246
00247 def retry_test(self, count, notes):
00248 self.set_note(notes)
00249
00250 self._result.retry()
00251 self._retry_suffix = "_retry%d" % count
00252 self._retry_name = " Retry %d" % count
00253
00254 self.write_images()
00255
00256 def set_operator_result(self, pass_bool):
00257 if pass_bool:
00258 self._result.manual_pass()
00259 else:
00260 self._result.manual_fail()
00261
00262 def get_name(self):
00263 return self._subtest.get_name()
00264
00265 def get_pass_bool(self):
00266 return self._result.get_pass_bool()
00267
00268 def set_note(self, txt):
00269 self._subresult_note = txt
00270
00271 def get_note(self):
00272 return self._subresult_note
00273
00274 def filename_base(self):
00275 return self._subtest.get_name().replace(' ', '_').replace('/', '__') + str(self._subtest.get_key()) + self._retry_suffix
00276
00277 def filename(self):
00278 return self.filename_base() + '/index.html'
00279
00280
00281
00282
00283 def write_images(self, path = None):
00284 if not path:
00285 path = TEMP_DIR
00286
00287 dir_name = os.path.join(path, self.filename_base())
00288 if not os.path.isdir(dir_name):
00289 os.makedirs(dir_name)
00290
00291 for plot in self._plots:
00292 img_file = os.path.join(dir_name, plot.title + '.' + plot.image_format)
00293 open(img_file, 'w').write(plot.image)
00294
00295 def html_image_result(self, img_path):
00296 html = '<H4 ALIGN=CENTER>Result Details</H4>'
00297
00298
00299 html += self._text_result.replace('IMG_PATH', os.path.join(img_path, self.filename_base()))
00300
00301 return html
00302
00303
00304
00305
00306 def make_result_page(self, back_link = False, link_dir = TEMP_DIR, prev = None, next = None):
00307 html = "<html><head><title>Qualification Test Results: %s</title>\
00308 <style type=\"text/css\">\
00309 body { color: black; background: white; }\
00310 div.pass { background: green; padding: 0.5em; border: none; }\
00311 div.warn { background: yellow; padding: 0.5em; border: none; }\
00312 div.error { background: red; padding: 0.5em; border: none; }\
00313 strong { font-weight: bold; color: red; }\
00314 em { font-style:normal; font-weight: bold; }\
00315 </style>\
00316 </head><body>\n" % self._subtest.get_name()
00317
00318 html += self.html_header()
00319
00320 html += '<hr size="3">\n'
00321
00322
00323 html += self.html_image_result(link_dir)
00324
00325 html += '<hr size="3">\n'
00326 html += self._html_test_params()
00327 html += '<hr size="3">\n'
00328 html += self._html_test_values()
00329 html += '<hr size="3">\n'
00330 html += self._html_test_info()
00331 html += '<hr size="3">\n'
00332
00333
00334
00335 if back_link:
00336 if prev:
00337 prev_file = os.path.join(link_dir, prev.filename())
00338 html += '<p align=center><a href="%s">Previous: %s</a></p>\n' % (prev_file, prev.get_name())
00339
00340 back_index_file = os.path.join(link_dir, 'index.html')
00341 html += '<p align=center><a href="%s">Back to Index</a></p>\n' % back_index_file
00342
00343 if next:
00344 next_file = os.path.join(link_dir, next.filename())
00345 html += '<p align=center><a href="%s">Next: %s</a></p>\n' % (next_file, next.get_name())
00346
00347
00348 html += '</body></html>'
00349
00350 return html
00351
00352 def _html_test_params(self):
00353 html = ['<H4 align=center>Subtest Parameters</H4>']
00354
00355 if len(self._params) == 0:
00356 html.append('<p>No subtest parameters.</p>')
00357 else:
00358 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00359 html.append('<tr><td><b>Parameter</b></td><td><b>Value</b></td></tr>')
00360 for param in self._params:
00361 html.append('<tr><td>%s</td><td>%s</td></tr>' % (param.key, param.value))
00362 html.append('</table>')
00363
00364 return '\n'.join(html)
00365
00366 def _html_test_values(self):
00367 html = ['<H4 align=center>Subtest Measurements</H4>']
00368
00369 if len(self._values) == 0:
00370 html.append('<p>No subtest values.</p>')
00371 else:
00372 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00373 html.append('<tr><td><b>Measurement</b></td><td><b>Value</b></td><td><b>Min</b></td><td><b>Max</b></td></tr>')
00374 for value in self._values:
00375 html.append('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>' % (value.key, value.value, value.min, value.max))
00376 html.append('</table>')
00377
00378 return '\n'.join(html)
00379
00380
00381 def _html_test_info(self):
00382 html = ['<H4 align=center>Subtest Information</H4>']
00383
00384 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00385 html.append('<tr><td><b>Parameter</b></td><td><b>Value</b></td></tr>')
00386
00387 if self._subtest._pre_script:
00388 html.append('<tr><td>Pre-test</td><td>%s</td></tr>' % os.path.basename(self._subtest._pre_script))
00389 else:
00390 html.append('<tr><td>Pre-test</td><td>None</td></tr>')
00391
00392 if self._subtest._post_script:
00393 html.append('<tr><td>Post-test</td><td>%s</td></tr>' % os.path.basename(self._subtest._post_script))
00394 else:
00395 html.append('<tr><td>Post-test</td><td>None</td></tr>')
00396
00397 launch_file = self._subtest._test_script
00398 (path, launch_pkg) = roslib.packages.get_dir_pkg(launch_file)
00399 path_idx = launch_file.find(launch_pkg) + len(launch_pkg) + 1
00400
00401
00402 html.append('<tr><td>Launch package</td><td>%s</td></tr>' % launch_pkg)
00403 html.append('<tr><td>Launch filepath</td><td>%s</td></tr>' % launch_file[path_idx:])
00404
00405 html.append('</table>')
00406
00407 return '\n'.join(html)
00408
00409
00410
00411 def html_header(self):
00412 html = ['<H4 ALIGN=CENTER>Results of %s%s</H4>\n' % (self._subtest.get_name(), self._retry_name)]
00413
00414 html.append("<p><b>Test Status:</b></p>\n<H4>%s</H4>\n" % self._result.get_html_msg())
00415
00416 if self._summary != '':
00417 html.append('<p><em>Summary</em></p>\n' )
00418 html.append('<p>%s</p>\n' % self._summary)
00419 if self._subresult_note != '':
00420 html.append('<p><em>Operator\'s Notes:</em></p>\n')
00421 html.append('<p>%s</p>\n' % self._subresult_note)
00422
00423 return '\n'.join(html)
00424
00425 def make_index_line(self, link, link_dir):
00426 result = self._result.get_html_msg()
00427
00428 path = os.path.join(link_dir, self.filename())
00429 if link:
00430 hyperlink = '<a href=\"%s\">%s</a>' % (path, self._subtest.get_name())
00431 else:
00432 hyperlink = self._subtest.get_name()
00433
00434 summary = '\n'.join([self._summary, self._subresult_note])
00435
00436 return '<tr><td>%s</td><td>%s</td><td>%s</td></tr>\n' % (hyperlink, summary, result)
00437
00438
00439
00440
00441
00442
00443
00444 class QualTestResult(object):
00445
00446
00447
00448 def __init__(self, qual_item, qual_test, start_time):
00449 self._qual_test = qual_test
00450
00451 self._subresults = []
00452
00453 self._retrys = []
00454
00455 self._prestarts = []
00456
00457 self._shutdown_result = None
00458
00459 self._start_time = start_time
00460 self._start_time_filestr = self._start_time.strftime("%Y%m%d_%H%M%S")
00461 self._start_time_name = self._start_time.strftime("%Y/%m/%d %I:%M%p")
00462
00463 self._item = qual_item
00464 self._serial = qual_item.serial
00465 self._item_name = qual_item.name
00466
00467 if not os.path.isdir(TEMP_DIR):
00468 os.makedirs(TEMP_DIR)
00469
00470
00471
00472 try:
00473 config = qual_item._config
00474 self._config_only = True
00475 except Exception, e:
00476 self._config_only = False
00477
00478 self._tar_filename = ''
00479
00480 self._results_name = '%s_%s' % (self._serial, self._start_time_filestr)
00481
00482
00483 self._made_dir = None
00484 self.set_results_dir(os.path.join(RESULTS_DIR, self._results_name))
00485
00486 self._error = False
00487 self._canceled = False
00488
00489 self._test_log = {}
00490
00491 self._note = ''
00492 self._operator = ''
00493
00494 def close(self):
00495
00496 if self._made_dir and len(os.listdir(self._made_dir)) == 0:
00497 os.rmdir(self._made_dir)
00498
00499
00500 shutil.rmtree(TEMP_DIR)
00501
00502 @property
00503 def results_dir(self): return self._results_dir
00504
00505 @property
00506 def tar_name(self): return self._tar_filename
00507
00508 def set_results_dir(self, path):
00509 if not path.endswith('/'):
00510 path += '/'
00511
00512 self._results_dir = path
00513 if not os.path.isdir(self._results_dir):
00514 self._made_dir = self._results_dir
00515 os.makedirs(self._results_dir)
00516 else:
00517 self._made_dir = None
00518
00519
00520 def set_notes(self, note):
00521 self._note = note
00522
00523 def set_operator(self, name):
00524 self._operator = name
00525
00526 def log(self, entry):
00527 self._test_log[datetime.now()] = entry
00528
00529
00530 def get_prestarts(self):
00531 return self._prestarts[:]
00532
00533 def get_subresults(self, reverse = False):
00534 vals = self._subresults[:]
00535 if reverse:
00536 vals = vals.reverse()
00537 return vals
00538
00539 def get_retrys(self, reverse = False):
00540 vals = self._retrys[:]
00541 if reverse:
00542 vals = vals.reverse()
00543 return vals
00544
00545
00546 def get_subresult(self, index):
00547 if len(self._subresults) == 0 or index >= len(self._subresults) or index < 0:
00548 return None
00549
00550 return self._subresults[index]
00551
00552 def get_retry(self, index):
00553 if len(self._retrys) == 0 or index >= len(self._retrys) or index < 0:
00554 return None
00555
00556 return self._retrys[index]
00557
00558
00559 def add_shutdown_result(self, msg):
00560 script = self._qual_test.getShutdownScript()
00561
00562 self._shutdown_result = TestScriptResult(script, msg)
00563
00564 def add_prestartup_result(self, index, msg):
00565 test_script = self._qual_test.pre_startup_scripts[index]
00566
00567 self._prestarts.append(TestScriptResult(test_script, msg))
00568
00569 def add_sub_result(self, index, msg):
00570 subtest = self._qual_test.subtests[index]
00571
00572 sub = SubTestResult(subtest, msg)
00573
00574 self._subresults.append(sub)
00575
00576 return sub
00577
00578 def cancel(self):
00579 self._canceled = True
00580
00581 def error(self):
00582 self._error = True
00583
00584
00585 def retry_subresult(self, index, notes = ''):
00586 retry_count = len(self._retrys) + 1
00587
00588 sub = self.get_subresult(index)
00589 if not sub:
00590 return
00591
00592 del self._subresults[index]
00593
00594
00595 sub.retry_test(retry_count, notes)
00596
00597
00598
00599
00600
00601
00602
00603 self._retrys.append(sub)
00604
00605
00606 def prestarts_ok(self):
00607 for prestart in self.get_prestarts():
00608 if not prestart.get_pass_bool():
00609 return False
00610
00611 return True
00612
00613 def is_prestart_error(self):
00614 if len(self.get_prestarts()) == 0:
00615 return False
00616
00617 return self.get_prestarts()[-1].has_error()
00618
00619 def get_pass_bool(self):
00620 if self._canceled or self._error:
00621 return False
00622
00623 if not self.prestarts_ok():
00624 return False
00625
00626 if self._shutdown_result and not self._shutdown_result.get_pass_bool():
00627 return False
00628
00629 if len(self._subresults) == 0:
00630 return False
00631
00632 for res in self._subresults:
00633 if not res.get_pass_bool():
00634 return False
00635
00636 return True
00637
00638 def get_test_result_str_invent(self):
00639 if self.get_pass_bool():
00640 return "PASS"
00641 return "FAIL"
00642
00643
00644 def get_test_result_str(self):
00645 if len(self.get_subresults()) == 0 or not self.prestarts_ok():
00646 return "Fail"
00647
00648 if self._canceled:
00649 return "Cancel"
00650 if self._error:
00651 return "Error"
00652
00653 manual = False
00654 for res in self.get_subresults():
00655 if res._result.is_retry():
00656 continue
00657
00658 if res._result.is_human_required():
00659 return "Human Required"
00660
00661 if res._result.is_error():
00662 return "Error"
00663 if res._result.is_cancel():
00664 return "Cancel"
00665 if not res._result.get_pass_bool():
00666 return "Fail"
00667
00668 if res._result.is_manual():
00669 manual = True
00670
00671 if manual:
00672 return "Operator Pass"
00673 return "Pass"
00674
00675
00676
00677 def make_summary_page(self, link = True, link_dir = TEMP_DIR):
00678 html = "<html><head>\n"
00679 html += "<title>Qualification Test Result for %s as %s: %s</title>\n" % (self._serial, self._item_name, self._start_time_name)
00680 html += "<style type=\"text/css\">\
00681 body { color: black; background: white; }\
00682 div.pass { background: green; padding: 0.5em; border: none; }\
00683 div.warn { background: yellow; padding: 0.5em; border: none; }\
00684 div.error { background: red; padding: 0.5em; border: none; }\
00685 strong { font-weight: bold; color: red; }\
00686 em { font-style: normal; font-weight: bold; }\
00687 </style>\
00688 </head>\n<body>\n"
00689
00690 if not self._config_only:
00691 html += '<H2 ALIGN=CENTER>Qualification of: %s</H2>\n<br>\n' % self._serial
00692 else:
00693 html += '<H2 ALIGN=CENTER>Configuration of: %s</H2>\n<br>\n' % self._serial
00694 st = self.get_subresult(0)
00695 if st is not None:
00696 html += st.html_header()
00697 else:
00698 html += '<p>No data from configuration, result: %s.</p>\n' % (self.get_test_result_str())
00699 html += '</body></html>'
00700 return html
00701
00702 html += '<H3 align=center>%s</H3>\n' % self._qual_test.getName()
00703
00704 if self.get_pass_bool():
00705 result_header = '<H3>Test Result: <em>%s</em></H3>\n' % self.get_test_result_str()
00706 else:
00707 result_header = '<H3>Test Result: <strong>%s</strong></H3>\n' % self.get_test_result_str()
00708
00709 html += result_header
00710 html += '<HR size="2">'
00711
00712 if self._operator == '':
00713 operator = 'Unknown'
00714 else:
00715 operator = self._operator
00716
00717 if self._note is None or self._note == '':
00718 self._note = 'No notes given.'
00719
00720 html += '<H5><b>Test Engineer\'s Notes</b></H5>\n'
00721 html += '<p><b>Test Engineer: %s</b></p>\n' % operator
00722 html += '<p>%s</p>\n' % self._note
00723
00724 html += '<H5>Test Date</H5>\n'
00725 html += '<p>Completed Test at %s on %s.</p>\n' % (self._start_time_name, self._serial)
00726 html += '<H5><b>Results Directory</b></H5>\n<p>%s</p>\n' % self._results_dir
00727
00728 if self._canceled:
00729 html += '<p><b>Test canceled by operator.</b></p>\n'
00730
00731 if len(self.get_subresults()) == 0:
00732 if self.prestarts_ok():
00733 html += '<p>No subtests completed. Test may have ended badly.</p>\n'
00734 elif self.is_prestart_error():
00735 html += '<p>Error during pretests. Check system and retry.</p>\n'
00736 else:
00737 html += '<p>Prestartup failure. Component may be damaged.</p>\n'
00738 else:
00739 html += '<HR size="2">\n'
00740
00741 html += self.make_index(link, link_dir)
00742
00743 if len(self.get_retrys()) > 0:
00744 html += '<hr size="2">\n'
00745 html += self.make_retry_index(link, link_dir)
00746
00747
00748 startup = self._qual_test.getStartupScript()
00749 if startup:
00750 html += '<hr size="2">\n'
00751 html += self.make_startup_data()
00752
00753
00754 if len(self._prestarts) > 0:
00755 html += '<hr size="2">\n'
00756 html += self.make_prestart_table()
00757
00758 if self._qual_test.getShutdownScript():
00759 html += '<hr size="2">\n'
00760 html += self.make_shutdown_results()
00761
00762 html += '<hr size="2">\n'
00763 html += self.make_log_table()
00764 html += '<hr size="2">\n'
00765
00766 html += '</body></html>'
00767
00768 return html
00769
00770
00771 def make_startup_data(self):
00772 startup = self._qual_test.getStartupScript()
00773
00774 html = ['<H4 align=center>Startup Script</H4>']
00775 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00776 html.append('<tr><td><b>Parameter</b></td><td><b>Value</b></td></b>')
00777 html.append('<tr><td>Name</td><td>%s</td>' % startup.get_name())
00778
00779
00780 launch_file = startup.launch_file
00781 (path, launch_pkg) = roslib.packages.get_dir_pkg(launch_file)
00782 path_idx = launch_file.find(launch_pkg) + len(launch_pkg) + 1
00783
00784 html.append('<tr><td>Launch package</td><td>%s</td></tr>' % launch_pkg)
00785 html.append('<tr><td>Launch filepath</td><td>%s</td></tr>' % launch_file[path_idx:])
00786 html.append('</table>\n')
00787
00788 return '\n'.join(html)
00789
00790
00791 def make_shutdown_results(self):
00792 shutdown = self._qual_test.getShutdownScript()
00793
00794 html = ['<H4 align=center>Shutdown Script</H4>']
00795
00796 if not self._shutdown_result:
00797 html.append('<p>Shutdown script: %s</p>' % shutdown.get_name())
00798 html.append('<p>No shutdown results.</p>')
00799 return '\n'.join(html)
00800
00801 name = self._shutdown_result.get_name()
00802 launch = self._shutdown_result.get_launch()
00803 res = self._shutdown_result.get_result_msg()
00804 msg = self._shutdown_result.get_msg()
00805
00806
00807 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00808 html.append('<tr><td><b>Name</b></td><td><b>Launch File</b></td><td><b>Result</b></td><td><b>Message</b></td></tr></b>')
00809 html.append('<tr><td>%s</td><td>%s</td>' % (name, launch))
00810 html.append('<td>%s</td><td>%s</td></tr>' % (res, msg))
00811 html.append('</table>\n')
00812
00813 return '\n'.join(html)
00814
00815
00816 def line_summary(self):
00817 if self._config_only and self.get_pass_bool():
00818 sum = "Reconfigured %s as %s." % (self._serial, self._qual_test.getName())
00819 else:
00820 sum = "Qualification of %s. Test name: %s. Result: %s." % (self._serial, self._qual_test.getName(), self.get_test_result_str())
00821 if self._note != '':
00822 sum += " Notes: %s" % (self._note)
00823
00824 return sum
00825
00826
00827 def make_retry_index(self, link, link_dir):
00828 html = ['<H4 AlIGN=CENTER>Retried Subtest Index</H4>' ]
00829 html.append('<table border="1" cellpadding="2" cellspacing="0">\n')
00830 html.append('<tr><td><b>Test Name</b></td><td><b>Summary</b></td><td><b>Final Result</b></td></tr></b>\n')
00831
00832 for st in self.get_retrys():
00833 html.append(st.make_index_line(link, link_dir))
00834
00835 html.append('</table>\n')
00836
00837 return '\n'.join(html)
00838
00839
00840 def make_index(self, link, link_dir):
00841 html = '<H4 AlIGN=CENTER>Results Index</H4>\n'
00842 html += '<table border="1" cellpadding="2" cellspacing="0">\n'
00843 html += '<tr><td><b>Test Name</b></td><td><b>Summary</b></td><td><b>Final Result</b></td></tr></b>\n'
00844
00845 for st in self.get_subresults():
00846 html += st.make_index_line(link, link_dir)
00847
00848 html += '</table>\n'
00849
00850 return html
00851
00852
00853 def make_log_table(self):
00854
00855 kys = dict.keys(self._test_log)
00856 kys.sort()
00857
00858 html = ['<H4 AlIGN=CENTER>Test Log Data</H4>']
00859 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00860 html.append('<tr><td><b>Time</b></td><td><b>Log Message</b></td></tr></b>')
00861 for ky in kys:
00862 time_str = ky.strftime("%m/%d/%Y %H:%M:%S")
00863 html.append('<tr><td>%s</td><td>%s</td></tr>' % (time_str, self._test_log[ky]))
00864 html.append('</table>\n')
00865
00866 return '\n'.join(html)
00867
00868
00869 def make_prestart_table(self):
00870 if len(self.get_prestarts()) == 0:
00871 return '<p>No prestartup scripts.</p>\n'
00872
00873 html = ['<H4 ALIGN=CENTER>Prestartup Script Data</H4>']
00874 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00875 html.append('<tr><td><b>Script</b></td><td><b>Launch File</b></td>')
00876 html.append('<td><b>Result</b></td><td><b>Message</b></td></tr></b>')
00877 for prestart in self.get_prestarts():
00878 html.append('<tr><td>%s</td><td>%s</td>' % (prestart.get_name(), prestart.get_launch()))
00879 html.append('<td>%s</td><td>%s</td></tr>' % (prestart.get_result_msg(), prestart.get_msg()))
00880 html.append('</table>\n')
00881
00882 return '\n'.join(html)
00883
00884 def write_results_to_file(self, temp = True, local_link = False):
00885 write_dir = TEMP_DIR if temp else self._results_dir
00886
00887
00888 link_dir = write_dir
00889 header_link_dir = write_dir
00890 if local_link:
00891 link_dir = '../'
00892 header_link_dir = ''
00893
00894 if not os.path.isdir(write_dir):
00895 os.mkdir(write_dir)
00896
00897 index_path = os.path.join(write_dir, 'index.html')
00898 index = open(index_path, 'w')
00899 index.write(self.make_summary_page(True, header_link_dir))
00900 index.close()
00901
00902 for i, st in enumerate(self._subresults):
00903 prev = self.get_subresult(i - 1)
00904 next = self.get_subresult(i + 1)
00905
00906 if not os.path.isdir(os.path.join(write_dir, st.filename_base())):
00907 os.mkdir(os.path.join(write_dir, st.filename_base()))
00908
00909 st_path = os.path.join(write_dir, st.filename())
00910 st_file = open(st_path, 'w')
00911 st_file.write(st.make_result_page(True, link_dir, prev, next))
00912 st_file.close()
00913
00914 st.write_images(write_dir)
00915
00916 for i, st in enumerate(self._retrys):
00917 prev = self.get_retry(i - 1)
00918 next = self.get_retry(i + 1)
00919
00920 if not os.path.isdir(os.path.join(write_dir, st.filename_base())):
00921 os.mkdir(os.path.join(write_dir, st.filename_base()))
00922
00923 st_path = os.path.join(write_dir, st.filename())
00924 st_file = open(st_path, 'w')
00925 st_file.write(st.make_result_page(True, link_dir, prev, next))
00926 st_file.close()
00927
00928 st.write_images(write_dir)
00929
00930
00931
00932 if not temp:
00933 self._write_tar_file()
00934
00935
00936 def _write_tar_file(self):
00937 self._tar_filename = os.path.join(self._results_dir, self._results_name + '_data.tar')
00938
00939
00940 tar = tarfile.open(self._tar_filename, 'w:')
00941 for filename in os.listdir(self._results_dir):
00942
00943 fullname = os.path.join(self._results_dir, filename)
00944 tar.add(fullname, arcname=filename)
00945 tar.close()
00946
00947 def _log_config_result(self, invent):
00948 sub = self.get_subresult(0)
00949 if not sub:
00950 return True, 'No subresult found!'
00951
00952 invent.add_attachment(self._serial, sub.filename_base() + '.html', 'text/html',
00953 sub.make_result_page(), self.line_summary())
00954 return True, 'Logged reconfiguration in inventory system.'
00955
00956
00957
00958 def log_results(self, invent):
00959
00960 self.write_results_to_file(temp = False, local_link = True)
00961
00962 if invent == None:
00963 return False, "Attempted to log results to inventory, but no invent client found."
00964 if self.is_prestart_error():
00965 return True, "Test recorded internal error, not submitting to inventory system."
00966
00967 prefix = self._start_time_filestr + "_"
00968
00969 if self._config_only:
00970 return self._log_config_result(invent)
00971
00972 invent.setKV(self._serial, "Test Status", self.get_test_result_str_invent())
00973
00974
00975 f = open(self._tar_filename, "rb")
00976 my_tar_data = f.read()
00977 f.close()
00978
00979 my_data = self.export_data()
00980
00981 try:
00982 ok = wg_invent_client.submit_log(invent, my_data, my_tar_data)
00983 msg = 'Wrote tar file, uploaded to inventory system.'
00984 if not ok:
00985 msg = 'Unable to upload to Invent. Check console output for details.'
00986
00987 return ok, msg
00988 except Exception, e:
00989 import traceback
00990 self.log('Caught exception uploading test parameters to invent.\n%s' % traceback.format_exc())
00991 return False, 'Caught exception loading tar file to inventory.\n%s' % traceback.format_exc()
00992
00993 def export_data(self):
00994 """
00995 Exports result data to wg_invent_client.TestData
00996
00997 Unit testing and Invent logging only.
00998
00999 \return wg_invent_client.TestData : Data for test
01000 """
01001
01002 my_data = wg_invent_client.TestData(self._qual_test.testid, self._qual_test.get_name(),
01003 time.time(),
01004 self._serial, self.get_test_result_str_invent())
01005
01006 if self._tar_filename and os.path.exists(self._tar_filename):
01007 my_data.set_attachment('application/tar', os.path.basename(self._tar_filename))
01008
01009 my_data.set_note(self._note)
01010
01011 for st in (self.get_subresults() + self.get_retrys()):
01012 my_data.add_subtest(st.export_data())
01013
01014 return my_data
01015
01016 def get_qual_team(self):
01017 if socket.gethostname() == 'nsf':
01018 return 'watts@willowgarage.com'
01019
01020 return 'qualdevteam@lists.willowgarage.com'
01021
01022
01023
01024
01025 def make_email_message(self):
01026 msg = MIMEMultipart('alternative')
01027 msg['Subject'] = "--QualResult-- %s" % self.line_summary()
01028 msg['From'] = "qual.test@willowgarage.com"
01029 msg['To'] = self.get_qual_team()
01030
01031 msg.attach(MIMEText(self.make_summary_page(False), 'html'))
01032
01033
01034 if self._tar_filename is not None and self._tar_filename != '':
01035 part = MIMEBase('application', 'octet-stream')
01036 with open(self._tar_filename, 'rb') as f:
01037 data = f.read()
01038 part.set_payload( data )
01039 Encoders.encode_base64(part)
01040 part.add_header('Content-Disposition', 'attachment; filename="%s"'
01041 % os.path.basename(self._tar_filename))
01042 msg.attach(part)
01043
01044 return msg
01045
01046
01047 def email_qual_team(self):
01048 try:
01049 msg = self.make_email_message()
01050
01051 s = smtplib.SMTP('localhost')
01052 s.sendmail('qual.test@willowgarage.com', self.get_qual_team(), msg.as_string())
01053 s.quit()
01054
01055 return True
01056 except Exception, e:
01057 import traceback
01058 print 'Unable to sent mail, caught exception!\n%s' % traceback.format_exc()
01059 return False