|
Lines 31-37
Link Here
|
| 31 |
# <http://www.gnu.org/licenses/>. |
31 |
# <http://www.gnu.org/licenses/>. |
| 32 |
|
32 |
|
| 33 |
|
33 |
|
| 34 |
from BaseTest import BaseCase, TestError, ProcessFailedError |
34 |
from BaseTest import BaseCase, TestError |
| 35 |
|
35 |
|
| 36 |
|
36 |
|
| 37 |
class ObjectNotFoundError(TestError): |
37 |
class ObjectNotFoundError(TestError): |
|
Lines 47-52
class ObjectNotFoundError(TestError):
Link Here
|
| 47 |
% (test.name, helper, test.modname) |
47 |
% (test.name, helper, test.modname) |
| 48 |
TestError.__init__(self, error, test) |
48 |
TestError.__init__(self, error, test) |
| 49 |
|
49 |
|
|
|
50 |
|
| 50 |
class LeftoverObjectFound(TestError): |
51 |
class LeftoverObjectFound(TestError): |
| 51 |
'''Assertion error in the Univention Admin test suite. |
52 |
'''Assertion error in the Univention Admin test suite. |
| 52 |
|
53 |
|
|
Lines 60-65
class LeftoverObjectFound(TestError):
Link Here
|
| 60 |
% (test.name, helper, test.modname) |
61 |
% (test.name, helper, test.modname) |
| 61 |
TestError.__init__(self, error, test) |
62 |
TestError.__init__(self, error, test) |
| 62 |
|
63 |
|
|
|
64 |
|
| 63 |
class ObjectNotIdentifiedError(TestError): |
65 |
class ObjectNotIdentifiedError(TestError): |
| 64 |
'''Assertion error in the Univention Admin test suite. |
66 |
'''Assertion error in the Univention Admin test suite. |
| 65 |
|
67 |
|
|
Lines 70-75
class ObjectNotIdentifiedError(TestError):
Link Here
|
| 70 |
% (test.name, dn, test.modname) |
72 |
% (test.name, dn, test.modname) |
| 71 |
TestError.__init__(self, error, test) |
73 |
TestError.__init__(self, error, test) |
| 72 |
|
74 |
|
|
|
75 |
|
| 73 |
class PropertyInvalidError(TestError): |
76 |
class PropertyInvalidError(TestError): |
| 74 |
'''Assertion error in the Univention Admin test suite. |
77 |
'''Assertion error in the Univention Admin test suite. |
| 75 |
|
78 |
|
|
Lines 81-90
class PropertyInvalidError(TestError):
Link Here
|
| 81 |
e1 = 'Incorrect property %s (%s)' % (property, description) |
84 |
e1 = 'Incorrect property %s (%s)' % (property, description) |
| 82 |
e2 = 'of object %s at DN %s (module %s).' \ |
85 |
e2 = 'of object %s at DN %s (module %s).' \ |
| 83 |
% (test.name, dn, test.modname) |
86 |
% (test.name, dn, test.modname) |
| 84 |
e3= 'Expected: "%s" Actual: "%s"' % (expected, actual) |
87 |
e3 = 'Expected: "%s" Actual: "%s"' % (expected, actual) |
| 85 |
error = '%s %s %s' % (e1, e2, e3) |
88 |
error = '%s %s %s' % (e1, e2, e3) |
| 86 |
TestError.__init__(self, error, test) |
89 |
TestError.__init__(self, error, test) |
| 87 |
|
90 |
|
|
|
91 |
|
| 88 |
class ObjectClassNotPresentError(TestError): |
92 |
class ObjectClassNotPresentError(TestError): |
| 89 |
'''Assertion error in the Univention Admin test suite. |
93 |
'''Assertion error in the Univention Admin test suite. |
| 90 |
|
94 |
|
|
Lines 97-103
class ObjectClassNotPresentError(TestError):
Link Here
|
| 97 |
% (option, description) |
101 |
% (option, description) |
| 98 |
e2 = 'of object %s at DN %s (module %s).' \ |
102 |
e2 = 'of object %s at DN %s (module %s).' \ |
| 99 |
% (test.name, dn, test.modname) |
103 |
% (test.name, dn, test.modname) |
| 100 |
e3= 'Expected: "%s" Actual: "%s"' % (expected, actual) |
104 |
e3 = 'Expected: "%s" Actual: "%s"' % (expected, actual) |
| 101 |
error = '%s %s %s' % (e1, e2, e3) |
105 |
error = '%s %s %s' % (e1, e2, e3) |
| 102 |
TestError.__init__(self, error, test) |
106 |
TestError.__init__(self, error, test) |
| 103 |
|
107 |
|
|
Lines 157-171
class GenericTestCase(BaseCase):
Link Here
|
| 157 |
# NOTE: this filter might be a little fragile... |
161 |
# NOTE: this filter might be a little fragile... |
| 158 |
cmd.filter('%s="%s"' % (self.identifier, self.name)) |
162 |
cmd.filter('%s="%s"' % (self.identifier, self.name)) |
| 159 |
|
163 |
|
| 160 |
def __setupCommandUpdate(self, cmd, name, properties, options = None): |
164 |
def __setupCommandUpdate(self, cmd, name, properties, options=None): |
| 161 |
descriptions = self.module.property_descriptions |
165 |
descriptions = self.module.property_descriptions |
| 162 |
special = set(('position',)) |
166 |
special = set(('position',)) |
| 163 |
single = (p for p in properties |
167 |
single = ( |
| 164 |
if not p in special |
168 |
p for p in properties |
| 165 |
if not descriptions[p].multivalue) |
169 |
if p not in special |
| 166 |
multi = (p for p in properties |
170 |
if not descriptions[p].multivalue) |
| 167 |
if not p in special |
171 |
multi = ( |
| 168 |
if descriptions[p].multivalue) |
172 |
p for p in properties |
|
|
173 |
if p not in special |
| 174 |
if descriptions[p].multivalue) |
| 169 |
# set identifying property |
175 |
# set identifying property |
| 170 |
if name is not None: |
176 |
if name is not None: |
| 171 |
cmd.set(self.identifier, name) |
177 |
cmd.set(self.identifier, name) |
|
Lines 276-301
class GenericTestCase(BaseCase):
Link Here
|
| 276 |
self.dn = dn |
282 |
self.dn = dn |
| 277 |
self.__leftover_dns.add(dn) |
283 |
self.__leftover_dns.add(dn) |
| 278 |
|
284 |
|
| 279 |
def testObjectExists(self, dn = None): |
285 |
def testObjectExists(self, dn=None): |
| 280 |
'''Check that the object exists. |
286 |
'''Check that the object exists. |
| 281 |
|
287 |
|
| 282 |
"DN", if given, indicates the DN of the object to test. |
288 |
"DN", if given, indicates the DN of the object to test. |
| 283 |
|
289 |
|
| 284 |
Raise "ObjectNotFoundError" if it does not exists. |
290 |
Raise "ObjectNotFoundError" if it does not exists. |
| 285 |
''' |
291 |
''' |
| 286 |
dn, attr = self.search(dn = dn) |
292 |
dn, attr = self.search(dn=dn) |
| 287 |
if attr is None: |
293 |
if attr is None: |
| 288 |
raise ObjectNotFoundError(self, dn) |
294 |
raise ObjectNotFoundError(self, dn) |
| 289 |
self.useDN(dn) |
295 |
self.useDN(dn) |
| 290 |
|
296 |
|
| 291 |
def testObjectExistsNot(self, dn = None): |
297 |
def testObjectExistsNot(self, dn=None): |
| 292 |
'''Check that the object does not exists. |
298 |
'''Check that the object does not exists. |
| 293 |
|
299 |
|
| 294 |
"DN", if given, indicates the DN of the object to test. |
300 |
"DN", if given, indicates the DN of the object to test. |
| 295 |
|
301 |
|
| 296 |
Raise "LeftoverObjectFound" if it does exists. |
302 |
Raise "LeftoverObjectFound" if it does exists. |
| 297 |
''' |
303 |
''' |
| 298 |
dn, attr = self.search(dn = dn) |
304 |
dn, attr = self.search(dn=dn) |
| 299 |
if bool(attr): |
305 |
if bool(attr): |
| 300 |
self.useDN(dn) |
306 |
self.useDN(dn) |
| 301 |
raise LeftoverObjectFound(self, dn) |
307 |
raise LeftoverObjectFound(self, dn) |
|
Lines 306-312
class GenericTestCase(BaseCase):
Link Here
|
| 306 |
Raise "ObjectNotIdentifiedError" if the module cannot identify |
312 |
Raise "ObjectNotIdentifiedError" if the module cannot identify |
| 307 |
the current object. |
313 |
the current object. |
| 308 |
''' |
314 |
''' |
| 309 |
_, attr = self.search(dn = self.dn) |
315 |
_, attr = self.search(dn=self.dn) |
| 310 |
if not self.module.identify(self.dn, attr): |
316 |
if not self.module.identify(self.dn, attr): |
| 311 |
raise ObjectNotIdentifiedError(self, self.dn) |
317 |
raise ObjectNotIdentifiedError(self, self.dn) |
| 312 |
|
318 |
|
|
Lines 317-323
class GenericTestCase(BaseCase):
Link Here
|
| 317 |
|
323 |
|
| 318 |
Raise "ObjectClassNotPresentError" if an option is not present. |
324 |
Raise "ObjectClassNotPresentError" if an option is not present. |
| 319 |
''' |
325 |
''' |
| 320 |
attr = self.ldap.get(dn = self.dn, attr = ['objectClass']) |
326 |
attr = self.ldap.get(dn=self.dn, attr=['objectClass']) |
| 321 |
for o in options: |
327 |
for o in options: |
| 322 |
classes = self.module.options[o].objectClasses |
328 |
classes = self.module.options[o].objectClasses |
| 323 |
if not classes: |
329 |
if not classes: |
|
Lines 336-349
class GenericTestCase(BaseCase):
Link Here
|
| 336 |
values. |
342 |
values. |
| 337 |
''' |
343 |
''' |
| 338 |
special = set(('position',)) |
344 |
special = set(('position',)) |
| 339 |
props = (p for p in properties |
345 |
props = ( |
| 340 |
if not p in special |
346 |
p for p in properties |
| 341 |
if not p in self.uncheckedProperties) |
347 |
if p not in special |
| 342 |
obj = self.open(dn = self.dn) |
348 |
if p not in self.uncheckedProperties) |
|
|
349 |
obj = self.open(dn=self.dn) |
| 343 |
for prop in props: |
350 |
for prop in props: |
| 344 |
self._testProperty(prop, obj, properties) |
351 |
self._testProperty(prop, obj, properties) |
| 345 |
|
352 |
|
| 346 |
def create(self, properties, options = None, name = None): |
353 |
def create(self, properties, options=None, name=None): |
| 347 |
'''Create an object of the current module. |
354 |
'''Create an object of the current module. |
| 348 |
|
355 |
|
| 349 |
"Properties" is a mapping from property names to values. |
356 |
"Properties" is a mapping from property names to values. |
|
Lines 357-365
class GenericTestCase(BaseCase):
Link Here
|
| 357 |
self.__setupCommandUpdate(cmd, name, properties, options) |
364 |
self.__setupCommandUpdate(cmd, name, properties, options) |
| 358 |
return cmd.run() |
365 |
return cmd.run() |
| 359 |
|
366 |
|
| 360 |
def modify(self, properties, dn = None, name = None, newName = None): |
367 |
def modify(self, properties, dn=None, name=None, newName=None): |
| 361 |
'''Modify an object of the current module. |
368 |
'''Modify an object of the current module. |
| 362 |
|
369 |
|
| 363 |
"Properties" is a mapping from property names to values; |
370 |
"Properties" is a mapping from property names to values; |
| 364 |
only values that should be changed need to be included. |
371 |
only values that should be changed need to be included. |
| 365 |
"DN", if given, is the DN of the object to modify. |
372 |
"DN", if given, is the DN of the object to modify. |
|
Lines 376-384
class GenericTestCase(BaseCase):
Link Here
|
| 376 |
self.__setupCommandUpdate(cmd, newName, properties) |
383 |
self.__setupCommandUpdate(cmd, newName, properties) |
| 377 |
return cmd.run() |
384 |
return cmd.run() |
| 378 |
|
385 |
|
| 379 |
def remove(self, name = None, dn = None, recursive = False): |
386 |
def remove(self, name=None, dn=None, recursive=False): |
| 380 |
'''Remove an object of the current module. |
387 |
'''Remove an object of the current module. |
| 381 |
|
388 |
|
| 382 |
"Name" is the name of the object to remove (defaults to the current objects name). |
389 |
"Name" is the name of the object to remove (defaults to the current objects name). |
| 383 |
"DN", if given, is the DN of the object to remove. |
390 |
"DN", if given, is the DN of the object to remove. |
| 384 |
"Recursive" should be True to remove the object recursively. |
391 |
"Recursive" should be True to remove the object recursively. |
|
Lines 391-397
class GenericTestCase(BaseCase):
Link Here
|
| 391 |
if recursive: |
398 |
if recursive: |
| 392 |
cmd.recursive() |
399 |
cmd.recursive() |
| 393 |
return cmd.run() |
400 |
return cmd.run() |
| 394 |
|
401 |
|
| 395 |
def hookAfterCreated(self, dn): |
402 |
def hookAfterCreated(self, dn): |
| 396 |
'''Perform additional actions after creating an object. |
403 |
'''Perform additional actions after creating an object. |
| 397 |
|
404 |
|
|
Lines 418-424
class GenericTestCase(BaseCase):
Link Here
|
| 418 |
|
425 |
|
| 419 |
def setUp(self): |
426 |
def setUp(self): |
| 420 |
'''Hook method for setting up the test fixture before exercising it. |
427 |
'''Hook method for setting up the test fixture before exercising it. |
| 421 |
|
428 |
|
| 422 |
Override this method to set up values for these properties: |
429 |
Override this method to set up values for these properties: |
| 423 |
"name": The name of the object that will be tested. |
430 |
"name": The name of the object that will be tested. |
| 424 |
You most definitely want to set this one. |
431 |
You most definitely want to set this one. |
|
Lines 460-466
class GenericTestCase(BaseCase):
Link Here
|
| 460 |
self.createOptions = set() |
467 |
self.createOptions = set() |
| 461 |
self.dn = None |
468 |
self.dn = None |
| 462 |
self.superordinate() |
469 |
self.superordinate() |
| 463 |
self.arg() |
|
|
| 464 |
self.__leftover_dns = set() |
470 |
self.__leftover_dns = set() |
| 465 |
|
471 |
|
| 466 |
def runTest(self): |
472 |
def runTest(self): |
|
Lines 484-491
class GenericTestCase(BaseCase):
Link Here
|
| 484 |
is identified by the module and has the correct options and |
490 |
is identified by the module and has the correct options and |
| 485 |
property values set. |
491 |
property values set. |
| 486 |
''' |
492 |
''' |
| 487 |
proc = self.create(self.createProperties, |
493 |
proc = self.create(self.createProperties, self.createOptions) |
| 488 |
self.createOptions) |
|
|
| 489 |
self._checkProcess(proc, 'create') |
494 |
self._checkProcess(proc, 'create') |
| 490 |
self.testObjectExists(self.dn) |
495 |
self.testObjectExists(self.dn) |
| 491 |
self.testObjectIdentify() |
496 |
self.testObjectIdentify() |
|
Lines 500-506
class GenericTestCase(BaseCase):
Link Here
|
| 500 |
exists, is identified by the module and has the correct property |
505 |
exists, is identified by the module and has the correct property |
| 501 |
values set. |
506 |
values set. |
| 502 |
''' |
507 |
''' |
| 503 |
proc = self.modify(self.modifyProperties, dn = self.dn) |
508 |
proc = self.modify(self.modifyProperties, dn=self.dn) |
| 504 |
self._checkProcess(proc, 'modify') |
509 |
self._checkProcess(proc, 'modify') |
| 505 |
if self.newName is not None: |
510 |
if self.newName is not None: |
| 506 |
self.dn = None |
511 |
self.dn = None |
|
Lines 516-522
class GenericTestCase(BaseCase):
Link Here
|
| 516 |
This test removes the current object and then checks that it |
521 |
This test removes the current object and then checks that it |
| 517 |
does not exist any more. |
522 |
does not exist any more. |
| 518 |
''' |
523 |
''' |
| 519 |
proc = self.remove(dn = self.dn) |
524 |
proc = self.remove(dn=self.dn) |
| 520 |
self._checkProcess(proc, 'remove') |
525 |
self._checkProcess(proc, 'remove') |
| 521 |
self.testObjectExistsNot(self.dn) |
526 |
self.testObjectExistsNot(self.dn) |
| 522 |
self.hookAfterRemoved(self.dn) |
527 |
self.hookAfterRemoved(self.dn) |
|
Lines 529-535
class GenericTestCase(BaseCase):
Link Here
|
| 529 |
When overriding this method, make sure you call to your superclass first! |
534 |
When overriding this method, make sure you call to your superclass first! |
| 530 |
''' |
535 |
''' |
| 531 |
for dn in self.__leftover_dns: |
536 |
for dn in self.__leftover_dns: |
| 532 |
self.remove(dn = dn, recursive = True) |
537 |
self.remove(dn=dn, recursive=True) |
| 533 |
|
538 |
|
| 534 |
def shortDescription(self): |
539 |
def shortDescription(self): |
| 535 |
return 'testing module %s' % self.modname |
540 |
return 'testing module %s' % self.modname |