VirtualBox

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

Last change on this file since 61271 was 61271, checked in by vboxsync, 9 years ago

Bugfixes. Data structure unnecessary complicated now because I didn't want to play around with temp sql tables... anyways, it seems to work better.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.5 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: wuireport.py 61271 2016-05-29 02:45:57Z vboxsync $
3
4"""
5Test Manager WUI - Reports.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2015 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: 61271 $"
30
31
32# Validation Kit imports.
33from common import webutils;
34from testmanager.webui.wuicontentbase import WuiContentBase, WuiSvnLinkWithTooltip;
35from testmanager.webui.wuihlpgraph import WuiHlpGraphDataTable, WuiHlpBarGraph;
36from testmanager.core.report import ReportModelBase;
37
38
39class WuiReportBase(WuiContentBase):
40 """
41 Base class for the reports.
42 """
43
44 def __init__(self, oModel, dParams, fSubReport = False, fnDPrint = None, oDisp = None):
45 WuiContentBase.__init__(self, fnDPrint = fnDPrint, oDisp = oDisp);
46 self._oModel = oModel;
47 self._dParams = dParams;
48 self._fSubReport = fSubReport;
49 self._sTitle = None;
50
51 def generateNavigator(self, sWhere):
52 """
53 Generates the navigator (manipulate _dParams).
54 Returns HTML.
55 """
56 assert sWhere == 'top' or sWhere == 'bottom';
57
58 return '';
59
60 def generateReportBody(self):
61 """
62 This is overridden by the child class to generate the report.
63 Returns HTML.
64 """
65 return '<h3>Must override generateReportBody!</h3>';
66
67 def show(self):
68 """
69 Generate the report.
70 Returns (sTitle, HTML).
71 """
72
73 sTitle = self._sTitle if self._sTitle is not None else type(self).__name__;
74 sReport = self.generateReportBody();
75 if not self._fSubReport:
76 sReport = self.generateNavigator('top') + sReport + self.generateNavigator('bottom');
77 sTitle = self._oModel.sSubject + ' - ' + sTitle; ## @todo add subject to title in a proper way!
78
79 return (sTitle, sReport);
80
81
82class WuiReportSuccessRate(WuiReportBase):
83 """
84 Generates a report displaying the success rate over time.
85 """
86
87 def generateReportBody(self):
88 self._sTitle = 'Success rate';
89
90 adPeriods = self._oModel.getSuccessRates();
91
92 sReport = '';
93
94 oTable = WuiHlpGraphDataTable('Period', [ 'Succeeded', 'Skipped', 'Failed' ]);
95
96 #for i in range(len(adPeriods) - 1, -1, -1):
97 for i, dStatuses in enumerate(adPeriods):
98 cSuccess = dStatuses[ReportModelBase.ksTestStatus_Success] + dStatuses[ReportModelBase.ksTestStatus_Skipped];
99 cTotal = cSuccess + dStatuses[ReportModelBase.ksTestStatus_Failure];
100 sPeriod = self._oModel.getPeriodDesc(i);
101 if cTotal > 0:
102 iPctSuccess = dStatuses[ReportModelBase.ksTestStatus_Success] * 100 / cTotal;
103 iPctSkipped = dStatuses[ReportModelBase.ksTestStatus_Skipped] * 100 / cTotal;
104 iPctFailure = dStatuses[ReportModelBase.ksTestStatus_Failure] * 100 / cTotal;
105 oTable.addRow(sPeriod, [ iPctSuccess, iPctSkipped, iPctFailure ],
106 [ '%s%% (%d)' % (iPctSuccess, dStatuses[ReportModelBase.ksTestStatus_Success]),
107 '%s%% (%d)' % (iPctSkipped, dStatuses[ReportModelBase.ksTestStatus_Skipped]),
108 '%s%% (%d)' % (iPctFailure, dStatuses[ReportModelBase.ksTestStatus_Failure]), ]);
109 else:
110 oTable.addRow(sPeriod, [ 0, 0, 0 ], [ '0%', '0%', '0%' ]);
111
112 cTotalNow = adPeriods[0][ReportModelBase.ksTestStatus_Success];
113 cTotalNow += adPeriods[0][ReportModelBase.ksTestStatus_Skipped];
114 cSuccessNow = cTotalNow;
115 cTotalNow += adPeriods[0][ReportModelBase.ksTestStatus_Failure];
116 sReport += '<p>Current success rate: ';
117 if cTotalNow > 0:
118 sReport += '%s%% (thereof %s%% skipped)</p>\n' \
119 % ( cSuccessNow * 100 / cTotalNow, adPeriods[0][ReportModelBase.ksTestStatus_Skipped] * 100 / cTotalNow);
120 else:
121 sReport += 'N/A</p>\n'
122
123 oGraph = WuiHlpBarGraph('success-rate', oTable, self._oDisp);
124 oGraph.setRangeMax(100);
125 sReport += oGraph.renderGraph();
126
127 return sReport;
128
129
130class WuiReportFailuresBase(WuiReportBase):
131 """
132 Common parent of WuiReportFailureReasons and WuiReportTestCaseFailures.
133 """
134
135 def _formatEdgeOccurenceSubject(self, oTransient):
136 """
137 Worker for _formatEdgeOccurence that child classes overrides to format
138 their type of subject data in the best possible way.
139 """
140 _ = oTransient;
141 assert False;
142 return '';
143
144 def _formatEdgeOccurence(self, oTransient):
145 """
146 Helper for formatting the transients.
147 oTransient is of type ReportFailureReasonTransient or ReportTestCaseFailureTransient.
148 """
149 sHtml = u'<li>';
150 if oTransient.fEnter: sHtml += 'Since ';
151 else: sHtml += 'Until ';
152 sHtml += WuiSvnLinkWithTooltip(oTransient.iRevision, oTransient.sRepository, fBracketed = 'False').toHtml();
153 sHtml += u', %s: ' % (self.formatTsShort(oTransient.tsDone),);
154 sHtml += self._formatEdgeOccurenceSubject(oTransient);
155 sHtml += u'</li>\n';
156 return sHtml;
157
158 def _generateTransitionList(self, oSet):
159 """
160 Generates the enter and leave lists.
161 """
162 sHtml = u'<h4>Movements:</h4>\n' \
163 u'<ul>\n';
164 if len(oSet.aoEnterInfo) == 0 and len(oSet.aoLeaveInfo) == 0:
165 sHtml += u'<li>No changes</li>\n';
166 else:
167 for oTransient in oSet.aoEnterInfo:
168 sHtml += self._formatEdgeOccurence(oTransient);
169 for oTransient in oSet.aoLeaveInfo:
170 sHtml += self._formatEdgeOccurence(oTransient);
171 sHtml += u'</ul>\n';
172
173 return sHtml;
174
175
176
177class WuiReportFailureReasons(WuiReportFailuresBase):
178 """
179 Generates a report displaying the failure reasons over time.
180 """
181
182 def _formatEdgeOccurenceSubject(self, oTransient):
183 return u'%s / %s' % ( webutils.escapeElem(oTransient.oReason.oCategory.sShort),
184 webutils.escapeElem(oTransient.oReason.sShort),);
185
186
187 def generateReportBody(self):
188 self._sTitle = 'Failure reasons';
189
190 #
191 # Get the data and generate transition list.
192 #
193 oSet = self._oModel.getFailureReasons();
194 sHtml = self._generateTransitionList(oSet);
195
196 #
197 # Check if most of the stuff is without any assign reason, if so, skip
198 # that part of the graph so it doesn't offset the interesting bits.
199 #
200 fIncludeWithoutReason = True;
201 for oPeriod in reversed(oSet.aoPeriods):
202 if oPeriod.cWithoutReason > oSet.cMaxHits * 4:
203 fIncludeWithoutReason = False;
204 sHtml += '<p>Warning: Many failures without assigned reason!</p>\n';
205 break;
206
207 #
208 # Generate the graph.
209 #
210 aidSorted = sorted(oSet.dSubjects, key = lambda idReason: oSet.dcHitsPerId[idReason], reverse = True);
211
212 asNames = [];
213 for idReason in aidSorted:
214 oReason = oSet.dSubjects[idReason];
215 asNames.append('%s / %s' % (oReason.oCategory.sShort, oReason.sShort,) )
216 if fIncludeWithoutReason:
217 asNames.append('No reason');
218
219 oTable = WuiHlpGraphDataTable('Period', asNames);
220
221 cMax = oSet.cMaxHits;
222 for _, oPeriod in enumerate(reversed(oSet.aoPeriods)):
223 aiValues = [];
224
225 for idReason in aidSorted:
226 oRow = oPeriod.dRowsById.get(idReason, None);
227 iValue = oRow.cHits if oRow is not None else 0;
228 aiValues.append(iValue);
229
230 if fIncludeWithoutReason:
231 aiValues.append(oPeriod.cWithoutReason);
232 if oPeriod.cWithoutReason > cMax:
233 cMax = oPeriod.cWithoutReason;
234
235 oTable.addRow(oPeriod.sDesc, aiValues);
236
237 oGraph = WuiHlpBarGraph('failure-reason', oTable, self._oDisp);
238 oGraph.setRangeMax(max(cMax + 1, 3));
239 sHtml += oGraph.renderGraph();
240
241 #
242 # Table form necessary?
243 #
244 #sHtml += u'<p>TODO: Show graph content in table form.</p>';
245
246 return sHtml;
247
248
249class WuiReportTestCaseFailures(WuiReportFailuresBase):
250 """
251 Generates a report displaying the failure reasons over time.
252 """
253
254 def _formatEdgeOccurenceSubject(self, oTransient):
255 return u'%s (#%u)' % ( webutils.escapeElem(oTransient.oTestCase.sName), oTransient.oTestCase.idTestCase,);
256
257
258 def generateReportBody(self):
259 self._sTitle = 'Test Case Failures';
260
261 #
262 # Get the data and generate transition list.
263 #
264 oSet = self._oModel.getTestCaseFailures();
265 sHtml = self._generateTransitionList(oSet);
266
267 #
268 # Generate the graph.
269 #
270 aidSorted = sorted(oSet.dSubjects,
271 key = lambda idKey: oSet.dcHitsPerId[idKey] * 10000 / oSet.dcTotalPerId[idKey],
272 reverse = True);
273
274 asNames = [];
275 for idKey in aidSorted:
276 oSubject = oSet.dSubjects[idKey];
277 asNames.append(oSubject.sName);
278
279 oTable = WuiHlpGraphDataTable('Period', asNames);
280
281 uPctMax = 10;
282 for _, oPeriod in enumerate(reversed(oSet.aoPeriods)):
283 aiValues = [];
284 asValues = [];
285
286 for idKey in aidSorted:
287 oRow = oPeriod.dRowsById.get(idKey, None);
288 if oRow is not None:
289 uPct = oRow.cHits * 100 / oRow.cTotal;
290 uPctMax = max(uPctMax, uPct);
291 aiValues.append(uPct);
292 asValues.append('%u%% (%u/%u)' % (uPct, oRow.cHits, oRow.cTotal));
293 else:
294 aiValues.append(0);
295 asValues.append('0');
296
297 oTable.addRow(oPeriod.sDesc, aiValues, asValues);
298
299 if True: # pylint: disable=W0125
300 aiValues = [];
301 asValues = [];
302 for idKey in aidSorted:
303 uPct = oSet.dcHitsPerId[idKey] * 100 / oSet.dcTotalPerId[idKey];
304 uPctMax = max(uPctMax, uPct);
305 aiValues.append(uPct);
306 asValues.append('%u%% (%u/%u)' % (uPct, oSet.dcHitsPerId[idKey], oSet.dcTotalPerId[idKey]));
307 oTable.addRow('Totals', aiValues, asValues);
308
309 oGraph = WuiHlpBarGraph('testcase-failures', oTable, self._oDisp);
310 oGraph.setRangeMax(uPct + 2);
311 sHtml += oGraph.renderGraph();
312
313 return sHtml;
314
315
316class WuiReportSummary(WuiReportBase):
317 """
318 Summary report.
319 """
320
321 def generateReportBody(self):
322 self._sTitle = 'Summary';
323 sHtml = '<p>This will display several reports and listings useful to get an overview of %s (id=%s).</p>' \
324 % (self._oModel.sSubject, self._oModel.aidSubjects,);
325
326 oSuccessRate = WuiReportSuccessRate( self._oModel, self._dParams, fSubReport = True,
327 fnDPrint = self._fnDPrint, oDisp = self._oDisp);
328 oTestCaseFailures = WuiReportTestCaseFailures(self._oModel, self._dParams, fSubReport = True,
329 fnDPrint = self._fnDPrint, oDisp = self._oDisp);
330 oFailureReasons = WuiReportFailureReasons( self._oModel, self._dParams, fSubReport = True,
331 fnDPrint = self._fnDPrint, oDisp = self._oDisp);
332 for oReport in [oSuccessRate, oTestCaseFailures, oFailureReasons, ]:
333 (sTitle, sContent) = oReport.show();
334 sHtml += '<br>'; # drop this layout hack
335 sHtml += '<div>';
336 sHtml += '<h3>%s</h3>\n' % (webutils.escapeElem(sTitle),);
337 sHtml += sContent;
338 sHtml += '</div>';
339
340 return sHtml;
341
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