VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testmanager/webui/wuireport.py@ 79087

Last change on this file since 79087 was 79087, checked in by vboxsync, 6 years ago

ValKit: New pylint version - cleanup in progress.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.5 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: wuireport.py 79087 2019-06-11 11:58:28Z vboxsync $
3
4"""
5Test Manager WUI - Reports.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2019 Oracle Corporation
11
12This file is part of VirtualBox Open Source Edition (OSE), as
13available from http://www.virtualbox.org. This file is free software;
14you can redistribute it and/or modify it under the terms of the GNU
15General Public License (GPL) as published by the Free Software
16Foundation, in version 2 as it comes in the "COPYING" file of the
17VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19
20The contents of this file may alternatively be used under the terms
21of the Common Development and Distribution License Version 1.0
22(CDDL) only, as it comes in the "COPYING.CDDL" file of the
23VirtualBox OSE distribution, in which case the provisions of the
24CDDL are applicable instead of those of the GPL.
25
26You may elect to license modified versions of this file under the
27terms and conditions of either the GPL or the CDDL or both.
28"""
29__version__ = "$Revision: 79087 $"
30
31
32# Validation Kit imports.
33from common import webutils;
34from testmanager.webui.wuicontentbase import WuiContentBase, WuiTmLink, WuiSvnLinkWithTooltip;
35from testmanager.webui.wuihlpgraph import WuiHlpGraphDataTable, WuiHlpBarGraph;
36from testmanager.webui.wuitestresult import WuiTestSetLink;
37from testmanager.webui.wuiadmintestcase import WuiTestCaseDetailsLink;
38from testmanager.webui.wuiadmintestbox import WuiTestBoxDetailsLink;
39from testmanager.core.report import ReportModelBase;
40
41
42class WuiReportSummaryLink(WuiTmLink):
43 """ Generic report summary link. """
44
45 def __init__(self, sSubject, aIdSubjects, sName = WuiContentBase.ksShortReportLink,
46 tsNow = None, cPeriods = None, cHoursPerPeriod = None, fBracketed = False, dExtraParams = None):
47 from testmanager.webui.wuimain import WuiMain;
48 dParams = {
49 WuiMain.ksParamAction: WuiMain.ksActionReportSummary,
50 WuiMain.ksParamReportSubject: sSubject,
51 WuiMain.ksParamReportSubjectIds: aIdSubjects,
52 };
53 if dExtraParams is not None:
54 dParams.update(dExtraParams);
55 if tsNow is not None:
56 dParams[WuiMain.ksParamEffectiveDate] = tsNow;
57 if cPeriods is not None:
58 dParams[WuiMain.ksParamReportPeriods] = cPeriods;
59 if cPeriods is not None:
60 dParams[WuiMain.ksParamReportPeriodInHours] = cHoursPerPeriod;
61 WuiTmLink.__init__(self, sName, WuiMain.ksScriptName, dParams, fBracketed = fBracketed);
62
63
64class WuiReportBase(WuiContentBase):
65 """
66 Base class for the reports.
67 """
68
69 def __init__(self, oModel, dParams, fSubReport = False, aiSortColumns = None, fnDPrint = None, oDisp = None):
70 WuiContentBase.__init__(self, fnDPrint = fnDPrint, oDisp = oDisp);
71 self._oModel = oModel;
72 self._dParams = dParams;
73 self._fSubReport = fSubReport;
74 self._sTitle = None;
75 self._aiSortColumns = aiSortColumns;
76
77 # Additional URL parameters for reports
78 self._dExtraParams = {};
79 dCurParams = None if oDisp is None else oDisp.getParameters();
80 if dCurParams is not None:
81 from testmanager.webui.wuimain import WuiMain;
82 for sExtraParam in [ WuiMain.ksParamReportPeriods, WuiMain.ksParamReportPeriodInHours,
83 WuiMain.ksParamEffectiveDate, ]:
84 if sExtraParam in dCurParams:
85 self._dExtraParams[sExtraParam] = dCurParams[sExtraParam];
86
87
88 def generateNavigator(self, sWhere):
89 """
90 Generates the navigator (manipulate _dParams).
91 Returns HTML.
92 """
93 assert sWhere == 'top' or sWhere == 'bottom';
94
95 return '';
96
97 def generateReportBody(self):
98 """
99 This is overridden by the child class to generate the report.
100 Returns HTML.
101 """
102 return '<h3>Must override generateReportBody!</h3>';
103
104 def show(self):
105 """
106 Generate the report.
107 Returns (sTitle, HTML).
108 """
109
110 sTitle = self._sTitle if self._sTitle is not None else type(self).__name__;
111 sReport = self.generateReportBody();
112 if not self._fSubReport:
113 sReport = self.generateNavigator('top') + sReport + self.generateNavigator('bottom');
114 sTitle = self._oModel.sSubject + ' - ' + sTitle; ## @todo add subject to title in a proper way!
115
116 sReport += '\n\n<!-- HEYYOU: sSubject=%s aidSubjects=%s -->\n\n' % (self._oModel.sSubject, self._oModel.aidSubjects);
117 return (sTitle, sReport);
118
119 @staticmethod
120 def fmtPct(cHits, cTotal):
121 """
122 Formats a percent number.
123 Returns a string.
124 """
125 uPct = cHits * 100 / cTotal;
126 if uPct >= 10 and (uPct > 103 or uPct <= 95):
127 return '%s%%' % (uPct,);
128 return '%.1f%%' % (cHits * 100.0 / cTotal,);
129
130 @staticmethod
131 def fmtPctWithHits(cHits, cTotal):
132 """
133 Formats a percent number with total in parentheses.
134 Returns a string.
135 """
136 return '%s (%s)' % (WuiReportBase.fmtPct(cHits, cTotal), cHits);
137
138 @staticmethod
139 def fmtPctWithHitsAndTotal(cHits, cTotal):
140 """
141 Formats a percent number with total in parentheses.
142 Returns a string.
143 """
144 return '%s (%s/%s)' % (WuiReportBase.fmtPct(cHits, cTotal), cHits, cTotal);
145
146
147
148class WuiReportSuccessRate(WuiReportBase):
149 """
150 Generates a report displaying the success rate over time.
151 """
152
153 def generateReportBody(self):
154 self._sTitle = 'Success rate';
155
156 adPeriods = self._oModel.getSuccessRates();
157
158 sReport = '';
159
160 oTable = WuiHlpGraphDataTable('Period', [ 'Succeeded', 'Skipped', 'Failed' ]);
161
162 #for i in range(len(adPeriods) - 1, -1, -1):
163 for i, dStatuses in enumerate(adPeriods):
164 cSuccess = dStatuses[ReportModelBase.ksTestStatus_Success] + dStatuses[ReportModelBase.ksTestStatus_Skipped];
165 cTotal = cSuccess + dStatuses[ReportModelBase.ksTestStatus_Failure];
166 sPeriod = self._oModel.getPeriodDesc(i);
167 if cTotal > 0:
168 oTable.addRow(sPeriod,
169 [ dStatuses[ReportModelBase.ksTestStatus_Success] * 100 / cTotal,
170 dStatuses[ReportModelBase.ksTestStatus_Skipped] * 100 / cTotal,
171 dStatuses[ReportModelBase.ksTestStatus_Failure] * 100 / cTotal, ],
172 [ self.fmtPctWithHits(dStatuses[ReportModelBase.ksTestStatus_Success], cTotal),
173 self.fmtPctWithHits(dStatuses[ReportModelBase.ksTestStatus_Skipped], cTotal),
174 self.fmtPctWithHits(dStatuses[ReportModelBase.ksTestStatus_Failure], cTotal), ]);
175 else:
176 oTable.addRow(sPeriod, [ 0, 0, 0 ], [ '0%', '0%', '0%' ]);
177
178 cTotalNow = adPeriods[0][ReportModelBase.ksTestStatus_Success];
179 cTotalNow += adPeriods[0][ReportModelBase.ksTestStatus_Skipped];
180 cSuccessNow = cTotalNow;
181 cTotalNow += adPeriods[0][ReportModelBase.ksTestStatus_Failure];
182 sReport += '<p>Current success rate: ';
183 if cTotalNow > 0:
184 sReport += '%s%% (thereof %s%% skipped)</p>\n' \
185 % ( cSuccessNow * 100 / cTotalNow, adPeriods[0][ReportModelBase.ksTestStatus_Skipped] * 100 / cTotalNow);
186 else:
187 sReport += 'N/A</p>\n'
188
189 oGraph = WuiHlpBarGraph('success-rate', oTable, self._oDisp);
190 oGraph.setRangeMax(100);
191 sReport += oGraph.renderGraph();
192
193 return sReport;
194
195
196class WuiReportFailuresBase(WuiReportBase):
197 """
198 Common parent of WuiReportFailureReasons and WuiReportTestCaseFailures.
199 """
200
201 def _splitSeriesIntoMultipleGraphs(self, aidSorted, cMaxSeriesPerGraph = 8):
202 """
203 Splits the ID array into one or more arrays, making sure we don't
204 have too many series per graph.
205 Returns array of ID arrays.
206 """
207 if len(aidSorted) <= cMaxSeriesPerGraph + 2:
208 return [aidSorted,];
209 cGraphs = len(aidSorted) / cMaxSeriesPerGraph + (len(aidSorted) % cMaxSeriesPerGraph != 0);
210 cPerGraph = len(aidSorted) / cGraphs + (len(aidSorted) % cGraphs != 0);
211
212 aaoRet = [];
213 cLeft = len(aidSorted);
214 iSrc = 0;
215 while cLeft > 0:
216 cThis = cPerGraph;
217 if cLeft <= cPerGraph + 2:
218 cThis = cLeft;
219 elif cLeft <= cPerGraph * 2 + 4:
220 cThis = cLeft / 2;
221 aaoRet.append(aidSorted[iSrc : iSrc + cThis]);
222 iSrc += cThis;
223 cLeft -= cThis;
224 return aaoRet;
225
226 def _formatEdgeOccurenceSubject(self, oTransient):
227 """
228 Worker for _formatEdgeOccurence that child classes overrides to format
229 their type of subject data in the best possible way.
230 """
231 _ = oTransient;
232 assert False;
233 return '';
234
235 def _formatEdgeOccurence(self, oTransient):
236 """
237 Helper for formatting the transients.
238 oTransient is of type ReportFailureReasonTransient or ReportTestCaseFailureTransient.
239 """
240 sHtml = u'<li>';
241 if oTransient.fEnter: sHtml += 'Since ';
242 else: sHtml += 'Until ';
243 sHtml += WuiSvnLinkWithTooltip(oTransient.iRevision, oTransient.sRepository, fBracketed = 'False').toHtml();
244 sHtml += u', %s: ' % (WuiTestSetLink(oTransient.idTestSet, self.formatTsShort(oTransient.tsDone),
245 fBracketed = False).toHtml(), )
246 sHtml += self._formatEdgeOccurenceSubject(oTransient);
247 sHtml += u'</li>\n';
248 return sHtml;
249
250 def _generateTransitionList(self, oSet):
251 """
252 Generates the enter and leave lists.
253 """
254 # Skip this if we're looking at builds.
255 if self._oModel.sSubject in [self._oModel.ksSubBuild,] and len(self._oModel.aidSubjects) in [1, 2]:
256 return u'';
257
258 sHtml = u'<h4>Movements:</h4>\n' \
259 u'<ul>\n';
260 if not oSet.aoEnterInfo and not oSet.aoLeaveInfo:
261 sHtml += u'<li>No changes</li>\n';
262 else:
263 for oTransient in oSet.aoEnterInfo:
264 sHtml += self._formatEdgeOccurence(oTransient);
265 for oTransient in oSet.aoLeaveInfo:
266 sHtml += self._formatEdgeOccurence(oTransient);
267 sHtml += u'</ul>\n';
268
269 return sHtml;
270
271
272 def _formatSeriesNameColumnHeadersForTable(self):
273 """ Formats the series name column for the HTML table. """
274 return '<th>Subject Name</th>';
275
276 def _formatSeriesNameForTable(self, oSet, idKey):
277 """ Formats the series name for the HTML table. """
278 _ = oSet;
279 return '<td>%d</td>' % (idKey,);
280
281 def _formatRowValueForTable(self, oRow, oPeriod, cColsPerSeries):
282 """ Formats a row value for the HTML table. """
283 _ = oPeriod;
284 if oRow is None:
285 return u'<td colspan="%d"> </td>' % (cColsPerSeries,);
286 if cColsPerSeries == 2:
287 return u'<td align="right">%u%%</td><td align="center">%u / %u</td>' \
288 % (oRow.cHits * 100 / oRow.cTotal, oRow.cHits, oRow.cTotal);
289 return u'<td align="center">%u</td>' % (oRow.cHits,);
290
291 def _formatSeriesTotalForTable(self, oSet, idKey, cColsPerSeries):
292 """ Formats the totals cell for a data series in the HTML table. """
293 dcTotalPerId = getattr(oSet, 'dcTotalPerId', None);
294 if cColsPerSeries == 2:
295 return u'<td align="right">%u%%</td><td align="center">%u/%u</td>' \
296 % (oSet.dcHitsPerId[idKey] * 100 / dcTotalPerId[idKey], oSet.dcHitsPerId[idKey], dcTotalPerId[idKey]);
297 return u'<td align="center">%u</td>' % (oSet.dcHitsPerId[idKey],);
298
299 def _generateTableForSet(self, oSet, aidSorted = None, iSortColumn = 0,
300 fWithTotals = True, cColsPerSeries = None):
301 """
302 Turns the set into a table.
303
304 Returns raw html.
305 """
306 sHtml = u'<table class="tmtbl-report-set" width="100%%">\n';
307 if cColsPerSeries is None:
308 cColsPerSeries = 2 if hasattr(oSet, 'dcTotalPerId') else 1;
309
310 # Header row.
311 sHtml += u' <tr><thead><th>#</th>';
312 sHtml += self._formatSeriesNameColumnHeadersForTable();
313 for iPeriod, oPeriod in enumerate(reversed(oSet.aoPeriods)):
314 sHtml += u'<th colspan="%d"><a href="javascript:ahrefActionSortByColumns(\'%s\',[%s]);">%s</a>%s</th>' \
315 % ( cColsPerSeries, self._oDisp.ksParamSortColumns, iPeriod, webutils.escapeElem(oPeriod.sDesc),
316 '&#x25bc;' if iPeriod == iSortColumn else '');
317 if fWithTotals:
318 sHtml += u'<th colspan="%d"><a href="javascript:ahrefActionSortByColumns(\'%s\',[%s]);">Total</a>%s</th>' \
319 % ( cColsPerSeries, self._oDisp.ksParamSortColumns, len(oSet.aoPeriods),
320 '&#x25bc;' if iSortColumn == len(oSet.aoPeriods) else '');
321 sHtml += u'</thead></td>\n';
322
323 # Each data series.
324 if aidSorted is None:
325 aidSorted = oSet.dSubjects.keys();
326 sHtml += u' <tbody>\n';
327 for iRow, idKey in enumerate(aidSorted):
328 sHtml += u' <tr class="%s">' % ('tmodd' if iRow & 1 else 'tmeven',);
329 sHtml += u'<td align="left">#%u</td>' % (iRow + 1,);
330 sHtml += self._formatSeriesNameForTable(oSet, idKey);
331 for oPeriod in reversed(oSet.aoPeriods):
332 oRow = oPeriod.dRowsById.get(idKey, None);
333 sHtml += self._formatRowValueForTable(oRow, oPeriod, cColsPerSeries);
334 if fWithTotals:
335 sHtml += self._formatSeriesTotalForTable(oSet, idKey, cColsPerSeries);
336 sHtml += u' </tr>\n';
337 sHtml += u' </tbody>\n';
338 sHtml += u'</table>\n';
339 return sHtml;
340
341
342class WuiReportFailuresWithTotalBase(WuiReportFailuresBase):
343 """
344 For ReportPeriodSetWithTotalBase.
345 """
346
347 def _formatSeriedNameForGraph(self, oSubject):
348 """
349 Format the subject name for the graph.
350 """
351 return str(oSubject);
352
353 def _getSortedIds(self, oSet):
354 """
355 Get default sorted subject IDs and which column.
356 """
357
358 # Figure the sorting column.
359 if self._aiSortColumns is not None \
360 and self._aiSortColumns \
361 and abs(self._aiSortColumns[0]) <= len(oSet.aoPeriods):
362 iSortColumn = abs(self._aiSortColumns[0]);
363 fByTotal = iSortColumn >= len(oSet.aoPeriods); # pylint: disable=unused-variable
364 elif oSet.cMaxTotal < 10:
365 iSortColumn = len(oSet.aoPeriods);
366 else:
367 iSortColumn = 0;
368
369 if iSortColumn >= len(oSet.aoPeriods):
370 # Sort the total.
371 aidSortedRaw = sorted(oSet.dSubjects,
372 key = lambda idKey: oSet.dcHitsPerId[idKey] * 10000 / oSet.dcTotalPerId[idKey],
373 reverse = True);
374 else:
375 # Sort by NOW column.
376 dTmp = {};
377 for idKey in oSet.dSubjects:
378 oRow = oSet.aoPeriods[-1 - iSortColumn].dRowsById.get(idKey, None);
379 if oRow is None: dTmp[idKey] = 0;
380 else: dTmp[idKey] = oRow.cHits * 10000 / max(1, oRow.cTotal);
381 aidSortedRaw = sorted(dTmp, key = lambda idKey: dTmp[idKey], reverse = True);
382 return (aidSortedRaw, iSortColumn);
383
384 def _generateGraph(self, oSet, sIdBase, aidSortedRaw):
385 """
386 Generates graph.
387 """
388 sHtml = u'';
389 fGenerateGraph = len(aidSortedRaw) <= 6 and len(aidSortedRaw) > 0; ## Make this configurable.
390 if fGenerateGraph:
391 # Figure the graph width for all of them.
392 uPctMax = max(oSet.uMaxPct, oSet.cMaxHits * 100 / oSet.cMaxTotal);
393 uPctMax = max(uPctMax + 2, 10);
394
395 for _, aidSorted in enumerate(self._splitSeriesIntoMultipleGraphs(aidSortedRaw, 8)):
396 asNames = [];
397 for idKey in aidSorted:
398 oSubject = oSet.dSubjects[idKey];
399 asNames.append(self._formatSeriedNameForGraph(oSubject));
400
401 oTable = WuiHlpGraphDataTable('Period', asNames);
402
403 for _, oPeriod in enumerate(reversed(oSet.aoPeriods)):
404 aiValues = [];
405 asValues = [];
406
407 for idKey in aidSorted:
408 oRow = oPeriod.dRowsById.get(idKey, None);
409 if oRow is not None:
410 aiValues.append(oRow.cHits * 100 / oRow.cTotal);
411 asValues.append(self.fmtPctWithHitsAndTotal(oRow.cHits, oRow.cTotal));
412 else:
413 aiValues.append(0);
414 asValues.append('0');
415
416 oTable.addRow(oPeriod.sDesc, aiValues, asValues);
417
418 if True: # pylint: disable=using-constant-test
419 aiValues = [];
420 asValues = [];
421 for idKey in aidSorted:
422 uPct = oSet.dcHitsPerId[idKey] * 100 / oSet.dcTotalPerId[idKey];
423 aiValues.append(uPct);
424 asValues.append(self.fmtPctWithHitsAndTotal(oSet.dcHitsPerId[idKey], oSet.dcTotalPerId[idKey]));
425 oTable.addRow('Totals', aiValues, asValues);
426
427 oGraph = WuiHlpBarGraph(sIdBase, oTable, self._oDisp);
428 oGraph.setRangeMax(uPctMax);
429 sHtml += '<br>\n';
430 sHtml += oGraph.renderGraph();
431 return sHtml;
432
433
434
435class WuiReportFailureReasons(WuiReportFailuresBase):
436 """
437 Generates a report displaying the failure reasons over time.
438 """
439
440 def _formatEdgeOccurenceSubject(self, oTransient):
441 return u'%s / %s' % ( webutils.escapeElem(oTransient.oReason.oCategory.sShort),
442 webutils.escapeElem(oTransient.oReason.sShort),);
443
444 def _formatSeriesNameColumnHeadersForTable(self):
445 return '<th>Failure Reason</th>';
446
447 def _formatSeriesNameForTable(self, oSet, idKey):
448 oReason = oSet.dSubjects[idKey];
449 sHtml = u'<td>';
450 sHtml += u'%s / %s' % ( webutils.escapeElem(oReason.oCategory.sShort), webutils.escapeElem(oReason.sShort),);
451 sHtml += u'</td>';
452 return sHtml;
453
454
455 def generateReportBody(self):
456 self._sTitle = 'Failure reasons';
457
458 #
459 # Get the data and sort the data series in descending order of badness.
460 #
461 oSet = self._oModel.getFailureReasons();
462 aidSortedRaw = sorted(oSet.dSubjects, key = lambda idReason: oSet.dcHitsPerId[idReason], reverse = True);
463
464 #
465 # Generate table and transition list. These are the most useful ones with the current graph machinery.
466 #
467 sHtml = self._generateTableForSet(oSet, aidSortedRaw, len(oSet.aoPeriods));
468 sHtml += self._generateTransitionList(oSet);
469
470 #
471 # Check if most of the stuff is without any assign reason, if so, skip
472 # that part of the graph so it doesn't offset the interesting bits.
473 #
474 fIncludeWithoutReason = True;
475 for oPeriod in reversed(oSet.aoPeriods):
476 if oPeriod.cWithoutReason > oSet.cMaxHits * 4:
477 fIncludeWithoutReason = False;
478 sHtml += '<p>Warning: Many failures without assigned reason!</p>\n';
479 break;
480
481 #
482 # Generate the graph.
483 #
484 fGenerateGraph = len(aidSortedRaw) <= 9 and len(aidSortedRaw) > 0; ## Make this configurable.
485 if fGenerateGraph:
486 aidSorted = aidSortedRaw;
487
488 asNames = [];
489 for idReason in aidSorted:
490 oReason = oSet.dSubjects[idReason];
491 asNames.append('%s / %s' % (oReason.oCategory.sShort, oReason.sShort,) )
492 if fIncludeWithoutReason:
493 asNames.append('No reason');
494
495 oTable = WuiHlpGraphDataTable('Period', asNames);
496
497 cMax = oSet.cMaxHits;
498 for _, oPeriod in enumerate(reversed(oSet.aoPeriods)):
499 aiValues = [];
500
501 for idReason in aidSorted:
502 oRow = oPeriod.dRowsById.get(idReason, None);
503 iValue = oRow.cHits if oRow is not None else 0;
504 aiValues.append(iValue);
505
506 if fIncludeWithoutReason:
507 aiValues.append(oPeriod.cWithoutReason);
508 if oPeriod.cWithoutReason > cMax:
509 cMax = oPeriod.cWithoutReason;
510
511 oTable.addRow(oPeriod.sDesc, aiValues);
512
513 oGraph = WuiHlpBarGraph('failure-reason', oTable, self._oDisp);
514 oGraph.setRangeMax(max(cMax + 1, 3));
515 sHtml += oGraph.renderGraph();
516 return sHtml;
517
518
519class WuiReportTestCaseFailures(WuiReportFailuresWithTotalBase):
520 """
521 Generates a report displaying the failure reasons over time.
522 """
523
524 def _formatEdgeOccurenceSubject(self, oTransient):
525 sHtml = u'%s ' % ( webutils.escapeElem(oTransient.oSubject.sName),);
526 sHtml += WuiTestCaseDetailsLink(oTransient.oSubject.idTestCase, fBracketed = False).toHtml();
527 return sHtml;
528
529 def _formatSeriesNameColumnHeadersForTable(self):
530 return '<th>Test Case</th>';
531
532 def _formatSeriesNameForTable(self, oSet, idKey):
533 oTestCase = oSet.dSubjects[idKey];
534 sHtml = u'<td>';
535 sHtml += WuiReportSummaryLink(ReportModelBase.ksSubTestCase, oTestCase.idTestCase, sName = oTestCase.sName,
536 dExtraParams = self._dExtraParams).toHtml();
537 sHtml += u' ';
538 sHtml += WuiTestCaseDetailsLink(oTestCase.idTestCase).toHtml();
539 sHtml += u'</td>';
540 return sHtml;
541
542 def _formatSeriedNameForGraph(self, oSubject):
543 return oSubject.sName;
544
545 def generateReportBody(self):
546 self._sTitle = 'Test Case Failures';
547 oSet = self._oModel.getTestCaseFailures();
548 (aidSortedRaw, iSortColumn) = self._getSortedIds(oSet);
549
550 sHtml = self._generateTableForSet(oSet, aidSortedRaw, iSortColumn);
551 sHtml += self._generateTransitionList(oSet);
552 sHtml += self._generateGraph(oSet, 'testcase-graph', aidSortedRaw);
553 return sHtml;
554
555
556class WuiReportTestCaseArgsFailures(WuiReportFailuresWithTotalBase):
557 """
558 Generates a report displaying the failure reasons over time.
559 """
560
561 @staticmethod
562 def _formatName(oTestCaseArgs):
563 """ Internal helper for formatting the testcase name. """
564 if oTestCaseArgs.sSubName:
565 sName = u'%s / %s' % ( oTestCaseArgs.oTestCase.sName, oTestCaseArgs.sSubName, );
566 else:
567 sName = u'%s / #%u' % ( oTestCaseArgs.oTestCase.sName, oTestCaseArgs.idTestCaseArgs, );
568 return sName;
569
570 def _formatEdgeOccurenceSubject(self, oTransient):
571 sHtml = u'%s ' % ( webutils.escapeElem(self._formatName(oTransient.oSubject)),);
572 sHtml += WuiTestCaseDetailsLink(oTransient.oSubject.idTestCase, fBracketed = False).toHtml();
573 return sHtml;
574
575 def _formatSeriesNameColumnHeadersForTable(self):
576 return '<th>Test Case / Variation</th>';
577
578 def _formatSeriesNameForTable(self, oSet, idKey):
579 oTestCaseArgs = oSet.dSubjects[idKey];
580 sHtml = u'<td>';
581 sHtml += WuiReportSummaryLink(ReportModelBase.ksSubTestCaseArgs, oTestCaseArgs.idTestCaseArgs,
582 sName = self._formatName(oTestCaseArgs), dExtraParams = self._dExtraParams).toHtml();
583 sHtml += u' ';
584 sHtml += WuiTestCaseDetailsLink(oTestCaseArgs.idTestCase).toHtml();
585 sHtml += u'</td>';
586 return sHtml;
587
588 def _formatSeriedNameForGraph(self, oSubject):
589 return self._formatName(oSubject);
590
591 def generateReportBody(self):
592 self._sTitle = 'Test Case Variation Failures';
593 oSet = self._oModel.getTestCaseVariationFailures();
594 (aidSortedRaw, iSortColumn) = self._getSortedIds(oSet);
595
596 sHtml = self._generateTableForSet(oSet, aidSortedRaw, iSortColumn);
597 sHtml += self._generateTransitionList(oSet);
598 sHtml += self._generateGraph(oSet, 'testcasearg-graph', aidSortedRaw);
599 return sHtml;
600
601
602
603class WuiReportTestBoxFailures(WuiReportFailuresWithTotalBase):
604 """
605 Generates a report displaying the failure reasons over time.
606 """
607
608 def _formatEdgeOccurenceSubject(self, oTransient):
609 sHtml = u'%s ' % ( webutils.escapeElem(oTransient.oSubject.sName),);
610 sHtml += WuiTestBoxDetailsLink(oTransient.oSubject.idTestBox, fBracketed = False).toHtml();
611 return sHtml;
612
613 def _formatSeriesNameColumnHeadersForTable(self):
614 return '<th colspan="5">Test Box</th>';
615
616 def _formatSeriesNameForTable(self, oSet, idKey):
617 oTestBox = oSet.dSubjects[idKey];
618 sHtml = u'<td>';
619 sHtml += WuiReportSummaryLink(ReportModelBase.ksSubTestBox, oTestBox.idTestBox, sName = oTestBox.sName,
620 dExtraParams = self._dExtraParams).toHtml();
621 sHtml += u' ';
622 sHtml += WuiTestBoxDetailsLink(oTestBox.idTestBox).toHtml();
623 sHtml += u'</td>';
624 sOsAndVer = '%s %s' % (oTestBox.sOs, oTestBox.sOsVersion.strip(),);
625 if len(sOsAndVer) < 22:
626 sHtml += u'<td>%s</td>' % (webutils.escapeElem(sOsAndVer),);
627 else: # wonder if td.title works..
628 sHtml += u'<td title="%s" width="1%%" style="white-space:nowrap;">%s...</td>' \
629 % (webutils.escapeAttr(sOsAndVer), webutils.escapeElem(sOsAndVer[:20]));
630 sHtml += u'<td>%s</td>' % (webutils.escapeElem(oTestBox.getArchBitString()),);
631 sHtml += u'<td>%s</td>' % (webutils.escapeElem(oTestBox.getPrettyCpuVendor()),);
632 sHtml += u'<td>%s' % (oTestBox.getPrettyCpuVersion(),);
633 if oTestBox.fCpuNestedPaging: sHtml += u', np';
634 elif oTestBox.fCpuHwVirt: sHtml += u', hw';
635 else: sHtml += u', raw';
636 if oTestBox.fCpu64BitGuest: sHtml += u', 64';
637 sHtml += u'</td>';
638 return sHtml;
639
640 def _formatSeriedNameForGraph(self, oSubject):
641 return oSubject.sName;
642
643 def generateReportBody(self):
644 self._sTitle = 'Test Box Failures';
645 oSet = self._oModel.getTestBoxFailures();
646 (aidSortedRaw, iSortColumn) = self._getSortedIds(oSet);
647
648 sHtml = self._generateTableForSet(oSet, aidSortedRaw, iSortColumn);
649 sHtml += self._generateTransitionList(oSet);
650 sHtml += self._generateGraph(oSet, 'testbox-graph', aidSortedRaw);
651 return sHtml;
652
653
654class WuiReportSummary(WuiReportBase):
655 """
656 Summary report.
657 """
658
659 def generateReportBody(self):
660 self._sTitle = 'Summary';
661 sHtml = '<p>This will display several reports and listings useful to get an overview of %s (id=%s).</p>' \
662 % (self._oModel.sSubject, self._oModel.aidSubjects,);
663
664 aoReports = [];
665
666 aoReports.append(WuiReportSuccessRate( self._oModel, self._dParams, fSubReport = True,
667 aiSortColumns = self._aiSortColumns,
668 fnDPrint = self._fnDPrint, oDisp = self._oDisp));
669 aoReports.append(WuiReportTestCaseFailures(self._oModel, self._dParams, fSubReport = True,
670 aiSortColumns = self._aiSortColumns,
671 fnDPrint = self._fnDPrint, oDisp = self._oDisp));
672 if self._oModel.sSubject == ReportModelBase.ksSubTestCase:
673 aoReports.append(WuiReportTestCaseArgsFailures(self._oModel, self._dParams, fSubReport = True,
674 aiSortColumns = self._aiSortColumns,
675 fnDPrint = self._fnDPrint, oDisp = self._oDisp));
676 aoReports.append(WuiReportTestBoxFailures( self._oModel, self._dParams, fSubReport = True,
677 aiSortColumns = self._aiSortColumns,
678 fnDPrint = self._fnDPrint, oDisp = self._oDisp));
679 aoReports.append(WuiReportFailureReasons( self._oModel, self._dParams, fSubReport = True,
680 aiSortColumns = self._aiSortColumns,
681 fnDPrint = self._fnDPrint, oDisp = self._oDisp));
682
683 for oReport in aoReports:
684 (sTitle, sContent) = oReport.show();
685 sHtml += '<br>'; # drop this layout hack
686 sHtml += '<div>';
687 sHtml += '<h3>%s</h3>\n' % (webutils.escapeElem(sTitle),);
688 sHtml += sContent;
689 sHtml += '</div>';
690
691 return sHtml;
692
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette