VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testmanager/core/testgroup.py@ 92892

Last change on this file since 92892 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.5 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: testgroup.py 82968 2020-02-04 10:35:17Z vboxsync $
3
4"""
5Test Manager - Test groups management.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2020 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: 82968 $"
30
31
32# Standard python imports.
33import unittest;
34
35# Validation Kit imports.
36from testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMRowInUse, \
37 TMTooManyRows, TMInvalidData, TMRowNotFound, TMRowAlreadyExists;
38from testmanager.core.testcase import TestCaseData, TestCaseDataEx;
39
40
41class TestGroupMemberData(ModelDataBase):
42 """Representation of a test group member database row."""
43
44 ksParam_idTestGroup = 'TestGroupMember_idTestGroup';
45 ksParam_idTestCase = 'TestGroupMember_idTestCase';
46 ksParam_tsEffective = 'TestGroupMember_tsEffective';
47 ksParam_tsExpire = 'TestGroupMember_tsExpire';
48 ksParam_uidAuthor = 'TestGroupMember_uidAuthor';
49 ksParam_iSchedPriority = 'TestGroupMember_iSchedPriority';
50 ksParam_aidTestCaseArgs = 'TestGroupMember_aidTestCaseArgs';
51
52 kasAllowNullAttributes = ['idTestGroup', 'idTestCase', 'tsEffective', 'tsExpire', 'uidAuthor', 'aidTestCaseArgs' ];
53 kiMin_iSchedPriority = 0;
54 kiMax_iSchedPriority = 31;
55
56 kcDbColumns = 7;
57
58 def __init__(self):
59 ModelDataBase.__init__(self)
60
61 #
62 # Initialize with defaults.
63 # See the database for explanations of each of these fields.
64 #
65 self.idTestGroup = None;
66 self.idTestCase = None;
67 self.tsEffective = None;
68 self.tsExpire = None;
69 self.uidAuthor = None;
70 self.iSchedPriority = 16;
71 self.aidTestCaseArgs = None;
72
73 def initFromDbRow(self, aoRow):
74 """
75 Reinitialize from a SELECT * FROM TestCaseGroupMembers.
76 Return self. Raises exception if no row.
77 """
78 if aoRow is None:
79 raise TMRowNotFound('Test group member not found.')
80
81 self.idTestGroup = aoRow[0];
82 self.idTestCase = aoRow[1];
83 self.tsEffective = aoRow[2];
84 self.tsExpire = aoRow[3];
85 self.uidAuthor = aoRow[4];
86 self.iSchedPriority = aoRow[5];
87 self.aidTestCaseArgs = aoRow[6];
88 return self
89
90
91 def getAttributeParamNullValues(self, sAttr):
92 # Arrays default to [] as NULL currently. That doesn't work for us.
93 if sAttr == 'aidTestCaseArgs':
94 aoNilValues = [None, '-1'];
95 else:
96 aoNilValues = ModelDataBase.getAttributeParamNullValues(self, sAttr);
97 return aoNilValues;
98
99 def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb):
100 if sAttr != 'aidTestCaseArgs':
101 return ModelDataBase._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
102
103 # -1 is a special value, which when present make the whole thing NULL (None).
104 (aidVariations, sError) = self.validateListOfInts(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull,
105 iMin = -1, iMax = 0x7ffffffe);
106 if sError is None:
107 if aidVariations is None:
108 pass;
109 elif -1 in aidVariations:
110 aidVariations = None;
111 elif 0 in aidVariations:
112 sError = 'Invalid test case varation ID #0.';
113 else:
114 aidVariations = sorted(aidVariations);
115 return (aidVariations, sError);
116
117
118
119class TestGroupMemberDataEx(TestGroupMemberData):
120 """Extended representation of a test group member."""
121
122 def __init__(self):
123 """Extend parent class"""
124 TestGroupMemberData.__init__(self)
125 self.oTestCase = None; # TestCaseDataEx.
126
127 def initFromDbRowEx(self, aoRow, oDb, tsNow = None):
128 """
129 Reinitialize from a SELECT * FROM TestGroupMembers, TestCases row.
130 Will query the necessary additional data from oDb using tsNow.
131
132 Returns self. Raises exception if no row or database error.
133 """
134 TestGroupMemberData.initFromDbRow(self, aoRow);
135 self.oTestCase = TestCaseDataEx();
136 self.oTestCase.initFromDbRowEx(aoRow[TestGroupMemberData.kcDbColumns:], oDb, tsNow);
137 return self;
138
139 def initFromParams(self, oDisp, fStrict = True):
140 self.oTestCase = None;
141 return TestGroupMemberData.initFromParams(self, oDisp, fStrict);
142
143 def getDataAttributes(self):
144 asAttributes = TestGroupMemberData.getDataAttributes(self);
145 asAttributes.remove('oTestCase');
146 return asAttributes;
147
148 def _validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor = ModelDataBase.ksValidateFor_Other):
149 dErrors = TestGroupMemberData._validateAndConvertWorker(self, asAllowNullAttributes, oDb, enmValidateFor);
150 if self.ksParam_idTestCase not in dErrors:
151 self.oTestCase = TestCaseDataEx()
152 try:
153 self.oTestCase.initFromDbWithId(oDb, self.idTestCase);
154 except Exception as oXcpt:
155 self.oTestCase = TestCaseDataEx()
156 dErrors[self.ksParam_idTestCase] = str(oXcpt);
157 return dErrors;
158
159
160class TestGroupMemberData2(TestCaseData):
161 """Special representation of a Test Group Member item"""
162
163 def __init__(self):
164 """Extend parent class"""
165 TestCaseData.__init__(self)
166 self.idTestGroup = None
167 self.aidTestCaseArgs = []
168
169 def initFromDbRowEx(self, aoRow):
170 """
171 Reinitialize from this query:
172
173 SELECT TestCases.*,
174 TestGroupMembers.idTestGroup,
175 TestGroupMembers.aidTestCaseArgs
176 FROM TestCases, TestGroupMembers
177 WHERE TestCases.idTestCase = TestGroupMembers.idTestCase
178
179 Represents complete test group member (test case) info.
180 Returns object of type TestGroupMemberData2. Raises exception if no row.
181 """
182 TestCaseData.initFromDbRow(self, aoRow);
183 self.idTestGroup = aoRow[-2]
184 self.aidTestCaseArgs = aoRow[-1]
185 return self;
186
187
188class TestGroupData(ModelDataBase):
189 """
190 Test group data.
191 """
192
193 ksIdAttr = 'idTestGroup';
194
195 ksParam_idTestGroup = 'TestGroup_idTestGroup'
196 ksParam_tsEffective = 'TestGroup_tsEffective'
197 ksParam_tsExpire = 'TestGroup_tsExpire'
198 ksParam_uidAuthor = 'TestGroup_uidAuthor'
199 ksParam_sName = 'TestGroup_sName'
200 ksParam_sDescription = 'TestGroup_sDescription'
201 ksParam_sComment = 'TestGroup_sComment'
202
203 kasAllowNullAttributes = ['idTestGroup', 'tsEffective', 'tsExpire', 'uidAuthor', 'sDescription', 'sComment' ];
204
205 kcDbColumns = 7;
206
207 def __init__(self):
208 ModelDataBase.__init__(self);
209
210 #
211 # Initialize with defaults.
212 # See the database for explanations of each of these fields.
213 #
214 self.idTestGroup = None
215 self.tsEffective = None
216 self.tsExpire = None
217 self.uidAuthor = None
218 self.sName = None
219 self.sDescription = None
220 self.sComment = None
221
222 def initFromDbRow(self, aoRow):
223 """
224 Reinitialize from a SELECT * FROM TestGroups row.
225 Returns object of type TestGroupData. Raises exception if no row.
226 """
227 if aoRow is None:
228 raise TMRowNotFound('Test group not found.')
229
230 self.idTestGroup = aoRow[0]
231 self.tsEffective = aoRow[1]
232 self.tsExpire = aoRow[2]
233 self.uidAuthor = aoRow[3]
234 self.sName = aoRow[4]
235 self.sDescription = aoRow[5]
236 self.sComment = aoRow[6]
237 return self
238
239 def initFromDbWithId(self, oDb, idTestGroup, tsNow = None, sPeriodBack = None):
240 """
241 Initialize the object from the database.
242 """
243 oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
244 'SELECT *\n'
245 'FROM TestGroups\n'
246 'WHERE idTestGroup = %s\n'
247 , ( idTestGroup,), tsNow, sPeriodBack));
248 aoRow = oDb.fetchOne()
249 if aoRow is None:
250 raise TMRowNotFound('idTestGroup=%s not found (tsNow=%s sPeriodBack=%s)' % (idTestGroup, tsNow, sPeriodBack,));
251 return self.initFromDbRow(aoRow);
252
253
254class TestGroupDataEx(TestGroupData):
255 """
256 Extended test group data.
257 """
258
259 ksParam_aoMembers = 'TestGroupDataEx_aoMembers';
260 kasAltArrayNull = [ 'aoMembers', ];
261
262 ## Helper parameter containing the comma separated list with the IDs of
263 # potential members found in the parameters.
264 ksParam_aidTestCases = 'TestGroupDataEx_aidTestCases';
265
266
267 def __init__(self):
268 TestGroupData.__init__(self);
269 self.aoMembers = []; # TestGroupMemberDataEx.
270
271 def _initExtraMembersFromDb(self, oDb, tsNow = None, sPeriodBack = None):
272 """
273 Worker shared by the initFromDb* methods.
274 Returns self. Raises exception if no row or database error.
275 """
276 self.aoMembers = [];
277 _ = sPeriodBack; ## @todo sPeriodBack
278
279 if tsNow is None:
280 oDb.execute('SELECT TestGroupMembers.*, TestCases.*\n'
281 'FROM TestGroupMembers\n'
282 'LEFT OUTER JOIN TestCases ON (\n'
283 ' TestGroupMembers.idTestCase = TestCases.idTestCase\n'
284 ' AND TestCases.tsExpire = \'infinity\'::TIMESTAMP)\n'
285 'WHERE TestGroupMembers.idTestGroup = %s\n'
286 ' AND TestGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
287 'ORDER BY TestCases.sName, TestCases.idTestCase\n'
288 , (self.idTestGroup,));
289 else:
290 oDb.execute('SELECT TestGroupMembers.*, TestCases.*\n'
291 'FROM TestGroupMembers\n'
292 'LEFT OUTER JOIN TestCases ON (\n'
293 ' TestGroupMembers.idTestCase = TestCases.idTestCase\n'
294 ' AND TestCases.tsExpire > %s\n'
295 ' AND TestCases.tsEffective <= %s)\n'
296 'WHERE TestGroupMembers.idTestGroup = %s\n'
297 ' AND TestGroupMembers.tsExpire > %s\n'
298 ' AND TestGroupMembers.tsEffective <= %s\n'
299 'ORDER BY TestCases.sName, TestCases.idTestCase\n'
300 , (tsNow, tsNow, self.idTestGroup, tsNow, tsNow));
301
302 for aoRow in oDb.fetchAll():
303 self.aoMembers.append(TestGroupMemberDataEx().initFromDbRowEx(aoRow, oDb, tsNow));
304 return self;
305
306 def initFromDbRowEx(self, aoRow, oDb, tsNow = None, sPeriodBack = None):
307 """
308 Reinitialize from a SELECT * FROM TestGroups row. Will query the
309 necessary additional data from oDb using tsNow.
310 Returns self. Raises exception if no row or database error.
311 """
312 TestGroupData.initFromDbRow(self, aoRow);
313 return self._initExtraMembersFromDb(oDb, tsNow, sPeriodBack);
314
315 def initFromDbWithId(self, oDb, idTestGroup, tsNow = None, sPeriodBack = None):
316 """
317 Initialize the object from the database.
318 """
319 TestGroupData.initFromDbWithId(self, oDb, idTestGroup, tsNow, sPeriodBack);
320 return self._initExtraMembersFromDb(oDb, tsNow, sPeriodBack);
321
322
323 def getAttributeParamNullValues(self, sAttr):
324 if sAttr != 'aoMembers':
325 return TestGroupData.getAttributeParamNullValues(self, sAttr);
326 return ['', [], None];
327
328 def convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict):
329 if sAttr != 'aoMembers':
330 return TestGroupData.convertParamToAttribute(self, sAttr, sParam, oValue, oDisp, fStrict);
331
332 aoNewValue = [];
333 aidSelected = oDisp.getListOfIntParams(sParam, iMin = 1, iMax = 0x7ffffffe, aiDefaults = [])
334 sIds = oDisp.getStringParam(self.ksParam_aidTestCases, sDefault = '');
335 for idTestCase in sIds.split(','):
336 try: idTestCase = int(idTestCase);
337 except: pass;
338 oDispWrapper = self.DispWrapper(oDisp, '%s[%s][%%s]' % (TestGroupDataEx.ksParam_aoMembers, idTestCase,))
339 oMember = TestGroupMemberDataEx().initFromParams(oDispWrapper, fStrict = False);
340 if idTestCase in aidSelected:
341 aoNewValue.append(oMember);
342 return aoNewValue;
343
344 def _validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb):
345 if sAttr != 'aoMembers':
346 return TestGroupData._validateAndConvertAttribute(self, sAttr, sParam, oValue, aoNilValues, fAllowNull, oDb);
347
348 asErrors = [];
349 aoNewMembers = [];
350 for oOldMember in oValue:
351 oNewMember = TestGroupMemberDataEx().initFromOther(oOldMember);
352 aoNewMembers.append(oNewMember);
353
354 dErrors = oNewMember.validateAndConvert(oDb, ModelDataBase.ksValidateFor_Other);
355 if dErrors:
356 asErrors.append(str(dErrors));
357
358 if not asErrors:
359 for i, _ in enumerate(aoNewMembers):
360 idTestCase = aoNewMembers[i];
361 for j in range(i + 1, len(aoNewMembers)):
362 if aoNewMembers[j].idTestCase == idTestCase:
363 asErrors.append('Duplicate testcase #%d!' % (idTestCase, ));
364 break;
365
366 return (aoNewMembers, None if not asErrors else '<br>\n'.join(asErrors));
367
368
369class TestGroupLogic(ModelLogicBase):
370 """
371 Test case management logic.
372 """
373
374 def __init__(self, oDb):
375 ModelLogicBase.__init__(self, oDb)
376 self.dCache = None;
377
378 #
379 # Standard methods.
380 #
381
382 def fetchForListing(self, iStart, cMaxRows, tsNow, aiSortColumns = None):
383 """
384 Fetches test groups.
385
386 Returns an array (list) of TestGroupDataEx items, empty list if none.
387 Raises exception on error.
388 """
389 _ = aiSortColumns;
390 if tsNow is None:
391 self._oDb.execute('SELECT *\n'
392 'FROM TestGroups\n'
393 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
394 'ORDER BY sName ASC\n'
395 'LIMIT %s OFFSET %s\n'
396 , (cMaxRows, iStart,));
397 else:
398 self._oDb.execute('SELECT *\n'
399 'FROM TestGroups\n'
400 'WHERE tsExpire > %s\n'
401 ' AND tsEffective <= %s\n'
402 'ORDER BY sName ASC\n'
403 'LIMIT %s OFFSET %s\n'
404 , (tsNow, tsNow, cMaxRows, iStart,));
405
406 aoRet = [];
407 for aoRow in self._oDb.fetchAll():
408 aoRet.append(TestGroupDataEx().initFromDbRowEx(aoRow, self._oDb, tsNow));
409 return aoRet;
410
411 def addEntry(self, oData, uidAuthor, fCommit = False):
412 """
413 Adds a testgroup to the database.
414 """
415
416 #
417 # Validate inputs.
418 #
419 assert isinstance(oData, TestGroupDataEx);
420 dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Add);
421 if dErrors:
422 raise TMInvalidData('addEntry invalid input: %s' % (dErrors,));
423 self._assertUniq(oData, None);
424
425 #
426 # Do the job.
427 #
428 self._oDb.execute('INSERT INTO TestGroups (uidAuthor, sName, sDescription, sComment)\n'
429 'VALUES (%s, %s, %s, %s)\n'
430 'RETURNING idTestGroup\n'
431 , ( uidAuthor,
432 oData.sName,
433 oData.sDescription,
434 oData.sComment ));
435 idTestGroup = self._oDb.fetchOne()[0];
436 oData.idTestGroup = idTestGroup;
437
438 for oMember in oData.aoMembers:
439 oMember.idTestGroup = idTestGroup;
440 self._insertTestGroupMember(uidAuthor, oMember)
441
442 self._oDb.maybeCommit(fCommit);
443 return True;
444
445 def editEntry(self, oData, uidAuthor, fCommit = False):
446 """
447 Modifies a test group.
448 """
449
450 #
451 # Validate inputs and read in the old(/current) data.
452 #
453 assert isinstance(oData, TestGroupDataEx);
454 dErrors = oData.validateAndConvert(self._oDb, oData.ksValidateFor_Edit);
455 if dErrors:
456 raise TMInvalidData('editEntry invalid input: %s' % (dErrors,));
457 self._assertUniq(oData, oData.idTestGroup);
458
459 oOldData = TestGroupDataEx().initFromDbWithId(self._oDb, oData.idTestGroup);
460
461 #
462 # Update the data that needs updating.
463 #
464
465 if not oData.isEqualEx(oOldData, [ 'aoMembers', 'tsEffective', 'tsExpire', 'uidAuthor', ]):
466 self._historizeTestGroup(oData.idTestGroup);
467 self._oDb.execute('INSERT INTO TestGroups\n'
468 ' (uidAuthor, idTestGroup, sName, sDescription, sComment)\n'
469 'VALUES (%s, %s, %s, %s, %s)\n'
470 , ( uidAuthor,
471 oData.idTestGroup,
472 oData.sName,
473 oData.sDescription,
474 oData.sComment ));
475
476 # Create a lookup dictionary for old entries.
477 dOld = {};
478 for oOld in oOldData.aoMembers:
479 dOld[oOld.idTestCase] = oOld;
480 assert len(dOld) == len(oOldData.aoMembers);
481
482 # Add new members, updated existing ones.
483 dNew = {};
484 for oNewMember in oData.aoMembers:
485 oNewMember.idTestGroup = oData.idTestGroup;
486 if oNewMember.idTestCase in dNew:
487 raise TMRowAlreadyExists('Duplicate test group member: idTestCase=%d (%s / %s)'
488 % (oNewMember.idTestCase, oNewMember, dNew[oNewMember.idTestCase],));
489 dNew[oNewMember.idTestCase] = oNewMember;
490
491 oOldMember = dOld.get(oNewMember.idTestCase, None);
492 if oOldMember is not None:
493 if oNewMember.isEqualEx(oOldMember, [ 'uidAuthor', 'tsEffective', 'tsExpire' ]):
494 continue; # Skip, nothing changed.
495 self._historizeTestGroupMember(oData.idTestGroup, oNewMember.idTestCase);
496 self._insertTestGroupMember(uidAuthor, oNewMember);
497
498 # Expire members that have been removed.
499 sQuery = self._oDb.formatBindArgs('UPDATE TestGroupMembers\n'
500 'SET tsExpire = CURRENT_TIMESTAMP\n'
501 'WHERE idTestGroup = %s\n'
502 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
503 , ( oData.idTestGroup, ));
504 if dNew:
505 sQuery += ' AND idTestCase NOT IN (%s)' % (', '.join([str(iKey) for iKey in dNew]),);
506 self._oDb.execute(sQuery);
507
508 self._oDb.maybeCommit(fCommit);
509 return True;
510
511 def removeEntry(self, uidAuthor, idTestGroup, fCascade = False, fCommit = False):
512 """
513 Deletes a test group.
514 """
515 _ = uidAuthor; ## @todo record uidAuthor.
516
517 #
518 # Cascade.
519 #
520 if fCascade is not True:
521 self._oDb.execute('SELECT SchedGroups.idSchedGroup, SchedGroups.sName\n'
522 'FROM SchedGroupMembers, SchedGroups\n'
523 'WHERE SchedGroupMembers.idTestGroup = %s\n'
524 ' AND SchedGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
525 ' AND SchedGroups.idSchedGroup = SchedGroupMembers.idSchedGroup\n'
526 ' AND SchedGroups.tsExpire = \'infinity\'::TIMESTAMP\n'
527 , ( idTestGroup, ));
528 aoGroups = self._oDb.fetchAll();
529 if aoGroups:
530 asGroups = ['%s (#%d)' % (sName, idSchedGroup) for idSchedGroup, sName in aoGroups];
531 raise TMRowInUse('Test group #%d is member of one or more scheduling groups: %s'
532 % (idTestGroup, ', '.join(asGroups),));
533 else:
534 self._oDb.execute('UPDATE SchedGroupMembers\n'
535 'SET tsExpire = CURRENT_TIMESTAMP\n'
536 'WHERE idTestGroup = %s\n'
537 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
538 , ( idTestGroup, ));
539
540 #
541 # Remove the group.
542 #
543 self._oDb.execute('UPDATE TestGroupMembers\n'
544 'SET tsExpire = CURRENT_TIMESTAMP\n'
545 'WHERE idTestGroup = %s\n'
546 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
547 , (idTestGroup,))
548 self._oDb.execute('UPDATE TestGroups\n'
549 'SET tsExpire = CURRENT_TIMESTAMP\n'
550 'WHERE idTestGroup = %s\n'
551 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
552 , (idTestGroup,))
553
554 self._oDb.maybeCommit(fCommit)
555 return True;
556
557 def cachedLookup(self, idTestGroup):
558 """
559 Looks up the most recent TestGroupDataEx object for idTestGroup
560 via an object cache.
561
562 Returns a shared TestGroupDataEx object. None if not found.
563 Raises exception on DB error.
564 """
565 if self.dCache is None:
566 self.dCache = self._oDb.getCache('TestGroupDataEx');
567 oEntry = self.dCache.get(idTestGroup, None);
568 if oEntry is None:
569 fNeedTsNow = False;
570 self._oDb.execute('SELECT *\n'
571 'FROM TestGroups\n'
572 'WHERE idTestGroup = %s\n'
573 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
574 , (idTestGroup, ));
575 if self._oDb.getRowCount() == 0:
576 # Maybe it was deleted, try get the last entry.
577 self._oDb.execute('SELECT *\n'
578 'FROM TestGroups\n'
579 'WHERE idTestGroup = %s\n'
580 'ORDER BY tsExpire DESC\n'
581 'LIMIT 1\n'
582 , (idTestGroup, ));
583 fNeedTsNow = True;
584 elif self._oDb.getRowCount() > 1:
585 raise self._oDb.integrityException('%s infinity rows for %s' % (self._oDb.getRowCount(), idTestGroup));
586
587 if self._oDb.getRowCount() == 1:
588 aaoRow = self._oDb.fetchOne();
589 oEntry = TestGroupDataEx();
590 tsNow = oEntry.initFromDbRow(aaoRow).tsEffective if fNeedTsNow else None;
591 oEntry.initFromDbRowEx(aaoRow, self._oDb, tsNow);
592 self.dCache[idTestGroup] = oEntry;
593 return oEntry;
594
595
596 #
597 # Other methods.
598 #
599
600 def fetchOrderedByName(self, tsNow = None):
601 """
602 Return list of objects of type TestGroupData ordered by name.
603 May raise exception on database error.
604 """
605 if tsNow is None:
606 self._oDb.execute('SELECT *\n'
607 'FROM TestGroups\n'
608 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
609 'ORDER BY sName ASC\n');
610 else:
611 self._oDb.execute('SELECT *\n'
612 'FROM TestGroups\n'
613 'WHERE tsExpire > %s\n'
614 ' AND tsEffective <= %s\n'
615 'ORDER BY sName ASC\n'
616 , (tsNow, tsNow,));
617 aoRet = []
618 for _ in range(self._oDb.getRowCount()):
619 aoRet.append(TestGroupData().initFromDbRow(self._oDb.fetchOne()));
620 return aoRet;
621
622 def getMembers(self, idTestGroup):
623 """
624 Fetches all test case records from DB which are
625 belong to current Test Group.
626 Returns list of objects of type TestGroupMemberData2 (!).
627 """
628 self._oDb.execute('SELECT TestCases.*,\n'
629 ' TestGroupMembers.idTestGroup,\n'
630 ' TestGroupMembers.aidTestCaseArgs\n'
631 'FROM TestCases, TestGroupMembers\n'
632 'WHERE TestCases.tsExpire = \'infinity\'::TIMESTAMP\n'
633 ' AND TestGroupMembers.tsExpire = \'infinity\'::TIMESTAMP\n'
634 ' AND TestGroupMembers.idTestCase = TestCases.idTestCase\n'
635 ' AND TestGroupMembers.idTestGroup = %s\n'
636 'ORDER BY TestCases.idTestCase ASC;',
637 (idTestGroup,))
638
639 aaoRows = self._oDb.fetchAll()
640 aoRet = []
641 for aoRow in aaoRows:
642 aoRet.append(TestGroupMemberData2().initFromDbRowEx(aoRow))
643
644 return aoRet
645
646 def getAll(self, tsNow=None):
647 """Return list of objects of type TestGroupData"""
648
649 if tsNow is None:
650 self._oDb.execute('SELECT *\n'
651 'FROM TestGroups\n'
652 'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
653 'ORDER BY idTestGroup ASC;')
654 else:
655 self._oDb.execute('SELECT *\n'
656 'FROM TestGroups\n'
657 'WHERE tsExpire > %s\n'
658 ' AND tsEffective <= %s\n'
659 'ORDER BY idTestGroup ASC;',
660 (tsNow, tsNow))
661
662 aaoRows = self._oDb.fetchAll()
663 aoRet = []
664 for aoRow in aaoRows:
665 aoRet.append(TestGroupData().initFromDbRow(aoRow))
666
667 return aoRet
668
669 def getById(self, idTestGroup, tsNow=None):
670 """Get Test Group data by its ID"""
671
672 if tsNow is None:
673 self._oDb.execute('SELECT *\n'
674 'FROM TestGroups\n'
675 'WHERE tsExpire = \'infinity\'::timestamp\n'
676 ' AND idTestGroup = %s\n'
677 'ORDER BY idTestGroup ASC;'
678 , (idTestGroup,))
679 else:
680 self._oDb.execute('SELECT *\n'
681 'FROM TestGroups\n'
682 'WHERE tsExpire > %s\n'
683 ' AND tsEffective <= %s\n'
684 ' AND idTestGroup = %s\n'
685 'ORDER BY idTestGroup ASC;'
686 , (tsNow, tsNow, idTestGroup))
687
688 aRows = self._oDb.fetchAll()
689 if len(aRows) not in (0, 1):
690 raise TMTooManyRows('Found more than one test groups with the same credentials. Database structure is corrupted.')
691 try:
692 return TestGroupData().initFromDbRow(aRows[0])
693 except IndexError:
694 return None
695
696 #
697 # Helpers.
698 #
699
700 def _assertUniq(self, oData, idTestGroupIgnore):
701 """ Checks that the test group name is unique, raises exception if it isn't. """
702 self._oDb.execute('SELECT idTestGroup\n'
703 'FROM TestGroups\n'
704 'WHERE sName = %s\n'
705 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
706 + ('' if idTestGroupIgnore is None else ' AND idTestGroup <> %d\n' % (idTestGroupIgnore,))
707 , ( oData.sName, ))
708 if self._oDb.getRowCount() > 0:
709 raise TMRowAlreadyExists('A Test group with name "%s" already exist.' % (oData.sName,));
710 return True;
711
712 def _historizeTestGroup(self, idTestGroup):
713 """ Historize Test Group record. """
714 self._oDb.execute('UPDATE TestGroups\n'
715 'SET tsExpire = CURRENT_TIMESTAMP\n'
716 'WHERE idTestGroup = %s\n'
717 ' AND tsExpire = \'infinity\'::TIMESTAMP\n'
718 , ( idTestGroup, ));
719 return True;
720
721 def _historizeTestGroupMember(self, idTestGroup, idTestCase):
722 """ Historize Test Group Member record. """
723 self._oDb.execute('UPDATE TestGroupMembers\n'
724 'SET tsExpire = CURRENT_TIMESTAMP\n'
725 'WHERE idTestGroup = %s\n'
726 ' AND idTestCase = %s\n'
727 ' AND tsExpire = \'infinity\'::timestamp\n'
728 , (idTestGroup, idTestCase,));
729 return True;
730
731 def _insertTestGroupMember(self, uidAuthor, oMember):
732 """ Inserts a test group member. """
733 self._oDb.execute('INSERT INTO TestGroupMembers\n'
734 ' (uidAuthor, idTestGroup, idTestCase, iSchedPriority, aidTestCaseArgs)\n'
735 'VALUES (%s, %s, %s, %s, %s)\n'
736 , ( uidAuthor,
737 oMember.idTestGroup,
738 oMember.idTestCase,
739 oMember.iSchedPriority,
740 oMember.aidTestCaseArgs, ));
741 return True;
742
743
744
745#
746# Unit testing.
747#
748
749# pylint: disable=missing-docstring
750class TestGroupMemberDataTestCase(ModelDataBaseTestCase):
751 def setUp(self):
752 self.aoSamples = [TestGroupMemberData(),];
753
754class TestGroupDataTestCase(ModelDataBaseTestCase):
755 def setUp(self):
756 self.aoSamples = [TestGroupData(),];
757
758if __name__ == '__main__':
759 unittest.main();
760 # not reached.
761
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