1 # -*- test-case-name: twisted.python.test.test_util
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
6 Tests for L{twisted.python.util}.
16 from twisted.trial import unittest
18 from twisted.python import util
19 from twisted.internet import reactor
20 from twisted.internet.interfaces import IReactorProcess
21 from twisted.internet.protocol import ProcessProtocol
22 from twisted.internet.defer import Deferred
23 from twisted.internet.error import ProcessDone
25 from twisted.test.test_process import MockOS
29 class UtilTestCase(unittest.TestCase):
32 l = ["a", 1, "ab", "a", 3, 4, 1, 2, 2, 4, 6]
33 self.assertEqual(util.uniquify(l), ["a", 1, "ab", 3, 4, 2, 6])
36 self.failUnless(util.raises(ZeroDivisionError, divmod, 1, 0))
37 self.failIf(util.raises(ZeroDivisionError, divmod, 0, 1))
40 util.raises(TypeError, divmod, 1, 0)
41 except ZeroDivisionError:
44 raise unittest.FailTest, "util.raises didn't raise when it should have"
46 def testUninterruptably(self):
49 exc = self.exceptions.pop()
51 raise exc(errno.EINTR, "Interrupted system call!")
54 self.exceptions = [None]
56 self.assertEqual(util.untilConcludes(f, 1, 2), 3)
57 self.assertEqual(self.calls, 1)
59 self.exceptions = [None, OSError, IOError]
61 self.assertEqual(util.untilConcludes(f, 2, 3), 5)
62 self.assertEqual(self.calls, 3)
64 def testNameToLabel(self):
66 Test the various kinds of inputs L{nameToLabel} supports.
72 ('fooBar', 'Foo Bar'),
73 ('fooBarBaz', 'Foo Bar Baz'),
75 for inp, out in nameData:
76 got = util.nameToLabel(inp)
79 "nameToLabel(%r) == %r != %r" % (inp, got, out))
82 def test_uidFromNumericString(self):
84 When L{uidFromString} is called with a base-ten string representation
85 of an integer, it returns the integer.
87 self.assertEqual(util.uidFromString("100"), 100)
90 def test_uidFromUsernameString(self):
92 When L{uidFromString} is called with a base-ten string representation
93 of an integer, it returns the integer.
95 pwent = pwd.getpwuid(os.getuid())
96 self.assertEqual(util.uidFromString(pwent.pw_name), pwent.pw_uid)
98 test_uidFromUsernameString.skip = (
99 "Username/UID conversion requires the pwd module.")
102 def test_gidFromNumericString(self):
104 When L{gidFromString} is called with a base-ten string representation
105 of an integer, it returns the integer.
107 self.assertEqual(util.gidFromString("100"), 100)
110 def test_gidFromGroupnameString(self):
112 When L{gidFromString} is called with a base-ten string representation
113 of an integer, it returns the integer.
115 grent = grp.getgrgid(os.getgid())
116 self.assertEqual(util.gidFromString(grent.gr_name), grent.gr_gid)
118 test_gidFromGroupnameString.skip = (
119 "Group Name/GID conversion requires the grp module.")
123 class SwitchUIDTest(unittest.TestCase):
125 Tests for L{util.switchUID}.
128 if getattr(os, "getuid", None) is None:
129 skip = "getuid/setuid not available"
133 self.mockos = MockOS()
134 self.patch(util, "os", self.mockos)
135 self.patch(util, "initgroups", self.initgroups)
136 self.initgroupsCalls = []
139 def initgroups(self, uid, gid):
141 Save L{util.initgroups} calls in C{self.initgroupsCalls}.
143 self.initgroupsCalls.append((uid, gid))
148 L{util.switchUID} calls L{util.initgroups} and then C{os.setuid} with
151 util.switchUID(12000, None)
152 self.assertEqual(self.initgroupsCalls, [(12000, None)])
153 self.assertEqual(self.mockos.actions, [("setuid", 12000)])
158 L{util.switchUID} calls L{util.initgroups} and then C{os.seteuid} with
159 the given uid if the C{euid} parameter is set to C{True}.
161 util.switchUID(12000, None, True)
162 self.assertEqual(self.initgroupsCalls, [(12000, None)])
163 self.assertEqual(self.mockos.seteuidCalls, [12000])
166 def test_currentUID(self):
168 If the current uid is the same as the uid passed to L{util.switchUID},
169 then initgroups does not get called, but a warning is issued.
171 uid = self.mockos.getuid()
172 util.switchUID(uid, None)
173 self.assertEqual(self.initgroupsCalls, [])
174 self.assertEqual(self.mockos.actions, [])
175 warnings = self.flushWarnings([util.switchUID])
176 self.assertEqual(len(warnings), 1)
177 self.assertIn('tried to drop privileges and setuid %i' % uid,
178 warnings[0]['message'])
179 self.assertIn('but uid is already %i' % uid, warnings[0]['message'])
182 def test_currentEUID(self):
184 If the current euid is the same as the euid passed to L{util.switchUID},
185 then initgroups does not get called, but a warning is issued.
187 euid = self.mockos.geteuid()
188 util.switchUID(euid, None, True)
189 self.assertEqual(self.initgroupsCalls, [])
190 self.assertEqual(self.mockos.seteuidCalls, [])
191 warnings = self.flushWarnings([util.switchUID])
192 self.assertEqual(len(warnings), 1)
193 self.assertIn('tried to drop privileges and seteuid %i' % euid,
194 warnings[0]['message'])
195 self.assertIn('but euid is already %i' % euid, warnings[0]['message'])
199 class TestMergeFunctionMetadata(unittest.TestCase):
201 Tests for L{mergeFunctionMetadata}.
204 def test_mergedFunctionBehavesLikeMergeTarget(self):
206 After merging C{foo}'s data into C{bar}, the returned function behaves
209 foo_object = object()
210 bar_object = object()
215 def bar(x, y, (a, b), c=10, *d, **e):
218 baz = util.mergeFunctionMetadata(foo, bar)
219 self.assertIdentical(baz(1, 2, (3, 4), quux=10), bar_object)
222 def test_moduleIsMerged(self):
224 Merging C{foo} into C{bar} returns a function with C{foo}'s
232 bar.__module__ = 'somewhere.else'
234 baz = util.mergeFunctionMetadata(foo, bar)
235 self.assertEqual(baz.__module__, foo.__module__)
238 def test_docstringIsMerged(self):
240 Merging C{foo} into C{bar} returns a function with C{foo}'s docstring.
253 baz = util.mergeFunctionMetadata(foo, bar)
254 self.assertEqual(baz.__doc__, foo.__doc__)
257 def test_nameIsMerged(self):
259 Merging C{foo} into C{bar} returns a function with C{foo}'s name.
268 baz = util.mergeFunctionMetadata(foo, bar)
269 self.assertEqual(baz.__name__, foo.__name__)
272 def test_instanceDictionaryIsMerged(self):
274 Merging C{foo} into C{bar} returns a function with C{bar}'s
275 dictionary, updated by C{foo}'s.
288 baz = util.mergeFunctionMetadata(foo, bar)
289 self.assertEqual(foo.a, baz.a)
290 self.assertEqual(foo.b, baz.b)
291 self.assertEqual(bar.c, baz.c)
295 class OrderedDictTest(unittest.TestCase):
296 def testOrderedDict(self):
297 d = util.OrderedDict()
302 self.assertEqual(repr(d), "{'a': 'b', 'b': 'a', 3: 12, 1234: 4321}")
303 self.assertEqual(d.values(), ['b', 'a', 12, 4321])
305 self.assertEqual(repr(d), "{'a': 'b', 'b': 'a', 1234: 4321}")
306 self.assertEqual(d, {'a': 'b', 'b': 'a', 1234:4321})
307 self.assertEqual(d.keys(), ['a', 'b', 1234])
308 self.assertEqual(list(d.iteritems()),
309 [('a', 'b'), ('b','a'), (1234, 4321)])
311 self.assertEqual(item, (1234, 4321))
313 def testInitialization(self):
314 d = util.OrderedDict({'monkey': 'ook',
316 self.failUnless(d._order)
318 d = util.OrderedDict(((1,1),(3,3),(2,2),(0,0)))
319 self.assertEqual(repr(d), "{1: 1, 3: 3, 2: 2, 0: 0}")
321 class InsensitiveDictTest(unittest.TestCase):
322 def testPreserve(self):
323 InsensitiveDict=util.InsensitiveDict
324 dct=InsensitiveDict({'Foo':'bar', 1:2, 'fnz':{1:2}}, preserve=1)
325 self.assertEqual(dct['fnz'], {1:2})
326 self.assertEqual(dct['foo'], 'bar')
327 self.assertEqual(dct.copy(), dct)
328 self.assertEqual(dct['foo'], dct.get('Foo'))
329 assert 1 in dct and 'foo' in dct
330 self.assertEqual(eval(repr(dct)), dct)
331 keys=['Foo', 'fnz', 1]
333 assert x in dct.keys()
334 assert (x, dct[x]) in dct.items()
335 self.assertEqual(len(keys), len(dct))
339 def testNoPreserve(self):
340 InsensitiveDict=util.InsensitiveDict
341 dct=InsensitiveDict({'Foo':'bar', 1:2, 'fnz':{1:2}}, preserve=0)
342 keys=['foo', 'fnz', 1]
344 assert x in dct.keys()
345 assert (x, dct[x]) in dct.items()
346 self.assertEqual(len(keys), len(dct))
353 class PasswordTestingProcessProtocol(ProcessProtocol):
355 Write the string C{"secret\n"} to a subprocess and then collect all of
356 its output and fire a Deferred with it when the process ends.
358 def connectionMade(self):
360 self.transport.write('secret\n')
362 def childDataReceived(self, fd, output):
363 self.output.append((fd, output))
365 def processEnded(self, reason):
366 self.finished.callback((reason, self.output))
369 class GetPasswordTest(unittest.TestCase):
370 if not IReactorProcess.providedBy(reactor):
371 skip = "Process support required to test getPassword"
373 def test_stdin(self):
375 Making sure getPassword accepts a password from standard input by
376 running a child process which uses getPassword to read in a string
377 which it then writes it out again. Write a string to the child
378 process and then read one and make sure it is the right string.
380 p = PasswordTestingProcessProtocol()
381 p.finished = Deferred()
382 reactor.spawnProcess(
388 'from twisted.python.util import getPassword\n'
389 'sys.stdout.write(getPassword())\n'
390 'sys.stdout.flush()\n')],
391 env={'PYTHONPATH': os.pathsep.join(sys.path)})
393 def processFinished((reason, output)):
394 reason.trap(ProcessDone)
395 self.assertIn((1, 'secret'), output)
397 return p.finished.addCallback(processFinished)
401 class SearchUpwardsTest(unittest.TestCase):
402 def testSearchupwards(self):
403 os.makedirs('searchupwards/a/b/c')
404 file('searchupwards/foo.txt', 'w').close()
405 file('searchupwards/a/foo.txt', 'w').close()
406 file('searchupwards/a/b/c/foo.txt', 'w').close()
407 os.mkdir('searchupwards/bar')
408 os.mkdir('searchupwards/bam')
409 os.mkdir('searchupwards/a/bar')
410 os.mkdir('searchupwards/a/b/bam')
411 actual=util.searchupwards('searchupwards/a/b/c',
414 expected=os.path.abspath('searchupwards') + os.sep
415 self.assertEqual(actual, expected)
416 shutil.rmtree('searchupwards')
417 actual=util.searchupwards('searchupwards/a/b/c',
421 self.assertEqual(actual, expected)
425 class IntervalDifferentialTestCase(unittest.TestCase):
426 def testDefault(self):
427 d = iter(util.IntervalDifferential([], 10))
429 self.assertEqual(d.next(), (10, None))
431 def testSingle(self):
432 d = iter(util.IntervalDifferential([5], 10))
434 self.assertEqual(d.next(), (5, 0))
437 d = iter(util.IntervalDifferential([5, 7], 10))
439 self.assertEqual(d.next(), (5, 0))
440 self.assertEqual(d.next(), (2, 1))
441 self.assertEqual(d.next(), (3, 0))
442 self.assertEqual(d.next(), (4, 1))
443 self.assertEqual(d.next(), (1, 0))
444 self.assertEqual(d.next(), (5, 0))
445 self.assertEqual(d.next(), (1, 1))
446 self.assertEqual(d.next(), (4, 0))
447 self.assertEqual(d.next(), (3, 1))
448 self.assertEqual(d.next(), (2, 0))
449 self.assertEqual(d.next(), (5, 0))
450 self.assertEqual(d.next(), (0, 1))
452 def testTriple(self):
453 d = iter(util.IntervalDifferential([2, 4, 5], 10))
455 self.assertEqual(d.next(), (2, 0))
456 self.assertEqual(d.next(), (2, 0))
457 self.assertEqual(d.next(), (0, 1))
458 self.assertEqual(d.next(), (1, 2))
459 self.assertEqual(d.next(), (1, 0))
460 self.assertEqual(d.next(), (2, 0))
461 self.assertEqual(d.next(), (0, 1))
462 self.assertEqual(d.next(), (2, 0))
463 self.assertEqual(d.next(), (0, 2))
464 self.assertEqual(d.next(), (2, 0))
465 self.assertEqual(d.next(), (0, 1))
466 self.assertEqual(d.next(), (2, 0))
467 self.assertEqual(d.next(), (1, 2))
468 self.assertEqual(d.next(), (1, 0))
469 self.assertEqual(d.next(), (0, 1))
470 self.assertEqual(d.next(), (2, 0))
471 self.assertEqual(d.next(), (2, 0))
472 self.assertEqual(d.next(), (0, 1))
473 self.assertEqual(d.next(), (0, 2))
475 def testInsert(self):
476 d = iter(util.IntervalDifferential([], 10))
477 self.assertEqual(d.next(), (10, None))
479 self.assertEqual(d.next(), (3, 0))
480 self.assertEqual(d.next(), (3, 0))
482 self.assertEqual(d.next(), (3, 0))
483 self.assertEqual(d.next(), (3, 0))
484 self.assertEqual(d.next(), (0, 1))
485 self.assertEqual(d.next(), (3, 0))
486 self.assertEqual(d.next(), (3, 0))
487 self.assertEqual(d.next(), (0, 1))
489 def testRemove(self):
490 d = iter(util.IntervalDifferential([3, 5], 10))
491 self.assertEqual(d.next(), (3, 0))
492 self.assertEqual(d.next(), (2, 1))
493 self.assertEqual(d.next(), (1, 0))
495 self.assertEqual(d.next(), (4, 0))
496 self.assertEqual(d.next(), (5, 0))
498 self.assertEqual(d.next(), (10, None))
499 self.assertRaises(ValueError, d.removeInterval, 10)
503 class Record(util.FancyEqMixin):
505 Trivial user of L{FancyEqMixin} used by tests.
507 compareAttributes = ('a', 'b')
509 def __init__(self, a, b):
515 class DifferentRecord(util.FancyEqMixin):
517 Trivial user of L{FancyEqMixin} which is not related to L{Record}.
519 compareAttributes = ('a', 'b')
521 def __init__(self, a, b):
527 class DerivedRecord(Record):
529 A class with an inheritance relationship to L{Record}.
534 class EqualToEverything(object):
536 A class the instances of which consider themselves equal to everything.
538 def __eq__(self, other):
542 def __ne__(self, other):
547 class EqualToNothing(object):
549 A class the instances of which consider themselves equal to nothing.
551 def __eq__(self, other):
555 def __ne__(self, other):
560 class EqualityTests(unittest.TestCase):
562 Tests for L{FancyEqMixin}.
564 def test_identity(self):
566 Instances of a class which mixes in L{FancyEqMixin} but which
567 defines no comparison attributes compare by identity.
569 class Empty(util.FancyEqMixin):
572 self.assertFalse(Empty() == Empty())
573 self.assertTrue(Empty() != Empty())
575 self.assertTrue(empty == empty)
576 self.assertFalse(empty != empty)
579 def test_equality(self):
581 Instances of a class which mixes in L{FancyEqMixin} should compare
582 equal if all of their attributes compare equal. They should not
583 compare equal if any of their attributes do not compare equal.
585 self.assertTrue(Record(1, 2) == Record(1, 2))
586 self.assertFalse(Record(1, 2) == Record(1, 3))
587 self.assertFalse(Record(1, 2) == Record(2, 2))
588 self.assertFalse(Record(1, 2) == Record(3, 4))
591 def test_unequality(self):
593 Unequality between instances of a particular L{record} should be
594 defined as the negation of equality.
596 self.assertFalse(Record(1, 2) != Record(1, 2))
597 self.assertTrue(Record(1, 2) != Record(1, 3))
598 self.assertTrue(Record(1, 2) != Record(2, 2))
599 self.assertTrue(Record(1, 2) != Record(3, 4))
602 def test_differentClassesEquality(self):
604 Instances of different classes which mix in L{FancyEqMixin} should not
607 self.assertFalse(Record(1, 2) == DifferentRecord(1, 2))
610 def test_differentClassesInequality(self):
612 Instances of different classes which mix in L{FancyEqMixin} should
615 self.assertTrue(Record(1, 2) != DifferentRecord(1, 2))
618 def test_inheritedClassesEquality(self):
620 An instance of a class which derives from a class which mixes in
621 L{FancyEqMixin} should compare equal to an instance of the base class
622 if and only if all of their attributes compare equal.
624 self.assertTrue(Record(1, 2) == DerivedRecord(1, 2))
625 self.assertFalse(Record(1, 2) == DerivedRecord(1, 3))
626 self.assertFalse(Record(1, 2) == DerivedRecord(2, 2))
627 self.assertFalse(Record(1, 2) == DerivedRecord(3, 4))
630 def test_inheritedClassesInequality(self):
632 An instance of a class which derives from a class which mixes in
633 L{FancyEqMixin} should compare unequal to an instance of the base
634 class if any of their attributes compare unequal.
636 self.assertFalse(Record(1, 2) != DerivedRecord(1, 2))
637 self.assertTrue(Record(1, 2) != DerivedRecord(1, 3))
638 self.assertTrue(Record(1, 2) != DerivedRecord(2, 2))
639 self.assertTrue(Record(1, 2) != DerivedRecord(3, 4))
642 def test_rightHandArgumentImplementsEquality(self):
644 The right-hand argument to the equality operator is given a chance
645 to determine the result of the operation if it is of a type
646 unrelated to the L{FancyEqMixin}-based instance on the left-hand
649 self.assertTrue(Record(1, 2) == EqualToEverything())
650 self.assertFalse(Record(1, 2) == EqualToNothing())
653 def test_rightHandArgumentImplementsUnequality(self):
655 The right-hand argument to the non-equality operator is given a
656 chance to determine the result of the operation if it is of a type
657 unrelated to the L{FancyEqMixin}-based instance on the left-hand
660 self.assertFalse(Record(1, 2) != EqualToEverything())
661 self.assertTrue(Record(1, 2) != EqualToNothing())
665 class RunAsEffectiveUserTests(unittest.TestCase):
667 Test for the L{util.runAsEffectiveUser} function.
670 if getattr(os, "geteuid", None) is None:
671 skip = "geteuid/seteuid not available"
674 self.mockos = MockOS()
675 self.patch(os, "geteuid", self.mockos.geteuid)
676 self.patch(os, "getegid", self.mockos.getegid)
677 self.patch(os, "seteuid", self.mockos.seteuid)
678 self.patch(os, "setegid", self.mockos.setegid)
681 def _securedFunction(self, startUID, startGID, wantUID, wantGID):
683 Check if wanted UID/GID matched start or saved ones.
685 self.assertTrue(wantUID == startUID or
686 wantUID == self.mockos.seteuidCalls[-1])
687 self.assertTrue(wantGID == startGID or
688 wantGID == self.mockos.setegidCalls[-1])
691 def test_forwardResult(self):
693 L{util.runAsEffectiveUser} forwards the result obtained by calling the
696 result = util.runAsEffectiveUser(0, 0, lambda: 1)
697 self.assertEqual(result, 1)
700 def test_takeParameters(self):
702 L{util.runAsEffectiveUser} pass the given parameters to the given
705 result = util.runAsEffectiveUser(0, 0, lambda x: 2*x, 3)
706 self.assertEqual(result, 6)
709 def test_takesKeyworkArguments(self):
711 L{util.runAsEffectiveUser} pass the keyword parameters to the given
714 result = util.runAsEffectiveUser(0, 0, lambda x, y=1, z=1: x*y*z, 2, z=3)
715 self.assertEqual(result, 6)
718 def _testUIDGIDSwitch(self, startUID, startGID, wantUID, wantGID,
719 expectedUIDSwitches, expectedGIDSwitches):
721 Helper method checking the calls to C{os.seteuid} and C{os.setegid}
722 made by L{util.runAsEffectiveUser}, when switching from startUID to
723 wantUID and from startGID to wantGID.
725 self.mockos.euid = startUID
726 self.mockos.egid = startGID
727 util.runAsEffectiveUser(
729 self._securedFunction, startUID, startGID, wantUID, wantGID)
730 self.assertEqual(self.mockos.seteuidCalls, expectedUIDSwitches)
731 self.assertEqual(self.mockos.setegidCalls, expectedGIDSwitches)
732 self.mockos.seteuidCalls = []
733 self.mockos.setegidCalls = []
738 Check UID/GID switches when current effective UID is root.
740 self._testUIDGIDSwitch(0, 0, 0, 0, [], [])
741 self._testUIDGIDSwitch(0, 0, 1, 0, [1, 0], [])
742 self._testUIDGIDSwitch(0, 0, 0, 1, [], [1, 0])
743 self._testUIDGIDSwitch(0, 0, 1, 1, [1, 0], [1, 0])
748 Check UID/GID switches when current effective UID is non-root.
750 self._testUIDGIDSwitch(1, 0, 0, 0, [0, 1], [])
751 self._testUIDGIDSwitch(1, 0, 1, 0, [], [])
752 self._testUIDGIDSwitch(1, 0, 1, 1, [0, 1, 0, 1], [1, 0])
753 self._testUIDGIDSwitch(1, 0, 2, 1, [0, 2, 0, 1], [1, 0])
758 Check UID/GID switches when current effective GID is non-root.
760 self._testUIDGIDSwitch(0, 1, 0, 0, [], [0, 1])
761 self._testUIDGIDSwitch(0, 1, 0, 1, [], [])
762 self._testUIDGIDSwitch(0, 1, 1, 1, [1, 0], [])
763 self._testUIDGIDSwitch(0, 1, 1, 2, [1, 0], [2, 1])
766 def test_UIDGID(self):
768 Check UID/GID switches when current effective UID/GID is non-root.
770 self._testUIDGIDSwitch(1, 1, 0, 0, [0, 1], [0, 1])
771 self._testUIDGIDSwitch(1, 1, 0, 1, [0, 1], [])
772 self._testUIDGIDSwitch(1, 1, 1, 0, [0, 1, 0, 1], [0, 1])
773 self._testUIDGIDSwitch(1, 1, 1, 1, [], [])
774 self._testUIDGIDSwitch(1, 1, 2, 1, [0, 2, 0, 1], [])
775 self._testUIDGIDSwitch(1, 1, 1, 2, [0, 1, 0, 1], [2, 1])
776 self._testUIDGIDSwitch(1, 1, 2, 2, [0, 2, 0, 1], [2, 1])
780 class UnsignedIDTests(unittest.TestCase):
782 Tests for L{util.unsignedID} and L{util.setIDFunction}.
786 Save the value of L{util._idFunction} and arrange for it to be restored
789 self.addCleanup(setattr, util, '_idFunction', util._idFunction)
792 def test_setIDFunction(self):
794 L{util.setIDFunction} returns the last value passed to it.
797 previous = util.setIDFunction(value)
798 result = util.setIDFunction(previous)
799 self.assertIdentical(value, result)
802 def test_unsignedID(self):
804 L{util.unsignedID} uses the function passed to L{util.setIDFunction} to
805 determine the unique integer id of an object and then adjusts it to be
806 positive if necessary.
811 # A fake object identity mapping
812 objects = {foo: 17, bar: -73}
816 util.setIDFunction(fakeId)
818 self.assertEqual(util.unsignedID(foo), 17)
819 self.assertEqual(util.unsignedID(bar), (sys.maxint + 1) * 2 - 73)
822 def test_defaultIDFunction(self):
824 L{util.unsignedID} uses the built in L{id} by default.
829 idValue += (sys.maxint + 1) * 2
831 self.assertEqual(util.unsignedID(obj), idValue)
835 class InitGroupsTests(unittest.TestCase):
837 Tests for L{util.initgroups}.
841 skip = "pwd not available"
845 self.addCleanup(setattr, util, "_c_initgroups", util._c_initgroups)
846 self.addCleanup(setattr, util, "setgroups", util.setgroups)
849 def test_initgroupsForceC(self):
851 If we fake the presence of the C extension, it's called instead of the
852 Python implementation.
855 util._c_initgroups = lambda x, y: calls.append((x, y))
857 util.setgroups = calls.append
859 util.initgroups(os.getuid(), 4)
860 self.assertEqual(calls, [(pwd.getpwuid(os.getuid())[0], 4)])
861 self.assertFalse(setgroupsCalls)
864 def test_initgroupsForcePython(self):
866 If we fake the absence of the C extension, the Python implementation is
867 called instead, calling C{os.setgroups}.
869 util._c_initgroups = None
871 util.setgroups = calls.append
872 util.initgroups(os.getuid(), os.getgid())
873 # Something should be in the calls, we don't really care what
874 self.assertTrue(calls)
877 def test_initgroupsInC(self):
879 If the C extension is present, it's called instead of the Python
880 version. We check that by making sure C{os.setgroups} is not called.
883 util.setgroups = calls.append
885 util.initgroups(os.getuid(), os.getgid())
888 self.assertFalse(calls)
891 if util._c_initgroups is None:
892 test_initgroupsInC.skip = "C initgroups not available"