Changeset 61424 in vbox
- Timestamp:
- Jun 3, 2016 2:22:30 AM (9 years ago)
- svn:sync-xref-src-repo-rev:
- 107741
- Location:
- trunk/src/VBox/ValidationKit/testmanager
- Files:
-
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/ValidationKit/testmanager/batch/virtual_test_sheriff.py
r61404 r61424 54 54 from testmanager.core.testgroup import TestGroupData; 55 55 from testmanager.core.testset import TestSetLogic, TestSetData; 56 from testmanager.core.testresults import TestResultLogic ;56 from testmanager.core.testresults import TestResultLogic, TestResultFileData; 57 57 from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData; 58 58 from testmanager.core.useraccount import UserAccountLogic; … … 68 68 ## Max log file we'll read into memory. (256 MB) 69 69 kcbMaxLogRead = 0x10000000; 70 71 70 72 71 def __init__(self, oSheriff, oTestSet, oTree, oBuild, oTestBox, oTestGroup, oTestCase): … … 97 96 98 97 # Investigation notes. 99 self.tReason = None; # None or one of the ktReason_XXX constants. 100 self.dReasonForResultId = {}; # Reason assignments indexed by idTestResult. 98 self.tReason = None; # None or one of the ktReason_XXX constants. 99 self.dReasonForResultId = {}; # Reason assignments indexed by idTestResult. 100 self.dCommentForResultId = {}; # Comment assignments indexed by idTestResult. 101 101 102 102 # … … 108 108 self.oSheriff.dprint('noteReason: %s -> %s' % (self.tReason, tReason,)); 109 109 self.tReason = tReason; 110 111 def noteReasonForId(self, tReason, idTestResult): 110 return True; 111 112 def noteReasonForId(self, tReason, idTestResult, sComment = None): 112 113 """ Notes down a possible reason for a specific test result. """ 113 self.oSheriff.dprint('noteReasonForId: %u: %s -> %s' 114 % (idTestResult, self.dReasonForResultId.get(idTestResult, None), tReason,)); 114 self.oSheriff.dprint('noteReasonForId: %u: %s -> %s%s' 115 % (idTestResult, self.dReasonForResultId.get(idTestResult, None), tReason, 116 (' (%s)' % (sComment,)) if sComment is not None else '')); 115 117 self.dReasonForResultId[idTestResult] = tReason; 118 if sComment is not None: 119 self.dCommentForResultId[idTestResult] = sComment; 120 return True; 116 121 117 122 … … 161 166 return self.sMainLog; 162 167 168 def getLogFile(self, oFile): 169 """ 170 Tries to reads the given file as a utf-8 log file. 171 oFile is a TestFileDataEx instance. 172 Returns empty string if problems opening or reading the file. 173 """ 174 sContent = ''; 175 (oFile, oSizeOrError, _) = self.oTestSet.openFile(oFile.sFile, 'rb'); 176 if oFile is not None: 177 try: 178 sContent = oFile.read(min(self.kcbMaxLogRead, oSizeOrError)).decode('utf-8', 'replace'); 179 except Exception as oXcpt: 180 self.oSheriff.vprint('Error reading the "%s" log file: %s' % (oFile.sFile, oXcpt,)) 181 else: 182 self.oSheriff.vprint('Error opening the "%s" log file: %s' % (oFile.sFile, oSizeOrError,)); 183 return sContent; 184 185 163 186 def isSingleTestFailure(self): 164 187 """ … … 195 218 self.uidSelf = -1; 196 219 self.oLogFile = None; 220 self.asBsodReasons = []; 197 221 198 222 oParser = OptionParser(); … … 355 379 ktReason_Guru_VERR_IEM_INSTR_NOT_IMPLEMENTED = ( 'Guru Meditations', 'VERR_IEM_INSTR_NOT_IMPLEMENTED' ); 356 380 ktReason_Guru_VERR_IEM_ASPECT_NOT_IMPLEMENTED = ( 'Guru Meditations', 'VERR_IEM_ASPECT_NOT_IMPLEMENTED' ); 381 ktReason_Guru_VERR_TRPM_DONT_PANIC = ( 'Guru Meditations', 'VERR_TRPM_DONT_PANIC' ); 357 382 ktReason_Guru_VINF_EM_TRIPLE_FAULT = ( 'Guru Meditations', 'VINF_EM_TRIPLE_FAULT' ); 358 383 ktReason_XPCOM_Exit_Minus_11 = ( 'API / (XP)COM', 'exit -11' ); 384 ktReason_XPCOM_VBoxSVC_Hang = ( 'API / (XP)COM', 'VBoxSVC hang' ); 385 ktReason_XPCOM_VBoxSVC_Hang_Plus_Heap_Corruption = ( 'API / (XP)COM', 'VBoxSVC hang + heap corruption' ); 386 ktReason_Unknown_Heap_Corruption = ( 'Unknown', 'Heap corruption' ); 387 ktReason_Unknown_Reboot_Loop = ( 'Unknown', 'Reboot loop' ); 359 388 ## @} 360 389 390 ## BSOD category. 391 ksBsodCategory = 'BSOD'; 392 ## Special reason indicating that the flesh and blood sheriff has work to do. 393 ksBsodAddNew = 'Add new BSOD'; 394 395 ## Used for indica that we shouldn't report anything for this test result ID and 396 ## consider promoting the previous error to test set level if it's the only one. 397 ktHarmless = ( 'Probably', 'Caused by previous error' ); 398 399 361 400 def caseClosed(self, oCaseFile): 362 401 """ … … 366 405 # Log it and create a dReasonForReasultId we can use below. 367 406 # 368 if len(oCaseFile.dReasonForResultId): 369 self.vprint('Closing %s with following reasons: %s' % (oCaseFile.sName, oCaseFile.dReasonForResultId,)); 370 dReasonForReasultId = oCaseFile.dReasonForResultId; 407 dCommentForResultId = oCaseFile.dCommentForResultId; 408 if len(oCaseFile.dReasonForResultId) > 0: 409 # Must weed out ktHarmless. 410 dReasonForResultId = {}; 411 for idKey, tReason in oCaseFile.dReasonForResultId.items(): 412 if tReason is not self.ktHarmless: 413 dReasonForResultId[idKey] = tReason; 414 if len(dReasonForResultId) == 0: 415 self.vprint('TODO: Closing %s without a real reason, only %s.' % (oCaseFile.sName, oCaseFile.dReasonForResultId)); 416 return False; 417 418 # Try promote to single reason. 419 if len(dReasonForResultId) > 1: 420 atValues = dReasonForResultId.values(); 421 if len(atValues) == atValues.count(atValues[0]): 422 self.dprint('Merged %d reasons to a single one: %s' % (len(atValues), atValues[0])); 423 dReasonForResultId = { oCaseFile.oTestSet.idTestResult: atValues[0], }; 424 if len(dCommentForResultId) > 0: 425 dCommentForResultId = { oCaseFile.oTestSet.idTestResult: dCommentForResultId.values()[0], }; 371 426 elif oCaseFile.tReason is not None: 372 self.vprint('Closing %s with following reason: %s' % (oCaseFile.sName, oCaseFile.tReason,)); 373 dReasonForReasultId = { oCaseFile.oTestSet.idTestResult: oCaseFile.tReason, }; 427 dReasonForResultId = { oCaseFile.oTestSet.idTestResult: oCaseFile.tReason, }; 374 428 else: 375 self.vprint('Closing %s without a reason ... weird!' % (oCaseFile.sName,));429 self.vprint('Closing %s without a reason - this should not happen!' % (oCaseFile.sName,)); 376 430 return False; 377 431 432 self.vprint('Closing %s with following reason%s: %s' 433 % ( oCaseFile.sName, 's' if dReasonForResultId > 0 else '', dReasonForResultId, )); 434 378 435 # 379 436 # Add the test failure reason record(s). 380 437 # 381 for idTestResult, tReason in dReasonForRe asultId.items():438 for idTestResult, tReason in dReasonForResultId.items(): 382 439 oFailureReason = self.oFailureReasonLogic.cachedLookupByNameAndCategory(tReason[1], tReason[0]); 383 440 if oFailureReason is not None: 441 sComment = 'Set by $Revision$' # Handy for reverting later. 442 if idTestResult in dCommentForResultId: 443 sComment += ': ' + dCommentForResultId[idTestResult]; 444 384 445 oAdd = TestResultFailureData(); 385 oAdd.initFromValues(idTestResult 386 idFailureReason 387 uidAuthor 388 idTestSet 389 sComment = 'Set by $Revision$',); # Handy for reverting later.446 oAdd.initFromValues(idTestResult = idTestResult, 447 idFailureReason = oFailureReason.idFailureReason, 448 uidAuthor = self.uidSelf, 449 idTestSet = oCaseFile.oTestSet.idTestSet, 450 sComment = sComment,); 390 451 if self.oConfig.fRealRun: 391 452 try: … … 398 459 return True; 399 460 461 # 462 # Tools for assiting log parsing. 463 # 464 465 @staticmethod 466 def matchFollowedByLines(sStr, off, asFollowingLines): 467 """ Worker for isThisFollowedByTheseLines. """ 468 469 # Advance off to the end of the line. 470 off = sStr.find('\n', off); 471 if off < 0: 472 return False; 473 off += 1; 474 475 # Match each string with the subsequent lines. 476 for iLine, sLine in enumerate(asFollowingLines): 477 offEnd = sStr.find('\n', off); 478 if offEnd < 0: 479 return iLine + 1 == len(asFollowingLines) and sStr.find(sLine, off) < 0; 480 if len(sLine) > 0 and sStr.find(sLine, off, offEnd) < 0: 481 return False; 482 483 # next line. 484 off = offEnd + 1; 485 486 return True; 487 488 @staticmethod 489 def isThisFollowedByTheseLines(sStr, sFirst, asFollowingLines): 490 """ 491 Looks for a line contining sFirst which is then followed by lines 492 with the strings in asFollowingLines. (No newline chars anywhere!) 493 Returns True / False. 494 """ 495 off = sStr.find(sFirst, 0); 496 while off >= 0: 497 if VirtualTestSheriff.matchFollowedByLines(sStr, off, asFollowingLines): 498 return True; 499 off = sStr.find(sFirst, off + 1); 500 return False; 501 502 @staticmethod 503 def findAndReturnResetOfLine(sHaystack, sNeedle): 504 """ 505 Looks for sNeedle in sHaystack. 506 Returns The text following the needle up to the end of the line. 507 Returns None if not found. 508 """ 509 off = sHaystack.find(sNeedle); 510 if off < 0: 511 return None; 512 off += len(sNeedle) 513 offEol = sHaystack.find('\n', off); 514 if offEol < 0: 515 offEol = len(sHaystack); 516 return sHaystack[off:offEol] 517 518 @staticmethod 519 def findInAnyAndReturnResetOfLine(asHaystacks, sNeedle): 520 """ 521 Looks for sNeedle in zeroe or more haystacks (asHaystack). 522 Returns The text following the first needed found up to the end of the line. 523 Returns None if not found. 524 """ 525 for sHaystack in asHaystacks: 526 sRet = VirtualTestSheriff.findAndReturnResetOfLine(sHaystack, sNeedle); 527 if sRet is not None: 528 return sRet; 529 return None; 530 531 532 # 533 # The investigative units. 534 # 400 535 401 536 def investigateBadTestBox(self, oCaseFile): … … 434 569 ( True, 'VERR_IEM_INSTR_NOT_IMPLEMENTED', ktReason_Guru_VERR_IEM_INSTR_NOT_IMPLEMENTED ), 435 570 ( True, 'VERR_IEM_ASPECT_NOT_IMPLEMENTED', ktReason_Guru_VERR_IEM_ASPECT_NOT_IMPLEMENTED ), 571 ( True, 'VERR_TRPM_DONT_PANIC', ktReason_Guru_VERR_TRPM_DONT_PANIC ), 436 572 ( True, 'VINF_EM_TRIPLE_FAULT', ktReason_Guru_VINF_EM_TRIPLE_FAULT ), 437 ( True, 'vboxinstaller: Exit code: -11 (', ktReason_XPCOM_Exit_Minus_11),438 573 ]; 574 575 def investigateVMResult(self, oCaseFile, oFailedResult, sResultLog): 576 """ 577 Investigates a failed VM run. 578 """ 579 580 def investigateLogSet(): 581 """ 582 Investigates the current set of VM related logs. 583 """ 584 self.dprint('investigateLogSet: %u chars result log, %u chars VM log, %u chars kernel log' 585 % ( len(sResultLog) if sResultLog is not None else 0, 586 len(sVMLog) if sVMLog is not None else 0, 587 len(sKrnlLog) if sKrnlLog is not None else 0), ); 588 #self.dprint('main.log<<<\n%s\n<<<\n' % (sResultLog,)); 589 #self.dprint('vbox.log<<<\n%s\n<<<\n' % (sVMLog,)); 590 #self.dprint('krnl.log<<<\n%s\n<<<\n' % (sKrnlLog,)); 591 592 # TODO: more 593 594 # 595 # Look for BSODs. Some stupid stupid inconsistencies in reason and log messages here, so don't try prettify this. 596 # 597 sDetails = self.findInAnyAndReturnResetOfLine([ sVMLog, sResultLog ], 598 'GIM: HyperV: Guest indicates a fatal condition! P0='); 599 if sDetails is not None: 600 # P0=%#RX64 P1=%#RX64 P2=%#RX64 P3=%#RX64 P4=%#RX64 " 601 sKey = sDetails.split(' ', 1)[0]; 602 try: sKey = '0x%08X' % (int(sKey, 16),); 603 except: pass; 604 if sKey in self.asBsodReasons or sKey.lower() in self.asBsodReasons: 605 tReason = ( self.ksBsodCategory, sKey ); 606 else: 607 self.dprint('BSOD "%s" not found in %s;' % (sKey, self.asBsodReasons)); 608 tReason = ( self.ksBsodCategory, self.ksBsodAddNew ); 609 return oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult, sComment = sDetails.strip()); 610 611 # 612 # Look for linux panic. 613 # 614 if sKrnlLog is not None: 615 pass; ## @todo 616 617 # 618 # Loop thru the simple stuff. 619 # 620 fFoundSomething = False; 621 for fStopOnHit, sNeedle, tReason in self.katSimpleMainAndVmLogReasons: 622 if sResultLog.find(sNeedle) > 0 or sVMLog.find(sNeedle) > 0: 623 oCaseFile.noteReasonForId(tReason, oFailedResult.idTestResult); 624 if fStopOnHit: 625 return True; 626 fFoundSomething = True; 627 628 # 629 # Check for repeated reboots... 630 # 631 cResets = sVMLog.count('Changing the VM state from \'RUNNING\' to \'RESETTING\''); 632 if cResets > 10: 633 return oCaseFile.noteReasonForId(self.ktReason_Unknown_Reboot_Loop, oFailedResult.idTestResult, 634 sComment = 'Counted %s reboots' % (cResets,)); 635 636 return fFoundSomething; 637 638 # 639 # Check if we got any VM or/and kernel logs. Treat them as sets in 640 # case we run multiple VMs here. 641 # 642 sVMLog = None; 643 sKrnlLog = None; 644 for oFile in oFailedResult.aoFiles: 645 if oFile.sKind == TestResultFileData.ksKind_LogReleaseVm: 646 if sVMLog is not None: 647 if investigateLogSet() is True: 648 return True; 649 sKrnlLog = None; 650 sVMLog = oCaseFile.getLogFile(oFile); 651 elif oFile.sKind == TestResultFileData.ksKind_LogGuestKernel: 652 sKrnlLog = oCaseFile.getLogFile(oFile); 653 if sVMLog is not None and investigateLogSet() is True: 654 return True; 655 656 return None; 657 658 659 def isResultFromVMRun(self, oFailedResult, sResultLog): 660 """ 661 Checks if this result and corresponding log snippet looks like a VM run. 662 """ 663 664 # Look for startVmEx/ startVmAndConnectToTxsViaTcp and similar output in the log. 665 if sResultLog.find(' startVm') > 0: 666 return True; 667 668 # Any other indicators? No? 669 _ = oFailedResult; 670 return False; 671 439 672 440 673 def investigateVBoxVMTest(self, oCaseFile, fSingleVM): … … 450 683 451 684 # 452 # Do some quick searches thru the main log to see if there is anything 453 # immediately incriminating evidence there. 454 # 455 sMainLog = oCaseFile.getMainLog(); 456 for fStopOnHit, sNeedle, tReason in self.katSimpleMainAndVmLogReasons: 457 if sMainLog.find(sNeedle) > 0: 458 oCaseFile.noteReason(tReason); 459 if fStopOnHit: 460 if oCaseFile.isSingleTestFailure(): 461 return self.caseClosed(oCaseFile); 462 break; 463 685 # Get a list of test result failures we should be looking into and the main log. 686 # 687 aoFailedResults = oCaseFile.oTree.getListOfFailures(); 688 sMainLog = oCaseFile.getMainLog(); 689 690 # 691 # There are a set of errors ending up on the top level result record. 692 # Should deal with these first. 693 # 694 if len(aoFailedResults) == 1 and aoFailedResults[0] == oCaseFile.oTree: 695 # Check if we've just got that XPCOM client smoke test shutdown issue. This will currently always 696 # be reported on the top result because vboxinstall.py doesn't add an error for it. It is easy to 697 # ignore other failures in the test if we're not a little bit careful here. 698 if sMainLog.find('vboxinstaller: Exit code: -11 (') > 0: 699 oCaseFile.noteReason(self.ktReason_XPCOM_Exit_Minus_11); 700 return self.caseClosed(oCaseFile); 701 702 # Hang after starting VBoxSVC (e.g. idTestSet=136307258) 703 if self.isThisFollowedByTheseLines(sMainLog, 'oVBoxMgr=<vboxapi.VirtualBoxManager object at', 704 (' Timeout: ', ' Attempting to abort child...',) ): 705 if sMainLog.find('*** glibc detected *** /') > 0: 706 oCaseFile.noteReason(self.ktReason_XPCOM_VBoxSVC_Hang_Plus_Heap_Corruption); 707 else: 708 oCaseFile.noteReason(self.ktReason_XPCOM_VBoxSVC_Hang); 709 return self.caseClosed(oCaseFile); 710 711 712 713 # Look for heap corruption without visible hang. 714 if sMainLog.find('*** glibc detected *** /') > 0 \ 715 or sMainLog.find("-1073740940"): # STATUS_HEAP_CORRUPTION / 0xc0000374 716 oCaseFile.noteReason(self.ktReason_Unknown_Heap_Corruption); 717 return self.caseClosed(oCaseFile); 718 719 # 720 # Go thru each failed result. 721 # 722 for oFailedResult in aoFailedResults: 723 self.dprint('Looking at test result #%u - %s' % (oFailedResult.idTestResult, oFailedResult.getFullName(),)); 724 sResultLog = TestSetData.extractLogSectionElapsed(sMainLog, oFailedResult.tsCreated, oFailedResult.tsElapsed); 725 if oFailedResult.sName == 'Installing VirtualBox': 726 self.vprint('TODO: Installation failure'); 727 elif oFailedResult.sName == 'Uninstalling VirtualBox': 728 self.vprint('TODO: Uninstallation failure'); 729 elif self.isResultFromVMRun(oFailedResult, sResultLog): 730 self.investigateVMResult(oCaseFile, oFailedResult, sResultLog); 731 elif sResultLog.find('The machine is not mutable (state is ') > 0: 732 self.vprint('Ignorining "machine not mutable" error as it is probably due to an earlier problem'); 733 oCaseFile.noteReasonForId(self.ktHarmless, oFailedResult.idTestResult); 734 else: 735 self.vprint('TODO: Cannot place idTestResult=%u - %s' % (oFailedResult.idTestResult, oFailedResult.sName,)); 736 self.dprint('%s + %s <<\n%s\n<<' % (oFailedResult.tsCreated, oFailedResult.tsElapsed, sResultLog,)); 737 738 # 739 # Report home and close the case if we got them all, otherwise log it. 740 # 741 if len(oCaseFile.dReasonForResultId) >= len(aoFailedResults): 742 return self.caseClosed(oCaseFile); 743 744 if len(oCaseFile.dReasonForResultId) > 0: 745 self.vprint('TODO: Got %u out of %u - close, but no cigar. :-/' 746 % (len(oCaseFile.dReasonForResultId), len(aoFailedResults))); 747 else: 748 self.vprint('XXX: Could not figure out anything at all! :-('); 464 749 return False; 465 750 … … 472 757 # Get a list of failed test sets without any assigned failure reason. 473 758 # 759 cGot = 0; 474 760 aoTestSets = self.oTestSetLogic.fetchFailedSetsWithoutReason(cHoursBack = self.oConfig.cHoursBack, tsNow = self.tsNow); 475 761 for oTestSet in aoTestSets: … … 490 776 if oTestSet.enmStatus == TestSetData.ksTestStatus_BadTestBox: 491 777 self.dprint('investigateBadTestBox is taking over %s.' % (oCaseFile.sLongName,)); 492 self.investigateBadTestBox(oCaseFile);778 fRc = self.investigateBadTestBox(oCaseFile); 493 779 elif oCaseFile.isVBoxUnitTest(): 494 780 self.dprint('investigateVBoxUnitTest is taking over %s.' % (oCaseFile.sLongName,)); 495 self.investigateVBoxUnitTest(oCaseFile);781 fRc = self.investigateVBoxUnitTest(oCaseFile); 496 782 elif oCaseFile.isVBoxInstallTest(): 497 783 self.dprint('investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,)); 498 self.investigateVBoxVMTest(oCaseFile, fSingleVM = True);784 fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = True); 499 785 elif oCaseFile.isVBoxSmokeTest(): 500 786 self.dprint('investigateVBoxVMTest is taking over %s.' % (oCaseFile.sLongName,)); 501 self.investigateVBoxVMTest(oCaseFile, fSingleVM = False);787 fRc = self.investigateVBoxVMTest(oCaseFile, fSingleVM = False); 502 788 else: 503 789 self.vprint('reasoningFailures: Unable to classify test set: %s' % (oCaseFile.sLongName,)); 790 fRc = False; 791 cGot += fRc is True; 792 self.vprint('reasoningFailures: Got %u out of %u' % (cGot, len(aoTestSets), )); 504 793 return 0; 505 794 … … 516 805 self.oFailureReasonLogic = FailureReasonLogic(self.oDb); 517 806 self.oTestResultFailureLogic = TestResultFailureLogic(self.oDb); 807 self.asBsodReasons = self.oFailureReasonLogic.fetchForSheriffByNamedCategory(self.ksBsodCategory); 518 808 519 809 # Get a fix on our 'now' before we do anything.. -
trunk/src/VBox/ValidationKit/testmanager/core/failurereason.py
r61286 r61424 219 219 aoRows.append(FailureReasonDataEx().initFromDbRowEx(aoRow, self.oCategoryLogic, self.oUserAccountLogic)); 220 220 return aoRows 221 222 223 def fetchForSheriffByNamedCategory(self, sFailureCategory): 224 """ 225 Fetches the short names of the reasons in the named category. 226 227 Returns array of strings. 228 Raises exception on error. 229 """ 230 self._oDb.execute('SELECT FailureReasons.sShort\n' 231 'FROM FailureReasons,\n' 232 ' FailureCategories\n' 233 'WHERE FailureReasons.tsExpire = \'infinity\'::TIMESTAMP\n' 234 ' AND FailureReasons.idFailureCategory = FailureCategories.idFailureCategory\n' 235 ' AND FailureCategories.sShort = %s\n' 236 'ORDER BY FailureReasons.sShort ASC\n' 237 , ( sFailureCategory,)); 238 return [aoRow[0] for aoRow in self._oDb.fetchAll()]; 239 221 240 222 241 def fetchForCombo(self, sFirstEntry = 'Select a failure reason', tsEffective = None): -
trunk/src/VBox/ValidationKit/testmanager/core/testresults.py
r61284 r61424 199 199 cChildErrors = 0; 200 200 for oChild in self.aoChildren: 201 cChanges += oChild.deepCountErrorContributers(); 202 cChildErrors += oChild.cErrors; 201 if oChild.cErrors > 0: 202 cChildErrors += oChild.cErrors; 203 cChanges += oChild.deepCountErrorContributers(); 203 204 204 205 # Did we contribute as well? 205 if self.cErrors != cChildErrors: 206 assert self.cErrors >= cChildErrors; 206 if self.cErrors > cChildErrors: 207 207 cChanges += 1; 208 208 return cChanges; 209 210 def getListOfFailures(self): 211 """ 212 Get a list of test results insances actually contributing to cErrors. 213 214 Returns a list of TestResultDataEx insance from this tree. (shared!) 215 """ 216 # Check each child (if any). 217 aoRet = []; 218 cChildErrors = 0; 219 for oChild in self.aoChildren: 220 if oChild.cErrors > 0: 221 cChildErrors += oChild.cErrors; 222 aoRet.extend(oChild.getListOfFailures()); 223 224 # Did we contribute as well? 225 if self.cErrors > cChildErrors: 226 aoRet.append(self); 227 228 return aoRet; 229 230 def getFullName(self): 231 """ Constructs the full name of this test result. """ 232 if self.oParent is None: 233 return self.sName; 234 return self.oParent.getFullName() + ' / ' + self.sName; 235 209 236 210 237 … … 351 378 ksParam_idStrKind = 'TestResultFile_idStrKind'; 352 379 ksParam_idStrMime = 'TestResultFile_idStrMime'; 380 381 ## @name Kind of files. 382 ## @{ 383 ksKind_LogReleaseVm = 'log/release/vm'; 384 ksKind_LogDebugVm = 'log/debug/vm'; 385 ksKind_LogReleaseSvc = 'log/release/svc'; 386 ksKind_LogRebugSvc = 'log/debug/svc'; 387 ksKind_LogReleaseClient = 'log/release/client'; 388 ksKind_LogDebugClient = 'log/debug/client'; 389 ksKind_LogInstaller = 'log/installer'; 390 ksKind_LogUninstaller = 'log/uninstaller'; 391 ksKind_LogGuestKernel = 'log/guest/kernel'; 392 ksKind_CrashReportVm = 'crash/report/vm'; 393 ksKind_CrashDumpVm = 'crash/dump/vm'; 394 ksKind_CrashReportSvc = 'crash/report/svc'; 395 ksKind_CrashDumpSvc = 'crash/dump/svc'; 396 ksKind_CrashReportClient = 'crash/report/client'; 397 ksKind_CrashDumpClient = 'crash/dump/client'; 398 ksKind_MiscOther = 'misc/other'; 399 ksKind_ScreenshotFailure = 'screenshot/failure'; 400 ksKind_ScreenshotSuccesss = 'screenshot/success'; 401 #kSkind_ScreenCaptureFailure = 'screencapture/failure'; 402 ## @} 353 403 354 404 def __init__(self): -
trunk/src/VBox/ValidationKit/testmanager/core/testset.py
r61327 r61424 38 38 from common import utils; 39 39 from testmanager import config; 40 from testmanager.core import db; 40 41 from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, \ 41 42 TMExceptionBase, TMTooManyRows, TMRowNotFound; … … 229 230 return oFile; 230 231 232 @staticmethod 233 def findLogOffsetForTimestamp(sLogContent, tsTimestamp, offStart = 0, fAfter = False): 234 """ 235 Log parsing utility function for finding the offset for the given timestamp. 236 237 We ASSUME the log lines are prefixed with UTC timestamps on the format 238 '09:43:55.789353'. 239 240 Return index into the sLogContent string, 0 if not found. 241 """ 242 # Turn tsTimestamp into a string compatible with what we expect to find in the log. 243 oTsZulu = db.dbTimestampToZuluDatetime(tsTimestamp); 244 sWantedTs = oTsZulu.strftime('%H:%M:%S.%f'); 245 assert len(sWantedTs) == 15; 246 247 # Now loop thru the string, line by line. 248 offRet = offStart; 249 off = offStart; 250 while True: 251 sThisTs = sLogContent[off : off + 15]; 252 if len(sThisTs) >= 15 \ 253 and sThisTs[2] == ':' \ 254 and sThisTs[5] == ':' \ 255 and sThisTs[8] == '.' \ 256 and sThisTs[14] in '0123456789': 257 if sThisTs < sWantedTs: 258 offRet = off; 259 elif sThisTs == sWantedTs: 260 if not fAfter: 261 return off; 262 offRet = off; 263 else: 264 if fAfter: 265 offRet = off; 266 break; 267 268 # next line. 269 off = sLogContent.find('\n', off); 270 if off < 0: 271 if fAfter: 272 offRet = len(sLogContent); 273 break; 274 off += 1; 275 276 return offRet; 277 278 @staticmethod 279 def extractLogSection(sLogContent, tsStart, tsLast): 280 """ 281 Returns log section from tsStart to tsLast (or all if we cannot make sense of it). 282 """ 283 offStart = TestSetData.findLogOffsetForTimestamp(sLogContent, tsStart); 284 offEnd = TestSetData.findLogOffsetForTimestamp(sLogContent, tsLast, offStart, fAfter = True); 285 return sLogContent[offStart : offEnd]; 286 287 @staticmethod 288 def extractLogSectionElapsed(sLogContent, tsStart, tsElapsed): 289 """ 290 Returns log section from tsStart and tsElapsed forward (or all if we cannot make sense of it). 291 """ 292 tsStart = db.dbTimestampToZuluDatetime(tsStart); 293 tsLast = tsStart + tsElapsed; 294 return TestSetData.extractLogSection(sLogContent, tsStart, tsLast); 295 231 296 232 297 -
trunk/src/VBox/ValidationKit/testmanager/webui/wuiadmin.py
r61422 r61424 405 405 return False 406 406 407 def _actionGenericDoRemove(self, oLogicType, sParamId, sRedirAction):408 """409 Delete entry (using oLogicType.removeEntry).410 411 oLogicType is a class that implements addEntry.412 413 sParamId is the name (ksParam_...) of the HTTP variable hold the ID of414 the database entry to delete.415 416 sRedirAction is what action to redirect to on success.417 """418 idEntry = self.getIntParam(sParamId, iMin = 1, iMax = 0x7fffffe)419 fCascade = self.getBoolParam('fCascadeDelete', False);420 self._checkForUnknownParameters()421 422 try:423 self._sPageTitle = None424 self._sPageBody = None425 self._sRedirectTo = self._sActionUrlBase + sRedirAction;426 return oLogicType(self._oDb).removeEntry(self._oCurUser.uid, idEntry, fCascade = fCascade, fCommit = True);427 except Exception as oXcpt:428 self._oDb.rollback();429 self._sPageTitle = 'Unable to delete entry';430 self._sPageBody = str(oXcpt);431 if config.g_kfDebugDbXcpt:432 self._sPageBody += cgitb.html(sys.exc_info());433 self._sRedirectTo = None;434 return False;435 436 407 437 408 # -
trunk/src/VBox/ValidationKit/testmanager/webui/wuibase.py
r61267 r61424 822 822 return True 823 823 824 def _actionGenericDoRemove(self, oLogicType, sParamId, sRedirAction): 825 """ 826 Delete entry (using oLogicType.removeEntry). 827 828 oLogicType is a class that implements addEntry. 829 830 sParamId is the name (ksParam_...) of the HTTP variable hold the ID of 831 the database entry to delete. 832 833 sRedirAction is what action to redirect to on success. 834 """ 835 idEntry = self.getIntParam(sParamId, iMin = 1, iMax = 0x7ffffffe) 836 fCascade = self.getBoolParam('fCascadeDelete', False); 837 self._checkForUnknownParameters() 838 839 try: 840 self._sPageTitle = None 841 self._sPageBody = None 842 self._sRedirectTo = self._sActionUrlBase + sRedirAction; 843 return oLogicType(self._oDb).removeEntry(self._oCurUser.uid, idEntry, fCascade = fCascade, fCommit = True); 844 except Exception as oXcpt: 845 self._oDb.rollback(); 846 self._sPageTitle = 'Unable to delete entry'; 847 self._sPageBody = str(oXcpt); 848 if config.g_kfDebugDbXcpt: 849 self._sPageBody += cgitb.html(sys.exc_info()); 850 self._sRedirectTo = None; 851 return False; 824 852 825 853 def _actionGenericFormEdit(self, oDataType, oFormType, sIdParamName = None, sRedirectTo = None): -
trunk/src/VBox/ValidationKit/testmanager/webui/wuicontentbase.py
r61272 r61424 606 606 aoActions.append(WuiTmLink('Details', '', dParams)); 607 607 608 # Add delete operation if available. 609 if hasattr(self._oDisp, self._sActionBase + 'DoRemove'): 610 dParams = self._oDisp.getParameters(); 611 dParams[WuiDispatcherBase.ksParamAction] = getattr(self._oDisp, self._sActionBase + 'DoRemove'); 612 dParams[getattr(oData, 'ksParam_' + oData.ksIdAttr)] = getattr(oData, oData.ksIdAttr); 613 aoActions.append(WuiTmLink('Delete', '', dParams, sConfirm = "Are you absolutely sure?")); 614 608 615 return aoActions; 609 616 -
trunk/src/VBox/ValidationKit/testmanager/webui/wuimain.py
r61422 r61424 68 68 ksActionTestResultFailureEdit = 'TestResultFailureEdit' 69 69 ksActionTestResultFailureEditPost = 'TestResultFailureEditPost' 70 ksActionTestResultFailureDoRemove = 'TestResultFailureDoRemove' 70 71 ksActionViewLog = 'ViewLog' 71 72 ksActionGetFile = 'GetFile' … … 208 209 self._dDispatch[self.ksActionTestResultFailureAddPost] = self._actionTestResultFailureAddPost; 209 210 self._dDispatch[self.ksActionTestResultFailureDetails] = self._actionTestResultFailureDetails; 211 self._dDispatch[self.ksActionTestResultFailureDoRemove] = self._actionTestResultFailureDoRemove; 210 212 self._dDispatch[self.ksActionTestResultFailureEdit] = self._actionTestResultFailureEdit; 211 213 self._dDispatch[self.ksActionTestResultFailureEditPost] = self._actionTestResultFailureEditPost; … … 969 971 WuiTestResultFailure, self.ksActionResultsUnGrouped); 970 972 973 def _actionTestResultFailureDoRemove(self): 974 """ Action wrapper. """ 975 from testmanager.core.testresultfailures import TestResultFailureData, TestResultFailureLogic; 976 from testmanager.webui.wuitestresultfailure import WuiTestResultFailure; 977 return self._actionGenericDoRemove(TestResultFailureLogic, TestResultFailureData.ksParam_idTestResult, 978 self.ksActionResultsUnGrouped); 979 971 980 def _actionTestResultFailureDetails(self): 972 981 """ Pro forma. """ -
trunk/src/VBox/ValidationKit/testmanager/webui/wuitestresult.py
r61306 r61424 182 182 if oTestResult.tsElapsed is not None: 183 183 tsEvent += oTestResult.tsElapsed; 184 sHtml = ' <tr class="%s tmtbl-events-leaf tmtbl-events-lvl%s tmstatusrow-%s" >\n' \185 ' <td >%s</td>\n' \184 sHtml = ' <tr class="%s tmtbl-events-leaf tmtbl-events-lvl%s tmstatusrow-%s" id="S%u">\n' \ 185 ' <td id="E%u">%s</td>\n' \ 186 186 ' <td>%s</td>\n' \ 187 187 ' <td>%s</td>\n' \ … … 190 190 ' <td>%s</td>\n' \ 191 191 ' </tr>\n' \ 192 % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth, oTestResult.enmStatus, 193 webutils.escapeElem(self.formatTsShort(tsEvent)),192 % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth, oTestResult.enmStatus, oTestResult.idTestResult, 193 oTestResult.idTestResult, webutils.escapeElem(self.formatTsShort(tsEvent)), 194 194 sElapsedGraph, 195 195 webutils.escapeElem(self.formatIntervalShort(oTestResult.tsElapsed)) if oTestResult.tsElapsed is not None … … 307 307 # Done? 308 308 if oTestResult.tsElapsed is not None: 309 sHtml += ' <tr class="%s tmtbl-events-final tmtbl-events-lvl%s tmstatusrow-%s" >\n' \309 sHtml += ' <tr class="%s tmtbl-events-final tmtbl-events-lvl%s tmstatusrow-%s" id="E%d">\n' \ 310 310 ' <td>%s</td>\n' \ 311 311 ' <td>%s</td>\n' \ … … 315 315 ' <td>%s</td>\n' \ 316 316 ' </tr>\n' \ 317 % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth, oTestResult.enmStatus, 317 % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth, oTestResult.enmStatus, oTestResult.idTestResult, 318 318 webutils.escapeElem(self.formatTsShort(oTestResult.tsCreated + oTestResult.tsElapsed)), 319 319 sElapsedGraph,
Note:
See TracChangeset
for help on using the changeset viewer.