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 stream = StringIO(plot.image)
00293 im = Image.open(stream)
00294
00295 img_file = os.path.join(dir_name, plot.title + '.' + plot.image_format)
00296 im.save(img_file)
00297
00298 def html_image_result(self, img_path):
00299 html = '<H4 ALIGN=CENTER>Result Details</H4>'
00300
00301
00302 html += self._text_result.replace('IMG_PATH', os.path.join(img_path, self.filename_base()))
00303
00304 return html
00305
00306
00307
00308
00309 def make_result_page(self, back_link = False, link_dir = TEMP_DIR, prev = None, next = None):
00310 html = "<html><head><title>Qualification Test Results: %s</title>\
00311 <style type=\"text/css\">\
00312 body { color: black; background: white; }\
00313 div.pass { background: green; padding: 0.5em; border: none; }\
00314 div.warn { background: yellow; padding: 0.5em; border: none; }\
00315 div.error { background: red; padding: 0.5em; border: none; }\
00316 strong { font-weight: bold; color: red; }\
00317 em { font-style:normal; font-weight: bold; }\
00318 </style>\
00319 </head><body>\n" % self._subtest.get_name()
00320
00321 html += self.html_header()
00322
00323 html += '<hr size="3">\n'
00324
00325
00326 html += self.html_image_result(link_dir)
00327
00328 html += '<hr size="3">\n'
00329 html += self._html_test_params()
00330 html += '<hr size="3">\n'
00331 html += self._html_test_values()
00332 html += '<hr size="3">\n'
00333 html += self._html_test_info()
00334 html += '<hr size="3">\n'
00335
00336
00337
00338 if back_link:
00339 if prev:
00340 prev_file = os.path.join(link_dir, prev.filename())
00341 html += '<p align=center><a href="%s">Previous: %s</a></p>\n' % (prev_file, prev.get_name())
00342
00343 back_index_file = os.path.join(link_dir, 'index.html')
00344 html += '<p align=center><a href="%s">Back to Index</a></p>\n' % back_index_file
00345
00346 if next:
00347 next_file = os.path.join(link_dir, next.filename())
00348 html += '<p align=center><a href="%s">Next: %s</a></p>\n' % (next_file, next.get_name())
00349
00350
00351 html += '</body></html>'
00352
00353 return html
00354
00355 def _html_test_params(self):
00356 html = ['<H4 align=center>Subtest Parameters</H4>']
00357
00358 if len(self._params) == 0:
00359 html.append('<p>No subtest parameters.</p>')
00360 else:
00361 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00362 html.append('<tr><td><b>Parameter</b></td><td><b>Value</b></td></tr>')
00363 for param in self._params:
00364 html.append('<tr><td>%s</td><td>%s</td></tr>' % (param.key, param.value))
00365 html.append('</table>')
00366
00367 return '\n'.join(html)
00368
00369 def _html_test_values(self):
00370 html = ['<H4 align=center>Subtest Measurements</H4>']
00371
00372 if len(self._values) == 0:
00373 html.append('<p>No subtest values.</p>')
00374 else:
00375 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00376 html.append('<tr><td><b>Measurement</b></td><td><b>Value</b></td><td><b>Min</b></td><td><b>Max</b></td></tr>')
00377 for value in self._values:
00378 html.append('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>' % (value.key, value.value, value.min, value.max))
00379 html.append('</table>')
00380
00381 return '\n'.join(html)
00382
00383
00384 def _html_test_info(self):
00385 html = ['<H4 align=center>Subtest Information</H4>']
00386
00387 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00388 html.append('<tr><td><b>Parameter</b></td><td><b>Value</b></td></tr>')
00389
00390 if self._subtest._pre_script:
00391 html.append('<tr><td>Pre-test</td><td>%s</td></tr>' % os.path.basename(self._subtest._pre_script))
00392 else:
00393 html.append('<tr><td>Pre-test</td><td>None</td></tr>')
00394
00395 if self._subtest._post_script:
00396 html.append('<tr><td>Post-test</td><td>%s</td></tr>' % os.path.basename(self._subtest._post_script))
00397 else:
00398 html.append('<tr><td>Post-test</td><td>None</td></tr>')
00399
00400 launch_file = self._subtest._test_script
00401 (path, launch_pkg) = roslib.packages.get_dir_pkg(launch_file)
00402 path_idx = launch_file.find(launch_pkg) + len(launch_pkg) + 1
00403
00404
00405 html.append('<tr><td>Launch package</td><td>%s</td></tr>' % launch_pkg)
00406 html.append('<tr><td>Launch filepath</td><td>%s</td></tr>' % launch_file[path_idx:])
00407
00408 html.append('</table>')
00409
00410 return '\n'.join(html)
00411
00412
00413
00414 def html_header(self):
00415 html = ['<H4 ALIGN=CENTER>Results of %s%s</H4>\n' % (self._subtest.get_name(), self._retry_name)]
00416
00417 html.append("<p><b>Test Status:</b></p>\n<H4>%s</H4>\n" % self._result.get_html_msg())
00418
00419 if self._summary != '':
00420 html.append('<p><em>Summary</em></p>\n' )
00421 html.append('<p>%s</p>\n' % self._summary)
00422 if self._subresult_note != '':
00423 html.append('<p><em>Operator\'s Notes:</em></p>\n')
00424 html.append('<p>%s</p>\n' % self._subresult_note)
00425
00426 return '\n'.join(html)
00427
00428 def make_index_line(self, link, link_dir):
00429 result = self._result.get_html_msg()
00430
00431 path = os.path.join(link_dir, self.filename())
00432 if link:
00433 hyperlink = '<a href=\"%s\">%s</a>' % (path, self._subtest.get_name())
00434 else:
00435 hyperlink = self._subtest.get_name()
00436
00437 summary = '\n'.join([self._summary, self._subresult_note])
00438
00439 return '<tr><td>%s</td><td>%s</td><td>%s</td></tr>\n' % (hyperlink, summary, result)
00440
00441
00442
00443
00444
00445
00446
00447 class QualTestResult(object):
00448
00449
00450
00451 def __init__(self, qual_item, qual_test, start_time):
00452 self._qual_test = qual_test
00453
00454 self._subresults = []
00455
00456 self._retrys = []
00457
00458 self._prestarts = []
00459
00460 self._shutdown_result = None
00461
00462 self._start_time = start_time
00463 self._start_time_filestr = self._start_time.strftime("%Y%m%d_%H%M%S")
00464 self._start_time_name = self._start_time.strftime("%Y/%m/%d %I:%M%p")
00465
00466 self._item = qual_item
00467 self._serial = qual_item.serial
00468 self._item_name = qual_item.name
00469
00470 if not os.path.isdir(TEMP_DIR):
00471 os.makedirs(TEMP_DIR)
00472
00473
00474
00475 try:
00476 config = qual_item._config
00477 self._config_only = True
00478 except Exception, e:
00479 self._config_only = False
00480
00481 self._tar_filename = ''
00482
00483 self._results_name = '%s_%s' % (self._serial, self._start_time_filestr)
00484
00485
00486 self._made_dir = None
00487 self.set_results_dir(os.path.join(RESULTS_DIR, self._results_name))
00488
00489 self._error = False
00490 self._canceled = False
00491
00492 self._test_log = {}
00493
00494 self._note = ''
00495 self._operator = ''
00496
00497 def close(self):
00498
00499 if self._made_dir and len(os.listdir(self._made_dir)) == 0:
00500 os.rmdir(self._made_dir)
00501
00502
00503 shutil.rmtree(TEMP_DIR)
00504
00505 @property
00506 def results_dir(self): return self._results_dir
00507
00508 @property
00509 def tar_name(self): return self._tar_filename
00510
00511 def set_results_dir(self, path):
00512 if not path.endswith('/'):
00513 path += '/'
00514
00515 self._results_dir = path
00516 if not os.path.isdir(self._results_dir):
00517 self._made_dir = self._results_dir
00518 os.makedirs(self._results_dir)
00519 else:
00520 self._made_dir = None
00521
00522
00523 def set_notes(self, note):
00524 self._note = note
00525
00526 def set_operator(self, name):
00527 self._operator = name
00528
00529 def log(self, entry):
00530 self._test_log[datetime.now()] = entry
00531
00532
00533 def get_prestarts(self):
00534 return self._prestarts[:]
00535
00536 def get_subresults(self, reverse = False):
00537 vals = self._subresults[:]
00538 if reverse:
00539 vals = vals.reverse()
00540 return vals
00541
00542 def get_retrys(self, reverse = False):
00543 vals = self._retrys[:]
00544 if reverse:
00545 vals = vals.reverse()
00546 return vals
00547
00548
00549 def get_subresult(self, index):
00550 if len(self._subresults) == 0 or index >= len(self._subresults) or index < 0:
00551 return None
00552
00553 return self._subresults[index]
00554
00555 def get_retry(self, index):
00556 if len(self._retrys) == 0 or index >= len(self._retrys) or index < 0:
00557 return None
00558
00559 return self._retrys[index]
00560
00561
00562 def add_shutdown_result(self, msg):
00563 script = self._qual_test.getShutdownScript()
00564
00565 self._shutdown_result = TestScriptResult(script, msg)
00566
00567 def add_prestartup_result(self, index, msg):
00568 test_script = self._qual_test.pre_startup_scripts[index]
00569
00570 self._prestarts.append(TestScriptResult(test_script, msg))
00571
00572 def add_sub_result(self, index, msg):
00573 subtest = self._qual_test.subtests[index]
00574
00575 sub = SubTestResult(subtest, msg)
00576
00577 self._subresults.append(sub)
00578
00579 return sub
00580
00581 def cancel(self):
00582 self._canceled = True
00583
00584 def error(self):
00585 self._error = True
00586
00587
00588 def retry_subresult(self, index, notes = ''):
00589 retry_count = len(self._retrys) + 1
00590
00591 sub = self.get_subresult(index)
00592 if not sub:
00593 return
00594
00595 del self._subresults[index]
00596
00597
00598 sub.retry_test(retry_count, notes)
00599
00600
00601
00602
00603
00604
00605
00606 self._retrys.append(sub)
00607
00608
00609 def prestarts_ok(self):
00610 for prestart in self.get_prestarts():
00611 if not prestart.get_pass_bool():
00612 return False
00613
00614 return True
00615
00616 def is_prestart_error(self):
00617 if len(self.get_prestarts()) == 0:
00618 return False
00619
00620 return self.get_prestarts()[-1].has_error()
00621
00622 def get_pass_bool(self):
00623 if self._canceled or self._error:
00624 return False
00625
00626 if not self.prestarts_ok():
00627 return False
00628
00629 if self._shutdown_result and not self._shutdown_result.get_pass_bool():
00630 return False
00631
00632 if len(self._subresults) == 0:
00633 return False
00634
00635 for res in self._subresults:
00636 if not res.get_pass_bool():
00637 return False
00638
00639 return True
00640
00641 def get_test_result_str_invent(self):
00642 if self.get_pass_bool():
00643 return "PASS"
00644 return "FAIL"
00645
00646
00647 def get_test_result_str(self):
00648 if len(self.get_subresults()) == 0 or not self.prestarts_ok():
00649 return "Fail"
00650
00651 if self._canceled:
00652 return "Cancel"
00653 if self._error:
00654 return "Error"
00655
00656 manual = False
00657 for res in self.get_subresults():
00658 if res._result.is_retry():
00659 continue
00660
00661 if res._result.is_human_required():
00662 return "Human Required"
00663
00664 if res._result.is_error():
00665 return "Error"
00666 if res._result.is_cancel():
00667 return "Cancel"
00668 if not res._result.get_pass_bool():
00669 return "Fail"
00670
00671 if res._result.is_manual():
00672 manual = True
00673
00674 if manual:
00675 return "Operator Pass"
00676 return "Pass"
00677
00678
00679
00680 def make_summary_page(self, link = True, link_dir = TEMP_DIR):
00681 html = "<html><head>\n"
00682 html += "<title>Qualification Test Result for %s as %s: %s</title>\n" % (self._serial, self._item_name, self._start_time_name)
00683 html += "<style type=\"text/css\">\
00684 body { color: black; background: white; }\
00685 div.pass { background: green; padding: 0.5em; border: none; }\
00686 div.warn { background: yellow; padding: 0.5em; border: none; }\
00687 div.error { background: red; padding: 0.5em; border: none; }\
00688 strong { font-weight: bold; color: red; }\
00689 em { font-style: normal; font-weight: bold; }\
00690 </style>\
00691 </head>\n<body>\n"
00692
00693 if not self._config_only:
00694 html += '<H2 ALIGN=CENTER>Qualification of: %s</H2>\n<br>\n' % self._serial
00695 else:
00696 html += '<H2 ALIGN=CENTER>Configuration of: %s</H2>\n<br>\n' % self._serial
00697 st = self.get_subresult(0)
00698 if st is not None:
00699 html += st.html_header()
00700 else:
00701 html += '<p>No data from configuration, result: %s.</p>\n' % (self.get_test_result_str())
00702 html += '</body></html>'
00703 return html
00704
00705 html += '<H3 align=center>%s</H3>\n' % self._qual_test.getName()
00706
00707 if self.get_pass_bool():
00708 result_header = '<H3>Test Result: <em>%s</em></H3>\n' % self.get_test_result_str()
00709 else:
00710 result_header = '<H3>Test Result: <strong>%s</strong></H3>\n' % self.get_test_result_str()
00711
00712 html += result_header
00713 html += '<HR size="2">'
00714
00715 if self._operator == '':
00716 operator = 'Unknown'
00717 else:
00718 operator = self._operator
00719
00720 if self._note is None or self._note == '':
00721 self._note = 'No notes given.'
00722
00723 html += '<H5><b>Test Engineer\'s Notes</b></H5>\n'
00724 html += '<p><b>Test Engineer: %s</b></p>\n' % operator
00725 html += '<p>%s</p>\n' % self._note
00726
00727 html += '<H5>Test Date</H5>\n'
00728 html += '<p>Completed Test at %s on %s.</p>\n' % (self._start_time_name, self._serial)
00729 html += '<H5><b>Results Directory</b></H5>\n<p>%s</p>\n' % self._results_dir
00730
00731 if self._canceled:
00732 html += '<p><b>Test canceled by operator.</b></p>\n'
00733
00734 if len(self.get_subresults()) == 0:
00735 if self.prestarts_ok():
00736 html += '<p>No subtests completed. Test may have ended badly.</p>\n'
00737 elif self.is_prestart_error():
00738 html += '<p>Error during pretests. Check system and retry.</p>\n'
00739 else:
00740 html += '<p>Prestartup failure. Component may be damaged.</p>\n'
00741 else:
00742 html += '<HR size="2">\n'
00743
00744 html += self.make_index(link, link_dir)
00745
00746 if len(self.get_retrys()) > 0:
00747 html += '<hr size="2">\n'
00748 html += self.make_retry_index(link, link_dir)
00749
00750
00751 startup = self._qual_test.getStartupScript()
00752 if startup:
00753 html += '<hr size="2">\n'
00754 html += self.make_startup_data()
00755
00756
00757 if len(self._prestarts) > 0:
00758 html += '<hr size="2">\n'
00759 html += self.make_prestart_table()
00760
00761 if self._qual_test.getShutdownScript():
00762 html += '<hr size="2">\n'
00763 html += self.make_shutdown_results()
00764
00765 html += '<hr size="2">\n'
00766 html += self.make_log_table()
00767 html += '<hr size="2">\n'
00768
00769 html += '</body></html>'
00770
00771 return html
00772
00773
00774 def make_startup_data(self):
00775 startup = self._qual_test.getStartupScript()
00776
00777 html = ['<H4 align=center>Startup Script</H4>']
00778 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00779 html.append('<tr><td><b>Parameter</b></td><td><b>Value</b></td></b>')
00780 html.append('<tr><td>Name</td><td>%s</td>' % startup.get_name())
00781
00782
00783 launch_file = startup.launch_file
00784 (path, launch_pkg) = roslib.packages.get_dir_pkg(launch_file)
00785 path_idx = launch_file.find(launch_pkg) + len(launch_pkg) + 1
00786
00787 html.append('<tr><td>Launch package</td><td>%s</td></tr>' % launch_pkg)
00788 html.append('<tr><td>Launch filepath</td><td>%s</td></tr>' % launch_file[path_idx:])
00789 html.append('</table>\n')
00790
00791 return '\n'.join(html)
00792
00793
00794 def make_shutdown_results(self):
00795 shutdown = self._qual_test.getShutdownScript()
00796
00797 html = ['<H4 align=center>Shutdown Script</H4>']
00798
00799 if not self._shutdown_result:
00800 html.append('<p>Shutdown script: %s</p>' % shutdown.get_name())
00801 html.append('<p>No shutdown results.</p>')
00802 return '\n'.join(html)
00803
00804 name = self._shutdown_result.get_name()
00805 launch = self._shutdown_result.get_launch()
00806 res = self._shutdown_result.get_result_msg()
00807 msg = self._shutdown_result.get_msg()
00808
00809
00810 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00811 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>')
00812 html.append('<tr><td>%s</td><td>%s</td>' % (name, launch))
00813 html.append('<td>%s</td><td>%s</td></tr>' % (res, msg))
00814 html.append('</table>\n')
00815
00816 return '\n'.join(html)
00817
00818
00819 def line_summary(self):
00820 if self._config_only and self.get_pass_bool():
00821 sum = "Reconfigured %s as %s." % (self._serial, self._qual_test.getName())
00822 else:
00823 sum = "Qualification of %s. Test name: %s. Result: %s." % (self._serial, self._qual_test.getName(), self.get_test_result_str())
00824 if self._note != '':
00825 sum += " Notes: %s" % (self._note)
00826
00827 return sum
00828
00829
00830 def make_retry_index(self, link, link_dir):
00831 html = ['<H4 AlIGN=CENTER>Retried Subtest Index</H4>' ]
00832 html.append('<table border="1" cellpadding="2" cellspacing="0">\n')
00833 html.append('<tr><td><b>Test Name</b></td><td><b>Summary</b></td><td><b>Final Result</b></td></tr></b>\n')
00834
00835 for st in self.get_retrys():
00836 html.append(st.make_index_line(link, link_dir))
00837
00838 html.append('</table>\n')
00839
00840 return '\n'.join(html)
00841
00842
00843 def make_index(self, link, link_dir):
00844 html = '<H4 AlIGN=CENTER>Results Index</H4>\n'
00845 html += '<table border="1" cellpadding="2" cellspacing="0">\n'
00846 html += '<tr><td><b>Test Name</b></td><td><b>Summary</b></td><td><b>Final Result</b></td></tr></b>\n'
00847
00848 for st in self.get_subresults():
00849 html += st.make_index_line(link, link_dir)
00850
00851 html += '</table>\n'
00852
00853 return html
00854
00855
00856 def make_log_table(self):
00857
00858 kys = dict.keys(self._test_log)
00859 kys.sort()
00860
00861 html = ['<H4 AlIGN=CENTER>Test Log Data</H4>']
00862 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00863 html.append('<tr><td><b>Time</b></td><td><b>Log Message</b></td></tr></b>')
00864 for ky in kys:
00865 time_str = ky.strftime("%m/%d/%Y %H:%M:%S")
00866 html.append('<tr><td>%s</td><td>%s</td></tr>' % (time_str, self._test_log[ky]))
00867 html.append('</table>\n')
00868
00869 return '\n'.join(html)
00870
00871
00872 def make_prestart_table(self):
00873 if len(self.get_prestarts()) == 0:
00874 return '<p>No prestartup scripts.</p>\n'
00875
00876 html = ['<H4 ALIGN=CENTER>Prestartup Script Data</H4>']
00877 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00878 html.append('<tr><td><b>Script</b></td><td><b>Launch File</b></td>')
00879 html.append('<td><b>Result</b></td><td><b>Message</b></td></tr></b>')
00880 for prestart in self.get_prestarts():
00881 html.append('<tr><td>%s</td><td>%s</td>' % (prestart.get_name(), prestart.get_launch()))
00882 html.append('<td>%s</td><td>%s</td></tr>' % (prestart.get_result_msg(), prestart.get_msg()))
00883 html.append('</table>\n')
00884
00885 return '\n'.join(html)
00886
00887 def write_results_to_file(self, temp = True, local_link = False):
00888 write_dir = TEMP_DIR if temp else self._results_dir
00889
00890
00891 link_dir = write_dir
00892 header_link_dir = write_dir
00893 if local_link:
00894 link_dir = '../'
00895 header_link_dir = ''
00896
00897 if not os.path.isdir(write_dir):
00898 os.mkdir(write_dir)
00899
00900 index_path = os.path.join(write_dir, 'index.html')
00901 index = open(index_path, 'w')
00902 index.write(self.make_summary_page(True, header_link_dir))
00903 index.close()
00904
00905 for i, st in enumerate(self._subresults):
00906 prev = self.get_subresult(i - 1)
00907 next = self.get_subresult(i + 1)
00908
00909 if not os.path.isdir(os.path.join(write_dir, st.filename_base())):
00910 os.mkdir(os.path.join(write_dir, st.filename_base()))
00911
00912 st_path = os.path.join(write_dir, st.filename())
00913 st_file = open(st_path, 'w')
00914 st_file.write(st.make_result_page(True, link_dir, prev, next))
00915 st_file.close()
00916
00917 st.write_images(write_dir)
00918
00919 for i, st in enumerate(self._retrys):
00920 prev = self.get_retry(i - 1)
00921 next = self.get_retry(i + 1)
00922
00923 if not os.path.isdir(os.path.join(write_dir, st.filename_base())):
00924 os.mkdir(os.path.join(write_dir, st.filename_base()))
00925
00926 st_path = os.path.join(write_dir, st.filename())
00927 st_file = open(st_path, 'w')
00928 st_file.write(st.make_result_page(True, link_dir, prev, next))
00929 st_file.close()
00930
00931 st.write_images(write_dir)
00932
00933
00934
00935 if not temp:
00936 self._write_tar_file()
00937
00938
00939 def _write_tar_file(self):
00940 self._tar_filename = os.path.join(self._results_dir, self._results_name + '_data.tar')
00941
00942
00943 tar = tarfile.open(self._tar_filename, 'w:')
00944 for filename in os.listdir(self._results_dir):
00945
00946 fullname = os.path.join(self._results_dir, filename)
00947 tar.add(fullname, arcname=filename)
00948 tar.close()
00949
00950 def _log_config_result(self, invent):
00951 sub = self.get_subresult(0)
00952 if not sub:
00953 return True, 'No subresult found!'
00954
00955 invent.add_attachment(self._serial, sub.filename_base() + '.html', 'text/html',
00956 sub.make_result_page(), self.line_summary())
00957 return True, 'Logged reconfiguration in inventory system.'
00958
00959
00960
00961 def log_results(self, invent):
00962
00963 self.write_results_to_file(temp = False, local_link = True)
00964
00965 if invent == None:
00966 return False, "Attempted to log results to inventory, but no invent client found."
00967 if self.is_prestart_error():
00968 return True, "Test recorded internal error, not submitting to inventory system."
00969
00970 prefix = self._start_time_filestr + "_"
00971
00972 if self._config_only:
00973 return self._log_config_result(invent)
00974
00975 invent.setKV(self._serial, "Test Status", self.get_test_result_str_invent())
00976
00977
00978 f = open(self._tar_filename, "rb")
00979 my_tar_data = f.read()
00980 f.close()
00981
00982 my_data = self.export_data()
00983
00984 try:
00985 ok = wg_invent_client.submit_log(invent, my_data, my_tar_data)
00986 msg = 'Wrote tar file, uploaded to inventory system.'
00987 if not ok:
00988 msg = 'Unable to upload to Invent. Check console output for details.'
00989
00990 return ok, msg
00991 except Exception, e:
00992 import traceback
00993 self.log('Caught exception uploading test parameters to invent.\n%s' % traceback.format_exc())
00994 return False, 'Caught exception loading tar file to inventory.\n%s' % traceback.format_exc()
00995
00996 def export_data(self):
00997 """
00998 Exports result data to wg_invent_client.TestData
00999
01000 Unit testing and Invent logging only.
01001
01002 \return wg_invent_client.TestData : Data for test
01003 """
01004
01005 my_data = wg_invent_client.TestData(self._qual_test.testid, self._qual_test.get_name(),
01006 time.time(),
01007 self._serial, self.get_test_result_str_invent())
01008
01009 if self._tar_filename and os.path.exists(self._tar_filename):
01010 my_data.set_attachment('application/tar', os.path.basename(self._tar_filename))
01011
01012 my_data.set_note(self._note)
01013
01014 for st in (self.get_subresults() + self.get_retrys()):
01015 my_data.add_subtest(st.export_data())
01016
01017 return my_data
01018
01019 def get_qual_team(self):
01020 if socket.gethostname() == 'nsf':
01021 return 'watts@willowgarage.com'
01022
01023 return 'qualdevteam@lists.willowgarage.com'
01024
01025
01026
01027
01028 def make_email_message(self):
01029 msg = MIMEMultipart('alternative')
01030 msg['Subject'] = "--QualResult-- %s" % self.line_summary()
01031 msg['From'] = "qual.test@willowgarage.com"
01032 msg['To'] = self.get_qual_team()
01033
01034 msg.attach(MIMEText(self.make_summary_page(False), 'html'))
01035
01036
01037 if self._tar_filename is not None and self._tar_filename != '':
01038 part = MIMEBase('application', 'octet-stream')
01039 with open(self._tar_filename, 'rb') as f:
01040 data = f.read()
01041 part.set_payload( data )
01042 Encoders.encode_base64(part)
01043 part.add_header('Content-Disposition', 'attachment; filename="%s"'
01044 % os.path.basename(self._tar_filename))
01045 msg.attach(part)
01046
01047 return msg
01048
01049
01050 def email_qual_team(self):
01051 try:
01052 msg = self.make_email_message()
01053
01054 s = smtplib.SMTP('localhost')
01055 s.sendmail('qual.test@willowgarage.com', self.get_qual_team(), msg.as_string())
01056 s.quit()
01057
01058 return True
01059 except Exception, e:
01060 import traceback
01061 print 'Unable to sent mail, caught exception!\n%s' % traceback.format_exc()
01062 return False